Date: Sun, 8 Nov 2026 19:13:54 -0800
Subject: [PATCH 06/34] Say which apps need a key, and where to get it, before
the key form (#1297)
---
app/src/components/manage/plugin-page.tsx | 8 +-
.../components/plugins/broker-directory.tsx | 23 +-
.../plugins/brokered-account-row.tsx | 79 +++--
app/src/lib/connections/rows.ts | 9 +-
app/src/lib/plugins/enablement-notes.ts | 40 ++-
app/src/lib/plugins/mutations.ts | 28 +-
app/src/lib/plugins/provider-vocabulary.ts | 9 +-
app/src/lib/plugins/queries.ts | 8 +
app/tests/arcade-breadth.test.tsx | 1 +
app/tests/arcade-coverage.test.tsx | 1 +
app/tests/arcade-directory-screen.test.tsx | 5 +
app/tests/brokered-account-row.test.tsx | 58 +++-
app/tests/composio-picker-add-error.test.tsx | 4 +
app/tests/composio-picker.test.tsx | 1 +
app/tests/connection-rows.test.ts | 26 ++
app/tests/key-only-apps-directory.test.tsx | 179 +++++++++++
server/src/plugins/app-names.ts | 25 ++
server/src/plugins/broker.ts | 11 +
server/src/plugins/composio-client/fields.ts | 38 ++-
server/src/plugins/composio-vendor.ts | 9 +-
server/src/plugins/key-guides.ts | 297 ++++++++++++++++++
.../src/plugins/route-groups/connections.ts | 27 +-
server/tests/fixtures/composio-key-apps.json | 229 ++++++++++++++
server/tests/key-only-apps.test.ts | 226 +++++++++++++
server/tests/plugin-routes.test.ts | 17 +-
25 files changed, 1311 insertions(+), 47 deletions(-)
create mode 100644 app/tests/key-only-apps-directory.test.tsx
create mode 100644 server/src/plugins/app-names.ts
create mode 100644 server/src/plugins/key-guides.ts
create mode 100644 server/tests/fixtures/composio-key-apps.json
create mode 100644 server/tests/key-only-apps.test.ts
diff --git a/app/src/components/manage/plugin-page.tsx b/app/src/components/manage/plugin-page.tsx
index e4fc5d2625..2698800be6 100644
--- a/app/src/components/manage/plugin-page.tsx
+++ b/app/src/components/manage/plugin-page.tsx
@@ -919,7 +919,13 @@ export function PluginPage({ pluginKey: key }: { pluginKey: string }) {
How this is reached
- {brokeredReach(provider, brokeredApp).description}
+ {
+ brokeredReach(
+ provider,
+ brokeredApp,
+ brokeredAccount.kind === "fields",
+ ).description
+ }
diff --git a/app/src/components/plugins/broker-directory.tsx b/app/src/components/plugins/broker-directory.tsx
index d634ac7e08..4165e6ae02 100644
--- a/app/src/components/plugins/broker-directory.tsx
+++ b/app/src/components/plugins/broker-directory.tsx
@@ -1,4 +1,4 @@
-import { IconPlug, IconSearch } from "@tabler/icons-react";
+import { IconKey, IconPlug, IconSearch } from "@tabler/icons-react";
import { useMutation, useQuery } from "@tanstack/react-query";
import * as React from "react";
import {
@@ -24,6 +24,7 @@ import { useManagePaths } from "@/lib/manage-paths";
import {
consentBreadth,
coverageWarning,
+ keyNeededLine,
matchingApps,
} from "@/lib/plugins/enablement-notes";
import {
@@ -228,6 +229,7 @@ export function BrokerDirectory({ provider }: { provider: PluginsProvider }) {
{entry.name}
{entry.description}
+
{entry.enabled ? null : }
{entry.enabled ? null : }
@@ -270,6 +272,25 @@ export function BrokerDirectory({ provider }: { provider: PluginsProvider }) {
);
}
+/**
+ * "Needs a key from 1Password" on an app that takes a key typed in, so nobody who expected a
+ * sign-in is surprised at Connect. Drawn whether or not the app is added: the person connecting
+ * reads this list too. Draws nothing for an app you sign in to.
+ */
+export function KeyNeededNote({ app }: { app: ComposioApp }) {
+ const line = keyNeededLine(app);
+ if (!line) return null;
+ return (
+
+
+ {line}
+
+ );
+}
+
/** The warning on a row, shown before Add is pressed. Draws nothing when there is none. */
export function CoverageNote({ app }: { app: ComposioApp }) {
const warning = coverageWarning(app);
diff --git a/app/src/components/plugins/brokered-account-row.tsx b/app/src/components/plugins/brokered-account-row.tsx
index 3ae4b3f1c4..54fa342b12 100644
--- a/app/src/components/plugins/brokered-account-row.tsx
+++ b/app/src/components/plugins/brokered-account-row.tsx
@@ -25,8 +25,11 @@ import {
connectBrokeredWithFieldsMutationOptions,
disconnectBrokeredMutationOptions,
invalidatePlugins,
+ type KeyForm,
+ type KeyGuide,
recheckBrokeredConnectionMutationOptions,
} from "@/lib/plugins/mutations";
+import { schemeNeedsKey } from "@/lib/plugins/enablement-notes";
import {
type BrokeredAccountKind,
brokeredKeyMissing,
@@ -60,15 +63,6 @@ import {
*/
export type { BrokeredAccountKind };
-/** The schemes whose secret a person types, which is the whole of what `fields` means here. */
-const FIELD_SCHEMES = [
- "API_KEY",
- "BASIC",
- "BEARER_TOKEN",
- "BASIC_WITH_JWT",
- "S2S_OAUTH2",
-];
-
/**
* Which of the three a recorded scheme is.
*
@@ -79,9 +73,7 @@ const FIELD_SCHEMES = [
*/
function kindOf(authScheme: string | null): BrokeredAccountKind {
if (authScheme === "NO_AUTH") return "no-auth";
- return authScheme !== null && FIELD_SCHEMES.includes(authScheme)
- ? "fields"
- : "consent";
+ return schemeNeedsKey(authScheme) ? "fields" : "consent";
}
/** The row's state and the things it can do, from {@link useBrokeredAccount}. */
@@ -214,6 +206,8 @@ export type BrokeredAccount = {
* so reading the request would make this null on every open — see the hook for what that cost.
*/
fields: BrokerField[] | null;
+ /** Where the key comes from, as the same answer gave it. Null beside a null `fields`. */
+ guide: KeyGuide | null;
/** Ask the app what it needs, which is the first press on a `fields` app. */
requestFields: () => void;
/**
@@ -520,7 +514,7 @@ export function useBrokeredAccount(input: {
* list away, which a `??` could not. A REFUSAL leaves the list alone, because a refusal is not an
* answer about the app; what the dialog does about it is decided where the dialog is drawn.
*/
- const [published, setPublished] = useState(null);
+ const [published, setPublished] = useState(null);
if (fieldsRequest.isSuccess && fieldsRequest.data !== published) {
setPublished(fieldsRequest.data);
}
@@ -672,7 +666,8 @@ export function useBrokeredAccount(input: {
*/
disconnected: disconnect.isSuccess,
kind: kindOf(authScheme),
- fields: published,
+ fields: published?.fields ?? null,
+ guide: published?.guide ?? null,
requestFields: () => {
report(null);
/*
@@ -834,7 +829,7 @@ function accountSentence(input: {
* way this app is connected, and a row that replaced the whole line took it away with the trip
* it was right to drop.
*/
- const asked = `${title.charAt(0).toUpperCase()}${title.slice(1)} connects with a key you already have. Connect asks for it.`;
+ const asked = `Needs a key from ${title}. Connect shows where to get one.`;
return disconnectedReassurance
? `${asked} ${disconnectedReassurance}`
: asked;
@@ -850,6 +845,41 @@ function accountSentence(input: {
: disconnectedDescription;
}
+/**
+ * Above the form: why this app asks for a key, what to do at the provider, and a link to its page.
+ *
+ * Most apps in the directory have no sign-in, and a person who pressed Connect expecting one meets
+ * a form. This is what tells them that is expected and where the key comes from, before the boxes.
+ */
+function KeyGuideNote({
+ guide,
+ title,
+}: {
+ guide: KeyGuide | null;
+ title: string;
+}) {
+ return (
+
+
+ {title} doesn't offer a sign-in here, so it connects with a key from
+ your {title} account.
+
+ {guide ?
{guide.steps}
: null}
+ {guide?.url ? (
+
+ Get a key from {title}
+
+
+ ) : null}
+
+ );
+}
+
/**
* The row itself, for a `PageRows` card on either screen.
*
@@ -1069,7 +1099,8 @@ export function BrokeredAccountRow({
>
) : (
- /* The arrow says this leaves OpenBot for the vendor's consent page. It does. */
+ /* The arrow says this leaves OpenBot for the vendor's consent page. It does, for
+ every app but a key app. */
{
@@ -1090,7 +1121,8 @@ export function BrokeredAccountRow({
variant="outline"
>
Connect
-
+ {/* A key app opens a form here and leaves for nowhere, so no arrow. */}
+ {account.kind === "fields" ? null : }
)}
@@ -1134,11 +1166,14 @@ export function BrokeredAccountRow({
{account.fieldsError}
) : account.fields ? (
-
+ <>
+
+
+ >
) : account.requestingFields ? (
Checking what the app needs…
diff --git a/app/src/lib/connections/rows.ts b/app/src/lib/connections/rows.ts
index edd8243a7c..40802a0242 100644
--- a/app/src/lib/connections/rows.ts
+++ b/app/src/lib/connections/rows.ts
@@ -1,4 +1,5 @@
import type { IdentityProviders, LinkedAccount } from "@/lib/identity/queries";
+import { schemeNeedsKey } from "@/lib/plugins/enablement-notes";
import { brokeredProvenance } from "@/lib/plugins/provider-vocabulary";
import type { CatalogueItem, PluginServer } from "@/lib/plugins/queries";
@@ -131,8 +132,12 @@ export function connectionRows(input: {
key: server.id,
title: server.title,
// No line rather than "Connect your Todoist account.", which only repeated the tile's
- // Connect button.
- summary: server.summary,
+ // Connect button. A key app not yet connected says it needs a key, so nobody who
+ // expected a sign-in is surprised when the page asks for one.
+ summary:
+ !input.connected.has(server.id) && schemeNeedsKey(server.authScheme)
+ ? `Needs a key from ${server.title}`
+ : server.summary,
state: input.connected.has(server.id) ? "connected" : "available",
mark: { kind: "logo", logo: server.logo },
}),
diff --git a/app/src/lib/plugins/enablement-notes.ts b/app/src/lib/plugins/enablement-notes.ts
index 08c5cee8cf..df464aee20 100644
--- a/app/src/lib/plugins/enablement-notes.ts
+++ b/app/src/lib/plugins/enablement-notes.ts
@@ -89,9 +89,47 @@ export function consentBreadth(
* render; sorting it in place would rewrite what the cache holds. By name rather than by the
* vendor's own order, which is popularity — useful for a landing page, useless for finding the one
* app somebody typed half the name of.
+ *
+ * APPS YOU SIGN IN TO COME FIRST, THEN APPS THAT NEED A KEY, each half by name. Most of the
+ * catalogue only takes a key or a token typed in, and someone browsing expects a sign-in. Listing
+ * the sign-in apps first puts the ones anybody can connect at the top of every search, and each
+ * key app's row says so (see {@link keyNeededLine}).
*/
export function matchingApps(apps: ComposioApp[]): ComposioApp[] {
- return [...apps].sort((left, right) => left.name.localeCompare(right.name));
+ return [...apps].sort(
+ (left, right) =>
+ Number(needsKey(left)) - Number(needsKey(right)) ||
+ left.name.localeCompare(right.name),
+ );
+}
+
+/** The schemes whose secret a person types, which is the whole of what a key app means here. */
+const FIELD_SCHEMES = [
+ "API_KEY",
+ "BASIC",
+ "BEARER_TOKEN",
+ "BASIC_WITH_JWT",
+ "S2S_OAUTH2",
+];
+
+/** Whether an added app's recorded scheme is one a person types a key for. */
+export function schemeNeedsKey(authScheme: string | null): boolean {
+ return authScheme !== null && FIELD_SCHEMES.includes(authScheme);
+}
+
+/** Whether connecting this app means typing a key from it rather than signing in. */
+export function needsKey(app: Pick): boolean {
+ return app.connection.kind === "fields";
+}
+
+/**
+ * The line on a directory row that says, before anybody adds the app, that it takes a key.
+ * Null for an app you sign in to, or one that needs nothing.
+ */
+export function keyNeededLine(
+ app: Pick,
+): string | null {
+ return needsKey(app) ? `Needs a key from ${app.name}` : null;
}
/**
diff --git a/app/src/lib/plugins/mutations.ts b/app/src/lib/plugins/mutations.ts
index 02d3bacf34..2fb4ee0d7e 100644
--- a/app/src/lib/plugins/mutations.ts
+++ b/app/src/lib/plugins/mutations.ts
@@ -580,20 +580,32 @@ export type BrokerField = {
*/
export function brokeredConnectionFieldsMutationOptions() {
return mutationOptions({
- mutationFn: async (serverId: string): Promise => {
- const fields = await client(
+ mutationFn: async (serverId: string): Promise => {
+ const fallback = "Couldn't find out what this app needs.";
+ const response = await client(
`/api/plugins/servers/${encodeURIComponent(serverId)}/connect`,
- "fields",
- {
- method: "POST",
- fallback: "Couldn't find out what this app needs.",
- },
+ { method: "POST", fallback },
);
- return fields ?? null;
+ const body = (await response.json().catch(() => null)) as {
+ fields?: BrokerField[];
+ guide?: KeyGuide;
+ } | null;
+ if (!body || typeof body !== "object") throw new Error(fallback);
+ if (!body.fields) return null;
+ return { fields: body.fields, guide: body.guide ?? null };
},
});
}
+/**
+ * Where a person gets the key an app asks for: what to do there, and the provider's own page for
+ * it. `url` is null where nobody has a page to point at; the steps are always there.
+ */
+export type KeyGuide = { steps: string; url: string | null };
+
+/** What the first press on a key app answers: the boxes to fill in, and where the key comes from. */
+export type KeyForm = { fields: BrokerField[]; guide: KeyGuide | null };
+
/**
* What handing a key over comes back with. Declared once, for the reason
* {@link BrokeredConnectionConfirmation} gives.
diff --git a/app/src/lib/plugins/provider-vocabulary.ts b/app/src/lib/plugins/provider-vocabulary.ts
index ceacc30bd6..3a67e492f8 100644
--- a/app/src/lib/plugins/provider-vocabulary.ts
+++ b/app/src/lib/plugins/provider-vocabulary.ts
@@ -202,10 +202,14 @@ export function brokeredConnectionDescription(
* authorization URL), so OpenBot never holds the password; a key app takes a key you paste, so
* that sentence promises nothing about passwords; and an app with no account has nothing to sign
* in to.
+ *
+ * `takesKey` is the team edition's key app, read by the same page. "There is no token to paste"
+ * was said on those too, right above a Connect that asks for one.
*/
export function brokeredReach(
_provider: PluginsProvider,
personal?: BrokeredApp,
+ takesKey = false,
): {
description: string;
badge: string;
@@ -213,8 +217,9 @@ export function brokeredReach(
const badge = "Through OpenBot";
if (!personal) {
return {
- description:
- "This deployment holds one key for every app like this. Each person connects their own account, and a Bot sees only what that person can see. There is no token to paste.",
+ description: takesKey
+ ? "Each person connects their own account with a key from the app, and a Bot sees only what that person can see."
+ : "This deployment holds one key for every app like this. Each person connects their own account, and a Bot sees only what that person can see. There is no token to paste.",
badge,
};
}
diff --git a/app/src/lib/plugins/queries.ts b/app/src/lib/plugins/queries.ts
index eea74eac92..2432f5b4d5 100644
--- a/app/src/lib/plugins/queries.ts
+++ b/app/src/lib/plugins/queries.ts
@@ -214,6 +214,14 @@ export type ComposioApp = {
classifiedCount: number | null;
permissionCount: number | null;
scopedActionCount: number | null;
+ /**
+ * How a person connects the app, as the server decided it off the broker's catalogue. `fields`
+ * is the one that asks for a key typed in; every other kind is a sign-in, or nothing at all.
+ * The directory says so on the row before anybody adds the app.
+ */
+ connection: {
+ kind: "consent" | "self-registering" | "fields" | "no-auth" | "unsupported";
+ };
enabled: boolean;
};
diff --git a/app/tests/arcade-breadth.test.tsx b/app/tests/arcade-breadth.test.tsx
index 78950f5940..856b1359e1 100644
--- a/app/tests/arcade-breadth.test.tsx
+++ b/app/tests/arcade-breadth.test.tsx
@@ -42,6 +42,7 @@ function app(overrides: Partial = {}): ComposioApp {
classifiedCount: null,
permissionCount: null,
scopedActionCount: null,
+ connection: { kind: "consent" },
enabled: false,
...overrides,
};
diff --git a/app/tests/arcade-coverage.test.tsx b/app/tests/arcade-coverage.test.tsx
index c34fab6ed7..3a6d855acb 100644
--- a/app/tests/arcade-coverage.test.tsx
+++ b/app/tests/arcade-coverage.test.tsx
@@ -42,6 +42,7 @@ function app(overrides: Partial & { slug: string }): ComposioApp {
classifiedCount: null,
permissionCount: null,
scopedActionCount: null,
+ connection: { kind: "consent" },
enabled: false,
...overrides,
};
diff --git a/app/tests/arcade-directory-screen.test.tsx b/app/tests/arcade-directory-screen.test.tsx
index aa02f7a2ab..fe333d9d94 100644
--- a/app/tests/arcade-directory-screen.test.tsx
+++ b/app/tests/arcade-directory-screen.test.tsx
@@ -63,6 +63,9 @@ function airtableFromFixture() {
classifiedCount: tools.filter((tool) => tool.metadata?.behavior).length,
permissionCount: new Set(sets.map((s) => [...s].sort().join(" "))).size,
scopedActionCount: sets.length,
+ // Arcade has no per-person key form: an app with an authorization block is a sign-in, as
+ // `connectionOf` in `arcade-adapter.ts` reads it, and AirtableApi has one.
+ connection: { kind: sets.length > 0 ? "consent" : "no-auth" },
enabled: false,
};
}
@@ -164,6 +167,8 @@ test("the Arcade directory renders the coverage warning and the breadth line on
expect(row.textContent).toContain(
"This app uses 1 separate permission across 2 actions. A further 1 requests no permission of its own and becomes callable with any approval here.",
);
+ // Arcade signs a person in to its apps and never asks them for a key, so no row says one.
+ expect(row.textContent).not.toContain("Needs a key");
});
test("the Arcade directory redirects on a Composio deployment", async () => {
diff --git a/app/tests/brokered-account-row.test.tsx b/app/tests/brokered-account-row.test.tsx
index 599f45381a..acf6918e42 100644
--- a/app/tests/brokered-account-row.test.tsx
+++ b/app/tests/brokered-account-row.test.tsx
@@ -225,6 +225,8 @@ type Deployment = {
authScheme?: string;
/** What the app publishes as the things a person types in, for the `API_KEY` schemes. */
fields?: BrokerField[];
+ /** Where the key comes from, as the server answers it beside the fields. Absent by default. */
+ guide?: { steps: string; url: string | null };
/** Whether a real call was ever made with this key and worked, as this deployment recorded it. */
verified?: boolean;
/** When that happened. Null wherever `verified` is false — a check that failed records no time. */
@@ -560,7 +562,11 @@ function installDeployment(deployment: Deployment): Server {
);
}
if (SERVER_FIELD_SCHEMES.includes(state.authScheme)) {
- return json({ fields: state.fields });
+ return json(
+ deployment.guide
+ ? { fields: state.fields, guide: deployment.guide }
+ : { fields: state.fields },
+ );
}
return json({ authorizationUrl: CONSENT_URL });
}
@@ -940,6 +946,44 @@ test("a key app asks for what the app asked for, with its own help text", async
expect(view.queryByText(/starting with 'pplx-'/)).toBeTruthy();
});
+/**
+ * THE DIALOG SAYS WHY IT ASKS FOR A KEY AND WHERE TO GET ONE, ABOVE THE BOXES.
+ *
+ * Reported from OpenBot Desktop 0.3.3: Connect on a key-only app opened a form with the provider's
+ * developer wording and nothing else, and the person, who expected a sign-in, had nowhere to go.
+ * The guide is the server's answer for 1Password (see `server/src/plugins/key-guides.ts`).
+ */
+test("a key app's dialog explains the key and links to the provider's page for it", async () => {
+ installDeployment({
+ authScheme: "API_KEY",
+ composioConfigured: true,
+ confirms: false,
+ fields: [PERPLEXITY_KEY],
+ guide: {
+ steps:
+ "1Password connects through a Connect server that your 1Password admin runs. They create it in 1Password.com under Developer, along with an access token for it.",
+ url: "https://www.1password.dev/connect/get-started",
+ },
+ recorded: false,
+ });
+
+ const view = renderAccountScreen(queryClient());
+ await userEvent.click(await view.findByRole("button", { name: "Connect" }));
+
+ const guide = await view.findByTestId("key-guide");
+ expect(guide.textContent).toContain(
+ "Gmail doesn't offer a sign-in here, so it connects with a key from your Gmail account.",
+ );
+ expect(guide.textContent).toContain("Connect server");
+ const link = view.getByRole("link", { name: /Get a key from Gmail/ });
+ expect(link.getAttribute("href")).toBe(
+ "https://www.1password.dev/connect/get-started",
+ );
+ expect(link.getAttribute("target")).toBe("_blank");
+ // The guide sits above the form, which still draws the app's own box.
+ expect(await view.findByLabelText("API Key")).toBeTruthy();
+});
+
test("a key app nobody has connected says it will ask for a key, not send you off", async () => {
installDeployment({
authScheme: "API_KEY",
@@ -952,7 +996,9 @@ test("a key app nobody has connected says it will ask for a key, not send you of
const view = renderAccountScreen(queryClient());
expect(
- await view.findByText(/Gmail connects with a key you already have/),
+ await view.findByText(
+ /Needs a key from Gmail\. Connect shows where to get one\./,
+ ),
).toBeTruthy();
/*
* The screen's own not-connected sentence is written for the kind that leaves, and pressing
@@ -975,7 +1021,12 @@ test("a key app nobody has connected keeps the screen's own reassurance", async
// The row's own sentence, which is the one the screen's cannot be: pressing Connect here opens a
// form rather than leaving for a consent screen.
- expect(await view.findByText(/Connect asks for it\./)).toBeTruthy();
+ expect(
+ await view.findByText(/Connect shows where to get one\./),
+ ).toBeTruthy();
+ // And the row above it no longer says there is nothing to paste.
+ expect(view.queryByText(/There is no token to paste/)).toBeNull();
+ expect(view.getByText(/with a key from the app/)).toBeTruthy();
/*
* And the half of the screen's line that survives it. Replacing the whole line took away the one
* thing an administrator reading this row needs to know — that the connector is finished whether
@@ -1189,6 +1240,7 @@ function accountState(overrides: Partial): BrokeredAccount {
disconnected: false,
disconnecting: false,
fields: null,
+ guide: null,
/*
* NULL BESIDE THE NULL `submissionError` BELOW, because these cases are about the sentences the
* ROW carries and neither refusal is one of them: both belong to the dialog, which is drawn only
diff --git a/app/tests/composio-picker-add-error.test.tsx b/app/tests/composio-picker-add-error.test.tsx
index 3eac88d817..3ecd03c318 100644
--- a/app/tests/composio-picker-add-error.test.tsx
+++ b/app/tests/composio-picker-add-error.test.tsx
@@ -96,6 +96,8 @@ beforeEach(() => {
logo: null,
categories: [],
actionCount: 167,
+ // What the server sends on every row; Slack and Gmail are both sign-in apps.
+ connection: { kind: "consent" },
enabled: false,
},
{
@@ -105,6 +107,8 @@ beforeEach(() => {
logo: null,
categories: [],
actionCount: 42,
+ // What the server sends on every row; Slack and Gmail are both sign-in apps.
+ connection: { kind: "consent" },
enabled: false,
},
],
diff --git a/app/tests/composio-picker.test.tsx b/app/tests/composio-picker.test.tsx
index 4b3f10ba19..be15abc4ab 100644
--- a/app/tests/composio-picker.test.tsx
+++ b/app/tests/composio-picker.test.tsx
@@ -30,6 +30,7 @@ function app(overrides: Partial & { slug: string }): ComposioApp {
classifiedCount: null,
permissionCount: null,
scopedActionCount: null,
+ connection: { kind: "consent" },
enabled: false,
...overrides,
};
diff --git a/app/tests/connection-rows.test.ts b/app/tests/connection-rows.test.ts
index 8970196e29..4f1ac45a7c 100644
--- a/app/tests/connection-rows.test.ts
+++ b/app/tests/connection-rows.test.ts
@@ -117,3 +117,29 @@ test("and the same for Arcade's GitHub app, which is the same vendor through the
}).map((row) => row.key),
).toEqual(["arcade-gmail", "github"]);
});
+
+/**
+ * A key app's tile says it takes a key before anybody opens it, so the form on its page is no
+ * surprise. Reported from OpenBot Desktop 0.3.3, where every key app read as one more sign-in.
+ * Once it is connected the tile goes back to the app's own line.
+ */
+test("a key app nobody has connected says it needs a key; a sign-in app does not", () => {
+ const keyed = (id: string, title: string, authScheme: string) =>
+ ({ ...app(id, title), authScheme }) as PluginServer;
+ const rows = connectionRows({
+ ...none,
+ brokered: [
+ keyed("composio-_1password", "1Password", "API_KEY"),
+ keyed("composio-mixpanel", "Mixpanel", "BASIC"),
+ keyed("composio-gmail", "Gmail", "OAUTH2"),
+ keyed("composio-posthog", "PostHog", "API_KEY"),
+ ],
+ connected: new Set(["composio-posthog"]),
+ });
+ expect(rows.map((row) => [row.key, row.summary])).toEqual([
+ ["composio-posthog", ""],
+ ["composio-_1password", "Needs a key from 1Password"],
+ ["composio-mixpanel", "Needs a key from Mixpanel"],
+ ["composio-gmail", ""],
+ ]);
+});
diff --git a/app/tests/key-only-apps-directory.test.tsx b/app/tests/key-only-apps-directory.test.tsx
new file mode 100644
index 0000000000..5619ac3b76
--- /dev/null
+++ b/app/tests/key-only-apps-directory.test.tsx
@@ -0,0 +1,179 @@
+import { afterAll, afterEach, beforeAll, expect, test } from "bun:test";
+import { GlobalRegistrator } from "@happy-dom/global-registrator";
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+import {
+ createMemoryHistory,
+ createRootRoute,
+ createRoute,
+ createRouter,
+ Outlet,
+ RouterProvider,
+} from "@tanstack/react-router";
+import { cleanup, render, waitFor } from "@testing-library/react";
+import type { ComposioApp } from "@/lib/plugins/queries";
+import { Route as ComposioRoute } from "@/routes/_authed/admin/plugins/composio";
+import { displayName } from "../../server/src/plugins/app-names";
+import captured from "../../server/tests/fixtures/composio-key-apps.json";
+
+/**
+ * THE DIRECTORY SAYS WHICH APPS TAKE A KEY, BEFORE ANYBODY PRESSES ADD OR CONNECT.
+ *
+ * Reported from OpenBot Desktop 0.3.3: a person browsing for apps expected a sign-in and kept
+ * meeting a key form at Connect. Most of the catalogue has no sign-in, so the form is right; the
+ * surprise is the bug. A key app's row now says "Needs a key from 1Password", and apps you sign in
+ * to are listed first.
+ *
+ * The rows are Composio's own catalogue answer, captured read-only on 2026-11-08. Each one's
+ * `connection` is the server's verdict for that exact row, which `server/tests/key-only-apps.test.ts`
+ * pins against `connectionOf`: HubSpot and Gmail sign in, 1Password and PostHog take a key.
+ */
+
+beforeAll(() => GlobalRegistrator.register());
+afterEach(cleanup);
+afterAll(() => GlobalRegistrator.unregister());
+
+const originalFetch = global.fetch;
+afterEach(() => {
+ global.fetch = originalFetch;
+});
+
+const VERDICT: Record = {
+ hubspot: "consent",
+ gmail: "consent",
+ _1password: "fields",
+ posthog: "fields",
+};
+
+/** The captured rows, as the directory route answers them: the server's own projection. */
+function directory(): ComposioApp[] {
+ return captured.catalogue.map((row) => ({
+ slug: row.slug,
+ // The server's own naming, so 1Password arrives as its brand spells it and not "1password".
+ name: displayName(row.slug, row.name),
+ description: row.meta.description,
+ logo: null,
+ categories: [],
+ actionCount: row.meta.tools_count,
+ classifiedCount: null,
+ permissionCount: null,
+ scopedActionCount: null,
+ connection: { kind: VERDICT[row.slug] ?? "unsupported" },
+ enabled: false,
+ }));
+}
+
+const originalOptions = { ...ComposioRoute.options };
+afterEach(() => {
+ Object.assign(ComposioRoute.options, originalOptions);
+});
+
+function renderDirectory() {
+ const rootRoute = createRootRoute({ component: Outlet });
+ const authedRoute = createRoute({
+ id: "/_authed",
+ getParentRoute: () => rootRoute,
+ component: Outlet,
+ });
+ const adminRoute = createRoute({
+ path: "/admin",
+ getParentRoute: () => authedRoute,
+ component: Outlet,
+ });
+ const pluginsRoute = createRoute({
+ path: "/plugins/",
+ getParentRoute: () => adminRoute,
+ component: () => the plugins index
,
+ });
+ const wired = (
+ ComposioRoute as unknown as {
+ update: (options: unknown) => typeof ComposioRoute;
+ }
+ ).update({
+ id: "/plugins/composio",
+ path: "/plugins/composio",
+ getParentRoute: () => adminRoute,
+ });
+ const tree = rootRoute.addChildren([
+ authedRoute.addChildren([adminRoute.addChildren([pluginsRoute, wired])]),
+ ]);
+ const router = createRouter({
+ routeTree: tree,
+ history: createMemoryHistory({
+ initialEntries: ["/admin/plugins/composio"],
+ }),
+ });
+ return render(
+
+
+ ,
+ );
+}
+
+function stubFetch(apps: ComposioApp[]) {
+ global.fetch = (async (input: RequestInfo | URL) => {
+ const path = String(input).split("?")[0] ?? "";
+ const json = (body: unknown) =>
+ new Response(JSON.stringify(body), {
+ status: 200,
+ headers: { "content-type": "application/json" },
+ });
+ if (path === "/api/plugins/composio/apps") return json({ apps });
+ if (path === "/api/plugins") {
+ return json({
+ catalogue: [],
+ servers: [],
+ skills: [],
+ botsMayCallBack: false,
+ redirectUri: null,
+ composioConfigured: true,
+ provider: "composio",
+ });
+ }
+ return json({ connections: [] });
+ }) as unknown as typeof fetch;
+}
+
+test("a key-only app says it needs a key before Add, and a sign-in app does not", async () => {
+ stubFetch(directory());
+ const view = renderDirectory();
+
+ const onePassword = await view.findByTestId("directory-_1password");
+ expect(onePassword.textContent).toContain("Needs a key from 1Password");
+ // Composio's catalogue spells it "1password"; the row never does.
+ expect(onePassword.textContent).not.toContain("1password");
+ expect((await view.findByTestId("directory-posthog")).textContent).toContain(
+ "Needs a key from PostHog",
+ );
+
+ // HubSpot also takes a key, but it has a sign-in, and that is how it connects.
+ const hubspot = await view.findByTestId("directory-hubspot");
+ expect(hubspot.textContent).not.toContain("Needs a key");
+ expect(
+ (await view.findByTestId("directory-gmail")).textContent,
+ ).not.toContain("Needs a key");
+
+ // The page never names the broker.
+ expect(view.container.textContent ?? "").not.toMatch(/composio/i);
+});
+
+test("apps you sign in to are listed ahead of apps that need a key", async () => {
+ stubFetch(directory());
+ const view = renderDirectory();
+
+ await view.findByTestId("directory-_1password");
+ await waitFor(() => {
+ const order = Array.from(
+ view.container.querySelectorAll("[data-testid^='directory-']"),
+ ).map((row) => row.getAttribute("data-testid"));
+ expect(order).toEqual([
+ "directory-gmail",
+ "directory-hubspot",
+ "directory-_1password",
+ "directory-posthog",
+ ]);
+ });
+});
diff --git a/server/src/plugins/app-names.ts b/server/src/plugins/app-names.ts
new file mode 100644
index 0000000000..c424998347
--- /dev/null
+++ b/server/src/plugins/app-names.ts
@@ -0,0 +1,25 @@
+/**
+ * An app's name as a person reads it, from the name the broker's catalogue publishes.
+ *
+ * The catalogue usually carries the brand's own spelling ("PostHog", "HubSpot"), but a handful of
+ * names arrive all lowercase. On 2026-11-08 there were 12 of 1,723, and "1password" was the one a
+ * person meets first. The directory showed "Needs a key from 1password", and enabling an app
+ * stores its title from this same name, so the tile and the app page would say it too.
+ *
+ * The map holds brands whose spelling is checked against the brand's own site. Past that, an
+ * all-lowercase word gets a capital first letter ("mailfloss" reads "Mailfloss"). A name with a
+ * dot is left alone, because those are domain-style brands written lowercase on purpose
+ * ("incident.io", "urlscan.io"). A name starting with a digit has no first letter to capitalise,
+ * which is why 1Password is in the map.
+ */
+const DISPLAY_NAMES: Record = {
+ _1password: "1Password",
+};
+
+export function displayName(slug: string, published: string): string {
+ if (Object.hasOwn(DISPLAY_NAMES, slug)) return DISPLAY_NAMES[slug] as string;
+ if (published !== published.toLowerCase() || published.includes(".")) {
+ return published;
+ }
+ return published.charAt(0).toUpperCase() + published.slice(1);
+}
diff --git a/server/src/plugins/broker.ts b/server/src/plugins/broker.ts
index 249da7a1ea..dade6e7a53 100644
--- a/server/src/plugins/broker.ts
+++ b/server/src/plugins/broker.ts
@@ -764,6 +764,17 @@ export type ConnectedAppBroker = {
toolkit: string;
authScheme: FieldScheme;
}): Promise;
+ /**
+ * The provider's own page for making the key this app asks for, where the broker publishes one.
+ *
+ * Optional because only a broker with a typed-key flow has anything to say: Arcade has no
+ * per-person fields at all. Null where the broker published no link, which is most apps; the
+ * connect route then falls back to the map in `./key-guides`.
+ */
+ connectionGuideUrl?(request: {
+ toolkit: string;
+ authScheme: FieldScheme;
+ }): Promise;
/**
* Connect this person with the secret they typed, answering the account it made.
*
diff --git a/server/src/plugins/composio-client/fields.ts b/server/src/plugins/composio-client/fields.ts
index ec687c987b..c70b193385 100644
--- a/server/src/plugins/composio-client/fields.ts
+++ b/server/src/plugins/composio-client/fields.ts
@@ -445,6 +445,37 @@ export function fieldMethods(context: ClientContext) {
});
},
+ /**
+ * The provider's page for making this app's key, where Composio publishes one.
+ *
+ * Two places can carry it: `auth_hint_url` on the connection mode the app was added with, and
+ * `auth_guide_url` on the app. Both are empty for nearly every app, so null is the ordinary
+ * answer. Anything that is not text is read as no link: this is a link shown beside a form,
+ * and a shape this file cannot read is no reason to refuse the form a person came for.
+ */
+ async connectionGuideUrl({ toolkit, authScheme }): Promise {
+ const detail = await askVendor(
+ {
+ outcome: `where to get ${toolkit}'s key could not be read`,
+ app: toolkit,
+ },
+ () => vendor.toolkits.retrieve(toolkit),
+ );
+ if (typeof detail !== "object" || detail === null) return null;
+ const modes = Array.isArray(detail.auth_config_details)
+ ? detail.auth_config_details
+ : [];
+ const mode = modes.find(
+ (entry: unknown) =>
+ typeof entry === "object" &&
+ entry !== null &&
+ textOf((entry as { mode?: unknown }).mode) === authScheme,
+ ) as { auth_hint_url?: unknown } | undefined;
+ return (
+ textOf(mode?.auth_hint_url) ?? textOf(detail.auth_guide_url) ?? null
+ );
+ },
+
/**
* One person's account made from what they typed, and the ONE call here that drops its error.
*
@@ -677,5 +708,10 @@ export function fieldMethods(context: ClientContext) {
return { accountId };
},
- } satisfies Pick;
+ } satisfies Required<
+ Pick<
+ ComposioBroker,
+ "connectionFields" | "connectionGuideUrl" | "connectWithFields"
+ >
+ >;
}
diff --git a/server/src/plugins/composio-vendor.ts b/server/src/plugins/composio-vendor.ts
index ae3666189f..395f4da688 100644
--- a/server/src/plugins/composio-vendor.ts
+++ b/server/src/plugins/composio-vendor.ts
@@ -1,4 +1,5 @@
import { itsAppPage } from "../edition";
+import { displayName } from "./app-names";
import {
BrokerAccountGoneError,
BrokerAccountHeldError,
@@ -147,6 +148,11 @@ export type VendorToolkit = {
*/
export type VendorToolkitDetail = {
auth_config_details?: unknown;
+ /**
+ * The app's own page about connecting it, where Composio publishes one. Read by
+ * {@link ComposioBroker.connectionGuideUrl} alone, and only ever as a link to show a person.
+ */
+ auth_guide_url?: unknown;
};
/**
@@ -1094,7 +1100,8 @@ export function appOf(row: VendorToolkit, position: number): BrokerApp {
return {
slug,
- name,
+ // The brand's own spelling where the catalogue lowercased it. See `./app-names`.
+ name: displayName(slug, name),
description,
logo,
categories,
diff --git a/server/src/plugins/key-guides.ts b/server/src/plugins/key-guides.ts
new file mode 100644
index 0000000000..ec6d0e96cd
--- /dev/null
+++ b/server/src/plugins/key-guides.ts
@@ -0,0 +1,297 @@
+import type { BrokerField } from "./broker";
+
+/**
+ * Where a person gets the key an app asks for, and the boxes of its form in words they can follow.
+ *
+ * WHY THIS EXISTS. Most of the broker's catalogue has no sign-in it can send somebody to: of 1,723
+ * apps read on 2026-11-08, 126 had a sign-in the broker runs itself and 1,344 only took a key or a
+ * token typed in. The form for those draws the provider's own field names and descriptions, which
+ * are written for a developer ("Bearer Token", "Service Account Token (ops_* prefix), Connect
+ * Server Access Token, or Events API Bearer Token"), and says nothing about where to go. A person
+ * who expected a sign-in met a form and no way forward.
+ *
+ * THE LINK COMES FROM THE BROKER FIRST. Composio publishes an `auth_hint_url` on a connection mode
+ * and an `auth_guide_url` on the app. Both are empty for nearly every app (the guide on all 1,723;
+ * the hint on all but a few, Braze, Beehiiv and HiBob among them), so the map below is what most
+ * key apps get. Every link in it was opened on 2026-11-08 and lands on the provider's own page for
+ * making the key, or on its documentation for it.
+ *
+ * THE MAP IS THE MOST-USED KEY-ONLY APPS, NOT ALL OF THEM. An app missing from it still gets the
+ * plain explanation and the provider's own field help; it gets no link, because a guessed link is
+ * worse than none. Add an app by its catalogue slug.
+ */
+export type KeyGuide = {
+ /** What to do at the provider, in one or two sentences a person can follow. */
+ steps: string;
+ /** The provider's own page for making the key, or its documentation for it. */
+ url: string;
+ /**
+ * Boxes whose published label or help does not serve a person, rewritten, keyed by the field's
+ * published `name`. The provider's specifics (a prefix, a menu path) are kept in the rewrite.
+ */
+ fields?: Record;
+};
+
+export const KEY_GUIDES: Record = {
+ _1password: {
+ steps:
+ "1Password connects through a Connect server that your 1Password admin runs. They create it in 1Password.com under Developer, along with an access token for it.",
+ url: "https://www.1password.dev/connect/get-started",
+ fields: {
+ full: {
+ label: "Connect server address",
+ help: "The web address of your Connect server, for example https://connect.example.com. Your 1Password admin can tell you what it is.",
+ },
+ generic_api_key: {
+ label: "Access token",
+ help: "The access token made for that Connect server. Your 1Password admin can find it in 1Password.com under Developer, then Connect servers.",
+ },
+ },
+ },
+ openai: {
+ steps: "Create a secret key on the API keys page of your OpenAI account.",
+ url: "https://platform.openai.com/api-keys",
+ },
+ posthog: {
+ steps:
+ "In PostHog, open Settings, then Personal API keys, and create a key. Copy it straight away, because PostHog shows it only once.",
+ url: "https://posthog.com/docs/api",
+ },
+ sendgrid: {
+ steps:
+ "In SendGrid, open Settings, then API Keys, and create a key with full access. Copy it straight away, because SendGrid shows it only once.",
+ url: "https://www.twilio.com/docs/sendgrid/ui/account-and-settings/api-keys",
+ },
+ elevenlabs: {
+ steps:
+ "Create an API key in your ElevenLabs account and copy it straight away, because it's shown only once.",
+ url: "https://elevenlabs.io/app/developers/api-keys",
+ },
+ firecrawl: {
+ steps: "Sign in to Firecrawl and copy your key from the API Keys page.",
+ url: "https://www.firecrawl.dev/app/api-keys",
+ },
+ tavily: {
+ steps: "Sign in to Tavily and copy a key from the API Keys section.",
+ url: "https://app.tavily.com/home",
+ },
+ serpapi: {
+ steps: "Sign in to SerpApi and copy your private API key.",
+ url: "https://serpapi.com/manage-api-key",
+ },
+ perplexityai: {
+ steps:
+ "Create an API key in your Perplexity account and copy it straight away, because it's shown only once.",
+ url: "https://www.perplexity.ai/account/api/keys",
+ },
+ exa: {
+ steps: "Sign in to Exa and create or copy a key on the API keys page.",
+ url: "https://dashboard.exa.ai/api-keys",
+ },
+ cloudflare: {
+ steps:
+ "Copy the Global API Key from your Cloudflare profile, and enter the email address you sign in to Cloudflare with.",
+ url: "https://developers.cloudflare.com/fundamentals/api/get-started/keys/",
+ },
+ pandadoc: {
+ steps: "Create an API key in your PandaDoc account. The link shows how.",
+ url: "https://developers.pandadoc.com/reference/api-key-authentication-process",
+ },
+ mixpanel: {
+ steps:
+ "In Mixpanel, open Organization Settings, then Service Accounts, and create one. Copy its secret straight away, because it's shown only once.",
+ url: "https://developer.mixpanel.com/reference/service-accounts",
+ },
+ algolia: {
+ steps:
+ "Copy your Application ID and an API key from the Algolia dashboard. A search-only key is enough for reading.",
+ url: "https://www.algolia.com/doc/guides/security/api-keys/",
+ },
+ apify: {
+ steps:
+ "In Apify Console, open Settings, then API & Integrations, and copy your API token.",
+ url: "https://console.apify.com/settings/integrations",
+ },
+ apollo: {
+ steps:
+ "In Apollo, open Settings, then Integrations, then API Keys, and create a key. Turn on Set as master key so every action works.",
+ url: "https://docs.apollo.io/docs/create-api-key",
+ },
+ replicate: {
+ steps: "Sign in to Replicate and copy a token from the API tokens page.",
+ url: "https://replicate.com/account/api-tokens",
+ },
+ deepgram: {
+ steps: "Create an API key in the Deepgram console.",
+ url: "https://developers.deepgram.com/docs/create-additional-api-keys",
+ },
+ datadog: {
+ steps:
+ "In Datadog, open Organization Settings. Copy an API key from API Keys and an application key from Application Keys.",
+ url: "https://docs.datadoghq.com/account_management/api-app-keys/",
+ },
+ telegram: {
+ steps:
+ "In Telegram, message @BotFather and send /newbot to make a bot and get its token. This connects a bot, not your own Telegram account.",
+ url: "https://core.telegram.org/bots/tutorial#obtain-your-bot-token",
+ },
+ shortcut: {
+ steps: "Create an API token in your Shortcut settings.",
+ url: "https://developer.shortcut.com/api/rest/v3",
+ },
+ toggl: {
+ steps: "Copy the API token from your Toggl Track profile settings.",
+ url: "https://track.toggl.com/profile",
+ },
+ cloudinary: {
+ steps:
+ "Copy your cloud name, API key and API secret from the Cloudinary console.",
+ url: "https://console.cloudinary.com/settings/api-keys",
+ },
+ postmark: {
+ steps:
+ "In Postmark, open your server, then the API Tokens tab, and copy the Server API token.",
+ url: "https://postmarkapp.com/support/article/1008-what-are-the-account-and-server-api-tokens",
+ },
+ amplitude: {
+ steps: "Copy your project's API key and secret key from Amplitude.",
+ url: "https://amplitude.com/docs/apis/authentication",
+ },
+ segment: {
+ steps: "Create a Public API token in your Segment workspace.",
+ url: "https://twilio.com/docs/segment/api/public-api",
+ },
+ close: {
+ steps:
+ "In Close, open Settings, then Developer, then API Keys, and create a key. Copy it straight away, because it's shown in full only once.",
+ url: "https://help.close.com/integrations/api-keys-oauth",
+ },
+ ashby: {
+ steps:
+ "An Ashby admin creates the key under Admin, then Integrations, then API Keys. Copy it straight away, because it's shown only once.",
+ url: "https://developers.ashbyhq.com/docs/authentication",
+ },
+ assemblyai: {
+ steps:
+ "In the AssemblyAI dashboard, open Workspace, then Manage, then API Keys, and copy a key for the project you want to use.",
+ url: "https://www.assemblyai.com/app/api-keys",
+ },
+ freshdesk: {
+ steps:
+ "In Freshdesk, open your profile picture, then Profile Settings, and select View API key.",
+ url: "https://support.freshdesk.com/support/solutions/articles/215517-how-to-find-your-api-key",
+ },
+ heygen: {
+ steps: "In HeyGen, open Settings, then API, and copy your key.",
+ url: "https://app.heygen.com/settings?nav=API",
+ },
+ mem0: {
+ steps: "Copy an API key from your Mem0 settings.",
+ url: "https://app.mem0.ai/dashboard/api-keys",
+ },
+ browserbase_tool: {
+ steps: "Sign in to Browserbase and copy your API key from Settings.",
+ url: "https://www.browserbase.com/settings",
+ },
+};
+
+/**
+ * Labels the catalogue publishes again and again, in the words the rest of OpenBot uses.
+ *
+ * Only the generic ones. A label naming the app ("Postmark Server API Token") already says what
+ * it is and is left alone.
+ */
+const PLAIN_LABELS: Record = {
+ "API Key": "API key",
+ "Api Key": "API key",
+ "API Token": "API token",
+ "API Secret": "API secret",
+ "Bearer Token": "Access token",
+ "Base URL": "Web address",
+ "API URL": "API web address",
+ "Sub Domain": "Subdomain",
+ Username: "Username",
+ Password: "Password",
+};
+
+/**
+ * The broker's own name, which a person never reads: they see OpenBot. A provider's help sentence
+ * sometimes names it ("lets Composio access your campaigns"), and that is the sentence's one word
+ * about who is asking, so it becomes OpenBot.
+ */
+const BROKER_NAME = /\bcomposio\b/gi;
+
+function forPerson(text: string): string {
+ return text.replace(BROKER_NAME, "OpenBot");
+}
+
+/** The form's boxes, each labelled and explained for a person. Names and order are untouched. */
+export function fieldsForPerson(
+ toolkit: string,
+ fields: BrokerField[],
+): BrokerField[] {
+ const rewrites = Object.hasOwn(KEY_GUIDES, toolkit)
+ ? (KEY_GUIDES[toolkit]?.fields ?? {})
+ : {};
+ return fields.map((field) => {
+ const rewrite = Object.hasOwn(rewrites, field.name)
+ ? rewrites[field.name]
+ : undefined;
+ const label =
+ rewrite?.label ??
+ (Object.hasOwn(PLAIN_LABELS, field.label)
+ ? PLAIN_LABELS[field.label]
+ : undefined) ??
+ field.label;
+ return {
+ ...field,
+ label: forPerson(label),
+ help: forPerson(rewrite?.help ?? field.help),
+ };
+ });
+}
+
+/** What the connect dialog says above the form: what to do, and the page to do it on. */
+export type ConnectionGuide = {
+ steps: string;
+ url: string | null;
+};
+
+/**
+ * A link the broker published is used only when it is a provider's page: https, and not the
+ * broker's own site, which would put the broker's name in front of a person and is not where a key
+ * is made.
+ */
+function providerLink(url: string | null | undefined): string | null {
+ if (!url) return null;
+ let parsed: URL;
+ try {
+ parsed = new URL(url);
+ } catch {
+ return null;
+ }
+ if (parsed.protocol !== "https:") return null;
+ if (/(^|\.)composio\.dev$/i.test(parsed.hostname)) return null;
+ return parsed.toString();
+}
+
+/**
+ * The guide for one app: the broker's own link where it published one, else the map's, and the
+ * map's steps where the app is in it.
+ */
+export function connectionGuide(input: {
+ toolkit: string;
+ title: string;
+ /** The link the broker published for this app's key, if any. */
+ published: string | null;
+}): ConnectionGuide {
+ const mapped = Object.hasOwn(KEY_GUIDES, input.toolkit)
+ ? KEY_GUIDES[input.toolkit]
+ : undefined;
+ return {
+ steps:
+ mapped?.steps ??
+ `Sign in to ${input.title} and look for API keys in its settings or developer pages.`,
+ url: providerLink(input.published) ?? mapped?.url ?? null,
+ };
+}
diff --git a/server/src/plugins/route-groups/connections.ts b/server/src/plugins/route-groups/connections.ts
index 80444b8c7a..859b496cdb 100644
--- a/server/src/plugins/route-groups/connections.ts
+++ b/server/src/plugins/route-groups/connections.ts
@@ -21,6 +21,7 @@ import {
sharedAccountOnly,
sharedAccountOnlySentence,
} from "../deployment-clients";
+import { connectionGuide, fieldsForPerson } from "../key-guides";
import {
authorizationUrlFor,
type ConnectOrigin,
@@ -408,7 +409,31 @@ export function mountConnectionRoutes({
return context.json({ error: refusal.error }, refusal.status);
}
- if (submitted === null) return context.json({ fields: published });
+ /*
+ * THE FIRST PRESS ANSWERS THE FORM A PERSON CAN FOLLOW: the same boxes under the same
+ * names, labelled and explained in their words, and a guide saying where the key comes
+ * from. The names are untouched, so the second press is checked against `published`
+ * exactly as before. The broker's own link is asked for and a failure there costs only the
+ * link: the form is what the person came for, and `./key-guides` has the fallback.
+ */
+ if (submitted === null) {
+ const guideUrl = composio.broker.connectionGuideUrl?.bind(
+ composio.broker,
+ );
+ const link = guideUrl
+ ? await withVendorDeadline(() =>
+ guideUrl({ toolkit, authScheme }),
+ ).catch(() => null)
+ : null;
+ return context.json({
+ fields: fieldsForPerson(toolkit, published),
+ guide: connectionGuide({
+ toolkit,
+ title: row.title,
+ published: link,
+ }),
+ });
+ }
/*
* AN APP WITH NO BOXES CANNOT BE CONNECTED BY SOMEBODY TYPING, WHICH IS THE ONLY WAY THIS
diff --git a/server/tests/fixtures/composio-key-apps.json b/server/tests/fixtures/composio-key-apps.json
new file mode 100644
index 0000000000..25141c2ebf
--- /dev/null
+++ b/server/tests/fixtures/composio-key-apps.json
@@ -0,0 +1,229 @@
+{
+ "_captured": "GET https://backend.composio.dev/api/v3/toolkits (catalogue rows) and /api/v3/toolkits/{slug} (details), read-only, 2026-11-08. Verbatim, trimmed to the keys the server reads.",
+ "catalogue": [
+ {
+ "name": "HubSpot",
+ "slug": "hubspot",
+ "type": "native",
+ "app": "HubSpot",
+ "toolkit_type": "api",
+ "auth_schemes": ["OAUTH2", "API_KEY"],
+ "composio_managed_auth_schemes": ["OAUTH2"],
+ "is_local_toolkit": false,
+ "meta": {
+ "triggers_count": 2,
+ "tools_count": 343,
+ "description": "HubSpot is an inbound marketing, sales, and customer service platform integrating CRM, email automation, and analytics to facilitate lead nurturing and seamless customer experiences",
+ "logo": "https://logos.composio.dev/api/hubspot",
+ "app_url": "https://www.hubspot.com/",
+ "categories": [
+ {
+ "id": "crm",
+ "name": "crm"
+ },
+ {
+ "id": "marketing-automation",
+ "name": "marketing automation"
+ }
+ ],
+ "created_at": "2024-05-03T11:44:32.061Z",
+ "updated_at": "2026-11-08T11:16:45.396Z",
+ "version": "20261104_00"
+ },
+ "no_auth": false,
+ "auth_guide_url": null,
+ "deprecated": {
+ "toolkitId": "635cc036-aba3-48ad-a6ab-1abd84f8817b"
+ }
+ },
+ {
+ "name": "Gmail",
+ "slug": "gmail",
+ "type": "native",
+ "app": "Gmail",
+ "toolkit_type": "api",
+ "auth_schemes": ["OAUTH2"],
+ "composio_managed_auth_schemes": ["OAUTH2"],
+ "is_local_toolkit": false,
+ "meta": {
+ "triggers_count": 2,
+ "tools_count": 61,
+ "description": "Gmail is Google\u2019s email service, featuring spam protection, search functions, and seamless integration with other G Suite apps for productivity",
+ "logo": "https://logos.composio.dev/api/gmail",
+ "app_url": "https://mail.google.com",
+ "categories": [
+ {
+ "id": "email",
+ "name": "email"
+ }
+ ],
+ "created_at": "2024-05-03T11:44:32.061Z",
+ "updated_at": "2026-11-08T00:36:45.480Z",
+ "version": "20261105_00"
+ },
+ "no_auth": false,
+ "auth_guide_url": null,
+ "deprecated": {
+ "toolkitId": "a90e7d79-4f7a-4ff2-bd7d-19c78640b8f8"
+ }
+ },
+ {
+ "name": "1password",
+ "slug": "_1password",
+ "type": "native",
+ "app": "1password",
+ "toolkit_type": "api",
+ "auth_schemes": ["API_KEY"],
+ "composio_managed_auth_schemes": [],
+ "is_local_toolkit": false,
+ "meta": {
+ "triggers_count": 0,
+ "tools_count": 6,
+ "description": "Password manager and digital vault for secure credential storage and team collaboration",
+ "logo": "https://logos.composio.dev/api/1password",
+ "app_url": "https://1password.com",
+ "categories": [
+ {
+ "id": "security-&-identity-tools",
+ "name": "security & identity tools"
+ },
+ {
+ "id": "developer-tools",
+ "name": "developer tools"
+ }
+ ],
+ "created_at": "2026-03-13T21:40:05.341Z",
+ "updated_at": "2026-11-08T11:16:45.396Z",
+ "version": "20261101_00"
+ },
+ "no_auth": false,
+ "auth_guide_url": null,
+ "deprecated": {
+ "toolkitId": "344237d4-2a7d-47ef-9dbb-c490cf0e546f"
+ }
+ },
+ {
+ "name": "PostHog",
+ "slug": "posthog",
+ "type": "native",
+ "app": "PostHog",
+ "toolkit_type": "api",
+ "auth_schemes": ["API_KEY"],
+ "composio_managed_auth_schemes": [],
+ "is_local_toolkit": false,
+ "meta": {
+ "triggers_count": 11,
+ "tools_count": 686,
+ "description": "PostHog is an open-source product analytics platform tracking user interactions and behaviors to help teams refine features, improve funnels, and reduce churn",
+ "logo": "https://logos.composio.dev/api/posthog",
+ "app_url": "https://posthog.com/",
+ "categories": [
+ {
+ "id": "analytics",
+ "name": "analytics"
+ }
+ ],
+ "created_at": "2024-08-13T09:10:05.121Z",
+ "updated_at": "2026-11-08T11:16:45.396Z",
+ "version": "20261101_00"
+ },
+ "no_auth": false,
+ "auth_guide_url": null,
+ "deprecated": {
+ "toolkitId": "8153cee4-4659-48ec-9bd2-27b36603e42b"
+ }
+ }
+ ],
+ "details": {
+ "_1password": {
+ "name": "1password",
+ "slug": "_1password",
+ "composio_managed_auth_schemes": [],
+ "auth_config_details": [
+ {
+ "name": "onepassword_api_key",
+ "mode": "API_KEY",
+ "required_scopes": [],
+ "fields": {
+ "auth_config_creation": {
+ "required": [],
+ "optional": []
+ },
+ "connected_account_initiation": {
+ "required": [
+ {
+ "name": "full",
+ "displayName": "Base URL",
+ "type": "string",
+ "description": "For Connect Server: your self-hosted server URL (e.g., https://connect.example.com). For Events API: https://events.1password.com, https://events.ent.1password.com, https://events.1password.ca, or https://events.1password.eu based on your account server.",
+ "required": true,
+ "is_secret": false,
+ "user_visible": true
+ },
+ {
+ "name": "generic_api_key",
+ "displayName": "Bearer Token",
+ "type": "string",
+ "description": "Service Account Token (ops_* prefix), Connect Server Access Token, or Events API Bearer Token. Get it from your 1Password account settings.",
+ "required": true,
+ "is_secret": true,
+ "legacy_template_name": "api_key",
+ "user_visible": true
+ }
+ ],
+ "optional": []
+ }
+ },
+ "auth_hint_url": null
+ }
+ ],
+ "auth_guide_url": null
+ },
+ "braze": {
+ "name": "Braze",
+ "slug": "braze",
+ "composio_managed_auth_schemes": [],
+ "auth_config_details": [
+ {
+ "name": "braze_api_key",
+ "mode": "API_KEY",
+ "required_scopes": [],
+ "fields": {
+ "auth_config_creation": {
+ "required": [],
+ "optional": []
+ },
+ "connected_account_initiation": {
+ "required": [
+ {
+ "name": "full",
+ "displayName": "REST Endpoint",
+ "type": "string",
+ "description": "Copy the exact REST endpoint shown for your workspace in Braze under Settings > APIs and Identifiers > API Keys, for example https://rest.iad-01.braze.com or https://rest.fra-01.braze.eu. Use the REST endpoint, not an SDK endpoint, and omit a trailing slash.",
+ "required": true,
+ "is_secret": false,
+ "user_visible": true
+ },
+ {
+ "name": "generic_api_key",
+ "displayName": "REST API Key",
+ "type": "string",
+ "description": "Create or copy a server-side REST API key under Settings > APIs and Identifiers > API Keys in your Braze workspace. Use a separate Braze REST API key for each connected integration. The key must include every endpoint permission needed by the tools you use and the campaigns.list permission used to validate the connection. Braze does not allow a key's permissions or IP allowlist to be changed after creation, so create a replacement key if either must change.",
+ "required": true,
+ "is_secret": true,
+ "user_visible": true
+ }
+ ],
+ "optional": []
+ }
+ },
+ "proxy": {
+ "base_url": "{{full}}"
+ },
+ "auth_hint_url": "https://www.braze.com/docs/api/basics#creating-a-rest-api-key"
+ }
+ ],
+ "auth_guide_url": null
+ }
+ }
+}
diff --git a/server/tests/key-only-apps.test.ts b/server/tests/key-only-apps.test.ts
new file mode 100644
index 0000000000..ba640592e0
--- /dev/null
+++ b/server/tests/key-only-apps.test.ts
@@ -0,0 +1,226 @@
+import { describe, expect, test } from "bun:test";
+import { buildComposioClient } from "../src/plugins/composio-adapter";
+import { displayName } from "../src/plugins/app-names";
+import { appOf, connectionOf } from "../src/plugins/composio-vendor";
+import {
+ connectionGuide,
+ fieldsForPerson,
+ KEY_GUIDES,
+} from "../src/plugins/key-guides";
+import captured from "./fixtures/composio-key-apps.json";
+
+/**
+ * Connecting an app that only takes a key, as a person meets it.
+ *
+ * Reported from OpenBot Desktop 0.3.3: pressing Connect on 1Password opened a form asking for a
+ * "Base URL" and a "Bearer Token", worded for a developer, with nowhere to go to get either. The
+ * person expected a sign-in. Composio has none for 1Password (it publishes `API_KEY` and nothing it
+ * holds a client for), so the form is right; what was wrong is that nothing said so, or where the
+ * key comes from.
+ *
+ * Every row and detail here is Composio's own answer, captured read-only on 2026-11-08 into
+ * `fixtures/composio-key-apps.json`.
+ */
+
+type Row = Parameters[0];
+const row = (slug: string): Row => {
+ const found = captured.catalogue.find((entry) => entry.slug === slug);
+ if (!found) throw new Error(`${slug} is not in the captured catalogue`);
+ return found as Row;
+};
+
+/** A vendor whose only answer is the captured detail; anything else names itself and fails. */
+function vendorAnswering(details: Record) {
+ const refuse = (what: string) => async () => {
+ throw new Error(`${what} should not have been called in this test.`);
+ };
+ return {
+ tools: {
+ list: refuse("tools.list"),
+ getRawComposioToolBySlug: refuse("tools.getRawComposioToolBySlug"),
+ execute: refuse("tools.execute"),
+ },
+ toolkits: {
+ list: refuse("toolkits.list"),
+ retrieve: async (slug: string) => {
+ if (!(slug in details))
+ throw new Error(`no captured detail for ${slug}`);
+ return details[slug];
+ },
+ },
+ authConfigs: {
+ list: refuse("authConfigs.list"),
+ create: refuse("authConfigs.create"),
+ delete: refuse("authConfigs.delete"),
+ },
+ connectedAccounts: {
+ list: refuse("connectedAccounts.list"),
+ link: refuse("connectedAccounts.link"),
+ create: refuse("connectedAccounts.create"),
+ delete: refuse("connectedAccounts.delete"),
+ },
+ };
+}
+
+describe("an app with a sign-in never asks for a key", () => {
+ /*
+ * HubSpot publishes OAUTH2 and API_KEY, and Composio holds an OAuth client for it. The sign-in
+ * wins, so the connect flow is a link to HubSpot's own consent page and no form is ever drawn.
+ */
+ test("HubSpot, which also takes a key, connects by signing in", () => {
+ expect(row("hubspot").auth_schemes).toEqual(["OAUTH2", "API_KEY"]);
+ expect(connectionOf(row("hubspot"))).toEqual({ kind: "consent" });
+ expect(connectionOf(row("gmail"))).toEqual({ kind: "consent" });
+ });
+
+ test("1Password, which has no sign-in, is the key form", () => {
+ expect(connectionOf(row("_1password"))).toEqual({
+ kind: "fields",
+ authScheme: "API_KEY",
+ });
+ });
+});
+
+describe("a key-only app's form says where the key comes from", () => {
+ const { broker } = buildComposioClient(
+ vendorAnswering(captured.details) as never,
+ );
+
+ test("the 1Password form is relabelled for a person, under the names the provider reads", async () => {
+ const published = await broker.connectionFields({
+ toolkit: "_1password",
+ authScheme: "API_KEY",
+ });
+ // What main drew, verbatim from the provider.
+ expect(published.map((field) => field.label)).toEqual([
+ "Base URL",
+ "Bearer Token",
+ ]);
+
+ const shown = fieldsForPerson("_1password", published);
+ expect(shown.map((field) => field.name)).toEqual([
+ "full",
+ "generic_api_key",
+ ]);
+ expect(shown.map((field) => field.label)).toEqual([
+ "Connect server address",
+ "Access token",
+ ]);
+ expect(shown[0]?.help).toContain("https://connect.example.com");
+ expect(shown[1]?.secret).toBe(true);
+ });
+
+ test("the guide for 1Password links to 1Password's own page for making the token", async () => {
+ const published = await broker.connectionGuideUrl?.({
+ toolkit: "_1password",
+ authScheme: "API_KEY",
+ });
+ // Composio publishes no link for 1Password, so the maintained map answers.
+ expect(published).toBeNull();
+ const guide = connectionGuide({
+ toolkit: "_1password",
+ title: "1Password",
+ published: published ?? null,
+ });
+ expect(guide.url).toBe("https://www.1password.dev/connect/get-started");
+ expect(guide.steps).toContain("Connect server");
+ });
+
+ test("a link Composio publishes is used ahead of the map", async () => {
+ const published = await broker.connectionGuideUrl?.({
+ toolkit: "braze",
+ authScheme: "API_KEY",
+ });
+ expect(published).toBe(
+ "https://www.braze.com/docs/api/basics#creating-a-rest-api-key",
+ );
+ expect(
+ connectionGuide({
+ toolkit: "braze",
+ title: "Braze",
+ published: published ?? null,
+ }).url,
+ ).toBe("https://www.braze.com/docs/api/basics#creating-a-rest-api-key");
+ });
+
+ test("an app missing from the map still gets steps, and no guessed link", () => {
+ const guide = connectionGuide({
+ toolkit: "0codekit",
+ title: "0CodeKit",
+ published: null,
+ });
+ expect(guide.url).toBeNull();
+ expect(guide.steps).toContain("0CodeKit");
+ });
+
+ test("a link to the broker's own site is never shown", () => {
+ expect(
+ connectionGuide({
+ toolkit: "0codekit",
+ title: "0CodeKit",
+ published: "https://composio.dev/toolkits/0codekit",
+ }).url,
+ ).toBeNull();
+ });
+});
+
+describe("the broker's name never reaches the form", () => {
+ /*
+ * Instantly's own help sentence, verbatim from Composio's catalogue on 2026-11-08, names the
+ * broker to the person filling the box in.
+ */
+ test("a provider's help sentence naming it says OpenBot instead", () => {
+ const [field] = fieldsForPerson("instantly", [
+ {
+ name: "generic_api_key",
+ label: "Instantly API Key",
+ help: "Your Instantly API key — lets Composio access your campaigns and leads. Create one under Settings → Integrations → API Keys; must be a v2 key (v1 keys no longer work).",
+ required: true,
+ secret: true,
+ },
+ ]);
+ expect(field?.help).not.toMatch(/composio/i);
+ expect(field?.help).toContain("lets OpenBot access your campaigns");
+ });
+
+ test("no guide in the map names it or links to it", () => {
+ for (const [slug, guide] of Object.entries(KEY_GUIDES)) {
+ const text = [
+ guide.steps,
+ guide.url,
+ ...Object.values(guide.fields ?? {}).flatMap((field) => [
+ field.label ?? "",
+ field.help ?? "",
+ ]),
+ ].join(" ");
+ expect(`${slug}: ${text}`).not.toMatch(/composio/i);
+ expect(new URL(guide.url).protocol).toBe("https:");
+ }
+ });
+});
+
+describe("an app is named as its brand spells it", () => {
+ /*
+ * Composio's catalogue names 1Password "1password", and the directory row said "Needs a key
+ * from 1password". Enabling the app stores its title from the same name, so the tile and the
+ * app page would have said it too.
+ */
+ test("the directory names 1Password with its capitals, and leaves PostHog alone", () => {
+ expect(row("_1password").name).toBe("1password");
+ expect(appOf(row("_1password"), 0).name).toBe("1Password");
+ expect(appOf(row("posthog"), 0).name).toBe("PostHog");
+ expect(appOf(row("hubspot"), 0).name).toBe("HubSpot");
+ });
+
+ /*
+ * The other lowercase names in the catalogue on 2026-11-08: a plain word gets a capital, and a
+ * domain-style brand written lowercase on purpose is left as it is.
+ */
+ test("a lowercase word gets a capital; a domain-style name is left as written", () => {
+ expect(displayName("mailfloss", "mailfloss")).toBe("Mailfloss");
+ expect(displayName("bexio", "bexio")).toBe("Bexio");
+ expect(displayName("incident_io", "incident.io")).toBe("incident.io");
+ expect(displayName("urlscanio", "urlscan.io")).toBe("urlscan.io");
+ expect(displayName("7shifts", "7shifts")).toBe("7shifts");
+ });
+});
diff --git a/server/tests/plugin-routes.test.ts b/server/tests/plugin-routes.test.ts
index fd21d5154b..747dc4ae74 100644
--- a/server/tests/plugin-routes.test.ts
+++ b/server/tests/plugin-routes.test.ts
@@ -2479,9 +2479,18 @@ describe("connecting an app whose secret a person types", () => {
const response = await connectFields();
expect(response.status).toBe(200);
- // The vendor's own list, passed through rather than restated: `help` is written for the person
- // filling the box in, and nothing here is in a position to improve on it.
- expect(await response.json()).toEqual({ fields: PUBLISHED });
+ /*
+ * The vendor's own list under the vendor's own names, with the one generic label ("Base URL")
+ * said in the person's words, and a guide saying where the key comes from. Firecrawl is in
+ * the maintained map, so the link is Firecrawl's own API keys page.
+ */
+ expect(await response.json()).toEqual({
+ fields: [PUBLISHED[0], { ...PUBLISHED[1], label: "Web address" }],
+ guide: {
+ steps: "Sign in to Firecrawl and copy your key from the API Keys page.",
+ url: "https://www.firecrawl.dev/app/api-keys",
+ },
+ });
/*
* ASKED WITH THE SCHEME RECORDED ON THE ROW, never one derived again here. A form drawn for
* `BASIC` in front of a config created for `API_KEY` asks for boxes the person's app does not
@@ -2648,7 +2657,7 @@ describe("connecting an app whose secret a person types", () => {
published: [],
});
- expect(await (await connectFields()).json()).toEqual({ fields: [] });
+ expect((await (await connectFields()).json()).fields).toEqual([]);
const response = await connectFields({ values: {} });
From 2692433a87fd91788dec374b7780c1c4a4eac79f Mon Sep 17 00:00:00 2001
From: David McKay
Date: Sun, 8 Nov 2026 19:29:47 -0800
Subject: [PATCH 07/34] Let a person connect several accounts of one app, and
let their Bots use each (#1298)
---
app/src/components/approvals/rules.tsx | 2 +
.../plugins/brokered-account-row.tsx | 269 +-
app/src/lib/approvals.ts | 8 +-
app/src/lib/plugins/mutations.ts | 96 +-
app/src/lib/plugins/queries.ts | 41 +
app/tests/brokered-account-row.test.tsx | 118 +-
server/drizzle/0084_brokered_accounts.sql | 17 +
server/drizzle/0085_approval_rule_account.sql | 1 +
server/drizzle/meta/0084_snapshot.json | 11791 +++++++++++++++
server/drizzle/meta/0085_snapshot.json | 11797 ++++++++++++++++
server/drizzle/meta/_journal.json | 14 +
server/src/approvals/policy.ts | 30 +-
server/src/approvals/service.ts | 4 +
server/src/approvals/store.ts | 6 +
server/src/approvals/types.ts | 10 +
server/src/db/schema/approvals.ts | 7 +
server/src/db/schema/plugins.ts | 61 +
server/src/plugins/account-argument.ts | 70 +
server/src/plugins/arcade-adapter.ts | 33 +
server/src/plugins/broker.ts | 34 +
.../src/plugins/composio-client/accounts.ts | 62 +-
server/src/plugins/composio-client/consent.ts | 28 +-
server/src/plugins/composio-client/model.ts | 2 +-
server/src/plugins/plugin-store/accounts.ts | 482 +
server/src/plugins/plugin-store/calls.ts | 158 +-
.../src/plugins/plugin-store/connections.ts | 94 +-
server/src/plugins/plugin-store/model.ts | 2 +
.../src/plugins/route-groups/connections.ts | 216 +-
server/src/plugins/shared-accounts.ts | 35 +
server/src/plugins/store.ts | 3 +
server/src/plugins/tools.ts | 47 +-
server/src/plugins/transport.ts | 7 +
.../approvals-account.integration.test.ts | 169 +
...cade-multiple-accounts.integration.test.ts | 223 +
...ered-multiple-accounts.integration.test.ts | 657 +
server/tests/plugin-routes.test.ts | 55 +-
server/tests/plugin-store.integration.test.ts | 117 +
shared/tool-name.ts | 8 +-
38 files changed, 26626 insertions(+), 148 deletions(-)
create mode 100644 server/drizzle/0084_brokered_accounts.sql
create mode 100644 server/drizzle/0085_approval_rule_account.sql
create mode 100644 server/drizzle/meta/0084_snapshot.json
create mode 100644 server/drizzle/meta/0085_snapshot.json
create mode 100644 server/src/plugins/account-argument.ts
create mode 100644 server/src/plugins/plugin-store/accounts.ts
create mode 100644 server/tests/approvals-account.integration.test.ts
create mode 100644 server/tests/arcade-multiple-accounts.integration.test.ts
create mode 100644 server/tests/brokered-multiple-accounts.integration.test.ts
diff --git a/app/src/components/approvals/rules.tsx b/app/src/components/approvals/rules.tsx
index a76c35a4c3..4d1345cfe7 100644
--- a/app/src/components/approvals/rules.tsx
+++ b/app/src/components/approvals/rules.tsx
@@ -174,6 +174,8 @@ export function RuleRow({
: folderRule
? folderWords(rule.scope, folders.data?.grants)
: `on ${rule.scope}`,
+ // A rule saved for one of several accounts at the app covers that account only.
+ rule.account ? `from ${rule.account}` : null,
rule.botId === "*"
? null
: rule.botId.includes("*")
diff --git a/app/src/components/plugins/brokered-account-row.tsx b/app/src/components/plugins/brokered-account-row.tsx
index 54fa342b12..55a2c76236 100644
--- a/app/src/components/plugins/brokered-account-row.tsx
+++ b/app/src/components/plugins/brokered-account-row.tsx
@@ -1,5 +1,5 @@
-import { IconArrowUpRight } from "@tabler/icons-react";
-import { useMutation, useQueryClient } from "@tanstack/react-query";
+import { IconArrowUpRight, IconUser } from "@tabler/icons-react";
+import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useEffect, useState } from "react";
import { ConnectionFields } from "@/components/plugins/connection-fields";
import { Button } from "@/components/ui/button";
@@ -15,21 +15,27 @@ import {
ItemActions,
ItemContent,
ItemDescription,
+ ItemMedia,
ItemTitle,
} from "@/components/ui/item";
+import { Separator } from "@/components/ui/separator";
import {
type BrokerField,
brokeredConnectionFieldsMutationOptions,
confirmBrokeredConnectionMutationOptions,
connectAccountMutationOptions,
+ connectAnotherAccountMutationOptions,
connectBrokeredWithFieldsMutationOptions,
+ disconnectBrokeredAccountMutationOptions,
disconnectBrokeredMutationOptions,
invalidatePlugins,
type KeyForm,
type KeyGuide,
recheckBrokeredConnectionMutationOptions,
+ setDefaultBrokeredAccountMutationOptions,
} from "@/lib/plugins/mutations";
import { schemeNeedsKey } from "@/lib/plugins/enablement-notes";
+import { brokeredAccountsQueryOptions } from "@/lib/plugins/queries";
import {
type BrokeredAccountKind,
brokeredKeyMissing,
@@ -177,8 +183,8 @@ export type BrokeredAccount = {
* TRUE WHERE THE BROKER AUTHORIZES PER ACTION, which is Arcade: a consent there adds what it asks
* for to the one connection, and the connect route lets it through over a standing row. A Bot's
* card sends a person here to reconnect on both brokers, and on Arcade this is where it finishes,
- * with no Disconnect first. FALSE ON COMPOSIO, whose second consent would attach a second account
- * and is refused, so reconnecting there is Disconnect and then Connect.
+ * with no Disconnect first. FALSE ON COMPOSIO, where a second consent adds a second account, which
+ * is what Connect another account asks for. Reconnecting there is Disconnect and then Connect.
*/
reconnectsInPlace: boolean;
/** See {@link BrokeredAccountKind}. Derived here so no branch below re-asks. */
@@ -247,6 +253,19 @@ export type BrokeredAccount = {
/** Spend one read-only call at the vendor to find out whether the key still works. */
recheck: () => void;
rechecking: boolean;
+ /** The app this row is about, for the account list beneath it. */
+ serverId: string;
+ /** Whether the last key handed over was taken, which closes the form it was typed into. */
+ fieldsSubmitted: boolean;
+ /** Leave for the app's sign-in page to add one more account beside the ones connected. */
+ connectAnother: () => void;
+ connectingAnother: boolean;
+ /** Make one of the person's accounts the one their Bots use when they name none. */
+ makeDefault: (accountId: string) => void;
+ /** Disconnect one of the person's accounts and keep the rest. */
+ disconnectAccount: (accountId: string) => void;
+ /** Which account a press is in flight for, so only its buttons wait. */
+ changingAccount: string | null;
};
export function useBrokeredAccount(input: {
@@ -576,6 +595,42 @@ export function useBrokeredAccount(input: {
* at all, null included. Its null IS the new record. The one path where it writes a name and then
* raises never reaches here — a refusal clears both answers — so what is left is authoritative.
*/
+ /*
+ * ONE MORE ACCOUNT, ONE PERSON'S ACCOUNT MADE THE DEFAULT, ONE ENDED. Each refetches the plugin
+ * queries on its own `onSettled`, which is where the account list beneath the row is read from.
+ */
+ const connectAnother = useMutation({
+ ...connectAnotherAccountMutationOptions(returnTo),
+ onError: (thrown: Error) => report(thrown.message),
+ onSuccess: (start) => {
+ forgetConfirmation();
+ forgetRecheck();
+ if (start !== null && "connected" in start) {
+ void invalidatePlugins(queryClient);
+ return;
+ }
+ if (start === null) {
+ report(
+ "This app connects with a key, not a sign-in page, so nothing was connected. Reload the page and press Connect again.",
+ );
+ return;
+ }
+ window.location.href = start.authorizationUrl;
+ },
+ });
+ const makeDefault = useMutation({
+ ...setDefaultBrokeredAccountMutationOptions(queryClient),
+ onError: (thrown: Error) => report(thrown.message),
+ });
+ const disconnectAccount = useMutation({
+ ...disconnectBrokeredAccountMutationOptions(queryClient),
+ onError: (thrown: Error) => {
+ report(thrown.message);
+ forgetAnswers();
+ },
+ onSuccess: forgetAnswers,
+ });
+
const answered =
recheck.data && recheck.data.probe !== null
? recheck.data
@@ -686,6 +741,26 @@ export function useBrokeredAccount(input: {
},
submittingFields: submission.isPending,
submissionError: submission.error?.message ?? null,
+ serverId,
+ fieldsSubmitted: submission.isSuccess,
+ connectAnother: () => {
+ report(null);
+ connectAnother.mutate(serverId);
+ },
+ connectingAnother: connectAnother.isPending,
+ makeDefault: (accountId: string) => {
+ report(null);
+ makeDefault.mutate({ serverId, accountId });
+ },
+ disconnectAccount: (accountId: string) => {
+ report(null);
+ disconnectAccount.mutate({ serverId, accountId });
+ },
+ changingAccount: makeDefault.isPending
+ ? (makeDefault.variables?.accountId ?? null)
+ : disconnectAccount.isPending
+ ? (disconnectAccount.variables?.accountId ?? null)
+ : null,
};
}
@@ -723,6 +798,8 @@ function accountSentence(input: {
edition: Edition;
/** Who reads the row: see {@link brokeredKeyMissing}. */
reader: "admin" | "person";
+ /** The one connected account's name, where the app reported one. */
+ identity?: string | null;
}): string {
const {
account,
@@ -730,6 +807,7 @@ function accountSentence(input: {
edition,
disconnectedDescription,
disconnectedReassurance,
+ identity,
provider,
reader,
title,
@@ -841,7 +919,7 @@ function accountSentence(input: {
* mail is an argument, not a branch — see this file's opening comment.
*/
return account.connected
- ? `Signed in with ${title}. ${connectedDescription}`
+ ? `Signed in with ${title}${identity ? ` as ${identity}` : ""}. ${connectedDescription}`
: disconnectedDescription;
}
@@ -974,6 +1052,41 @@ export function BrokeredAccountRow({
useEffect(() => {
if (account.connected) setAsking(false);
}, [account.connected]);
+ /*
+ * And a key handed over for ANOTHER account closes it too: the row was connected before the form
+ * opened, so the effect above has nothing to see change.
+ */
+ useEffect(() => {
+ if (account.fieldsSubmitted) setAsking(false);
+ }, [account.fieldsSubmitted]);
+
+ /*
+ * THE PERSON'S ACCOUNTS AT THE APP, BY NAME, once the row is connected. While the list is loading
+ * or could not be read the row reads as it always did: one connection, with its own Disconnect.
+ */
+ const listing = useQuery({
+ ...brokeredAccountsQueryOptions(account.serverId),
+ enabled:
+ account.configured && account.connected && account.kind !== "no-auth",
+ });
+ const accounts =
+ account.connected && !listing.isPending && !listing.error && listing.data
+ ? listing.data.accounts
+ : [];
+ const canAddAnother =
+ account.connected &&
+ !listing.isPending &&
+ !listing.error &&
+ listing.data?.canAddAnother === true;
+ const several = accounts.length > 1;
+ const addAnother = () => {
+ if (account.kind === "fields") {
+ setAsking(true);
+ account.requestFields();
+ return;
+ }
+ account.connectAnother();
+ };
return (
<>
@@ -981,22 +1094,29 @@ export function BrokeredAccountRow({
{/* Not "Connect your account": the row is also the connected state, and a title has to
read for both. */}
- {heading ?? "Your account"}
+
+ {several && !heading
+ ? "Your accounts"
+ : (heading ?? "Your account")}
+
{/* Unclamped where the key is missing: that sentence is the only place the setting is
named, so it is the point rather than a hint. */}
- {accountSentence({
- account,
- connectedDescription,
- edition,
- disconnectedDescription,
- disconnectedReassurance,
- provider,
- reader,
- title,
- })}
+ {several
+ ? `You connected ${accounts.length} ${title} accounts. Bots use your default account unless you ask for another.`
+ : accountSentence({
+ account,
+ connectedDescription,
+ edition,
+ disconnectedDescription,
+ disconnectedReassurance,
+ identity: accounts[0]?.identity ?? null,
+ provider,
+ reader,
+ title,
+ })}
{/* A fact about the row itself rather than about the connection, so it holds under
either of the sentences above and is never clamped away. */}
@@ -1088,15 +1208,36 @@ export function BrokeredAccountRow({
) : null}
-
- Disconnect
-
+ {/*
+ * ONE MORE ACCOUNT BESIDE THE ONES CONNECTED, where the app can tell a person's
+ * accounts apart. A key app opens the form; a sign-in app leaves for its page.
+ */}
+ {canAddAnother && account.kind !== "no-auth" ? (
+
+ Connect another account
+ {account.kind === "consent" ? : null}
+
+ ) : null}
+ {/* With several accounts each has its own Disconnect, in its row below. */}
+ {several ? null : (
+
+ Disconnect
+
+ )}
>
) : (
/* The arrow says this leaves OpenBot for the vendor's consent page. It does, for
@@ -1129,6 +1270,24 @@ export function BrokeredAccountRow({
)}
+ {/*
+ * ONE ROW PER ACCOUNT, where there are several: its name, whether Bots use it by default, and
+ * what can be done to it. With one account the row above already names it.
+ */}
+ {several
+ ? accounts.map((held) => (
+ account.disconnectAccount(held.id)}
+ onMakeDefault={() => account.makeDefault(held.id)}
+ />
+ ))
+ : null}
+
@@ -1211,3 +1370,63 @@ export function BrokeredAccountRow({
>
);
}
+
+/** One of a person's several accounts at an app, beneath the row that names the app. */
+function AccountListRow({
+ changing,
+ connectedAt,
+ isDefault,
+ label,
+ onDisconnect,
+ onMakeDefault,
+}: {
+ changing: boolean;
+ connectedAt: string | null;
+ isDefault: boolean;
+ label: string;
+ onDisconnect: () => void;
+ onMakeDefault: () => void;
+}) {
+ return (
+ <>
+
+ -
+
+
+
+
+ {label}
+
+ {isDefault
+ ? "Default. Bots use this account unless you ask for another."
+ : connectedAt
+ ? `Connected ${formatDate(connectedAt)}. Bots use it when you ask for it.`
+ : "Bots use it when you ask for it."}
+
+
+
+ {isDefault ? null : (
+
+ Make default
+
+ )}
+
+ Disconnect
+
+
+
+ >
+ );
+}
diff --git a/app/src/lib/approvals.ts b/app/src/lib/approvals.ts
index 422806e117..1152a25a94 100644
--- a/app/src/lib/approvals.ts
+++ b/app/src/lib/approvals.ts
@@ -19,7 +19,11 @@ export type ApprovalRuleInput = {
scope: string;
behaviour: RuleBehaviour;
};
-export type ApprovalRuleRow = ApprovalRuleInput & { id: string };
+export type ApprovalRuleRow = ApprovalRuleInput & {
+ id: string;
+ /** The one account at the app a saved rule is for, where the person holds several. */
+ account?: string | null;
+};
export const BEHAVIOUR_LABELS: Record = {
allow: "Take action without asking",
pre_approved: "Take action if pre-approved",
@@ -43,6 +47,8 @@ export type ApprovalInboxData = {
toolCallId?: string;
args: unknown;
target: unknown;
+ /** Which of the person's accounts at the app, where they hold several. */
+ account?: string;
policy?: {
behaviour: "allow" | "ask" | "hand_off" | "deny";
source: string;
diff --git a/app/src/lib/plugins/mutations.ts b/app/src/lib/plugins/mutations.ts
index 2fb4ee0d7e..cede293113 100644
--- a/app/src/lib/plugins/mutations.ts
+++ b/app/src/lib/plugins/mutations.ts
@@ -433,29 +433,50 @@ export function connectAccountMutationOptions(
returnTo: "settings" | "admin" = "settings",
) {
return mutationOptions({
- mutationFn: async (serverId: string): Promise => {
- const fallback = "Couldn't connect that account.";
- const response = await client(
- `/api/plugins/servers/${encodeURIComponent(serverId)}/connect?returnTo=${returnTo}`,
- { method: "POST", fallback },
- );
- // Read as `client` reads an envelope: a body that is not an object is a failed request.
- const body = (await response.json().catch(() => null)) as unknown;
- if (!body || typeof body !== "object" || Array.isArray(body)) {
- throw new Error(fallback);
- }
- const answer = body as {
- authorizationUrl?: unknown;
- connected?: unknown;
- };
- if (answer.connected === true) return { connected: true };
- return typeof answer.authorizationUrl === "string"
- ? { authorizationUrl: answer.authorizationUrl }
- : null;
- },
+ mutationFn: (serverId: string): Promise =>
+ startConnect(serverId, returnTo, false),
});
}
+/**
+ * Connect one more account at an app the person already has an account at: a second mailbox, a
+ * work calendar beside a personal one. The same press as Connect, asking for another account
+ * rather than for more permissions on the one that stands.
+ */
+export function connectAnotherAccountMutationOptions(
+ returnTo: "settings" | "admin" = "settings",
+) {
+ return mutationOptions({
+ mutationFn: (serverId: string): Promise =>
+ startConnect(serverId, returnTo, true),
+ });
+}
+
+async function startConnect(
+ serverId: string,
+ returnTo: "settings" | "admin",
+ another: boolean,
+): Promise {
+ const fallback = "Couldn't connect that account.";
+ const response = await client(
+ `/api/plugins/servers/${encodeURIComponent(serverId)}/connect?returnTo=${returnTo}${another ? "&another=1" : ""}`,
+ { method: "POST", fallback },
+ );
+ // Read as `client` reads an envelope: a body that is not an object is a failed request.
+ const body = (await response.json().catch(() => null)) as unknown;
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
+ throw new Error(fallback);
+ }
+ const answer = body as {
+ authorizationUrl?: unknown;
+ connected?: unknown;
+ };
+ if (answer.connected === true) return { connected: true };
+ return typeof answer.authorizationUrl === "string"
+ ? { authorizationUrl: answer.authorizationUrl }
+ : null;
+}
+
/**
* What one press of Connect started: a consent page to visit, an account that is already
* connected, or `null` for neither (an app whose key somebody types). See
@@ -532,6 +553,41 @@ export function disconnectBrokeredMutationOptions(queryClient: QueryClient) {
});
}
+const ACCOUNT_FALLBACK = "Couldn't change that account.";
+
+/** Make one of the person's accounts at an app the one their Bots use when they name none. */
+export function setDefaultBrokeredAccountMutationOptions(
+ queryClient: QueryClient,
+) {
+ return mutationOptions({
+ mutationFn: async (variables: { serverId: string; accountId: string }) => {
+ await client(
+ `/api/plugins/servers/${encodeURIComponent(variables.serverId)}/accounts/${encodeURIComponent(variables.accountId)}/default`,
+ { method: "POST", fallback: ACCOUNT_FALLBACK },
+ );
+ },
+ onSettled: () => invalidatePlugins(queryClient),
+ });
+}
+
+/**
+ * Disconnect one of the person's accounts at an app and keep the rest. The last one ends the
+ * connection, as Disconnect always has.
+ */
+export function disconnectBrokeredAccountMutationOptions(
+ queryClient: QueryClient,
+) {
+ return mutationOptions({
+ mutationFn: async (variables: { serverId: string; accountId: string }) => {
+ await client(
+ `/api/plugins/servers/${encodeURIComponent(variables.serverId)}/accounts/${encodeURIComponent(variables.accountId)}`,
+ { method: "DELETE", fallback: "Couldn't disconnect that account." },
+ );
+ },
+ onSettled: () => invalidatePlugins(queryClient),
+ });
+}
+
export function removeSkillMutationOptions(queryClient: QueryClient) {
return mutationOptions({
mutationFn: async (slug: string) => {
diff --git a/app/src/lib/plugins/queries.ts b/app/src/lib/plugins/queries.ts
index 2432f5b4d5..3d65430f81 100644
--- a/app/src/lib/plugins/queries.ts
+++ b/app/src/lib/plugins/queries.ts
@@ -318,6 +318,7 @@ export const pluginKeys = {
forAgent: (agentId: string) => ["plugins", "for-agent", agentId] as const,
connections: () => ["plugins", "connections"] as const,
siblings: (serverId: string) => ["plugins", "siblings", serverId] as const,
+ accounts: (serverId: string) => ["plugins", "accounts", serverId] as const,
composioApps: (query: string) =>
["plugins", "composio", "apps", query] as const,
};
@@ -570,3 +571,43 @@ export function agentPluginsQueryOptions(agentId: string) {
},
});
}
+
+/** One of the signed-in person's accounts at an app, as `/servers/:id/accounts` answers it. */
+export type BrokeredAccountRecord = {
+ /** Which account a Disconnect or Make default press is about. Opaque. */
+ id: string;
+ /** What the app says the account is: an email address or a login. Null where it said none. */
+ identity: string | null;
+ /** What the person and their Bots call it: the identity, or "account 2" where there is none. */
+ label: string;
+ /** The account a Bot uses when it doesn't name one. */
+ isDefault: boolean;
+ connectedAt: string | null;
+};
+
+export type BrokeredAccounts = {
+ accounts: BrokeredAccountRecord[];
+ /** Whether this app can take another account beside the ones listed. The server decides. */
+ canAddAnother: boolean;
+};
+
+/**
+ * The person's own accounts at one brokered app. The server asks the app each time, so an account
+ * connected or ended elsewhere is listed as it stands now.
+ */
+export function brokeredAccountsQueryOptions(serverId: string) {
+ return queryOptions({
+ queryKey: pluginKeys.accounts(serverId),
+ queryFn: async (): Promise => {
+ const response = await client(
+ `/api/plugins/servers/${encodeURIComponent(serverId)}/accounts`,
+ { fallback: "Couldn't list your accounts. Reload the page." },
+ );
+ const body: Partial = await response.json();
+ return {
+ accounts: Array.isArray(body.accounts) ? body.accounts : [],
+ canAddAnother: body.canAddAnother === true,
+ };
+ },
+ });
+}
diff --git a/app/tests/brokered-account-row.test.tsx b/app/tests/brokered-account-row.test.tsx
index acf6918e42..32c729858b 100644
--- a/app/tests/brokered-account-row.test.tsx
+++ b/app/tests/brokered-account-row.test.tsx
@@ -1269,6 +1269,13 @@ function accountState(overrides: Partial): BrokeredAccount {
submittingFields: false,
verified: false,
verifiedAt: null,
+ serverId: "gmail",
+ fieldsSubmitted: false,
+ connectAnother: () => {},
+ connectingAnother: false,
+ makeDefault: () => {},
+ disconnectAccount: () => {},
+ changingAccount: null,
...overrides,
};
}
@@ -1276,13 +1283,15 @@ function accountState(overrides: Partial): BrokeredAccount {
/** The row with both screens' arguments filled in, so only the account differs between cases. */
function renderRow(account: BrokeredAccount) {
return render(
- ,
+
+
+ ,
);
}
@@ -2720,3 +2729,98 @@ test("a re-check that spent nothing leaves the rejection the record still names"
// Never the absolution, which is the other app's state and not this one's.
expect(view.queryByText(/hasn't been checked with Gmail/)).toBeNull();
});
+
+/**
+ * A person's accounts at the app, as `/servers/:id/accounts` answers them, for the cases below.
+ * Every other request is a 404, so nothing here can be answered by accident.
+ */
+function answerAccounts(body: unknown) {
+ global.fetch = (async (input: RequestInfo | URL) => {
+ const requested = typeof input === "string" ? input : String(input);
+ const path = requested.split("?")[0] ?? requested;
+ if (path.endsWith("/accounts")) {
+ return new Response(JSON.stringify(body), {
+ headers: { "content-type": "application/json" },
+ });
+ }
+ return new Response(null, { status: 404 });
+ }) as typeof fetch;
+}
+
+test("several accounts are listed by name, each with its own Disconnect, and another can be connected", async () => {
+ answerAccounts({
+ accounts: [
+ {
+ id: "a1",
+ identity: "dana@work.example",
+ label: "dana@work.example",
+ isDefault: true,
+ connectedAt: "2026-10-01T09:00:00.000Z",
+ },
+ {
+ id: "a2",
+ identity: "dana.home@gmail.example",
+ label: "dana.home@gmail.example",
+ isDefault: false,
+ connectedAt: "2026-10-20T09:00:00.000Z",
+ },
+ ],
+ canAddAnother: true,
+ });
+ const pressed: string[] = [];
+ const view = renderRow(
+ accountState({
+ connected: true,
+ kind: "consent",
+ connectAnother: () => pressed.push("another"),
+ makeDefault: (id) => pressed.push(`default:${id}`),
+ disconnectAccount: (id) => pressed.push(`disconnect:${id}`),
+ }),
+ );
+
+ expect(await view.findByText("dana@work.example")).toBeTruthy();
+ expect(view.getByText("dana.home@gmail.example")).toBeTruthy();
+ expect(view.getByText("Your accounts")).toBeTruthy();
+ expect(
+ view.getByText(
+ "Default. Bots use this account unless you ask for another.",
+ ),
+ ).toBeTruthy();
+ // One Disconnect per account, and none for the app as a whole.
+ expect(view.getAllByRole("button", { name: "Disconnect" })).toHaveLength(2);
+ expect(view.getAllByRole("button", { name: "Make default" })).toHaveLength(1);
+
+ await userEvent.click(
+ view.getByRole("button", { name: /Connect another account/ }),
+ );
+ await userEvent.click(view.getByRole("button", { name: "Make default" }));
+ await userEvent.click(
+ view.getAllByRole("button", { name: "Disconnect" })[1] as HTMLElement,
+ );
+ expect(pressed).toEqual(["another", "default:a2", "disconnect:a2"]);
+});
+
+test("one account is named in the row, which keeps its Disconnect and offers another", async () => {
+ answerAccounts({
+ accounts: [
+ {
+ id: "a1",
+ identity: "dana@work.example",
+ label: "dana@work.example",
+ isDefault: true,
+ connectedAt: null,
+ },
+ ],
+ canAddAnother: true,
+ });
+ const view = renderRow(accountState({ connected: true, kind: "consent" }));
+
+ expect(
+ await view.findByText(/Signed in with Gmail as dana@work\.example\./),
+ ).toBeTruthy();
+ expect(view.getByText("Your account")).toBeTruthy();
+ expect(view.getAllByRole("button", { name: "Disconnect" })).toHaveLength(1);
+ expect(
+ view.getByRole("button", { name: /Connect another account/ }),
+ ).toBeTruthy();
+});
diff --git a/server/drizzle/0084_brokered_accounts.sql b/server/drizzle/0084_brokered_accounts.sql
new file mode 100644
index 0000000000..a62f9124dc
--- /dev/null
+++ b/server/drizzle/0084_brokered_accounts.sql
@@ -0,0 +1,17 @@
+CREATE TABLE "brokered_accounts" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "provider" text NOT NULL,
+ "app" text NOT NULL,
+ "user_id" text NOT NULL,
+ "vendor_user_id" text NOT NULL,
+ "account_id" text DEFAULT '' NOT NULL,
+ "identity" text,
+ "is_default" boolean DEFAULT false NOT NULL,
+ "connected_at" timestamp with time zone,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ CONSTRAINT "brokered_accounts_provider_check" CHECK ("brokered_accounts"."provider" IN ('composio', 'arcade'))
+);
+--> statement-breakpoint
+CREATE UNIQUE INDEX "brokered_accounts_account_idx" ON "brokered_accounts" USING btree ("provider","app","user_id","vendor_user_id","account_id");--> statement-breakpoint
+CREATE UNIQUE INDEX "brokered_accounts_default_idx" ON "brokered_accounts" USING btree ("provider","app","user_id") WHERE "brokered_accounts"."is_default";--> statement-breakpoint
+CREATE INDEX "brokered_accounts_user_idx" ON "brokered_accounts" USING btree ("user_id");
\ No newline at end of file
diff --git a/server/drizzle/0085_approval_rule_account.sql b/server/drizzle/0085_approval_rule_account.sql
new file mode 100644
index 0000000000..c867821f92
--- /dev/null
+++ b/server/drizzle/0085_approval_rule_account.sql
@@ -0,0 +1 @@
+ALTER TABLE "approval_rules" ADD COLUMN "account" text;
\ No newline at end of file
diff --git a/server/drizzle/meta/0084_snapshot.json b/server/drizzle/meta/0084_snapshot.json
new file mode 100644
index 0000000000..069f8a3037
--- /dev/null
+++ b/server/drizzle/meta/0084_snapshot.json
@@ -0,0 +1,11791 @@
+{
+ "id": "35e68b14-b041-4974-b872-6189ad083cdf",
+ "prevId": "77c183aa-bb83-4d28-9e1f-83932f4330e7",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.accounts": {
+ "name": "accounts",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "account_id": {
+ "name": "account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issuer": {
+ "name": "issuer",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "access_token": {
+ "name": "access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token": {
+ "name": "refresh_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "access_token_expires_at": {
+ "name": "access_token_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token_expires_at": {
+ "name": "refresh_token_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password": {
+ "name": "password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "accounts_provider_account_idx": {
+ "name": "accounts_provider_account_idx",
+ "columns": [
+ {
+ "expression": "provider_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "account_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "accounts_user_id_users_id_fk": {
+ "name": "accounts_user_id_users_id_fk",
+ "tableFrom": "accounts",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.agents": {
+ "name": "agents",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "agent_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "configuration": {
+ "name": "configuration",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "package_id": {
+ "name": "package_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "override": {
+ "name": "override",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "agents_package_id_deployment_packages_id_fk": {
+ "name": "agents_package_id_deployment_packages_id_fk",
+ "tableFrom": "agents",
+ "tableTo": "deployment_packages",
+ "columnsFrom": [
+ "package_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.attachments": {
+ "name": "attachments",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "uploaded_by": {
+ "name": "uploaded_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "mime_type": {
+ "name": "mime_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "size_bytes": {
+ "name": "size_bytes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "bytes": {
+ "name": "bytes",
+ "type": "bytea",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "attached_at": {
+ "name": "attached_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "upload_group": {
+ "name": "upload_group",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "attachments_channel_idx": {
+ "name": "attachments_channel_idx",
+ "columns": [
+ {
+ "expression": "channel_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "attachments_uploaded_by_idx": {
+ "name": "attachments_uploaded_by_idx",
+ "columns": [
+ {
+ "expression": "uploaded_by",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "attachments_upload_group_idx": {
+ "name": "attachments_upload_group_idx",
+ "columns": [
+ {
+ "expression": "channel_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "uploaded_by",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "upload_group",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"attachments\".\"attached_at\" is null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "attachments_staged_idx": {
+ "name": "attachments_staged_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"attachments\".\"attached_at\" is null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "attachments_channel_id_channels_id_fk": {
+ "name": "attachments_channel_id_channels_id_fk",
+ "tableFrom": "attachments",
+ "tableTo": "channels",
+ "columnsFrom": [
+ "channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "attachments_uploaded_by_users_id_fk": {
+ "name": "attachments_uploaded_by_users_id_fk",
+ "tableFrom": "attachments",
+ "tableTo": "users",
+ "columnsFrom": [
+ "uploaded_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.audit_events": {
+ "name": "audit_events",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "actor_user_id": {
+ "name": "actor_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "initiator_kind": {
+ "name": "initiator_kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'person'"
+ },
+ "initiator_id": {
+ "name": "initiator_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "event_type": {
+ "name": "event_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_type": {
+ "name": "target_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_id": {
+ "name": "target_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "payload": {
+ "name": "payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "audit_events_created_at_idx": {
+ "name": "audit_events_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_events_type_time_idx": {
+ "name": "audit_events_type_time_idx",
+ "columns": [
+ {
+ "expression": "event_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": false,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": false,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_events_actor_time_idx": {
+ "name": "audit_events_actor_time_idx",
+ "columns": [
+ {
+ "expression": "actor_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": false,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": false,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_events_target_time_idx": {
+ "name": "audit_events_target_time_idx",
+ "columns": [
+ {
+ "expression": "target_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "target_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": false,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": false,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_events_initiator_time_idx": {
+ "name": "audit_events_initiator_time_idx",
+ "columns": [
+ {
+ "expression": "initiator_kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": false,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": false,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.channel_agents": {
+ "name": "channel_agents",
+ "schema": "",
+ "columns": {
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "channel_agents_channel_id_channels_id_fk": {
+ "name": "channel_agents_channel_id_channels_id_fk",
+ "tableFrom": "channel_agents",
+ "tableTo": "channels",
+ "columnsFrom": [
+ "channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "channel_agents_agent_id_agents_id_fk": {
+ "name": "channel_agents_agent_id_agents_id_fk",
+ "tableFrom": "channel_agents",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "channel_agents_channel_id_agent_id_pk": {
+ "name": "channel_agents_channel_id_agent_id_pk",
+ "columns": [
+ "channel_id",
+ "agent_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.channel_memberships": {
+ "name": "channel_memberships",
+ "schema": "",
+ "columns": {
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "pinned_at": {
+ "name": "pinned_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_read_at": {
+ "name": "last_read_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "channel_memberships_channel_id_channels_id_fk": {
+ "name": "channel_memberships_channel_id_channels_id_fk",
+ "tableFrom": "channel_memberships",
+ "tableTo": "channels",
+ "columnsFrom": [
+ "channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "channel_memberships_user_id_users_id_fk": {
+ "name": "channel_memberships_user_id_users_id_fk",
+ "tableFrom": "channel_memberships",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "channel_memberships_channel_id_user_id_pk": {
+ "name": "channel_memberships_channel_id_user_id_pk",
+ "columns": [
+ "channel_id",
+ "user_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.channels": {
+ "name": "channels",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "suggested_prompts": {
+ "name": "suggested_prompts",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'"
+ },
+ "allowed_groups": {
+ "name": "allowed_groups",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'"
+ },
+ "package_id": {
+ "name": "package_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "override": {
+ "name": "override",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "summary": {
+ "name": "summary",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "summary_at": {
+ "name": "summary_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "opening": {
+ "name": "opening",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_message": {
+ "name": "last_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_message_source_id": {
+ "name": "last_message_source_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_message_at": {
+ "name": "last_message_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_message_agent_id": {
+ "name": "last_message_agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "channels_recent_activity_idx": {
+ "name": "channels_recent_activity_idx",
+ "columns": [
+ {
+ "expression": "COALESCE(\"last_message_at\", \"created_at\") DESC",
+ "asc": true,
+ "isExpression": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "channels_awaiting_summary_idx": {
+ "name": "channels_awaiting_summary_idx",
+ "columns": [
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"channels\".\"summary\" is null and \"channels\".\"deleted_at\" is null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "channels_package_id_deployment_packages_id_fk": {
+ "name": "channels_package_id_deployment_packages_id_fk",
+ "tableFrom": "channels",
+ "tableTo": "deployment_packages",
+ "columnsFrom": [
+ "package_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "channels_last_message_agent_id_agents_id_fk": {
+ "name": "channels_last_message_agent_id_agents_id_fk",
+ "tableFrom": "channels",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "last_message_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.credentials": {
+ "name": "credentials",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "kind": {
+ "name": "kind",
+ "type": "credential_kind",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "encrypted_value": {
+ "name": "encrypted_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key_id": {
+ "name": "key_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "credentials_active_key_idx": {
+ "name": "credentials_active_key_idx",
+ "columns": [
+ {
+ "expression": "kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "key_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"credentials\".\"revoked_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.deployment_packages": {
+ "name": "deployment_packages",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "tenant_id": {
+ "name": "tenant_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source_path": {
+ "name": "source_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "checksum": {
+ "name": "checksum",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "loaded_at": {
+ "name": "loaded_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "deployment_packages_tenant_id_unique": {
+ "name": "deployment_packages_tenant_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "tenant_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.intelligence_channel_mappings": {
+ "name": "intelligence_channel_mappings",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "model_selection": {
+ "name": "model_selection",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "intelligence_channel_mappings_thread_idx": {
+ "name": "intelligence_channel_mappings_thread_idx",
+ "columns": [
+ {
+ "expression": "thread_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "intelligence_channel_mappings_user_id_users_id_fk": {
+ "name": "intelligence_channel_mappings_user_id_users_id_fk",
+ "tableFrom": "intelligence_channel_mappings",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "intelligence_channel_mappings_channel_id_channels_id_fk": {
+ "name": "intelligence_channel_mappings_channel_id_channels_id_fk",
+ "tableFrom": "intelligence_channel_mappings",
+ "tableTo": "channels",
+ "columnsFrom": [
+ "channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "intelligence_channel_mappings_user_id_channel_id_pk": {
+ "name": "intelligence_channel_mappings_user_id_channel_id_pk",
+ "columns": [
+ "user_id",
+ "channel_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.revoked_access": {
+ "name": "revoked_access",
+ "schema": "",
+ "columns": {
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "revoked_by": {
+ "name": "revoked_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.sessions": {
+ "name": "sessions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "sessions_user_id_users_id_fk": {
+ "name": "sessions_user_id_users_id_fk",
+ "tableFrom": "sessions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "sessions_token_unique": {
+ "name": "sessions_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.sso_providers": {
+ "name": "sso_providers",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "issuer": {
+ "name": "issuer",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "oidc_config": {
+ "name": "oidc_config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "saml_config": {
+ "name": "saml_config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "domain": {
+ "name": "domain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "sso_providers_user_id_users_id_fk": {
+ "name": "sso_providers_user_id_users_id_fk",
+ "tableFrom": "sso_providers",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "sso_providers_provider_id_unique": {
+ "name": "sso_providers_provider_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "provider_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_instructions": {
+ "name": "user_instructions",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "instructions": {
+ "name": "instructions",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "user_instructions_user_id_users_id_fk": {
+ "name": "user_instructions_user_id_users_id_fk",
+ "tableFrom": "user_instructions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_roles": {
+ "name": "user_roles",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "role",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "user_roles_user_id_users_id_fk": {
+ "name": "user_roles_user_id_users_id_fk",
+ "tableFrom": "user_roles",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "user_roles_user_id_role_pk": {
+ "name": "user_roles_user_id_role_pk",
+ "columns": [
+ "user_id",
+ "role"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.users": {
+ "name": "users",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "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": {
+ "name": "image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "email_verified": {
+ "name": "email_verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "preferences": {
+ "name": "preferences",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "groups": {
+ "name": "groups",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'"
+ },
+ "onboarding_step": {
+ "name": "onboarding_step",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "onboarding_completed_at": {
+ "name": "onboarding_completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_signed_in_at": {
+ "name": "last_signed_in_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "users_email_unique": {
+ "name": "users_email_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "email"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.verifications": {
+ "name": "verifications",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.action_policy": {
+ "name": "action_policy",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "mode": {
+ "name": "mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "deny": {
+ "name": "deny",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "allow": {
+ "name": "allow",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "labels": {
+ "name": "labels",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "updated_by": {
+ "name": "updated_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.computer_page_frame": {
+ "name": "computer_page_frame",
+ "schema": "",
+ "columns": {
+ "computer_id": {
+ "name": "computer_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "tool_call_id": {
+ "name": "tool_call_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "url": {
+ "name": "url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "frame": {
+ "name": "frame",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "captured_at": {
+ "name": "captured_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "computer_page_frame_captured_idx": {
+ "name": "computer_page_frame_captured_idx",
+ "columns": [
+ {
+ "expression": "captured_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "computer_page_frame_computer_id_tool_call_id_pk": {
+ "name": "computer_page_frame_computer_id_tool_call_id_pk",
+ "columns": [
+ "computer_id",
+ "tool_call_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.computer_snapshot": {
+ "name": "computer_snapshot",
+ "schema": "",
+ "columns": {
+ "computer_id": {
+ "name": "computer_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "snapshot_id": {
+ "name": "snapshot_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "url": {
+ "name": "url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "elements": {
+ "name": "elements",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "taken_at": {
+ "name": "taken_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "session": {
+ "name": "session",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.agent_preferences": {
+ "name": "agent_preferences",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hidden_at": {
+ "name": "hidden_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pinned_at": {
+ "name": "pinned_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "agent_preferences_user_id_users_id_fk": {
+ "name": "agent_preferences_user_id_users_id_fk",
+ "tableFrom": "agent_preferences",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "agent_preferences_agent_id_agents_id_fk": {
+ "name": "agent_preferences_agent_id_agents_id_fk",
+ "tableFrom": "agent_preferences",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "agent_preferences_user_id_agent_id_pk": {
+ "name": "agent_preferences_user_id_agent_id_pk",
+ "columns": [
+ "user_id",
+ "agent_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.agent_profiles": {
+ "name": "agent_profiles",
+ "schema": "",
+ "columns": {
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role_description": {
+ "name": "role_description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "avatar_seed": {
+ "name": "avatar_seed",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "visibility": {
+ "name": "visibility",
+ "type": "agent_visibility",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "isolated": {
+ "name": "isolated",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "callback_token_hash": {
+ "name": "callback_token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "callback_token_issued_at": {
+ "name": "callback_token_issued_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "agent_profiles_visibility_deleted_idx": {
+ "name": "agent_profiles_visibility_deleted_idx",
+ "columns": [
+ {
+ "expression": "visibility",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "deleted_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "agent_profiles_agent_id_agents_id_fk": {
+ "name": "agent_profiles_agent_id_agents_id_fk",
+ "tableFrom": "agent_profiles",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "agent_profiles_owner_user_id_users_id_fk": {
+ "name": "agent_profiles_owner_user_id_users_id_fk",
+ "tableFrom": "agent_profiles",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.routine_runs": {
+ "name": "routine_runs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "routine_id": {
+ "name": "routine_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "finished_at": {
+ "name": "finished_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "routine_run_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "waiting": {
+ "name": "waiting",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error": {
+ "name": "error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source": {
+ "name": "source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'schedule'"
+ }
+ },
+ "indexes": {
+ "routine_runs_by_routine_idx": {
+ "name": "routine_runs_by_routine_idx",
+ "columns": [
+ {
+ "expression": "routine_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "started_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "routine_runs_routine_id_routines_id_fk": {
+ "name": "routine_runs_routine_id_routines_id_fk",
+ "tableFrom": "routine_runs",
+ "tableTo": "routines",
+ "columnsFrom": [
+ "routine_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.routine_sweeps": {
+ "name": "routine_sweeps",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "swept_at": {
+ "name": "swept_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "owner": {
+ "name": "owner",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.routines": {
+ "name": "routines",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "instruction": {
+ "name": "instruction",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "goal_id": {
+ "name": "goal_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "goal_by_title": {
+ "name": "goal_by_title",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "cron": {
+ "name": "cron",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "timezone": {
+ "name": "timezone",
+ "type": "text",
+ "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 with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_run_at": {
+ "name": "last_run_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enabled_at": {
+ "name": "enabled_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "routines_due_idx": {
+ "name": "routines_due_idx",
+ "columns": [
+ {
+ "expression": "enabled",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "next_run_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "routines_by_owner_idx": {
+ "name": "routines_by_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "enabled",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "routines_owner_user_id_users_id_fk": {
+ "name": "routines_owner_user_id_users_id_fk",
+ "tableFrom": "routines",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "routines_agent_id_agents_id_fk": {
+ "name": "routines_agent_id_agents_id_fk",
+ "tableFrom": "routines",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "routines_goal_id_responsibilities_id_fk": {
+ "name": "routines_goal_id_responsibilities_id_fk",
+ "tableFrom": "routines",
+ "tableTo": "responsibilities",
+ "columnsFrom": [
+ "goal_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.component_exclusions": {
+ "name": "component_exclusions",
+ "schema": "",
+ "columns": {
+ "component_name": {
+ "name": "component_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "withheld_by": {
+ "name": "withheld_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "component_exclusions_component_name_components_name_fk": {
+ "name": "component_exclusions_component_name_components_name_fk",
+ "tableFrom": "component_exclusions",
+ "tableTo": "components",
+ "columnsFrom": [
+ "component_name"
+ ],
+ "columnsTo": [
+ "name"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "component_exclusions_agent_id_agents_id_fk": {
+ "name": "component_exclusions_agent_id_agents_id_fk",
+ "tableFrom": "component_exclusions",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "component_exclusions_component_name_agent_id_pk": {
+ "name": "component_exclusions_component_name_agent_id_pk",
+ "columns": [
+ "component_name",
+ "agent_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.component_functions": {
+ "name": "component_functions",
+ "schema": "",
+ "columns": {
+ "component_name": {
+ "name": "component_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "function_name": {
+ "name": "function_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "granted_by": {
+ "name": "granted_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "component_functions_component_name_components_name_fk": {
+ "name": "component_functions_component_name_components_name_fk",
+ "tableFrom": "component_functions",
+ "tableTo": "components",
+ "columnsFrom": [
+ "component_name"
+ ],
+ "columnsTo": [
+ "name"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "component_functions_component_name_function_name_pk": {
+ "name": "component_functions_component_name_function_name_pk",
+ "columns": [
+ "component_name",
+ "function_name"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.components": {
+ "name": "components",
+ "schema": "",
+ "columns": {
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "draft_description": {
+ "name": "draft_description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "published_description": {
+ "name": "published_description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "published": {
+ "name": "published",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "published_at": {
+ "name": "published_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_by": {
+ "name": "updated_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.app_access_requests": {
+ "name": "app_access_requests",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "requested_by": {
+ "name": "requested_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "app_key": {
+ "name": "app_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "app_title": {
+ "name": "app_title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "resolved_at": {
+ "name": "resolved_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resolved_by": {
+ "name": "resolved_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "app_access_requests_pending_idx": {
+ "name": "app_access_requests_pending_idx",
+ "columns": [
+ {
+ "expression": "requested_by",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "app_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "coalesce(\"agent_id\", '')",
+ "asc": true,
+ "isExpression": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"app_access_requests\".\"status\" = 'pending'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "app_access_requests_status_idx": {
+ "name": "app_access_requests_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "app_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "app_access_requests_requested_by_users_id_fk": {
+ "name": "app_access_requests_requested_by_users_id_fk",
+ "tableFrom": "app_access_requests",
+ "tableTo": "users",
+ "columnsFrom": [
+ "requested_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "app_access_requests_agent_id_agents_id_fk": {
+ "name": "app_access_requests_agent_id_agents_id_fk",
+ "tableFrom": "app_access_requests",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "app_access_requests_kind_check": {
+ "name": "app_access_requests_kind_check",
+ "value": "\"app_access_requests\".\"kind\" IN ('enable', 'grant', 'connect_shared')"
+ },
+ "app_access_requests_status_check": {
+ "name": "app_access_requests_status_check",
+ "value": "\"app_access_requests\".\"status\" IN ('pending', 'approved', 'dismissed', 'cancelled')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.brokered_accounts": {
+ "name": "brokered_accounts",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "app": {
+ "name": "app",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "vendor_user_id": {
+ "name": "vendor_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "account_id": {
+ "name": "account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "identity": {
+ "name": "identity",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_default": {
+ "name": "is_default",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "connected_at": {
+ "name": "connected_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "brokered_accounts_account_idx": {
+ "name": "brokered_accounts_account_idx",
+ "columns": [
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "app",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "vendor_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "account_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "brokered_accounts_default_idx": {
+ "name": "brokered_accounts_default_idx",
+ "columns": [
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "app",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"brokered_accounts\".\"is_default\"",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "brokered_accounts_user_idx": {
+ "name": "brokered_accounts_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "brokered_accounts_provider_check": {
+ "name": "brokered_accounts_provider_check",
+ "value": "\"brokered_accounts\".\"provider\" IN ('composio', 'arcade')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.brokered_connections": {
+ "name": "brokered_connections",
+ "schema": "",
+ "columns": {
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "app": {
+ "name": "app",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "holder": {
+ "name": "holder",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "vendor_user_id": {
+ "name": "vendor_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "connected_by": {
+ "name": "connected_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "connected_at": {
+ "name": "connected_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "verified": {
+ "name": "verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "verified_at": {
+ "name": "verified_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "probe_action": {
+ "name": "probe_action",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "brokered_connections_person_idx": {
+ "name": "brokered_connections_person_idx",
+ "columns": [
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "app",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"brokered_connections\".\"holder\" = 'person'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "brokered_connections_deployment_idx": {
+ "name": "brokered_connections_deployment_idx",
+ "columns": [
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "app",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"brokered_connections\".\"holder\" = 'deployment'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "brokered_connections_user_idx": {
+ "name": "brokered_connections_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "brokered_connections_holder_check": {
+ "name": "brokered_connections_holder_check",
+ "value": "(\"brokered_connections\".\"holder\" = 'person' AND \"brokered_connections\".\"user_id\" IS NOT NULL) OR (\"brokered_connections\".\"holder\" = 'deployment' AND \"brokered_connections\".\"user_id\" IS NULL)"
+ },
+ "brokered_connections_provider_check": {
+ "name": "brokered_connections_provider_check",
+ "value": "\"brokered_connections\".\"provider\" IN ('composio', 'arcade')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.brokered_identities": {
+ "name": "brokered_identities",
+ "schema": "",
+ "columns": {
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "vendor_user_id": {
+ "name": "vendor_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "brokered_identities_provider_user_id_pk": {
+ "name": "brokered_identities_provider_user_id_pk",
+ "columns": [
+ "provider",
+ "user_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "brokered_identities_provider_check": {
+ "name": "brokered_identities_provider_check",
+ "value": "\"brokered_identities\".\"provider\" IN ('composio', 'arcade')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.composio_connections": {
+ "name": "composio_connections",
+ "schema": "",
+ "columns": {
+ "toolkit": {
+ "name": "toolkit",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "connected_at": {
+ "name": "connected_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "verified": {
+ "name": "verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "verified_at": {
+ "name": "verified_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "probe_action": {
+ "name": "probe_action",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "composio_connections_user_idx": {
+ "name": "composio_connections_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "composio_connections_toolkit_user_id_pk": {
+ "name": "composio_connections_toolkit_user_id_pk",
+ "columns": [
+ "toolkit",
+ "user_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.mcp_servers": {
+ "name": "mcp_servers",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "logo": {
+ "name": "logo",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "vendor": {
+ "name": "vendor",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "url": {
+ "name": "url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provenance": {
+ "name": "provenance",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'first-party'"
+ },
+ "credential_id": {
+ "name": "credential_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "auth_scheme": {
+ "name": "auth_scheme",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "account_mode": {
+ "name": "account_mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "shared_vendor_user_id": {
+ "name": "shared_vendor_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owner_grants": {
+ "name": "owner_grants",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "entra_scope": {
+ "name": "entra_scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "tools_refreshed_at": {
+ "name": "tools_refreshed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "skipped_tools": {
+ "name": "skipped_tools",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "listing_revision": {
+ "name": "listing_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "added_by": {
+ "name": "added_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "mcp_servers_credential_id_credentials_id_fk": {
+ "name": "mcp_servers_credential_id_credentials_id_fk",
+ "tableFrom": "mcp_servers",
+ "tableTo": "credentials",
+ "columnsFrom": [
+ "credential_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "mcp_servers_account_mode_check": {
+ "name": "mcp_servers_account_mode_check",
+ "value": "\"mcp_servers\".\"account_mode\" IS NULL OR \"mcp_servers\".\"account_mode\" IN ('personal', 'shared')"
+ },
+ "mcp_servers_owner_grants_check": {
+ "name": "mcp_servers_owner_grants_check",
+ "value": "\"mcp_servers\".\"owner_grants\" IS NULL OR \"mcp_servers\".\"owner_grants\" IN ('any', 'admin')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.mcp_tools": {
+ "name": "mcp_tools",
+ "schema": "",
+ "columns": {
+ "server_id": {
+ "name": "server_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "vendor_name": {
+ "name": "vendor_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "input_schema": {
+ "name": "input_schema",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "effect": {
+ "name": "effect",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "destructive": {
+ "name": "destructive",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "unclassified": {
+ "name": "unclassified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "declared_read": {
+ "name": "declared_read",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "version": {
+ "name": "version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "mcp_tools_server_id_mcp_servers_id_fk": {
+ "name": "mcp_tools_server_id_mcp_servers_id_fk",
+ "tableFrom": "mcp_tools",
+ "tableTo": "mcp_servers",
+ "columnsFrom": [
+ "server_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "mcp_tools_server_id_name_pk": {
+ "name": "mcp_tools_server_id_name_pk",
+ "columns": [
+ "server_id",
+ "name"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.mcp_user_credentials": {
+ "name": "mcp_user_credentials",
+ "schema": "",
+ "columns": {
+ "server_id": {
+ "name": "server_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "credential_id": {
+ "name": "credential_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "connected_at": {
+ "name": "connected_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "mcp_user_credentials_user_idx": {
+ "name": "mcp_user_credentials_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "mcp_user_credentials_server_id_mcp_servers_id_fk": {
+ "name": "mcp_user_credentials_server_id_mcp_servers_id_fk",
+ "tableFrom": "mcp_user_credentials",
+ "tableTo": "mcp_servers",
+ "columnsFrom": [
+ "server_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "mcp_user_credentials_user_id_users_id_fk": {
+ "name": "mcp_user_credentials_user_id_users_id_fk",
+ "tableFrom": "mcp_user_credentials",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "mcp_user_credentials_credential_id_credentials_id_fk": {
+ "name": "mcp_user_credentials_credential_id_credentials_id_fk",
+ "tableFrom": "mcp_user_credentials",
+ "tableTo": "credentials",
+ "columnsFrom": [
+ "credential_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "mcp_user_credentials_server_id_user_id_pk": {
+ "name": "mcp_user_credentials_server_id_user_id_pk",
+ "columns": [
+ "server_id",
+ "user_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.pending_authorizations": {
+ "name": "pending_authorizations",
+ "schema": "",
+ "columns": {
+ "actor_id": {
+ "name": "actor_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "server_id": {
+ "name": "server_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action": {
+ "name": "action",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "holder": {
+ "name": "holder",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "vendor_user_id": {
+ "name": "vendor_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "auth_id": {
+ "name": "auth_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "outcome": {
+ "name": "outcome",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "pending_authorizations_server_idx": {
+ "name": "pending_authorizations_server_idx",
+ "columns": [
+ {
+ "expression": "server_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "pending_authorizations_actor_id_users_id_fk": {
+ "name": "pending_authorizations_actor_id_users_id_fk",
+ "tableFrom": "pending_authorizations",
+ "tableTo": "users",
+ "columnsFrom": [
+ "actor_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "pending_authorizations_server_id_mcp_servers_id_fk": {
+ "name": "pending_authorizations_server_id_mcp_servers_id_fk",
+ "tableFrom": "pending_authorizations",
+ "tableTo": "mcp_servers",
+ "columnsFrom": [
+ "server_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "pending_authorizations_holder_check": {
+ "name": "pending_authorizations_holder_check",
+ "value": "(\"pending_authorizations\".\"holder\" = 'person' AND \"pending_authorizations\".\"user_id\" IS NOT NULL) OR (\"pending_authorizations\".\"holder\" = 'deployment' AND \"pending_authorizations\".\"user_id\" IS NULL)"
+ },
+ "pending_authorizations_outcome_check": {
+ "name": "pending_authorizations_outcome_check",
+ "value": "\"pending_authorizations\".\"outcome\" IS NULL OR \"pending_authorizations\".\"outcome\" IN ('completed', 'failed', 'unreconciled', 'withdrawn')"
+ },
+ "pending_authorizations_completion_check": {
+ "name": "pending_authorizations_completion_check",
+ "value": "(\"pending_authorizations\".\"completed_at\" IS NULL) = (\"pending_authorizations\".\"outcome\" IS NULL)"
+ },
+ "pending_authorizations_user_is_actor_check": {
+ "name": "pending_authorizations_user_is_actor_check",
+ "value": "\"pending_authorizations\".\"user_id\" IS NULL OR \"pending_authorizations\".\"user_id\" = \"pending_authorizations\".\"actor_id\""
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.plugin_grants": {
+ "name": "plugin_grants",
+ "schema": "",
+ "columns": {
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ref": {
+ "name": "ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "granted_by": {
+ "name": "granted_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "granted_as": {
+ "name": "granted_as",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "plugin_grants_agent_idx": {
+ "name": "plugin_grants_agent_idx",
+ "columns": [
+ {
+ "expression": "agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "plugin_grants_agent_id_agents_id_fk": {
+ "name": "plugin_grants_agent_id_agents_id_fk",
+ "tableFrom": "plugin_grants",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "plugin_grants_kind_ref_agent_id_pk": {
+ "name": "plugin_grants_kind_ref_agent_id_pk",
+ "columns": [
+ "kind",
+ "ref",
+ "agent_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "plugin_grants_granted_as_check": {
+ "name": "plugin_grants_granted_as_check",
+ "value": "\"plugin_grants\".\"granted_as\" IS NULL OR \"plugin_grants\".\"granted_as\" IN ('admin', 'owner')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.sandboxed_components": {
+ "name": "sandboxed_components",
+ "schema": "",
+ "columns": {
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "draft_description": {
+ "name": "draft_description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "draft_html": {
+ "name": "draft_html",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "draft_css": {
+ "name": "draft_css",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "draft_js_functions": {
+ "name": "draft_js_functions",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "draft_argument_schema": {
+ "name": "draft_argument_schema",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "published_description": {
+ "name": "published_description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "published_html": {
+ "name": "published_html",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "published_css": {
+ "name": "published_css",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "published_js_functions": {
+ "name": "published_js_functions",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "published_argument_schema": {
+ "name": "published_argument_schema",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sample_arguments": {
+ "name": "sample_arguments",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "revision": {
+ "name": "revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "published": {
+ "name": "published",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "published_at": {
+ "name": "published_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "authored_by": {
+ "name": "authored_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.skill_tools": {
+ "name": "skill_tools",
+ "schema": "",
+ "columns": {
+ "skill_id": {
+ "name": "skill_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ref": {
+ "name": "ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "declared_by": {
+ "name": "declared_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "skill_tools_ref_idx": {
+ "name": "skill_tools_ref_idx",
+ "columns": [
+ {
+ "expression": "ref",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "skill_tools_skill_id_skills_id_fk": {
+ "name": "skill_tools_skill_id_skills_id_fk",
+ "tableFrom": "skill_tools",
+ "tableTo": "skills",
+ "columnsFrom": [
+ "skill_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "skill_tools_skill_id_ref_pk": {
+ "name": "skill_tools_skill_id_ref_pk",
+ "columns": [
+ "skill_id",
+ "ref"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.skills": {
+ "name": "skills",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "summary": {
+ "name": "summary",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "instructions": {
+ "name": "instructions",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "origin": {
+ "name": "origin",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'yours'"
+ },
+ "installed_by": {
+ "name": "installed_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "skills_slug_key": {
+ "name": "skills_slug_key",
+ "columns": [
+ {
+ "expression": "slug",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "skills_owner_idx": {
+ "name": "skills_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "skills_owner_user_id_users_id_fk": {
+ "name": "skills_owner_user_id_users_id_fk",
+ "tableFrom": "skills",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.work_items": {
+ "name": "work_items",
+ "schema": "",
+ "columns": {
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "run_at": {
+ "name": "run_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "claimed_by": {
+ "name": "claimed_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lease_until": {
+ "name": "lease_until",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "attempts": {
+ "name": "attempts",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "finished_at": {
+ "name": "finished_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "payload": {
+ "name": "payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "work_items_claimable_idx": {
+ "name": "work_items_claimable_idx",
+ "columns": [
+ {
+ "expression": "kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "run_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "work_items_open_thread_idx": {
+ "name": "work_items_open_thread_idx",
+ "columns": [
+ {
+ "expression": "kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "(\"payload\"->>'threadId')",
+ "asc": true,
+ "isExpression": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"work_items\".\"finished_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "work_items_kind_key_pk": {
+ "name": "work_items_kind_key_pk",
+ "columns": [
+ "kind",
+ "key"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.voice_sessions": {
+ "name": "voice_sessions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "anchor_message_id": {
+ "name": "anchor_message_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ended_at": {
+ "name": "ended_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "duration_seconds": {
+ "name": "duration_seconds",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transcript": {
+ "name": "transcript",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "summary": {
+ "name": "summary",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "summary_status": {
+ "name": "summary_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'failed'"
+ }
+ },
+ "indexes": {
+ "voice_sessions_channel_started_idx": {
+ "name": "voice_sessions_channel_started_idx",
+ "columns": [
+ {
+ "expression": "channel_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "started_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "voice_sessions_channel_id_channels_id_fk": {
+ "name": "voice_sessions_channel_id_channels_id_fk",
+ "tableFrom": "voice_sessions",
+ "tableTo": "channels",
+ "columnsFrom": [
+ "channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "voice_sessions_user_id_users_id_fk": {
+ "name": "voice_sessions_user_id_users_id_fk",
+ "tableFrom": "voice_sessions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "voice_sessions_duration_check": {
+ "name": "voice_sessions_duration_check",
+ "value": "\"voice_sessions\".\"duration_seconds\" >= 0"
+ },
+ "voice_sessions_summary_check": {
+ "name": "voice_sessions_summary_check",
+ "value": "(\"voice_sessions\".\"summary_status\" = 'ready' AND \"voice_sessions\".\"summary\" IS NOT NULL) OR (\"voice_sessions\".\"summary_status\" = 'failed' AND \"voice_sessions\".\"summary\" IS NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.learning_open_skills": {
+ "name": "learning_open_skills",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "skill_name": {
+ "name": "skill_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "learning_open_skills_user_id_users_id_fk": {
+ "name": "learning_open_skills_user_id_users_id_fk",
+ "tableFrom": "learning_open_skills",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "learning_open_skills_user_id_skill_name_pk": {
+ "name": "learning_open_skills_user_id_skill_name_pk",
+ "columns": [
+ "user_id",
+ "skill_name"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.learning_person_spaces": {
+ "name": "learning_person_spaces",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "container_id": {
+ "name": "container_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "learning_person_spaces_user_id_users_id_fk": {
+ "name": "learning_person_spaces_user_id_users_id_fk",
+ "tableFrom": "learning_person_spaces",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "learning_person_spaces_container_id_unique": {
+ "name": "learning_person_spaces_container_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "container_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.learning_settings": {
+ "name": "learning_settings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "settings": {
+ "name": "settings",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.learning_skill_approvals": {
+ "name": "learning_skill_approvals",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "skill_name": {
+ "name": "skill_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "approved_by": {
+ "name": "approved_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sharing": {
+ "name": "sharing",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'none'"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "learning_skill_approvals_user_id_users_id_fk": {
+ "name": "learning_skill_approvals_user_id_users_id_fk",
+ "tableFrom": "learning_skill_approvals",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "learning_skill_approvals_user_id_skill_name_pk": {
+ "name": "learning_skill_approvals_user_id_skill_name_pk",
+ "columns": [
+ "user_id",
+ "skill_name"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "learning_skill_approvals_approved_by_check": {
+ "name": "learning_skill_approvals_approved_by_check",
+ "value": "\"learning_skill_approvals\".\"approved_by\" IN ('person', 'admin')"
+ },
+ "learning_skill_approvals_sharing_check": {
+ "name": "learning_skill_approvals_sharing_check",
+ "value": "\"learning_skill_approvals\".\"sharing\" IN ('none', 'requested', 'approved', 'declined')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.learning_skill_sharing": {
+ "name": "learning_skill_sharing",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "mode": {
+ "name": "mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'closed'"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "learning_skill_sharing_open_idx": {
+ "name": "learning_skill_sharing_open_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"learning_skill_sharing\".\"mode\" <> 'closed'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "learning_skill_sharing_user_id_users_id_fk": {
+ "name": "learning_skill_sharing_user_id_users_id_fk",
+ "tableFrom": "learning_skill_sharing",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "learning_skill_sharing_mode_check": {
+ "name": "learning_skill_sharing_mode_check",
+ "value": "\"learning_skill_sharing\".\"mode\" IN ('closed', 'open', 'choose')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.learning_thread_bindings": {
+ "name": "learning_thread_bindings",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "container_id": {
+ "name": "container_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "learning_thread_bindings_user_id_thread_id_pk": {
+ "name": "learning_thread_bindings_user_id_thread_id_pk",
+ "columns": [
+ "user_id",
+ "thread_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.responsibilities": {
+ "name": "responsibilities",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "instruction": {
+ "name": "instruction",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "success_criteria": {
+ "name": "success_criteria",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "responsibility_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'active'"
+ },
+ "progress": {
+ "name": "progress",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "last_result": {
+ "name": "last_result",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "subscriptions": {
+ "name": "subscriptions",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "recurring": {
+ "name": "recurring",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "responsibilities_owner_status_idx": {
+ "name": "responsibilities_owner_status_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "responsibilities_owner_user_id_users_id_fk": {
+ "name": "responsibilities_owner_user_id_users_id_fk",
+ "tableFrom": "responsibilities",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "responsibilities_agent_id_agents_id_fk": {
+ "name": "responsibilities_agent_id_agents_id_fk",
+ "tableFrom": "responsibilities",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.responsibility_events": {
+ "name": "responsibility_events",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source": {
+ "name": "source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "payload": {
+ "name": "payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "responsibility_events_source_id_idx": {
+ "name": "responsibility_events_source_id_idx",
+ "columns": [
+ {
+ "expression": "owner_user_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": {}
+ }
+ },
+ "foreignKeys": {
+ "responsibility_events_owner_user_id_users_id_fk": {
+ "name": "responsibility_events_owner_user_id_users_id_fk",
+ "tableFrom": "responsibility_events",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.responsibility_runs": {
+ "name": "responsibility_runs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "responsibility_id": {
+ "name": "responsibility_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_id": {
+ "name": "event_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "responsibility_run_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'queued'"
+ },
+ "reply_text": {
+ "name": "reply_text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error": {
+ "name": "error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "waiting": {
+ "name": "waiting",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "continuation": {
+ "name": "continuation",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "finished_at": {
+ "name": "finished_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "responsibility_runs_event_idx": {
+ "name": "responsibility_runs_event_idx",
+ "columns": [
+ {
+ "expression": "responsibility_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "responsibility_runs_status_idx": {
+ "name": "responsibility_runs_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "responsibility_runs_responsibility_id_responsibilities_id_fk": {
+ "name": "responsibility_runs_responsibility_id_responsibilities_id_fk",
+ "tableFrom": "responsibility_runs",
+ "tableTo": "responsibilities",
+ "columnsFrom": [
+ "responsibility_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "responsibility_runs_event_id_responsibility_events_id_fk": {
+ "name": "responsibility_runs_event_id_responsibility_events_id_fk",
+ "tableFrom": "responsibility_runs",
+ "tableTo": "responsibility_events",
+ "columnsFrom": [
+ "event_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.responsibility_source_bindings": {
+ "name": "responsibility_source_bindings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source": {
+ "name": "source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "repository": {
+ "name": "repository",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "credential_id": {
+ "name": "credential_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "responsibility_source_bindings_owner_idx": {
+ "name": "responsibility_source_bindings_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "responsibility_source_bindings_owner_user_id_users_id_fk": {
+ "name": "responsibility_source_bindings_owner_user_id_users_id_fk",
+ "tableFrom": "responsibility_source_bindings",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "responsibility_source_bindings_credential_id_credentials_id_fk": {
+ "name": "responsibility_source_bindings_credential_id_credentials_id_fk",
+ "tableFrom": "responsibility_source_bindings",
+ "tableTo": "credentials",
+ "columnsFrom": [
+ "credential_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.responsibility_triggers": {
+ "name": "responsibility_triggers",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "responsibility_id": {
+ "name": "responsibility_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "credential_id": {
+ "name": "credential_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "responsibility_triggers_owner_idx": {
+ "name": "responsibility_triggers_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "responsibility_triggers_responsibility_idx": {
+ "name": "responsibility_triggers_responsibility_idx",
+ "columns": [
+ {
+ "expression": "responsibility_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "responsibility_triggers_kind_idx": {
+ "name": "responsibility_triggers_kind_idx",
+ "columns": [
+ {
+ "expression": "kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "responsibility_triggers_owner_user_id_users_id_fk": {
+ "name": "responsibility_triggers_owner_user_id_users_id_fk",
+ "tableFrom": "responsibility_triggers",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "responsibility_triggers_responsibility_id_responsibilities_id_fk": {
+ "name": "responsibility_triggers_responsibility_id_responsibilities_id_fk",
+ "tableFrom": "responsibility_triggers",
+ "tableTo": "responsibilities",
+ "columnsFrom": [
+ "responsibility_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "responsibility_triggers_credential_id_credentials_id_fk": {
+ "name": "responsibility_triggers_credential_id_credentials_id_fk",
+ "tableFrom": "responsibility_triggers",
+ "tableTo": "credentials",
+ "columnsFrom": [
+ "credential_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.approval_preferences": {
+ "name": "approval_preferences",
+ "schema": "",
+ "columns": {
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "auto_review": {
+ "name": "auto_review",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "host_commands": {
+ "name": "host_commands",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'ask'"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "approval_preferences_owner_user_id_users_id_fk": {
+ "name": "approval_preferences_owner_user_id_users_id_fk",
+ "tableFrom": "approval_preferences",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "approval_preferences_host_commands_check": {
+ "name": "approval_preferences_host_commands_check",
+ "value": "\"approval_preferences\".\"host_commands\" IN ('ask', 'allow', 'never')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.approval_requests": {
+ "name": "approval_requests",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "run_id": {
+ "name": "run_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "tool_call_id": {
+ "name": "tool_call_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action_digest": {
+ "name": "action_digest",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action": {
+ "name": "action",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "decision": {
+ "name": "decision",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "result": {
+ "name": "result",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "decided_at": {
+ "name": "decided_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "consumed_at": {
+ "name": "consumed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "approval_requests_action_idx": {
+ "name": "approval_requests_action_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "run_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "tool_call_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "approval_requests_inbox_idx": {
+ "name": "approval_requests_inbox_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "approval_requests_owner_user_id_users_id_fk": {
+ "name": "approval_requests_owner_user_id_users_id_fk",
+ "tableFrom": "approval_requests",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "approval_status_check": {
+ "name": "approval_status_check",
+ "value": "\"approval_requests\".\"status\" IN ('pending', 'approved', 'denied', 'consumed', 'completed')"
+ },
+ "approval_decision_check": {
+ "name": "approval_decision_check",
+ "value": "\"approval_requests\".\"decision\" IN ('allow_once', 'allow_always', 'deny', 'handled')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.approval_rules": {
+ "name": "approval_rules",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "bot_id": {
+ "name": "bot_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "tool_ref": {
+ "name": "tool_ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "effect": {
+ "name": "effect",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "behaviour": {
+ "name": "behaviour",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'allow'"
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "approval_rules_owner_idx": {
+ "name": "approval_rules_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "approval_rules_owner_user_id_users_id_fk": {
+ "name": "approval_rules_owner_user_id_users_id_fk",
+ "tableFrom": "approval_rules",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "approval_rules_behaviour_check": {
+ "name": "approval_rules_behaviour_check",
+ "value": "\"approval_rules\".\"behaviour\" IN ('allow', 'pre_approved', 'ask', 'hand_off')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.approval_team_rules": {
+ "name": "approval_team_rules",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "bot_id": {
+ "name": "bot_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "tool_ref": {
+ "name": "tool_ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "effect": {
+ "name": "effect",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "behaviour": {
+ "name": "behaviour",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "approval_team_rules_created_by_users_id_fk": {
+ "name": "approval_team_rules_created_by_users_id_fk",
+ "tableFrom": "approval_team_rules",
+ "tableTo": "users",
+ "columnsFrom": [
+ "created_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "approval_team_rules_behaviour_check": {
+ "name": "approval_team_rules_behaviour_check",
+ "value": "\"approval_team_rules\".\"behaviour\" IN ('allow', 'pre_approved', 'ask', 'hand_off')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.approval_team_settings": {
+ "name": "approval_team_settings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "enforce_auto_review": {
+ "name": "enforce_auto_review",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "custom_rules_enabled": {
+ "name": "custom_rules_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "host_commands_cap": {
+ "name": "host_commands_cap",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'allow'"
+ },
+ "updated_by": {
+ "name": "updated_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "approval_team_settings_updated_by_users_id_fk": {
+ "name": "approval_team_settings_updated_by_users_id_fk",
+ "tableFrom": "approval_team_settings",
+ "tableTo": "users",
+ "columnsFrom": [
+ "updated_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "approval_team_settings_host_commands_cap_check": {
+ "name": "approval_team_settings_host_commands_cap_check",
+ "value": "\"approval_team_settings\".\"host_commands_cap\" IN ('ask', 'allow', 'never')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.memory_sources": {
+ "name": "memory_sources",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "tool_ref": {
+ "name": "tool_ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "args": {
+ "name": "args",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "sync_status": {
+ "name": "sync_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'idle'"
+ },
+ "sync_error": {
+ "name": "sync_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_sync_at": {
+ "name": "last_sync_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "next_sync_at": {
+ "name": "next_sync_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "memory_sources_owner_idx": {
+ "name": "memory_sources_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "memory_sources_due_idx": {
+ "name": "memory_sources_due_idx",
+ "columns": [
+ {
+ "expression": "enabled",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "next_sync_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "memory_sources_owner_user_id_users_id_fk": {
+ "name": "memory_sources_owner_user_id_users_id_fk",
+ "tableFrom": "memory_sources",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "memory_sources_agent_id_agents_id_fk": {
+ "name": "memory_sources_agent_id_agents_id_fk",
+ "tableFrom": "memory_sources",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.personal_memories": {
+ "name": "personal_memories",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source_id": {
+ "name": "source_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "import_digest": {
+ "name": "import_digest",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provenance": {
+ "name": "provenance",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "review_state": {
+ "name": "review_state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'unreviewed'"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "formed_by": {
+ "name": "formed_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'person'"
+ },
+ "formed_by_agent_id": {
+ "name": "formed_by_agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_app": {
+ "name": "source_app",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_ref": {
+ "name": "source_ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_link": {
+ "name": "source_link",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "observed_at": {
+ "name": "observed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "intelligence_memory_id": {
+ "name": "intelligence_memory_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "intelligence_digest": {
+ "name": "intelligence_digest",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "personal_memories_owner_idx": {
+ "name": "personal_memories_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "enabled",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "personal_memories_intelligence_idx": {
+ "name": "personal_memories_intelligence_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "intelligence_memory_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "personal_memories_formed_idx": {
+ "name": "personal_memories_formed_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "formed_by_agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "personal_memories_import_idx": {
+ "name": "personal_memories_import_idx",
+ "columns": [
+ {
+ "expression": "source_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "external_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "personal_memories_formed_digest_per_bot_idx": {
+ "name": "personal_memories_formed_digest_per_bot_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "formed_by_agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "import_digest",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"personal_memories\".\"formed_by\" = 'bot'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "personal_memories_owner_user_id_users_id_fk": {
+ "name": "personal_memories_owner_user_id_users_id_fk",
+ "tableFrom": "personal_memories",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "personal_memories_source_id_memory_sources_id_fk": {
+ "name": "personal_memories_source_id_memory_sources_id_fk",
+ "tableFrom": "personal_memories",
+ "tableTo": "memory_sources",
+ "columnsFrom": [
+ "source_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "personal_memories_formed_by_agent_id_agents_id_fk": {
+ "name": "personal_memories_formed_by_agent_id_agents_id_fk",
+ "tableFrom": "personal_memories",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "formed_by_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.default_opt_outs": {
+ "name": "default_opt_outs",
+ "schema": "",
+ "columns": {
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "feature": {
+ "name": "feature",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "default_opt_outs_owner_user_id_users_id_fk": {
+ "name": "default_opt_outs_owner_user_id_users_id_fk",
+ "tableFrom": "default_opt_outs",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "default_opt_outs_agent_id_agents_id_fk": {
+ "name": "default_opt_outs_agent_id_agents_id_fk",
+ "tableFrom": "default_opt_outs",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "default_opt_outs_owner_user_id_agent_id_feature_scope_pk": {
+ "name": "default_opt_outs_owner_user_id_agent_id_feature_scope_pk",
+ "columns": [
+ "owner_user_id",
+ "agent_id",
+ "feature",
+ "scope"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "default_opt_outs_feature_check": {
+ "name": "default_opt_outs_feature_check",
+ "value": "\"default_opt_outs\".\"feature\" IN ('memory_source', 'proactive_research')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.proactive_people": {
+ "name": "proactive_people",
+ "schema": "",
+ "columns": {
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "last_pinged_at": {
+ "name": "last_pinged_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "proactive_people_owner_user_id_users_id_fk": {
+ "name": "proactive_people_owner_user_id_users_id_fk",
+ "tableFrom": "proactive_people",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.proactive_pings": {
+ "name": "proactive_pings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "item_count": {
+ "name": "item_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "text": {
+ "name": "text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "push_body": {
+ "name": "push_body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "offered_at": {
+ "name": "offered_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "proactive_pings_owner_created_idx": {
+ "name": "proactive_pings_owner_created_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "proactive_pings_unoffered_idx": {
+ "name": "proactive_pings_unoffered_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"proactive_pings\".\"offered_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "proactive_pings_owner_user_id_users_id_fk": {
+ "name": "proactive_pings_owner_user_id_users_id_fk",
+ "tableFrom": "proactive_pings",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "proactive_pings_item_count_check": {
+ "name": "proactive_pings_item_count_check",
+ "value": "\"proactive_pings\".\"item_count\" > 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.proactive_runs": {
+ "name": "proactive_runs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "setting_id": {
+ "name": "setting_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": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "trigger": {
+ "name": "trigger",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "outcome": {
+ "name": "outcome",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'running'"
+ },
+ "skip_reason": {
+ "name": "skip_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "items_raised": {
+ "name": "items_raised",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "reads": {
+ "name": "reads",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "error": {
+ "name": "error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "finished_at": {
+ "name": "finished_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "proactive_runs_setting_started_idx": {
+ "name": "proactive_runs_setting_started_idx",
+ "columns": [
+ {
+ "expression": "setting_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "started_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "proactive_runs_owner_started_idx": {
+ "name": "proactive_runs_owner_started_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "started_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "proactive_runs_agent_started_idx": {
+ "name": "proactive_runs_agent_started_idx",
+ "columns": [
+ {
+ "expression": "agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "started_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "proactive_runs_one_running_idx": {
+ "name": "proactive_runs_one_running_idx",
+ "columns": [
+ {
+ "expression": "setting_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"proactive_runs\".\"outcome\" = 'running'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "proactive_runs_setting_id_proactive_settings_id_fk": {
+ "name": "proactive_runs_setting_id_proactive_settings_id_fk",
+ "tableFrom": "proactive_runs",
+ "tableTo": "proactive_settings",
+ "columnsFrom": [
+ "setting_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "proactive_runs_owner_user_id_users_id_fk": {
+ "name": "proactive_runs_owner_user_id_users_id_fk",
+ "tableFrom": "proactive_runs",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "proactive_runs_agent_id_agents_id_fk": {
+ "name": "proactive_runs_agent_id_agents_id_fk",
+ "tableFrom": "proactive_runs",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "proactive_runs_trigger_check": {
+ "name": "proactive_runs_trigger_check",
+ "value": "\"proactive_runs\".\"trigger\" IN ('schedule', 'run_now')"
+ },
+ "proactive_runs_outcome_check": {
+ "name": "proactive_runs_outcome_check",
+ "value": "\"proactive_runs\".\"outcome\" IN ('running', 'quiet', 'raised', 'failed', 'skipped')"
+ },
+ "proactive_runs_skip_reason_check": {
+ "name": "proactive_runs_skip_reason_check",
+ "value": "(\"proactive_runs\".\"outcome\" = 'skipped' AND \"proactive_runs\".\"skip_reason\" IS NOT NULL AND \"proactive_runs\".\"skip_reason\" IN ('off_admin', 'off_person', 'paused', 'capped', 'unreachable', 'no_sources', 'busy')) OR (\"proactive_runs\".\"outcome\" <> 'skipped' AND \"proactive_runs\".\"skip_reason\" IS NULL)"
+ },
+ "proactive_runs_finished_check": {
+ "name": "proactive_runs_finished_check",
+ "value": "(\"proactive_runs\".\"outcome\" = 'running') = (\"proactive_runs\".\"finished_at\" IS NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.proactive_settings": {
+ "name": "proactive_settings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "focus": {
+ "name": "focus",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "subject": {
+ "name": "subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "source_link": {
+ "name": "source_link",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "interval_minutes": {
+ "name": "interval_minutes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 60
+ },
+ "next_run_at": {
+ "name": "next_run_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "last_run_at": {
+ "name": "last_run_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_status": {
+ "name": "last_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'idle'"
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "proactive_settings_owner_agent_idx": {
+ "name": "proactive_settings_owner_agent_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "proactive_settings_due_idx": {
+ "name": "proactive_settings_due_idx",
+ "columns": [
+ {
+ "expression": "enabled",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "next_run_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "proactive_settings_owner_user_id_users_id_fk": {
+ "name": "proactive_settings_owner_user_id_users_id_fk",
+ "tableFrom": "proactive_settings",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "proactive_settings_agent_id_agents_id_fk": {
+ "name": "proactive_settings_agent_id_agents_id_fk",
+ "tableFrom": "proactive_settings",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "proactive_settings_interval_check": {
+ "name": "proactive_settings_interval_check",
+ "value": "\"proactive_settings\".\"interval_minutes\" >= 60 AND \"proactive_settings\".\"interval_minutes\" <= 1440"
+ },
+ "proactive_settings_status_check": {
+ "name": "proactive_settings_status_check",
+ "value": "\"proactive_settings\".\"last_status\" IN ('idle', 'running', 'succeeded', 'error')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.proactive_suggestions": {
+ "name": "proactive_suggestions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "setting_id": {
+ "name": "setting_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "run_id": {
+ "name": "run_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "detail": {
+ "name": "detail",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source_app": {
+ "name": "source_app",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_ref": {
+ "name": "source_ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_link": {
+ "name": "source_link",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'open'"
+ },
+ "dedupe_key": {
+ "name": "dedupe_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ping_id": {
+ "name": "ping_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "delivered_at": {
+ "name": "delivered_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resolved_at": {
+ "name": "resolved_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "proactive_suggestions_owner_idx": {
+ "name": "proactive_suggestions_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "proactive_suggestions_run_idx": {
+ "name": "proactive_suggestions_run_idx",
+ "columns": [
+ {
+ "expression": "run_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "proactive_suggestions_open_dedupe_idx": {
+ "name": "proactive_suggestions_open_dedupe_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "dedupe_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"proactive_suggestions\".\"status\" = 'open' AND \"proactive_suggestions\".\"dedupe_key\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "proactive_suggestions_unpinged_idx": {
+ "name": "proactive_suggestions_unpinged_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"proactive_suggestions\".\"status\" = 'open' AND \"proactive_suggestions\".\"ping_id\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "proactive_suggestions_owner_user_id_users_id_fk": {
+ "name": "proactive_suggestions_owner_user_id_users_id_fk",
+ "tableFrom": "proactive_suggestions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "proactive_suggestions_agent_id_agents_id_fk": {
+ "name": "proactive_suggestions_agent_id_agents_id_fk",
+ "tableFrom": "proactive_suggestions",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "proactive_suggestions_setting_id_proactive_settings_id_fk": {
+ "name": "proactive_suggestions_setting_id_proactive_settings_id_fk",
+ "tableFrom": "proactive_suggestions",
+ "tableTo": "proactive_settings",
+ "columnsFrom": [
+ "setting_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "proactive_suggestions_ping_id_proactive_pings_id_fk": {
+ "name": "proactive_suggestions_ping_id_proactive_pings_id_fk",
+ "tableFrom": "proactive_suggestions",
+ "tableTo": "proactive_pings",
+ "columnsFrom": [
+ "ping_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "proactive_suggestions_status_check": {
+ "name": "proactive_suggestions_status_check",
+ "value": "\"proactive_suggestions\".\"status\" IN ('open', 'dismissed', 'started', 'superseded')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.bot_tasks": {
+ "name": "bot_tasks",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "suggestion_id": {
+ "name": "suggestion_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "why": {
+ "name": "why",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_app": {
+ "name": "source_app",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_link": {
+ "name": "source_link",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "origin": {
+ "name": "origin",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "resolved_at": {
+ "name": "resolved_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "bot_tasks_owner_created_idx": {
+ "name": "bot_tasks_owner_created_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "bot_tasks_channel_idx": {
+ "name": "bot_tasks_channel_idx",
+ "columns": [
+ {
+ "expression": "channel_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "bot_tasks_suggestion_idx": {
+ "name": "bot_tasks_suggestion_idx",
+ "columns": [
+ {
+ "expression": "suggestion_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "bot_tasks_owner_user_id_users_id_fk": {
+ "name": "bot_tasks_owner_user_id_users_id_fk",
+ "tableFrom": "bot_tasks",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "bot_tasks_agent_id_agents_id_fk": {
+ "name": "bot_tasks_agent_id_agents_id_fk",
+ "tableFrom": "bot_tasks",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "bot_tasks_channel_id_channels_id_fk": {
+ "name": "bot_tasks_channel_id_channels_id_fk",
+ "tableFrom": "bot_tasks",
+ "tableTo": "channels",
+ "columnsFrom": [
+ "channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "bot_tasks_suggestion_id_proactive_suggestions_id_fk": {
+ "name": "bot_tasks_suggestion_id_proactive_suggestions_id_fk",
+ "tableFrom": "bot_tasks",
+ "tableTo": "proactive_suggestions",
+ "columnsFrom": [
+ "suggestion_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "bot_tasks_origin_check": {
+ "name": "bot_tasks_origin_check",
+ "value": "\"bot_tasks\".\"origin\" IN ('check_in')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.demonstrations": {
+ "name": "demonstrations",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "bot_id": {
+ "name": "bot_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'recording'"
+ },
+ "actions": {
+ "name": "actions",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "draft": {
+ "name": "draft",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "skill_slug": {
+ "name": "skill_slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "finished_at": {
+ "name": "finished_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "demonstrations_owner_idx": {
+ "name": "demonstrations_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "demonstrations_active_bot_idx": {
+ "name": "demonstrations_active_bot_idx",
+ "columns": [
+ {
+ "expression": "bot_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"demonstrations\".\"status\" = 'recording'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "demonstrations_owner_user_id_users_id_fk": {
+ "name": "demonstrations_owner_user_id_users_id_fk",
+ "tableFrom": "demonstrations",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "demonstrations_bot_id_agents_id_fk": {
+ "name": "demonstrations_bot_id_agents_id_fk",
+ "tableFrom": "demonstrations",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "bot_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.delivery_bindings": {
+ "name": "delivery_bindings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transport": {
+ "name": "transport",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "identity": {
+ "name": "identity",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "realm": {
+ "name": "realm",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "address": {
+ "name": "address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "mention_id": {
+ "name": "mention_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "opted_out_at": {
+ "name": "opted_out_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "delivery_binding_identity_idx": {
+ "name": "delivery_binding_identity_idx",
+ "columns": [
+ {
+ "expression": "transport",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "realm",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "identity",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "delivery_bindings_owner_idx": {
+ "name": "delivery_bindings_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "delivery_bindings_owner_user_id_users_id_fk": {
+ "name": "delivery_bindings_owner_user_id_users_id_fk",
+ "tableFrom": "delivery_bindings",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "delivery_bindings_channel_id_channels_id_fk": {
+ "name": "delivery_bindings_channel_id_channels_id_fk",
+ "tableFrom": "delivery_bindings",
+ "tableTo": "channels",
+ "columnsFrom": [
+ "channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "delivery_bindings_agent_id_agents_id_fk": {
+ "name": "delivery_bindings_agent_id_agents_id_fk",
+ "tableFrom": "delivery_bindings",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.delivery_challenges": {
+ "name": "delivery_challenges",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transport": {
+ "name": "transport",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "address": {
+ "name": "address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "consumed_at": {
+ "name": "consumed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "delivery_challenges_owner_user_id_users_id_fk": {
+ "name": "delivery_challenges_owner_user_id_users_id_fk",
+ "tableFrom": "delivery_challenges",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "delivery_challenges_channel_id_channels_id_fk": {
+ "name": "delivery_challenges_channel_id_channels_id_fk",
+ "tableFrom": "delivery_challenges",
+ "tableTo": "channels",
+ "columnsFrom": [
+ "channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "delivery_challenges_agent_id_agents_id_fk": {
+ "name": "delivery_challenges_agent_id_agents_id_fk",
+ "tableFrom": "delivery_challenges",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.delivery_inbox": {
+ "name": "delivery_inbox",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "binding_id": {
+ "name": "binding_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source": {
+ "name": "source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "realm": {
+ "name": "realm",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "text": {
+ "name": "text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "state": {
+ "name": "state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'queued'"
+ },
+ "error": {
+ "name": "error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "delivery_inbox_dedupe_idx": {
+ "name": "delivery_inbox_dedupe_idx",
+ "columns": [
+ {
+ "expression": "source",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "realm",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "external_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "delivery_inbox_owner_idx": {
+ "name": "delivery_inbox_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_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": {
+ "delivery_inbox_owner_user_id_users_id_fk": {
+ "name": "delivery_inbox_owner_user_id_users_id_fk",
+ "tableFrom": "delivery_inbox",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "delivery_inbox_channel_id_channels_id_fk": {
+ "name": "delivery_inbox_channel_id_channels_id_fk",
+ "tableFrom": "delivery_inbox",
+ "tableTo": "channels",
+ "columnsFrom": [
+ "channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "delivery_inbox_agent_id_agents_id_fk": {
+ "name": "delivery_inbox_agent_id_agents_id_fk",
+ "tableFrom": "delivery_inbox",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.delivery_outbox": {
+ "name": "delivery_outbox",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "dedupe_key": {
+ "name": "dedupe_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "binding_id": {
+ "name": "binding_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "device_id": {
+ "name": "device_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "transport": {
+ "name": "transport",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "text": {
+ "name": "text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "request_id": {
+ "name": "request_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "state": {
+ "name": "state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'queued'"
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error": {
+ "name": "error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "delivery_outbox_owner_idx": {
+ "name": "delivery_outbox_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "delivery_outbox_provider_idx": {
+ "name": "delivery_outbox_provider_idx",
+ "columns": [
+ {
+ "expression": "transport",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "provider_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "delivery_outbox_owner_user_id_users_id_fk": {
+ "name": "delivery_outbox_owner_user_id_users_id_fk",
+ "tableFrom": "delivery_outbox",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "delivery_outbox_channel_id_channels_id_fk": {
+ "name": "delivery_outbox_channel_id_channels_id_fk",
+ "tableFrom": "delivery_outbox",
+ "tableTo": "channels",
+ "columnsFrom": [
+ "channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "delivery_outbox_agent_id_agents_id_fk": {
+ "name": "delivery_outbox_agent_id_agents_id_fk",
+ "tableFrom": "delivery_outbox",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "delivery_outbox_dedupe_key_unique": {
+ "name": "delivery_outbox_dedupe_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "dedupe_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.push_devices": {
+ "name": "push_devices",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "platform": {
+ "name": "platform",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "push_devices_owner_idx": {
+ "name": "push_devices_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "push_devices_owner_user_id_users_id_fk": {
+ "name": "push_devices_owner_user_id_users_id_fk",
+ "tableFrom": "push_devices",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "push_devices_token_unique": {
+ "name": "push_devices_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.group_bot_threads": {
+ "name": "group_bot_threads",
+ "schema": "",
+ "columns": {
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "group_bot_threads_thread_idx": {
+ "name": "group_bot_threads_thread_idx",
+ "columns": [
+ {
+ "expression": "thread_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "group_bot_threads_owner_user_id_users_id_fk": {
+ "name": "group_bot_threads_owner_user_id_users_id_fk",
+ "tableFrom": "group_bot_threads",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "group_bot_threads_channel_id_channels_id_fk": {
+ "name": "group_bot_threads_channel_id_channels_id_fk",
+ "tableFrom": "group_bot_threads",
+ "tableTo": "channels",
+ "columnsFrom": [
+ "channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "group_bot_threads_owner_user_id_channel_id_agent_id_pk": {
+ "name": "group_bot_threads_owner_user_id_channel_id_agent_id_pk",
+ "columns": [
+ "owner_user_id",
+ "channel_id",
+ "agent_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.group_messages": {
+ "name": "group_messages",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_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": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "text": {
+ "name": "text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'completed'"
+ },
+ "details": {
+ "name": "details",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "group_messages_channel_idx": {
+ "name": "group_messages_channel_idx",
+ "columns": [
+ {
+ "expression": "channel_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": {
+ "group_messages_channel_id_channels_id_fk": {
+ "name": "group_messages_channel_id_channels_id_fk",
+ "tableFrom": "group_messages",
+ "tableTo": "channels",
+ "columnsFrom": [
+ "channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "group_messages_owner_user_id_users_id_fk": {
+ "name": "group_messages_owner_user_id_users_id_fk",
+ "tableFrom": "group_messages",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "group_messages_status_check": {
+ "name": "group_messages_status_check",
+ "value": "\"group_messages\".\"status\" IN ('queued', 'running', 'waiting', 'completed', 'failed')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.action_records": {
+ "name": "action_records",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "surface": {
+ "name": "surface",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "bot_id": {
+ "name": "bot_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "actor_user_id": {
+ "name": "actor_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "tool_name": {
+ "name": "tool_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "command": {
+ "name": "command",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "outcome": {
+ "name": "outcome",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "action_records_created_idx": {
+ "name": "action_records_created_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "action_records_bot_idx": {
+ "name": "action_records_bot_idx",
+ "columns": [
+ {
+ "expression": "bot_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": {},
+ "policies": {},
+ "checkConstraints": {
+ "action_records_surface_check": {
+ "name": "action_records_surface_check",
+ "value": "\"action_records\".\"surface\" IN ('cloud', 'local')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.capability_settings": {
+ "name": "capability_settings",
+ "schema": "",
+ "columns": {
+ "scope_kind": {
+ "name": "scope_kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope_id": {
+ "name": "scope_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "capability": {
+ "name": "capability",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "allowed": {
+ "name": "allowed",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_by": {
+ "name": "updated_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "capability_settings_scope_kind_scope_id_capability_pk": {
+ "name": "capability_settings_scope_kind_scope_id_capability_pk",
+ "columns": [
+ "scope_kind",
+ "scope_id",
+ "capability"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "capability_settings_scope_kind_check": {
+ "name": "capability_settings_scope_kind_check",
+ "value": "\"capability_settings\".\"scope_kind\" IN ('organization', 'role', 'group')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.enterprise_settings": {
+ "name": "enterprise_settings",
+ "schema": "",
+ "columns": {
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_by": {
+ "name": "updated_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.model_usage": {
+ "name": "model_usage",
+ "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": false
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "run_id": {
+ "name": "run_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source": {
+ "name": "source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "allowed": {
+ "name": "allowed",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "model_usage_created_idx": {
+ "name": "model_usage_created_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "model_usage_model_idx": {
+ "name": "model_usage_model_idx",
+ "columns": [
+ {
+ "expression": "model",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "model_usage_source_check": {
+ "name": "model_usage_source_check",
+ "value": "\"model_usage\".\"source\" IN ('configured', 'observed', 'unknown')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.network_policies": {
+ "name": "network_policies",
+ "schema": "",
+ "columns": {
+ "scope_kind": {
+ "name": "scope_kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope_id": {
+ "name": "scope_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "mode": {
+ "name": "mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "rules": {
+ "name": "rules",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{\"entries\":[]}'::jsonb"
+ },
+ "locked": {
+ "name": "locked",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "updated_by": {
+ "name": "updated_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "network_policies_scope_kind_scope_id_pk": {
+ "name": "network_policies_scope_kind_scope_id_pk",
+ "columns": [
+ "scope_kind",
+ "scope_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "network_policies_scope_kind_check": {
+ "name": "network_policies_scope_kind_check",
+ "value": "\"network_policies\".\"scope_kind\" IN ('organization', 'group')"
+ },
+ "network_policies_mode_check": {
+ "name": "network_policies_mode_check",
+ "value": "\"network_policies\".\"mode\" IN ('allow_all', 'defaults_plus_allowlist', 'allowlist_only')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.scim_connection_bindings": {
+ "name": "scim_connection_bindings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "connection_id": {
+ "name": "connection_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "connection_key": {
+ "name": "connection_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provisioning_domain_id": {
+ "name": "provisioning_domain_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "decommissioned_at": {
+ "name": "decommissioned_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "decommission_status": {
+ "name": "decommission_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'active'"
+ },
+ "decommission_cursor_user_id": {
+ "name": "decommission_cursor_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "decommission_reconciled_user_count": {
+ "name": "decommission_reconciled_user_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "decommission_batch_count": {
+ "name": "decommission_batch_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "decommission_revision": {
+ "name": "decommission_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "decommission_completed_at": {
+ "name": "decommission_completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "decommission_lease_id": {
+ "name": "decommission_lease_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "decommission_lease_expires_at": {
+ "name": "decommission_lease_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "scim_connection_bindings_connection_idx": {
+ "name": "scim_connection_bindings_connection_idx",
+ "columns": [
+ {
+ "expression": "connection_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "scim_connection_bindings_connection_key_unique": {
+ "name": "scim_connection_bindings_connection_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "connection_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scim_group_members": {
+ "name": "scim_group_members",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "connection_id": {
+ "name": "connection_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "group_id": {
+ "name": "group_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scim_user_id": {
+ "name": "scim_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "membership_key": {
+ "name": "membership_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "scim_group_members_connection_idx": {
+ "name": "scim_group_members_connection_idx",
+ "columns": [
+ {
+ "expression": "connection_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scim_group_members_group_idx": {
+ "name": "scim_group_members_group_idx",
+ "columns": [
+ {
+ "expression": "group_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scim_group_members_scim_user_idx": {
+ "name": "scim_group_members_scim_user_idx",
+ "columns": [
+ {
+ "expression": "scim_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scim_group_members_group_id_scim_groups_id_fk": {
+ "name": "scim_group_members_group_id_scim_groups_id_fk",
+ "tableFrom": "scim_group_members",
+ "tableTo": "scim_groups",
+ "columnsFrom": [
+ "group_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "scim_group_members_scim_user_id_scim_users_id_fk": {
+ "name": "scim_group_members_scim_user_id_scim_users_id_fk",
+ "tableFrom": "scim_group_members",
+ "tableTo": "scim_users",
+ "columnsFrom": [
+ "scim_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "scim_group_members_membership_key_unique": {
+ "name": "scim_group_members_membership_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "membership_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scim_groups": {
+ "name": "scim_groups",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "connection_id": {
+ "name": "connection_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provisioning_domain_id": {
+ "name": "provisioning_domain_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "revision": {
+ "name": "revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "display_name": {
+ "name": "display_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "display_name_key": {
+ "name": "display_name_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "external_id_key": {
+ "name": "external_id_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "order_key": {
+ "name": "order_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "scim_groups_connection_idx": {
+ "name": "scim_groups_connection_idx",
+ "columns": [
+ {
+ "expression": "connection_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scim_groups_domain_idx": {
+ "name": "scim_groups_domain_idx",
+ "columns": [
+ {
+ "expression": "provisioning_domain_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "scim_groups_display_name_key_unique": {
+ "name": "scim_groups_display_name_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "display_name_key"
+ ]
+ },
+ "scim_groups_external_id_key_unique": {
+ "name": "scim_groups_external_id_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "external_id_key"
+ ]
+ },
+ "scim_groups_order_key_unique": {
+ "name": "scim_groups_order_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "order_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scim_identity_tombstones": {
+ "name": "scim_identity_tombstones",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "connection_id": {
+ "name": "connection_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provisioning_domain_id": {
+ "name": "provisioning_domain_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "external_id_key": {
+ "name": "external_id_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "profile": {
+ "name": "profile",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "scim_identity_tombstones_connection_idx": {
+ "name": "scim_identity_tombstones_connection_idx",
+ "columns": [
+ {
+ "expression": "connection_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scim_identity_tombstones_domain_idx": {
+ "name": "scim_identity_tombstones_domain_idx",
+ "columns": [
+ {
+ "expression": "provisioning_domain_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scim_identity_tombstones_user_idx": {
+ "name": "scim_identity_tombstones_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scim_identity_tombstones_user_id_users_id_fk": {
+ "name": "scim_identity_tombstones_user_id_users_id_fk",
+ "tableFrom": "scim_identity_tombstones",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "scim_identity_tombstones_external_id_key_unique": {
+ "name": "scim_identity_tombstones_external_id_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "external_id_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scim_projection_grants": {
+ "name": "scim_projection_grants",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "connection_id": {
+ "name": "connection_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provisioning_domain_id": {
+ "name": "provisioning_domain_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scim_user_id": {
+ "name": "scim_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source_kind": {
+ "name": "source_kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source_id": {
+ "name": "source_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source_value": {
+ "name": "source_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "grant_key": {
+ "name": "grant_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "scim_projection_grants_connection_idx": {
+ "name": "scim_projection_grants_connection_idx",
+ "columns": [
+ {
+ "expression": "connection_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scim_projection_grants_domain_idx": {
+ "name": "scim_projection_grants_domain_idx",
+ "columns": [
+ {
+ "expression": "provisioning_domain_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scim_projection_grants_scim_user_idx": {
+ "name": "scim_projection_grants_scim_user_idx",
+ "columns": [
+ {
+ "expression": "scim_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scim_projection_grants_user_idx": {
+ "name": "scim_projection_grants_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scim_projection_grants_scim_user_id_scim_users_id_fk": {
+ "name": "scim_projection_grants_scim_user_id_scim_users_id_fk",
+ "tableFrom": "scim_projection_grants",
+ "tableTo": "scim_users",
+ "columnsFrom": [
+ "scim_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "scim_projection_grants_user_id_users_id_fk": {
+ "name": "scim_projection_grants_user_id_users_id_fk",
+ "tableFrom": "scim_projection_grants",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "scim_projection_grants_grant_key_unique": {
+ "name": "scim_projection_grants_grant_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "grant_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scim_subjects": {
+ "name": "scim_subjects",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "profile_source_id": {
+ "name": "profile_source_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revision": {
+ "name": "revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "scim_subjects_profile_source_idx": {
+ "name": "scim_subjects_profile_source_idx",
+ "columns": [
+ {
+ "expression": "profile_source_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scim_subjects_user_id_users_id_fk": {
+ "name": "scim_subjects_user_id_users_id_fk",
+ "tableFrom": "scim_subjects",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "scim_subjects_user_id_unique": {
+ "name": "scim_subjects_user_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "user_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scim_users": {
+ "name": "scim_users",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "connection_id": {
+ "name": "connection_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provisioning_domain_id": {
+ "name": "provisioning_domain_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "connection_user_key": {
+ "name": "connection_user_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_name": {
+ "name": "user_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_name_key": {
+ "name": "user_name_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "primary_email": {
+ "name": "primary_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "work_email_value_index": {
+ "name": "work_email_value_index",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email_value_index": {
+ "name": "email_value_index",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "display_name": {
+ "name": "display_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "formatted_name": {
+ "name": "formatted_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "given_name": {
+ "name": "given_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "family_name": {
+ "name": "family_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "serialized_emails": {
+ "name": "serialized_emails",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "serialized_attributes": {
+ "name": "serialized_attributes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "external_id_key": {
+ "name": "external_id_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "active": {
+ "name": "active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "order_key": {
+ "name": "order_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "scim_users_connection_idx": {
+ "name": "scim_users_connection_idx",
+ "columns": [
+ {
+ "expression": "connection_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scim_users_domain_idx": {
+ "name": "scim_users_domain_idx",
+ "columns": [
+ {
+ "expression": "provisioning_domain_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scim_users_user_idx": {
+ "name": "scim_users_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scim_users_user_id_users_id_fk": {
+ "name": "scim_users_user_id_users_id_fk",
+ "tableFrom": "scim_users",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "scim_users_connection_user_key_unique": {
+ "name": "scim_users_connection_user_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "connection_user_key"
+ ]
+ },
+ "scim_users_user_name_key_unique": {
+ "name": "scim_users_user_name_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "user_name_key"
+ ]
+ },
+ "scim_users_external_id_key_unique": {
+ "name": "scim_users_external_id_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "external_id_key"
+ ]
+ },
+ "scim_users_order_key_unique": {
+ "name": "scim_users_order_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "order_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.bot_lifecycle": {
+ "name": "bot_lifecycle",
+ "schema": "",
+ "columns": {
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "paused_at": {
+ "name": "paused_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "notify": {
+ "name": "notify",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'all'"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "bot_lifecycle_owner_user_id_users_id_fk": {
+ "name": "bot_lifecycle_owner_user_id_users_id_fk",
+ "tableFrom": "bot_lifecycle",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "bot_lifecycle_agent_id_agents_id_fk": {
+ "name": "bot_lifecycle_agent_id_agents_id_fk",
+ "tableFrom": "bot_lifecycle",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "bot_lifecycle_owner_user_id_agent_id_pk": {
+ "name": "bot_lifecycle_owner_user_id_agent_id_pk",
+ "columns": [
+ "owner_user_id",
+ "agent_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.update_routing_preferences": {
+ "name": "update_routing_preferences",
+ "schema": "",
+ "columns": {
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transports": {
+ "name": "transports",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "update_routing_preferences_owner_user_id_users_id_fk": {
+ "name": "update_routing_preferences_owner_user_id_users_id_fk",
+ "tableFrom": "update_routing_preferences",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "update_routing_preferences_owner_user_id_kind_pk": {
+ "name": "update_routing_preferences_owner_user_id_kind_pk",
+ "columns": [
+ "owner_user_id",
+ "kind"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.saved_logins": {
+ "name": "saved_logins",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "origin": {
+ "name": "origin",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "encrypted_password": {
+ "name": "encrypted_password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "last_used_at": {
+ "name": "last_used_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "saved_logins_owner_origin_username_idx": {
+ "name": "saved_logins_owner_origin_username_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "origin",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "username",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "saved_logins_owner_idx": {
+ "name": "saved_logins_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "saved_logins_owner_user_id_users_id_fk": {
+ "name": "saved_logins_owner_user_id_users_id_fk",
+ "tableFrom": "saved_logins",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.sign_in_requests": {
+ "name": "sign_in_requests",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "bot_id": {
+ "name": "bot_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "origin": {
+ "name": "origin",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reason": {
+ "name": "reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "method": {
+ "name": "method",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "outcome": {
+ "name": "outcome",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "tool_call_id": {
+ "name": "tool_call_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "control_request_id": {
+ "name": "control_request_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "continuation": {
+ "name": "continuation",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "resolved_at": {
+ "name": "resolved_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "filling_until": {
+ "name": "filling_until",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "sign_in_requests_owner_idx": {
+ "name": "sign_in_requests_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "sign_in_requests_owner_user_id_users_id_fk": {
+ "name": "sign_in_requests_owner_user_id_users_id_fk",
+ "tableFrom": "sign_in_requests",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "sign_in_requests_status_check": {
+ "name": "sign_in_requests_status_check",
+ "value": "\"sign_in_requests\".\"status\" IN ('pending', 'filling', 'code_required', 'taken_over', 'signed_in', 'failed', 'cancelled', 'expired')"
+ },
+ "sign_in_requests_method_check": {
+ "name": "sign_in_requests_method_check",
+ "value": "\"sign_in_requests\".\"method\" IS NULL OR \"sign_in_requests\".\"method\" IN ('typed', 'saved', 'takeover')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.team_bot_assignments": {
+ "name": "team_bot_assignments",
+ "schema": "",
+ "columns": {
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "group_name": {
+ "name": "group_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "assigned_by": {
+ "name": "assigned_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "assigned_at": {
+ "name": "assigned_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "team_bot_assignments_agent_id_agents_id_fk": {
+ "name": "team_bot_assignments_agent_id_agents_id_fk",
+ "tableFrom": "team_bot_assignments",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "team_bot_assignments_assigned_by_users_id_fk": {
+ "name": "team_bot_assignments_assigned_by_users_id_fk",
+ "tableFrom": "team_bot_assignments",
+ "tableTo": "users",
+ "columnsFrom": [
+ "assigned_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "team_bot_assignments_agent_id_group_name_pk": {
+ "name": "team_bot_assignments_agent_id_group_name_pk",
+ "columns": [
+ "agent_id",
+ "group_name"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.team_bot_audience": {
+ "name": "team_bot_audience",
+ "schema": "",
+ "columns": {
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "team_bot_audience_agent_id_agents_id_fk": {
+ "name": "team_bot_audience_agent_id_agents_id_fk",
+ "tableFrom": "team_bot_audience",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "team_bot_audience_agent_id_kind_value_pk": {
+ "name": "team_bot_audience_agent_id_kind_value_pk",
+ "columns": [
+ "agent_id",
+ "kind",
+ "value"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "team_bot_audience_kind_check": {
+ "name": "team_bot_audience_kind_check",
+ "value": "\"team_bot_audience\".\"kind\" IN ('user', 'group')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.team_bot_consent_defaults": {
+ "name": "team_bot_consent_defaults",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "server_id": {
+ "name": "server_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "team_bot_consent_defaults_user_id_users_id_fk": {
+ "name": "team_bot_consent_defaults_user_id_users_id_fk",
+ "tableFrom": "team_bot_consent_defaults",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "team_bot_consent_defaults_user_id_server_id_pk": {
+ "name": "team_bot_consent_defaults_user_id_server_id_pk",
+ "columns": [
+ "user_id",
+ "server_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.team_bot_consents": {
+ "name": "team_bot_consents",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "server_id": {
+ "name": "server_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "decision": {
+ "name": "decision",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "team_bot_consents_user_id_users_id_fk": {
+ "name": "team_bot_consents_user_id_users_id_fk",
+ "tableFrom": "team_bot_consents",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "team_bot_consents_agent_id_agents_id_fk": {
+ "name": "team_bot_consents_agent_id_agents_id_fk",
+ "tableFrom": "team_bot_consents",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "team_bot_consents_user_id_agent_id_server_id_pk": {
+ "name": "team_bot_consents_user_id_agent_id_server_id_pk",
+ "columns": [
+ "user_id",
+ "agent_id",
+ "server_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "team_bot_consents_decision_check": {
+ "name": "team_bot_consents_decision_check",
+ "value": "\"team_bot_consents\".\"decision\" IN ('always', 'once')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.team_bot_publications": {
+ "name": "team_bot_publications",
+ "schema": "",
+ "columns": {
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "published_by": {
+ "name": "published_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "audience": {
+ "name": "audience",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "published_at": {
+ "name": "published_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "team_bot_publications_agent_id_agents_id_fk": {
+ "name": "team_bot_publications_agent_id_agents_id_fk",
+ "tableFrom": "team_bot_publications",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "team_bot_publications_published_by_users_id_fk": {
+ "name": "team_bot_publications_published_by_users_id_fk",
+ "tableFrom": "team_bot_publications",
+ "tableTo": "users",
+ "columnsFrom": [
+ "published_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "team_bot_publications_audience_check": {
+ "name": "team_bot_publications_audience_check",
+ "value": "\"team_bot_publications\".\"audience\" IN ('team', 'people')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.shared_use_approval_members": {
+ "name": "shared_use_approval_members",
+ "schema": "",
+ "columns": {
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "server_id": {
+ "name": "server_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "shared_use_approval_members_approval_fk": {
+ "name": "shared_use_approval_members_approval_fk",
+ "tableFrom": "shared_use_approval_members",
+ "tableTo": "shared_use_approvals",
+ "columnsFrom": [
+ "agent_id",
+ "server_id"
+ ],
+ "columnsTo": [
+ "agent_id",
+ "server_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "shared_use_approval_members_agent_id_server_id_kind_value_pk": {
+ "name": "shared_use_approval_members_agent_id_server_id_kind_value_pk",
+ "columns": [
+ "agent_id",
+ "server_id",
+ "kind",
+ "value"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "shared_use_approval_members_kind_check": {
+ "name": "shared_use_approval_members_kind_check",
+ "value": "\"shared_use_approval_members\".\"kind\" IN ('user', 'group')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.shared_use_approvals": {
+ "name": "shared_use_approvals",
+ "schema": "",
+ "columns": {
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "server_id": {
+ "name": "server_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "audience": {
+ "name": "audience",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "outside_input": {
+ "name": "outside_input",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "approved_by": {
+ "name": "approved_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "approved_as": {
+ "name": "approved_as",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "approved_at": {
+ "name": "approved_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "shared_use_approvals_agent_id_agents_id_fk": {
+ "name": "shared_use_approvals_agent_id_agents_id_fk",
+ "tableFrom": "shared_use_approvals",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "shared_use_approvals_server_id_mcp_servers_id_fk": {
+ "name": "shared_use_approvals_server_id_mcp_servers_id_fk",
+ "tableFrom": "shared_use_approvals",
+ "tableTo": "mcp_servers",
+ "columnsFrom": [
+ "server_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "shared_use_approvals_agent_id_server_id_pk": {
+ "name": "shared_use_approvals_agent_id_server_id_pk",
+ "columns": [
+ "agent_id",
+ "server_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "shared_use_approvals_audience_check": {
+ "name": "shared_use_approvals_audience_check",
+ "value": "\"shared_use_approvals\".\"audience\" IN ('owner', 'people', 'team')"
+ },
+ "shared_use_approvals_approved_as_check": {
+ "name": "shared_use_approvals_approved_as_check",
+ "value": "\"shared_use_approvals\".\"approved_as\" IS NULL OR \"shared_use_approvals\".\"approved_as\" IN ('admin', 'owner')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.shared_use_requests": {
+ "name": "shared_use_requests",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "server_id": {
+ "name": "server_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "proposed_audience": {
+ "name": "proposed_audience",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "proposed_outside_input": {
+ "name": "proposed_outside_input",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "proposed_members": {
+ "name": "proposed_members",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "reason": {
+ "name": "reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "requested_by": {
+ "name": "requested_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "decided_by": {
+ "name": "decided_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "decided_at": {
+ "name": "decided_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "shared_use_requests_pending_idx": {
+ "name": "shared_use_requests_pending_idx",
+ "columns": [
+ {
+ "expression": "agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "server_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"shared_use_requests\".\"status\" = 'pending'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "shared_use_requests_status_idx": {
+ "name": "shared_use_requests_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "shared_use_requests_agent_id_agents_id_fk": {
+ "name": "shared_use_requests_agent_id_agents_id_fk",
+ "tableFrom": "shared_use_requests",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "shared_use_requests_server_id_mcp_servers_id_fk": {
+ "name": "shared_use_requests_server_id_mcp_servers_id_fk",
+ "tableFrom": "shared_use_requests",
+ "tableTo": "mcp_servers",
+ "columnsFrom": [
+ "server_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "shared_use_requests_audience_check": {
+ "name": "shared_use_requests_audience_check",
+ "value": "\"shared_use_requests\".\"proposed_audience\" IN ('owner', 'people', 'team')"
+ },
+ "shared_use_requests_reason_check": {
+ "name": "shared_use_requests_reason_check",
+ "value": "\"shared_use_requests\".\"reason\" IN ('publish', 'trigger', 'grant', 'refused_call')"
+ },
+ "shared_use_requests_status_check": {
+ "name": "shared_use_requests_status_check",
+ "value": "\"shared_use_requests\".\"status\" IN ('pending', 'approved', 'declined', 'superseded')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.identity_link_challenges": {
+ "name": "identity_link_challenges",
+ "schema": "",
+ "columns": {
+ "token_hash": {
+ "name": "token_hash",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "identity_link_challenges_expiry_idx": {
+ "name": "identity_link_challenges_expiry_idx",
+ "columns": [
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "identity_link_challenges_user_provider_idx": {
+ "name": "identity_link_challenges_user_provider_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.identity_links": {
+ "name": "identity_links",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "realm": {
+ "name": "realm",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "subject": {
+ "name": "subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "handle": {
+ "name": "handle",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "verified_by": {
+ "name": "verified_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "credential_id": {
+ "name": "credential_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'active'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "identity_links_identity_idx": {
+ "name": "identity_links_identity_idx",
+ "columns": [
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "realm",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "subject",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "identity_links_user_realm_idx": {
+ "name": "identity_links_user_realm_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "realm",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "identity_links_credential_idx": {
+ "name": "identity_links_credential_idx",
+ "columns": [
+ {
+ "expression": "credential_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"identity_links\".\"credential_id\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "identity_links_credential_id_credentials_id_fk": {
+ "name": "identity_links_credential_id_credentials_id_fk",
+ "tableFrom": "identity_links",
+ "tableTo": "credentials",
+ "columnsFrom": [
+ "credential_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "identity_links_verified_by_check": {
+ "name": "identity_links_verified_by_check",
+ "value": "\"identity_links\".\"verified_by\" IN ('challenge', 'oauth')"
+ },
+ "identity_links_status_check": {
+ "name": "identity_links_status_check",
+ "value": "\"identity_links\".\"status\" IN ('active', 'needs_reconnect')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.recorder_connect_codes": {
+ "name": "recorder_connect_codes",
+ "schema": "",
+ "columns": {
+ "code_hash": {
+ "name": "code_hash",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "via": {
+ "name": "via",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_session": {
+ "name": "organization_session",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "organization_session_expires_at": {
+ "name": "organization_session_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "recorder_connect_codes_user_idx": {
+ "name": "recorder_connect_codes_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "recorder_connect_codes_user_id_users_id_fk": {
+ "name": "recorder_connect_codes_user_id_users_id_fk",
+ "tableFrom": "recorder_connect_codes",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.recorder_connections": {
+ "name": "recorder_connections",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token_hash": {
+ "name": "token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "label": {
+ "name": "label",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "via": {
+ "name": "via",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "extension_id": {
+ "name": "extension_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "install_id": {
+ "name": "install_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_session": {
+ "name": "organization_session",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "organization_session_expires_at": {
+ "name": "organization_session_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "last_used_at": {
+ "name": "last_used_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "activity": {
+ "name": "activity",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "activity_at": {
+ "name": "activity_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "recorder_connections_user_idx": {
+ "name": "recorder_connections_user_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": {}
+ },
+ "recorder_connections_live_install_idx": {
+ "name": "recorder_connections_live_install_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "install_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"recorder_connections\".\"revoked_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "recorder_connections_user_id_users_id_fk": {
+ "name": "recorder_connections_user_id_users_id_fk",
+ "tableFrom": "recorder_connections",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "recorder_connections_token_hash_unique": {
+ "name": "recorder_connections_token_hash_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token_hash"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.recorder_trajectory_bindings": {
+ "name": "recorder_trajectory_bindings",
+ "schema": "",
+ "columns": {
+ "trajectory_id": {
+ "name": "trajectory_id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "recorder_trajectory_bindings_user_idx": {
+ "name": "recorder_trajectory_bindings_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "recorder_trajectory_bindings_user_id_users_id_fk": {
+ "name": "recorder_trajectory_bindings_user_id_users_id_fk",
+ "tableFrom": "recorder_trajectory_bindings",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {
+ "public.agent_type": {
+ "name": "agent_type",
+ "schema": "public",
+ "values": [
+ "built_in",
+ "remote_ag_ui",
+ "remote_mastra"
+ ]
+ },
+ "public.credential_kind": {
+ "name": "credential_kind",
+ "schema": "public",
+ "values": [
+ "model",
+ "connector",
+ "agent",
+ "mcp",
+ "mcp_oauth_client",
+ "mcp_user_token"
+ ]
+ },
+ "public.role": {
+ "name": "role",
+ "schema": "public",
+ "values": [
+ "admin",
+ "user"
+ ]
+ },
+ "public.agent_visibility": {
+ "name": "agent_visibility",
+ "schema": "public",
+ "values": [
+ "public",
+ "private"
+ ]
+ },
+ "public.routine_run_status": {
+ "name": "routine_run_status",
+ "schema": "public",
+ "values": [
+ "succeeded",
+ "failed",
+ "skipped",
+ "waiting"
+ ]
+ },
+ "public.responsibility_run_status": {
+ "name": "responsibility_run_status",
+ "schema": "public",
+ "values": [
+ "queued",
+ "running",
+ "waiting",
+ "succeeded",
+ "failed",
+ "skipped"
+ ]
+ },
+ "public.responsibility_status": {
+ "name": "responsibility_status",
+ "schema": "public",
+ "values": [
+ "active",
+ "paused",
+ "completed"
+ ]
+ }
+ },
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/server/drizzle/meta/0085_snapshot.json b/server/drizzle/meta/0085_snapshot.json
new file mode 100644
index 0000000000..f82d4402db
--- /dev/null
+++ b/server/drizzle/meta/0085_snapshot.json
@@ -0,0 +1,11797 @@
+{
+ "id": "e2aa32e0-7fb8-4de8-8d4a-d66498985cf4",
+ "prevId": "35e68b14-b041-4974-b872-6189ad083cdf",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.accounts": {
+ "name": "accounts",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "account_id": {
+ "name": "account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issuer": {
+ "name": "issuer",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "access_token": {
+ "name": "access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token": {
+ "name": "refresh_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "access_token_expires_at": {
+ "name": "access_token_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token_expires_at": {
+ "name": "refresh_token_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password": {
+ "name": "password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "accounts_provider_account_idx": {
+ "name": "accounts_provider_account_idx",
+ "columns": [
+ {
+ "expression": "provider_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "account_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "accounts_user_id_users_id_fk": {
+ "name": "accounts_user_id_users_id_fk",
+ "tableFrom": "accounts",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.agents": {
+ "name": "agents",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "agent_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "configuration": {
+ "name": "configuration",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "package_id": {
+ "name": "package_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "override": {
+ "name": "override",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "agents_package_id_deployment_packages_id_fk": {
+ "name": "agents_package_id_deployment_packages_id_fk",
+ "tableFrom": "agents",
+ "tableTo": "deployment_packages",
+ "columnsFrom": [
+ "package_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.attachments": {
+ "name": "attachments",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "uploaded_by": {
+ "name": "uploaded_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "mime_type": {
+ "name": "mime_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "size_bytes": {
+ "name": "size_bytes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "bytes": {
+ "name": "bytes",
+ "type": "bytea",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "attached_at": {
+ "name": "attached_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "upload_group": {
+ "name": "upload_group",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "attachments_channel_idx": {
+ "name": "attachments_channel_idx",
+ "columns": [
+ {
+ "expression": "channel_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "attachments_uploaded_by_idx": {
+ "name": "attachments_uploaded_by_idx",
+ "columns": [
+ {
+ "expression": "uploaded_by",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "attachments_upload_group_idx": {
+ "name": "attachments_upload_group_idx",
+ "columns": [
+ {
+ "expression": "channel_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "uploaded_by",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "upload_group",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"attachments\".\"attached_at\" is null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "attachments_staged_idx": {
+ "name": "attachments_staged_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"attachments\".\"attached_at\" is null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "attachments_channel_id_channels_id_fk": {
+ "name": "attachments_channel_id_channels_id_fk",
+ "tableFrom": "attachments",
+ "tableTo": "channels",
+ "columnsFrom": [
+ "channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "attachments_uploaded_by_users_id_fk": {
+ "name": "attachments_uploaded_by_users_id_fk",
+ "tableFrom": "attachments",
+ "tableTo": "users",
+ "columnsFrom": [
+ "uploaded_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.audit_events": {
+ "name": "audit_events",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "actor_user_id": {
+ "name": "actor_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "initiator_kind": {
+ "name": "initiator_kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'person'"
+ },
+ "initiator_id": {
+ "name": "initiator_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "event_type": {
+ "name": "event_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_type": {
+ "name": "target_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_id": {
+ "name": "target_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "payload": {
+ "name": "payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "audit_events_created_at_idx": {
+ "name": "audit_events_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_events_type_time_idx": {
+ "name": "audit_events_type_time_idx",
+ "columns": [
+ {
+ "expression": "event_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": false,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": false,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_events_actor_time_idx": {
+ "name": "audit_events_actor_time_idx",
+ "columns": [
+ {
+ "expression": "actor_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": false,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": false,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_events_target_time_idx": {
+ "name": "audit_events_target_time_idx",
+ "columns": [
+ {
+ "expression": "target_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "target_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": false,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": false,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "audit_events_initiator_time_idx": {
+ "name": "audit_events_initiator_time_idx",
+ "columns": [
+ {
+ "expression": "initiator_kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": false,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": false,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.channel_agents": {
+ "name": "channel_agents",
+ "schema": "",
+ "columns": {
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "channel_agents_channel_id_channels_id_fk": {
+ "name": "channel_agents_channel_id_channels_id_fk",
+ "tableFrom": "channel_agents",
+ "tableTo": "channels",
+ "columnsFrom": [
+ "channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "channel_agents_agent_id_agents_id_fk": {
+ "name": "channel_agents_agent_id_agents_id_fk",
+ "tableFrom": "channel_agents",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "channel_agents_channel_id_agent_id_pk": {
+ "name": "channel_agents_channel_id_agent_id_pk",
+ "columns": [
+ "channel_id",
+ "agent_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.channel_memberships": {
+ "name": "channel_memberships",
+ "schema": "",
+ "columns": {
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "pinned_at": {
+ "name": "pinned_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_read_at": {
+ "name": "last_read_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "channel_memberships_channel_id_channels_id_fk": {
+ "name": "channel_memberships_channel_id_channels_id_fk",
+ "tableFrom": "channel_memberships",
+ "tableTo": "channels",
+ "columnsFrom": [
+ "channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "channel_memberships_user_id_users_id_fk": {
+ "name": "channel_memberships_user_id_users_id_fk",
+ "tableFrom": "channel_memberships",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "channel_memberships_channel_id_user_id_pk": {
+ "name": "channel_memberships_channel_id_user_id_pk",
+ "columns": [
+ "channel_id",
+ "user_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.channels": {
+ "name": "channels",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "suggested_prompts": {
+ "name": "suggested_prompts",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'"
+ },
+ "allowed_groups": {
+ "name": "allowed_groups",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'"
+ },
+ "package_id": {
+ "name": "package_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "override": {
+ "name": "override",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "summary": {
+ "name": "summary",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "summary_at": {
+ "name": "summary_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "opening": {
+ "name": "opening",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_message": {
+ "name": "last_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_message_source_id": {
+ "name": "last_message_source_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_message_at": {
+ "name": "last_message_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_message_agent_id": {
+ "name": "last_message_agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "channels_recent_activity_idx": {
+ "name": "channels_recent_activity_idx",
+ "columns": [
+ {
+ "expression": "COALESCE(\"last_message_at\", \"created_at\") DESC",
+ "asc": true,
+ "isExpression": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "channels_awaiting_summary_idx": {
+ "name": "channels_awaiting_summary_idx",
+ "columns": [
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"channels\".\"summary\" is null and \"channels\".\"deleted_at\" is null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "channels_package_id_deployment_packages_id_fk": {
+ "name": "channels_package_id_deployment_packages_id_fk",
+ "tableFrom": "channels",
+ "tableTo": "deployment_packages",
+ "columnsFrom": [
+ "package_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "channels_last_message_agent_id_agents_id_fk": {
+ "name": "channels_last_message_agent_id_agents_id_fk",
+ "tableFrom": "channels",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "last_message_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.credentials": {
+ "name": "credentials",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "kind": {
+ "name": "kind",
+ "type": "credential_kind",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "encrypted_value": {
+ "name": "encrypted_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key_id": {
+ "name": "key_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "credentials_active_key_idx": {
+ "name": "credentials_active_key_idx",
+ "columns": [
+ {
+ "expression": "kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "key_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"credentials\".\"revoked_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.deployment_packages": {
+ "name": "deployment_packages",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "tenant_id": {
+ "name": "tenant_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source_path": {
+ "name": "source_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "checksum": {
+ "name": "checksum",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "loaded_at": {
+ "name": "loaded_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "deployment_packages_tenant_id_unique": {
+ "name": "deployment_packages_tenant_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "tenant_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.intelligence_channel_mappings": {
+ "name": "intelligence_channel_mappings",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "model_selection": {
+ "name": "model_selection",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "intelligence_channel_mappings_thread_idx": {
+ "name": "intelligence_channel_mappings_thread_idx",
+ "columns": [
+ {
+ "expression": "thread_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "intelligence_channel_mappings_user_id_users_id_fk": {
+ "name": "intelligence_channel_mappings_user_id_users_id_fk",
+ "tableFrom": "intelligence_channel_mappings",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "intelligence_channel_mappings_channel_id_channels_id_fk": {
+ "name": "intelligence_channel_mappings_channel_id_channels_id_fk",
+ "tableFrom": "intelligence_channel_mappings",
+ "tableTo": "channels",
+ "columnsFrom": [
+ "channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "intelligence_channel_mappings_user_id_channel_id_pk": {
+ "name": "intelligence_channel_mappings_user_id_channel_id_pk",
+ "columns": [
+ "user_id",
+ "channel_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.revoked_access": {
+ "name": "revoked_access",
+ "schema": "",
+ "columns": {
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "revoked_by": {
+ "name": "revoked_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.sessions": {
+ "name": "sessions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "sessions_user_id_users_id_fk": {
+ "name": "sessions_user_id_users_id_fk",
+ "tableFrom": "sessions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "sessions_token_unique": {
+ "name": "sessions_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.sso_providers": {
+ "name": "sso_providers",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "issuer": {
+ "name": "issuer",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "oidc_config": {
+ "name": "oidc_config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "saml_config": {
+ "name": "saml_config",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "domain": {
+ "name": "domain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "sso_providers_user_id_users_id_fk": {
+ "name": "sso_providers_user_id_users_id_fk",
+ "tableFrom": "sso_providers",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "sso_providers_provider_id_unique": {
+ "name": "sso_providers_provider_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "provider_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_instructions": {
+ "name": "user_instructions",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "instructions": {
+ "name": "instructions",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "user_instructions_user_id_users_id_fk": {
+ "name": "user_instructions_user_id_users_id_fk",
+ "tableFrom": "user_instructions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_roles": {
+ "name": "user_roles",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "role",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "user_roles_user_id_users_id_fk": {
+ "name": "user_roles_user_id_users_id_fk",
+ "tableFrom": "user_roles",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "user_roles_user_id_role_pk": {
+ "name": "user_roles_user_id_role_pk",
+ "columns": [
+ "user_id",
+ "role"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.users": {
+ "name": "users",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "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": {
+ "name": "image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "email_verified": {
+ "name": "email_verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "preferences": {
+ "name": "preferences",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "groups": {
+ "name": "groups",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'"
+ },
+ "onboarding_step": {
+ "name": "onboarding_step",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "onboarding_completed_at": {
+ "name": "onboarding_completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_signed_in_at": {
+ "name": "last_signed_in_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "users_email_unique": {
+ "name": "users_email_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "email"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.verifications": {
+ "name": "verifications",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.action_policy": {
+ "name": "action_policy",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "mode": {
+ "name": "mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "deny": {
+ "name": "deny",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "allow": {
+ "name": "allow",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "labels": {
+ "name": "labels",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "updated_by": {
+ "name": "updated_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.computer_page_frame": {
+ "name": "computer_page_frame",
+ "schema": "",
+ "columns": {
+ "computer_id": {
+ "name": "computer_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "tool_call_id": {
+ "name": "tool_call_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "url": {
+ "name": "url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "frame": {
+ "name": "frame",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "captured_at": {
+ "name": "captured_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "computer_page_frame_captured_idx": {
+ "name": "computer_page_frame_captured_idx",
+ "columns": [
+ {
+ "expression": "captured_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "computer_page_frame_computer_id_tool_call_id_pk": {
+ "name": "computer_page_frame_computer_id_tool_call_id_pk",
+ "columns": [
+ "computer_id",
+ "tool_call_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.computer_snapshot": {
+ "name": "computer_snapshot",
+ "schema": "",
+ "columns": {
+ "computer_id": {
+ "name": "computer_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "snapshot_id": {
+ "name": "snapshot_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "url": {
+ "name": "url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "elements": {
+ "name": "elements",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "taken_at": {
+ "name": "taken_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "session": {
+ "name": "session",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.agent_preferences": {
+ "name": "agent_preferences",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "hidden_at": {
+ "name": "hidden_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pinned_at": {
+ "name": "pinned_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "agent_preferences_user_id_users_id_fk": {
+ "name": "agent_preferences_user_id_users_id_fk",
+ "tableFrom": "agent_preferences",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "agent_preferences_agent_id_agents_id_fk": {
+ "name": "agent_preferences_agent_id_agents_id_fk",
+ "tableFrom": "agent_preferences",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "agent_preferences_user_id_agent_id_pk": {
+ "name": "agent_preferences_user_id_agent_id_pk",
+ "columns": [
+ "user_id",
+ "agent_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.agent_profiles": {
+ "name": "agent_profiles",
+ "schema": "",
+ "columns": {
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role_description": {
+ "name": "role_description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "avatar_seed": {
+ "name": "avatar_seed",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "visibility": {
+ "name": "visibility",
+ "type": "agent_visibility",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "isolated": {
+ "name": "isolated",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "callback_token_hash": {
+ "name": "callback_token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "callback_token_issued_at": {
+ "name": "callback_token_issued_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "agent_profiles_visibility_deleted_idx": {
+ "name": "agent_profiles_visibility_deleted_idx",
+ "columns": [
+ {
+ "expression": "visibility",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "deleted_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "agent_profiles_agent_id_agents_id_fk": {
+ "name": "agent_profiles_agent_id_agents_id_fk",
+ "tableFrom": "agent_profiles",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "agent_profiles_owner_user_id_users_id_fk": {
+ "name": "agent_profiles_owner_user_id_users_id_fk",
+ "tableFrom": "agent_profiles",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.routine_runs": {
+ "name": "routine_runs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "routine_id": {
+ "name": "routine_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "finished_at": {
+ "name": "finished_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "routine_run_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "waiting": {
+ "name": "waiting",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error": {
+ "name": "error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source": {
+ "name": "source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'schedule'"
+ }
+ },
+ "indexes": {
+ "routine_runs_by_routine_idx": {
+ "name": "routine_runs_by_routine_idx",
+ "columns": [
+ {
+ "expression": "routine_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "started_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "routine_runs_routine_id_routines_id_fk": {
+ "name": "routine_runs_routine_id_routines_id_fk",
+ "tableFrom": "routine_runs",
+ "tableTo": "routines",
+ "columnsFrom": [
+ "routine_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.routine_sweeps": {
+ "name": "routine_sweeps",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "swept_at": {
+ "name": "swept_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "owner": {
+ "name": "owner",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.routines": {
+ "name": "routines",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "instruction": {
+ "name": "instruction",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "goal_id": {
+ "name": "goal_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "goal_by_title": {
+ "name": "goal_by_title",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "cron": {
+ "name": "cron",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "timezone": {
+ "name": "timezone",
+ "type": "text",
+ "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 with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_run_at": {
+ "name": "last_run_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enabled_at": {
+ "name": "enabled_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "routines_due_idx": {
+ "name": "routines_due_idx",
+ "columns": [
+ {
+ "expression": "enabled",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "next_run_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "routines_by_owner_idx": {
+ "name": "routines_by_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "enabled",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "routines_owner_user_id_users_id_fk": {
+ "name": "routines_owner_user_id_users_id_fk",
+ "tableFrom": "routines",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "routines_agent_id_agents_id_fk": {
+ "name": "routines_agent_id_agents_id_fk",
+ "tableFrom": "routines",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "routines_goal_id_responsibilities_id_fk": {
+ "name": "routines_goal_id_responsibilities_id_fk",
+ "tableFrom": "routines",
+ "tableTo": "responsibilities",
+ "columnsFrom": [
+ "goal_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.component_exclusions": {
+ "name": "component_exclusions",
+ "schema": "",
+ "columns": {
+ "component_name": {
+ "name": "component_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "withheld_by": {
+ "name": "withheld_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "component_exclusions_component_name_components_name_fk": {
+ "name": "component_exclusions_component_name_components_name_fk",
+ "tableFrom": "component_exclusions",
+ "tableTo": "components",
+ "columnsFrom": [
+ "component_name"
+ ],
+ "columnsTo": [
+ "name"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "component_exclusions_agent_id_agents_id_fk": {
+ "name": "component_exclusions_agent_id_agents_id_fk",
+ "tableFrom": "component_exclusions",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "component_exclusions_component_name_agent_id_pk": {
+ "name": "component_exclusions_component_name_agent_id_pk",
+ "columns": [
+ "component_name",
+ "agent_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.component_functions": {
+ "name": "component_functions",
+ "schema": "",
+ "columns": {
+ "component_name": {
+ "name": "component_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "function_name": {
+ "name": "function_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "granted_by": {
+ "name": "granted_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "component_functions_component_name_components_name_fk": {
+ "name": "component_functions_component_name_components_name_fk",
+ "tableFrom": "component_functions",
+ "tableTo": "components",
+ "columnsFrom": [
+ "component_name"
+ ],
+ "columnsTo": [
+ "name"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "component_functions_component_name_function_name_pk": {
+ "name": "component_functions_component_name_function_name_pk",
+ "columns": [
+ "component_name",
+ "function_name"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.components": {
+ "name": "components",
+ "schema": "",
+ "columns": {
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "draft_description": {
+ "name": "draft_description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "published_description": {
+ "name": "published_description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "published": {
+ "name": "published",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "published_at": {
+ "name": "published_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_by": {
+ "name": "updated_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.app_access_requests": {
+ "name": "app_access_requests",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "requested_by": {
+ "name": "requested_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "app_key": {
+ "name": "app_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "app_title": {
+ "name": "app_title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "resolved_at": {
+ "name": "resolved_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resolved_by": {
+ "name": "resolved_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "app_access_requests_pending_idx": {
+ "name": "app_access_requests_pending_idx",
+ "columns": [
+ {
+ "expression": "requested_by",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "app_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "coalesce(\"agent_id\", '')",
+ "asc": true,
+ "isExpression": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"app_access_requests\".\"status\" = 'pending'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "app_access_requests_status_idx": {
+ "name": "app_access_requests_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "app_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "app_access_requests_requested_by_users_id_fk": {
+ "name": "app_access_requests_requested_by_users_id_fk",
+ "tableFrom": "app_access_requests",
+ "tableTo": "users",
+ "columnsFrom": [
+ "requested_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "app_access_requests_agent_id_agents_id_fk": {
+ "name": "app_access_requests_agent_id_agents_id_fk",
+ "tableFrom": "app_access_requests",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "app_access_requests_kind_check": {
+ "name": "app_access_requests_kind_check",
+ "value": "\"app_access_requests\".\"kind\" IN ('enable', 'grant', 'connect_shared')"
+ },
+ "app_access_requests_status_check": {
+ "name": "app_access_requests_status_check",
+ "value": "\"app_access_requests\".\"status\" IN ('pending', 'approved', 'dismissed', 'cancelled')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.brokered_accounts": {
+ "name": "brokered_accounts",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "app": {
+ "name": "app",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "vendor_user_id": {
+ "name": "vendor_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "account_id": {
+ "name": "account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "identity": {
+ "name": "identity",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_default": {
+ "name": "is_default",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "connected_at": {
+ "name": "connected_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "brokered_accounts_account_idx": {
+ "name": "brokered_accounts_account_idx",
+ "columns": [
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "app",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "vendor_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "account_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "brokered_accounts_default_idx": {
+ "name": "brokered_accounts_default_idx",
+ "columns": [
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "app",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"brokered_accounts\".\"is_default\"",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "brokered_accounts_user_idx": {
+ "name": "brokered_accounts_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "brokered_accounts_provider_check": {
+ "name": "brokered_accounts_provider_check",
+ "value": "\"brokered_accounts\".\"provider\" IN ('composio', 'arcade')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.brokered_connections": {
+ "name": "brokered_connections",
+ "schema": "",
+ "columns": {
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "app": {
+ "name": "app",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "holder": {
+ "name": "holder",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "vendor_user_id": {
+ "name": "vendor_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "connected_by": {
+ "name": "connected_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "connected_at": {
+ "name": "connected_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "verified": {
+ "name": "verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "verified_at": {
+ "name": "verified_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "probe_action": {
+ "name": "probe_action",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "brokered_connections_person_idx": {
+ "name": "brokered_connections_person_idx",
+ "columns": [
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "app",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"brokered_connections\".\"holder\" = 'person'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "brokered_connections_deployment_idx": {
+ "name": "brokered_connections_deployment_idx",
+ "columns": [
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "app",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"brokered_connections\".\"holder\" = 'deployment'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "brokered_connections_user_idx": {
+ "name": "brokered_connections_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "brokered_connections_holder_check": {
+ "name": "brokered_connections_holder_check",
+ "value": "(\"brokered_connections\".\"holder\" = 'person' AND \"brokered_connections\".\"user_id\" IS NOT NULL) OR (\"brokered_connections\".\"holder\" = 'deployment' AND \"brokered_connections\".\"user_id\" IS NULL)"
+ },
+ "brokered_connections_provider_check": {
+ "name": "brokered_connections_provider_check",
+ "value": "\"brokered_connections\".\"provider\" IN ('composio', 'arcade')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.brokered_identities": {
+ "name": "brokered_identities",
+ "schema": "",
+ "columns": {
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "vendor_user_id": {
+ "name": "vendor_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "brokered_identities_provider_user_id_pk": {
+ "name": "brokered_identities_provider_user_id_pk",
+ "columns": [
+ "provider",
+ "user_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "brokered_identities_provider_check": {
+ "name": "brokered_identities_provider_check",
+ "value": "\"brokered_identities\".\"provider\" IN ('composio', 'arcade')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.composio_connections": {
+ "name": "composio_connections",
+ "schema": "",
+ "columns": {
+ "toolkit": {
+ "name": "toolkit",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "connected_at": {
+ "name": "connected_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "verified": {
+ "name": "verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "verified_at": {
+ "name": "verified_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "probe_action": {
+ "name": "probe_action",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "composio_connections_user_idx": {
+ "name": "composio_connections_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "composio_connections_toolkit_user_id_pk": {
+ "name": "composio_connections_toolkit_user_id_pk",
+ "columns": [
+ "toolkit",
+ "user_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.mcp_servers": {
+ "name": "mcp_servers",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "logo": {
+ "name": "logo",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "vendor": {
+ "name": "vendor",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "url": {
+ "name": "url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provenance": {
+ "name": "provenance",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'first-party'"
+ },
+ "credential_id": {
+ "name": "credential_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "auth_scheme": {
+ "name": "auth_scheme",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "account_mode": {
+ "name": "account_mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "shared_vendor_user_id": {
+ "name": "shared_vendor_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owner_grants": {
+ "name": "owner_grants",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "entra_scope": {
+ "name": "entra_scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "tools_refreshed_at": {
+ "name": "tools_refreshed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "skipped_tools": {
+ "name": "skipped_tools",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "listing_revision": {
+ "name": "listing_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "added_by": {
+ "name": "added_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "mcp_servers_credential_id_credentials_id_fk": {
+ "name": "mcp_servers_credential_id_credentials_id_fk",
+ "tableFrom": "mcp_servers",
+ "tableTo": "credentials",
+ "columnsFrom": [
+ "credential_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "mcp_servers_account_mode_check": {
+ "name": "mcp_servers_account_mode_check",
+ "value": "\"mcp_servers\".\"account_mode\" IS NULL OR \"mcp_servers\".\"account_mode\" IN ('personal', 'shared')"
+ },
+ "mcp_servers_owner_grants_check": {
+ "name": "mcp_servers_owner_grants_check",
+ "value": "\"mcp_servers\".\"owner_grants\" IS NULL OR \"mcp_servers\".\"owner_grants\" IN ('any', 'admin')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.mcp_tools": {
+ "name": "mcp_tools",
+ "schema": "",
+ "columns": {
+ "server_id": {
+ "name": "server_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "vendor_name": {
+ "name": "vendor_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "input_schema": {
+ "name": "input_schema",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "effect": {
+ "name": "effect",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "destructive": {
+ "name": "destructive",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "unclassified": {
+ "name": "unclassified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "declared_read": {
+ "name": "declared_read",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "version": {
+ "name": "version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "mcp_tools_server_id_mcp_servers_id_fk": {
+ "name": "mcp_tools_server_id_mcp_servers_id_fk",
+ "tableFrom": "mcp_tools",
+ "tableTo": "mcp_servers",
+ "columnsFrom": [
+ "server_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "mcp_tools_server_id_name_pk": {
+ "name": "mcp_tools_server_id_name_pk",
+ "columns": [
+ "server_id",
+ "name"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.mcp_user_credentials": {
+ "name": "mcp_user_credentials",
+ "schema": "",
+ "columns": {
+ "server_id": {
+ "name": "server_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "credential_id": {
+ "name": "credential_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "connected_at": {
+ "name": "connected_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "mcp_user_credentials_user_idx": {
+ "name": "mcp_user_credentials_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "mcp_user_credentials_server_id_mcp_servers_id_fk": {
+ "name": "mcp_user_credentials_server_id_mcp_servers_id_fk",
+ "tableFrom": "mcp_user_credentials",
+ "tableTo": "mcp_servers",
+ "columnsFrom": [
+ "server_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "mcp_user_credentials_user_id_users_id_fk": {
+ "name": "mcp_user_credentials_user_id_users_id_fk",
+ "tableFrom": "mcp_user_credentials",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "mcp_user_credentials_credential_id_credentials_id_fk": {
+ "name": "mcp_user_credentials_credential_id_credentials_id_fk",
+ "tableFrom": "mcp_user_credentials",
+ "tableTo": "credentials",
+ "columnsFrom": [
+ "credential_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "mcp_user_credentials_server_id_user_id_pk": {
+ "name": "mcp_user_credentials_server_id_user_id_pk",
+ "columns": [
+ "server_id",
+ "user_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.pending_authorizations": {
+ "name": "pending_authorizations",
+ "schema": "",
+ "columns": {
+ "actor_id": {
+ "name": "actor_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "server_id": {
+ "name": "server_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action": {
+ "name": "action",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "holder": {
+ "name": "holder",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "vendor_user_id": {
+ "name": "vendor_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "auth_id": {
+ "name": "auth_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "outcome": {
+ "name": "outcome",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "pending_authorizations_server_idx": {
+ "name": "pending_authorizations_server_idx",
+ "columns": [
+ {
+ "expression": "server_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "pending_authorizations_actor_id_users_id_fk": {
+ "name": "pending_authorizations_actor_id_users_id_fk",
+ "tableFrom": "pending_authorizations",
+ "tableTo": "users",
+ "columnsFrom": [
+ "actor_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "pending_authorizations_server_id_mcp_servers_id_fk": {
+ "name": "pending_authorizations_server_id_mcp_servers_id_fk",
+ "tableFrom": "pending_authorizations",
+ "tableTo": "mcp_servers",
+ "columnsFrom": [
+ "server_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "pending_authorizations_holder_check": {
+ "name": "pending_authorizations_holder_check",
+ "value": "(\"pending_authorizations\".\"holder\" = 'person' AND \"pending_authorizations\".\"user_id\" IS NOT NULL) OR (\"pending_authorizations\".\"holder\" = 'deployment' AND \"pending_authorizations\".\"user_id\" IS NULL)"
+ },
+ "pending_authorizations_outcome_check": {
+ "name": "pending_authorizations_outcome_check",
+ "value": "\"pending_authorizations\".\"outcome\" IS NULL OR \"pending_authorizations\".\"outcome\" IN ('completed', 'failed', 'unreconciled', 'withdrawn')"
+ },
+ "pending_authorizations_completion_check": {
+ "name": "pending_authorizations_completion_check",
+ "value": "(\"pending_authorizations\".\"completed_at\" IS NULL) = (\"pending_authorizations\".\"outcome\" IS NULL)"
+ },
+ "pending_authorizations_user_is_actor_check": {
+ "name": "pending_authorizations_user_is_actor_check",
+ "value": "\"pending_authorizations\".\"user_id\" IS NULL OR \"pending_authorizations\".\"user_id\" = \"pending_authorizations\".\"actor_id\""
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.plugin_grants": {
+ "name": "plugin_grants",
+ "schema": "",
+ "columns": {
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ref": {
+ "name": "ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "granted_by": {
+ "name": "granted_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "granted_as": {
+ "name": "granted_as",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "plugin_grants_agent_idx": {
+ "name": "plugin_grants_agent_idx",
+ "columns": [
+ {
+ "expression": "agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "plugin_grants_agent_id_agents_id_fk": {
+ "name": "plugin_grants_agent_id_agents_id_fk",
+ "tableFrom": "plugin_grants",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "plugin_grants_kind_ref_agent_id_pk": {
+ "name": "plugin_grants_kind_ref_agent_id_pk",
+ "columns": [
+ "kind",
+ "ref",
+ "agent_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "plugin_grants_granted_as_check": {
+ "name": "plugin_grants_granted_as_check",
+ "value": "\"plugin_grants\".\"granted_as\" IS NULL OR \"plugin_grants\".\"granted_as\" IN ('admin', 'owner')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.sandboxed_components": {
+ "name": "sandboxed_components",
+ "schema": "",
+ "columns": {
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "draft_description": {
+ "name": "draft_description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "draft_html": {
+ "name": "draft_html",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "draft_css": {
+ "name": "draft_css",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "draft_js_functions": {
+ "name": "draft_js_functions",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "draft_argument_schema": {
+ "name": "draft_argument_schema",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "published_description": {
+ "name": "published_description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "published_html": {
+ "name": "published_html",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "published_css": {
+ "name": "published_css",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "published_js_functions": {
+ "name": "published_js_functions",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "published_argument_schema": {
+ "name": "published_argument_schema",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sample_arguments": {
+ "name": "sample_arguments",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "revision": {
+ "name": "revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "published": {
+ "name": "published",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "published_at": {
+ "name": "published_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "authored_by": {
+ "name": "authored_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.skill_tools": {
+ "name": "skill_tools",
+ "schema": "",
+ "columns": {
+ "skill_id": {
+ "name": "skill_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ref": {
+ "name": "ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "declared_by": {
+ "name": "declared_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "skill_tools_ref_idx": {
+ "name": "skill_tools_ref_idx",
+ "columns": [
+ {
+ "expression": "ref",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "skill_tools_skill_id_skills_id_fk": {
+ "name": "skill_tools_skill_id_skills_id_fk",
+ "tableFrom": "skill_tools",
+ "tableTo": "skills",
+ "columnsFrom": [
+ "skill_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "skill_tools_skill_id_ref_pk": {
+ "name": "skill_tools_skill_id_ref_pk",
+ "columns": [
+ "skill_id",
+ "ref"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.skills": {
+ "name": "skills",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "summary": {
+ "name": "summary",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "instructions": {
+ "name": "instructions",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "origin": {
+ "name": "origin",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'yours'"
+ },
+ "installed_by": {
+ "name": "installed_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "skills_slug_key": {
+ "name": "skills_slug_key",
+ "columns": [
+ {
+ "expression": "slug",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "skills_owner_idx": {
+ "name": "skills_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "skills_owner_user_id_users_id_fk": {
+ "name": "skills_owner_user_id_users_id_fk",
+ "tableFrom": "skills",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.work_items": {
+ "name": "work_items",
+ "schema": "",
+ "columns": {
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "run_at": {
+ "name": "run_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "claimed_by": {
+ "name": "claimed_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lease_until": {
+ "name": "lease_until",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "attempts": {
+ "name": "attempts",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "finished_at": {
+ "name": "finished_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "payload": {
+ "name": "payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "work_items_claimable_idx": {
+ "name": "work_items_claimable_idx",
+ "columns": [
+ {
+ "expression": "kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "run_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "work_items_open_thread_idx": {
+ "name": "work_items_open_thread_idx",
+ "columns": [
+ {
+ "expression": "kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "(\"payload\"->>'threadId')",
+ "asc": true,
+ "isExpression": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"work_items\".\"finished_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "work_items_kind_key_pk": {
+ "name": "work_items_kind_key_pk",
+ "columns": [
+ "kind",
+ "key"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.voice_sessions": {
+ "name": "voice_sessions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "anchor_message_id": {
+ "name": "anchor_message_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ended_at": {
+ "name": "ended_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "duration_seconds": {
+ "name": "duration_seconds",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transcript": {
+ "name": "transcript",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "summary": {
+ "name": "summary",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "summary_status": {
+ "name": "summary_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'failed'"
+ }
+ },
+ "indexes": {
+ "voice_sessions_channel_started_idx": {
+ "name": "voice_sessions_channel_started_idx",
+ "columns": [
+ {
+ "expression": "channel_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "started_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "voice_sessions_channel_id_channels_id_fk": {
+ "name": "voice_sessions_channel_id_channels_id_fk",
+ "tableFrom": "voice_sessions",
+ "tableTo": "channels",
+ "columnsFrom": [
+ "channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "voice_sessions_user_id_users_id_fk": {
+ "name": "voice_sessions_user_id_users_id_fk",
+ "tableFrom": "voice_sessions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "voice_sessions_duration_check": {
+ "name": "voice_sessions_duration_check",
+ "value": "\"voice_sessions\".\"duration_seconds\" >= 0"
+ },
+ "voice_sessions_summary_check": {
+ "name": "voice_sessions_summary_check",
+ "value": "(\"voice_sessions\".\"summary_status\" = 'ready' AND \"voice_sessions\".\"summary\" IS NOT NULL) OR (\"voice_sessions\".\"summary_status\" = 'failed' AND \"voice_sessions\".\"summary\" IS NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.learning_open_skills": {
+ "name": "learning_open_skills",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "skill_name": {
+ "name": "skill_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "learning_open_skills_user_id_users_id_fk": {
+ "name": "learning_open_skills_user_id_users_id_fk",
+ "tableFrom": "learning_open_skills",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "learning_open_skills_user_id_skill_name_pk": {
+ "name": "learning_open_skills_user_id_skill_name_pk",
+ "columns": [
+ "user_id",
+ "skill_name"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.learning_person_spaces": {
+ "name": "learning_person_spaces",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "container_id": {
+ "name": "container_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "learning_person_spaces_user_id_users_id_fk": {
+ "name": "learning_person_spaces_user_id_users_id_fk",
+ "tableFrom": "learning_person_spaces",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "learning_person_spaces_container_id_unique": {
+ "name": "learning_person_spaces_container_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "container_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.learning_settings": {
+ "name": "learning_settings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "settings": {
+ "name": "settings",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.learning_skill_approvals": {
+ "name": "learning_skill_approvals",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "skill_name": {
+ "name": "skill_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "approved_by": {
+ "name": "approved_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sharing": {
+ "name": "sharing",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'none'"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "learning_skill_approvals_user_id_users_id_fk": {
+ "name": "learning_skill_approvals_user_id_users_id_fk",
+ "tableFrom": "learning_skill_approvals",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "learning_skill_approvals_user_id_skill_name_pk": {
+ "name": "learning_skill_approvals_user_id_skill_name_pk",
+ "columns": [
+ "user_id",
+ "skill_name"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "learning_skill_approvals_approved_by_check": {
+ "name": "learning_skill_approvals_approved_by_check",
+ "value": "\"learning_skill_approvals\".\"approved_by\" IN ('person', 'admin')"
+ },
+ "learning_skill_approvals_sharing_check": {
+ "name": "learning_skill_approvals_sharing_check",
+ "value": "\"learning_skill_approvals\".\"sharing\" IN ('none', 'requested', 'approved', 'declined')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.learning_skill_sharing": {
+ "name": "learning_skill_sharing",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "mode": {
+ "name": "mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'closed'"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "learning_skill_sharing_open_idx": {
+ "name": "learning_skill_sharing_open_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"learning_skill_sharing\".\"mode\" <> 'closed'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "learning_skill_sharing_user_id_users_id_fk": {
+ "name": "learning_skill_sharing_user_id_users_id_fk",
+ "tableFrom": "learning_skill_sharing",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "learning_skill_sharing_mode_check": {
+ "name": "learning_skill_sharing_mode_check",
+ "value": "\"learning_skill_sharing\".\"mode\" IN ('closed', 'open', 'choose')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.learning_thread_bindings": {
+ "name": "learning_thread_bindings",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "container_id": {
+ "name": "container_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "learning_thread_bindings_user_id_thread_id_pk": {
+ "name": "learning_thread_bindings_user_id_thread_id_pk",
+ "columns": [
+ "user_id",
+ "thread_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.responsibilities": {
+ "name": "responsibilities",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "instruction": {
+ "name": "instruction",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "success_criteria": {
+ "name": "success_criteria",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "responsibility_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'active'"
+ },
+ "progress": {
+ "name": "progress",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "last_result": {
+ "name": "last_result",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "subscriptions": {
+ "name": "subscriptions",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "recurring": {
+ "name": "recurring",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "responsibilities_owner_status_idx": {
+ "name": "responsibilities_owner_status_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "responsibilities_owner_user_id_users_id_fk": {
+ "name": "responsibilities_owner_user_id_users_id_fk",
+ "tableFrom": "responsibilities",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "responsibilities_agent_id_agents_id_fk": {
+ "name": "responsibilities_agent_id_agents_id_fk",
+ "tableFrom": "responsibilities",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.responsibility_events": {
+ "name": "responsibility_events",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source": {
+ "name": "source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "payload": {
+ "name": "payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "responsibility_events_source_id_idx": {
+ "name": "responsibility_events_source_id_idx",
+ "columns": [
+ {
+ "expression": "owner_user_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": {}
+ }
+ },
+ "foreignKeys": {
+ "responsibility_events_owner_user_id_users_id_fk": {
+ "name": "responsibility_events_owner_user_id_users_id_fk",
+ "tableFrom": "responsibility_events",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.responsibility_runs": {
+ "name": "responsibility_runs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "responsibility_id": {
+ "name": "responsibility_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_id": {
+ "name": "event_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "responsibility_run_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'queued'"
+ },
+ "reply_text": {
+ "name": "reply_text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error": {
+ "name": "error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "waiting": {
+ "name": "waiting",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "continuation": {
+ "name": "continuation",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "finished_at": {
+ "name": "finished_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "responsibility_runs_event_idx": {
+ "name": "responsibility_runs_event_idx",
+ "columns": [
+ {
+ "expression": "responsibility_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "responsibility_runs_status_idx": {
+ "name": "responsibility_runs_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "responsibility_runs_responsibility_id_responsibilities_id_fk": {
+ "name": "responsibility_runs_responsibility_id_responsibilities_id_fk",
+ "tableFrom": "responsibility_runs",
+ "tableTo": "responsibilities",
+ "columnsFrom": [
+ "responsibility_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "responsibility_runs_event_id_responsibility_events_id_fk": {
+ "name": "responsibility_runs_event_id_responsibility_events_id_fk",
+ "tableFrom": "responsibility_runs",
+ "tableTo": "responsibility_events",
+ "columnsFrom": [
+ "event_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.responsibility_source_bindings": {
+ "name": "responsibility_source_bindings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source": {
+ "name": "source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "repository": {
+ "name": "repository",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "credential_id": {
+ "name": "credential_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "responsibility_source_bindings_owner_idx": {
+ "name": "responsibility_source_bindings_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "responsibility_source_bindings_owner_user_id_users_id_fk": {
+ "name": "responsibility_source_bindings_owner_user_id_users_id_fk",
+ "tableFrom": "responsibility_source_bindings",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "responsibility_source_bindings_credential_id_credentials_id_fk": {
+ "name": "responsibility_source_bindings_credential_id_credentials_id_fk",
+ "tableFrom": "responsibility_source_bindings",
+ "tableTo": "credentials",
+ "columnsFrom": [
+ "credential_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.responsibility_triggers": {
+ "name": "responsibility_triggers",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "responsibility_id": {
+ "name": "responsibility_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "credential_id": {
+ "name": "credential_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "responsibility_triggers_owner_idx": {
+ "name": "responsibility_triggers_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "responsibility_triggers_responsibility_idx": {
+ "name": "responsibility_triggers_responsibility_idx",
+ "columns": [
+ {
+ "expression": "responsibility_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "responsibility_triggers_kind_idx": {
+ "name": "responsibility_triggers_kind_idx",
+ "columns": [
+ {
+ "expression": "kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "responsibility_triggers_owner_user_id_users_id_fk": {
+ "name": "responsibility_triggers_owner_user_id_users_id_fk",
+ "tableFrom": "responsibility_triggers",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "responsibility_triggers_responsibility_id_responsibilities_id_fk": {
+ "name": "responsibility_triggers_responsibility_id_responsibilities_id_fk",
+ "tableFrom": "responsibility_triggers",
+ "tableTo": "responsibilities",
+ "columnsFrom": [
+ "responsibility_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "responsibility_triggers_credential_id_credentials_id_fk": {
+ "name": "responsibility_triggers_credential_id_credentials_id_fk",
+ "tableFrom": "responsibility_triggers",
+ "tableTo": "credentials",
+ "columnsFrom": [
+ "credential_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.approval_preferences": {
+ "name": "approval_preferences",
+ "schema": "",
+ "columns": {
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "auto_review": {
+ "name": "auto_review",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "host_commands": {
+ "name": "host_commands",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'ask'"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "approval_preferences_owner_user_id_users_id_fk": {
+ "name": "approval_preferences_owner_user_id_users_id_fk",
+ "tableFrom": "approval_preferences",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "approval_preferences_host_commands_check": {
+ "name": "approval_preferences_host_commands_check",
+ "value": "\"approval_preferences\".\"host_commands\" IN ('ask', 'allow', 'never')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.approval_requests": {
+ "name": "approval_requests",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "run_id": {
+ "name": "run_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "tool_call_id": {
+ "name": "tool_call_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action_digest": {
+ "name": "action_digest",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action": {
+ "name": "action",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "decision": {
+ "name": "decision",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "result": {
+ "name": "result",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "decided_at": {
+ "name": "decided_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "consumed_at": {
+ "name": "consumed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "approval_requests_action_idx": {
+ "name": "approval_requests_action_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "run_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "tool_call_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "approval_requests_inbox_idx": {
+ "name": "approval_requests_inbox_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "approval_requests_owner_user_id_users_id_fk": {
+ "name": "approval_requests_owner_user_id_users_id_fk",
+ "tableFrom": "approval_requests",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "approval_status_check": {
+ "name": "approval_status_check",
+ "value": "\"approval_requests\".\"status\" IN ('pending', 'approved', 'denied', 'consumed', 'completed')"
+ },
+ "approval_decision_check": {
+ "name": "approval_decision_check",
+ "value": "\"approval_requests\".\"decision\" IN ('allow_once', 'allow_always', 'deny', 'handled')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.approval_rules": {
+ "name": "approval_rules",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "bot_id": {
+ "name": "bot_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "tool_ref": {
+ "name": "tool_ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "effect": {
+ "name": "effect",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "behaviour": {
+ "name": "behaviour",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'allow'"
+ },
+ "account": {
+ "name": "account",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "approval_rules_owner_idx": {
+ "name": "approval_rules_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "approval_rules_owner_user_id_users_id_fk": {
+ "name": "approval_rules_owner_user_id_users_id_fk",
+ "tableFrom": "approval_rules",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "approval_rules_behaviour_check": {
+ "name": "approval_rules_behaviour_check",
+ "value": "\"approval_rules\".\"behaviour\" IN ('allow', 'pre_approved', 'ask', 'hand_off')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.approval_team_rules": {
+ "name": "approval_team_rules",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "bot_id": {
+ "name": "bot_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "tool_ref": {
+ "name": "tool_ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "effect": {
+ "name": "effect",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "behaviour": {
+ "name": "behaviour",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "approval_team_rules_created_by_users_id_fk": {
+ "name": "approval_team_rules_created_by_users_id_fk",
+ "tableFrom": "approval_team_rules",
+ "tableTo": "users",
+ "columnsFrom": [
+ "created_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "approval_team_rules_behaviour_check": {
+ "name": "approval_team_rules_behaviour_check",
+ "value": "\"approval_team_rules\".\"behaviour\" IN ('allow', 'pre_approved', 'ask', 'hand_off')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.approval_team_settings": {
+ "name": "approval_team_settings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "enforce_auto_review": {
+ "name": "enforce_auto_review",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "custom_rules_enabled": {
+ "name": "custom_rules_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "host_commands_cap": {
+ "name": "host_commands_cap",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'allow'"
+ },
+ "updated_by": {
+ "name": "updated_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "approval_team_settings_updated_by_users_id_fk": {
+ "name": "approval_team_settings_updated_by_users_id_fk",
+ "tableFrom": "approval_team_settings",
+ "tableTo": "users",
+ "columnsFrom": [
+ "updated_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "approval_team_settings_host_commands_cap_check": {
+ "name": "approval_team_settings_host_commands_cap_check",
+ "value": "\"approval_team_settings\".\"host_commands_cap\" IN ('ask', 'allow', 'never')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.memory_sources": {
+ "name": "memory_sources",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "tool_ref": {
+ "name": "tool_ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "args": {
+ "name": "args",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "sync_status": {
+ "name": "sync_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'idle'"
+ },
+ "sync_error": {
+ "name": "sync_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_sync_at": {
+ "name": "last_sync_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "next_sync_at": {
+ "name": "next_sync_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "memory_sources_owner_idx": {
+ "name": "memory_sources_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "memory_sources_due_idx": {
+ "name": "memory_sources_due_idx",
+ "columns": [
+ {
+ "expression": "enabled",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "next_sync_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "memory_sources_owner_user_id_users_id_fk": {
+ "name": "memory_sources_owner_user_id_users_id_fk",
+ "tableFrom": "memory_sources",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "memory_sources_agent_id_agents_id_fk": {
+ "name": "memory_sources_agent_id_agents_id_fk",
+ "tableFrom": "memory_sources",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.personal_memories": {
+ "name": "personal_memories",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source_id": {
+ "name": "source_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "import_digest": {
+ "name": "import_digest",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provenance": {
+ "name": "provenance",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "review_state": {
+ "name": "review_state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'unreviewed'"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "formed_by": {
+ "name": "formed_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'person'"
+ },
+ "formed_by_agent_id": {
+ "name": "formed_by_agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_app": {
+ "name": "source_app",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_ref": {
+ "name": "source_ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_link": {
+ "name": "source_link",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "observed_at": {
+ "name": "observed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "intelligence_memory_id": {
+ "name": "intelligence_memory_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "intelligence_digest": {
+ "name": "intelligence_digest",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "personal_memories_owner_idx": {
+ "name": "personal_memories_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "enabled",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "personal_memories_intelligence_idx": {
+ "name": "personal_memories_intelligence_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "intelligence_memory_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "personal_memories_formed_idx": {
+ "name": "personal_memories_formed_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "formed_by_agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "personal_memories_import_idx": {
+ "name": "personal_memories_import_idx",
+ "columns": [
+ {
+ "expression": "source_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "external_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "personal_memories_formed_digest_per_bot_idx": {
+ "name": "personal_memories_formed_digest_per_bot_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "formed_by_agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "import_digest",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"personal_memories\".\"formed_by\" = 'bot'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "personal_memories_owner_user_id_users_id_fk": {
+ "name": "personal_memories_owner_user_id_users_id_fk",
+ "tableFrom": "personal_memories",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "personal_memories_source_id_memory_sources_id_fk": {
+ "name": "personal_memories_source_id_memory_sources_id_fk",
+ "tableFrom": "personal_memories",
+ "tableTo": "memory_sources",
+ "columnsFrom": [
+ "source_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "personal_memories_formed_by_agent_id_agents_id_fk": {
+ "name": "personal_memories_formed_by_agent_id_agents_id_fk",
+ "tableFrom": "personal_memories",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "formed_by_agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.default_opt_outs": {
+ "name": "default_opt_outs",
+ "schema": "",
+ "columns": {
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "feature": {
+ "name": "feature",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "default_opt_outs_owner_user_id_users_id_fk": {
+ "name": "default_opt_outs_owner_user_id_users_id_fk",
+ "tableFrom": "default_opt_outs",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "default_opt_outs_agent_id_agents_id_fk": {
+ "name": "default_opt_outs_agent_id_agents_id_fk",
+ "tableFrom": "default_opt_outs",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "default_opt_outs_owner_user_id_agent_id_feature_scope_pk": {
+ "name": "default_opt_outs_owner_user_id_agent_id_feature_scope_pk",
+ "columns": [
+ "owner_user_id",
+ "agent_id",
+ "feature",
+ "scope"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "default_opt_outs_feature_check": {
+ "name": "default_opt_outs_feature_check",
+ "value": "\"default_opt_outs\".\"feature\" IN ('memory_source', 'proactive_research')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.proactive_people": {
+ "name": "proactive_people",
+ "schema": "",
+ "columns": {
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "last_pinged_at": {
+ "name": "last_pinged_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "proactive_people_owner_user_id_users_id_fk": {
+ "name": "proactive_people_owner_user_id_users_id_fk",
+ "tableFrom": "proactive_people",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.proactive_pings": {
+ "name": "proactive_pings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "item_count": {
+ "name": "item_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "text": {
+ "name": "text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "push_body": {
+ "name": "push_body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "offered_at": {
+ "name": "offered_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "proactive_pings_owner_created_idx": {
+ "name": "proactive_pings_owner_created_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "proactive_pings_unoffered_idx": {
+ "name": "proactive_pings_unoffered_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"proactive_pings\".\"offered_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "proactive_pings_owner_user_id_users_id_fk": {
+ "name": "proactive_pings_owner_user_id_users_id_fk",
+ "tableFrom": "proactive_pings",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "proactive_pings_item_count_check": {
+ "name": "proactive_pings_item_count_check",
+ "value": "\"proactive_pings\".\"item_count\" > 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.proactive_runs": {
+ "name": "proactive_runs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "setting_id": {
+ "name": "setting_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": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "trigger": {
+ "name": "trigger",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "outcome": {
+ "name": "outcome",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'running'"
+ },
+ "skip_reason": {
+ "name": "skip_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "items_raised": {
+ "name": "items_raised",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "reads": {
+ "name": "reads",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "error": {
+ "name": "error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "finished_at": {
+ "name": "finished_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "proactive_runs_setting_started_idx": {
+ "name": "proactive_runs_setting_started_idx",
+ "columns": [
+ {
+ "expression": "setting_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "started_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "proactive_runs_owner_started_idx": {
+ "name": "proactive_runs_owner_started_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "started_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "proactive_runs_agent_started_idx": {
+ "name": "proactive_runs_agent_started_idx",
+ "columns": [
+ {
+ "expression": "agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "started_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "proactive_runs_one_running_idx": {
+ "name": "proactive_runs_one_running_idx",
+ "columns": [
+ {
+ "expression": "setting_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"proactive_runs\".\"outcome\" = 'running'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "proactive_runs_setting_id_proactive_settings_id_fk": {
+ "name": "proactive_runs_setting_id_proactive_settings_id_fk",
+ "tableFrom": "proactive_runs",
+ "tableTo": "proactive_settings",
+ "columnsFrom": [
+ "setting_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "proactive_runs_owner_user_id_users_id_fk": {
+ "name": "proactive_runs_owner_user_id_users_id_fk",
+ "tableFrom": "proactive_runs",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "proactive_runs_agent_id_agents_id_fk": {
+ "name": "proactive_runs_agent_id_agents_id_fk",
+ "tableFrom": "proactive_runs",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "proactive_runs_trigger_check": {
+ "name": "proactive_runs_trigger_check",
+ "value": "\"proactive_runs\".\"trigger\" IN ('schedule', 'run_now')"
+ },
+ "proactive_runs_outcome_check": {
+ "name": "proactive_runs_outcome_check",
+ "value": "\"proactive_runs\".\"outcome\" IN ('running', 'quiet', 'raised', 'failed', 'skipped')"
+ },
+ "proactive_runs_skip_reason_check": {
+ "name": "proactive_runs_skip_reason_check",
+ "value": "(\"proactive_runs\".\"outcome\" = 'skipped' AND \"proactive_runs\".\"skip_reason\" IS NOT NULL AND \"proactive_runs\".\"skip_reason\" IN ('off_admin', 'off_person', 'paused', 'capped', 'unreachable', 'no_sources', 'busy')) OR (\"proactive_runs\".\"outcome\" <> 'skipped' AND \"proactive_runs\".\"skip_reason\" IS NULL)"
+ },
+ "proactive_runs_finished_check": {
+ "name": "proactive_runs_finished_check",
+ "value": "(\"proactive_runs\".\"outcome\" = 'running') = (\"proactive_runs\".\"finished_at\" IS NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.proactive_settings": {
+ "name": "proactive_settings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "focus": {
+ "name": "focus",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "subject": {
+ "name": "subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "source_link": {
+ "name": "source_link",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "interval_minutes": {
+ "name": "interval_minutes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 60
+ },
+ "next_run_at": {
+ "name": "next_run_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "last_run_at": {
+ "name": "last_run_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_status": {
+ "name": "last_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'idle'"
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "proactive_settings_owner_agent_idx": {
+ "name": "proactive_settings_owner_agent_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "proactive_settings_due_idx": {
+ "name": "proactive_settings_due_idx",
+ "columns": [
+ {
+ "expression": "enabled",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "next_run_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "proactive_settings_owner_user_id_users_id_fk": {
+ "name": "proactive_settings_owner_user_id_users_id_fk",
+ "tableFrom": "proactive_settings",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "proactive_settings_agent_id_agents_id_fk": {
+ "name": "proactive_settings_agent_id_agents_id_fk",
+ "tableFrom": "proactive_settings",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "proactive_settings_interval_check": {
+ "name": "proactive_settings_interval_check",
+ "value": "\"proactive_settings\".\"interval_minutes\" >= 60 AND \"proactive_settings\".\"interval_minutes\" <= 1440"
+ },
+ "proactive_settings_status_check": {
+ "name": "proactive_settings_status_check",
+ "value": "\"proactive_settings\".\"last_status\" IN ('idle', 'running', 'succeeded', 'error')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.proactive_suggestions": {
+ "name": "proactive_suggestions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "setting_id": {
+ "name": "setting_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "run_id": {
+ "name": "run_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "detail": {
+ "name": "detail",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source_app": {
+ "name": "source_app",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_ref": {
+ "name": "source_ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_link": {
+ "name": "source_link",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'open'"
+ },
+ "dedupe_key": {
+ "name": "dedupe_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ping_id": {
+ "name": "ping_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "delivered_at": {
+ "name": "delivered_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resolved_at": {
+ "name": "resolved_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "proactive_suggestions_owner_idx": {
+ "name": "proactive_suggestions_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "proactive_suggestions_run_idx": {
+ "name": "proactive_suggestions_run_idx",
+ "columns": [
+ {
+ "expression": "run_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "proactive_suggestions_open_dedupe_idx": {
+ "name": "proactive_suggestions_open_dedupe_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "dedupe_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"proactive_suggestions\".\"status\" = 'open' AND \"proactive_suggestions\".\"dedupe_key\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "proactive_suggestions_unpinged_idx": {
+ "name": "proactive_suggestions_unpinged_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"proactive_suggestions\".\"status\" = 'open' AND \"proactive_suggestions\".\"ping_id\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "proactive_suggestions_owner_user_id_users_id_fk": {
+ "name": "proactive_suggestions_owner_user_id_users_id_fk",
+ "tableFrom": "proactive_suggestions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "proactive_suggestions_agent_id_agents_id_fk": {
+ "name": "proactive_suggestions_agent_id_agents_id_fk",
+ "tableFrom": "proactive_suggestions",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "proactive_suggestions_setting_id_proactive_settings_id_fk": {
+ "name": "proactive_suggestions_setting_id_proactive_settings_id_fk",
+ "tableFrom": "proactive_suggestions",
+ "tableTo": "proactive_settings",
+ "columnsFrom": [
+ "setting_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "proactive_suggestions_ping_id_proactive_pings_id_fk": {
+ "name": "proactive_suggestions_ping_id_proactive_pings_id_fk",
+ "tableFrom": "proactive_suggestions",
+ "tableTo": "proactive_pings",
+ "columnsFrom": [
+ "ping_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "proactive_suggestions_status_check": {
+ "name": "proactive_suggestions_status_check",
+ "value": "\"proactive_suggestions\".\"status\" IN ('open', 'dismissed', 'started', 'superseded')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.bot_tasks": {
+ "name": "bot_tasks",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "suggestion_id": {
+ "name": "suggestion_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "why": {
+ "name": "why",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_app": {
+ "name": "source_app",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_link": {
+ "name": "source_link",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "origin": {
+ "name": "origin",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "resolved_at": {
+ "name": "resolved_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "bot_tasks_owner_created_idx": {
+ "name": "bot_tasks_owner_created_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "bot_tasks_channel_idx": {
+ "name": "bot_tasks_channel_idx",
+ "columns": [
+ {
+ "expression": "channel_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "bot_tasks_suggestion_idx": {
+ "name": "bot_tasks_suggestion_idx",
+ "columns": [
+ {
+ "expression": "suggestion_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "bot_tasks_owner_user_id_users_id_fk": {
+ "name": "bot_tasks_owner_user_id_users_id_fk",
+ "tableFrom": "bot_tasks",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "bot_tasks_agent_id_agents_id_fk": {
+ "name": "bot_tasks_agent_id_agents_id_fk",
+ "tableFrom": "bot_tasks",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "bot_tasks_channel_id_channels_id_fk": {
+ "name": "bot_tasks_channel_id_channels_id_fk",
+ "tableFrom": "bot_tasks",
+ "tableTo": "channels",
+ "columnsFrom": [
+ "channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "bot_tasks_suggestion_id_proactive_suggestions_id_fk": {
+ "name": "bot_tasks_suggestion_id_proactive_suggestions_id_fk",
+ "tableFrom": "bot_tasks",
+ "tableTo": "proactive_suggestions",
+ "columnsFrom": [
+ "suggestion_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "bot_tasks_origin_check": {
+ "name": "bot_tasks_origin_check",
+ "value": "\"bot_tasks\".\"origin\" IN ('check_in')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.demonstrations": {
+ "name": "demonstrations",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "bot_id": {
+ "name": "bot_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'recording'"
+ },
+ "actions": {
+ "name": "actions",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "draft": {
+ "name": "draft",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "skill_slug": {
+ "name": "skill_slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "finished_at": {
+ "name": "finished_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "demonstrations_owner_idx": {
+ "name": "demonstrations_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "demonstrations_active_bot_idx": {
+ "name": "demonstrations_active_bot_idx",
+ "columns": [
+ {
+ "expression": "bot_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"demonstrations\".\"status\" = 'recording'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "demonstrations_owner_user_id_users_id_fk": {
+ "name": "demonstrations_owner_user_id_users_id_fk",
+ "tableFrom": "demonstrations",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "demonstrations_bot_id_agents_id_fk": {
+ "name": "demonstrations_bot_id_agents_id_fk",
+ "tableFrom": "demonstrations",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "bot_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.delivery_bindings": {
+ "name": "delivery_bindings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transport": {
+ "name": "transport",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "identity": {
+ "name": "identity",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "realm": {
+ "name": "realm",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "address": {
+ "name": "address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "mention_id": {
+ "name": "mention_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "opted_out_at": {
+ "name": "opted_out_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "delivery_binding_identity_idx": {
+ "name": "delivery_binding_identity_idx",
+ "columns": [
+ {
+ "expression": "transport",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "realm",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "identity",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "delivery_bindings_owner_idx": {
+ "name": "delivery_bindings_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "delivery_bindings_owner_user_id_users_id_fk": {
+ "name": "delivery_bindings_owner_user_id_users_id_fk",
+ "tableFrom": "delivery_bindings",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "delivery_bindings_channel_id_channels_id_fk": {
+ "name": "delivery_bindings_channel_id_channels_id_fk",
+ "tableFrom": "delivery_bindings",
+ "tableTo": "channels",
+ "columnsFrom": [
+ "channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "delivery_bindings_agent_id_agents_id_fk": {
+ "name": "delivery_bindings_agent_id_agents_id_fk",
+ "tableFrom": "delivery_bindings",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.delivery_challenges": {
+ "name": "delivery_challenges",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transport": {
+ "name": "transport",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "address": {
+ "name": "address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "consumed_at": {
+ "name": "consumed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "delivery_challenges_owner_user_id_users_id_fk": {
+ "name": "delivery_challenges_owner_user_id_users_id_fk",
+ "tableFrom": "delivery_challenges",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "delivery_challenges_channel_id_channels_id_fk": {
+ "name": "delivery_challenges_channel_id_channels_id_fk",
+ "tableFrom": "delivery_challenges",
+ "tableTo": "channels",
+ "columnsFrom": [
+ "channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "delivery_challenges_agent_id_agents_id_fk": {
+ "name": "delivery_challenges_agent_id_agents_id_fk",
+ "tableFrom": "delivery_challenges",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.delivery_inbox": {
+ "name": "delivery_inbox",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "binding_id": {
+ "name": "binding_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source": {
+ "name": "source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "realm": {
+ "name": "realm",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "text": {
+ "name": "text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "state": {
+ "name": "state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'queued'"
+ },
+ "error": {
+ "name": "error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "delivery_inbox_dedupe_idx": {
+ "name": "delivery_inbox_dedupe_idx",
+ "columns": [
+ {
+ "expression": "source",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "realm",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "external_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "delivery_inbox_owner_idx": {
+ "name": "delivery_inbox_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_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": {
+ "delivery_inbox_owner_user_id_users_id_fk": {
+ "name": "delivery_inbox_owner_user_id_users_id_fk",
+ "tableFrom": "delivery_inbox",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "delivery_inbox_channel_id_channels_id_fk": {
+ "name": "delivery_inbox_channel_id_channels_id_fk",
+ "tableFrom": "delivery_inbox",
+ "tableTo": "channels",
+ "columnsFrom": [
+ "channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "delivery_inbox_agent_id_agents_id_fk": {
+ "name": "delivery_inbox_agent_id_agents_id_fk",
+ "tableFrom": "delivery_inbox",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.delivery_outbox": {
+ "name": "delivery_outbox",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "dedupe_key": {
+ "name": "dedupe_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "binding_id": {
+ "name": "binding_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "device_id": {
+ "name": "device_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "transport": {
+ "name": "transport",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "text": {
+ "name": "text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "request_id": {
+ "name": "request_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "state": {
+ "name": "state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'queued'"
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error": {
+ "name": "error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "delivery_outbox_owner_idx": {
+ "name": "delivery_outbox_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "delivery_outbox_provider_idx": {
+ "name": "delivery_outbox_provider_idx",
+ "columns": [
+ {
+ "expression": "transport",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "provider_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "delivery_outbox_owner_user_id_users_id_fk": {
+ "name": "delivery_outbox_owner_user_id_users_id_fk",
+ "tableFrom": "delivery_outbox",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "delivery_outbox_channel_id_channels_id_fk": {
+ "name": "delivery_outbox_channel_id_channels_id_fk",
+ "tableFrom": "delivery_outbox",
+ "tableTo": "channels",
+ "columnsFrom": [
+ "channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "delivery_outbox_agent_id_agents_id_fk": {
+ "name": "delivery_outbox_agent_id_agents_id_fk",
+ "tableFrom": "delivery_outbox",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "delivery_outbox_dedupe_key_unique": {
+ "name": "delivery_outbox_dedupe_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "dedupe_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.push_devices": {
+ "name": "push_devices",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "platform": {
+ "name": "platform",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "push_devices_owner_idx": {
+ "name": "push_devices_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "push_devices_owner_user_id_users_id_fk": {
+ "name": "push_devices_owner_user_id_users_id_fk",
+ "tableFrom": "push_devices",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "push_devices_token_unique": {
+ "name": "push_devices_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.group_bot_threads": {
+ "name": "group_bot_threads",
+ "schema": "",
+ "columns": {
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "group_bot_threads_thread_idx": {
+ "name": "group_bot_threads_thread_idx",
+ "columns": [
+ {
+ "expression": "thread_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "group_bot_threads_owner_user_id_users_id_fk": {
+ "name": "group_bot_threads_owner_user_id_users_id_fk",
+ "tableFrom": "group_bot_threads",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "group_bot_threads_channel_id_channels_id_fk": {
+ "name": "group_bot_threads_channel_id_channels_id_fk",
+ "tableFrom": "group_bot_threads",
+ "tableTo": "channels",
+ "columnsFrom": [
+ "channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "group_bot_threads_owner_user_id_channel_id_agent_id_pk": {
+ "name": "group_bot_threads_owner_user_id_channel_id_agent_id_pk",
+ "columns": [
+ "owner_user_id",
+ "channel_id",
+ "agent_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.group_messages": {
+ "name": "group_messages",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "channel_id": {
+ "name": "channel_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": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "text": {
+ "name": "text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'completed'"
+ },
+ "details": {
+ "name": "details",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "group_messages_channel_idx": {
+ "name": "group_messages_channel_idx",
+ "columns": [
+ {
+ "expression": "channel_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": {
+ "group_messages_channel_id_channels_id_fk": {
+ "name": "group_messages_channel_id_channels_id_fk",
+ "tableFrom": "group_messages",
+ "tableTo": "channels",
+ "columnsFrom": [
+ "channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "group_messages_owner_user_id_users_id_fk": {
+ "name": "group_messages_owner_user_id_users_id_fk",
+ "tableFrom": "group_messages",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "group_messages_status_check": {
+ "name": "group_messages_status_check",
+ "value": "\"group_messages\".\"status\" IN ('queued', 'running', 'waiting', 'completed', 'failed')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.action_records": {
+ "name": "action_records",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "surface": {
+ "name": "surface",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "bot_id": {
+ "name": "bot_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "actor_user_id": {
+ "name": "actor_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "tool_name": {
+ "name": "tool_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "command": {
+ "name": "command",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "outcome": {
+ "name": "outcome",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "action_records_created_idx": {
+ "name": "action_records_created_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "action_records_bot_idx": {
+ "name": "action_records_bot_idx",
+ "columns": [
+ {
+ "expression": "bot_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": {},
+ "policies": {},
+ "checkConstraints": {
+ "action_records_surface_check": {
+ "name": "action_records_surface_check",
+ "value": "\"action_records\".\"surface\" IN ('cloud', 'local')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.capability_settings": {
+ "name": "capability_settings",
+ "schema": "",
+ "columns": {
+ "scope_kind": {
+ "name": "scope_kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope_id": {
+ "name": "scope_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "capability": {
+ "name": "capability",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "allowed": {
+ "name": "allowed",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_by": {
+ "name": "updated_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "capability_settings_scope_kind_scope_id_capability_pk": {
+ "name": "capability_settings_scope_kind_scope_id_capability_pk",
+ "columns": [
+ "scope_kind",
+ "scope_id",
+ "capability"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "capability_settings_scope_kind_check": {
+ "name": "capability_settings_scope_kind_check",
+ "value": "\"capability_settings\".\"scope_kind\" IN ('organization', 'role', 'group')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.enterprise_settings": {
+ "name": "enterprise_settings",
+ "schema": "",
+ "columns": {
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_by": {
+ "name": "updated_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.model_usage": {
+ "name": "model_usage",
+ "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": false
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "run_id": {
+ "name": "run_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source": {
+ "name": "source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "allowed": {
+ "name": "allowed",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "model_usage_created_idx": {
+ "name": "model_usage_created_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "model_usage_model_idx": {
+ "name": "model_usage_model_idx",
+ "columns": [
+ {
+ "expression": "model",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "model_usage_source_check": {
+ "name": "model_usage_source_check",
+ "value": "\"model_usage\".\"source\" IN ('configured', 'observed', 'unknown')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.network_policies": {
+ "name": "network_policies",
+ "schema": "",
+ "columns": {
+ "scope_kind": {
+ "name": "scope_kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scope_id": {
+ "name": "scope_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "mode": {
+ "name": "mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "rules": {
+ "name": "rules",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{\"entries\":[]}'::jsonb"
+ },
+ "locked": {
+ "name": "locked",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "updated_by": {
+ "name": "updated_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "network_policies_scope_kind_scope_id_pk": {
+ "name": "network_policies_scope_kind_scope_id_pk",
+ "columns": [
+ "scope_kind",
+ "scope_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "network_policies_scope_kind_check": {
+ "name": "network_policies_scope_kind_check",
+ "value": "\"network_policies\".\"scope_kind\" IN ('organization', 'group')"
+ },
+ "network_policies_mode_check": {
+ "name": "network_policies_mode_check",
+ "value": "\"network_policies\".\"mode\" IN ('allow_all', 'defaults_plus_allowlist', 'allowlist_only')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.scim_connection_bindings": {
+ "name": "scim_connection_bindings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "connection_id": {
+ "name": "connection_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "connection_key": {
+ "name": "connection_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provisioning_domain_id": {
+ "name": "provisioning_domain_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "decommissioned_at": {
+ "name": "decommissioned_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "decommission_status": {
+ "name": "decommission_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'active'"
+ },
+ "decommission_cursor_user_id": {
+ "name": "decommission_cursor_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "decommission_reconciled_user_count": {
+ "name": "decommission_reconciled_user_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "decommission_batch_count": {
+ "name": "decommission_batch_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "decommission_revision": {
+ "name": "decommission_revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "decommission_completed_at": {
+ "name": "decommission_completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "decommission_lease_id": {
+ "name": "decommission_lease_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "decommission_lease_expires_at": {
+ "name": "decommission_lease_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "scim_connection_bindings_connection_idx": {
+ "name": "scim_connection_bindings_connection_idx",
+ "columns": [
+ {
+ "expression": "connection_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "scim_connection_bindings_connection_key_unique": {
+ "name": "scim_connection_bindings_connection_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "connection_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scim_group_members": {
+ "name": "scim_group_members",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "connection_id": {
+ "name": "connection_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "group_id": {
+ "name": "group_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scim_user_id": {
+ "name": "scim_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "membership_key": {
+ "name": "membership_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "scim_group_members_connection_idx": {
+ "name": "scim_group_members_connection_idx",
+ "columns": [
+ {
+ "expression": "connection_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scim_group_members_group_idx": {
+ "name": "scim_group_members_group_idx",
+ "columns": [
+ {
+ "expression": "group_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scim_group_members_scim_user_idx": {
+ "name": "scim_group_members_scim_user_idx",
+ "columns": [
+ {
+ "expression": "scim_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scim_group_members_group_id_scim_groups_id_fk": {
+ "name": "scim_group_members_group_id_scim_groups_id_fk",
+ "tableFrom": "scim_group_members",
+ "tableTo": "scim_groups",
+ "columnsFrom": [
+ "group_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "scim_group_members_scim_user_id_scim_users_id_fk": {
+ "name": "scim_group_members_scim_user_id_scim_users_id_fk",
+ "tableFrom": "scim_group_members",
+ "tableTo": "scim_users",
+ "columnsFrom": [
+ "scim_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "scim_group_members_membership_key_unique": {
+ "name": "scim_group_members_membership_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "membership_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scim_groups": {
+ "name": "scim_groups",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "connection_id": {
+ "name": "connection_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provisioning_domain_id": {
+ "name": "provisioning_domain_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "revision": {
+ "name": "revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "display_name": {
+ "name": "display_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "display_name_key": {
+ "name": "display_name_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "external_id_key": {
+ "name": "external_id_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "order_key": {
+ "name": "order_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "scim_groups_connection_idx": {
+ "name": "scim_groups_connection_idx",
+ "columns": [
+ {
+ "expression": "connection_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scim_groups_domain_idx": {
+ "name": "scim_groups_domain_idx",
+ "columns": [
+ {
+ "expression": "provisioning_domain_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "scim_groups_display_name_key_unique": {
+ "name": "scim_groups_display_name_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "display_name_key"
+ ]
+ },
+ "scim_groups_external_id_key_unique": {
+ "name": "scim_groups_external_id_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "external_id_key"
+ ]
+ },
+ "scim_groups_order_key_unique": {
+ "name": "scim_groups_order_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "order_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scim_identity_tombstones": {
+ "name": "scim_identity_tombstones",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "connection_id": {
+ "name": "connection_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provisioning_domain_id": {
+ "name": "provisioning_domain_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "external_id_key": {
+ "name": "external_id_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "profile": {
+ "name": "profile",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "scim_identity_tombstones_connection_idx": {
+ "name": "scim_identity_tombstones_connection_idx",
+ "columns": [
+ {
+ "expression": "connection_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scim_identity_tombstones_domain_idx": {
+ "name": "scim_identity_tombstones_domain_idx",
+ "columns": [
+ {
+ "expression": "provisioning_domain_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scim_identity_tombstones_user_idx": {
+ "name": "scim_identity_tombstones_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scim_identity_tombstones_user_id_users_id_fk": {
+ "name": "scim_identity_tombstones_user_id_users_id_fk",
+ "tableFrom": "scim_identity_tombstones",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "scim_identity_tombstones_external_id_key_unique": {
+ "name": "scim_identity_tombstones_external_id_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "external_id_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scim_projection_grants": {
+ "name": "scim_projection_grants",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "connection_id": {
+ "name": "connection_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provisioning_domain_id": {
+ "name": "provisioning_domain_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scim_user_id": {
+ "name": "scim_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source_kind": {
+ "name": "source_kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source_id": {
+ "name": "source_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source_value": {
+ "name": "source_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "grant_key": {
+ "name": "grant_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "scim_projection_grants_connection_idx": {
+ "name": "scim_projection_grants_connection_idx",
+ "columns": [
+ {
+ "expression": "connection_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scim_projection_grants_domain_idx": {
+ "name": "scim_projection_grants_domain_idx",
+ "columns": [
+ {
+ "expression": "provisioning_domain_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scim_projection_grants_scim_user_idx": {
+ "name": "scim_projection_grants_scim_user_idx",
+ "columns": [
+ {
+ "expression": "scim_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scim_projection_grants_user_idx": {
+ "name": "scim_projection_grants_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scim_projection_grants_scim_user_id_scim_users_id_fk": {
+ "name": "scim_projection_grants_scim_user_id_scim_users_id_fk",
+ "tableFrom": "scim_projection_grants",
+ "tableTo": "scim_users",
+ "columnsFrom": [
+ "scim_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "scim_projection_grants_user_id_users_id_fk": {
+ "name": "scim_projection_grants_user_id_users_id_fk",
+ "tableFrom": "scim_projection_grants",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "scim_projection_grants_grant_key_unique": {
+ "name": "scim_projection_grants_grant_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "grant_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scim_subjects": {
+ "name": "scim_subjects",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "profile_source_id": {
+ "name": "profile_source_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revision": {
+ "name": "revision",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "scim_subjects_profile_source_idx": {
+ "name": "scim_subjects_profile_source_idx",
+ "columns": [
+ {
+ "expression": "profile_source_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scim_subjects_user_id_users_id_fk": {
+ "name": "scim_subjects_user_id_users_id_fk",
+ "tableFrom": "scim_subjects",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "scim_subjects_user_id_unique": {
+ "name": "scim_subjects_user_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "user_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scim_users": {
+ "name": "scim_users",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "connection_id": {
+ "name": "connection_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provisioning_domain_id": {
+ "name": "provisioning_domain_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "connection_user_key": {
+ "name": "connection_user_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_name": {
+ "name": "user_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_name_key": {
+ "name": "user_name_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "primary_email": {
+ "name": "primary_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "work_email_value_index": {
+ "name": "work_email_value_index",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email_value_index": {
+ "name": "email_value_index",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "display_name": {
+ "name": "display_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "formatted_name": {
+ "name": "formatted_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "given_name": {
+ "name": "given_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "family_name": {
+ "name": "family_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "serialized_emails": {
+ "name": "serialized_emails",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "serialized_attributes": {
+ "name": "serialized_attributes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "external_id_key": {
+ "name": "external_id_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "active": {
+ "name": "active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "order_key": {
+ "name": "order_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "scim_users_connection_idx": {
+ "name": "scim_users_connection_idx",
+ "columns": [
+ {
+ "expression": "connection_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scim_users_domain_idx": {
+ "name": "scim_users_domain_idx",
+ "columns": [
+ {
+ "expression": "provisioning_domain_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "scim_users_user_idx": {
+ "name": "scim_users_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scim_users_user_id_users_id_fk": {
+ "name": "scim_users_user_id_users_id_fk",
+ "tableFrom": "scim_users",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "scim_users_connection_user_key_unique": {
+ "name": "scim_users_connection_user_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "connection_user_key"
+ ]
+ },
+ "scim_users_user_name_key_unique": {
+ "name": "scim_users_user_name_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "user_name_key"
+ ]
+ },
+ "scim_users_external_id_key_unique": {
+ "name": "scim_users_external_id_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "external_id_key"
+ ]
+ },
+ "scim_users_order_key_unique": {
+ "name": "scim_users_order_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "order_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.bot_lifecycle": {
+ "name": "bot_lifecycle",
+ "schema": "",
+ "columns": {
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "paused_at": {
+ "name": "paused_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "notify": {
+ "name": "notify",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'all'"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "bot_lifecycle_owner_user_id_users_id_fk": {
+ "name": "bot_lifecycle_owner_user_id_users_id_fk",
+ "tableFrom": "bot_lifecycle",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "bot_lifecycle_agent_id_agents_id_fk": {
+ "name": "bot_lifecycle_agent_id_agents_id_fk",
+ "tableFrom": "bot_lifecycle",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "bot_lifecycle_owner_user_id_agent_id_pk": {
+ "name": "bot_lifecycle_owner_user_id_agent_id_pk",
+ "columns": [
+ "owner_user_id",
+ "agent_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.update_routing_preferences": {
+ "name": "update_routing_preferences",
+ "schema": "",
+ "columns": {
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transports": {
+ "name": "transports",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "update_routing_preferences_owner_user_id_users_id_fk": {
+ "name": "update_routing_preferences_owner_user_id_users_id_fk",
+ "tableFrom": "update_routing_preferences",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "update_routing_preferences_owner_user_id_kind_pk": {
+ "name": "update_routing_preferences_owner_user_id_kind_pk",
+ "columns": [
+ "owner_user_id",
+ "kind"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.saved_logins": {
+ "name": "saved_logins",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "origin": {
+ "name": "origin",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "encrypted_password": {
+ "name": "encrypted_password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "last_used_at": {
+ "name": "last_used_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "saved_logins_owner_origin_username_idx": {
+ "name": "saved_logins_owner_origin_username_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "origin",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "username",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "saved_logins_owner_idx": {
+ "name": "saved_logins_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "saved_logins_owner_user_id_users_id_fk": {
+ "name": "saved_logins_owner_user_id_users_id_fk",
+ "tableFrom": "saved_logins",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.sign_in_requests": {
+ "name": "sign_in_requests",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "owner_user_id": {
+ "name": "owner_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "bot_id": {
+ "name": "bot_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "origin": {
+ "name": "origin",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reason": {
+ "name": "reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "method": {
+ "name": "method",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "outcome": {
+ "name": "outcome",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "thread_id": {
+ "name": "thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "tool_call_id": {
+ "name": "tool_call_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "control_request_id": {
+ "name": "control_request_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "continuation": {
+ "name": "continuation",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "resolved_at": {
+ "name": "resolved_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "filling_until": {
+ "name": "filling_until",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "sign_in_requests_owner_idx": {
+ "name": "sign_in_requests_owner_idx",
+ "columns": [
+ {
+ "expression": "owner_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "sign_in_requests_owner_user_id_users_id_fk": {
+ "name": "sign_in_requests_owner_user_id_users_id_fk",
+ "tableFrom": "sign_in_requests",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "sign_in_requests_status_check": {
+ "name": "sign_in_requests_status_check",
+ "value": "\"sign_in_requests\".\"status\" IN ('pending', 'filling', 'code_required', 'taken_over', 'signed_in', 'failed', 'cancelled', 'expired')"
+ },
+ "sign_in_requests_method_check": {
+ "name": "sign_in_requests_method_check",
+ "value": "\"sign_in_requests\".\"method\" IS NULL OR \"sign_in_requests\".\"method\" IN ('typed', 'saved', 'takeover')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.team_bot_assignments": {
+ "name": "team_bot_assignments",
+ "schema": "",
+ "columns": {
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "group_name": {
+ "name": "group_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "assigned_by": {
+ "name": "assigned_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "assigned_at": {
+ "name": "assigned_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "team_bot_assignments_agent_id_agents_id_fk": {
+ "name": "team_bot_assignments_agent_id_agents_id_fk",
+ "tableFrom": "team_bot_assignments",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "team_bot_assignments_assigned_by_users_id_fk": {
+ "name": "team_bot_assignments_assigned_by_users_id_fk",
+ "tableFrom": "team_bot_assignments",
+ "tableTo": "users",
+ "columnsFrom": [
+ "assigned_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "team_bot_assignments_agent_id_group_name_pk": {
+ "name": "team_bot_assignments_agent_id_group_name_pk",
+ "columns": [
+ "agent_id",
+ "group_name"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.team_bot_audience": {
+ "name": "team_bot_audience",
+ "schema": "",
+ "columns": {
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "team_bot_audience_agent_id_agents_id_fk": {
+ "name": "team_bot_audience_agent_id_agents_id_fk",
+ "tableFrom": "team_bot_audience",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "team_bot_audience_agent_id_kind_value_pk": {
+ "name": "team_bot_audience_agent_id_kind_value_pk",
+ "columns": [
+ "agent_id",
+ "kind",
+ "value"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "team_bot_audience_kind_check": {
+ "name": "team_bot_audience_kind_check",
+ "value": "\"team_bot_audience\".\"kind\" IN ('user', 'group')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.team_bot_consent_defaults": {
+ "name": "team_bot_consent_defaults",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "server_id": {
+ "name": "server_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "team_bot_consent_defaults_user_id_users_id_fk": {
+ "name": "team_bot_consent_defaults_user_id_users_id_fk",
+ "tableFrom": "team_bot_consent_defaults",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "team_bot_consent_defaults_user_id_server_id_pk": {
+ "name": "team_bot_consent_defaults_user_id_server_id_pk",
+ "columns": [
+ "user_id",
+ "server_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.team_bot_consents": {
+ "name": "team_bot_consents",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "server_id": {
+ "name": "server_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "decision": {
+ "name": "decision",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "team_bot_consents_user_id_users_id_fk": {
+ "name": "team_bot_consents_user_id_users_id_fk",
+ "tableFrom": "team_bot_consents",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "team_bot_consents_agent_id_agents_id_fk": {
+ "name": "team_bot_consents_agent_id_agents_id_fk",
+ "tableFrom": "team_bot_consents",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "team_bot_consents_user_id_agent_id_server_id_pk": {
+ "name": "team_bot_consents_user_id_agent_id_server_id_pk",
+ "columns": [
+ "user_id",
+ "agent_id",
+ "server_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "team_bot_consents_decision_check": {
+ "name": "team_bot_consents_decision_check",
+ "value": "\"team_bot_consents\".\"decision\" IN ('always', 'once')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.team_bot_publications": {
+ "name": "team_bot_publications",
+ "schema": "",
+ "columns": {
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "published_by": {
+ "name": "published_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "audience": {
+ "name": "audience",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "published_at": {
+ "name": "published_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "team_bot_publications_agent_id_agents_id_fk": {
+ "name": "team_bot_publications_agent_id_agents_id_fk",
+ "tableFrom": "team_bot_publications",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "team_bot_publications_published_by_users_id_fk": {
+ "name": "team_bot_publications_published_by_users_id_fk",
+ "tableFrom": "team_bot_publications",
+ "tableTo": "users",
+ "columnsFrom": [
+ "published_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "team_bot_publications_audience_check": {
+ "name": "team_bot_publications_audience_check",
+ "value": "\"team_bot_publications\".\"audience\" IN ('team', 'people')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.shared_use_approval_members": {
+ "name": "shared_use_approval_members",
+ "schema": "",
+ "columns": {
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "server_id": {
+ "name": "server_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "shared_use_approval_members_approval_fk": {
+ "name": "shared_use_approval_members_approval_fk",
+ "tableFrom": "shared_use_approval_members",
+ "tableTo": "shared_use_approvals",
+ "columnsFrom": [
+ "agent_id",
+ "server_id"
+ ],
+ "columnsTo": [
+ "agent_id",
+ "server_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "shared_use_approval_members_agent_id_server_id_kind_value_pk": {
+ "name": "shared_use_approval_members_agent_id_server_id_kind_value_pk",
+ "columns": [
+ "agent_id",
+ "server_id",
+ "kind",
+ "value"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "shared_use_approval_members_kind_check": {
+ "name": "shared_use_approval_members_kind_check",
+ "value": "\"shared_use_approval_members\".\"kind\" IN ('user', 'group')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.shared_use_approvals": {
+ "name": "shared_use_approvals",
+ "schema": "",
+ "columns": {
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "server_id": {
+ "name": "server_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "audience": {
+ "name": "audience",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "outside_input": {
+ "name": "outside_input",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "approved_by": {
+ "name": "approved_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "approved_as": {
+ "name": "approved_as",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "approved_at": {
+ "name": "approved_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "shared_use_approvals_agent_id_agents_id_fk": {
+ "name": "shared_use_approvals_agent_id_agents_id_fk",
+ "tableFrom": "shared_use_approvals",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "shared_use_approvals_server_id_mcp_servers_id_fk": {
+ "name": "shared_use_approvals_server_id_mcp_servers_id_fk",
+ "tableFrom": "shared_use_approvals",
+ "tableTo": "mcp_servers",
+ "columnsFrom": [
+ "server_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "shared_use_approvals_agent_id_server_id_pk": {
+ "name": "shared_use_approvals_agent_id_server_id_pk",
+ "columns": [
+ "agent_id",
+ "server_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "shared_use_approvals_audience_check": {
+ "name": "shared_use_approvals_audience_check",
+ "value": "\"shared_use_approvals\".\"audience\" IN ('owner', 'people', 'team')"
+ },
+ "shared_use_approvals_approved_as_check": {
+ "name": "shared_use_approvals_approved_as_check",
+ "value": "\"shared_use_approvals\".\"approved_as\" IS NULL OR \"shared_use_approvals\".\"approved_as\" IN ('admin', 'owner')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.shared_use_requests": {
+ "name": "shared_use_requests",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "server_id": {
+ "name": "server_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "proposed_audience": {
+ "name": "proposed_audience",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "proposed_outside_input": {
+ "name": "proposed_outside_input",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "proposed_members": {
+ "name": "proposed_members",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "reason": {
+ "name": "reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "requested_by": {
+ "name": "requested_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "decided_by": {
+ "name": "decided_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "decided_at": {
+ "name": "decided_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "shared_use_requests_pending_idx": {
+ "name": "shared_use_requests_pending_idx",
+ "columns": [
+ {
+ "expression": "agent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "server_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"shared_use_requests\".\"status\" = 'pending'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "shared_use_requests_status_idx": {
+ "name": "shared_use_requests_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "shared_use_requests_agent_id_agents_id_fk": {
+ "name": "shared_use_requests_agent_id_agents_id_fk",
+ "tableFrom": "shared_use_requests",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "shared_use_requests_server_id_mcp_servers_id_fk": {
+ "name": "shared_use_requests_server_id_mcp_servers_id_fk",
+ "tableFrom": "shared_use_requests",
+ "tableTo": "mcp_servers",
+ "columnsFrom": [
+ "server_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "shared_use_requests_audience_check": {
+ "name": "shared_use_requests_audience_check",
+ "value": "\"shared_use_requests\".\"proposed_audience\" IN ('owner', 'people', 'team')"
+ },
+ "shared_use_requests_reason_check": {
+ "name": "shared_use_requests_reason_check",
+ "value": "\"shared_use_requests\".\"reason\" IN ('publish', 'trigger', 'grant', 'refused_call')"
+ },
+ "shared_use_requests_status_check": {
+ "name": "shared_use_requests_status_check",
+ "value": "\"shared_use_requests\".\"status\" IN ('pending', 'approved', 'declined', 'superseded')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.identity_link_challenges": {
+ "name": "identity_link_challenges",
+ "schema": "",
+ "columns": {
+ "token_hash": {
+ "name": "token_hash",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "identity_link_challenges_expiry_idx": {
+ "name": "identity_link_challenges_expiry_idx",
+ "columns": [
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "identity_link_challenges_user_provider_idx": {
+ "name": "identity_link_challenges_user_provider_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.identity_links": {
+ "name": "identity_links",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "realm": {
+ "name": "realm",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "subject": {
+ "name": "subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "handle": {
+ "name": "handle",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "verified_by": {
+ "name": "verified_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "credential_id": {
+ "name": "credential_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'active'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "identity_links_identity_idx": {
+ "name": "identity_links_identity_idx",
+ "columns": [
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "realm",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "subject",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "identity_links_user_realm_idx": {
+ "name": "identity_links_user_realm_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "realm",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "identity_links_credential_idx": {
+ "name": "identity_links_credential_idx",
+ "columns": [
+ {
+ "expression": "credential_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"identity_links\".\"credential_id\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "identity_links_credential_id_credentials_id_fk": {
+ "name": "identity_links_credential_id_credentials_id_fk",
+ "tableFrom": "identity_links",
+ "tableTo": "credentials",
+ "columnsFrom": [
+ "credential_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "identity_links_verified_by_check": {
+ "name": "identity_links_verified_by_check",
+ "value": "\"identity_links\".\"verified_by\" IN ('challenge', 'oauth')"
+ },
+ "identity_links_status_check": {
+ "name": "identity_links_status_check",
+ "value": "\"identity_links\".\"status\" IN ('active', 'needs_reconnect')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.recorder_connect_codes": {
+ "name": "recorder_connect_codes",
+ "schema": "",
+ "columns": {
+ "code_hash": {
+ "name": "code_hash",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "via": {
+ "name": "via",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_session": {
+ "name": "organization_session",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "organization_session_expires_at": {
+ "name": "organization_session_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "recorder_connect_codes_user_idx": {
+ "name": "recorder_connect_codes_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "recorder_connect_codes_user_id_users_id_fk": {
+ "name": "recorder_connect_codes_user_id_users_id_fk",
+ "tableFrom": "recorder_connect_codes",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.recorder_connections": {
+ "name": "recorder_connections",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token_hash": {
+ "name": "token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "label": {
+ "name": "label",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "via": {
+ "name": "via",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "extension_id": {
+ "name": "extension_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "install_id": {
+ "name": "install_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_session": {
+ "name": "organization_session",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "organization_session_expires_at": {
+ "name": "organization_session_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "last_used_at": {
+ "name": "last_used_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "activity": {
+ "name": "activity",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "activity_at": {
+ "name": "activity_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "recorder_connections_user_idx": {
+ "name": "recorder_connections_user_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": {}
+ },
+ "recorder_connections_live_install_idx": {
+ "name": "recorder_connections_live_install_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "install_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"recorder_connections\".\"revoked_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "recorder_connections_user_id_users_id_fk": {
+ "name": "recorder_connections_user_id_users_id_fk",
+ "tableFrom": "recorder_connections",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "recorder_connections_token_hash_unique": {
+ "name": "recorder_connections_token_hash_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token_hash"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.recorder_trajectory_bindings": {
+ "name": "recorder_trajectory_bindings",
+ "schema": "",
+ "columns": {
+ "trajectory_id": {
+ "name": "trajectory_id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "recorder_trajectory_bindings_user_idx": {
+ "name": "recorder_trajectory_bindings_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "recorder_trajectory_bindings_user_id_users_id_fk": {
+ "name": "recorder_trajectory_bindings_user_id_users_id_fk",
+ "tableFrom": "recorder_trajectory_bindings",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {
+ "public.agent_type": {
+ "name": "agent_type",
+ "schema": "public",
+ "values": [
+ "built_in",
+ "remote_ag_ui",
+ "remote_mastra"
+ ]
+ },
+ "public.credential_kind": {
+ "name": "credential_kind",
+ "schema": "public",
+ "values": [
+ "model",
+ "connector",
+ "agent",
+ "mcp",
+ "mcp_oauth_client",
+ "mcp_user_token"
+ ]
+ },
+ "public.role": {
+ "name": "role",
+ "schema": "public",
+ "values": [
+ "admin",
+ "user"
+ ]
+ },
+ "public.agent_visibility": {
+ "name": "agent_visibility",
+ "schema": "public",
+ "values": [
+ "public",
+ "private"
+ ]
+ },
+ "public.routine_run_status": {
+ "name": "routine_run_status",
+ "schema": "public",
+ "values": [
+ "succeeded",
+ "failed",
+ "skipped",
+ "waiting"
+ ]
+ },
+ "public.responsibility_run_status": {
+ "name": "responsibility_run_status",
+ "schema": "public",
+ "values": [
+ "queued",
+ "running",
+ "waiting",
+ "succeeded",
+ "failed",
+ "skipped"
+ ]
+ },
+ "public.responsibility_status": {
+ "name": "responsibility_status",
+ "schema": "public",
+ "values": [
+ "active",
+ "paused",
+ "completed"
+ ]
+ }
+ },
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/server/drizzle/meta/_journal.json b/server/drizzle/meta/_journal.json
index 21b7a7edff..88b52f2265 100644
--- a/server/drizzle/meta/_journal.json
+++ b/server/drizzle/meta/_journal.json
@@ -589,6 +589,20 @@
"when": 1794110188202,
"tag": "0083_learning_skill_approvals",
"breakpoints": true
+ },
+ {
+ "idx": 84,
+ "version": "7",
+ "when": 1794154009098,
+ "tag": "0084_brokered_accounts",
+ "breakpoints": true
+ },
+ {
+ "idx": 85,
+ "version": "7",
+ "when": 1794155767768,
+ "tag": "0085_approval_rule_account",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/server/src/approvals/policy.ts b/server/src/approvals/policy.ts
index 117373c420..e6467f2ccb 100644
--- a/server/src/approvals/policy.ts
+++ b/server/src/approvals/policy.ts
@@ -41,9 +41,33 @@ export function globMatches(pattern: string, value: string): boolean {
type AnyRule = Pick<
ApprovalRule,
"id" | "botId" | "toolRef" | "effect" | "scope" | "behaviour"
->;
+> & { account?: string | null };
-export function ruleMatches(rule: AnyRule, candidate: ApprovalCandidate) {
+/**
+ * Whether a rule is about this action.
+ *
+ * WHICH ACCOUNT, where the person holds several at the app. A rule saved for one account (the card
+ * named it when "Always allow here" was pressed) is about that account and no other. A personal rule
+ * that names no account still asks, hands off or holds for every account, which is the strict
+ * direction; but it no longer ALLOWS a call that names an account, because it was written before the
+ * person had a second one to tell apart, so it cannot be read as consent for a mailbox it never saw.
+ * A team rule is the admin's policy for the app and holds whichever account a call runs in.
+ */
+export function ruleMatches(
+ rule: AnyRule,
+ candidate: ApprovalCandidate,
+ layer: "team_rule" | "personal_rule" = "personal_rule",
+) {
+ const ruleAccount = rule.account?.trim().toLowerCase() || null;
+ const callAccount = candidate.account?.trim().toLowerCase() || null;
+ if (ruleAccount !== null && ruleAccount !== callAccount) return false;
+ if (
+ ruleAccount === null &&
+ callAccount !== null &&
+ layer === "personal_rule" &&
+ (rule.behaviour === "allow" || rule.behaviour === "pre_approved")
+ )
+ return false;
return (
globMatches(rule.botId, candidate.botId) &&
globMatches(rule.toolRef, candidate.toolRef) &&
@@ -64,7 +88,7 @@ export function strictestRule(
["personal_rule", personal],
] as const) {
for (const rule of rules) {
- if (!ruleMatches(rule, candidate)) continue;
+ if (!ruleMatches(rule, candidate, layer)) continue;
if (!best || RANK[rule.behaviour] > RANK[best.rule.behaviour])
best = { rule, layer };
}
diff --git a/server/src/approvals/service.ts b/server/src/approvals/service.ts
index ad65904b30..376fee191a 100644
--- a/server/src/approvals/service.ts
+++ b/server/src/approvals/service.ts
@@ -84,6 +84,8 @@ export function equivalenceOf(candidate: ApprovalCandidate): string {
// alone made every later call to that tool "the same" as one the person declined.
stable(candidate.target),
stable(candidate.args),
+ // Which mailbox: a denial of a send from one account says nothing about another.
+ ...(candidate.account ? [candidate.account] : []),
]),
)
.digest("hex");
@@ -771,6 +773,8 @@ function publicApproval(request: ApprovalRecord) {
toolCallId: action.toolCallId,
args: approvalPreview(action.args),
target: approvalPreview(action.target),
+ // The account the card names: "send email from dana@work.example on Gmail".
+ ...(action.account ? { account: action.account } : {}),
...(action.policy
? {
policy: {
diff --git a/server/src/approvals/store.ts b/server/src/approvals/store.ts
index e8e24eba3e..b9dce6de23 100644
--- a/server/src/approvals/store.ts
+++ b/server/src/approvals/store.ts
@@ -56,6 +56,11 @@ export function createApprovalStore(database: Database): ApprovalStore {
eq(approvalRules.toolRef, action.toolRef),
eq(approvalRules.effect, action.effect),
eq(approvalRules.scope, action.scope),
+ // One mailbox's saved rule never answers for another, and a call naming none is answered only
+ // by a rule that names none.
+ action.account
+ ? eq(approvalRules.account, action.account)
+ : isNull(approvalRules.account),
eq(approvalRules.behaviour, "allow"),
isNull(approvalRules.revokedAt),
);
@@ -174,6 +179,7 @@ export function createApprovalStore(database: Database): ApprovalStore {
toolRef: request.action.toolRef,
effect: request.action.effect,
scope: request.action.scope,
+ account: request.action.account ?? null,
});
const [updated] = await tx
.update(approvalRequests)
diff --git a/server/src/approvals/types.ts b/server/src/approvals/types.ts
index e787eda307..1e52b89ea3 100644
--- a/server/src/approvals/types.ts
+++ b/server/src/approvals/types.ts
@@ -93,6 +93,12 @@ export type ApprovalCandidate = {
* the person approves, so not in the action's digest.
*/
declined?: AuditEventInput;
+ /**
+ * Which of the person's accounts at an app the call runs in, by the address the person knows it
+ * by, where they hold more than one. Part of what makes two actions the same action: a call to one
+ * mailbox is never answered by an approval, a denial or a saved rule about another.
+ */
+ account?: string;
};
export type ApprovalAction = ApprovalCandidate & {
runId: string;
@@ -133,6 +139,8 @@ export type ApprovalRule = {
effect: string;
scope: string;
behaviour: RuleBehaviour;
+ /** The one account the rule is for, or null for the app whichever account; see `ruleMatches`. */
+ account?: string | null;
revokedAt: Date | null;
createdAt: Date;
};
@@ -434,6 +442,8 @@ export function approvalAction(candidate: ApprovalCandidate): ApprovalAction {
scope: candidate.scope,
args: candidate.args,
target: candidate.target,
+ // Only where there is one, so a request opened before accounts were named keeps its digest.
+ ...(candidate.account ? { account: candidate.account } : {}),
}),
)
.digest("hex");
diff --git a/server/src/db/schema/approvals.ts b/server/src/db/schema/approvals.ts
index 66320c6b75..0677d667d2 100644
--- a/server/src/db/schema/approvals.ts
+++ b/server/src/db/schema/approvals.ts
@@ -127,6 +127,13 @@ export const approvalRules = pgTable(
.$type()
.notNull()
.default("allow"),
+ /**
+ * Which of the person's accounts at the app the rule is for, where they hold several: the
+ * address the card named when "Always allow here" was pressed. A rule for one mailbox never
+ * covers another. Null is a rule about the app whichever account it runs in; see `ruleMatches`
+ * in approvals/policy.ts for what such a rule may still allow.
+ */
+ account: text("account"),
revokedAt: timestamp("revoked_at", { withTimezone: true }),
createdAt: timestamp("created_at", { withTimezone: true })
.notNull()
diff --git a/server/src/db/schema/plugins.ts b/server/src/db/schema/plugins.ts
index 55a7bede7d..90cf480f09 100644
--- a/server/src/db/schema/plugins.ts
+++ b/server/src/db/schema/plugins.ts
@@ -561,6 +561,67 @@ export const brokeredConnections = pgTable(
],
);
+/**
+ * Each account a person holds at one brokered app, when they hold more than the one.
+ *
+ * `brokered_connections` says a person has an app connected and stays one row per person per app.
+ * This table says WHICH accounts stand behind that row: two Gmail mailboxes, a work and a personal
+ * calendar. It is a record of what the broker last listed for the person, rebuilt from the vendor's
+ * own listing whenever the app's page loads (`syncBrokeredAccounts`), so a row here is never the
+ * only evidence that an account exists.
+ *
+ * `vendor_user_id` IS WHO THE ACCOUNT IS FILED UNDER AT THE BROKER, and `account_id` IS WHICH OF
+ * THEIR ACCOUNTS IT IS. Composio files every account a person holds under one user id and tells them
+ * apart by a connected account id, which a call passes to pick one. Arcade keeps one account per user
+ * id and provider, so a person's second account there is filed under a second user id of theirs
+ * (`~`) and `account_id` is empty. A call reads both off the row it picked.
+ *
+ * `user_id` IS ALWAYS A PERSON. A Shared app's team account is one account and lives only in
+ * `brokered_connections`. No key to `users`, for the reason `brokered_connections.user_id` has none.
+ *
+ * `identity` IS WHAT THE VENDOR SAYS THE ACCOUNT IS — the mailbox address, the login — and is what a
+ * person and a model name the account by. Null where the vendor published none.
+ *
+ * `is_default` MARKS THE ACCOUNT A CALL USES WHEN IT NAMES NONE. The first one listed is the default
+ * until the person picks another; a partial unique index holds it to at most one per person per app.
+ */
+export const brokeredAccounts = pgTable(
+ "brokered_accounts",
+ {
+ id: uuid("id").primaryKey().defaultRandom(),
+ provider: text("provider").$type<"composio" | "arcade">().notNull(),
+ app: text("app").notNull(),
+ userId: text("user_id").notNull(),
+ vendorUserId: text("vendor_user_id").notNull(),
+ /** The vendor's id for the account, or the empty string where the user id alone names it. */
+ accountId: text("account_id").notNull().default(""),
+ identity: text("identity"),
+ isDefault: boolean("is_default").notNull().default(false),
+ /** When the vendor says the account was made, where it says. */
+ connectedAt: timestamp("connected_at", { withTimezone: true }),
+ createdAt: timestamp("created_at", { withTimezone: true })
+ .notNull()
+ .defaultNow(),
+ },
+ (table) => [
+ check(
+ "brokered_accounts_provider_check",
+ sql`${table.provider} IN ('composio', 'arcade')`,
+ ),
+ uniqueIndex("brokered_accounts_account_idx").on(
+ table.provider,
+ table.app,
+ table.userId,
+ table.vendorUserId,
+ table.accountId,
+ ),
+ uniqueIndex("brokered_accounts_default_idx")
+ .on(table.provider, table.app, table.userId)
+ .where(sql`${table.isDefault}`),
+ index("brokered_accounts_user_idx").on(table.userId),
+ ],
+);
+
/**
* The name a person's accounts are filed under at a broker, where it is not their own id.
*
diff --git a/server/src/plugins/account-argument.ts b/server/src/plugins/account-argument.ts
new file mode 100644
index 0000000000..dcffec3f08
--- /dev/null
+++ b/server/src/plugins/account-argument.ts
@@ -0,0 +1,70 @@
+/**
+ * The optional `account` argument a brokered app's tools take, so a Bot can pick which of a
+ * person's accounts at the app a call runs in.
+ *
+ * A NAME AND NEVER AN ID. The model is told the person's accounts by the address or login the
+ * vendor reported for each, and it answers with one of those. The call path looks the name up among
+ * the accounts of the person in the session, so the most a model can do with this argument is pick
+ * between that person's own accounts. Anything it does not find is refused.
+ *
+ * OURS ONLY WHERE THE APP'S OWN SCHEMA DOES NOT ALREADY USE THE NAME. An action whose own parameters
+ * include `account` keeps it: the argument goes to the vendor untouched and the call runs in the
+ * person's default account. {@link accountArgumentFor} is the one test both halves ask, so the tool
+ * a model is offered and the call path cannot disagree about whose `account` it is.
+ */
+export const ACCOUNT_ARG = "account";
+
+/** Whether this action's schema leaves `account` free for the account choice. */
+export function accountArgumentFor(
+ inputSchema: Record | null | undefined,
+): typeof ACCOUNT_ARG | null {
+ const properties = inputSchema?.properties;
+ if (
+ properties &&
+ typeof properties === "object" &&
+ Object.hasOwn(properties, ACCOUNT_ARG)
+ )
+ return null;
+ return ACCOUNT_ARG;
+}
+
+/** One of a person's accounts at an app, as a model is told about it. */
+export type AccountChoice = { label: string; isDefault: boolean };
+
+/**
+ * The action's schema with the `account` argument added, naming the person's accounts.
+ *
+ * Only where there is a choice to make: with one account, or none recorded, the schema is the
+ * app's own and the call runs where it always did.
+ */
+export function withAccountArgument(
+ inputSchema: Record,
+ appTitle: string,
+ choices: readonly AccountChoice[],
+): Record {
+ if (choices.length < 2 || accountArgumentFor(inputSchema) === null)
+ return inputSchema;
+ const fallback = choices.find((choice) => choice.isDefault) ?? choices[0];
+ const properties =
+ inputSchema.properties && typeof inputSchema.properties === "object"
+ ? (inputSchema.properties as Record)
+ : {};
+ return {
+ ...inputSchema,
+ type: "object",
+ properties: {
+ ...properties,
+ [ACCOUNT_ARG]: {
+ type: "string",
+ enum: choices.map((choice) => choice.label),
+ description: `Which of the person's ${appTitle} accounts to use: ${choices
+ .map((choice) =>
+ choice.isDefault ? `${choice.label} (default)` : choice.label,
+ )
+ .join(
+ ", ",
+ )}. Pick the one the request is about, for example their work or personal account. Leave it out to use ${fallback?.label}.`,
+ },
+ },
+ };
+}
diff --git a/server/src/plugins/arcade-adapter.ts b/server/src/plugins/arcade-adapter.ts
index 6e04268df6..ad2ee7c354 100644
--- a/server/src/plugins/arcade-adapter.ts
+++ b/server/src/plugins/arcade-adapter.ts
@@ -1916,6 +1916,39 @@ export function createArcadeClient(
}
return null;
},
+ /*
+ * ONE ACCOUNT PER USER ID, WHICH IS ALL ARCADE KEEPS. Arcade holds one connection per user id
+ * and auth provider, so the accounts listed under one user id are at most the one live
+ * connection, named by the address or login the provider reported for it. A person's second
+ * account at an app is filed under a second user id of theirs (see `anotherArcadeAccount` in
+ * `./shared-accounts`) and is listed by asking about that id. No account id: a call picks an
+ * Arcade account by the user id it runs as.
+ */
+ accountsOf: async ({ account, toolkit }) => {
+ for (const row of await connectionsFor(
+ client,
+ account.vendorUserId,
+ toolkit,
+ )) {
+ if (!connectionIsLive(row)) continue;
+ const info = row.provider_user_info as
+ | { email?: unknown; name?: unknown; login?: unknown }
+ | null
+ | undefined;
+ return [
+ {
+ accountId: null,
+ identity:
+ textOf(info?.email) ??
+ textOf(info?.login) ??
+ textOf(info?.name) ??
+ null,
+ connectedAt: null,
+ },
+ ];
+ }
+ return [];
+ },
// The mint. Stays a TOP-LEVEL member: the Composio connect flow calls it from routes.ts, so it
// cannot move behind the nullable `authorizations` sub-surface.
//
diff --git a/server/src/plugins/broker.ts b/server/src/plugins/broker.ts
index dade6e7a53..f4272c77a5 100644
--- a/server/src/plugins/broker.ts
+++ b/server/src/plugins/broker.ts
@@ -712,7 +712,34 @@ export type ConnectedAppBroker = {
* that needs it must not find the field optional because the last one did not.
*/
action?: string;
+ /**
+ * This person already has an account at the app and is adding another one beside it.
+ *
+ * Composio's SDK refuses a second link for a person who already holds an account unless it is
+ * told more than one is wanted (`allowMultiple`), and that refusal is what lets Connect adopt an
+ * account the vendor holds and this deployment lost track of. So the flag is set only where the
+ * person's connection row stands and they asked for another account. Ignored by a broker that
+ * files a second account under a user id of its own (Arcade): the caller hands that broker a
+ * different `account` instead.
+ */
+ another?: boolean;
}): Promise<{ redirectUrl: string; authorizationId: string | null }>;
+ /**
+ * Every live account this person holds at the app, as the vendor lists them, oldest first.
+ *
+ * What the app's page lists by name and what a Bot's call can be pointed at. `accountId` is the
+ * vendor's id for the account where it tells a person's accounts apart by one (Composio's
+ * connected account id), and null where an account is named by its user id alone (Arcade).
+ * `identity` is what the vendor says the account is — an email address, a login — and is never
+ * read out of a token.
+ *
+ * OPTIONAL, because a broker that cannot list accounts one by one still connects one: without it
+ * the app's page shows the single connection and a call names no account, as before.
+ */
+ accountsOf?(request: {
+ account: AccountRef;
+ toolkit: string;
+ }): Promise;
/** Whether this account is currently attached to this app at the vendor. */
isConnected(request: {
account: AccountRef;
@@ -858,6 +885,13 @@ export type ConnectedAppBroker = {
}): Promise;
};
+/** One live account a person holds at an app; see {@link ConnectedAppBroker.accountsOf}. */
+export type BrokerAccount = {
+ accountId: string | null;
+ identity: string | null;
+ connectedAt: string | null;
+};
+
/**
* @deprecated Kept for one release while callers move to {@link ConnectedAppBroker}.
*/
diff --git a/server/src/plugins/composio-client/accounts.ts b/server/src/plugins/composio-client/accounts.ts
index 234696f7db..354d10570f 100644
--- a/server/src/plugins/composio-client/accounts.ts
+++ b/server/src/plugins/composio-client/accounts.ts
@@ -1,5 +1,10 @@
-import { BrokerRefusalError, type ComposioBroker } from "../broker";
import {
+ type BrokerAccount,
+ BrokerRefusalError,
+ type ComposioBroker,
+} from "../broker";
+import {
+ type AccountRow,
askVendor,
sent,
textOf,
@@ -114,5 +119,58 @@ export function accountMethods(context: ClientContext) {
}
return null;
},
- } satisfies Pick;
+
+ /*
+ * EVERY ACTIVE ACCOUNT, BY ITS ID AND THE NAME THE VENDOR PUBLISHED FOR IT. Composio fills
+ * `displayName` with the mailbox address or login once an account is active, on `data` and on
+ * `state.val` alike, and that is the only field read for the name; tokens beside it are never
+ * read. An account with no id cannot be named by a call and is left out, as the withdrawal
+ * leaves it out of what it can end. Oldest first, so the first account a person connected is
+ * the one that stays their default.
+ */
+ async accountsOf({ account, toolkit }) {
+ const rows = await accountsFor(account.vendorUserId, toolkit, CONNECTED);
+ const listed = new Map();
+ for (const row of rows) {
+ const id = textOf(row.id);
+ if (id === null || listed.has(id)) continue;
+ listed.set(id, {
+ accountId: id,
+ identity: displayNameOf(row),
+ connectedAt: connectedAtOf(row),
+ });
+ }
+ return [...listed.values()].sort((left, right) =>
+ (left.connectedAt ?? "") < (right.connectedAt ?? "")
+ ? -1
+ : (left.connectedAt ?? "") > (right.connectedAt ?? "")
+ ? 1
+ : 0,
+ );
+ },
+ } satisfies Pick<
+ ComposioBroker,
+ "revokeAccount" | "accountName" | "accountsOf"
+ >;
+}
+
+/** The vendor's published name for an account: `data.displayName`, then `state.val.displayName`. */
+function displayNameOf(row: AccountRow): string | null {
+ const named = (value: unknown): string | null =>
+ value && typeof value === "object" && "displayName" in value
+ ? textOf((value as { displayName?: unknown }).displayName)
+ : null;
+ const state = (row as { state?: unknown }).state;
+ const val =
+ state && typeof state === "object"
+ ? (state as { val?: unknown }).val
+ : undefined;
+ return named(row.data) ?? named(val);
+}
+
+/** When the vendor says the account was made, as it spelled it (`createdAt` or `created_at`). */
+function connectedAtOf(row: AccountRow): string | null {
+ const shaped = row as { createdAt?: unknown; created_at?: unknown };
+ const when = textOf(shaped.createdAt) ?? textOf(shaped.created_at);
+ return when !== null && !Number.isNaN(Date.parse(when)) ? when : null;
}
diff --git a/server/src/plugins/composio-client/consent.ts b/server/src/plugins/composio-client/consent.ts
index 89581a8530..ee8a5fa7a9 100644
--- a/server/src/plugins/composio-client/consent.ts
+++ b/server/src/plugins/composio-client/consent.ts
@@ -141,6 +141,7 @@ export function consentMethods(
account,
toolkit,
returnUrl,
+ another,
}): Promise<{ redirectUrl: string; authorizationId: string | null }> {
const userId = account.vendorUserId;
/*
@@ -331,19 +332,14 @@ export function consentMethods(
* about this deployment's own pages. It is never logged and never quoted in the refusal
* below, for the reason the url itself is not.
*
- * NO `allowMultiple`, WHICH LEAVES THE VENDOR ENFORCING THE RULE THIS DEPLOYMENT ALREADY
- * STATES. One person holds one account per app here, because the call that runs an action
- * names the person and not the account — so with two accounts attached, which mailbox a Bot
- * reads would be Composio's choice and nothing here could say which one it had been. The
- * route refuses a second connection before it ever reaches this method, and that refusal is
- * the sentence a person reads; this is the same rule one layer further down, where the
- * vendor is the only party that can still see an account this deployment's rows have lost
- * track of. What it raises there is {@link BrokerAccountHeldError}, and the route answers
- * that by recording the account the vendor holds, so the person who pressed Connect is
- * connected through it and is not told to disconnect an account their page never showed.
- * `toolkits.authorize` passed `allowMultiple: true` unconditionally — the SDK
- * calls it a "magic function" for exactly that — which is the opposite of what this
- * deployment wants.
+ * `allowMultiple` ONLY FOR A SECOND ACCOUNT THE PERSON ASKED FOR. A person may hold several
+ * accounts for one app, and a call names the one it runs in (`connected_account_id`), so a
+ * second mailbox is no longer Composio's choice to make. Without the flag the vendor still
+ * refuses a link for a person who holds an account, and that refusal is how a press with no
+ * row here reaches {@link BrokerAccountHeldError}: the route records the account the vendor
+ * holds, so the person is connected through it and not told to disconnect an account their
+ * page never showed. `toolkits.authorize` passed `allowMultiple: true` unconditionally, which
+ * would end that adoption, so this method decides per press.
*
* THE ANSWER IS AN OBJECT BY CONSTRUCTION AND ITS ONE FIELD IS NOT. `link` builds what it
* returns with `createConnectionRequest(client, response.connected_account_id, INITIATED,
@@ -363,6 +359,12 @@ export function consentMethods(
() =>
vendor.connectedAccounts.link(userId, config.id, {
callbackUrl: returnUrl,
+ /*
+ * A SECOND ACCOUNT ONLY WHEN ONE WAS ASKED FOR. The connect route sets `another` where
+ * the person's connection row stands, so a press with no row still meets the SDK's
+ * refusal and adopts the account the vendor holds (see above).
+ */
+ ...(another ? { allowMultiple: true } : {}),
}),
);
diff --git a/server/src/plugins/composio-client/model.ts b/server/src/plugins/composio-client/model.ts
index 14465ab72d..ef3991c332 100644
--- a/server/src/plugins/composio-client/model.ts
+++ b/server/src/plugins/composio-client/model.ts
@@ -465,7 +465,7 @@ export type ComposioVendor = {
link(
userId: string,
authConfigId: string,
- options: { callbackUrl: string },
+ options: { callbackUrl: string; allowMultiple?: boolean },
): Promise<{ redirectUrl?: unknown }>;
/**
* Create one person's account from the secret they typed, WITHOUT a consent screen anywhere.
diff --git a/server/src/plugins/plugin-store/accounts.ts b/server/src/plugins/plugin-store/accounts.ts
new file mode 100644
index 0000000000..914c5d33e6
--- /dev/null
+++ b/server/src/plugins/plugin-store/accounts.ts
@@ -0,0 +1,482 @@
+import { and, asc, eq, inArray, sql } from "drizzle-orm";
+import { recordAuditEvent } from "../../audit";
+import { brokeredAccounts, mcpServers } from "../../db/schema";
+import { toolkitOfFor } from "../access";
+import { BrokerUnconfiguredError } from "../broker";
+import { type AccountRef, filedUnderPerson } from "../shared-accounts";
+import type { StoreContext } from "./context";
+import { PluginRefusedError } from "./model";
+
+/** A person's account, as their app page and a Bot's call read it. */
+export type BrokeredAccountRecord = {
+ /** Opaque to the browser: which row a Disconnect or Make default press is about. */
+ id: string;
+ /** What the vendor says the account is: an email address or a login. Null where it said none. */
+ identity: string | null;
+ /** What a person and a model call the account: the identity, or "account 2" where there is none. */
+ label: string;
+ isDefault: boolean;
+ connectedAt: string | null;
+};
+
+/** The account a call runs in, read off the row it picked. */
+export type PinnedAccount = {
+ vendorUserId: string;
+ /** The vendor's id for the account, or null where the user id alone names it. */
+ accountId: string | null;
+ label: string;
+ /** How many accounts the person holds at the app, which says whether there was a choice. */
+ among: number;
+};
+
+type PersonAccount = Extract;
+
+type Row = typeof brokeredAccounts.$inferSelect;
+
+const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
+
+const iso = (value: Date | null): string | null =>
+ value ? value.toISOString() : null;
+
+/** Oldest first, by when the vendor says it was made and then by when this deployment saw it. */
+const ordered = (rows: Row[]): Row[] =>
+ [...rows].sort((left, right) => {
+ const a = left.connectedAt?.getTime() ?? Number.POSITIVE_INFINITY;
+ const b = right.connectedAt?.getTime() ?? Number.POSITIVE_INFINITY;
+ if (a !== b) return a < b ? -1 : 1;
+ // A vendor that dates nothing (Arcade): the person's own id before the further ids made from it.
+ if (left.vendorUserId.length !== right.vendorUserId.length)
+ return left.vendorUserId.length - right.vendorUserId.length;
+ return left.createdAt.getTime() - right.createdAt.getTime();
+ });
+
+function recordsOf(rows: Row[]): BrokeredAccountRecord[] {
+ return ordered(rows).map((row, index) => ({
+ id: row.id,
+ identity: row.identity,
+ label: row.identity ?? `account ${index + 1}`,
+ isDefault: row.isDefault,
+ connectedAt: iso(row.connectedAt),
+ }));
+}
+
+/** A unique-index race between two syncs, which the second one can ignore. */
+const isUniqueViolation = (error: unknown): boolean => {
+ const code =
+ (error as { code?: unknown })?.code ??
+ (error as { cause?: { code?: unknown } })?.cause?.code;
+ return code === "23505";
+};
+
+/**
+ * The accounts each person holds at each brokered app, kept in step with the vendor's own listing.
+ *
+ * Shared by the store's account methods and by the call path, so the account a call is pointed at
+ * and the accounts a page lists are read the same way.
+ */
+export function accountBook(context: StoreContext) {
+ const { database, broker, brokerProvider } = context;
+
+ const rowsFor = (toolkit: string, userId: string) =>
+ database
+ .select()
+ .from(brokeredAccounts)
+ .where(
+ and(
+ eq(brokeredAccounts.provider, brokerProvider),
+ eq(brokeredAccounts.app, toolkit),
+ eq(brokeredAccounts.userId, userId),
+ ),
+ );
+
+ /** Make the oldest remaining account the default where none is. */
+ const settleDefault = async (toolkit: string, userId: string) => {
+ const rows = ordered(await rowsFor(toolkit, userId));
+ if (rows.length === 0 || rows.some((row) => row.isDefault)) return;
+ try {
+ await database
+ .update(brokeredAccounts)
+ .set({ isDefault: true })
+ .where(eq(brokeredAccounts.id, rows[0]?.id ?? ""));
+ } catch (error) {
+ if (!isUniqueViolation(error)) throw error;
+ }
+ };
+
+ /**
+ * Rebuild a person's accounts at one app from what the vendor lists now, and answer them.
+ *
+ * Asked about the person's own broker id and about every further id of theirs a row already
+ * names (Arcade files each further account under one; see `anotherArcadeAccount`). An account the
+ * vendor no longer lists is dropped here, and one it lists that was not here is added, so a row is
+ * never the only evidence of an account. The default survives where its account does.
+ *
+ * A broker that cannot list accounts one by one, and a Shared app's team account, answer from
+ * the table as it stands, which for them is empty.
+ */
+ async function sync(input: {
+ toolkit: string;
+ account: AccountRef;
+ }): Promise {
+ const { toolkit, account } = input;
+ if (account.holder !== "person") return [];
+ if (!broker?.accountsOf)
+ return recordsOf(await rowsFor(toolkit, account.userId));
+
+ const held = await rowsFor(toolkit, account.userId);
+ const filedUnder = [
+ ...new Set([
+ account.vendorUserId,
+ ...held
+ .map((row) => row.vendorUserId)
+ .filter((id) => filedUnderPerson(id, account.vendorUserId)),
+ ]),
+ ];
+ const listed: {
+ vendorUserId: string;
+ accountId: string;
+ identity: string | null;
+ connectedAt: Date | null;
+ }[] = [];
+ for (const vendorUserId of filedUnder) {
+ for (const found of await broker.accountsOf({
+ account: { ...account, vendorUserId },
+ toolkit,
+ })) {
+ listed.push({
+ vendorUserId,
+ accountId: found.accountId ?? "",
+ identity: found.identity,
+ connectedAt: found.connectedAt ? new Date(found.connectedAt) : null,
+ });
+ }
+ }
+
+ const key = (row: { vendorUserId: string; accountId: string }) =>
+ `${row.vendorUserId}\u0000${row.accountId}`;
+ const keep = new Set(listed.map(key));
+ const gone = held.filter((row) => !keep.has(key(row)));
+ if (gone.length > 0) {
+ await database.delete(brokeredAccounts).where(
+ inArray(
+ brokeredAccounts.id,
+ gone.map((row) => row.id),
+ ),
+ );
+ }
+ for (const found of listed) {
+ await database
+ .insert(brokeredAccounts)
+ .values({
+ provider: brokerProvider,
+ app: toolkit,
+ userId: account.userId,
+ vendorUserId: found.vendorUserId,
+ accountId: found.accountId,
+ identity: found.identity,
+ connectedAt: found.connectedAt,
+ })
+ .onConflictDoUpdate({
+ target: [
+ brokeredAccounts.provider,
+ brokeredAccounts.app,
+ brokeredAccounts.userId,
+ brokeredAccounts.vendorUserId,
+ brokeredAccounts.accountId,
+ ],
+ set: {
+ identity: found.identity,
+ connectedAt: found.connectedAt,
+ },
+ });
+ }
+ await settleDefault(toolkit, account.userId);
+ return recordsOf(await rowsFor(toolkit, account.userId));
+ }
+
+ /**
+ * The account a call runs in, picked from the accounts of the person whose account the call
+ * opens, or null where they hold none recorded here (a connection made before accounts were
+ * listed, which runs as it always did).
+ *
+ * `named` IS WHAT THE MODEL PASSED AND IS ONLY EVER MATCHED, never used. It is compared with the
+ * labels of this person's own accounts and nothing else, so a name that is somebody else's
+ * account, an id, or anything the person does not hold is refused with the list they do hold.
+ */
+ async function pick(input: {
+ toolkit: string;
+ title: string;
+ account: PersonAccount;
+ named: unknown;
+ }): Promise {
+ const { toolkit, title, account, named } = input;
+ let rows = ordered(await rowsFor(toolkit, account.userId)).filter((row) =>
+ filedUnderPerson(row.vendorUserId, account.vendorUserId),
+ );
+ const pinned = (row: Row, label: string): PinnedAccount => ({
+ vendorUserId: row.vendorUserId,
+ accountId: row.accountId === "" ? null : row.accountId,
+ label,
+ among: rows.length,
+ });
+
+ if (named === undefined || named === null || named === "") {
+ const records = recordsOf(rows);
+ const chosen = records.find((record) => record.isDefault) ?? records[0];
+ const row =
+ chosen && rows.find((candidate) => candidate.id === chosen.id);
+ return row && chosen ? pinned(row, chosen.label) : null;
+ }
+
+ const wanted =
+ typeof named === "string" ? named.trim().toLowerCase() : null;
+ const find = () => {
+ const records = recordsOf(rows);
+ const match = records.find(
+ (record) =>
+ record.label.toLowerCase() === wanted ||
+ (record.identity !== null &&
+ record.identity.toLowerCase() === wanted),
+ );
+ const row = match && rows.find((candidate) => candidate.id === match.id);
+ return { records, match, row };
+ };
+ let found = find();
+ /*
+ * NOTHING RECORDED YET, SO THE VENDOR IS ASKED ONCE. A connection made before this deployment
+ * listed accounts has no rows until the app's page is opened; a call naming an account is the
+ * one moment that matters, so the listing is made here rather than refusing a real account.
+ */
+ if (!found.row && rows.length === 0 && wanted !== null) {
+ await sync({ toolkit, account }).catch(() => []);
+ rows = ordered(await rowsFor(toolkit, account.userId)).filter((row) =>
+ filedUnderPerson(row.vendorUserId, account.vendorUserId),
+ );
+ found = find();
+ }
+ if (found.row && found.match) return pinned(found.row, found.match.label);
+
+ const yours = found.records.map((record) => record.label);
+ throw new PluginRefusedError(
+ yours.length > 0
+ ? `That isn't one of your ${title} accounts, so nothing was done. Use one of: ${yours.join(", ")}.`
+ : `You have no ${title} accounts to choose from, so nothing was done. Leave the account out to use the one you connected.`,
+ null,
+ );
+ }
+
+ return { rowsFor, sync, pick, settleDefault };
+}
+
+/** What a person does to their own accounts at one app. */
+export function accountMethods(context: StoreContext) {
+ const { database, broker, brokerProvider, auditStore } = context;
+ const book = accountBook(context);
+
+ return {
+ /** See {@link accountBook}'s `sync`. */
+ async syncBrokeredAccounts(input: {
+ toolkit: string;
+ account: AccountRef;
+ }): Promise {
+ return book.sync(input);
+ },
+
+ /**
+ * Each app's accounts for one person, as a Bot is told about them, read from the table alone.
+ * An app with no rows is absent.
+ */
+ async accountChoicesFor(
+ userId: string,
+ toolkits: string[],
+ ): Promise> {
+ const choices = new Map();
+ if (!userId || toolkits.length === 0) return choices;
+ const rows = await database
+ .select()
+ .from(brokeredAccounts)
+ .where(
+ and(
+ eq(brokeredAccounts.provider, brokerProvider),
+ eq(brokeredAccounts.userId, userId),
+ inArray(brokeredAccounts.app, [...new Set(toolkits)]),
+ ),
+ )
+ .orderBy(asc(brokeredAccounts.createdAt));
+ const byApp = new Map();
+ for (const row of rows) {
+ byApp.set(row.app, [...(byApp.get(row.app) ?? []), row]);
+ }
+ for (const [app, held] of byApp) choices.set(app, recordsOf(held));
+ return choices;
+ },
+
+ /**
+ * The same, keyed by the servers a Bot's tools are on: for each brokered Personal app this
+ * person holds accounts at, its title and their accounts. What `grantedTools` tells a model.
+ */
+ async accountChoicesForServers(
+ userId: string,
+ serverIds: string[],
+ ): Promise<
+ Map
+ > {
+ const answer = new Map<
+ string,
+ { title: string; choices: BrokeredAccountRecord[] }
+ >();
+ if (!userId || serverIds.length === 0) return answer;
+ const servers = await database
+ .select({
+ id: mcpServers.id,
+ title: mcpServers.title,
+ url: mcpServers.url,
+ accountMode: mcpServers.accountMode,
+ })
+ .from(mcpServers)
+ .where(
+ and(
+ inArray(mcpServers.id, [...new Set(serverIds)]),
+ eq(mcpServers.provenance, brokerProvider),
+ ),
+ );
+ const apps = new Map();
+ for (const server of servers) {
+ if (server.accountMode === "shared") continue;
+ const app = toolkitOfFor(brokerProvider, server.url);
+ if (!app) continue;
+ apps.set(app, [
+ ...(apps.get(app) ?? []),
+ { id: server.id, title: server.title },
+ ]);
+ }
+ if (apps.size === 0) return answer;
+ const rows = await database
+ .select()
+ .from(brokeredAccounts)
+ .where(
+ and(
+ eq(brokeredAccounts.provider, brokerProvider),
+ eq(brokeredAccounts.userId, userId),
+ inArray(brokeredAccounts.app, [...apps.keys()]),
+ ),
+ );
+ const byApp = new Map();
+ for (const row of rows) {
+ byApp.set(row.app, [...(byApp.get(row.app) ?? []), row]);
+ }
+ for (const [app, held] of byApp) {
+ const choices = recordsOf(held);
+ for (const server of apps.get(app) ?? [])
+ answer.set(server.id, { title: server.title, choices });
+ }
+ return answer;
+ },
+
+ /** Make one of a person's accounts the one a call uses when it names none. False where it is not theirs. */
+ async setDefaultBrokeredAccount(input: {
+ toolkit: string;
+ userId: string;
+ id: string;
+ }): Promise {
+ // Not an id this table could hold, so not one of theirs; asked as text it would be a 500.
+ if (!UUID.test(input.id)) return false;
+ return await database.transaction(async (transaction) => {
+ const mine = and(
+ eq(brokeredAccounts.provider, brokerProvider),
+ eq(brokeredAccounts.app, input.toolkit),
+ eq(brokeredAccounts.userId, input.userId),
+ );
+ const [row] = await transaction
+ .select({ id: brokeredAccounts.id })
+ .from(brokeredAccounts)
+ .where(and(mine, eq(brokeredAccounts.id, input.id)));
+ if (!row) return false;
+ await transaction
+ .update(brokeredAccounts)
+ .set({ isDefault: false })
+ .where(and(mine, sql`${brokeredAccounts.isDefault}`));
+ await transaction
+ .update(brokeredAccounts)
+ .set({ isDefault: true })
+ .where(eq(brokeredAccounts.id, row.id));
+ return true;
+ });
+ },
+
+ /**
+ * End one of a person's accounts at an app and keep the rest.
+ *
+ * `only` where it is the last account they hold there: the caller ends the whole connection
+ * instead, through the same disconnect the app's single account always had, so the trail, the
+ * siblings and the connection row are handled as they always were. `null` where the id is not
+ * one of this person's accounts at this app.
+ */
+ async disconnectBrokeredAccount(input: {
+ toolkit: string;
+ account: PersonAccount;
+ id: string;
+ by: string;
+ }): Promise<{ only: true } | { remaining: number } | null> {
+ if (!broker) throw new BrokerUnconfiguredError(brokerProvider, "person");
+ const rows = (
+ await book.rowsFor(input.toolkit, input.account.userId)
+ ).filter((row) =>
+ filedUnderPerson(row.vendorUserId, input.account.vendorUserId),
+ );
+ const row = rows.find((candidate) => candidate.id === input.id);
+ if (!row) return null;
+ if (rows.length === 1) return { only: true };
+
+ /*
+ * ONE ACCOUNT, NEVER THE SWEEP. `revoke` ends every account a person holds for the app; this
+ * ends the one they pressed Disconnect beside. Composio names it by its id; Arcade by the user
+ * id it is filed under, which holds that one account and nothing else.
+ */
+ if (row.accountId !== "") await broker.revokeAccount(row.accountId);
+ else
+ await broker.revoke({
+ account: { ...input.account, vendorUserId: row.vendorUserId },
+ toolkit: input.toolkit,
+ });
+
+ await database
+ .delete(brokeredAccounts)
+ .where(eq(brokeredAccounts.id, row.id));
+ await book.settleDefault(input.toolkit, input.account.userId);
+
+ const remaining = rows.length - 1;
+ await recordAuditEvent(auditStore, {
+ eventType: "mcp.account_disconnected",
+ targetType: "mcp_server",
+ targetId: input.toolkit,
+ payload: {
+ actor: input.by,
+ server: input.toolkit,
+ owner: input.account.userId,
+ reason: "self",
+ vendorRevocationRequested: true,
+ scope: "one_account",
+ remaining,
+ },
+ });
+ return { remaining };
+ },
+
+ /** Forget every account row a person holds at an app, when the whole connection ends. */
+ async forgetBrokeredAccounts(input: {
+ toolkit: string;
+ userId: string;
+ }): Promise {
+ await database
+ .delete(brokeredAccounts)
+ .where(
+ and(
+ eq(brokeredAccounts.provider, brokerProvider),
+ eq(brokeredAccounts.app, input.toolkit),
+ eq(brokeredAccounts.userId, input.userId),
+ ),
+ );
+ },
+ };
+}
diff --git a/server/src/plugins/plugin-store/calls.ts b/server/src/plugins/plugin-store/calls.ts
index 6cb86aab79..aa9907dc26 100644
--- a/server/src/plugins/plugin-store/calls.ts
+++ b/server/src/plugins/plugin-store/calls.ts
@@ -16,6 +16,7 @@ import {
} from "../../computer/policy";
import { withoutStatement } from "../../db/query-failure";
import {
+ brokeredAccounts,
brokeredConnections,
mcpTools,
pluginGrants,
@@ -28,12 +29,14 @@ import {
reviewedRead,
tokenHeaderFor,
} from "../catalogue";
+import { accountArgumentFor } from "../account-argument";
import { VERSION_ARG } from "../composio";
import { inspectToolArguments } from "../content-governance";
import { shareTargetOf } from "../share-target";
-import type { AccountMode } from "../shared-accounts";
+import { type AccountMode, filedUnderPerson } from "../shared-accounts";
import { transportFor } from "../transport";
import { withVendorDeadline } from "../vendor-deadline";
+import { accountBook, type PinnedAccount } from "./accounts";
import type { StoreContext } from "./context";
import {
effectiveUrl,
@@ -57,7 +60,9 @@ export function callMethods(context: StoreContext) {
requireServer,
broker,
brokerProvider,
+ personAccountOf,
} = context;
+ const accounts = accountBook(context);
return {
/**
* May this Bot use this plugin?
@@ -249,11 +254,91 @@ export function callMethods(context: StoreContext) {
* its refusal hold rather than guessing — a guessed version is a call against an action's
* other behaviour.
*/
- const { [VERSION_ARG]: _dropped, ...modelArgs } = args;
+ /*
+ * WHICH OF THE PERSON'S ACCOUNTS, WHERE THE MODEL NAMED ONE. A brokered app's tools take an
+ * optional `account` (see `../account-argument`), and it is taken out of the arguments here,
+ * before anything else reads them, so it never reaches the vendor as one of the action's own
+ * parameters. It is only ever a NAME, matched below against the accounts of the person whose
+ * account this call opens. An action whose own schema has an `account` keeps it.
+ */
+ const accountArg =
+ access.credential === "brokered"
+ ? accountArgumentFor(
+ advertised[0]?.inputSchema as Record | undefined,
+ )
+ : null;
+ const namedAccount =
+ accountArg !== null && Object.hasOwn(args, accountArg)
+ ? args[accountArg]
+ : undefined;
+ const callArgs =
+ accountArg !== null && Object.hasOwn(args, accountArg)
+ ? Object.fromEntries(
+ Object.entries(args).filter(([key]) => key !== accountArg),
+ )
+ : args;
+
+ const { [VERSION_ARG]: _dropped, ...modelArgs } = callArgs;
const vendorArgs = advertised[0]?.version
? { ...modelArgs, [VERSION_ARG]: advertised[0].version }
: modelArgs;
+ /*
+ * THE ACCOUNT THIS CALL RUNS IN, PICKED FROM THE ACCOUNTS OF THE PERSON IN THE SESSION.
+ *
+ * Whose accounts are asked about is `credentialActorId ?? actorId`, the same person the call
+ * opens below, and never anything in `args`. The name the model passed is matched against
+ * those accounts' labels and nothing else, so a name that is not theirs is refused here,
+ * before the policy, the approval or the vendor. With no name the person's default account is
+ * used. A person with no accounts recorded here (a connection from before they were listed)
+ * gets no pin, and the call runs as it always did.
+ *
+ * A Shared app has one team account and nothing to pick.
+ */
+ let pinned: PinnedAccount | null = null;
+ const opensAs = input.credentialActorId ?? input.actorId;
+ if (access.credential === "brokered" && access.toolkit && opensAs) {
+ if (app?.accountMode === "shared") {
+ if (
+ namedAccount !== undefined &&
+ namedAccount !== null &&
+ namedAccount !== ""
+ )
+ throw new PluginRefusedError(
+ `${row.title} uses one shared account, so there's no account to choose. Leave the account out.`,
+ null,
+ );
+ } else {
+ const person = await personAccountOf(opensAs);
+ if (person.holder === "person") {
+ try {
+ pinned = await accounts.pick({
+ toolkit: access.toolkit,
+ title: row.title,
+ account: person,
+ named: namedAccount,
+ });
+ } catch (error) {
+ if (error instanceof PluginRefusedError)
+ await recordAuditEvent(auditStore, {
+ eventType: "mcp.call_rejected",
+ targetType: "mcp_tool",
+ targetId: input.ref,
+ ...(input.initiator ? { initiator: input.initiator } : {}),
+ payload: {
+ actor: auditActor,
+ bot: input.botId,
+ server: serverId,
+ tool: toolName,
+ refusal: "unknown_account",
+ },
+ });
+ throw error;
+ }
+ }
+ }
+ }
+
/**
* The same policy the computer actions are judged by, asked about a tool call.
*
@@ -320,6 +405,11 @@ export function callMethods(context: StoreContext) {
access,
input.credentialActorId ?? input.actorId,
),
+ /*
+ * Which of that person's accounts, where they hold more than one recorded here: the label
+ * the person and the model know it by, never the vendor's id for it.
+ */
+ ...(pinned ? { reachedAccount: pinned.label } : {}),
decision: {
allowed: verdict.allowed,
mode: verdict.mode,
@@ -552,6 +642,12 @@ export function callMethods(context: StoreContext) {
scope: gateApp ? gateApp.id : serverId,
args: vendorArgs,
target: { serverId, toolName, url: row.url, effect },
+ /*
+ * WHICH MAILBOX, where the person holds more than one. It is part of what the gate
+ * compares and stores, so an approval, a denial or "Always allow here" for one account
+ * never answers for another, and the card says which account it is asking about.
+ */
+ ...(pinned && pinned.among > 1 ? { account: pinned.label } : {}),
continuation: currentApprovalContext(),
/*
* Whether this is a reviewed read of the person's own app: an app they connected with their
@@ -614,6 +710,25 @@ export function callMethods(context: StoreContext) {
access,
gatedMode,
);
+ /*
+ * THE PICKED ACCOUNT MUST BE FILED UNDER THE PERSON THIS CALL OPENS. It was read from that
+ * person's own rows, so this holds by construction; it is checked again against the id the
+ * connection row names, so no row of anybody else's can ever be the account a call runs in.
+ */
+ if (
+ pinned &&
+ vendorUserId !== undefined &&
+ !filedUnderPerson(pinned.vendorUserId, vendorUserId)
+ ) {
+ throw new PluginRefusedError(
+ `That ${row.title} account isn't yours, so nothing was done.`,
+ null,
+ );
+ }
+ const runsAs =
+ pinned && vendorUserId !== undefined
+ ? pinned.vendorUserId
+ : vendorUserId;
const vendor =
injectedVendor ?? transportFor(access.transport).callTool;
const result = await vendor(
@@ -621,7 +736,10 @@ export function callMethods(context: StoreContext) {
url: effectiveUrl(row, entry),
token,
tokenHeader: tokenHeaderFor(entry),
- actorId: vendorUserId ?? input.credentialActorId ?? input.actorId,
+ actorId: runsAs ?? input.credentialActorId ?? input.actorId,
+ ...(pinned?.accountId && runsAs !== undefined
+ ? { accountId: pinned.accountId }
+ : {}),
botId: input.botId,
threadId: input.threadId ?? currentApprovalContext()?.threadId,
},
@@ -674,7 +792,7 @@ export function callMethods(context: StoreContext) {
*/
if (!result.accountGone && !result.actionNotAllowed) return answer;
refused = result.accountGone ? "account" : "action";
- reachedVendorAs = vendorUserId;
+ reachedVendorAs = runsAs;
} catch (error) {
/*
* Recorded, then rethrown unchanged. The caller's behaviour is unaffected — what changes is
@@ -766,7 +884,33 @@ export function callMethods(context: StoreContext) {
* not connected for everybody, which is the truth and the state whose button fixes it. Like
* the confirm's, the delete files no trail entry: nobody disconnected anything here.
*/
- if (gone && access.credential === "brokered" && access.toolkit)
+ /*
+ * ONE OF SEVERAL ACCOUNTS GONE ENDS THAT ACCOUNT ONLY. The person still holds the others, so
+ * the connection row stays and only the account the call ran in is forgotten; the next call
+ * runs in their default, and the page lists what is left.
+ */
+ let othersRemain = false;
+ if (gone && pinned && access.toolkit && gatedMode !== "shared") {
+ const held = await accounts.rowsFor(access.toolkit, opensAs);
+ const ended = held.find(
+ (candidate) =>
+ candidate.vendorUserId === pinned?.vendorUserId &&
+ candidate.accountId === (pinned?.accountId ?? ""),
+ );
+ othersRemain = held.length > 1 && ended !== undefined;
+ if (othersRemain && ended) {
+ await database
+ .delete(brokeredAccounts)
+ .where(eq(brokeredAccounts.id, ended.id));
+ await accounts.settleDefault(access.toolkit, opensAs);
+ }
+ }
+ if (
+ gone &&
+ !othersRemain &&
+ access.credential === "brokered" &&
+ access.toolkit
+ )
await database
.delete(brokeredConnections)
.where(
@@ -798,7 +942,9 @@ export function callMethods(context: StoreContext) {
message: gone
? gatedMode === "shared"
? `The shared ${row.title} account is no longer connected, so this wasn't done. Ask an admin to reconnect it.`
- : `Your ${row.title} account is no longer connected, so this wasn't done. Reconnect it and ask again.`
+ : othersRemain && pinned
+ ? `Your ${row.title} account ${pinned.label} is no longer connected, so this wasn't done. Reconnect it, or ask again with another account.`
+ : `Your ${row.title} account is no longer connected, so this wasn't done. Reconnect it and ask again.`
: gatedMode === "shared"
? `The shared ${row.title} account doesn't allow this yet, so this wasn't done. Ask an admin to reconnect it.`
: `Your ${row.title} account doesn't allow this yet, so this wasn't done. Reconnect it and ask again.`,
diff --git a/server/src/plugins/plugin-store/connections.ts b/server/src/plugins/plugin-store/connections.ts
index 74f20bfb05..707f3d1721 100644
--- a/server/src/plugins/plugin-store/connections.ts
+++ b/server/src/plugins/plugin-store/connections.ts
@@ -2,6 +2,7 @@ import { and, asc, eq, inArray, isNull, sql } from "drizzle-orm";
import { createAuditStore, recordAuditEvent } from "../../audit";
import type { Database } from "../../db/client";
import {
+ brokeredAccounts,
brokeredConnections,
credentials as credentialRows,
mcpServers,
@@ -30,7 +31,7 @@ import {
sharedAccountOnly,
sharedAccountOnlySentence,
} from "../deployment-clients";
-import type { AccountRef } from "../shared-accounts";
+import { type AccountRef, filedUnderPerson } from "../shared-accounts";
import type { StoreContext } from "./context";
import {
attemptNamed,
@@ -1365,6 +1366,25 @@ export function connectionMethods(context: StoreContext) {
scopes: string[];
authProvider: string;
}): Promise<{ claimed: boolean }> {
+ /*
+ * A FURTHER ACCOUNT OF THE PERSON'S, FILED UNDER A FURTHER ID OF THEIRS (Arcade; see
+ * `anotherArcadeAccount`). The connection row keeps naming the person's own id, which is what
+ * a call with no account picked runs as, and the further account is recorded beside it as one
+ * of their accounts at the app.
+ */
+ const own =
+ input.account.holder === "person"
+ ? (await personAccountOf(input.account.userId)).vendorUserId
+ : input.account.vendorUserId;
+ const further =
+ input.account.holder === "person" &&
+ input.account.vendorUserId !== own &&
+ filedUnderPerson(input.account.vendorUserId, own)
+ ? input.account
+ : null;
+ const recorded: AccountRef = further
+ ? { ...further, vendorUserId: own }
+ : input.account;
return await database.transaction(async (transaction) => {
/*
* ONE STATEMENT, AND THE FIRST ONE. `completed_at` is truncated to milliseconds for the
@@ -1389,7 +1409,7 @@ export function connectionMethods(context: StoreContext) {
const [existing] = await transaction
.select({ app: brokeredConnections.app })
.from(brokeredConnections)
- .where(accountRow(input.toolkit, input.account));
+ .where(accountRow(input.toolkit, recorded));
const verifiedAt = new Date();
const set = {
@@ -1429,7 +1449,7 @@ export function connectionMethods(context: StoreContext) {
holder: input.account.holder,
userId:
input.account.holder === "person" ? input.account.userId : null,
- vendorUserId: input.account.vendorUserId,
+ vendorUserId: recorded.vendorUserId,
connectedBy,
...set,
})
@@ -1451,11 +1471,13 @@ export function connectionMethods(context: StoreContext) {
// that refusal is how the card knows the Connect it offered has happened (see
// `AppAccess.connectedAt`). Left at the first consent, the card went on offering
// Reconnect over an account that now allowed the action.
- set: {
- ...set,
- vendorUserId: input.account.vendorUserId,
- connectedAt: verifiedAt,
- },
+ set: further
+ ? set
+ : {
+ ...set,
+ vendorUserId: recorded.vendorUserId,
+ connectedAt: verifiedAt,
+ },
}
: {
target: [
@@ -1472,6 +1494,19 @@ export function connectionMethods(context: StoreContext) {
},
);
+ if (further) {
+ await transaction
+ .insert(brokeredAccounts)
+ .values({
+ provider: brokerProvider,
+ app: input.toolkit,
+ userId: further.userId,
+ vendorUserId: further.vendorUserId,
+ accountId: "",
+ })
+ .onConflictDoNothing();
+ }
+
await recordAuditEvent(
createAuditStore(transaction as unknown as Database),
{
@@ -3107,6 +3142,49 @@ export function connectionMethods(context: StoreContext) {
toolkit: input.toolkit,
});
+ /*
+ * AND EVERY FURTHER ACCOUNT OF THE PERSON'S AT THE APP, which a broker that keeps one account
+ * per user id holds under a further id of theirs (Arcade; see `anotherArcadeAccount`). The
+ * sweep above reaches only the person's own id. Then the account rows go: the connection they
+ * stood behind is ending.
+ */
+ if (input.account.holder === "person") {
+ const person = input.account;
+ const further = await database
+ .select({ vendorUserId: brokeredAccounts.vendorUserId })
+ .from(brokeredAccounts)
+ .where(
+ and(
+ eq(brokeredAccounts.provider, brokerProvider),
+ eq(brokeredAccounts.app, input.toolkit),
+ eq(brokeredAccounts.userId, person.userId),
+ ),
+ );
+ for (const vendorUserId of new Set(
+ further
+ .map((row) => row.vendorUserId)
+ .filter(
+ (id) =>
+ id !== person.vendorUserId &&
+ filedUnderPerson(id, person.vendorUserId),
+ ),
+ )) {
+ await broker.revoke({
+ account: { ...person, vendorUserId },
+ toolkit: input.toolkit,
+ });
+ }
+ await database
+ .delete(brokeredAccounts)
+ .where(
+ and(
+ eq(brokeredAccounts.provider, brokerProvider),
+ eq(brokeredAccounts.app, input.toolkit),
+ eq(brokeredAccounts.userId, person.userId),
+ ),
+ );
+ }
+
const asked = claim(ended);
/*
diff --git a/server/src/plugins/plugin-store/model.ts b/server/src/plugins/plugin-store/model.ts
index c3995b92bb..3a13ebd659 100644
--- a/server/src/plugins/plugin-store/model.ts
+++ b/server/src/plugins/plugin-store/model.ts
@@ -1274,6 +1274,8 @@ export type PluginStoreOptions = {
actorId?: string;
botId?: string;
threadId?: string;
+ /** Which of the person's accounts the call runs in; see `VendorTransport.callTool`. */
+ accountId?: string;
},
toolName: string,
args: Record,
diff --git a/server/src/plugins/route-groups/connections.ts b/server/src/plugins/route-groups/connections.ts
index 859b496cdb..e4ee0765cd 100644
--- a/server/src/plugins/route-groups/connections.ts
+++ b/server/src/plugins/route-groups/connections.ts
@@ -31,7 +31,11 @@ import {
redirectUriFor,
sealConnectState,
} from "../oauth";
-import { type AccountRef, asAccountRef } from "../shared-accounts";
+import {
+ type AccountRef,
+ anotherArcadeAccount,
+ asAccountRef,
+} from "../shared-accounts";
import {
CatalogueEntryUnknownError,
type OAuthClient,
@@ -266,42 +270,42 @@ export function mountConnectionRoutes({
* signed in, decided above and never by anything the request carries.
*/
/*
- * ONE ACCOUNT PER APP, WHERE A SECOND CONSENT WOULD MAKE A SECOND ONE. On Composio a second
- * link attaches a second account behind a row that already says connected, so it is refused.
- * A broker that runs a correlated flow is not that: Arcade keeps one connection per name and
- * provider, and a consent over it adds the permissions it asks for to that same connection,
- * as the per-action Authorize always has. So Connect over a standing Arcade row is let through.
- * A Bot's card sends somebody whose call Arcade refused to the app's page, as it does on
- * Composio, and the Reconnect there is this: it asks for everything the app needs, and the row
- * the verifier then updates is the one that stood. No Disconnect comes first.
+ * A PERSON MAY HOLD SEVERAL ACCOUNTS AT ONE APP, SO A STANDING CONNECTION IS NO REFUSAL.
+ *
+ * A press over a person's standing connection adds another account beside it: two Gmail
+ * mailboxes, a work and a personal calendar. A call names which one it runs in (see
+ * `plugin-store/accounts`), so a second account is no longer the vendor's choice to make.
+ *
+ * Composio files every account a person holds under one user id, and is told a second one is
+ * wanted (`another`). Arcade keeps one account per user id, and a Connect over a standing row
+ * there has always been the Reconnect that adds permissions to that account, so a further
+ * account is asked for in so many words (`?another=1`) and filed under a further id of the
+ * person's own (`anotherArcadeAccount`). Whose ids those are still comes from the session.
+ *
+ * A Shared app's team account is one account. Connecting a different one still means ending
+ * the one that stands first.
*/
const existing = await store.brokeredConnection({ toolkit, account });
- if (existing && !composio.broker.authorizations) {
- /*
- * Named with the step to take rather than only refused: a second link would attach a
- * second account behind a row that already says connected, and the way to a new one is
- * through the connection they have.
- *
- * THE APP'S TITLE, NOT THE ROW'S ID. `composio-linear` is this deployment's name for a
- * table row; "Linear" is the name of the thing the person connected and the only one of
- * the two they have ever seen on a screen. An internal key in a sentence addressed to a
- * person is both unhelpful and a small leak of how the rows are keyed.
- *
- * WHOSE ALREADY, SAID PLAINLY FOR A SHARED APP. "You already have an account" is true for
- * a Personal app and false for a Shared one — nobody signed in holds that account, the
- * deployment does — so a deployment account's refusal names the deployment's connection
- * rather than putting the actor's own claim on it.
- */
+ if (
+ existing &&
+ account.holder === "deployment" &&
+ !composio.broker.authorizations
+ ) {
return context.json(
{
- error:
- account.holder === "deployment"
- ? `${row.title} already has a shared account connected. Disconnect it first if you want to connect a different one.`
- : `You already have an account connected to ${row.title}. Disconnect it first if you want to connect a different one.`,
+ error: `${row.title} already has a shared account connected. Disconnect it first if you want to connect a different one.`,
},
409,
);
}
+ const another = Boolean(existing) && account.holder === "person";
+ const anotherArcade =
+ another &&
+ composio.broker.authorizations !== null &&
+ context.req.query("another") === "1" &&
+ account.holder === "person"
+ ? anotherArcadeAccount(account)
+ : null;
/*
* AN APP WHOSE SECRET THE PERSON HOLDS IS ANSWERED HERE AND NEVER SENT AT A CONSENT SCREEN.
@@ -877,7 +881,7 @@ export function mountConnectionRoutes({
return startAuthorization(context, {
broker: composio.broker,
serverId,
- account,
+ account: anotherArcade ?? account,
toolkit,
action: null,
returnUrl,
@@ -897,7 +901,12 @@ export function mountConnectionRoutes({
let redirectUrl: string;
try {
({ redirectUrl } = await withVendorDeadline(() =>
- composio.broker.authorize({ account, toolkit, returnUrl }),
+ composio.broker.authorize({
+ account,
+ toolkit,
+ returnUrl,
+ ...(another ? { another: true } : {}),
+ }),
));
} catch (error) {
// See `adoptHeld` above.
@@ -1485,6 +1494,151 @@ export function mountConnectionRoutes({
* gets `refused`, which names no vendor and no variable because they can act on neither. The
* operator's half is logged by {@link logReturnUrlRefusal}, where an operator looks.
*/
+ /**
+ * The accounts the signed-in person holds at one app, each by the name the vendor gave it.
+ *
+ * ASKED OF THE VENDOR ON EVERY READ, and the answer recorded, because this is what a Bot's call
+ * picks its account from: an account connected or ended anywhere since the last read is listed
+ * as it stands now. A person's own accounts only; a Shared app's one team account has nothing to
+ * list here and answers an empty list.
+ *
+ * `canAddAnother` is whether Connect another account is offered: a person's own app, on a
+ * broker that can tell their accounts apart.
+ */
+ routes.get("/servers/:id/accounts", requireUser, async (context) => {
+ const resolved = await brokeredAccountFor(
+ context,
+ context.req.param("id"),
+ {
+ readOnly: true,
+ },
+ );
+ if (resolved.refusal) {
+ return context.json(
+ { error: resolved.refusal.error },
+ resolved.refusal.status,
+ );
+ }
+ if (resolved.account.holder !== "person") {
+ return context.json({ accounts: [], canAddAnother: false });
+ }
+ try {
+ const accounts = await withVendorDeadline(() =>
+ store.syncBrokeredAccounts({
+ toolkit: resolved.toolkit,
+ account: resolved.account,
+ }),
+ );
+ return context.json({
+ accounts,
+ canAddAnother: Boolean(composio?.broker.accountsOf),
+ });
+ } catch (error) {
+ const refusal = personRefusal(
+ error,
+ `Couldn't list your ${resolved.title} accounts. Reload the page.`,
+ );
+ return context.json({ error: refusal.error }, refusal.status);
+ }
+ });
+
+ /** Make one of the person's accounts the one their Bots use when they name none. */
+ routes.post(
+ "/servers/:id/accounts/:accountId/default",
+ requireUser,
+ async (context) => {
+ const resolved = await brokeredAccountFor(
+ context,
+ context.req.param("id"),
+ );
+ if (resolved.refusal) {
+ return context.json(
+ { error: resolved.refusal.error },
+ resolved.refusal.status,
+ );
+ }
+ if (resolved.account.holder !== "person") {
+ return context.json(
+ { error: `${resolved.title} uses one shared account.` },
+ 400,
+ );
+ }
+ const changed = await store.setDefaultBrokeredAccount({
+ toolkit: resolved.toolkit,
+ userId: resolved.account.userId,
+ id: context.req.param("accountId"),
+ });
+ if (!changed) {
+ return context.json(
+ { error: "That account isn't connected. Reload the page." },
+ 404,
+ );
+ }
+ return context.json({ ok: true });
+ },
+ );
+
+ /**
+ * Disconnect one of the person's accounts at an app and keep the others.
+ *
+ * The id is looked up among the signed-in person's own accounts and nowhere else, so a request
+ * naming somebody else's is a 404 like any account that is not there. The last account goes the
+ * way the app's single account always went, through the whole disconnect.
+ */
+ routes.delete(
+ "/servers/:id/accounts/:accountId",
+ requireUser,
+ async (context) => {
+ const resolved = await brokeredAccountFor(
+ context,
+ context.req.param("id"),
+ );
+ if (resolved.refusal) {
+ return context.json(
+ { error: resolved.refusal.error },
+ resolved.refusal.status,
+ );
+ }
+ if (resolved.account.holder !== "person") {
+ return context.json(
+ { error: `${resolved.title} uses one shared account.` },
+ 400,
+ );
+ }
+ try {
+ const ended = await store.disconnectBrokeredAccount({
+ toolkit: resolved.toolkit,
+ account: resolved.account,
+ id: context.req.param("accountId"),
+ by: context.var.actor.id,
+ });
+ if (ended === null) {
+ return context.json(
+ { error: "That account isn't connected. Reload the page." },
+ 404,
+ );
+ }
+ if ("only" in ended) {
+ return context.json(
+ await store.disconnectBrokered({
+ toolkit: resolved.toolkit,
+ account: resolved.account,
+ by: context.var.actor.id,
+ reason: "self",
+ }),
+ );
+ }
+ return context.json(ended);
+ } catch (error) {
+ const refusal = personRefusal(
+ error,
+ "Couldn't disconnect this account. Press Disconnect again. It's safe to repeat. Ask an admin if it keeps happening.",
+ );
+ return context.json({ error: refusal.error }, refusal.status);
+ }
+ },
+ );
+
const consentReturnUrl = (
appUrl: string,
serverId: string,
diff --git a/server/src/plugins/shared-accounts.ts b/server/src/plugins/shared-accounts.ts
index 754bb59e00..cc92fceeef 100644
--- a/server/src/plugins/shared-accounts.ts
+++ b/server/src/plugins/shared-accounts.ts
@@ -178,3 +178,38 @@ export function mintDeploymentVendorUserId(
}
return `${DEPLOYMENT_VENDOR_PREFIX}${named}:${random()}`;
}
+
+/**
+ * What separates a person's own broker id from the id a further account of theirs is filed under.
+ *
+ * Arcade keeps one account per user id and auth provider, so a person's second Gmail there is filed
+ * under `~`. The suffix is random and minted once, when they press Connect another
+ * account; every such id starts with their own id and this mark, which is how a call checks that an
+ * account it was pointed at is filed under the person in the session and nobody else.
+ */
+export const ANOTHER_ACCOUNT_MARK = "~";
+
+/** A fresh id to file one more of this person's accounts under, at a broker that needs one. */
+export function anotherArcadeAccount(
+ account: Extract,
+): Extract {
+ return {
+ ...account,
+ vendorUserId: `${account.vendorUserId}${ANOTHER_ACCOUNT_MARK}${randomBytes(4).toString("hex")}`,
+ };
+}
+
+/** Whether `vendorUserId` is this person's own broker id, or one of their further accounts' ids. */
+export function filedUnderPerson(
+ vendorUserId: string,
+ personVendorUserId: string,
+): boolean {
+ return (
+ vendorUserId === personVendorUserId ||
+ (vendorUserId.startsWith(`${personVendorUserId}${ANOTHER_ACCOUNT_MARK}`) &&
+ vendorUserId.length > personVendorUserId.length + 1 &&
+ !vendorUserId
+ .slice(personVendorUserId.length + 1)
+ .includes(ANOTHER_ACCOUNT_MARK))
+ );
+}
diff --git a/server/src/plugins/store.ts b/server/src/plugins/store.ts
index 7e3d2c67bc..8d89268d75 100644
--- a/server/src/plugins/store.ts
+++ b/server/src/plugins/store.ts
@@ -1,3 +1,4 @@
+import { accountMethods } from "./plugin-store/accounts";
import { appAccessMethods } from "./plugin-store/app-access";
import { callMethods } from "./plugin-store/calls";
import { connectionMethods } from "./plugin-store/connections";
@@ -8,6 +9,7 @@ import { serverMethods } from "./plugin-store/servers";
import { skillMethods } from "./plugin-store/skills";
export type { CatalogueEntry } from "./catalogue";
+export type { BrokeredAccountRecord } from "./plugin-store/accounts";
export type {
AccessToken,
BrokeredDisconnectReason,
@@ -55,6 +57,7 @@ export function createPluginStore(options: PluginStoreOptions) {
...connectionMethods(context),
...callMethods(context),
...appAccessMethods(context),
+ ...accountMethods(context),
};
}
diff --git a/server/src/plugins/tools.ts b/server/src/plugins/tools.ts
index bde5b6572b..10a8b48e4a 100644
--- a/server/src/plugins/tools.ts
+++ b/server/src/plugins/tools.ts
@@ -10,6 +10,7 @@ import { SEARCH_TOOLS, systemOf } from "../agents/tool-search";
import { ApprovalRefusedError } from "../approvals/types";
import type { AuditInitiator } from "../audit";
import { markUntrusted } from "../untrusted-content";
+import { withAccountArgument } from "./account-argument";
import {
type CatalogueEntry,
catalogueEntry,
@@ -324,6 +325,50 @@ export async function grantedTools(options: {
const { store, botId, actorId, initiator, credentialActorFor } = options;
const granted = await store.listForAgent(botId);
+ /*
+ * WHICH ACCOUNTS THE PERSON HOLDS AT EACH APP, so a Bot holding Gmail tools knows there are two
+ * mailboxes and what they are called. Asked about the person whose accounts the call opens, which
+ * for a Team Bot is its owner. Only an app with more than one account changes a tool: the schema
+ * gains an `account` argument listing them (see `./account-argument`), and the call path checks
+ * whatever comes back against the same person's accounts.
+ */
+ const servers = [
+ ...new Set(granted.tools.map((tool) => tool.ref.split("/")[0] ?? "")),
+ ].filter(Boolean);
+ const opensAsFor = new Map();
+ for (const tool of granted.tools) {
+ opensAsFor.set(
+ tool.ref,
+ credentialActorFor ? await credentialActorFor(tool.ref) : actorId,
+ );
+ }
+ const choicesByPerson = new Map<
+ string,
+ Awaited>
+ >();
+ for (const person of new Set(opensAsFor.values())) {
+ /*
+ * A list that cannot be read leaves every tool as the app wrote it: the call still runs in the
+ * person's default account, and nothing about which tools a Bot holds depends on this read.
+ */
+ let held: Awaited>;
+ try {
+ held = await store.accountChoicesForServers(person, servers);
+ } catch {
+ held = new Map();
+ }
+ choicesByPerson.set(person, held);
+ }
+ const schemaFor = (tool: (typeof granted.tools)[number]) => {
+ const server = tool.ref.split("/")[0] ?? "";
+ const held = choicesByPerson
+ .get(opensAsFor.get(tool.ref) ?? actorId)
+ ?.get(server);
+ return held
+ ? withAccountArgument(tool.inputSchema, held.title, held.choices)
+ : tool.inputSchema;
+ };
+
return granted.tools.map((tool) => ({
name: tool.toolName,
ref: tool.ref,
@@ -336,7 +381,7 @@ export async function grantedTools(options: {
.filter(Boolean)
.join("\n\n")
: tool.description,
- parameters: parametersFor(tool.inputSchema),
+ parameters: parametersFor(schemaFor(tool)),
execute: async (args: unknown) => {
try {
const result = await store.callTool({
diff --git a/server/src/plugins/transport.ts b/server/src/plugins/transport.ts
index ba9970c020..abe7e78beb 100644
--- a/server/src/plugins/transport.ts
+++ b/server/src/plugins/transport.ts
@@ -119,6 +119,13 @@ export type VendorTransport = {
* own conversations. Like the two above it, it comes from the run and never from `args`.
*/
threadId?: string;
+ /**
+ * Which of the person's accounts at the app the call runs in, where the vendor tells them
+ * apart by an id (Composio's connected account id). Picked by the call path from the person's
+ * own accounts and never from `args`. Absent runs in whichever account the vendor holds for
+ * the person, which is what every call did before a person could hold two.
+ */
+ accountId?: string;
},
toolName: string,
args: Record,
diff --git a/server/tests/approvals-account.integration.test.ts b/server/tests/approvals-account.integration.test.ts
new file mode 100644
index 0000000000..3bd2b21e82
--- /dev/null
+++ b/server/tests/approvals-account.integration.test.ts
@@ -0,0 +1,169 @@
+import { afterAll, beforeAll, expect, test } from "bun:test";
+import { randomUUID } from "node:crypto";
+import { approvalWords } from "@openbot/shared/tool-name";
+import { and, eq, inArray } from "drizzle-orm";
+import { ruleMatches, strictestRule } from "../src/approvals/policy";
+import { equivalenceOf } from "../src/approvals/service";
+import { createApprovalStore } from "../src/approvals/store";
+import {
+ type ApprovalCandidate,
+ type ApprovalRule,
+ approvalAction,
+} from "../src/approvals/types";
+import { createDatabase } from "../src/db/client";
+import { approvalRules, users } from "../src/db/schema";
+import { workItems } from "../src/db/schema/work";
+import { TEST_POOL, testDatabaseUrl } from "./support/database";
+
+/**
+ * AN APPROVAL FOR ONE MAILBOX NEVER COVERS ANOTHER.
+ *
+ * A person with two Gmail accounts asked a Bot to send from their work address and pressed "Always
+ * allow here". The same send from their personal address is a different request: it is not the
+ * equivalent of the first, its digest is not the first's, and the rule saved for the work address
+ * does not allow it. The card names the account it is asking about.
+ */
+
+const database = createDatabase(testDatabaseUrl(), TEST_POOL);
+const prefix = `approval-account-${randomUUID()}`;
+const owner = `${prefix}-owner`;
+const store = createApprovalStore(database);
+const keys: string[] = [];
+const WORK = "dana@work.example";
+const HOME = "dana.home@gmail.example";
+
+beforeAll(async () => {
+ await database
+ .insert(users)
+ .values([{ id: owner, email: `${owner}@example.test` }]);
+ await store.setEnabled(owner, true);
+});
+
+afterAll(async () => {
+ if (keys.length)
+ await database
+ .delete(workItems)
+ .where(
+ and(
+ eq(workItems.kind, "approval.resume"),
+ inArray(workItems.key, keys),
+ ),
+ );
+ await database.delete(users).where(eq(users.id, owner));
+ await database.$client.close();
+});
+
+/** One Gmail send for the owner, from `account`, otherwise identical every time. */
+function send(account?: string): ApprovalCandidate {
+ const args = { to: "pat@example.test", subject: "Hi", body: "See you." };
+ return {
+ actorId: owner,
+ botId: `${prefix}-bot`,
+ toolRef: "gmail/GMAIL_SEND_EMAIL",
+ effect: "write",
+ scope: "gmail",
+ args,
+ target: { serverId: "gmail", toolName: "GMAIL_SEND_EMAIL" },
+ ...(account ? { account } : {}),
+ continuation: {
+ runId: randomUUID(),
+ threadId: `${prefix}-thread`,
+ toolCallId: randomUUID(),
+ toolName: "gmail__GMAIL_SEND_EMAIL",
+ args,
+ messages: [],
+ state: {},
+ context: [],
+ forwardedProps: {},
+ },
+ };
+}
+
+test("two otherwise identical calls to different accounts are different requests", () => {
+ const fromWork = send(WORK);
+ const fromHome = { ...send(HOME), continuation: fromWork.continuation };
+
+ expect(equivalenceOf(fromWork)).not.toBe(equivalenceOf(fromHome));
+ expect(approvalAction(fromWork).actionDigest).not.toBe(
+ approvalAction(fromHome).actionDigest,
+ );
+ // And the same account twice is the same request, which is what the comparison is for.
+ expect(equivalenceOf(fromWork)).toBe(
+ equivalenceOf({ ...send(WORK), continuation: fromWork.continuation }),
+ );
+ // A call that names no account hashes as it always did.
+ expect(equivalenceOf(send())).toBe(
+ equivalenceOf({ ...send(), account: undefined }),
+ );
+});
+
+test("Always allow here for one account is saved for that account, and does not allow another", async () => {
+ const first = await store.open(approvalAction(send(WORK)));
+ keys.push(first.id);
+ await store.decide(owner, first.id, "allow_always");
+
+ const [saved] = await database
+ .select()
+ .from(approvalRules)
+ .where(eq(approvalRules.ownerUserId, owner));
+ expect(saved?.account).toBe(WORK);
+
+ const againFromWork = await store.open(approvalAction(send(WORK)));
+ keys.push(againFromWork.id);
+ expect(againFromWork.status).toBe("approved");
+
+ const fromHome = await store.open(approvalAction(send(HOME)));
+ keys.push(fromHome.id);
+ expect(fromHome.status).toBe("pending");
+
+ // The policy layer reads the same rule the same way.
+ const rules = await store.rules(owner);
+ expect(strictestRule(send(WORK), [], rules)?.rule.behaviour).toBe("allow");
+ expect(strictestRule(send(HOME), [], rules)).toBeUndefined();
+});
+
+test("a personal allow rule naming no account no longer allows a call that names one; asking still holds everywhere", () => {
+ const rule = (
+ behaviour: ApprovalRule["behaviour"],
+ account: string | null = null,
+ ) => ({
+ id: randomUUID(),
+ botId: "*",
+ toolRef: "gmail/*",
+ effect: "write",
+ scope: "gmail",
+ behaviour,
+ account,
+ });
+
+ // Written before the person had a second account, so not consent for a mailbox it never saw.
+ expect(ruleMatches(rule("allow"), send(HOME))).toBe(false);
+ expect(ruleMatches(rule("pre_approved"), send(HOME))).toBe(false);
+ // It still covers a call that names no account, as it always did.
+ expect(ruleMatches(rule("allow"), send())).toBe(true);
+ // The strict behaviours hold for every account.
+ expect(ruleMatches(rule("ask"), send(HOME))).toBe(true);
+ expect(ruleMatches(rule("hand_off"), send(HOME))).toBe(true);
+ // A team rule is the admin's policy for the app, whichever account.
+ expect(ruleMatches(rule("allow"), send(HOME), "team_rule")).toBe(true);
+ // A rule for one account is about that account only, in either layer.
+ expect(ruleMatches(rule("ask", WORK), send(HOME), "team_rule")).toBe(false);
+ expect(ruleMatches(rule("allow", WORK), send(WORK))).toBe(true);
+});
+
+test("the card names the account it is asking about", () => {
+ const words = approvalWords({
+ toolRef: "gmail/GMAIL_SEND_EMAIL",
+ scope: "gmail",
+ account: WORK,
+ });
+ expect(`${words.action} on ${words.place}`).toBe(
+ `send email from ${WORK} on Gmail`,
+ );
+ // With one account there is nothing to tell apart, and the card reads as it did.
+ const single = approvalWords({
+ toolRef: "gmail/GMAIL_SEND_EMAIL",
+ scope: "gmail",
+ });
+ expect(`${single.action} on ${single.place}`).toBe("send email on Gmail");
+});
diff --git a/server/tests/arcade-multiple-accounts.integration.test.ts b/server/tests/arcade-multiple-accounts.integration.test.ts
new file mode 100644
index 0000000000..c67abfdf1e
--- /dev/null
+++ b/server/tests/arcade-multiple-accounts.integration.test.ts
@@ -0,0 +1,223 @@
+import { afterAll, beforeAll, describe, expect, test } from "bun:test";
+import { randomUUID } from "node:crypto";
+import { and, eq } from "drizzle-orm";
+import { createAuditStore } from "../src/audit";
+import { createDatabase } from "../src/db/client";
+import {
+ agents,
+ brokeredAccounts,
+ brokeredConnections,
+ mcpServers,
+ mcpTools,
+ pluginGrants,
+} from "../src/db/schema";
+import type { ConnectedAppBroker } from "../src/plugins/broker";
+import {
+ type AccountRef,
+ anotherArcadeAccount,
+} from "../src/plugins/shared-accounts";
+import { createPluginStore, PluginRefusedError } from "../src/plugins/store";
+import { stubBroker } from "./support/stub-broker";
+import { testDatabaseUrl } from "./support/database";
+import { removePeople, seedPeople } from "./support/people";
+
+/**
+ * ARCADE MIRRORS COMPOSIO: A PERSON'S SECOND ACCOUNT AT AN APP, AND A CALL THAT RUNS IN IT.
+ *
+ * Arcade keeps one account per user id and auth provider, so a person's second Gmail there is filed
+ * under a further id of their own (`~`). What is asserted: completing the consent
+ * for that further id leaves the connection row naming the person's own id and records the further
+ * account beside it; the page lists both by the address Arcade reports; a call naming the second
+ * address runs AS the further id; a name the person does not hold is refused; no name runs as the
+ * person's own id, which is the default.
+ */
+
+const database = createDatabase(testDatabaseUrl(), { max: 4 });
+const suite = randomUUID().slice(0, 8);
+const PERSON = `arcade-person-${suite}`;
+const APP = `gmail-${suite}`;
+const SERVER = `arcade-gmail-${suite}`;
+const BOT = `bot-arcade-${suite}`;
+const WORK = "dana@work.example";
+const HOME = "dana.home@gmail.example";
+
+const own: AccountRef = {
+ holder: "person",
+ userId: PERSON,
+ vendorUserId: PERSON,
+};
+const further = anotherArcadeAccount(
+ own as Extract,
+);
+
+/** Arcade's listing: one live account per user id, named by the address the provider reported. */
+const identities = new Map([
+ [PERSON, WORK],
+ [further.vendorUserId, HOME],
+]);
+
+const broker: ConnectedAppBroker = {
+ ...stubBroker({
+ grantIsLocal: true,
+ authorizations: {
+ status: async () => {
+ throw new Error("nothing here polls an authorization");
+ },
+ confirmReturn: async () => {
+ throw new Error("nothing here confirms a return leg");
+ },
+ },
+ }).broker,
+ accountsOf: async ({ account }) => {
+ const identity = identities.get(account.vendorUserId);
+ return identity ? [{ accountId: null, identity, connectedAt: null }] : [];
+ },
+};
+
+const reached: { actorId?: string; accountId?: string }[] = [];
+
+const store = createPluginStore({
+ brokerProvider: "arcade",
+ database,
+ auditStore: createAuditStore(database),
+ credentials: {} as never,
+ encryptionKey: "x".repeat(44),
+ policy: () => ({ mode: "enforce", deny: [], allow: ["true"] }),
+ broker,
+ deploymentId: `acme-${suite}`,
+ callVendor: async (connection) => {
+ reached.push({
+ ...(connection.actorId === undefined
+ ? {}
+ : { actorId: connection.actorId }),
+ ...(connection.accountId === undefined
+ ? {}
+ : { accountId: connection.accountId }),
+ });
+ return { text: "[]", isError: false };
+ },
+});
+
+async function consentFor(account: AccountRef, authId: string) {
+ const claimed = await store.claimAuthorizationAttempt({
+ actorId: PERSON,
+ serverId: SERVER,
+ action: null,
+ account,
+ });
+ if (!claimed.claimed) throw new Error("claim failed");
+ await store.recordAuthorizationMint({
+ actorId: PERSON,
+ createdAt: claimed.attempt.createdAt,
+ authId,
+ });
+ await store.completeAuthorizationAttempt({
+ actorId: PERSON,
+ authId,
+ toolkit: APP,
+ account,
+ action: null,
+ scopes: ["gmail.readonly"],
+ authProvider: "google",
+ });
+}
+
+const fetchEmails = (args: Record) =>
+ store.callTool({
+ ref: `${SERVER}/Gmail_ListEmails`,
+ args,
+ botId: BOT,
+ actorId: PERSON,
+ });
+
+beforeAll(async () => {
+ await seedPeople(database, [PERSON]);
+ await database.insert(mcpServers).values({
+ id: SERVER,
+ title: "Gmail",
+ vendor: "Arcade",
+ url: `arcade://${APP}`,
+ provenance: "arcade",
+ authScheme: "OAUTH2",
+ accountMode: "personal",
+ });
+ await database.insert(mcpTools).values({
+ serverId: SERVER,
+ name: "Gmail_ListEmails",
+ vendorName: "Gmail.ListEmails",
+ description: "List emails.",
+ effect: "read",
+ });
+ await database
+ .insert(agents)
+ .values({ id: BOT, name: "Helper", type: "built_in", configuration: {} });
+ await store.grant(
+ "mcp",
+ `${SERVER}/Gmail_ListEmails`,
+ BOT,
+ "admin@example.com",
+ );
+});
+
+afterAll(async () => {
+ await database.delete(pluginGrants).where(eq(pluginGrants.agentId, BOT));
+ await database.delete(agents).where(eq(agents.id, BOT));
+ await database.delete(brokeredAccounts).where(eq(brokeredAccounts.app, APP));
+ await database
+ .delete(brokeredConnections)
+ .where(eq(brokeredConnections.app, APP));
+ await database.delete(mcpServers).where(eq(mcpServers.id, SERVER));
+ await removePeople(database, [PERSON]);
+ await database.$client.close();
+});
+
+describe("a person with two Gmail accounts at Arcade", () => {
+ test("the second consent keeps the connection on the person's own id and records the further account", async () => {
+ await consentFor(own, `ar_own_${suite}`);
+ await consentFor(further, `ar_further_${suite}`);
+
+ const connections = await database
+ .select()
+ .from(brokeredConnections)
+ .where(
+ and(
+ eq(brokeredConnections.app, APP),
+ eq(brokeredConnections.userId, PERSON),
+ ),
+ );
+ expect(connections).toHaveLength(1);
+ expect(connections[0]?.vendorUserId).toBe(PERSON);
+
+ const listed = await store.syncBrokeredAccounts({
+ toolkit: APP,
+ account: own,
+ });
+ expect(
+ listed.map(({ label, isDefault }) => ({ label, isDefault })),
+ ).toEqual([
+ { label: WORK, isDefault: true },
+ { label: HOME, isDefault: false },
+ ]);
+ });
+
+ test("a call naming the second address runs as the further id, and no name runs as the person", async () => {
+ reached.length = 0;
+
+ await fetchEmails({ account: HOME });
+ await fetchEmails({});
+
+ expect(reached).toEqual([
+ { actorId: further.vendorUserId },
+ { actorId: PERSON },
+ ]);
+ });
+
+ test("a name the person does not hold is refused before the vendor is asked", async () => {
+ reached.length = 0;
+
+ await expect(
+ fetchEmails({ account: "someone.else@work.example" }),
+ ).rejects.toBeInstanceOf(PluginRefusedError);
+ expect(reached).toEqual([]);
+ });
+});
diff --git a/server/tests/brokered-multiple-accounts.integration.test.ts b/server/tests/brokered-multiple-accounts.integration.test.ts
new file mode 100644
index 0000000000..2209529243
--- /dev/null
+++ b/server/tests/brokered-multiple-accounts.integration.test.ts
@@ -0,0 +1,657 @@
+import { afterAll, afterEach, describe, expect, test } from "bun:test";
+import { randomUUID } from "node:crypto";
+import { ComposioMultipleConnectedAccountsError } from "@composio/core";
+import { and, eq, inArray } from "drizzle-orm";
+import { Hono, type MiddlewareHandler } from "hono";
+import type { z } from "zod";
+import { equivalenceOf } from "../src/approvals/service";
+import type { ApprovalCandidate, ApprovalGate } from "../src/approvals/types";
+import { createAuditStore } from "../src/audit";
+import type { AppVariables } from "../src/auth/guards";
+import { createDatabase } from "../src/db/client";
+import {
+ agents,
+ brokeredAccounts,
+ brokeredConnections,
+ mcpServers,
+ mcpTools,
+ pluginGrants,
+} from "../src/db/schema";
+import { useComposioClient } from "../src/plugins/composio";
+import {
+ buildComposioClient,
+ type ComposioVendor,
+} from "../src/plugins/composio-adapter";
+import { createPluginRoutes } from "../src/plugins/routes";
+import { createPluginStore, PluginRefusedError } from "../src/plugins/store";
+import { grantedTools } from "../src/plugins/tools";
+import { TEST_POOL, testDatabaseUrl } from "./support/database";
+
+/**
+ * A PERSON WITH TWO GMAIL ACCOUNTS, AND A BOT THAT CAN USE EACH ONE.
+ *
+ * WHAT WAS SEEN. Somebody with a work and a personal Gmail pressed Connect a second time and was told
+ * "You already have an account connected to Gmail. Disconnect it first if you want to connect a
+ * different one." Every call ran in whichever account Composio picked, so a second mailbox could not
+ * be connected at all.
+ *
+ * WHAT IS ASSERTED. The app page lists both accounts by the address Composio reports. A second
+ * Connect mints a link that asks for another account. A call naming the second address runs in the
+ * second connected account's id; a call naming an address the person does not hold, including
+ * somebody else's, is refused before the vendor is asked; a call naming none runs in the default. A
+ * connection made before accounts were listed calls exactly as it did.
+ *
+ * THE REAL ROUTES, THE REAL STORE, THE REAL COMPOSIO ADAPTER AND TRANSPORT. Only Composio's client
+ * is a double, and its connected accounts are the shape `@composio/core` 0.18.1 hands over after
+ * `transformConnectedAccountResponse`: the raw item spread with the camelCase fields beside it, the
+ * address in `data.displayName` and `state.val.displayName`, tokens beside it that nothing may read.
+ * Every listing honours the query it was given, so a question about one person cannot be answered
+ * with another's accounts.
+ */
+
+const database = createDatabase(testDatabaseUrl(), TEST_POOL);
+const suite = randomUUID().slice(0, 8);
+const PERSON = `person-${suite}`;
+/** Somebody else at this deployment, with a Gmail of their own. */
+const OTHER = `other-${suite}`;
+const BOT = `bot-multi-${suite}`;
+const WORK = "dana@work.example";
+const HOME = "dana.home@gmail.example";
+const THEIRS = "someone.else@work.example";
+
+const made: string[] = [];
+
+type HeldAccount = {
+ id: string;
+ userId: string;
+ toolkit: string;
+ email: string | null;
+ createdAt: string;
+ status: "ACTIVE" | "EXPIRED";
+};
+
+const refuse = (what: string) => async (): Promise => {
+ throw new Error(`${what} should not have been called in this test.`);
+};
+
+/** A connected account as the SDK's listing hands it over, tokens and all. */
+function vendorRow(account: HeldAccount, config: string) {
+ const val = {
+ status: account.status,
+ ...(account.email ? { displayName: account.email } : {}),
+ scope: "https://www.googleapis.com/auth/gmail.modify",
+ access_token: "ya29.not-a-real-token",
+ refresh_token: "1//not-a-real-refresh-token",
+ id_token: "eyJ.not-a-real-id-token",
+ token_type: "Bearer",
+ expires_in: 3599,
+ };
+ return {
+ id: account.id,
+ word_id: `gmail_${account.id}`,
+ wordId: `gmail_${account.id}`,
+ alias: null,
+ user_id: account.userId,
+ status: account.status,
+ status_reason: null,
+ statusReason: null,
+ is_disabled: false,
+ isDisabled: false,
+ toolkit: { slug: account.toolkit },
+ auth_config: {
+ id: config,
+ auth_scheme: "OAUTH2",
+ is_composio_managed: true,
+ is_disabled: false,
+ },
+ authConfig: {
+ id: config,
+ authScheme: "OAUTH2",
+ isComposioManaged: true,
+ isDisabled: false,
+ },
+ data: val,
+ state: { authScheme: "OAUTH2", val },
+ created_at: account.createdAt,
+ createdAt: account.createdAt,
+ updated_at: account.createdAt,
+ updatedAt: account.createdAt,
+ };
+}
+
+/** Composio holding `accounts` for one app, recording what it was asked. */
+function composioHolding(toolkit: string, accounts: HeldAccount[]) {
+ const config = `ac_${toolkit}`;
+ const asked = {
+ links: [] as { userId: string; allowMultiple: boolean }[],
+ executed: [] as { userId: string; connectedAccountId?: string }[],
+ deleted: [] as string[],
+ };
+ const vendor: ComposioVendor = {
+ tools: {
+ list: refuse("tools.list"),
+ getRawComposioToolBySlug: async (slug) =>
+ ({
+ slug,
+ toolkit: { slug: toolkit },
+ inputParameters: {
+ type: "object",
+ properties: { query: { type: "string" } },
+ },
+ }) as never,
+ execute: async (_slug, body) => {
+ asked.executed.push({
+ userId: body.userId,
+ ...(body.connectedAccountId === undefined
+ ? {}
+ : { connectedAccountId: body.connectedAccountId }),
+ });
+ return { data: { messages: [] }, error: null, successful: true };
+ },
+ },
+ toolkits: {
+ list: refuse("toolkits.list"),
+ retrieve: refuse("toolkits.retrieve"),
+ },
+ authConfigs: {
+ list: async (query) => ({
+ items:
+ query.toolkit === toolkit
+ ? [{ id: config, name: `${toolkit} (OpenBot)`, status: "ENABLED" }]
+ : [],
+ nextCursor: null,
+ }),
+ create: refuse("authConfigs.create"),
+ delete: refuse("authConfigs.delete"),
+ },
+ connectedAccounts: {
+ list: async (query) => ({
+ items: accounts
+ .filter(
+ (account) =>
+ query.userIds.includes(account.userId) &&
+ query.toolkitSlugs.includes(account.toolkit) &&
+ query.statuses.some((status) => status === account.status) &&
+ (query.authConfigIds === undefined ||
+ query.authConfigIds.includes(config)),
+ )
+ .map((account) => vendorRow(account, config)),
+ nextCursor: null,
+ }),
+ // `@composio/core` 0.18.1 `ConnectedAccounts.link`: refuses a held account unless asked for more.
+ link: async (userId, authConfigId, options) => {
+ const held = accounts.some(
+ (account) =>
+ account.userId === userId &&
+ account.toolkit === toolkit &&
+ account.status === "ACTIVE",
+ );
+ if (held && !options.allowMultiple) {
+ throw new ComposioMultipleConnectedAccountsError(
+ `Multiple connected accounts found for user ${userId} in auth config ${authConfigId}. Please use the allowMultiple option to allow multiple connected accounts.`,
+ );
+ }
+ asked.links.push({
+ userId,
+ allowMultiple: options.allowMultiple === true,
+ });
+ return {
+ redirectUrl: `https://connect.composio.test/link/${userId}`,
+ };
+ },
+ create: refuse("connectedAccounts.create"),
+ delete: async (id) => {
+ asked.deleted.push(id);
+ const at = accounts.findIndex((account) => account.id === id);
+ if (at >= 0) accounts.splice(at, 1);
+ return { success: true };
+ },
+ },
+ };
+ const client = buildComposioClient(vendor);
+ useComposioClient(client.actions);
+ return { broker: client.broker, asked };
+}
+
+/** Two of Dana's Gmail accounts, work first, and one of somebody else's. */
+function twoMailboxes(toolkit: string): HeldAccount[] {
+ return [
+ {
+ id: "ca_dana_work",
+ userId: PERSON,
+ toolkit,
+ email: WORK,
+ createdAt: "2026-10-01T09:00:00.000Z",
+ status: "ACTIVE",
+ },
+ {
+ id: "ca_dana_home",
+ userId: PERSON,
+ toolkit,
+ email: HOME,
+ createdAt: "2026-10-20T09:00:00.000Z",
+ status: "ACTIVE",
+ },
+ {
+ id: "ca_someone_else",
+ userId: OTHER,
+ toolkit,
+ email: THEIRS,
+ createdAt: "2026-09-01T09:00:00.000Z",
+ status: "ACTIVE",
+ },
+ ];
+}
+
+/** A Gmail app with one read action granted to the Bot, and the person's connection row. */
+async function enableGmail(toolkit: string) {
+ made.push(toolkit);
+ await database.insert(mcpServers).values({
+ id: toolkit,
+ title: "Gmail",
+ vendor: "Composio",
+ url: `composio://${toolkit}`,
+ provenance: "composio",
+ authScheme: "OAUTH2",
+ accountMode: "personal",
+ });
+ await database.insert(mcpTools).values({
+ serverId: toolkit,
+ name: "GMAIL_FETCH_EMAILS",
+ vendorName: "GMAIL_FETCH_EMAILS",
+ description: "Fetch emails.",
+ effect: "read",
+ version: "20260903_00",
+ inputSchema: { type: "object", properties: { query: { type: "string" } } },
+ });
+ await database
+ .insert(agents)
+ .values({ id: BOT, name: "Helper", type: "built_in", configuration: {} })
+ .onConflictDoNothing();
+ await database.insert(brokeredConnections).values({
+ provider: "composio",
+ app: toolkit,
+ holder: "person",
+ userId: PERSON,
+ vendorUserId: PERSON,
+ verified: true,
+ });
+}
+
+function storeFor(
+ broker: ReturnType["broker"],
+ approvalGate?: ApprovalGate,
+) {
+ return createPluginStore({
+ brokerProvider: "composio",
+ database,
+ auditStore: createAuditStore(database),
+ credentials: {} as never,
+ encryptionKey: "x".repeat(44),
+ policy: () => ({ mode: "enforce", deny: [], allow: ["true"] }),
+ broker,
+ deploymentId: `acme-${suite}`,
+ ...(approvalGate ? { approvalGate } : {}),
+ });
+}
+
+function routesAs(
+ store: ReturnType,
+ broker: ReturnType["broker"],
+ id: string,
+) {
+ const session: MiddlewareHandler<{ Variables: AppVariables }> = async (
+ context,
+ next,
+ ) => {
+ context.set("actor", {
+ id,
+ email: `${id}@example.test`,
+ role: "user",
+ } as never);
+ await next();
+ };
+ return new Hono().route(
+ "/api/plugins",
+ createPluginRoutes(
+ store,
+ session,
+ async () => true,
+ {
+ publicUrl: "https://openbot.example",
+ appUrl: "https://app.example",
+ encryptionKey: `${"A".repeat(43)}=`,
+ personHasAccess: async () => true,
+ },
+ { broker, provider: "composio" },
+ ),
+ );
+}
+
+type Listed = {
+ accounts: {
+ id: string;
+ label: string;
+ identity: string;
+ isDefault: boolean;
+ }[];
+ canAddAnother: boolean;
+};
+
+async function listAccounts(
+ app: ReturnType,
+ toolkit: string,
+): Promise {
+ const response = await app.request(
+ `/api/plugins/servers/${toolkit}/accounts`,
+ );
+ expect(response.status).toBe(200);
+ return (await response.json()) as Listed;
+}
+
+/** One Gmail read by the Bot for the person, with whatever arguments the model sent. */
+async function fetchEmails(
+ store: ReturnType,
+ toolkit: string,
+ args: Record,
+) {
+ await store.grant(
+ "mcp",
+ `${toolkit}/GMAIL_FETCH_EMAILS`,
+ BOT,
+ "admin@example.com",
+ );
+ return store.callTool({
+ ref: `${toolkit}/GMAIL_FETCH_EMAILS`,
+ args,
+ botId: BOT,
+ actorId: PERSON,
+ });
+}
+
+afterEach(async () => {
+ useComposioClient(null);
+ if (made.length === 0) return;
+ await database
+ .delete(brokeredAccounts)
+ .where(inArray(brokeredAccounts.app, made));
+ await database
+ .delete(brokeredConnections)
+ .where(inArray(brokeredConnections.app, made));
+ await database.delete(pluginGrants).where(eq(pluginGrants.agentId, BOT));
+ await database.delete(mcpServers).where(inArray(mcpServers.id, made));
+ made.length = 0;
+});
+
+afterAll(async () => {
+ await database.delete(agents).where(eq(agents.id, BOT));
+ await database.$client.end();
+});
+
+describe("a person with two Gmail accounts", () => {
+ test("the app page lists both by the address Gmail reports, the first connected as the default", async () => {
+ const toolkit = `gmail-${suite}-list`;
+ await enableGmail(toolkit);
+ const composio = composioHolding(toolkit, twoMailboxes(toolkit));
+ const store = storeFor(composio.broker);
+
+ const listed = await listAccounts(
+ routesAs(store, composio.broker, PERSON),
+ toolkit,
+ );
+
+ expect(listed.canAddAnother).toBe(true);
+ expect(
+ listed.accounts.map(({ label, identity, isDefault }) => ({
+ label,
+ identity,
+ isDefault,
+ })),
+ ).toEqual([
+ { label: WORK, identity: WORK, isDefault: true },
+ { label: HOME, identity: HOME, isDefault: false },
+ ]);
+ // Nobody else's mailbox, and no token, ever reaches the page.
+ const body = JSON.stringify(listed);
+ expect(body).not.toContain(THEIRS);
+ expect(body).not.toContain("not-a-real");
+ expect(body).not.toContain("ca_");
+ });
+
+ test("Connect over a standing connection asks for another account instead of refusing", async () => {
+ const toolkit = `gmail-${suite}-another`;
+ await enableGmail(toolkit);
+ const composio = composioHolding(
+ toolkit,
+ twoMailboxes(toolkit).slice(0, 1),
+ );
+ const store = storeFor(composio.broker);
+
+ const response = await routesAs(store, composio.broker, PERSON).request(
+ `/api/plugins/servers/${toolkit}/connect`,
+ { method: "POST" },
+ );
+
+ expect(response.status).toBe(200);
+ const body = (await response.json()) as {
+ authorizationUrl?: string;
+ error?: string;
+ };
+ expect(body.error).toBeUndefined();
+ expect(body.authorizationUrl).toStartWith(
+ `https://connect.composio.test/link/${PERSON}`,
+ );
+ expect(composio.asked.links).toEqual([
+ { userId: PERSON, allowMultiple: true },
+ ]);
+ });
+
+ test("a call naming the second address runs in the second connected account", async () => {
+ const toolkit = `gmail-${suite}-second`;
+ await enableGmail(toolkit);
+ const composio = composioHolding(toolkit, twoMailboxes(toolkit));
+ const store = storeFor(composio.broker);
+ await listAccounts(routesAs(store, composio.broker, PERSON), toolkit);
+
+ const result = await fetchEmails(store, toolkit, {
+ account: HOME,
+ query: "is:unread",
+ });
+
+ expect(result.isError).toBe(false);
+ expect(composio.asked.executed).toEqual([
+ { userId: PERSON, connectedAccountId: "ca_dana_home" },
+ ]);
+ });
+
+ test("a call naming no account runs in the default, and in the new default once the person changes it", async () => {
+ const toolkit = `gmail-${suite}-default`;
+ await enableGmail(toolkit);
+ const composio = composioHolding(toolkit, twoMailboxes(toolkit));
+ const store = storeFor(composio.broker);
+ const app = routesAs(store, composio.broker, PERSON);
+ const listed = await listAccounts(app, toolkit);
+
+ await fetchEmails(store, toolkit, { query: "is:unread" });
+
+ const home = listed.accounts.find((account) => account.label === HOME);
+ const made = await app.request(
+ `/api/plugins/servers/${toolkit}/accounts/${home?.id}/default`,
+ { method: "POST" },
+ );
+ expect(made.status).toBe(200);
+ await fetchEmails(store, toolkit, { query: "is:unread" });
+
+ expect(composio.asked.executed).toEqual([
+ { userId: PERSON, connectedAccountId: "ca_dana_work" },
+ { userId: PERSON, connectedAccountId: "ca_dana_home" },
+ ]);
+ });
+
+ test("an address the person does not hold, or somebody else's, is refused and nothing is sent", async () => {
+ const toolkit = `gmail-${suite}-foreign`;
+ await enableGmail(toolkit);
+ const composio = composioHolding(toolkit, twoMailboxes(toolkit));
+ const store = storeFor(composio.broker);
+ await listAccounts(routesAs(store, composio.broker, PERSON), toolkit);
+
+ for (const named of [
+ THEIRS,
+ "nobody@nowhere.example",
+ "ca_someone_else",
+ 42,
+ ]) {
+ const refused = fetchEmails(store, toolkit, { account: named });
+ await expect(refused).rejects.toBeInstanceOf(PluginRefusedError);
+ await expect(refused).rejects.toThrow(`Use one of: ${WORK}, ${HOME}.`);
+ }
+ expect(composio.asked.executed).toEqual([]);
+ });
+
+ test("the Bot is offered an account argument naming exactly the person's accounts", async () => {
+ const toolkit = `gmail-${suite}-offered`;
+ await enableGmail(toolkit);
+ const composio = composioHolding(toolkit, twoMailboxes(toolkit));
+ const store = storeFor(composio.broker);
+ await listAccounts(routesAs(store, composio.broker, PERSON), toolkit);
+ await store.grant(
+ "mcp",
+ `${toolkit}/GMAIL_FETCH_EMAILS`,
+ BOT,
+ "admin@example.com",
+ );
+
+ const tools = await grantedTools({ store, botId: BOT, actorId: PERSON });
+ const tool = tools.find(
+ (candidate) => candidate.ref === `${toolkit}/GMAIL_FETCH_EMAILS`,
+ );
+ const parameters = tool?.parameters as z.ZodType;
+
+ expect(parameters.safeParse({ account: HOME }).success).toBe(true);
+ expect(parameters.safeParse({ account: WORK }).success).toBe(true);
+ expect(parameters.safeParse({ account: THEIRS }).success).toBe(false);
+ expect(parameters.safeParse({}).success).toBe(true);
+ });
+
+ test("disconnecting one account ends that account only, and the other keeps working", async () => {
+ const toolkit = `gmail-${suite}-one`;
+ await enableGmail(toolkit);
+ const composio = composioHolding(toolkit, twoMailboxes(toolkit));
+ const store = storeFor(composio.broker);
+ const app = routesAs(store, composio.broker, PERSON);
+ const listed = await listAccounts(app, toolkit);
+ const work = listed.accounts.find((account) => account.label === WORK);
+
+ const response = await app.request(
+ `/api/plugins/servers/${toolkit}/accounts/${work?.id}`,
+ { method: "DELETE" },
+ );
+
+ expect(response.status).toBe(200);
+ expect(composio.asked.deleted).toEqual(["ca_dana_work"]);
+ const after = await listAccounts(app, toolkit);
+ expect(
+ after.accounts.map(({ label, isDefault }) => ({ label, isDefault })),
+ ).toEqual([{ label: HOME, isDefault: true }]);
+ // The app is still connected for the person.
+ expect(
+ await database
+ .select()
+ .from(brokeredConnections)
+ .where(
+ and(
+ eq(brokeredConnections.app, toolkit),
+ eq(brokeredConnections.userId, PERSON),
+ ),
+ ),
+ ).toHaveLength(1);
+ await fetchEmails(store, toolkit, {});
+ expect(composio.asked.executed).toEqual([
+ { userId: PERSON, connectedAccountId: "ca_dana_home" },
+ ]);
+ });
+
+ test("another person's account id is not one this person can disconnect or make default", async () => {
+ const toolkit = `gmail-${suite}-not-yours`;
+ await enableGmail(toolkit);
+ await database.insert(brokeredConnections).values({
+ provider: "composio",
+ app: toolkit,
+ holder: "person",
+ userId: OTHER,
+ vendorUserId: OTHER,
+ verified: true,
+ });
+ const composio = composioHolding(toolkit, twoMailboxes(toolkit));
+ const store = storeFor(composio.broker);
+ await listAccounts(routesAs(store, composio.broker, PERSON), toolkit);
+ const theirs = await listAccounts(
+ routesAs(store, composio.broker, OTHER),
+ toolkit,
+ );
+ const id = theirs.accounts[0]?.id;
+
+ const app = routesAs(store, composio.broker, PERSON);
+ const ended = await app.request(
+ `/api/plugins/servers/${toolkit}/accounts/${id}`,
+ {
+ method: "DELETE",
+ },
+ );
+ const defaulted = await app.request(
+ `/api/plugins/servers/${toolkit}/accounts/${id}/default`,
+ { method: "POST" },
+ );
+
+ expect(ended.status).toBe(404);
+ expect(defaulted.status).toBe(404);
+ expect(composio.asked.deleted).toEqual([]);
+ });
+});
+
+describe("asking the person first", () => {
+ test("two otherwise identical calls to different accounts reach the approval step as different requests, each naming its account", async () => {
+ const toolkit = `gmail-${suite}-approval`;
+ await enableGmail(toolkit);
+ const composio = composioHolding(toolkit, twoMailboxes(toolkit));
+ const asked: ApprovalCandidate[] = [];
+ const store = storeFor(composio.broker, async (candidate) => {
+ asked.push(candidate);
+ return undefined;
+ });
+ await listAccounts(routesAs(store, composio.broker, PERSON), toolkit);
+
+ await fetchEmails(store, toolkit, { account: WORK, query: "is:unread" });
+ await fetchEmails(store, toolkit, { account: HOME, query: "is:unread" });
+
+ expect(asked.map((candidate) => candidate.account)).toEqual([WORK, HOME]);
+ // The account is not one of the action's own arguments, and it is part of the comparison.
+ expect(asked[0]?.args).toEqual(asked[1]?.args);
+ const [work, home] = asked;
+ if (!work || !home) throw new Error("the approval step was not asked");
+ expect(equivalenceOf(work)).not.toBe(equivalenceOf(home));
+ });
+});
+
+describe("a connection made before accounts were listed", () => {
+ test("calls exactly as it did: as the person, naming no account", async () => {
+ const toolkit = `gmail-${suite}-legacy`;
+ await enableGmail(toolkit);
+ const composio = composioHolding(
+ toolkit,
+ twoMailboxes(toolkit).slice(0, 1),
+ );
+ const store = storeFor(composio.broker);
+
+ const result = await fetchEmails(store, toolkit, { query: "is:unread" });
+
+ expect(result.isError).toBe(false);
+ expect(composio.asked.executed).toEqual([{ userId: PERSON }]);
+ expect(
+ await database
+ .select()
+ .from(brokeredAccounts)
+ .where(eq(brokeredAccounts.app, toolkit)),
+ ).toEqual([]);
+ });
+});
diff --git a/server/tests/plugin-routes.test.ts b/server/tests/plugin-routes.test.ts
index 747dc4ae74..b2681bfd7c 100644
--- a/server/tests/plugin-routes.test.ts
+++ b/server/tests/plugin-routes.test.ts
@@ -1709,6 +1709,7 @@ function brokeredApp(
account: AccountRef;
toolkit: string;
returnUrl: string;
+ another?: boolean;
}> = [];
const queried: Array<{ toolkit: string; account: AccountRef }> = [];
const confirmed: Array<{ toolkit: string; account: AccountRef; by: string }> =
@@ -2001,6 +2002,7 @@ function brokeredApp(
account: AccountRef;
toolkit: string;
returnUrl: string;
+ another?: boolean;
}) => {
// Recorded BEFORE the throw, so a test about a failing broker can still say the
// address it was asked with was this deployment's own.
@@ -2238,29 +2240,31 @@ describe("connecting a brokered app", () => {
expect(authorized).toEqual([]);
});
- test("a second connection is refused with the step to take", async () => {
+ test("a second connection asks for another account rather than refusing", async () => {
+ /*
+ * A PERSON MAY HOLD SEVERAL ACCOUNTS AT ONE APP. A press over a standing connection used to be
+ * a 409 telling them to disconnect first, which left anybody with a work and a personal account
+ * able to give their Bots only one. The link is minted for the same person, and says another
+ * account is wanted so the vendor adds one beside the one that stands.
+ */
const { authorized, connect } = brokeredApp({
connectedAt: "2026-02-02T00:00:00.000Z",
});
const response = await connect({});
- expect(response.status).toBe(409);
- const refusal = (await response.json()).error as string;
- // Naming the remedy rather than only refusing: the person has an account attached already, and
- // the only way to a new link is through disconnecting the one they have.
- expect(refusal.toLowerCase()).toContain("disconnect");
- /*
- * THE APP'S TITLE, AND NOT THE ROW'S ID. "Linear" is the name of the thing this person
- * connected; `composio-linear` is how this deployment keys a table, which they have never seen
- * and cannot act on. The second assertion is not the first one twice: a sentence that named the
- * app and then quoted the row id beside it would satisfy one and fail the other.
- */
- expect(refusal).toContain("Linear");
- expect(refusal).not.toContain("composio-linear");
- // And nothing was minted, which is the half that matters: a link handed out here would attach
- // a second account behind a row that already says connected.
- expect(authorized).toEqual([]);
+ expect(response.status).toBe(200);
+ expect(await response.json()).toEqual({
+ authorizationUrl: AUTHORIZATION_URL,
+ });
+ expect(authorized).toEqual([
+ {
+ account: ADMIN_ACCOUNT,
+ toolkit: "linear",
+ returnUrl: RETURN_URL,
+ another: true,
+ },
+ ]);
});
test("a deployment with no broker is told which setting to set", async () => {
@@ -2827,12 +2831,10 @@ describe("connecting an app whose secret a person types", () => {
});
});
- test("an account already connected is refused before the form is drawn", async () => {
+ test("an account already connected still gets the form, to add another key", async () => {
/*
- * THE ONE-ACCOUNT GUARD STILL RUNS FIRST, which is the ordering both branches were put after on
- * purpose. Drawing the form for somebody who already has an account attached invites them to
- * type a key that would be refused after they had entered it, and asking Composio what the app
- * wants is a call made on behalf of a request that is going to be refused anyway.
+ * A SECOND KEY IS A SECOND ACCOUNT, as a second consent is. The form is drawn for somebody who
+ * already has an account at the app, so they can connect another one beside it.
*/
const { asked, submitted, connectFields } = brokeredApp({
connectedAt: "2026-02-02T00:00:00.000Z",
@@ -2840,13 +2842,8 @@ describe("connecting an app whose secret a person types", () => {
const response = await connectFields();
- expect(response.status).toBe(409);
- const refusal = (await response.json()).error as string;
- expect(refusal.toLowerCase()).toContain("disconnect");
- // The app's name rather than the row's id, as on the consent half above.
- expect(refusal).toContain("Firecrawl");
- expect(refusal).not.toContain("composio-firecrawl");
- expect(asked).toEqual([]);
+ expect(response.status).toBe(200);
+ expect(asked).toEqual([{ toolkit: "firecrawl", authScheme: "API_KEY" }]);
expect(submitted).toEqual([]);
});
diff --git a/server/tests/plugin-store.integration.test.ts b/server/tests/plugin-store.integration.test.ts
index 52b704208b..518202832e 100644
--- a/server/tests/plugin-store.integration.test.ts
+++ b/server/tests/plugin-store.integration.test.ts
@@ -24,6 +24,7 @@ import { createDatabase, type Database } from "../src/db/client";
import {
agents,
auditEvents,
+ brokeredAccounts,
brokeredConnections,
credentials as credentialRows,
credentials,
@@ -4493,6 +4494,10 @@ async function freshDatabase(): Promise {
* the guard's to refuse, because at that point nothing has established it is ours.
*/
await database.delete(brokeredConnections).where(ownedConnections());
+ // The accounts behind those connections, by the same two people and never by app.
+ await database
+ .delete(brokeredAccounts)
+ .where(inArray(brokeredAccounts.userId, ["user_asker", strangerId]));
// The person the connection outlives, who is a row in `users` like anybody else. Reached only
// through the check at the top of this function, because there is no suffix on this id to tell a
// fixture apart from somebody's account and ten cascades sit behind the difference.
@@ -4674,6 +4679,10 @@ afterAll(async () => {
.delete(mcpServers)
.where(inArray(mcpServers.id, ["gmail", "notion"]));
await database.delete(brokeredConnections).where(ownedConnections());
+ // The accounts behind those connections, by the same two people and never by app.
+ await database
+ .delete(brokeredAccounts)
+ .where(inArray(brokeredAccounts.userId, ["user_asker", strangerId]));
/*
* And the witness row, which is at neither `gmail` nor any person.
*
@@ -5349,6 +5358,114 @@ test("an identity a model puts in the arguments does not change whose account th
]);
});
+/**
+ * The same claim with several accounts: a name a model puts in the arguments picks among the
+ * asker's own accounts and nobody else's.
+ *
+ * `account` is the one argument the call path reads, and it is a NAME matched against the accounts
+ * of the person in the session. So naming somebody else's mailbox is refused before the vendor is
+ * asked, naming one of the asker's own runs in that account's id, and the identity keys beside it
+ * still reach the vendor as the action's own arguments and nothing more.
+ */
+test("with several accounts, an identity a model puts in the arguments still opens only the asker's own", async () => {
+ const { store, database } = await freshStore();
+ const reached: {
+ userId: string;
+ connectedAccountId?: string;
+ args: Record;
+ }[] = [];
+ useComposioClient({
+ listActions: async () => [],
+ execute: async ({ userId, connectedAccountId }, args) => {
+ reached.push({
+ userId,
+ ...(connectedAccountId === undefined ? {} : { connectedAccountId }),
+ args,
+ });
+ return vendorAnswered();
+ },
+ });
+ await seedComposioGmail(database, store);
+ await database.insert(brokeredAccounts).values([
+ {
+ provider: "composio",
+ app: "gmail",
+ userId: "user_asker",
+ vendorUserId: "user_asker",
+ accountId: "ca_asker_work",
+ identity: "asker@work.example",
+ isDefault: true,
+ connectedAt: new Date("2026-10-01T09:00:00.000Z"),
+ },
+ {
+ provider: "composio",
+ app: "gmail",
+ userId: "user_asker",
+ vendorUserId: "user_asker",
+ accountId: "ca_asker_home",
+ identity: "asker@home.example",
+ connectedAt: new Date("2026-10-02T09:00:00.000Z"),
+ },
+ {
+ provider: "composio",
+ app: "gmail",
+ userId: strangerId,
+ vendorUserId: strangerId,
+ accountId: "ca_stranger",
+ identity: "stranger@work.example",
+ isDefault: true,
+ },
+ ]);
+
+ // The stranger's mailbox by name, beside the three identity keys: refused, and nothing sent.
+ await expect(
+ store.callTool({
+ ref: "gmail/GMAIL_FETCH_EMAILS",
+ args: {
+ userId: strangerId,
+ user_id: strangerId,
+ entityId: strangerId,
+ account: "stranger@work.example",
+ query: "is:unread",
+ },
+ botId: "bot_helper",
+ actorId: "user_asker",
+ }),
+ ).rejects.toBeInstanceOf(PluginRefusedError);
+ expect(reached).toEqual([]);
+
+ // The asker's own second account by name: their own id, that account's id, and the model's
+ // other arguments passed through whole, minus the account choice itself. A Gmail listing also
+ // carries the lean-listing defaults every GMAIL_FETCH_EMAILS call gets (composio-shapes.ts).
+ await store.callTool({
+ ref: "gmail/GMAIL_FETCH_EMAILS",
+ args: {
+ userId: strangerId,
+ user_id: strangerId,
+ entityId: strangerId,
+ account: "asker@home.example",
+ query: "is:unread",
+ },
+ botId: "bot_helper",
+ actorId: "user_asker",
+ });
+ expect(reached).toEqual([
+ {
+ userId: "user_asker",
+ connectedAccountId: "ca_asker_home",
+ args: {
+ userId: strangerId,
+ user_id: strangerId,
+ entityId: strangerId,
+ query: "is:unread",
+ include_payload: false,
+ verbose: false,
+ max_results: 20,
+ },
+ },
+ ]);
+});
+
test("a Composio call is recorded as reaching the vendor as the person, not as the deployment", async () => {
const { store, database, auditStore } = await freshStore();
useComposioClient({
diff --git a/shared/tool-name.ts b/shared/tool-name.ts
index 16a503c26a..1f196f5f8e 100644
--- a/shared/tool-name.ts
+++ b/shared/tool-name.ts
@@ -160,6 +160,8 @@ export function approvalWords(action: {
scope: string;
/** What the action is aimed at, as the server resolved it. A folder's carries its name. */
target?: unknown;
+ /** Which of the person's accounts at the app, where they hold several: "from dana@work.example". */
+ account?: string;
}): {
action: string;
place: string;
@@ -177,6 +179,8 @@ export function approvalPhrase(action: {
toolRef: string;
scope: string;
target?: unknown;
+ /** Which of the person's accounts at the app, where they hold several: "from dana@work.example". */
+ account?: string;
}): string {
const { action: words, place, joiner } = approvalParts(action);
return joiner ? `${words} ${joiner} ${place}` : `${words} ${place}`;
@@ -186,13 +190,15 @@ function approvalParts(action: {
toolRef: string;
scope: string;
target?: unknown;
+ account?: string;
}): { action: string; place: string; joiner: "on" | "in" | "" } {
const slash = action.toolRef.indexOf("/");
const app = action.toolRef.slice(0, slash);
if (slash > 0 && app === action.scope) {
const { label } = appToolName(app, action.toolRef.slice(slash + 1));
+ const words = lowerFirst(label);
return {
- action: lowerFirst(label),
+ action: action.account ? `${words} from ${action.account}` : words,
place: connectorName(app),
joiner: "on",
};
From 8b5c55be4724eb38a99d53fda6bbfb617ff3c9b2 Mon Sep 17 00:00:00 2001
From: David McKay
Date: Sun, 8 Nov 2026 19:44:33 -0800
Subject: [PATCH 08/34] Say what happens next after a recording saves on Teach
a Bot (#1302)
---
app/src/components/browser/browser-screen.tsx | 9 +
app/src/components/browser/learning-next.tsx | 460 ++++++++++++++++++
app/src/components/browser/teach-steps.tsx | 67 ++-
app/src/lib/learning/queries.ts | 26 +
app/src/routes/_authed/_app/index.tsx | 16 +-
app/tests/browser-page.test.tsx | 308 ++++++++++++
app/tests/home-fallback-routing.test.tsx | 30 +-
server/src/index.ts | 1 +
server/src/learning/sharing-routes.ts | 87 +++-
.../tests/learning-recording-skills.test.ts | 189 +++++++
shared/learning.ts | 14 +
11 files changed, 1197 insertions(+), 10 deletions(-)
create mode 100644 app/src/components/browser/learning-next.tsx
create mode 100644 server/tests/learning-recording-skills.test.ts
diff --git a/app/src/components/browser/browser-screen.tsx b/app/src/components/browser/browser-screen.tsx
index 8d7bd2be7e..0cffa6d8ef 100644
--- a/app/src/components/browser/browser-screen.tsx
+++ b/app/src/components/browser/browser-screen.tsx
@@ -32,6 +32,7 @@ import {
useOpenedTab,
} from "./browser-bar";
import { useFittedWidth } from "./fitted-window";
+import { LearningNextNotice, savedToLearning } from "./learning-next";
import { NarrationNotice, NarrationSwitch } from "./narration";
import { NarrationChoiceDialog, useNarrationChoice } from "./narration-choice";
import {
@@ -184,6 +185,14 @@ export function ScreenBrowser({
{recording.stopped ? (
) : null}
+ {/* Once it is in the Learning space: what happens to it next, and the way to the skill it becomes. */}
+ {recording.stopped && savedToLearning(recording.stopped) ? (
+
+ ) : null}
{failure ? (
+ run.status === "succeeded" || run.status === "failed";
+
+/**
+ * Whether a stopped recording is in the person's Learning space, so there is a next step to say: not discarded, not
+ * empty, not kept in the browser, and no longer on its way or being retried.
+ */
+export function savedToLearning(stopped: Stopped | undefined): boolean {
+ return (
+ !!stopped &&
+ !stopped.discarded &&
+ !stopped.empty &&
+ !stopped.kept &&
+ !stopped.sending &&
+ !stopped.retrying
+ );
+}
+
+/**
+ * What Learning has made of the recording Stop just saved, read from the person's own review (their runs, and whether
+ * they or an admin approve) and the skills proposed from that recording since it stopped. Learn now starts the same
+ * run as Settings, Learning on each of the person's recording spaces.
+ *
+ * While nothing has come of it yet the review is read again, every 10 seconds during a run and every 30 otherwise, so
+ * an automatic run is seen too. Once a skill from it is there, the page tells the person, and the browser does too
+ * (a notification, where they allowed them) when they are looking at something else.
+ */
+export function useLearningNext(stopped: Stopped) {
+ const since = stopped.stoppedAt;
+ const [started, setStarted] = useState<{ from: string | null } | null>(null);
+ // How often the review is read again, set from what the last read said (below).
+ const pace = useRef(30_000);
+ const review = useQuery({
+ ...personalReviewQueryOptions(),
+ refetchInterval: () => pace.current,
+ });
+ const learnNow = useMutation(
+ startPersonalLearningRunMutationOptions(queryClient),
+ );
+
+ const sources = (review.data?.sources ?? []).filter(
+ (source) => source.kind === "recordings",
+ );
+ const latest = sources
+ .map((source) => source.latestRun)
+ .filter((run) => run !== null)
+ .reduce(
+ (newest, run) =>
+ !newest || run.createdAt > newest.createdAt ? run : newest,
+ null,
+ );
+ const off =
+ sources.length > 0 && sources.every((source) => source.unavailable);
+ const waitingCount = sources.reduce(
+ (count, source) => count + (source.proposals?.length ?? 0),
+ 0,
+ );
+ // Anything that can mean a new skill: a run moving on, a proposal arriving, one approved.
+ const revision = `${latest?.id ?? ""}:${latest?.status ?? ""}:${waitingCount}:${review.data?.approved ?? 0}`;
+ const skills = useQuery({
+ ...recordingSkillsQueryOptions(stopped.title, since, revision),
+ enabled: review.isSuccess && sources.length > 0 && !off,
+ placeholderData: keepPreviousData,
+ });
+
+ // A run Learn now started counts as going until a newer run than the one there was is over.
+ const runSince =
+ latest && Date.parse(latest.createdAt) >= since - CLOCK_SLACK_MS
+ ? latest
+ : null;
+ const startedRunOver =
+ started !== null &&
+ latest !== null &&
+ latest.id !== started.from &&
+ finished(latest);
+ const running =
+ learnNow.isPending ||
+ (latest !== null && !finished(latest)) ||
+ (started !== null && !startedRunOver);
+
+ const found = skills.data?.skills ?? [];
+ const learned = found.find((skill) => skill.status === "approved");
+ const proposed = found.find((skill) => skill.status === "pending_review");
+
+ let next: LearningNext;
+ if (review.error) next = { kind: "unreadable", reason: review.error.message };
+ else if (learned) next = { kind: "learned", skill: learned };
+ else if (proposed) next = { kind: "proposed", skill: proposed };
+ else if (review.data?.modelProblem)
+ next = { kind: "model", reason: review.data.modelProblem };
+ else if (off) next = { kind: "off" };
+ else if (running) next = { kind: "learning" };
+ else if (skills.error)
+ next = { kind: "unreadable", reason: skills.error.message };
+ else if (runSince?.status === "failed")
+ next = {
+ kind: "failed",
+ reason:
+ runSince.failure ??
+ "Learning stopped before it finished. Choose Learn now to try again.",
+ };
+ else if (
+ runSince?.status === "succeeded" &&
+ skills.isSuccess &&
+ !skills.isPlaceholderData
+ )
+ next = { kind: "none" };
+ else next = { kind: "waiting" };
+
+ const kind = next.kind;
+ useEffect(() => {
+ pace.current =
+ kind === "learned" || kind === "model" || kind === "off"
+ ? false
+ : kind === "learning"
+ ? 10_000
+ : 30_000;
+ }, [kind]);
+
+ useSkillNotice(stopped.title, next);
+
+ return {
+ loaded: review.isSuccess || review.isError,
+ next,
+ canReview: review.data?.canReview ?? false,
+ autoApprove: review.data?.autoApprove ?? null,
+ /** Why Learn now didn't start, in the server's sentence. */
+ learnError: learnNow.error?.message ?? null,
+ learning: running,
+ async learnNow() {
+ const from = latest?.id ?? null;
+ let any = false;
+ learnNow.reset();
+ for (const source of sources) {
+ try {
+ await learnNow.mutateAsync(source.containerId);
+ any = true;
+ } catch {
+ // Said beside the button from the mutation's own error.
+ }
+ }
+ setStarted(any ? { from } : null);
+ },
+ };
+}
+
+/** Whether this browser lets OpenBot interrupt. Some browsers throw on the mere question. */
+function notificationsAllowed() {
+ try {
+ return (
+ typeof Notification !== "undefined" &&
+ Notification.permission === "granted"
+ );
+ } catch {
+ return false;
+ }
+}
+
+/**
+ * Tells the person in a browser notification once a skill from this recording arrives while they are looking at
+ * something else (the browser they recorded in, say), as a Bot that needs them is told. Only a change seen while the
+ * page is open: a skill that was already there when it opened is on the page.
+ */
+function useSkillNotice(title: string, next: LearningNext) {
+ const seen = useRef(null);
+ const skill =
+ next.kind === "learned" || next.kind === "proposed"
+ ? `${next.kind}:${next.skill.id}`
+ : "";
+ useEffect(() => {
+ const before = seen.current;
+ seen.current = skill;
+ if (before === null || !skill || skill === before) return;
+ if (document.visibilityState !== "hidden" || !notificationsAllowed())
+ return;
+ try {
+ new Notification(
+ next.kind === "learned"
+ ? `Your Bots learned “${title}”`
+ : `A skill from “${title}” is ready to review`,
+ {
+ body:
+ next.kind === "learned"
+ ? "Open Teach a Bot to try it."
+ : "Review it in Settings, Learning.",
+ tag: `openbot-learned-${skill}`,
+ },
+ );
+ } catch {
+ // A browser that refuses the constructor still has the page.
+ }
+ }, [skill, title, next.kind]);
+}
+
+export type LearningNextState = ReturnType;
+
+/** The step's heading and sentence once a recording is saved, by what Learning has made of it. */
+export function learningNextWords(
+ learning: LearningNextState,
+ title: string,
+): { heading: string; sentence: string } {
+ const { next, canReview, autoApprove } = learning;
+ if (next.kind === "learned")
+ return {
+ heading: "Your Bots learned it",
+ sentence: `Your Bots can do “${title}” now. Try it, or ask any Bot to do it.`,
+ };
+ if (next.kind === "proposed")
+ return canReview
+ ? {
+ heading: "Your new skill is ready to review",
+ sentence: `Learning made a skill from “${title}”. Approve it in Settings, Learning, then ask any Bot to do it.`,
+ }
+ : {
+ heading: "Your new skill is waiting for an admin",
+ sentence: `Learning made a skill from “${title}”. Once an admin approves it, ask any Bot to do it.`,
+ };
+ if (next.kind === "model" || next.kind === "off")
+ return {
+ heading: `Saved “${title}”`,
+ sentence:
+ "Your recording is kept. OpenBot learns a skill from it once Learning can run.",
+ };
+ if (!canReview)
+ return {
+ heading: `Saved “${title}”`,
+ sentence:
+ "OpenBot learns a skill from it, and an admin reviews it. Once it's approved, ask any Bot to do it.",
+ };
+ return {
+ heading: `Saved “${title}”`,
+ sentence: autoApprove
+ ? "OpenBot learns a skill from it, and you'll find it in Settings, Learning. Once it's learned, ask any Bot to do it."
+ : "OpenBot learns a skill from it, and you'll see it in Settings, Learning to review. Once you approve it, ask any Bot to do it.",
+ };
+}
+
+/**
+ * The row under a saved recording on Teach a Bot that says what comes next in Learning, with the one thing to do about
+ * it: Learn now, Review, or Try it once the skill is learned.
+ */
+export function LearningNextRow({
+ learning,
+ title,
+ className,
+ more,
+}: {
+ learning: LearningNextState;
+ title: string;
+ className?: string;
+ /** Another way on beside the row's own action: See your skills, where the row is the whole of the next step. */
+ more?: ReactNode;
+}) {
+ const { next, canReview, learnError } = learning;
+ const learnNow = canReview ? (
+ void learning.learnNow()}
+ size="sm"
+ variant="outline"
+ >
+ Learn now
+
+ ) : null;
+ let icon = ;
+ let tone: "ok" | "warn" | "busy" | "plain" = "plain";
+ let heading: string;
+ let sentence: string;
+ let action: ReactNode = null;
+ switch (next.kind) {
+ case "learned":
+ tone = "ok";
+ heading = "Skill learned";
+ sentence = next.skill.description;
+ action = (
+ }>
+ Try it
+
+ );
+ break;
+ case "proposed":
+ heading = canReview ? "Ready to review" : "Waiting for an admin";
+ sentence = next.skill.description;
+ action = canReview ? (
+ }>
+ Review
+
+ ) : null;
+ break;
+ case "learning":
+ tone = "busy";
+ icon = ;
+ heading = "Learning from your recordings";
+ sentence = "This takes a few minutes. You can leave this page.";
+ break;
+ case "failed":
+ tone = "warn";
+ icon = ;
+ heading = "Learning didn't finish";
+ sentence = next.reason;
+ action = learnNow;
+ break;
+ case "none":
+ heading = "No skill from it yet";
+ sentence =
+ "Learning didn't find a skill in this recording. Record it again, and say what you're doing as you go.";
+ action = learnNow;
+ break;
+ case "model":
+ tone = "warn";
+ icon = ;
+ heading = "Learning can't run yet";
+ sentence = next.reason;
+ break;
+ case "off":
+ tone = "warn";
+ icon = ;
+ heading = "Learning is off";
+ sentence =
+ "Learning isn't available in the back end, so no skill is learned from it.";
+ break;
+ case "unreadable":
+ tone = "warn";
+ icon = ;
+ heading = "Couldn't check on Learning";
+ sentence = next.reason;
+ break;
+ case "waiting":
+ heading = "Learning runs on its own";
+ sentence = canReview
+ ? "Learn now to get the skill sooner."
+ : "An admin decides when it runs.";
+ action = learnNow;
+ break;
+ }
+ if (learnError && next.kind !== "learned" && next.kind !== "proposed") {
+ tone = "warn";
+ icon = ;
+ heading = "Learning didn't start";
+ sentence = learnError;
+ }
+ return (
+ // Announced as it changes, without being a second status beside the recording's own.
+ -
+
+ {icon}
+
+
+ {heading}
+
+ {sentence}
+
+
+ {action || more ? (
+
+ {action}
+ {more}
+
+ ) : null}
+
+ );
+}
+
+/**
+ * The next step under a saved recording in the browser drawn in the page (OpenBot Desktop, hosted OpenBot), where Teach
+ * a Bot is one page and Record lives in the browser's toolbar: under the toolbar's line that says where the recording
+ * went, what Learning does with it next, worded for who approves, and the row that follows it through Learning, with
+ * See your skills beside its action. Record another is the toolbar's Record.
+ */
+export function LearningNextNotice({ stopped }: { stopped: Stopped }) {
+ const learning = useLearningNext(stopped);
+ if (!learning.loaded) return null;
+ const { sentence } = learningNextWords(learning, stopped.title);
+ return (
+
+
+ {sentence}
+
+
}
+ >
+ See your skills
+
+ }
+ />
+
+ );
+}
diff --git a/app/src/components/browser/teach-steps.tsx b/app/src/components/browser/teach-steps.tsx
index b59f7562e8..a9b5923cf1 100644
--- a/app/src/components/browser/teach-steps.tsx
+++ b/app/src/components/browser/teach-steps.tsx
@@ -1,6 +1,7 @@
import type { BrowserApiState } from "@openbot/shared/openbot-browser";
import { IconArrowLeft, IconPlayerRecordFilled } from "@tabler/icons-react";
import { useMutation } from "@tanstack/react-query";
+import { Link } from "@tanstack/react-router";
import { type ReactNode, useEffect, useState } from "react";
import { PageRows } from "@/components/layout/page-shell";
import { Button } from "@/components/ui/button";
@@ -10,9 +11,21 @@ import { Separator } from "@/components/ui/separator";
import { openBrowserMutationOptions } from "@/lib/browser/mutations";
import { cn } from "@/lib/utils";
import { queryClient } from "@/query-client";
+import {
+ LearningNextRow,
+ learningNextWords,
+ savedToLearning,
+ useLearningNext,
+ YOUR_SKILLS,
+} from "./learning-next";
import { NativeNarrationRow } from "./narration";
import { NarrationChoiceDialog, useNarrationChoice } from "./narration-choice";
-import { RecordingRow, StoppedRow, useRecording } from "./recording";
+import {
+ RecordingRow,
+ type Stopped,
+ StoppedRow,
+ useRecording,
+} from "./recording";
/** Kept once the person has saved a recording: from then on the page opens on naming the task, past the introduction. */
export const INTRODUCED_KEY = "openbot.teach-a-bot.introduced";
@@ -41,7 +54,7 @@ function rememberIntroduced() {
* Teach a Bot where the person's browser is a window on their desktop (a host-side computer of people's browsers), as three steps on one stage:
* what it does, naming the task (with narration), and opening the browser already recording. While a recording runs the
* third step is the recording itself, with Show browser and Stop; once it stops it says what became of it, with Record
- * another. The introduction shows until the person has saved a recording, and Back always reaches it.
+ * another, and once it is saved, what Learning does with it next (`SavedStep`). The introduction shows until the person has saved a recording, and Back always reaches it.
*/
export function TeachSteps({ state }: { state: BrowserApiState }) {
const choice = useNarrationChoice();
@@ -181,6 +194,8 @@ export function TeachSteps({ state }: { state: BrowserApiState }) {