From cb02ca2e1b283af0c42e10fe7c0a15a73083d812 Mon Sep 17 00:00:00 2001 From: Tim Holm Date: Tue, 6 Oct 2026 14:10:42 +1100 Subject: [PATCH] feat: let agents ask for software, which their pod keeps for its sandbox request_software asks for a package from nixpkgs, stable or unstable, and the turn waits for whoever manages the pod's sandbox to allow it. Allowed, it is installed into the pod's shared Nix profile, on every command's PATH, and recorded at the nixpkgs commit it came from; each turn's first use of the sandbox gives it exactly the pod's recorded packages, so a new or upgraded sandbox gets them back. The pod's settings list its software, which its admins can remove. --- docker/sandbox/Dockerfile | 4 + docker/sandbox/nix/software-path.sh | 6 + .../contracts/src/http/groups/pod-sandbox.ts | 16 +- packages/contracts/src/sandbox-providers.ts | 33 + .../migration.sql | 14 + .../snapshot.json | 9178 +++++++++++++++++ .../conversations/tools/approval-deciders.ts | 9 +- .../src/conversations/tools/sandbox.test.ts | 31 + .../core/src/conversations/tools/sandbox.ts | 30 +- .../src/conversations/tools/sandbox/tools.ts | 5 +- .../conversations/tools/software/profile.ts | 84 + .../src/conversations/tools/software/tool.ts | 78 + .../src/sandboxes/sandbox-software.test.ts | 115 + .../core/src/sandboxes/sandbox-software.ts | 201 + packages/core/src/sandboxes/sql.ts | 31 + packages/server/src/http/app.test-support.ts | 2 + packages/server/src/http/services.ts | 2 + packages/server/src/http/session.test.ts | 2 + packages/server/src/index.ts | 2 + .../server/src/routes/pod-sandbox/routes.ts | 10 + packages/web/src/lib/pod-sandbox.ts | 19 + packages/web/src/lib/tool-names.ts | 3 + .../screens/PodSandboxSettings.stories.tsx | 5 +- .../web/src/screens/PodSandboxSettings.tsx | 10 +- .../screens/PodSandboxSoftware.stories.tsx | 68 + .../web/src/screens/PodSandboxSoftware.tsx | 85 + .../src/screens/ToolApprovalCard.stories.tsx | 17 + packages/web/src/screens/ToolApprovalCard.tsx | 28 +- .../web/src/screens/WorkspacePodsSettings.tsx | 2 +- 29 files changed, 10066 insertions(+), 24 deletions(-) create mode 100644 docker/sandbox/nix/software-path.sh create mode 100644 packages/core/drizzle/20261007024213_sandbox_software/migration.sql create mode 100644 packages/core/drizzle/20261007024213_sandbox_software/snapshot.json create mode 100644 packages/core/src/conversations/tools/software/profile.ts create mode 100644 packages/core/src/conversations/tools/software/tool.ts create mode 100644 packages/core/src/sandboxes/sandbox-software.test.ts create mode 100644 packages/core/src/sandboxes/sandbox-software.ts create mode 100644 packages/web/src/screens/PodSandboxSoftware.stories.tsx create mode 100644 packages/web/src/screens/PodSandboxSoftware.tsx diff --git a/docker/sandbox/Dockerfile b/docker/sandbox/Dockerfile index fb60bd5b..a8f5ff20 100644 --- a/docker/sandbox/Dockerfile +++ b/docker/sandbox/Dockerfile @@ -82,6 +82,10 @@ COPY nix/nix.conf nix/registry.json /etc/nix/ # Nix looks for its state here, and without it falls back to a store in the # user's home that needs namespaces the agents' user can't make. RUN mkdir -p /nix/store /nix/var/nix && chown -R 1000:1000 /nix +# The pod's software, on the PATH of login shells, as agents' commands run, +# and of interactive ones, as in the dock's terminal. +COPY nix/software-path.sh /etc/profile.d/sugabots-software.sh +RUN echo '. /etc/profile.d/sugabots-software.sh' >> /etc/bash.bashrc COPY sugabots-desktop /usr/local/bin/sugabots-desktop diff --git a/docker/sandbox/nix/software-path.sh b/docker/sandbox/nix/software-path.sh new file mode 100644 index 00000000..9f079b9a --- /dev/null +++ b/docker/sandbox/nix/software-path.sh @@ -0,0 +1,6 @@ +# The pod's software, which Sugabots installs into this Nix profile and keeps +# on every sandbox the pod has, ahead of the image's own. +case ":$PATH:" in +*:/nix/var/nix/profiles/sugabots/bin:*) ;; +*) PATH="/nix/var/nix/profiles/sugabots/bin:$PATH" ;; +esac diff --git a/packages/contracts/src/http/groups/pod-sandbox.ts b/packages/contracts/src/http/groups/pod-sandbox.ts index eff8124f..7b927fc5 100644 --- a/packages/contracts/src/http/groups/pod-sandbox.ts +++ b/packages/contracts/src/http/groups/pod-sandbox.ts @@ -3,7 +3,10 @@ import { newSandboxHostSchema, podSandboxNetworkSchema, podSandboxSchema, + podSandboxSoftwareSchema, sandboxHostSchema, + sandboxPackageNameSchema, + sandboxSoftwareChannelSchema, } from "../../sandbox-providers.ts"; import { uuidSchema } from "../../uuid.ts"; import { BadRequest, Conflict, refused } from "../errors.ts"; @@ -14,7 +17,8 @@ const params = { podId: uuidSchema }; /** * A pod's sandbox: how it stands, starting it afresh or moving its work to the - * current image, and the hosts it may reach beyond what the workspace allows. + * current image, the hosts it may reach beyond what the workspace allows, and + * the software it has beyond its image. */ export class PodSandboxApi extends HttpApiGroup.make("podSandbox") .add( @@ -45,5 +49,15 @@ export class PodSandboxApi extends HttpApiGroup.make("podSandbox") success: podSandboxNetworkSchema, error: refused, }), + HttpApiEndpoint.get("software", `${root}/software`, { + params, + success: podSandboxSoftwareSchema, + error: refused, + }), + HttpApiEndpoint.delete("removePackage", `${root}/software/:channel/:name`, { + params: { ...params, channel: sandboxSoftwareChannelSchema, name: sandboxPackageNameSchema }, + success: podSandboxSoftwareSchema, + error: refused, + }), ) .middleware(Session) {} diff --git a/packages/contracts/src/sandbox-providers.ts b/packages/contracts/src/sandbox-providers.ts index 0d5c12c2..9abe62d6 100644 --- a/packages/contracts/src/sandbox-providers.ts +++ b/packages/contracts/src/sandbox-providers.ts @@ -244,3 +244,36 @@ export const podSandboxNetworkSchema = Schema.Struct({ export type PodSandboxNetwork = typeof podSandboxNetworkSchema.Type; export const newSandboxHostSchema = Schema.Struct({ host: sandboxHostSchema }); + +/** Which nixpkgs a package comes from: a NixOS release, or unstable for newer versions. */ +export const sandboxSoftwareChannelSchema = Schema.Literals(["stable", "unstable"]); + +export type SandboxSoftwareChannel = typeof sandboxSoftwareChannelSchema.Type; + +/** + * A package's attribute in nixpkgs, such as `ffmpeg` or + * `python3Packages.pandas`: names joined by dots, of letters, digits, `_`, + * `-` and `+`. + */ +export const sandboxPackageNameSchema = Schema.String.check( + Schema.isMaxLength(200), + Schema.isPattern(/^[A-Za-z_][A-Za-z0-9_+-]*(?:\.[A-Za-z_][A-Za-z0-9_+-]*)*$/, { + message: "Enter a nixpkgs package name, such as ffmpeg or python3Packages.pandas", + }), +); + +/** Software a pod's sandbox has, each package at the nixpkgs commit it was installed from. */ +export const podSandboxSoftwareSchema = Schema.Struct({ + packages: Schema.Array( + Schema.Struct({ + name: sandboxPackageNameSchema, + channel: sandboxSoftwareChannelSchema, + nixpkgsRev: Schema.String, + /** Who allowed the agent's request for it; null once they've left. */ + addedByName: Schema.NullOr(Schema.String), + addedAt: isoTimestampSchema, + }), + ), +}); + +export type PodSandboxSoftware = typeof podSandboxSoftwareSchema.Type; diff --git a/packages/core/drizzle/20261007024213_sandbox_software/migration.sql b/packages/core/drizzle/20261007024213_sandbox_software/migration.sql new file mode 100644 index 00000000..871be1ab --- /dev/null +++ b/packages/core/drizzle/20261007024213_sandbox_software/migration.sql @@ -0,0 +1,14 @@ +CREATE TABLE "sandbox_pod_package" ( + "workspace_id" uuid NOT NULL, + "pod_id" uuid, + "name" text, + "channel" text, + "nixpkgs_rev" text NOT NULL, + "added_by_id" uuid, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "sandbox_pod_package_pkey" PRIMARY KEY("pod_id","channel","name"), + CONSTRAINT "sandbox_pod_package_channel_check" CHECK ("channel" in ('stable', 'unstable')) +); +--> statement-breakpoint +ALTER TABLE "sandbox_pod_package" ADD CONSTRAINT "sandbox_pod_package_added_by_id_user_id_fkey" FOREIGN KEY ("added_by_id") REFERENCES "user"("id") ON DELETE SET NULL;--> statement-breakpoint +ALTER TABLE "sandbox_pod_package" ADD CONSTRAINT "sandbox_pod_package_pod_workspace_fkey" FOREIGN KEY ("pod_id","workspace_id") REFERENCES "pod"("id","workspace_id") ON DELETE CASCADE; \ No newline at end of file diff --git a/packages/core/drizzle/20261007024213_sandbox_software/snapshot.json b/packages/core/drizzle/20261007024213_sandbox_software/snapshot.json new file mode 100644 index 00000000..70439ec2 --- /dev/null +++ b/packages/core/drizzle/20261007024213_sandbox_software/snapshot.json @@ -0,0 +1,9178 @@ +{ + "version": "8", + "dialect": "postgres", + "id": "6decb71f-0bdb-4c5b-a05a-b9879f1df1ae", + "prevIds": [ + "c8106c62-e3b3-404a-842f-818d9ed80c29" + ], + "ddl": [ + { + "isRlsEnabled": false, + "name": "model_request", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "chat", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "collaboration", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "message", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "routine", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "routine_execution", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "thread", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "thread_compaction", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "thread_participant", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "thread_read", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "thread_summary", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "tool_call", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "turn", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "event", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "connection", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "model_provider", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "provider_model", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "search_provider", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "workspace_default_model", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "sandbox", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "sandbox_allowed_host", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "sandbox_blocked_host", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "sandbox_lease", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "sandbox_pod_allowed_host", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "sandbox_pod_package", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "sandbox_provider", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "lane", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "lane_request", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "account", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "agent", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "pod", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "pod_member", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "session", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "user", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "verification", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "workspace", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "workspace_invite", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "workspace_member", + "entityType": "tables", + "schema": "public" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "model_request" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "workspace_id", + "entityType": "columns", + "schema": "public", + "table": "model_request" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "purpose", + "entityType": "columns", + "schema": "public", + "table": "model_request" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "pod_id", + "entityType": "columns", + "schema": "public", + "table": "model_request" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "agent_id", + "entityType": "columns", + "schema": "public", + "table": "model_request" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thread_id", + "entityType": "columns", + "schema": "public", + "table": "model_request" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "turn_id", + "entityType": "columns", + "schema": "public", + "table": "model_request" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "step", + "entityType": "columns", + "schema": "public", + "table": "model_request" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "provider_id", + "entityType": "columns", + "schema": "public", + "table": "model_request" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "preset", + "entityType": "columns", + "schema": "public", + "table": "model_request" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "model", + "entityType": "columns", + "schema": "public", + "table": "model_request" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "outcome", + "entityType": "columns", + "schema": "public", + "table": "model_request" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "input_tokens", + "entityType": "columns", + "schema": "public", + "table": "model_request" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "cache_read_tokens", + "entityType": "columns", + "schema": "public", + "table": "model_request" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "cache_write_tokens", + "entityType": "columns", + "schema": "public", + "table": "model_request" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "output_tokens", + "entityType": "columns", + "schema": "public", + "table": "model_request" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "reasoning_tokens", + "entityType": "columns", + "schema": "public", + "table": "model_request" + }, + { + "type": "numeric(18,10)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "cost_usd", + "entityType": "columns", + "schema": "public", + "table": "model_request" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "cost_source", + "entityType": "columns", + "schema": "public", + "table": "model_request" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "started_at", + "entityType": "columns", + "schema": "public", + "table": "model_request" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ended_at", + "entityType": "columns", + "schema": "public", + "table": "model_request" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "chat" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "workspace_id", + "entityType": "columns", + "schema": "public", + "table": "chat" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "pod_id", + "entityType": "columns", + "schema": "public", + "table": "chat" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "host_agent_id", + "entityType": "columns", + "schema": "public", + "table": "chat" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "main_thread_id", + "entityType": "columns", + "schema": "public", + "table": "chat" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "initiator_user_id", + "entityType": "columns", + "schema": "public", + "table": "chat" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "chat" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "chat" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "collaboration" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "parent_thread_id", + "entityType": "columns", + "schema": "public", + "table": "collaboration" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "parent_message_id", + "entityType": "columns", + "schema": "public", + "table": "collaboration" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "turn_id", + "entityType": "columns", + "schema": "public", + "table": "collaboration" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "child_thread_id", + "entityType": "columns", + "schema": "public", + "table": "collaboration" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "collaborator_agent_id", + "entityType": "columns", + "schema": "public", + "table": "collaboration" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "brief", + "entityType": "columns", + "schema": "public", + "table": "collaboration" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'waiting'", + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "collaboration" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "answer", + "entityType": "columns", + "schema": "public", + "table": "collaboration" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "at_offset", + "entityType": "columns", + "schema": "public", + "table": "collaboration" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "collaboration" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "collaboration" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "message" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thread_id", + "entityType": "columns", + "schema": "public", + "table": "message" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "author_user_id", + "entityType": "columns", + "schema": "public", + "table": "message" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "author_agent_id", + "entityType": "columns", + "schema": "public", + "table": "message" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "routine_trigger", + "entityType": "columns", + "schema": "public", + "table": "message" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "kind", + "entityType": "columns", + "schema": "public", + "table": "message" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "message" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "parts", + "entityType": "columns", + "schema": "public", + "table": "message" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "content", + "entityType": "columns", + "schema": "public", + "table": "message" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'[]'", + "generated": null, + "identity": null, + "name": "mentions", + "entityType": "columns", + "schema": "public", + "table": "message" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "turn_id", + "entityType": "columns", + "schema": "public", + "table": "message" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "routine_execution_id", + "entityType": "columns", + "schema": "public", + "table": "message" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "message" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "routine" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "workspace_id", + "entityType": "columns", + "schema": "public", + "table": "routine" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "agent_id", + "entityType": "columns", + "schema": "public", + "table": "routine" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "routine" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "instructions", + "entityType": "columns", + "schema": "public", + "table": "routine" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "trigger_kind", + "entityType": "columns", + "schema": "public", + "table": "routine" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "cron_expression", + "entityType": "columns", + "schema": "public", + "table": "routine" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "cron_timezone", + "entityType": "columns", + "schema": "public", + "table": "routine" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "next_scheduled_at", + "entityType": "columns", + "schema": "public", + "table": "routine" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "webhook_secret_digest", + "entityType": "columns", + "schema": "public", + "table": "routine" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'enabled'", + "generated": null, + "identity": null, + "name": "state", + "entityType": "columns", + "schema": "public", + "table": "routine" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'keep_in_run'", + "generated": null, + "identity": null, + "name": "results", + "entityType": "columns", + "schema": "public", + "table": "routine" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by_id", + "entityType": "columns", + "schema": "public", + "table": "routine" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "deleted_at", + "entityType": "columns", + "schema": "public", + "table": "routine" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "routine" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "routine" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "routine_execution" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "routine_id", + "entityType": "columns", + "schema": "public", + "table": "routine_execution" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "workspace_id", + "entityType": "columns", + "schema": "public", + "table": "routine_execution" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "agent_id", + "entityType": "columns", + "schema": "public", + "table": "routine_execution" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thread_id", + "entityType": "columns", + "schema": "public", + "table": "routine_execution" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "trigger_kind", + "entityType": "columns", + "schema": "public", + "table": "routine_execution" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "trigger_identity", + "entityType": "columns", + "schema": "public", + "table": "routine_execution" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "trigger", + "entityType": "columns", + "schema": "public", + "table": "routine_execution" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "routine_name", + "entityType": "columns", + "schema": "public", + "table": "routine_execution" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "instructions", + "entityType": "columns", + "schema": "public", + "table": "routine_execution" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'keep_in_run'", + "generated": null, + "identity": null, + "name": "results", + "entityType": "columns", + "schema": "public", + "table": "routine_execution" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'queued'", + "generated": null, + "identity": null, + "name": "state", + "entityType": "columns", + "schema": "public", + "table": "routine_execution" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "error", + "entityType": "columns", + "schema": "public", + "table": "routine_execution" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "pending_terminal_state", + "entityType": "columns", + "schema": "public", + "table": "routine_execution" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "pending_terminal_error", + "entityType": "columns", + "schema": "public", + "table": "routine_execution" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "accepted_at", + "entityType": "columns", + "schema": "public", + "table": "routine_execution" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "started_at", + "entityType": "columns", + "schema": "public", + "table": "routine_execution" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "finished_at", + "entityType": "columns", + "schema": "public", + "table": "routine_execution" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "routine_execution" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "routine_execution" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "thread" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "workspace_id", + "entityType": "columns", + "schema": "public", + "table": "thread" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "pod_id", + "entityType": "columns", + "schema": "public", + "table": "thread" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "host_agent_id", + "entityType": "columns", + "schema": "public", + "table": "thread" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "chat_id", + "entityType": "columns", + "schema": "public", + "table": "thread" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "type", + "entityType": "columns", + "schema": "public", + "table": "thread" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "title", + "entityType": "columns", + "schema": "public", + "table": "thread" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "system_agent_key", + "entityType": "columns", + "schema": "public", + "table": "thread" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "parent_thread_id", + "entityType": "columns", + "schema": "public", + "table": "thread" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "initiator_user_id", + "entityType": "columns", + "schema": "public", + "table": "thread" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "thread" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "thread" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thread_id", + "entityType": "columns", + "schema": "public", + "table": "thread_compaction" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "summary", + "entityType": "columns", + "schema": "public", + "table": "thread_compaction" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "history_starts_at", + "entityType": "columns", + "schema": "public", + "table": "thread_compaction" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "kept_from", + "entityType": "columns", + "schema": "public", + "table": "thread_compaction" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "thread_compaction" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "thread_participant" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thread_id", + "entityType": "columns", + "schema": "public", + "table": "thread_participant" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "thread_participant" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "agent_id", + "entityType": "columns", + "schema": "public", + "table": "thread_participant" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "thread_participant" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "thread_read" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thread_id", + "entityType": "columns", + "schema": "public", + "table": "thread_read" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "thread_read" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "read_through", + "entityType": "columns", + "schema": "public", + "table": "thread_read" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "thread_read" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "thread_read" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thread_id", + "entityType": "columns", + "schema": "public", + "table": "thread_summary" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "content", + "entityType": "columns", + "schema": "public", + "table": "thread_summary" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "source_message_id", + "entityType": "columns", + "schema": "public", + "table": "thread_summary" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "thread_summary" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thread_id", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "message_id", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "turn_id", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "tool", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "sdk_tool_call_id", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "approval_id", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "approval_status", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "approval_reason", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "decided_by_id", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "decided_at", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "connection_id", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "connection_revision", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "remote_tool_name", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "execution_input", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "input", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "output", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'running'", + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "error", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "mutating", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "at_offset", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "started_at", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "finished_at", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "tool_call" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "turn" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thread_id", + "entityType": "columns", + "schema": "public", + "table": "turn" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "agent_id", + "entityType": "columns", + "schema": "public", + "table": "turn" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "trigger_message_id", + "entityType": "columns", + "schema": "public", + "table": "turn" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "owner", + "entityType": "columns", + "schema": "public", + "table": "turn" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "turn" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "model", + "entityType": "columns", + "schema": "public", + "table": "turn" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "context_tokens", + "entityType": "columns", + "schema": "public", + "table": "turn" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "context_capacity", + "entityType": "columns", + "schema": "public", + "table": "turn" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "cancel_requested", + "entityType": "columns", + "schema": "public", + "table": "turn" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "runs", + "entityType": "columns", + "schema": "public", + "table": "turn" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "mutation_started", + "entityType": "columns", + "schema": "public", + "table": "turn" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "checkpoint", + "entityType": "columns", + "schema": "public", + "table": "turn" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "reason", + "entityType": "columns", + "schema": "public", + "table": "turn" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "error", + "entityType": "columns", + "schema": "public", + "table": "turn" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "started_at", + "entityType": "columns", + "schema": "public", + "table": "turn" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "finished_at", + "entityType": "columns", + "schema": "public", + "table": "turn" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "turn" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "turn" + }, + { + "type": "bigserial", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "seq", + "entityType": "columns", + "schema": "public", + "table": "event" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "channel", + "entityType": "columns", + "schema": "public", + "table": "event" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "type", + "entityType": "columns", + "schema": "public", + "table": "event" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "payload", + "entityType": "columns", + "schema": "public", + "table": "event" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "event" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "connection" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "workspace_id", + "entityType": "columns", + "schema": "public", + "table": "connection" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "pod_id", + "entityType": "columns", + "schema": "public", + "table": "connection" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "connection" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "handle", + "entityType": "columns", + "schema": "public", + "table": "connection" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "url", + "entityType": "columns", + "schema": "public", + "table": "connection" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'header'", + "generated": null, + "identity": null, + "name": "auth_kind", + "entityType": "columns", + "schema": "public", + "table": "connection" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "secret_header", + "entityType": "columns", + "schema": "public", + "table": "connection" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "secret_encrypted", + "entityType": "columns", + "schema": "public", + "table": "connection" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "oauth_encrypted", + "entityType": "columns", + "schema": "public", + "table": "connection" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "oauth_state", + "entityType": "columns", + "schema": "public", + "table": "connection" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "1", + "generated": null, + "identity": null, + "name": "configuration_revision", + "entityType": "columns", + "schema": "public", + "table": "connection" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'[]'", + "generated": null, + "identity": null, + "name": "tools", + "entityType": "columns", + "schema": "public", + "table": "connection" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'{}'", + "generated": null, + "identity": null, + "name": "tool_access", + "entityType": "columns", + "schema": "public", + "table": "connection" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_tested_at", + "entityType": "columns", + "schema": "public", + "table": "connection" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_test_error", + "entityType": "columns", + "schema": "public", + "table": "connection" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by_id", + "entityType": "columns", + "schema": "public", + "table": "connection" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "connection" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "connection" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "model_provider" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "workspace_id", + "entityType": "columns", + "schema": "public", + "table": "model_provider" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "preset", + "entityType": "columns", + "schema": "public", + "table": "model_provider" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "model_provider" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "base_url", + "entityType": "columns", + "schema": "public", + "table": "model_provider" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "api_format", + "entityType": "columns", + "schema": "public", + "table": "model_provider" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "active", + "entityType": "columns", + "schema": "public", + "table": "model_provider" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "api_key_encrypted", + "entityType": "columns", + "schema": "public", + "table": "model_provider" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'[]'", + "generated": null, + "identity": null, + "name": "custom_headers_encrypted", + "entityType": "columns", + "schema": "public", + "table": "model_provider" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "oauth_tokens_encrypted", + "entityType": "columns", + "schema": "public", + "table": "model_provider" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_tested_at", + "entityType": "columns", + "schema": "public", + "table": "model_provider" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_test_error", + "entityType": "columns", + "schema": "public", + "table": "model_provider" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by_id", + "entityType": "columns", + "schema": "public", + "table": "model_provider" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "model_provider" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "model_provider" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "provider_model" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "workspace_id", + "entityType": "columns", + "schema": "public", + "table": "provider_model" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "provider_id", + "entityType": "columns", + "schema": "public", + "table": "provider_model" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "model_id", + "entityType": "columns", + "schema": "public", + "table": "provider_model" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "display_name", + "entityType": "columns", + "schema": "public", + "table": "provider_model" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'[]'", + "generated": null, + "identity": null, + "name": "capabilities", + "entityType": "columns", + "schema": "public", + "table": "provider_model" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'[]'", + "generated": null, + "identity": null, + "name": "disabled_capabilities", + "entityType": "columns", + "schema": "public", + "table": "provider_model" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "context_length", + "entityType": "columns", + "schema": "public", + "table": "provider_model" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "enabled", + "entityType": "columns", + "schema": "public", + "table": "provider_model" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "source", + "entityType": "columns", + "schema": "public", + "table": "provider_model" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "provider_model" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "provider_model" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "search_provider" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "workspace_id", + "entityType": "columns", + "schema": "public", + "table": "search_provider" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "preset", + "entityType": "columns", + "schema": "public", + "table": "search_provider" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "base_url", + "entityType": "columns", + "schema": "public", + "table": "search_provider" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "enabled", + "entityType": "columns", + "schema": "public", + "table": "search_provider" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "api_key_encrypted", + "entityType": "columns", + "schema": "public", + "table": "search_provider" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_tested_at", + "entityType": "columns", + "schema": "public", + "table": "search_provider" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_test_error", + "entityType": "columns", + "schema": "public", + "table": "search_provider" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by_id", + "entityType": "columns", + "schema": "public", + "table": "search_provider" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "search_provider" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "search_provider" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "workspace_id", + "entityType": "columns", + "schema": "public", + "table": "workspace_default_model" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "model_id", + "entityType": "columns", + "schema": "public", + "table": "workspace_default_model" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "workspace_default_model" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "sandbox" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "workspace_id", + "entityType": "columns", + "schema": "public", + "table": "sandbox" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "pod_id", + "entityType": "columns", + "schema": "public", + "table": "sandbox" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "sandbox_provider_id", + "entityType": "columns", + "schema": "public", + "table": "sandbox" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "provider_sandbox_id", + "entityType": "columns", + "schema": "public", + "table": "sandbox" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "paused_at", + "entityType": "columns", + "schema": "public", + "table": "sandbox" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "last_used_at", + "entityType": "columns", + "schema": "public", + "table": "sandbox" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "sandbox" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "workspace_id", + "entityType": "columns", + "schema": "public", + "table": "sandbox_allowed_host" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "host", + "entityType": "columns", + "schema": "public", + "table": "sandbox_allowed_host" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "added_by_id", + "entityType": "columns", + "schema": "public", + "table": "sandbox_allowed_host" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "sandbox_allowed_host" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "workspace_id", + "entityType": "columns", + "schema": "public", + "table": "sandbox_blocked_host" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "host", + "entityType": "columns", + "schema": "public", + "table": "sandbox_blocked_host" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "blocked_by_id", + "entityType": "columns", + "schema": "public", + "table": "sandbox_blocked_host" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "sandbox_blocked_host" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "sandbox_id", + "entityType": "columns", + "schema": "public", + "table": "sandbox_lease" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "holder", + "entityType": "columns", + "schema": "public", + "table": "sandbox_lease" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "sandbox_lease" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "workspace_id", + "entityType": "columns", + "schema": "public", + "table": "sandbox_pod_allowed_host" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "pod_id", + "entityType": "columns", + "schema": "public", + "table": "sandbox_pod_allowed_host" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "host", + "entityType": "columns", + "schema": "public", + "table": "sandbox_pod_allowed_host" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "added_by_id", + "entityType": "columns", + "schema": "public", + "table": "sandbox_pod_allowed_host" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "sandbox_pod_allowed_host" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "workspace_id", + "entityType": "columns", + "schema": "public", + "table": "sandbox_pod_package" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "pod_id", + "entityType": "columns", + "schema": "public", + "table": "sandbox_pod_package" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "sandbox_pod_package" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "channel", + "entityType": "columns", + "schema": "public", + "table": "sandbox_pod_package" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "nixpkgs_rev", + "entityType": "columns", + "schema": "public", + "table": "sandbox_pod_package" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "added_by_id", + "entityType": "columns", + "schema": "public", + "table": "sandbox_pod_package" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "sandbox_pod_package" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "sandbox_provider" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "workspace_id", + "entityType": "columns", + "schema": "public", + "table": "sandbox_provider" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "preset", + "entityType": "columns", + "schema": "public", + "table": "sandbox_provider" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "base_url", + "entityType": "columns", + "schema": "public", + "table": "sandbox_provider" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "sandbox_url", + "entityType": "columns", + "schema": "public", + "table": "sandbox_provider" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "image", + "entityType": "columns", + "schema": "public", + "table": "sandbox_provider" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "enabled", + "entityType": "columns", + "schema": "public", + "table": "sandbox_provider" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "api_key_encrypted", + "entityType": "columns", + "schema": "public", + "table": "sandbox_provider" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "template_build", + "entityType": "columns", + "schema": "public", + "table": "sandbox_provider" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_tested_at", + "entityType": "columns", + "schema": "public", + "table": "sandbox_provider" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_test_error", + "entityType": "columns", + "schema": "public", + "table": "sandbox_provider" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by_id", + "entityType": "columns", + "schema": "public", + "table": "sandbox_provider" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "sandbox_provider" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "sandbox_provider" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "key", + "entityType": "columns", + "schema": "public", + "table": "lane" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "subject", + "entityType": "columns", + "schema": "public", + "table": "lane" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'idle'", + "generated": null, + "identity": null, + "name": "state", + "entityType": "columns", + "schema": "public", + "table": "lane" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "workflow", + "entityType": "columns", + "schema": "public", + "table": "lane" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "execution_id", + "entityType": "columns", + "schema": "public", + "table": "lane" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "payload", + "entityType": "columns", + "schema": "public", + "table": "lane" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "lane" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "lane_request" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "lane_key", + "entityType": "columns", + "schema": "public", + "table": "lane_request" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "workflow", + "entityType": "columns", + "schema": "public", + "table": "lane_request" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "payload", + "entityType": "columns", + "schema": "public", + "table": "lane_request" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "lane_request" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "account_id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "provider_id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "password", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "access_token", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refresh_token", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id_token", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "access_token_expires_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refresh_token_expires_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "scope", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "agent" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "workspace_id", + "entityType": "columns", + "schema": "public", + "table": "agent" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "pod_id", + "entityType": "columns", + "schema": "public", + "table": "agent" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "agent" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "handle", + "entityType": "columns", + "schema": "public", + "table": "agent" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "system_agent_key", + "entityType": "columns", + "schema": "public", + "table": "agent" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "provisioned_key", + "entityType": "columns", + "schema": "public", + "table": "agent" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "description", + "entityType": "columns", + "schema": "public", + "table": "agent" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "color", + "entityType": "columns", + "schema": "public", + "table": "agent" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "face", + "entityType": "columns", + "schema": "public", + "table": "agent" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "model", + "entityType": "columns", + "schema": "public", + "table": "agent" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "''", + "generated": null, + "identity": null, + "name": "prompt", + "entityType": "columns", + "schema": "public", + "table": "agent" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'[]'", + "generated": null, + "identity": null, + "name": "disabled_tools", + "entityType": "columns", + "schema": "public", + "table": "agent" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "uses_sandbox", + "entityType": "columns", + "schema": "public", + "table": "agent" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by_id", + "entityType": "columns", + "schema": "public", + "table": "agent" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "agent" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "agent" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "pod" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "workspace_id", + "entityType": "columns", + "schema": "public", + "table": "pod" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "owner_id", + "entityType": "columns", + "schema": "public", + "table": "pod" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "kind", + "entityType": "columns", + "schema": "public", + "table": "pod" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "pod" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "pod" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "color", + "entityType": "columns", + "schema": "public", + "table": "pod" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'{\"facilitator\":false}'", + "generated": null, + "identity": null, + "name": "routing", + "entityType": "columns", + "schema": "public", + "table": "pod" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by_id", + "entityType": "columns", + "schema": "public", + "table": "pod" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "pod" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "pod" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "pod_member" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "workspace_id", + "entityType": "columns", + "schema": "public", + "table": "pod_member" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "pod_id", + "entityType": "columns", + "schema": "public", + "table": "pod_member" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "pod_member" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "pod_member" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "token", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ip_address", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_agent", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "email", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "email_verified", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "image", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "onboarding_completed_at", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "referred_by", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "referral_code", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "identifier", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "value", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "workspace" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "workspace" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "workspace" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "logo", + "entityType": "columns", + "schema": "public", + "table": "workspace" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'UTC'", + "generated": null, + "identity": null, + "name": "time_zone", + "entityType": "columns", + "schema": "public", + "table": "workspace" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "workspace" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "workspace" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "workspace_invite" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "workspace_id", + "entityType": "columns", + "schema": "public", + "table": "workspace_invite" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "email", + "entityType": "columns", + "schema": "public", + "table": "workspace_invite" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'member'", + "generated": null, + "identity": null, + "name": "role", + "entityType": "columns", + "schema": "public", + "table": "workspace_invite" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'pending'", + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "workspace_invite" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "inviter_id", + "entityType": "columns", + "schema": "public", + "table": "workspace_invite" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "workspace_invite" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "workspace_invite" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "uuidv7()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "workspace_member" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "workspace_id", + "entityType": "columns", + "schema": "public", + "table": "workspace_member" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "workspace_member" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'member'", + "generated": null, + "identity": null, + "name": "role", + "entityType": "columns", + "schema": "public", + "table": "workspace_member" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "workspace_member" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "workspace_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "started_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "model_request_workspace_started_at_idx", + "entityType": "indexes", + "schema": "public", + "table": "model_request" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "thread_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": "\"thread_id\" is not null", + "with": "", + "method": "btree", + "concurrently": false, + "name": "model_request_thread_idx", + "entityType": "indexes", + "schema": "public", + "table": "model_request" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "pod_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "host_agent_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "chat_pod_host_idx", + "entityType": "indexes", + "schema": "public", + "table": "chat" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "parent_message_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "collaboration_parent_message_idx", + "entityType": "indexes", + "schema": "public", + "table": "collaboration" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "child_thread_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "collaboration_child_thread_idx", + "entityType": "indexes", + "schema": "public", + "table": "collaboration" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "collaborator_agent_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "created_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "collaboration_collaborator_created_at_idx", + "entityType": "indexes", + "schema": "public", + "table": "collaboration" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "turn_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "collaboration_turn_idx", + "entityType": "indexes", + "schema": "public", + "table": "collaboration" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "thread_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "created_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "message_thread_created_at_idx", + "entityType": "indexes", + "schema": "public", + "table": "message" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "routine_execution_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": "\"routine_execution_id\" is not null", + "with": "", + "method": "btree", + "concurrently": false, + "name": "message_routine_execution_idx", + "entityType": "indexes", + "schema": "public", + "table": "message" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "to_tsvector('english', \"content\")", + "isExpression": true, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": "\"status\" = 'complete'", + "with": "", + "method": "gin", + "concurrently": false, + "name": "message_content_search_idx", + "entityType": "indexes", + "schema": "public", + "table": "message" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "to_tsvector('simple', \"content\")", + "isExpression": true, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": "\"status\" = 'complete'", + "with": "", + "method": "gin", + "concurrently": false, + "name": "message_content_simple_search_idx", + "entityType": "indexes", + "schema": "public", + "table": "message" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "content", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": { + "name": "gin_trgm_ops", + "default": false + } + } + ], + "isUnique": false, + "where": "\"status\" = 'complete'", + "with": "", + "method": "gin", + "concurrently": false, + "name": "message_content_trigram_idx", + "entityType": "indexes", + "schema": "public", + "table": "message" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "agent_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "name", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": "\"deleted_at\" is null", + "with": "", + "method": "btree", + "concurrently": false, + "name": "routine_agent_name_idx", + "entityType": "indexes", + "schema": "public", + "table": "routine" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "workspace_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "routine_id_workspace_id_idx", + "entityType": "indexes", + "schema": "public", + "table": "routine" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "next_scheduled_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": "\"deleted_at\" is null and \"state\" = 'enabled' and \"trigger_kind\" = 'cron'", + "with": "", + "method": "btree", + "concurrently": false, + "name": "routine_due_idx", + "entityType": "indexes", + "schema": "public", + "table": "routine" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "thread_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "routine_execution_thread_idx", + "entityType": "indexes", + "schema": "public", + "table": "routine_execution" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "routine_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "trigger_kind", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "trigger_identity", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": "\"trigger_identity\" is not null", + "with": "", + "method": "btree", + "concurrently": false, + "name": "routine_execution_trigger_identity_idx", + "entityType": "indexes", + "schema": "public", + "table": "routine_execution" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "routine_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": "\"state\" = 'running'", + "with": "", + "method": "btree", + "concurrently": false, + "name": "routine_execution_running_idx", + "entityType": "indexes", + "schema": "public", + "table": "routine_execution" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "routine_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "accepted_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "routine_execution_list_idx", + "entityType": "indexes", + "schema": "public", + "table": "routine_execution" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "parent_thread_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "system_agent_key", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "thread_system_agent_key_idx", + "entityType": "indexes", + "schema": "public", + "table": "thread" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "workspace_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "created_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "thread_workspace_created_at_idx", + "entityType": "indexes", + "schema": "public", + "table": "thread" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "pod_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "host_agent_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "created_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "thread_pod_host_created_at_idx", + "entityType": "indexes", + "schema": "public", + "table": "thread" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "workspace_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "updated_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "thread_workspace_updated_at_idx", + "entityType": "indexes", + "schema": "public", + "table": "thread" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "pod_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "host_agent_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "updated_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "thread_pod_host_updated_at_idx", + "entityType": "indexes", + "schema": "public", + "table": "thread" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "chat_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "updated_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "thread_chat_updated_at_idx", + "entityType": "indexes", + "schema": "public", + "table": "thread" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "thread_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": "\"user_id\" is not null", + "with": "", + "method": "btree", + "concurrently": false, + "name": "thread_participant_user_idx", + "entityType": "indexes", + "schema": "public", + "table": "thread_participant" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "thread_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "agent_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": "\"agent_id\" is not null", + "with": "", + "method": "btree", + "concurrently": false, + "name": "thread_participant_agent_idx", + "entityType": "indexes", + "schema": "public", + "table": "thread_participant" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "thread_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "thread_read_user_thread_idx", + "entityType": "indexes", + "schema": "public", + "table": "thread_read" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "message_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "tool_call_message_idx", + "entityType": "indexes", + "schema": "public", + "table": "tool_call" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "turn_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "tool_call_turn_idx", + "entityType": "indexes", + "schema": "public", + "table": "tool_call" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "turn_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "sdk_tool_call_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "tool_call_sdk_call_idx", + "entityType": "indexes", + "schema": "public", + "table": "tool_call" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "approval_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "tool_call_approval_idx", + "entityType": "indexes", + "schema": "public", + "table": "tool_call" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "thread_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "started_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "turn_thread_started_at_idx", + "entityType": "indexes", + "schema": "public", + "table": "turn" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "trigger_message_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "agent_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "turn_trigger_agent_idx", + "entityType": "indexes", + "schema": "public", + "table": "turn" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "started_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": "\"status\" = 'running'", + "with": "", + "method": "btree", + "concurrently": false, + "name": "turn_active_idx", + "entityType": "indexes", + "schema": "public", + "table": "turn" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "channel", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "seq", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "event_channel_seq_idx", + "entityType": "indexes", + "schema": "public", + "table": "event" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "created_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "event_created_at_idx", + "entityType": "indexes", + "schema": "public", + "table": "event" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "pod_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "name", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "connection_name_idx", + "entityType": "indexes", + "schema": "public", + "table": "connection" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "pod_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "handle", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "connection_handle_idx", + "entityType": "indexes", + "schema": "public", + "table": "connection" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "workspace_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "connection_id_workspace_id_idx", + "entityType": "indexes", + "schema": "public", + "table": "connection" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "pod_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "connection_id_pod_id_idx", + "entityType": "indexes", + "schema": "public", + "table": "connection" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "oauth_state", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "connection_oauth_state_idx", + "entityType": "indexes", + "schema": "public", + "table": "connection" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "workspace_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "name", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "model_provider_name_idx", + "entityType": "indexes", + "schema": "public", + "table": "model_provider" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "workspace_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "model_provider_id_workspace_id_idx", + "entityType": "indexes", + "schema": "public", + "table": "model_provider" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "provider_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "model_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "provider_model_provider_id_idx", + "entityType": "indexes", + "schema": "public", + "table": "provider_model" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "workspace_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "model_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "provider_model_workspace_id_idx", + "entityType": "indexes", + "schema": "public", + "table": "provider_model" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "workspace_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "search_provider_workspace_idx", + "entityType": "indexes", + "schema": "public", + "table": "search_provider" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "workspace_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "search_provider_id_workspace_id_idx", + "entityType": "indexes", + "schema": "public", + "table": "search_provider" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "pod_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "sandbox_pod_id_idx", + "entityType": "indexes", + "schema": "public", + "table": "sandbox" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "workspace_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": "\"enabled\"", + "with": "", + "method": "btree", + "concurrently": false, + "name": "sandbox_provider_enabled_idx", + "entityType": "indexes", + "schema": "public", + "table": "sandbox_provider" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "workspace_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "sandbox_provider_id_workspace_id_idx", + "entityType": "indexes", + "schema": "public", + "table": "sandbox_provider" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "state", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "updated_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": "\"state\" <> 'idle'", + "with": "", + "method": "btree", + "concurrently": false, + "name": "lane_busy_idx", + "entityType": "indexes", + "schema": "public", + "table": "lane" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "subject", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": "\"state\" <> 'idle'", + "with": "", + "method": "btree", + "concurrently": false, + "name": "lane_subject_idx", + "entityType": "indexes", + "schema": "public", + "table": "lane" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "lane_key", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "created_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "lane_request_order_idx", + "entityType": "indexes", + "schema": "public", + "table": "lane_request" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "account_user_id_idx", + "entityType": "indexes", + "schema": "public", + "table": "account" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "pod_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "name", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "agent_name_idx", + "entityType": "indexes", + "schema": "public", + "table": "agent" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "pod_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "handle", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "agent_handle_idx", + "entityType": "indexes", + "schema": "public", + "table": "agent" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "pod_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "provisioned_key", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "agent_provisioned_key_idx", + "entityType": "indexes", + "schema": "public", + "table": "agent" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "workspace_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "system_agent_key", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "agent_system_agent_key_idx", + "entityType": "indexes", + "schema": "public", + "table": "agent" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "workspace_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "agent_id_workspace_id_idx", + "entityType": "indexes", + "schema": "public", + "table": "agent" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "pod_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "agent_pod_id_idx", + "entityType": "indexes", + "schema": "public", + "table": "agent" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "workspace_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "slug", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": "\"kind\" = 'shared'", + "with": "", + "method": "btree", + "concurrently": false, + "name": "pod_slug_idx", + "entityType": "indexes", + "schema": "public", + "table": "pod" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "workspace_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "owner_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": "\"kind\" = 'personal'", + "with": "", + "method": "btree", + "concurrently": false, + "name": "personal_pod_owner_idx", + "entityType": "indexes", + "schema": "public", + "table": "pod" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "workspace_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "pod_id_workspace_id_idx", + "entityType": "indexes", + "schema": "public", + "table": "pod" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "workspace_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "pod_workspace_id_idx", + "entityType": "indexes", + "schema": "public", + "table": "pod" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "pod_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "pod_member_idx", + "entityType": "indexes", + "schema": "public", + "table": "pod_member" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "pod_member_user_id_idx", + "entityType": "indexes", + "schema": "public", + "table": "pod_member" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "token", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "session_token_idx", + "entityType": "indexes", + "schema": "public", + "table": "session" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "session_user_id_idx", + "entityType": "indexes", + "schema": "public", + "table": "session" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "email", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "user_email_idx", + "entityType": "indexes", + "schema": "public", + "table": "user" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "referral_code", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "user_referral_code_idx", + "entityType": "indexes", + "schema": "public", + "table": "user" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "referred_by", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "user_referred_by_idx", + "entityType": "indexes", + "schema": "public", + "table": "user" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "identifier", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "verification_identifier_idx", + "entityType": "indexes", + "schema": "public", + "table": "verification" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "slug", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "workspace_slug_idx", + "entityType": "indexes", + "schema": "public", + "table": "workspace" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "workspace_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "workspace_invite_workspace_id_idx", + "entityType": "indexes", + "schema": "public", + "table": "workspace_invite" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "email", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "workspace_invite_email_idx", + "entityType": "indexes", + "schema": "public", + "table": "workspace_invite" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "workspace_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "workspace_member_idx", + "entityType": "indexes", + "schema": "public", + "table": "workspace_member" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "workspace_member_user_id_idx", + "entityType": "indexes", + "schema": "public", + "table": "workspace_member" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "workspace_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": "\"role\" = 'owner'", + "with": "", + "method": "btree", + "concurrently": false, + "name": "workspace_member_owner_idx", + "entityType": "indexes", + "schema": "public", + "table": "workspace_member" + }, + { + "nameExplicit": false, + "columns": [ + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "workspace", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "chat_workspace_id_workspace_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "chat" + }, + { + "nameExplicit": false, + "columns": [ + "main_thread_id" + ], + "schemaTo": "public", + "tableTo": "thread", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "chat_main_thread_id_thread_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "chat" + }, + { + "nameExplicit": false, + "columns": [ + "initiator_user_id" + ], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "RESTRICT", + "name": "chat_initiator_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "chat" + }, + { + "nameExplicit": true, + "columns": [ + "pod_id", + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "pod", + "columnsTo": [ + "id", + "workspace_id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "chat_pod_workspace_fkey", + "entityType": "fks", + "schema": "public", + "table": "chat" + }, + { + "nameExplicit": true, + "columns": [ + "host_agent_id", + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "agent", + "columnsTo": [ + "id", + "workspace_id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "chat_host_agent_workspace_fkey", + "entityType": "fks", + "schema": "public", + "table": "chat" + }, + { + "nameExplicit": false, + "columns": [ + "parent_thread_id" + ], + "schemaTo": "public", + "tableTo": "thread", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "collaboration_parent_thread_id_thread_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "collaboration" + }, + { + "nameExplicit": false, + "columns": [ + "parent_message_id" + ], + "schemaTo": "public", + "tableTo": "message", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "collaboration_parent_message_id_message_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "collaboration" + }, + { + "nameExplicit": false, + "columns": [ + "turn_id" + ], + "schemaTo": "public", + "tableTo": "turn", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "collaboration_turn_id_turn_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "collaboration" + }, + { + "nameExplicit": false, + "columns": [ + "child_thread_id" + ], + "schemaTo": "public", + "tableTo": "thread", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "collaboration_child_thread_id_thread_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "collaboration" + }, + { + "nameExplicit": false, + "columns": [ + "collaborator_agent_id" + ], + "schemaTo": "public", + "tableTo": "agent", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "collaboration_collaborator_agent_id_agent_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "collaboration" + }, + { + "nameExplicit": false, + "columns": [ + "thread_id" + ], + "schemaTo": "public", + "tableTo": "thread", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "message_thread_id_thread_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "message" + }, + { + "nameExplicit": false, + "columns": [ + "author_user_id" + ], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "RESTRICT", + "name": "message_author_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "message" + }, + { + "nameExplicit": false, + "columns": [ + "author_agent_id" + ], + "schemaTo": "public", + "tableTo": "agent", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "message_author_agent_id_agent_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "message" + }, + { + "nameExplicit": false, + "columns": [ + "turn_id" + ], + "schemaTo": "public", + "tableTo": "turn", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "message_turn_id_turn_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "message" + }, + { + "nameExplicit": false, + "columns": [ + "routine_execution_id" + ], + "schemaTo": "public", + "tableTo": "routine_execution", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "message_routine_execution_id_routine_execution_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "message" + }, + { + "nameExplicit": false, + "columns": [ + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "workspace", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "routine_workspace_id_workspace_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "routine" + }, + { + "nameExplicit": false, + "columns": [ + "created_by_id" + ], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "routine_created_by_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "routine" + }, + { + "nameExplicit": true, + "columns": [ + "agent_id", + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "agent", + "columnsTo": [ + "id", + "workspace_id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "routine_agent_workspace_fkey", + "entityType": "fks", + "schema": "public", + "table": "routine" + }, + { + "nameExplicit": false, + "columns": [ + "routine_id" + ], + "schemaTo": "public", + "tableTo": "routine", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "RESTRICT", + "name": "routine_execution_routine_id_routine_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "routine_execution" + }, + { + "nameExplicit": false, + "columns": [ + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "workspace", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "routine_execution_workspace_id_workspace_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "routine_execution" + }, + { + "nameExplicit": false, + "columns": [ + "thread_id" + ], + "schemaTo": "public", + "tableTo": "thread", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "routine_execution_thread_id_thread_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "routine_execution" + }, + { + "nameExplicit": true, + "columns": [ + "agent_id", + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "agent", + "columnsTo": [ + "id", + "workspace_id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "routine_execution_agent_workspace_fkey", + "entityType": "fks", + "schema": "public", + "table": "routine_execution" + }, + { + "nameExplicit": true, + "columns": [ + "routine_id", + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "routine", + "columnsTo": [ + "id", + "workspace_id" + ], + "onUpdate": "NO ACTION", + "onDelete": "RESTRICT", + "name": "routine_execution_routine_workspace_fkey", + "entityType": "fks", + "schema": "public", + "table": "routine_execution" + }, + { + "nameExplicit": false, + "columns": [ + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "workspace", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "thread_workspace_id_workspace_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "thread" + }, + { + "nameExplicit": false, + "columns": [ + "chat_id" + ], + "schemaTo": "public", + "tableTo": "chat", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "thread_chat_id_chat_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "thread" + }, + { + "nameExplicit": false, + "columns": [ + "parent_thread_id" + ], + "schemaTo": "public", + "tableTo": "thread", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "thread_parent_thread_id_thread_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "thread" + }, + { + "nameExplicit": false, + "columns": [ + "initiator_user_id" + ], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "RESTRICT", + "name": "thread_initiator_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "thread" + }, + { + "nameExplicit": true, + "columns": [ + "pod_id", + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "pod", + "columnsTo": [ + "id", + "workspace_id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "thread_pod_workspace_fkey", + "entityType": "fks", + "schema": "public", + "table": "thread" + }, + { + "nameExplicit": true, + "columns": [ + "host_agent_id", + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "agent", + "columnsTo": [ + "id", + "workspace_id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "thread_host_agent_workspace_fkey", + "entityType": "fks", + "schema": "public", + "table": "thread" + }, + { + "nameExplicit": false, + "columns": [ + "thread_id" + ], + "schemaTo": "public", + "tableTo": "thread", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "thread_compaction_thread_id_thread_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "thread_compaction" + }, + { + "nameExplicit": false, + "columns": [ + "thread_id" + ], + "schemaTo": "public", + "tableTo": "thread", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "thread_participant_thread_id_thread_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "thread_participant" + }, + { + "nameExplicit": false, + "columns": [ + "user_id" + ], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "thread_participant_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "thread_participant" + }, + { + "nameExplicit": false, + "columns": [ + "agent_id" + ], + "schemaTo": "public", + "tableTo": "agent", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "thread_participant_agent_id_agent_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "thread_participant" + }, + { + "nameExplicit": false, + "columns": [ + "thread_id" + ], + "schemaTo": "public", + "tableTo": "thread", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "thread_read_thread_id_thread_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "thread_read" + }, + { + "nameExplicit": false, + "columns": [ + "user_id" + ], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "thread_read_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "thread_read" + }, + { + "nameExplicit": false, + "columns": [ + "thread_id" + ], + "schemaTo": "public", + "tableTo": "thread", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "thread_summary_thread_id_thread_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "thread_summary" + }, + { + "nameExplicit": false, + "columns": [ + "source_message_id" + ], + "schemaTo": "public", + "tableTo": "message", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "thread_summary_source_message_id_message_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "thread_summary" + }, + { + "nameExplicit": false, + "columns": [ + "thread_id" + ], + "schemaTo": "public", + "tableTo": "thread", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "tool_call_thread_id_thread_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "tool_call" + }, + { + "nameExplicit": false, + "columns": [ + "message_id" + ], + "schemaTo": "public", + "tableTo": "message", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "tool_call_message_id_message_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "tool_call" + }, + { + "nameExplicit": false, + "columns": [ + "turn_id" + ], + "schemaTo": "public", + "tableTo": "turn", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "tool_call_turn_id_turn_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "tool_call" + }, + { + "nameExplicit": false, + "columns": [ + "decided_by_id" + ], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "tool_call_decided_by_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "tool_call" + }, + { + "nameExplicit": false, + "columns": [ + "thread_id" + ], + "schemaTo": "public", + "tableTo": "thread", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "turn_thread_id_thread_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "turn" + }, + { + "nameExplicit": false, + "columns": [ + "agent_id" + ], + "schemaTo": "public", + "tableTo": "agent", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "turn_agent_id_agent_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "turn" + }, + { + "nameExplicit": false, + "columns": [ + "trigger_message_id" + ], + "schemaTo": "public", + "tableTo": "message", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "turn_trigger_message_id_message_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "turn" + }, + { + "nameExplicit": false, + "columns": [ + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "workspace", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "connection_workspace_id_workspace_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "connection" + }, + { + "nameExplicit": false, + "columns": [ + "created_by_id" + ], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "connection_created_by_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "connection" + }, + { + "nameExplicit": true, + "columns": [ + "pod_id", + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "pod", + "columnsTo": [ + "id", + "workspace_id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "connection_pod_workspace_fkey", + "entityType": "fks", + "schema": "public", + "table": "connection" + }, + { + "nameExplicit": false, + "columns": [ + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "workspace", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "model_provider_workspace_id_workspace_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "model_provider" + }, + { + "nameExplicit": false, + "columns": [ + "created_by_id" + ], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "model_provider_created_by_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "model_provider" + }, + { + "nameExplicit": false, + "columns": [ + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "workspace", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "provider_model_workspace_id_workspace_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "provider_model" + }, + { + "nameExplicit": true, + "columns": [ + "provider_id", + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "model_provider", + "columnsTo": [ + "id", + "workspace_id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "provider_model_provider_workspace_fkey", + "entityType": "fks", + "schema": "public", + "table": "provider_model" + }, + { + "nameExplicit": false, + "columns": [ + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "workspace", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "search_provider_workspace_id_workspace_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "search_provider" + }, + { + "nameExplicit": false, + "columns": [ + "created_by_id" + ], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "search_provider_created_by_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "search_provider" + }, + { + "nameExplicit": false, + "columns": [ + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "workspace", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "workspace_default_model_workspace_id_workspace_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "workspace_default_model" + }, + { + "nameExplicit": true, + "columns": [ + "pod_id", + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "pod", + "columnsTo": [ + "id", + "workspace_id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "sandbox_pod_workspace_fkey", + "entityType": "fks", + "schema": "public", + "table": "sandbox" + }, + { + "nameExplicit": true, + "columns": [ + "sandbox_provider_id", + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "sandbox_provider", + "columnsTo": [ + "id", + "workspace_id" + ], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "sandbox_provider_workspace_fkey", + "entityType": "fks", + "schema": "public", + "table": "sandbox" + }, + { + "nameExplicit": false, + "columns": [ + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "workspace", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "sandbox_allowed_host_workspace_id_workspace_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "sandbox_allowed_host" + }, + { + "nameExplicit": false, + "columns": [ + "added_by_id" + ], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "sandbox_allowed_host_added_by_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "sandbox_allowed_host" + }, + { + "nameExplicit": false, + "columns": [ + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "workspace", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "sandbox_blocked_host_workspace_id_workspace_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "sandbox_blocked_host" + }, + { + "nameExplicit": false, + "columns": [ + "blocked_by_id" + ], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "sandbox_blocked_host_blocked_by_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "sandbox_blocked_host" + }, + { + "nameExplicit": false, + "columns": [ + "sandbox_id" + ], + "schemaTo": "public", + "tableTo": "sandbox", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "sandbox_lease_sandbox_id_sandbox_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "sandbox_lease" + }, + { + "nameExplicit": false, + "columns": [ + "added_by_id" + ], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "sandbox_pod_allowed_host_added_by_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "sandbox_pod_allowed_host" + }, + { + "nameExplicit": true, + "columns": [ + "pod_id", + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "pod", + "columnsTo": [ + "id", + "workspace_id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "sandbox_pod_allowed_host_pod_workspace_fkey", + "entityType": "fks", + "schema": "public", + "table": "sandbox_pod_allowed_host" + }, + { + "nameExplicit": false, + "columns": [ + "added_by_id" + ], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "sandbox_pod_package_added_by_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "sandbox_pod_package" + }, + { + "nameExplicit": true, + "columns": [ + "pod_id", + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "pod", + "columnsTo": [ + "id", + "workspace_id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "sandbox_pod_package_pod_workspace_fkey", + "entityType": "fks", + "schema": "public", + "table": "sandbox_pod_package" + }, + { + "nameExplicit": false, + "columns": [ + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "workspace", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "sandbox_provider_workspace_id_workspace_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "sandbox_provider" + }, + { + "nameExplicit": false, + "columns": [ + "created_by_id" + ], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "sandbox_provider_created_by_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "sandbox_provider" + }, + { + "nameExplicit": false, + "columns": [ + "lane_key" + ], + "schemaTo": "public", + "tableTo": "lane", + "columnsTo": [ + "key" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "lane_request_lane_key_lane_key_fkey", + "entityType": "fks", + "schema": "public", + "table": "lane_request" + }, + { + "nameExplicit": false, + "columns": [ + "user_id" + ], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "account_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "account" + }, + { + "nameExplicit": false, + "columns": [ + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "workspace", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "agent_workspace_id_workspace_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "agent" + }, + { + "nameExplicit": false, + "columns": [ + "created_by_id" + ], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "agent_created_by_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "agent" + }, + { + "nameExplicit": true, + "columns": [ + "pod_id", + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "pod", + "columnsTo": [ + "id", + "workspace_id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "agent_pod_workspace_fkey", + "entityType": "fks", + "schema": "public", + "table": "agent" + }, + { + "nameExplicit": false, + "columns": [ + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "workspace", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "pod_workspace_id_workspace_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "pod" + }, + { + "nameExplicit": false, + "columns": [ + "created_by_id" + ], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "pod_created_by_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "pod" + }, + { + "nameExplicit": true, + "columns": [ + "workspace_id", + "owner_id" + ], + "schemaTo": "public", + "tableTo": "workspace_member", + "columnsTo": [ + "workspace_id", + "user_id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "pod_owner_workspace_member_fkey", + "entityType": "fks", + "schema": "public", + "table": "pod" + }, + { + "nameExplicit": true, + "columns": [ + "pod_id", + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "pod", + "columnsTo": [ + "id", + "workspace_id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "pod_member_pod_workspace_fkey", + "entityType": "fks", + "schema": "public", + "table": "pod_member" + }, + { + "nameExplicit": true, + "columns": [ + "workspace_id", + "user_id" + ], + "schemaTo": "public", + "tableTo": "workspace_member", + "columnsTo": [ + "workspace_id", + "user_id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "pod_member_workspace_member_fkey", + "entityType": "fks", + "schema": "public", + "table": "pod_member" + }, + { + "nameExplicit": false, + "columns": [ + "user_id" + ], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "session_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "session" + }, + { + "nameExplicit": false, + "columns": [ + "referred_by" + ], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "user_referred_by_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "user" + }, + { + "nameExplicit": false, + "columns": [ + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "workspace", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "workspace_invite_workspace_id_workspace_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "workspace_invite" + }, + { + "nameExplicit": false, + "columns": [ + "inviter_id" + ], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "workspace_invite_inviter_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "workspace_invite" + }, + { + "nameExplicit": false, + "columns": [ + "workspace_id" + ], + "schemaTo": "public", + "tableTo": "workspace", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "workspace_member_workspace_id_workspace_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "workspace_member" + }, + { + "nameExplicit": false, + "columns": [ + "user_id" + ], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "workspace_member_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "workspace_member" + }, + { + "columns": [ + "workspace_id", + "host" + ], + "nameExplicit": false, + "name": "sandbox_allowed_host_pkey", + "entityType": "pks", + "schema": "public", + "table": "sandbox_allowed_host" + }, + { + "columns": [ + "workspace_id", + "host" + ], + "nameExplicit": false, + "name": "sandbox_blocked_host_pkey", + "entityType": "pks", + "schema": "public", + "table": "sandbox_blocked_host" + }, + { + "columns": [ + "sandbox_id", + "holder" + ], + "nameExplicit": false, + "name": "sandbox_lease_pkey", + "entityType": "pks", + "schema": "public", + "table": "sandbox_lease" + }, + { + "columns": [ + "pod_id", + "host" + ], + "nameExplicit": false, + "name": "sandbox_pod_allowed_host_pkey", + "entityType": "pks", + "schema": "public", + "table": "sandbox_pod_allowed_host" + }, + { + "columns": [ + "pod_id", + "channel", + "name" + ], + "nameExplicit": false, + "name": "sandbox_pod_package_pkey", + "entityType": "pks", + "schema": "public", + "table": "sandbox_pod_package" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "model_request_pkey", + "schema": "public", + "table": "model_request", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "chat_pkey", + "schema": "public", + "table": "chat", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "collaboration_pkey", + "schema": "public", + "table": "collaboration", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "message_pkey", + "schema": "public", + "table": "message", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "routine_pkey", + "schema": "public", + "table": "routine", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "routine_execution_pkey", + "schema": "public", + "table": "routine_execution", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "thread_pkey", + "schema": "public", + "table": "thread", + "entityType": "pks" + }, + { + "columns": [ + "thread_id" + ], + "nameExplicit": false, + "name": "thread_compaction_pkey", + "schema": "public", + "table": "thread_compaction", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "thread_participant_pkey", + "schema": "public", + "table": "thread_participant", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "thread_read_pkey", + "schema": "public", + "table": "thread_read", + "entityType": "pks" + }, + { + "columns": [ + "thread_id" + ], + "nameExplicit": false, + "name": "thread_summary_pkey", + "schema": "public", + "table": "thread_summary", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "tool_call_pkey", + "schema": "public", + "table": "tool_call", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "turn_pkey", + "schema": "public", + "table": "turn", + "entityType": "pks" + }, + { + "columns": [ + "seq" + ], + "nameExplicit": false, + "name": "event_pkey", + "schema": "public", + "table": "event", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "connection_pkey", + "schema": "public", + "table": "connection", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "model_provider_pkey", + "schema": "public", + "table": "model_provider", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "provider_model_pkey", + "schema": "public", + "table": "provider_model", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "search_provider_pkey", + "schema": "public", + "table": "search_provider", + "entityType": "pks" + }, + { + "columns": [ + "workspace_id" + ], + "nameExplicit": false, + "name": "workspace_default_model_pkey", + "schema": "public", + "table": "workspace_default_model", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "sandbox_pkey", + "schema": "public", + "table": "sandbox", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "sandbox_provider_pkey", + "schema": "public", + "table": "sandbox_provider", + "entityType": "pks" + }, + { + "columns": [ + "key" + ], + "nameExplicit": false, + "name": "lane_pkey", + "schema": "public", + "table": "lane", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "lane_request_pkey", + "schema": "public", + "table": "lane_request", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "account_pkey", + "schema": "public", + "table": "account", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "agent_pkey", + "schema": "public", + "table": "agent", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "pod_pkey", + "schema": "public", + "table": "pod", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "pod_member_pkey", + "schema": "public", + "table": "pod_member", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "session_pkey", + "schema": "public", + "table": "session", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "user_pkey", + "schema": "public", + "table": "user", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "verification_pkey", + "schema": "public", + "table": "verification", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "workspace_pkey", + "schema": "public", + "table": "workspace", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "workspace_invite_pkey", + "schema": "public", + "table": "workspace_invite", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "workspace_member_pkey", + "schema": "public", + "table": "workspace_member", + "entityType": "pks" + }, + { + "value": "\"purpose\" in ('agent-turn', 'facilitation', 'summary', 'compaction', 'trial', 'provider-check')", + "name": "model_request_purpose_valid", + "entityType": "checks", + "schema": "public", + "table": "model_request" + }, + { + "value": "\"outcome\" in ('started', 'completed', 'failed', 'aborted')", + "name": "model_request_outcome_valid", + "entityType": "checks", + "schema": "public", + "table": "model_request" + }, + { + "value": "(\"outcome\" = 'started') = (\"ended_at\" is null)", + "name": "model_request_ended_valid", + "entityType": "checks", + "schema": "public", + "table": "model_request" + }, + { + "value": "(\"cost_usd\" is null) = (\"cost_source\" is null) and (\"cost_usd\" is null or \"cost_usd\" >= 0)", + "name": "model_request_cost_valid", + "entityType": "checks", + "schema": "public", + "table": "model_request" + }, + { + "value": "num_nonnulls(\"author_user_id\", \"author_agent_id\", \"routine_trigger\") = 1", + "name": "message_one_author", + "entityType": "checks", + "schema": "public", + "table": "message" + }, + { + "value": "\"state\" in ('enabled', 'paused')", + "name": "routine_state_valid", + "entityType": "checks", + "schema": "public", + "table": "routine" + }, + { + "value": "\"results\" in ('keep_in_run', 'post_to_chat')", + "name": "routine_results_valid", + "entityType": "checks", + "schema": "public", + "table": "routine" + }, + { + "value": "(\n\t\t\t\t\"trigger_kind\" = 'cron'\n\t\t\t\tand \"cron_expression\" is not null\n\t\t\t\tand \"cron_timezone\" is not null\n\t\t\t\tand \"webhook_secret_digest\" is null\n\t\t\t) or (\n\t\t\t\t\"trigger_kind\" = 'webhook'\n\t\t\t\tand \"cron_expression\" is null\n\t\t\t\tand \"cron_timezone\" is null\n\t\t\t\tand \"next_scheduled_at\" is null\n\t\t\t\tand \"webhook_secret_digest\" is not null\n\t\t\t)", + "name": "routine_trigger_valid", + "entityType": "checks", + "schema": "public", + "table": "routine" + }, + { + "value": "\"trigger_kind\" <> 'cron'\n\t\t\t\tor (\"state\" = 'enabled' and \"next_scheduled_at\" is not null)\n\t\t\t\tor (\"state\" = 'paused' and \"next_scheduled_at\" is null)", + "name": "routine_schedule_state_valid", + "entityType": "checks", + "schema": "public", + "table": "routine" + }, + { + "value": "\"trigger_kind\" in ('cron', 'webhook', 'manual')\n\t\t\t\tand \"trigger\"->>'kind' = \"trigger_kind\"\n\t\t\t\tand (\"trigger_kind\" = 'webhook' or \"trigger_identity\" is not null)", + "name": "routine_execution_trigger_valid", + "entityType": "checks", + "schema": "public", + "table": "routine_execution" + }, + { + "value": "(\n\t\t\t\t\"state\" = 'queued'\n\t\t\t\tand \"started_at\" is null\n\t\t\t\tand \"finished_at\" is null\n\t\t\t\tand \"error\" is null\n\t\t\t) or (\n\t\t\t\t\"state\" = 'running'\n\t\t\t\tand \"started_at\" is not null\n\t\t\t\tand \"finished_at\" is null\n\t\t\t) or (\n\t\t\t\t\"state\" in ('completed', 'failed', 'cancelled')\n\t\t\t\tand \"finished_at\" is not null\n\t\t\t)", + "name": "routine_execution_state_valid", + "entityType": "checks", + "schema": "public", + "table": "routine_execution" + }, + { + "value": "(\"pending_terminal_state\" is null and \"pending_terminal_error\" is null)\n\t\t\t\tor (\"state\" = 'running' and \"pending_terminal_state\" = 'cancelled' and \"pending_terminal_error\" is null)\n\t\t\t\tor (\"state\" = 'running' and \"pending_terminal_state\" = 'failed')", + "name": "routine_execution_pending_terminal_valid", + "entityType": "checks", + "schema": "public", + "table": "routine_execution" + }, + { + "value": "\"results\" in ('keep_in_run', 'post_to_chat')", + "name": "routine_execution_results_valid", + "entityType": "checks", + "schema": "public", + "table": "routine_execution" + }, + { + "value": "\"type\" in ('chat', 'collaboration', 'routine', 'system_agent')", + "name": "thread_type_valid", + "entityType": "checks", + "schema": "public", + "table": "thread" + }, + { + "value": "num_nonnulls(\"user_id\", \"agent_id\") = 1", + "name": "thread_participant_one_identity", + "entityType": "checks", + "schema": "public", + "table": "thread_participant" + }, + { + "value": "\"channel\" in ('stable', 'unstable')", + "name": "sandbox_pod_package_channel_check", + "entityType": "checks", + "schema": "public", + "table": "sandbox_pod_package" + }, + { + "value": "\"preset\" in ('opensandbox', 'e2b')", + "name": "sandbox_provider_preset_check", + "entityType": "checks", + "schema": "public", + "table": "sandbox_provider" + }, + { + "value": "\"state\" in ('idle', 'starting', 'running')", + "name": "lane_state_check", + "entityType": "checks", + "schema": "public", + "table": "lane" + }, + { + "value": "(\"state\" = 'idle') = (\"execution_id\" is null)", + "name": "lane_execution_check", + "entityType": "checks", + "schema": "public", + "table": "lane" + }, + { + "value": "(\"system_agent_key\" is null) = (\"pod_id\" is not null)", + "name": "agent_placement_check", + "entityType": "checks", + "schema": "public", + "table": "agent" + }, + { + "value": "\"kind\" in ('personal', 'shared')", + "name": "pod_kind_check", + "entityType": "checks", + "schema": "public", + "table": "pod" + }, + { + "value": "\"kind\" = 'shared' or \"owner_id\" is not null", + "name": "pod_personal_owner_check", + "entityType": "checks", + "schema": "public", + "table": "pod" + }, + { + "value": "(\"kind\" = 'personal') = (\"slug\" = 'personal')", + "name": "pod_personal_slug_check", + "entityType": "checks", + "schema": "public", + "table": "pod" + }, + { + "value": "\"role\" in ('admin', 'member', 'viewer')", + "name": "workspace_invite_role_check", + "entityType": "checks", + "schema": "public", + "table": "workspace_invite" + }, + { + "value": "\"status\" in ('pending', 'accepted', 'canceled')", + "name": "workspace_invite_status_check", + "entityType": "checks", + "schema": "public", + "table": "workspace_invite" + }, + { + "value": "\"role\" in ('owner', 'admin', 'member', 'viewer')", + "name": "workspace_member_role_check", + "entityType": "checks", + "schema": "public", + "table": "workspace_member" + } + ], + "renames": [] +} \ No newline at end of file diff --git a/packages/core/src/conversations/tools/approval-deciders.ts b/packages/core/src/conversations/tools/approval-deciders.ts index 2899cd87..fdff7dda 100644 --- a/packages/core/src/conversations/tools/approval-deciders.ts +++ b/packages/core/src/conversations/tools/approval-deciders.ts @@ -1,11 +1,14 @@ import type { ToolApprovalDeciders } from "@sugabots/contracts"; import { REQUEST_NETWORK_ACCESS_TOOL } from "./network-access/tool.ts"; +import { REQUEST_SOFTWARE_TOOL } from "./software/tool.ts"; /** * Who decides a call to `tool` that waits for approval. A request for network - * access changes what the pod's sandbox may reach, so those who manage the - * pod's sandbox decide it; the pod's approvers decide everything else. + * access or software changes the pod's sandbox, so those who manage it decide + * it; the pod's approvers decide everything else. */ export function decidersOf(tool: string): ToolApprovalDeciders { - return tool === REQUEST_NETWORK_ACCESS_TOOL ? "sandbox-managers" : "pod"; + return tool === REQUEST_NETWORK_ACCESS_TOOL || tool === REQUEST_SOFTWARE_TOOL + ? "sandbox-managers" + : "pod"; } diff --git a/packages/core/src/conversations/tools/sandbox.test.ts b/packages/core/src/conversations/tools/sandbox.test.ts index 835e8291..3957c75e 100644 --- a/packages/core/src/conversations/tools/sandbox.test.ts +++ b/packages/core/src/conversations/tools/sandbox.test.ts @@ -1,9 +1,11 @@ import type { ToolSet } from "ai"; +import { eq } from "drizzle-orm"; import { Effect, Exit, Layer, Scope } from "effect"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; import { pod, sandboxAllowedHost, + sandboxPodPackage, user, workspace, workspaceMember, @@ -123,6 +125,35 @@ describe.skipIf(!configured)("sandbox tools, against Postgres and OpenSandbox", String(result.stdout).trim(), ); + it( + "gives the pod's sandbox the software it has recorded, on every command's PATH, and takes it away", + async () => { + // A NixOS 26.05 commit, which stays where it is. + const nixpkgsRev = "0d9e9b832d03ac387417e16ce1febf73b2e631e1"; + await onDatabase((db) => + db + .insert(sandboxPodPackage) + .values({ ...thePod, name: "hello", channel: "stable", nixpkgsRev }), + ); + + const installed = await run( + await toolsFor("turn-software-1", "thread-s", "agent-1"), + "hello", + ); + await onDatabase((db) => + db.delete(sandboxPodPackage).where(eq(sandboxPodPackage.podId, thePod.podId)), + ); + const removed = await run( + await toolsFor("turn-software-2", "thread-s", "agent-1"), + "command -v hello || echo gone", + ); + + expect(installed).toBe("Hello, world!"); + expect(removed).toBe("gone"); + }, + SLOW, + ); + it( "starts commands in the thread's folder, with the agent's home as HOME", async () => { diff --git a/packages/core/src/conversations/tools/sandbox.ts b/packages/core/src/conversations/tools/sandbox.ts index ab94c04c..0920a95d 100644 --- a/packages/core/src/conversations/tools/sandbox.ts +++ b/packages/core/src/conversations/tools/sandbox.ts @@ -8,6 +8,7 @@ import { allowedHostsOf, blockedHostsOf } from "../../sandboxes/allowed-hosts.ts import { PodSandboxes } from "../../sandboxes/pod-sandboxes.ts"; import { SandboxNetwork } from "../../sandboxes/sandbox-network.ts"; import { SandboxProviderRepository } from "../../sandboxes/sandbox-provider-repository.ts"; +import { SandboxSoftware } from "../../sandboxes/sandbox-software.ts"; import type { UserMessage } from "../../user-message.ts"; import { type BrowserSession, browserSession, browserTools } from "./browser/browser.ts"; import { REQUEST_NETWORK_ACCESS_TOOL, requestNetworkAccess } from "./network-access/tool.ts"; @@ -24,6 +25,8 @@ import { WRITE_FILE_TOOL, writeFileTool, } from "./sandbox/tools.ts"; +import { syncSoftware } from "./software/profile.ts"; +import { REQUEST_SOFTWARE_TOOL, requestSoftware } from "./software/tool.ts"; /** * The tools that work in a pod's sandbox: `run_command`, `read_file` and @@ -71,6 +74,7 @@ export const make = Effect.gen(function* () { const providers = yield* SandboxProviderRepository.Service; const podSandboxes = yield* PodSandboxes.Service; const network = yield* SandboxNetwork.Service; + const software = yield* SandboxSoftware.Service; const operation = yield* serviceOperations("SandboxTools"); const offeredIn = ({ turnId, @@ -89,6 +93,7 @@ export const make = Effect.gen(function* () { }, requests: { [REQUEST_NETWORK_ACCESS_TOOL]: requestNetworkAccess({ turnId, network, blocked, run }), + [REQUEST_SOFTWARE_TOOL]: requestSoftware({ turnId, software, openSandbox, run }), }, }); return Service.of({ @@ -134,6 +139,24 @@ export const make = Effect.gen(function* () { maxOutputCharacters: 2_000, }), ), + // As is the pod's software: a new sandbox installs it, and one that + // has it builds nothing. + Effect.tap(({ sandbox }) => + software.packagesOf(pod).pipe( + Effect.flatMap((packages) => syncSoftware(sandbox, packages)), + Effect.tap((synced) => + synced.exitCode === 0 + ? Effect.void + : Effect.logWarning( + "Could not give a sandbox its pod's software", + synced.stderr.text, + ), + ), + Effect.catchTag("SandboxUnavailable", (failure) => + Effect.logWarning("Could not give a sandbox its pod's software", failure), + ), + ), + ), ); const openSandbox = openOncePerTurn(() => runPromiseExit(prepared).then((exit) => { @@ -168,7 +191,12 @@ export const make = Effect.gen(function* () { export const layerNoDeps = Layer.effect(Service, make); export const layer = layerNoDeps.pipe( - Layer.provide([PodSandboxes.layer, SandboxNetwork.layer, SandboxProviderRepository.layer]), + Layer.provide([ + PodSandboxes.layer, + SandboxNetwork.layer, + SandboxSoftware.layer, + SandboxProviderRepository.layer, + ]), ); /** What a turn's tools work through. */ diff --git a/packages/core/src/conversations/tools/sandbox/tools.ts b/packages/core/src/conversations/tools/sandbox/tools.ts index d2ea9e21..8eec381c 100644 --- a/packages/core/src/conversations/tools/sandbox/tools.ts +++ b/packages/core/src/conversations/tools/sandbox/tools.ts @@ -5,6 +5,7 @@ import type { PodSandboxes } from "../../../sandboxes/pod-sandboxes.ts"; import { Sandboxes } from "../../../sandboxes/sandboxes.ts"; import { UserMessage } from "../../../user-message.ts"; import { REQUEST_NETWORK_ACCESS_TOOL } from "../network-access/tool.ts"; +import { REQUEST_SOFTWARE_TOOL } from "../software/tool.ts"; export const RUN_COMMAND_TOOL = "run_command"; export const READ_FILE_TOOL = "read_file"; @@ -135,7 +136,7 @@ const pathIn = (place: Place) => export function runCommandTool(openSandbox: OpenSandbox, place: Place) { return tool({ - description: `Run a bash command in the pod's sandbox, a Linux machine shared by the agents in this pod. Commands start in this thread's folder, ${place.folder}: a scratchpad kept between the thread's turns and shared with the other agents in the thread, so clone and build here. Your home, ${place.home} (also $HOME), is your own and kept across every thread in the pod: keep notes, settings and tools you want everywhere there. Nothing else carries over between commands, so cd or export in the same command. Returns the exit code and the end of stdout and stderr. A command still running at its timeout is stopped. It can't ask a person anything, so pass flags that skip prompts. For web pages, use the browser_ tools. Commands run without a screen; when a task needs one for something other than the browser, and the sandbox has sugabots-desktop, run "sugabots-desktop start" to get a desktop, set the DISPLAY it prints for the programs you start, and use scrot to take screenshots and xdotool to click and type. Stop it with "sugabots-desktop stop " when you're done. The sandbox, its browser included, reaches only the hosts the turn's note lists; to reach another the task needs, call ${REQUEST_NETWORK_ACCESS_TOOL}.`, + description: `Run a bash command in the pod's sandbox, a Linux machine shared by the agents in this pod. Commands start in this thread's folder, ${place.folder}: a scratchpad kept between the thread's turns and shared with the other agents in the thread, so clone and build here. Your home, ${place.home} (also $HOME), is your own and kept across every thread in the pod: keep notes, settings and tools you want everywhere there. Nothing else carries over between commands, so cd or export in the same command. Returns the exit code and the end of stdout and stderr. A command still running at its timeout is stopped. It can't ask a person anything, so pass flags that skip prompts. For web pages, use the browser_ tools. Commands run without a screen; when a task needs one for something other than the browser, and the sandbox has sugabots-desktop, run "sugabots-desktop start" to get a desktop, set the DISPLAY it prints for the programs you start, and use scrot to take screenshots and xdotool to click and type. Stop it with "sugabots-desktop stop " when you're done. The sandbox, its browser included, reaches only the hosts the turn's note lists; to reach another the task needs, call ${REQUEST_NETWORK_ACCESS_TOOL}. For software the sandbox lacks, run "nix shell nixpkgs# -c " for this once, or call ${REQUEST_SOFTWARE_TOOL} to keep it for the pod.`, inputSchema: Schema.Struct({ command: Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(20_000)).annotate({ description: "The bash command to run", @@ -245,7 +246,7 @@ export function allowedHostsNote(allowedHosts: readonly string[]): string { } /** `text` as one single-quoted bash word. */ -function shellQuoted(text: string) { +export function shellQuoted(text: string) { return `'${text.replaceAll("'", `'\\''`)}'`; } diff --git a/packages/core/src/conversations/tools/software/profile.ts b/packages/core/src/conversations/tools/software/profile.ts new file mode 100644 index 00000000..1a5c47ff --- /dev/null +++ b/packages/core/src/conversations/tools/software/profile.ts @@ -0,0 +1,84 @@ +import { Effect } from "effect"; +import type { SandboxSoftware } from "../../../sandboxes/sandbox-software.ts"; +import type { Sandboxes } from "../../../sandboxes/sandboxes.ts"; +import { shellQuoted } from "../sandbox/tools.ts"; + +/** + * The Nix profile the pod's packages are installed into, shared by its agents. + * The image puts its `bin` on every login shell's PATH. + */ +export const SOFTWARE_PROFILE = "/nix/var/nix/profiles/sugabots"; + +/** What the profile was last built from, so a sandbox that has it all builds nothing. */ +const BUILT_FROM = `${SOFTWARE_PROFILE}.built-from`; + +const INSTALL = { timeout: "15 minutes", maxOutputCharacters: 4_000 } as const; + +/** nixpkgs as an agent names it, by the channel a package comes from. */ +export const NIXPKGS = { stable: "nixpkgs", unstable: "nixpkgs-unstable" } as const; + +/** Where a package is built from: nixpkgs at its commit. */ +function flakeRef(software: SandboxSoftware.Package) { + return `github:NixOS/nixpkgs/${software.nixpkgsRev}#${software.name}`; +} + +/** What the profile holds when it has exactly `packages`, in a stable order. */ +function builtFrom(packages: readonly SandboxSoftware.Package[]) { + return packages.map(flakeRef).sort().join("\n"); +} + +/** + * Gives the sandbox exactly the pod's packages, rebuilding its profile if they + * changed since it was last built. Fails as the command does, with its output. + */ +export const syncSoftware = ( + sandbox: Sandboxes.Sandbox, + packages: readonly SandboxSoftware.Package[], +) => { + const want = builtFrom(packages); + const refs = packages.map((software) => shellQuoted(flakeRef(software))).join(" "); + const rebuild = [ + `rm -f ${SOFTWARE_PROFILE} ${SOFTWARE_PROFILE}-*-link`, + ...(refs ? [`nix profile install --profile ${SOFTWARE_PROFILE} ${refs}`] : []), + `printf '%s' ${shellQuoted(want)} > ${BUILT_FROM}`, + ].join(" && "); + return sandbox.exec( + `if [ "$(cat ${BUILT_FROM} 2>/dev/null)" = ${shellQuoted(want)} ]; then exit 0; fi; ${rebuild}`, + INSTALL, + ); +}; + +/** + * The nixpkgs commit `channel` is at now, which a new package is installed + * from and kept at. + */ +export const currentNixpkgs = (sandbox: Sandboxes.Sandbox, channel: keyof typeof NIXPKGS) => + Effect.map( + sandbox.exec(`nix flake metadata ${NIXPKGS[channel]} --json`, { + timeout: "5 minutes", + maxOutputCharacters: 20_000, + }), + (ran) => { + if (ran.exitCode !== 0) return undefined; + const rev = (JSON.parse(ran.stdout.text) as { locked?: { rev?: unknown } }).locked?.rev; + return typeof rev === "string" ? rev : undefined; + }, + ); + +/** + * Installs one more package into the profile, then marks the profile as built + * from `packages`, the pod's packages with it, so the next sync builds + * nothing. + */ +export const installSoftware = ( + sandbox: Sandboxes.Sandbox, + software: SandboxSoftware.Package, + packages: readonly SandboxSoftware.Package[], +) => + sandbox.exec( + [ + `nix profile install --profile ${SOFTWARE_PROFILE} ${shellQuoted(flakeRef(software))}`, + `printf '%s' ${shellQuoted(builtFrom(packages))} > ${BUILT_FROM}`, + ].join(" && "), + INSTALL, + ); diff --git a/packages/core/src/conversations/tools/software/tool.ts b/packages/core/src/conversations/tools/software/tool.ts new file mode 100644 index 00000000..44392db3 --- /dev/null +++ b/packages/core/src/conversations/tools/software/tool.ts @@ -0,0 +1,78 @@ +import { sandboxPackageNameSchema, sandboxSoftwareChannelSchema } from "@sugabots/contracts"; +import { tool } from "ai"; +import { Effect, Schema } from "effect"; +import type { RunEffect } from "../../../database/database.ts"; +import type { SandboxSoftware } from "../../../sandboxes/sandbox-software.ts"; +import type { OpenSandbox } from "../sandbox/tools.ts"; +import type { Request } from "../sandbox.ts"; +import { currentNixpkgs, installSoftware, NIXPKGS } from "./profile.ts"; + +export const REQUEST_SOFTWARE_TOOL = "request_software"; + +/** + * The `request_software` tool: an agent asking for a package from nixpkgs to + * be installed in its pod's sandbox for good. Every call waits for someone + * who manages the pod's sandbox to allow it (see + * `tools/approval-deciders.ts`), so the tool only runs once they have; it + * then installs the package, at the nixpkgs commit its channel is at, and + * records it for every sandbox the pod has after. + */ +export function requestSoftware({ + turnId, + software, + openSandbox, + run, +}: { + turnId: string; + software: Pick; + openSandbox: OpenSandbox; + run: RunEffect; +}): Request { + return { + tool: tool({ + description: `Ask for a package from nixpkgs to be installed in the pod's sandbox for good, for every agent in the pod. An admin of this pod decides, and your reply waits until they have; once allowed, it's installed, on the PATH of every command, and given to every sandbox the pod has from then on. For software needed only now, run "nix shell nixpkgs# -c " instead, which needs no one's say. Find a package's name with "nix search nixpkgs ". Packages come from a NixOS release unless you ask for "unstable", which has newer versions.`, + inputSchema: Schema.Struct({ + name: sandboxPackageNameSchema.annotate({ + description: "The package's name in nixpkgs, such as ffmpeg or python3Packages.pandas", + }), + channel: Schema.optional(sandboxSoftwareChannelSchema).annotate({ + description: `"stable", the default, for a NixOS release; "unstable" for newer versions`, + }), + }).pipe(Schema.toStandardSchemaV1, Schema.toStandardJSONSchemaV1), + execute: async ({ name, channel = "stable" }, { toolCallId }) => { + const request = await run(software.allowedRequest({ turnId, sdkToolCallId: toolCallId })); + if (!request) return { status: "failed", error: "The request wasn't allowed." }; + const { sandbox } = await openSandbox(); + const nixpkgsRev = await Effect.runPromise(currentNixpkgs(sandbox, channel)); + if (!nixpkgsRev) { + return { + status: "failed", + error: `Couldn't reach ${NIXPKGS[channel]} to install from. Try again shortly.`, + }; + } + const wanted = { name, channel, nixpkgsRev }; + const kept = await run(software.packagesOf(request.pod)); + const installed = await Effect.runPromise( + installSoftware(sandbox, wanted, [ + ...kept.filter((other) => other.name !== name || other.channel !== channel), + wanted, + ]), + ); + if (installed.exitCode !== 0) { + return { + status: "failed", + error: `Couldn't install ${name}: ${installed.stderr.text.trim().split("\n").at(-1) ?? "Nix failed"}`, + }; + } + await run(software.add(request, wanted)); + return { + status: "installed", + name, + channel, + note: "It's on the PATH of every command in the pod's sandbox now: run it by name.", + }; + }, + }), + refusal: () => undefined, + }; +} diff --git a/packages/core/src/sandboxes/sandbox-software.test.ts b/packages/core/src/sandboxes/sandbox-software.test.ts new file mode 100644 index 00000000..d693b088 --- /dev/null +++ b/packages/core/src/sandboxes/sandbox-software.test.ts @@ -0,0 +1,115 @@ +import { Effect, Exit } from "effect"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import { CurrentActor } from "../authorization/current-actor.ts"; +import { + pod, + podMember, + sandboxPodPackage, + user, + workspace, + workspaceMember, +} from "../database/schema.ts"; +import { closeDatabase, onDatabase, runOnPostgres } from "../database/testing.ts"; +import { SandboxSoftware } from "./sandbox-software.ts"; + +/** A pod's software as people see and change it, against Postgres. */ +describe.skipIf(!process.env.DATABASE_URL)("sandbox software, against Postgres", () => { + let software: SandboxSoftware.Interface; + let podId: string; + let adminId: string; + let memberId: string; + + beforeAll(async () => { + software = await runOnPostgres(Effect.provide(SandboxSoftware.Service, SandboxSoftware.layer)); + const suffix = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; + const [space] = await onDatabase((db) => + db + .insert(workspace) + .values({ name: `Software ${suffix}`, slug: `software-${suffix}` }) + .returning(), + ); + const people = await onDatabase((db) => + db + .insert(user) + .values([ + { name: "Ada", email: `software-ada-${suffix}@example.com` }, + { name: "Kim", email: `software-kim-${suffix}@example.com` }, + ]) + .returning(), + ); + const [admin, member] = people; + if (!space || !admin || !member) throw new Error("fixture"); + adminId = admin.id; + memberId = member.id; + await onDatabase((db) => + db.insert(workspaceMember).values([ + { workspaceId: space.id, userId: adminId, role: "admin" }, + { workspaceId: space.id, userId: memberId, role: "member" }, + ]), + ); + const [shared] = await onDatabase((db) => + db + .insert(pod) + .values({ + workspaceId: space.id, + ownerId: adminId, + kind: "shared", + name: "Builders", + slug: `builders-${suffix}`, + createdById: adminId, + }) + .returning(), + ); + if (!shared) throw new Error("fixture"); + podId = shared.id; + await onDatabase((db) => + db.insert(podMember).values({ workspaceId: space.id, podId, userId: memberId }), + ); + await onDatabase((db) => + db.insert(sandboxPodPackage).values( + ["ffmpeg", "jq"].map((name) => ({ + workspaceId: space.id, + podId, + name, + channel: "stable" as const, + nixpkgsRev: "0d9e9b832d03ac387417e16ce1febf73b2e631e1", + addedById: adminId, + })), + ), + ); + }); + + afterAll(closeDatabase); + + const as = (userId: string, effect: Effect.Effect) => + runOnPostgres( + Effect.exit( + effect.pipe(CurrentActor.provide(CurrentActor.AuthenticatedUserId.vouchedFor(userId))), + ), + ); + + it("shows anyone in the pod its software, and who allowed each package", async () => { + const exit = await as(memberId, software.podSoftware(podId)); + + if (!Exit.isSuccess(exit)) throw new Error(exit.toString()); + expect(exit.value.packages.map(({ name, addedByName }) => [name, addedByName])).toEqual([ + ["ffmpeg", "Ada"], + ["jq", "Ada"], + ]); + }); + + it("lets only someone who manages the pod's sandbox remove a package", async () => { + const refused = await as( + memberId, + software.removePackage({ podId, name: "jq", channel: "stable" }), + ); + const removed = await as( + adminId, + software.removePackage({ podId, name: "jq", channel: "stable" }), + ); + + expect(Exit.isFailure(refused) && refused.toString()).toContain("ActionForbidden"); + if (!Exit.isSuccess(removed)) throw new Error(removed.toString()); + expect(removed.value.packages.map(({ name }) => name)).toEqual(["ffmpeg"]); + }); +}); diff --git a/packages/core/src/sandboxes/sandbox-software.ts b/packages/core/src/sandboxes/sandbox-software.ts new file mode 100644 index 00000000..a85ee0f2 --- /dev/null +++ b/packages/core/src/sandboxes/sandbox-software.ts @@ -0,0 +1,201 @@ +export * as SandboxSoftware from "./sandbox-software.ts"; + +import type { PodSandboxSoftware, SandboxSoftwareChannel } from "@sugabots/contracts"; +import { and, asc, eq, inArray } from "drizzle-orm"; +import { Context, Effect, Layer } from "effect"; +import type { AuthorizationDenied } from "../authorization/access.ts"; +import { Authorization } from "../authorization/authorization.ts"; +import type { CurrentActor } from "../authorization/current-actor.ts"; +import { query, serviceOperations } from "../database/database.ts"; +import { sandboxPodPackage, thread, toolCall, user } from "../database/schema.ts"; + +/** + * The software each pod's sandbox has beyond its image: packages from + * nixpkgs, each at the nixpkgs commit it was installed from. An agent asks + * for one and someone who manages the pod's sandbox allows it; every sandbox + * the pod has is given the same packages, so a new one gets them back. + * + * Seeing a pod's software takes `pod.read`; removing a package takes + * `sandbox.manage`. + */ +export interface Interface { + readonly podSoftware: ( + podId: string, + ) => Effect.Effect; + /** Removing a package the pod doesn't have changes nothing. */ + readonly removePackage: (input: { + podId: string; + name: string; + channel: SandboxSoftwareChannel; + }) => Effect.Effect; + /** The pod's packages, to give its sandbox. */ + readonly packagesOf: (pod: Pod) => Effect.Effect; + /** + * The request the turn's call `sdkToolCallId` made for software, if a + * person allowed it: what {@link add} records the package by. + */ + readonly allowedRequest: (input: { + turnId: string; + sdkToolCallId: string; + }) => Effect.Effect; + /** Records a package the request's pod now has, installed from `nixpkgsRev`. */ + readonly add: (request: AllowedRequest, installed: Package) => Effect.Effect; +} + +export class Service extends Context.Service()( + "@sugabots/core/SandboxSoftware", +) {} + +export const make = Effect.gen(function* () { + const operation = yield* serviceOperations("SandboxSoftware"); + const authorization = yield* Authorization.Service; + + const rowsOf = (pod: Pod) => + query((db) => + db + .select() + .from(sandboxPodPackage) + .where( + and( + eq(sandboxPodPackage.workspaceId, pod.workspaceId), + eq(sandboxPodPackage.podId, pod.podId), + ), + ) + .orderBy(asc(sandboxPodPackage.name), asc(sandboxPodPackage.channel)), + ); + + const softwareOf = (pod: Pod) => + Effect.gen(function* () { + const rows = yield* rowsOf(pod); + const adderIds = [...new Set(rows.flatMap((row) => (row.addedById ? [row.addedById] : [])))]; + const adders = + adderIds.length === 0 + ? [] + : yield* query((db) => + db + .select({ id: user.id, name: user.name }) + .from(user) + .where(inArray(user.id, adderIds)), + ); + const names = new Map(adders.map((adder) => [adder.id, adder.name])); + return { + packages: rows.map((row) => ({ + name: row.name, + channel: row.channel, + nixpkgsRev: row.nixpkgsRev, + addedByName: (row.addedById && names.get(row.addedById)) ?? null, + addedAt: row.createdAt.toISOString(), + })), + } satisfies PodSandboxSoftware; + }); + + const podOf = (standing: { pod: { id: string; workspaceId: string } }): Pod => ({ + workspaceId: standing.pod.workspaceId, + podId: standing.pod.id, + }); + + return Service.of({ + podSoftware: (podId) => + operation( + "podSoftware", + Effect.flatMap(authorization.pod(podId, "pod.read"), (standing) => + softwareOf(podOf(standing)), + ), + ), + + removePackage: ({ podId, name, channel }) => + operation( + "removePackage", + Effect.gen(function* () { + const pod = podOf(yield* authorization.pod(podId, "sandbox.manage")); + yield* query((db) => + db + .delete(sandboxPodPackage) + .where( + and( + eq(sandboxPodPackage.podId, pod.podId), + eq(sandboxPodPackage.channel, channel), + eq(sandboxPodPackage.name, name), + ), + ), + ); + return yield* softwareOf(pod); + }), + ), + + packagesOf: (pod) => + operation( + "packagesOf", + Effect.map(rowsOf(pod), (rows) => + rows.map(({ name, channel, nixpkgsRev }) => ({ name, channel, nixpkgsRev })), + ), + ), + + allowedRequest: ({ turnId, sdkToolCallId }) => + operation( + "allowedRequest", + Effect.gen(function* () { + const [call] = yield* query((db) => + db + .select({ + workspaceId: thread.workspaceId, + podId: thread.podId, + approvalStatus: toolCall.approvalStatus, + decidedById: toolCall.decidedById, + }) + .from(toolCall) + .innerJoin(thread, eq(thread.id, toolCall.threadId)) + .where(and(eq(toolCall.turnId, turnId), eq(toolCall.sdkToolCallId, sdkToolCallId))) + .limit(1), + ); + if (call?.approvalStatus !== "allowed") return undefined; + return allowed({ + pod: { workspaceId: call.workspaceId, podId: call.podId }, + decidedById: call.decidedById, + }); + }), + ), + + add: (request, installed) => + operation( + "add", + query((db) => + db + .insert(sandboxPodPackage) + .values({ ...request.pod, ...installed, addedById: request.decidedById }) + .onConflictDoUpdate({ + target: [sandboxPodPackage.podId, sandboxPodPackage.channel, sandboxPodPackage.name], + set: { nixpkgsRev: installed.nixpkgsRev, addedById: request.decidedById }, + }), + ).pipe(Effect.asVoid), + ), + }); +}); + +export const layerNoDeps = Layer.effect(Service, make); + +export const layer = layerNoDeps.pipe(Layer.provide(Authorization.layer)); + +export interface Pod { + readonly workspaceId: string; + readonly podId: string; +} + +/** A package the pod's sandbox has: its attribute in nixpkgs, and the build it comes from. */ +export interface Package { + readonly name: string; + readonly channel: SandboxSoftwareChannel; + readonly nixpkgsRev: string; +} + +declare const allowedBrand: unique symbol; + +/** A request for software a person allowed, as only {@link Interface.allowedRequest} finds one. */ +export interface AllowedRequest { + readonly pod: Pod; + /** Who allowed it; null once they've left. */ + readonly decidedById: string | null; + readonly [allowedBrand]: true; +} + +const allowed = (request: Omit) => request as AllowedRequest; diff --git a/packages/core/src/sandboxes/sql.ts b/packages/core/src/sandboxes/sql.ts index 01a625ec..62dabe32 100644 --- a/packages/core/src/sandboxes/sql.ts +++ b/packages/core/src/sandboxes/sql.ts @@ -177,3 +177,34 @@ export const sandboxBlockedHost = pgTable( }, (table) => [compositePrimaryKey({ columns: [table.workspaceId, table.host] })], ); + +/** + * A package from nixpkgs the pod's sandbox has, from a NixOS release or from + * unstable, at the nixpkgs commit it was first installed from. Every sandbox + * the pod has is given the same build, so a new one gets the pod's software + * back. Added by allowing an agent's request for it. + */ +export const sandboxPodPackage = pgTable( + "sandbox_pod_package", + { + workspaceId: uuid("workspace_id").notNull(), + podId: uuid("pod_id").notNull(), + /** Its attribute in nixpkgs, such as `ffmpeg` or `python3Packages.pandas`. */ + name: text("name").notNull(), + channel: text("channel").$type<"stable" | "unstable">().notNull(), + /** The nixpkgs commit it is built from. */ + nixpkgsRev: text("nixpkgs_rev").notNull(), + /** Who allowed the request for it. */ + addedById: uuid("added_by_id").references(() => user.id, { onDelete: "set null" }), + createdAt: stamp("created_at"), + }, + (table) => [ + compositePrimaryKey({ columns: [table.podId, table.channel, table.name] }), + foreignKey({ + columns: [table.podId, table.workspaceId], + foreignColumns: [pod.id, pod.workspaceId], + name: "sandbox_pod_package_pod_workspace_fkey", + }).onDelete("cascade"), + check("sandbox_pod_package_channel_check", sql`${table.channel} in ('stable', 'unstable')`), + ], +); diff --git a/packages/server/src/http/app.test-support.ts b/packages/server/src/http/app.test-support.ts index f8f9a1b3..d2c14cb7 100644 --- a/packages/server/src/http/app.test-support.ts +++ b/packages/server/src/http/app.test-support.ts @@ -19,6 +19,7 @@ import { SearchProviderSetup } from "@sugabots/core/providers/search-providers/s import { PodSandboxSetup } from "@sugabots/core/sandboxes/pod-sandbox-setup"; import { SandboxNetwork } from "@sugabots/core/sandboxes/sandbox-network"; import { SandboxProviderSetup } from "@sugabots/core/sandboxes/sandbox-provider-setup"; +import { SandboxSoftware } from "@sugabots/core/sandboxes/sandbox-software"; import { unimplemented } from "@sugabots/core/testing"; import { AgentAdministration } from "@sugabots/core/workspaces/agents/agent-administration"; import { Membership } from "@sugabots/core/workspaces/membership/membership"; @@ -109,6 +110,7 @@ const fakes: Layer.Layer = Layer.mergeAll( unimplemented(SearchProviderSetup.Service), unimplemented(SandboxProviderSetup.Service), unimplemented(SandboxNetwork.Service), + unimplemented(SandboxSoftware.Service), unimplemented(DesktopViewer.Service), unimplemented(PodSandboxSetup.Service), unimplemented(ConnectionSetup.Service), diff --git a/packages/server/src/http/services.ts b/packages/server/src/http/services.ts index e1c85eea..9ac21b19 100644 --- a/packages/server/src/http/services.ts +++ b/packages/server/src/http/services.ts @@ -13,6 +13,7 @@ import type { SearchProviderSetup } from "@sugabots/core/providers/search-provid import type { PodSandboxSetup } from "@sugabots/core/sandboxes/pod-sandbox-setup"; import type { SandboxNetwork } from "@sugabots/core/sandboxes/sandbox-network"; import type { SandboxProviderSetup } from "@sugabots/core/sandboxes/sandbox-provider-setup"; +import type { SandboxSoftware } from "@sugabots/core/sandboxes/sandbox-software"; import type { AgentAdministration } from "@sugabots/core/workspaces/agents/agent-administration"; import type { Membership } from "@sugabots/core/workspaces/membership/membership"; import type { Onboarding } from "@sugabots/core/workspaces/onboarding/onboarding"; @@ -41,6 +42,7 @@ export type HttpServices = | SearchProviderSetup.Service | SandboxProviderSetup.Service | SandboxNetwork.Service + | SandboxSoftware.Service | DesktopViewer.Service | PodSandboxSetup.Service | ConnectionSetup.Service diff --git a/packages/server/src/http/session.test.ts b/packages/server/src/http/session.test.ts index be61a2f4..ae690cfd 100644 --- a/packages/server/src/http/session.test.ts +++ b/packages/server/src/http/session.test.ts @@ -35,6 +35,8 @@ const PLACEHOLDERS: Record = { memberId: "0199a3a0-0000-7000-8000-00000000000c", invitationId: "0199a3a0-0000-7000-8000-00000000000d", host: "api.example.com", + channel: "stable", + name: "ffmpeg", }; function fill(pattern: string): string { diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 29ee69d8..0e865cb6 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -30,6 +30,7 @@ import { PodSandboxSetup } from "@sugabots/core/sandboxes/pod-sandbox-setup"; import { PodSandboxes } from "@sugabots/core/sandboxes/pod-sandboxes"; import { SandboxNetwork } from "@sugabots/core/sandboxes/sandbox-network"; import { SandboxProviderSetup } from "@sugabots/core/sandboxes/sandbox-provider-setup"; +import { SandboxSoftware } from "@sugabots/core/sandboxes/sandbox-software"; import { AgentAdministration } from "@sugabots/core/workspaces/agents/agent-administration"; import { Membership } from "@sugabots/core/workspaces/membership/membership"; import { Onboarding } from "@sugabots/core/workspaces/onboarding/onboarding"; @@ -89,6 +90,7 @@ const WorkspacesAndProviders = Layer.mergeAll( SearchProviderSetup.layer, SandboxProviderSetup.layer, SandboxNetwork.layer, + SandboxSoftware.layer, PodSandboxSetup.layer, ConnectionSetup.layer, ModelTrials.layer, diff --git a/packages/server/src/routes/pod-sandbox/routes.ts b/packages/server/src/routes/pod-sandbox/routes.ts index 9e42ccd2..082ed8c4 100644 --- a/packages/server/src/routes/pod-sandbox/routes.ts +++ b/packages/server/src/routes/pod-sandbox/routes.ts @@ -1,6 +1,7 @@ import { BadRequest, Conflict, NotFound } from "@sugabots/contracts/http"; import { PodSandboxSetup } from "@sugabots/core/sandboxes/pod-sandbox-setup"; import { SandboxNetwork } from "@sugabots/core/sandboxes/sandbox-network"; +import { SandboxSoftware } from "@sugabots/core/sandboxes/sandbox-software"; import { Effect } from "effect"; import { HttpApiBuilder } from "effect/unstable/httpapi"; import { asSessionUser } from "../../auth/middleware.ts"; @@ -11,6 +12,7 @@ export const podSandboxRoutes = HttpApiBuilder.group(ServerApi, "podSandbox", (h Effect.gen(function* () { const setup = yield* PodSandboxSetup.Service; const network = yield* SandboxNetwork.Service; + const software = yield* SandboxSoftware.Service; return handlers .handle("get", ({ params }) => setup.get(params.podId).pipe(asSessionUser, asHttpError(podSandboxErrors)), @@ -33,6 +35,14 @@ export const podSandboxRoutes = HttpApiBuilder.group(ServerApi, "podSandbox", (h network .removePodHost({ podId: params.podId, host: params.host }) .pipe(asSessionUser, asHttpError(refusals)), + ) + .handle("software", ({ params }) => + software.podSoftware(params.podId).pipe(asSessionUser, asHttpError(refusals)), + ) + .handle("removePackage", ({ params }) => + software + .removePackage({ podId: params.podId, name: params.name, channel: params.channel }) + .pipe(asSessionUser, asHttpError(refusals)), ); }), ); diff --git a/packages/web/src/lib/pod-sandbox.ts b/packages/web/src/lib/pod-sandbox.ts index 0d702b58..12edf024 100644 --- a/packages/web/src/lib/pod-sandbox.ts +++ b/packages/web/src/lib/pod-sandbox.ts @@ -1,3 +1,4 @@ +import type { SandboxSoftwareChannel } from "@sugabots/contracts"; import { skipToken, useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { Effect } from "effect"; import { client } from "@/api.ts"; @@ -55,3 +56,21 @@ export function usePodSandboxNetwork(podId: string) { }), }; } + +/** The software the pod's sandbox has beyond its image, and removing a package. */ +export function usePodSandboxSoftware(podId: string) { + const queryClient = useQueryClient(); + const queryKey = ["pod-sandbox-software", podId]; + return { + software: useQuery({ + queryKey, + queryFn: ({ signal }) => + Effect.runPromise(client.api.podSandbox.software({ params: { podId } }), { signal }), + }), + removePackage: useMutation({ + mutationFn: (software: { name: string; channel: SandboxSoftwareChannel }) => + Effect.runPromise(client.api.podSandbox.removePackage({ params: { podId, ...software } })), + onSuccess: (data) => queryClient.setQueryData(queryKey, data), + }), + }; +} diff --git a/packages/web/src/lib/tool-names.ts b/packages/web/src/lib/tool-names.ts index 942c8ad6..421d2c08 100644 --- a/packages/web/src/lib/tool-names.ts +++ b/packages/web/src/lib/tool-names.ts @@ -11,6 +11,9 @@ export const BUILT_IN_HANDLE = ""; /** The built-in tool an agent asks with for its sandbox to reach another host. */ export const NETWORK_REQUEST_TOOL = "request_network_access"; +/** The built-in tool an agent asks with for software to be installed in its sandbox for good. */ +export const SOFTWARE_REQUEST_TOOL = "request_software"; + /** * The browser tools' names start with this. They are the product's own, all * one app, the agent's browser, and each step is named by what it does. diff --git a/packages/web/src/screens/PodSandboxSettings.stories.tsx b/packages/web/src/screens/PodSandboxSettings.stories.tsx index 767c3229..dac2d442 100644 --- a/packages/web/src/screens/PodSandboxSettings.stories.tsx +++ b/packages/web/src/screens/PodSandboxSettings.stories.tsx @@ -66,7 +66,7 @@ const meta = preview.meta({ title: "Views/PodSandboxSettings", component: PodSandboxSettings, tags: ["ai-generated"], - args: { podId, canManageNetwork: true }, + args: { podId, canManageSandbox: true }, parameters: { layout: "fullscreen", docs: { story: { inline: false, height: "520px" } } }, decorators: [ (Story, context) => ( @@ -79,6 +79,7 @@ const meta = preview.meta({ msw.use( answers(running), http.get(`${sandboxUrl}/network`, () => HttpResponse.json(network)), + http.get(`${sandboxUrl}/software`, () => HttpResponse.json({ packages: [] })), http.post(`${sandboxUrl}/*`, () => HttpResponse.json( { _tag: "InternalServerError", message: "This preview does not change sandboxes." }, @@ -144,7 +145,7 @@ export const NoneYet = meta.story({ /** Someone who can see the pod but not change it sees how it stands, without the actions. */ export const AsAMember = meta.story({ - args: { canManageNetwork: false }, + args: { canManageSandbox: false }, beforeEach({ msw }) { msw.use(answers({ ...running, canManage: false })); }, diff --git a/packages/web/src/screens/PodSandboxSettings.tsx b/packages/web/src/screens/PodSandboxSettings.tsx index 440fc24c..2db48df9 100644 --- a/packages/web/src/screens/PodSandboxSettings.tsx +++ b/packages/web/src/screens/PodSandboxSettings.tsx @@ -7,6 +7,7 @@ import { Alert } from "@/ui/alert.tsx"; import { DeleteDialog } from "@/ui/delete-dialog.tsx"; import { SettingsGroup, SettingsRow, SettingsValue } from "@/ui/settings-page.tsx"; import { PodSandboxNetworkSettings } from "./PodSandboxNetwork.tsx"; +import { PodSandboxSoftwareSettings } from "./PodSandboxSoftware.tsx"; /* * The pod's sandbox, experimental: how it stands for anyone who can see the @@ -16,11 +17,11 @@ import { PodSandboxNetworkSettings } from "./PodSandboxNetwork.tsx"; */ export function PodSandboxSettings({ podId, - canManageNetwork, + canManageSandbox, }: { podId: string; - /** Whether the person may change the hosts the pod's sandbox reaches. */ - canManageNetwork: boolean; + /** Whether the person may change what the pod's sandbox reaches and has installed. */ + canManageSandbox: boolean; }) { const sandbox = usePodSandbox(podId); if (sandbox.isPending) return null; @@ -30,7 +31,8 @@ export function PodSandboxSettings({ return ( <> - + + ); } diff --git a/packages/web/src/screens/PodSandboxSoftware.stories.tsx b/packages/web/src/screens/PodSandboxSoftware.stories.tsx new file mode 100644 index 00000000..f2aa163a --- /dev/null +++ b/packages/web/src/screens/PodSandboxSoftware.stories.tsx @@ -0,0 +1,68 @@ +import type { PodSandboxSoftware } from "@sugabots/contracts"; +import { expect, fn } from "storybook/test"; +import preview from "#storybook/preview"; +import { SettingsPage } from "@/ui/settings-page.tsx"; +import { SoftwareGroup } from "./PodSandboxSoftware.tsx"; + +const software: PodSandboxSoftware = { + packages: [ + { + name: "ffmpeg", + channel: "stable", + nixpkgsRev: "0d9e9b832d03ac387417e16ce1febf73b2e631e1", + addedByName: "Sam Rivera", + addedAt: "2026-10-05T09:30:00.000Z", + }, + { + name: "terraform", + channel: "unstable", + nixpkgsRev: "494ce7fd23ff6a5dff39e1fb11e9b6f2ac74bf25", + addedByName: null, + addedAt: "2026-10-06T01:10:00.000Z", + }, + ], +}; + +const meta = preview.meta({ + title: "Views/PodSandboxSoftware", + component: SoftwareGroup, + tags: ["ai-generated"], + parameters: { layout: "fullscreen" }, + args: { software, canManage: true, pending: false, onRemove: fn() }, + decorators: [ + (Story) => ( +
+ + + +
+ ), + ], +}); + +/** The pod's software, one package from unstable, to someone who may remove them. */ +export const Packages = meta.story({ + play: async ({ canvas, args, userEvent }) => { + await expect(canvas.getByText("Allowed by Sam Rivera")).toBeVisible(); + await expect(canvas.getByText("Unstable")).toBeVisible(); + await userEvent.click(canvas.getByRole("button", { name: "Remove ffmpeg" })); + await expect(args.onRemove).toHaveBeenCalledWith("ffmpeg", "stable"); + }, +}); + +/** Someone who may not change the pod's sandbox sees its software without removing any. */ +export const ReadOnly = meta.story({ + args: { canManage: false }, + play: async ({ canvas }) => { + await expect(canvas.getByText("ffmpeg")).toBeVisible(); + await expect(canvas.queryByRole("button", { name: "Remove ffmpeg" })).toBeNull(); + }, +}); + +/** Nothing installed beyond the image yet. */ +export const None = meta.story({ + args: { software: { packages: [] } }, + play: async ({ canvas }) => { + await expect(canvas.getByText("None yet")).toBeVisible(); + }, +}); diff --git a/packages/web/src/screens/PodSandboxSoftware.tsx b/packages/web/src/screens/PodSandboxSoftware.tsx new file mode 100644 index 00000000..fff945ee --- /dev/null +++ b/packages/web/src/screens/PodSandboxSoftware.tsx @@ -0,0 +1,85 @@ +import type { PodSandboxSoftware } from "@sugabots/contracts"; +import { X } from "lucide-react"; +import { failureMessage } from "@/lib/failure.ts"; +import { usePodSandboxSoftware } from "@/lib/pod-sandbox.ts"; +import { Alert } from "@/ui/alert.tsx"; +import { IconButton } from "@/ui/icon-button.tsx"; +import { SettingsGroup, SettingsRow, SettingsValue } from "@/ui/settings-page.tsx"; + +/** + * The software the pod's sandbox has beyond its image: packages from nixpkgs + * its agents asked for and someone allowed, which every sandbox the pod has + * is given. `canManage` is whether the person may remove them. + */ +export function PodSandboxSoftwareSettings({ + podId, + canManage, +}: { + podId: string; + canManage: boolean; +}) { + const { software, removePackage } = usePodSandboxSoftware(podId); + if (software.isPending) return null; + if (software.isError) return {failureMessage(software.error)}; + return ( + removePackage.mutate({ name, channel })} + /> + ); +} + +export function SoftwareGroup({ + software, + canManage, + pending, + error, + onRemove, +}: { + software: PodSandboxSoftware; + canManage: boolean; + pending: boolean; + error?: unknown; + onRemove: (name: string, channel: PodSandboxSoftware["packages"][number]["channel"]) => void; +}) { + return ( + <> + + {software.packages.length === 0 && ( + + )} + {software.packages.map((item) => ( + {item.name}} + sub={`Allowed by ${item.addedByName ?? "someone who has left"}`} + trailing={ + + {item.channel === "unstable" && Unstable} + {canManage && ( + onRemove(item.name, item.channel)} + > + + + )} + + } + /> + ))} + + {error !== undefined && {failureMessage(error)}} + + ); +} diff --git a/packages/web/src/screens/ToolApprovalCard.stories.tsx b/packages/web/src/screens/ToolApprovalCard.stories.tsx index 33936caf..ff84a29f 100644 --- a/packages/web/src/screens/ToolApprovalCard.stories.tsx +++ b/packages/web/src/screens/ToolApprovalCard.stories.tsx @@ -150,6 +150,23 @@ export const ANetworkRequest = meta.story({ }, }); +/** ASoftwareRequest is an agent asking for a package to be installed in its pod's sandbox, which an admin of the pod answers. */ +export const ASoftwareRequest = meta.story({ + args: { + ...ANetworkRequest.input.args, + call: { + ...ANetworkRequest.input.args.call, + tool: "request_software", + input: { name: "ffmpeg" }, + }, + }, + play: async ({ canvas }) => { + await expect( + canvas.getByText("Trip Planner wants to install software in its sandbox"), + ).toBeInTheDocument(); + }, +}); + /** ANetworkRequestNotYours is that request seen by someone who doesn't decide the pod's network access. */ export const ANetworkRequestNotYours = meta.story({ args: { ...ANetworkRequest.input.args, canApprove: false }, diff --git a/packages/web/src/screens/ToolApprovalCard.tsx b/packages/web/src/screens/ToolApprovalCard.tsx index 86415d6e..95a04eae 100644 --- a/packages/web/src/screens/ToolApprovalCard.tsx +++ b/packages/web/src/screens/ToolApprovalCard.tsx @@ -1,7 +1,7 @@ import { botColorVariables } from "@sugabots/avatars"; import type { ThreadParticipant, ToolCallPart } from "@sugabots/contracts"; import { cn } from "cn"; -import { ChevronDown, Globe, X } from "lucide-react"; +import { ChevronDown, Globe, type LucideIcon, Package, X } from "lucide-react"; import { Fragment, type ReactNode, useId, useLayoutEffect, useRef, useState } from "react"; import type { ConnectionLook } from "@/lib/connections.ts"; import { failureMessage } from "@/lib/failure.ts"; @@ -10,6 +10,7 @@ import { awaitedDeciders } from "@/lib/tool-approvals.ts"; import { connectionLabel, NETWORK_REQUEST_TOOL, + SOFTWARE_REQUEST_TOOL, splitToolKey, stepLabel, wordsFromKey, @@ -68,15 +69,15 @@ export function ToolApprovalCard({ const { handle, name } = splitToolKey(call.tool); const where = handle ? connectionLabel(handle, look?.name) : ""; const action = stepLabel(call.tool, name); - const networkRequest = call.tool === NETWORK_REQUEST_TOOL; + const sandboxRequest = SANDBOX_REQUESTS[call.tool]; const step: Step = { action, - who: networkRequest - ? `${agent.name} wants its sandbox to reach a new host` + who: sandboxRequest + ? `${agent.name} ${sandboxRequest.wants}` : `${agent.name} wants to use ${where || "a tool"}`, where, look, - networkRequest, + icon: sandboxRequest?.icon, }; const answerable = awaitsApproval(call) && canApprove; // The answer stays given from when it is sent until the thread's events say @@ -174,18 +175,25 @@ interface Step { who: string; where: string; look: ConnectionLook | undefined; - /** A request for the sandbox to reach another host, which no connection makes. */ - networkRequest: boolean; + /** Drawn in place of a connection's mark, for a request no connection makes. */ + icon: LucideIcon | undefined; } +/** The requests to change the agent's sandbox: what each asks, and its icon. */ +const SANDBOX_REQUESTS: Readonly> = { + [NETWORK_REQUEST_TOOL]: { wants: "wants its sandbox to reach a new host", icon: Globe }, + [SOFTWARE_REQUEST_TOOL]: { wants: "wants to install software in its sandbox", icon: Package }, +}; + const MARK_SIZES = { md: { box: "size-6 rounded-[7px]", icon: 14 }, sm: { box: "size-8 rounded-lg", icon: 16 }, } as const; -/** What the step uses: the connection's mark, or a globe for a network request. */ +/** What the step uses: the connection's mark, or the icon of a sandbox request. */ function StepMark({ step, size }: { step: Step; size: keyof typeof MARK_SIZES }) { - if (!step.networkRequest) { + const Icon = step.icon; + if (!Icon) { return ( ); @@ -196,7 +204,7 @@ function StepMark({ step, size }: { step: Step; size: keyof typeof MARK_SIZES }) aria-hidden className={cn("grid shrink-0 place-items-center bg-border-strong text-foreground", box)} > - + ); } diff --git a/packages/web/src/screens/WorkspacePodsSettings.tsx b/packages/web/src/screens/WorkspacePodsSettings.tsx index 17e6bce3..7fcb9bf8 100644 --- a/packages/web/src/screens/WorkspacePodsSettings.tsx +++ b/packages/web/src/screens/WorkspacePodsSettings.tsx @@ -276,7 +276,7 @@ function PodDetails({ canManage={may.manageConnections} signInError={connectionSignInError} /> - + {shared && } {shared && may.rename && (