diff --git a/packages/contracts/src/http/groups/onboarding.ts b/packages/contracts/src/http/groups/onboarding.ts index bbceb887..f1baecf5 100644 --- a/packages/contracts/src/http/groups/onboarding.ts +++ b/packages/contracts/src/http/groups/onboarding.ts @@ -3,20 +3,15 @@ import { completedInviteOnboardingSchema, completeInviteOnboardingSchema, completeOnboardingSchema, - onboardingStatusSchema, } from "../../onboarding.ts"; import { refused } from "../errors.ts"; import { Session } from "../middleware.ts"; -/** Whether the signed-in person has finished setting up, and marking that they have. */ +/** The signed-in person finishing setting up a workspace, or joining one by invitation. */ export class OnboardingApi extends HttpApiGroup.make("onboarding") .add( - HttpApiEndpoint.get("status", "/onboarding", { - success: onboardingStatusSchema, - }), HttpApiEndpoint.post("complete", "/onboarding/complete", { payload: completeOnboardingSchema, - success: onboardingStatusSchema, error: refused, }), HttpApiEndpoint.post("completeInvite", "/onboarding/complete-invite", { diff --git a/packages/contracts/src/me.ts b/packages/contracts/src/me.ts index 94e0d1e9..eba0115e 100644 --- a/packages/contracts/src/me.ts +++ b/packages/contracts/src/me.ts @@ -1,17 +1,14 @@ import { Schema } from "effect"; import { sessionUserSchema } from "./api.ts"; import { workspaceSchema } from "./membership.ts"; -import { onboardingStatusSchema } from "./onboarding.ts"; /** - * Who the caller is, whether they have finished setting up, and their - * workspaces: what the web app needs before it can draw anything, in one - * answer rather than three requests, the second and third of which would wait - * for the first to say they are signed in. + * Who the caller is and their workspaces: what the web app needs before it can + * draw anything, in one answer rather than two requests, the second of which + * would wait for the first to say they are signed in. */ export const meSchema = Schema.Struct({ user: sessionUserSchema, - onboarding: onboardingStatusSchema, workspaces: Schema.Array(workspaceSchema), }); diff --git a/packages/contracts/src/membership.ts b/packages/contracts/src/membership.ts index 0455c41a..0e0d0daa 100644 --- a/packages/contracts/src/membership.ts +++ b/packages/contracts/src/membership.ts @@ -21,6 +21,8 @@ export const workspaceSchema = Schema.Struct({ * know must not stop the workspace loading. */ timeZone: Schema.String, + /** When its setup was finished, or null while it is still being set up. */ + setupCompletedAt: Schema.NullOr(isoTimestampSchema), }); export type Workspace = typeof workspaceSchema.Type; diff --git a/packages/contracts/src/onboarding.ts b/packages/contracts/src/onboarding.ts index 559a9fcb..4fb20f19 100644 --- a/packages/contracts/src/onboarding.ts +++ b/packages/contracts/src/onboarding.ts @@ -1,9 +1,6 @@ import { Schema } from "effect"; import { uuidSchema } from "./uuid.ts"; -export const onboardingStatusSchema = Schema.Struct({ completed: Schema.Boolean }); -export type OnboardingStatus = typeof onboardingStatusSchema.Type; - export const completeOnboardingSchema = Schema.Struct({ workspaceId: uuidSchema, podId: uuidSchema, diff --git a/packages/core/drizzle/20261009010054_workspace_setup_completed/migration.sql b/packages/core/drizzle/20261009010054_workspace_setup_completed/migration.sql new file mode 100644 index 00000000..7e95edaf --- /dev/null +++ b/packages/core/drizzle/20261009010054_workspace_setup_completed/migration.sql @@ -0,0 +1,4 @@ +ALTER TABLE "workspace" ADD COLUMN "setup_completed_at" timestamp with time zone;--> statement-breakpoint +-- Every workspace made before this was recorded counts as set up, as of when it was made. +UPDATE "workspace" SET "setup_completed_at" = "created_at";--> statement-breakpoint +ALTER TABLE "user" DROP COLUMN "onboarding_completed_at"; diff --git a/packages/core/drizzle/20261009010054_workspace_setup_completed/snapshot.json b/packages/core/drizzle/20261009010054_workspace_setup_completed/snapshot.json new file mode 100644 index 00000000..ddbacf32 --- /dev/null +++ b/packages/core/drizzle/20261009010054_workspace_setup_completed/snapshot.json @@ -0,0 +1,9346 @@ +{ + "version": "8", + "dialect": "postgres", + "id": "89d671f0-4576-47b7-bd7f-522a591bdbb8", + "prevIds": [ + "9dd94c78-b89e-4867-8657-d96226271c2a" + ], + "ddl": [ + { + "isRlsEnabled": false, + "name": "blob", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "model_request", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "blob_deletion", + "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_file", + "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_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": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "key", + "entityType": "columns", + "schema": "public", + "table": "blob" + }, + { + "type": "bytea", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "bytes", + "entityType": "columns", + "schema": "public", + "table": "blob" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "content_type", + "entityType": "columns", + "schema": "public", + "table": "blob" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "size", + "entityType": "columns", + "schema": "public", + "table": "blob" + }, + { + "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": "blob" + }, + { + "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": "blob" + }, + { + "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": "blob_deletion" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "prefix", + "entityType": "columns", + "schema": "public", + "table": "blob_deletion" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "queued_at", + "entityType": "columns", + "schema": "public", + "table": "blob_deletion" + }, + { + "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_file" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thread_id", + "entityType": "columns", + "schema": "public", + "table": "thread_file" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "tool_call_id", + "entityType": "columns", + "schema": "public", + "table": "thread_file" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "content_type", + "entityType": "columns", + "schema": "public", + "table": "thread_file" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "size", + "entityType": "columns", + "schema": "public", + "table": "thread_file" + }, + { + "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_file" + }, + { + "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_kind", + "entityType": "columns", + "schema": "public", + "table": "sandbox_lease" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "holder_id", + "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": "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": { + "as": "settings->>'preset'", + "type": "stored" + }, + "identity": null, + "name": "preset", + "entityType": "columns", + "schema": "public", + "table": "sandbox_provider" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "settings", + "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": "managed_resources", + "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": "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": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "setup_completed_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": "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": "key", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": { + "name": "text_pattern_ops", + "default": false + } + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "blob_key_prefix_idx", + "entityType": "indexes", + "schema": "public", + "table": "blob" + }, + { + "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 + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "thread_file_thread_idx", + "entityType": "indexes", + "schema": "public", + "table": "thread_file" + }, + { + "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_file_thread_id_thread_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "thread_file" + }, + { + "nameExplicit": false, + "columns": [ + "tool_call_id" + ], + "schemaTo": "public", + "tableTo": "tool_call", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "thread_file_tool_call_id_tool_call_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "thread_file" + }, + { + "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": "CASCADE", + "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": [ + "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_kind", + "holder_id" + ], + "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": [ + "key" + ], + "nameExplicit": false, + "name": "blob_pkey", + "schema": "public", + "table": "blob", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "model_request_pkey", + "schema": "public", + "table": "model_request", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "blob_deletion_pkey", + "schema": "public", + "table": "blob_deletion", + "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_file_pkey", + "schema": "public", + "table": "thread_file", + "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": "\"holder_kind\" in ('turn', 'viewer')", + "name": "sandbox_lease_holder_kind_check", + "entityType": "checks", + "schema": "public", + "table": "sandbox_lease" + }, + { + "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/workspaces/membership/membership.ts b/packages/core/src/workspaces/membership/membership.ts index 52ef9cfa..51355ce5 100644 --- a/packages/core/src/workspaces/membership/membership.ts +++ b/packages/core/src/workspaces/membership/membership.ts @@ -205,7 +205,7 @@ export const make = Effect.gen(function* () { .where(eq(workspaceMember.userId, userId)) .orderBy(asc(workspaceMember.createdAt)), ), - ), + ).pipe(Effect.map((rows) => rows.map(workspaceView))), ), create: (input) => @@ -233,7 +233,7 @@ export const make = Effect.gen(function* () { yield* searchProviders.provisionDefault(created.id, userId); yield* modelProviders.seedPresets(created.id); yield* personalPods.provision({ workspaceId: created.id, userId }); - return created; + return workspaceView(created); }), ); }), @@ -264,7 +264,7 @@ export const make = Effect.gen(function* () { (failure) => (isUniqueViolation(failure) ? new SlugTaken() : undefined), ); if (!updated) return yield* new ResourceHidden({ resource: "workspace" }); - return updated; + return workspaceView(updated); }), ), @@ -664,14 +664,21 @@ export class EmailUnverified extends Data.TaggedError("EmailUnverified") impleme } } -/** A workspace as the API describes it. */ +/** The columns of a workspace the API describes, for `workspaceView`. */ const workspaceColumns = { id: workspace.id, name: workspace.name, slug: workspace.slug, timeZone: workspace.timeZone, + setupCompletedAt: workspace.setupCompletedAt, }; +function workspaceView( + row: Omit & { setupCompletedAt: Date | null }, +): Workspace { + return { ...row, setupCompletedAt: row.setupCompletedAt?.toISOString() ?? null }; +} + const INVITATION_LIFETIME = Duration.days(2); function requireSlugUnlikeUuid(slug: string) { diff --git a/packages/core/src/workspaces/onboarding/onboarding.test.ts b/packages/core/src/workspaces/onboarding/onboarding.test.ts index 7386bba0..a55a57f8 100644 --- a/packages/core/src/workspaces/onboarding/onboarding.test.ts +++ b/packages/core/src/workspaces/onboarding/onboarding.test.ts @@ -2,7 +2,6 @@ import { and, eq } from "drizzle-orm"; import { Effect } from "effect"; import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { ActionForbidden } from "../../authorization/access.ts"; -import { CurrentActor } from "../../authorization/current-actor.ts"; import { agent, modelProvider, @@ -43,12 +42,6 @@ describe.skipIf(!process.env.DATABASE_URL)("onboarding, against Postgres", () => }); const complete = (userId: string, agentId: string) => as(userId).complete({ workspaceId, podId, agentId }); - const isCompleted = (userId: string) => - runOnPostgres( - onboarding.isCompleted.pipe( - CurrentActor.provide(CurrentActor.AuthenticatedUserId.vouchedFor(userId)), - ), - ); beforeEach(async () => { const stamp = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; @@ -155,12 +148,6 @@ describe.skipIf(!process.env.DATABASE_URL)("onboarding, against Postgres", () => systemAgentId = system.id; }); - it("completes only for an admin's custom agent in their pod", async () => { - await complete(adminId, customAgentId); - - expect(await isCompleted(adminId)).toBe(true); - }); - it("does not complete for an agent with no model the workspace offers", async () => { await onDatabase((db) => db.update(agent).set({ model: "not-switched-on" }).where(eq(agent.id, customAgentId)), @@ -171,7 +158,6 @@ describe.skipIf(!process.env.DATABASE_URL)("onboarding, against Postgres", () => db.update(agent).set({ model: null }).where(eq(agent.id, customAgentId)), ); await expect(complete(adminId, customAgentId)).rejects.toBeInstanceOf(Onboarding.NoModelChosen); - expect(await isCompleted(adminId)).toBe(false); }); it("makes the first bot's model the workspace's default and the Scribe's", async () => { @@ -190,12 +176,32 @@ describe.skipIf(!process.env.DATABASE_URL)("onboarding, against Postgres", () => expect(chosen?.modelId).toBe("model"); }); + it("records when the workspace's setup was completed, the first time only", async () => { + const setupCompletedAt = () => + onDatabase((db) => + db + .select({ at: workspace.setupCompletedAt }) + .from(workspace) + .where(eq(workspace.id, workspaceId)), + ).then(([row]) => row?.at); + await expect(complete(adminId, systemAgentId)).rejects.toBeInstanceOf( + Onboarding.NotReadyToFinish, + ); + expect(await setupCompletedAt()).toBeNull(); + + await complete(adminId, customAgentId); + const first = await setupCompletedAt(); + await complete(adminId, customAgentId); + + expect(first).toBeInstanceOf(Date); + expect(await setupCompletedAt()).toEqual(first); + }); + it("does not accept a system agent or a member who may not choose the workspace's models", async () => { await expect(complete(adminId, systemAgentId)).rejects.toBeInstanceOf( Onboarding.NotReadyToFinish, ); await expect(complete(memberId, customAgentId)).rejects.toBeInstanceOf(ActionForbidden); - expect(await isCompleted(adminId)).toBe(false); }); it("completes an account from its accepted invitation", async () => { @@ -221,7 +227,6 @@ describe.skipIf(!process.env.DATABASE_URL)("onboarding, against Postgres", () => expect(await as(memberId).completeAcceptedInvite({ invitationId: invitation.id })).toBe( workspaceId, ); - expect(await isCompleted(memberId)).toBe(true); const [assistant] = await onDatabase((db) => db .select({ model: agent.model }) @@ -236,6 +241,5 @@ describe.skipIf(!process.env.DATABASE_URL)("onboarding, against Postgres", () => await expect( as(memberId).completeAcceptedInvite({ invitationId: crypto.randomUUID() }), ).rejects.toBeInstanceOf(Onboarding.InvitationNotAccepted); - expect(await isCompleted(memberId)).toBe(false); }); }); diff --git a/packages/core/src/workspaces/onboarding/onboarding.ts b/packages/core/src/workspaces/onboarding/onboarding.ts index 42e64bc1..e27f0527 100644 --- a/packages/core/src/workspaces/onboarding/onboarding.ts +++ b/packages/core/src/workspaces/onboarding/onboarding.ts @@ -11,6 +11,7 @@ import { pod, podMember, user, + workspace, workspaceInvite, workspaceMember, } from "../../database/schema.ts"; @@ -22,23 +23,24 @@ import { AgentRepository } from "../agents/agent-repository.ts"; import { PersonalPods } from "../pods/personal-pods.ts"; /** - * The current actor finishing their first run through the product. + * The current actor finishing setting up a workspace, or joining one by + * invitation. * - * Both writes are transactional and both lock the row they depend on, because - * the check and the write have to agree: two tabs finishing onboarding at once - * must not both decide they were the one that did it. + * Both are transactional and lock the row they check, so the check still holds + * when the writes that follow it land. */ export interface Interface { - readonly isCompleted: Effect.Effect; /** - * Marks onboarding done, once `podId` and `agentId` are a pod the actor is - * in and its crew agent, and that agent runs on a model the workspace - * offers. Finishing settles the first agent and the model the workspace - * runs it on, so it takes `workspace.providers.manage`. + * Finishes setting up `workspaceId`, once `podId` and `agentId` are a pod the + * actor is in and its crew agent, and that agent runs on a model the + * workspace offers. Finishing settles the first agent and the model the + * workspace runs it on, so it takes `workspace.providers.manage`. * * That model also becomes the workspace's default and every system agent's: * it is the one model the person setting the workspace up has chosen, and * the system agents have to run on something. + * + * The first time, it records when the workspace's setup was completed. */ readonly complete: (input: { workspaceId: string; @@ -50,10 +52,9 @@ export interface Interface { CurrentActor.Service >; /** - * Marks onboarding done for an actor who joined through `invitationId`, - * pointing their Personal Assistant at the workspace's default model, or - * another it offers while it does not offer that one. Returns the workspace - * they joined. + * Sets up an actor who joined through `invitationId`, pointing their + * Personal Assistant at the workspace's default model, or another it offers + * while it does not offer that one. Returns the workspace they joined. */ readonly completeAcceptedInvite: (input: { invitationId: string; @@ -74,19 +75,6 @@ export const make = Effect.gen(function* () { const agents = yield* AgentRepository.Service; return Service.of({ - isCompleted: operation( - "isCompleted", - Effect.flatMap(CurrentActor.Service, ({ userId }) => - query((db) => - db - .select({ completedAt: user.onboardingCompletedAt }) - .from(user) - .where(eq(user.id, userId)) - .limit(1), - ), - ).pipe(Effect.map(([row]) => row?.completedAt != null)), - ), - complete: ({ workspaceId, podId, agentId }) => operation( "complete", @@ -132,7 +120,7 @@ export const make = Effect.gen(function* () { agents.setAllSystemAgentModels(resolved, model), ]), ).pipe(Effect.catchTag("ModelNotEnabled", () => Effect.fail(new NoModelChosen()))); - yield* markCompleted(actor.userId); + yield* recordSetupCompleted(resolved); }), ), ), @@ -175,7 +163,6 @@ export const make = Effect.gen(function* () { } else { yield* personalPods.provision({ workspaceId, userId }); } - yield* markCompleted(userId); return workspaceId; }), ), @@ -218,7 +205,13 @@ export class InvitationNotAccepted } } -const markCompleted = (userId: string) => +/** Records that `workspaceId`'s setup is complete, unless it already was. */ +export const recordSetupCompleted = (workspaceId: string) => Effect.flatMap(DateTime.nowAsDate, (now) => - query((db) => db.update(user).set({ onboardingCompletedAt: now }).where(eq(user.id, userId))), + query((db) => + db + .update(workspace) + .set({ setupCompletedAt: now }) + .where(and(eq(workspace.id, workspaceId), isNull(workspace.setupCompletedAt))), + ), ); diff --git a/packages/core/src/workspaces/sql.ts b/packages/core/src/workspaces/sql.ts index fe586622..dc32d364 100644 --- a/packages/core/src/workspaces/sql.ts +++ b/packages/core/src/workspaces/sql.ts @@ -49,7 +49,6 @@ export const user = pgTable( email: text("email").notNull(), emailVerified: boolean("email_verified").notNull().default(false), image: text("image"), - onboardingCompletedAt: timestamp("onboarding_completed_at", { withTimezone: true }), /** Who sent the referral link this person signed up with. */ referredBy: uuid("referred_by").references((): AnyPgColumn => user.id, { onDelete: "set null", @@ -145,6 +144,8 @@ export const workspace = pgTable( logo: text("logo"), /** An IANA time zone: where the workspace's days and months begin. */ timeZone: text("time_zone").notNull().default(DEFAULT_TIME_ZONE), + /** When its setup was finished; null while it is still being set up. */ + setupCompletedAt: timestamp("setup_completed_at", { withTimezone: true }), createdAt: stamp("created_at"), updatedAt: updatedStamp("updated_at"), }, diff --git a/packages/server/src/auth/authentication.test.ts b/packages/server/src/auth/authentication.test.ts index f50798e6..1ccf5f03 100644 --- a/packages/server/src/auth/authentication.test.ts +++ b/packages/server/src/auth/authentication.test.ts @@ -6,7 +6,6 @@ import { closeDatabase, onDatabase, testInfrastructure } from "@sugabots/core/da import { Email } from "@sugabots/core/email/email"; import { Installation } from "@sugabots/core/installation/installation"; import { Membership } from "@sugabots/core/workspaces/membership/membership"; -import { Onboarding } from "@sugabots/core/workspaces/onboarding/onboarding"; import { eq } from "drizzle-orm"; import { ConfigProvider, Effect, Layer, ManagedRuntime, Schema } from "effect"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; @@ -45,7 +44,7 @@ async function appWith( sent: Email.Message[], ) { const runtime = ManagedRuntime.make( - Layer.mergeAll(Authentication.layer, Membership.layer, Onboarding.layer).pipe( + Layer.mergeAll(Authentication.layer, Membership.layer).pipe( Layer.provideMerge(Accounts.layer), Layer.provide([ Layer.succeed( @@ -81,7 +80,6 @@ async function appWith( Effect.all({ authentication: Authentication.Service, membership: Membership.Service, - onboarding: Onboarding.Service, accounts: Accounts.Service, }), ); @@ -90,7 +88,6 @@ async function appWith( Layer.mergeAll( Layer.succeed(Authentication.Service, services.authentication), Layer.succeed(Membership.Service, services.membership), - Layer.succeed(Onboarding.Service, services.onboarding), Layer.succeed(Accounts.Service, services.accounts), installationWithWebAppAt(ORIGIN), ), @@ -300,7 +297,7 @@ describe.skipIf(!process.env.DATABASE_URL)("accounts", () => { expect(response.status).toBe(400); }); - it("answers who someone is, their onboarding and their workspaces in one request", async () => { + it("answers who someone is and their workspaces in one request", async () => { const email = `boot-${crypto.randomUUID().slice(0, 8)}@example.com`; const token = await signUp("Boot Strap", email); @@ -309,7 +306,6 @@ describe.skipIf(!process.env.DATABASE_URL)("accounts", () => { expect(response.status).toBe(200); expect(await response.json()).toEqual({ user: expect.objectContaining({ name: "Boot Strap", email }), - onboarding: { completed: false }, workspaces: [], }); }); diff --git a/packages/server/src/http/app.test-support.ts b/packages/server/src/http/app.test-support.ts index f8f9a1b3..1a86ae86 100644 --- a/packages/server/src/http/app.test-support.ts +++ b/packages/server/src/http/app.test-support.ts @@ -104,7 +104,7 @@ const fakes: Layer.Layer = Layer.mergeAll( unimplemented(Membership.Service, { workspaces: Effect.succeed([]) }), unimplemented(PodAdministration.Service), unimplemented(AgentAdministration.Service), - unimplemented(Onboarding.Service, { isCompleted: Effect.succeed(false) }), + unimplemented(Onboarding.Service), unimplemented(ModelProviderSetup.Service), unimplemented(SearchProviderSetup.Service), unimplemented(SandboxProviderSetup.Service), diff --git a/packages/server/src/routes/onboarding/routes.ts b/packages/server/src/routes/onboarding/routes.ts index 12bf099e..f169bbec 100644 --- a/packages/server/src/routes/onboarding/routes.ts +++ b/packages/server/src/routes/onboarding/routes.ts @@ -6,21 +6,13 @@ import { asSessionUser } from "../../auth/middleware.ts"; import { ServerApi } from "../../http/api.ts"; import { asHttpError, refusals } from "../../http/errors.ts"; -/** Whether the signed-in person has finished setting up, and marking that they have. */ +/** The signed-in person finishing setting up a workspace, or joining one by invitation. */ export const onboardingRoutes = HttpApiBuilder.group(ServerApi, "onboarding", (handlers) => Effect.gen(function* () { const onboarding = yield* Onboarding.Service; return handlers - .handle("status", () => - onboarding.isCompleted.pipe( - Effect.map((completed) => ({ completed })), - asSessionUser, - ), - ) .handle("complete", ({ payload }) => - onboarding - .complete(payload) - .pipe(Effect.as({ completed: true }), asSessionUser, asHttpError(onboardingErrors)), + onboarding.complete(payload).pipe(asSessionUser, asHttpError(onboardingErrors)), ) .handle("completeInvite", ({ payload }) => onboarding.completeAcceptedInvite(payload).pipe( diff --git a/packages/server/src/routes/system/routes.ts b/packages/server/src/routes/system/routes.ts index 856bd6c2..cd3406e9 100644 --- a/packages/server/src/routes/system/routes.ts +++ b/packages/server/src/routes/system/routes.ts @@ -1,6 +1,5 @@ import { CurrentUser } from "@sugabots/contracts/http"; import { Membership } from "@sugabots/core/workspaces/membership/membership"; -import { Onboarding } from "@sugabots/core/workspaces/onboarding/onboarding"; import { Effect } from "effect"; import { HttpApiBuilder } from "effect/unstable/httpapi"; import { asSessionUser } from "../../auth/middleware.ts"; @@ -11,16 +10,11 @@ import { health } from "../../version.ts"; export const systemRoutes = HttpApiBuilder.group(ServerApi, "system", (handlers) => Effect.gen(function* () { const membership = yield* Membership.Service; - const onboarding = yield* Onboarding.Service; return handlers .handle("health", () => Effect.sync(health)) .handle("me", () => Effect.all( - { - user: Effect.service(CurrentUser), - onboarding: onboarding.isCompleted.pipe(Effect.map((completed) => ({ completed }))), - workspaces: membership.workspaces, - }, + { user: Effect.service(CurrentUser), workspaces: membership.workspaces }, { concurrency: "unbounded" }, ).pipe(asSessionUser), ) diff --git a/packages/server/src/routes/workspaces/routes.test.ts b/packages/server/src/routes/workspaces/routes.test.ts index 90ee20b2..02b7f71b 100644 --- a/packages/server/src/routes/workspaces/routes.test.ts +++ b/packages/server/src/routes/workspaces/routes.test.ts @@ -23,6 +23,7 @@ const created: Workspace = { name: "Nitric", slug: "nitric", timeZone: "Australia/Sydney", + setupCompletedAt: null, }; const createWith = (create: Membership.Interface["create"], payload: unknown) => diff --git a/packages/server/src/seed.ts b/packages/server/src/seed.ts index 78bc8cb8..9d42e604 100644 --- a/packages/server/src/seed.ts +++ b/packages/server/src/seed.ts @@ -10,6 +10,7 @@ import { Ids } from "@sugabots/core/ids/ids"; import { Installation } from "@sugabots/core/installation/installation"; import { AgentRepository } from "@sugabots/core/workspaces/agents/agent-repository"; import { Membership } from "@sugabots/core/workspaces/membership/membership"; +import { Onboarding } from "@sugabots/core/workspaces/onboarding/onboarding"; import { PersonalPods } from "@sugabots/core/workspaces/pods/personal-pods"; import { PodRepository } from "@sugabots/core/workspaces/pods/pod-repository"; import { and, eq, type SQL } from "drizzle-orm"; @@ -67,6 +68,8 @@ const seed = Effect.gen(function* () { Effect.map((created) => created.id), Effect.catchTag("SlugTaken", () => idOf(workspace, eq(workspace.slug, "dev"))), ); + // The seed stands in for setting it up, so it opens straight into the app. + yield* Onboarding.recordSetupCompleted(workspaceId); const personal = yield* personalPods.provision({ workspaceId, userId: person.id }); const supportId = yield* pods diff --git a/packages/web/src/lib/onboarding.ts b/packages/web/src/lib/onboarding.ts index 1e4ab1dc..07ccd564 100644 --- a/packages/web/src/lib/onboarding.ts +++ b/packages/web/src/lib/onboarding.ts @@ -1,26 +1,24 @@ import type { CompleteOnboarding, ProviderModel } from "@sugabots/contracts"; -import { queryOptions, useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; import { Effect } from "effect"; import { client } from "@/api.ts"; -import { useUpdateAgent } from "@/lib/agents.ts"; +import { agentsQuery, useUpdateAgent } from "@/lib/agents.ts"; import { useProviderActions } from "@/lib/model-providers.ts"; +import { workspacesQuery } from "@/lib/workspace.ts"; -/** Whether the signed-in person has finished setting up. */ -export const onboardingQuery = queryOptions({ - queryKey: ["onboarding"], - queryFn: ({ signal }) => Effect.runPromise(client.api.onboarding.status(), { signal }), -}); - -export function useOnboarding() { - return useQuery(onboardingQuery); -} - +/** Finishing setup makes the first bot's model the workspace's default and every system agent's. */ export function useCompleteOnboarding() { const queries = useQueryClient(); return useMutation({ mutationFn: (payload: CompleteOnboarding) => Effect.runPromise(client.api.onboarding.complete({ payload })), - onSuccess: () => queries.invalidateQueries({ queryKey: ["onboarding"] }), + onSuccess: (_, { workspaceId }) => + Promise.all([ + // The workspace is now set up, which the shell asks before it opens it. + queries.invalidateQueries({ queryKey: workspacesQuery.queryKey }), + queries.invalidateQueries({ queryKey: ["models", workspaceId] }), + queries.invalidateQueries({ queryKey: agentsQuery(workspaceId).queryKey }), + ]), }); } diff --git a/packages/web/src/lib/session.test.tsx b/packages/web/src/lib/session.test.tsx index 487929e2..e028d1df 100644 --- a/packages/web/src/lib/session.test.tsx +++ b/packages/web/src/lib/session.test.tsx @@ -10,7 +10,7 @@ vi.mock("@/api.ts", () => import("@/test-client.ts")); class Unreachable extends Data.TaggedError("Unreachable") {} -const meAsSam = { user: sam, onboarding: { completed: true }, workspaces: [] }; +const meAsSam = { user: sam, workspaces: [] }; beforeEach(() => { client.api.me.mockReturnValue(Effect.succeed(meAsSam)); diff --git a/packages/web/src/lib/workspace.ts b/packages/web/src/lib/workspace.ts index 9506fb1c..d3e70c1a 100644 --- a/packages/web/src/lib/workspace.ts +++ b/packages/web/src/lib/workspace.ts @@ -306,7 +306,8 @@ const NOTHING_YET: WorkspacePermissions = { deleteWorkspace: false, }; -function useWorkspaceStanding() { +/** The caller's role in the workspace being looked at, and what it lets them do there. */ +export function useWorkspaceStanding() { const workspaceId = useWorkspace().workspace?.id; return useQuery({ diff --git a/packages/web/src/main.tsx b/packages/web/src/main.tsx index 52228b24..4ab300a1 100644 --- a/packages/web/src/main.tsx +++ b/packages/web/src/main.tsx @@ -3,7 +3,6 @@ import { type QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { StrictMode, useState } from "react"; import { createRoot } from "react-dom/client"; import { failureMessage } from "@/lib/failure.ts"; -import { onboardingQuery } from "@/lib/onboarding.ts"; import { createQueryClient } from "@/lib/query.ts"; import { type Session, useSessionFromApi } from "@/lib/session.ts"; import { reloadWhenADeployReplacesChunks } from "@/lib/stale-chunks.ts"; @@ -73,13 +72,12 @@ function SessionRouter({ session }: { session: Session }) { } /** - * A query cache for one session, already holding the onboarding and workspaces - * `/me` answered with, so the shell need not ask for them again. + * A query cache for one session, already holding the workspaces `/me` answered + * with, so the shell need not ask for them again. */ function queriesStartingFrom(me: Me | undefined): QueryClient { const queries = createQueryClient(); if (me) { - queries.setQueryData(onboardingQuery.queryKey, me.onboarding); queries.setQueryData(workspacesQuery.queryKey, me.workspaces); } return queries; diff --git a/packages/web/src/router.tsx b/packages/web/src/router.tsx index 61fadcde..9c7ed3bc 100644 --- a/packages/web/src/router.tsx +++ b/packages/web/src/router.tsx @@ -16,22 +16,13 @@ import { useRouter, useSearch, } from "@tanstack/react-router"; -import { - type ComponentType, - lazy, - type ReactNode, - Suspense, - useEffect, - useRef, - useState, -} from "react"; +import { type ComponentType, lazy, type ReactNode, Suspense, useEffect, useState } from "react"; import { validate as isUuid } from "uuid"; import { usePodAgent } from "@/lib/agents.ts"; import { prefetchChat, useChatList } from "@/lib/chats.ts"; import { signInFailureReason } from "@/lib/connections.ts"; import { agentChatLink, podLink } from "@/lib/links.ts"; import { matchesMedia, SIDE_BY_SIDE, useMediaQuery } from "@/lib/media.ts"; -import { useOnboarding } from "@/lib/onboarding.ts"; import { RESET_PASSWORD_PATH } from "@/lib/password-reset.ts"; import { findPod, podsQuery, usePods } from "@/lib/pods.ts"; import { type Session, SessionContext, useSession } from "@/lib/session.ts"; @@ -39,6 +30,7 @@ import { chooseWorkspace, useDeleteWorkspace, useWorkspace, + useWorkspaceStanding, useWorkspaces, } from "@/lib/workspace.ts"; import { workspaceSettingSection } from "@/lib/workspace-settings.ts"; @@ -198,12 +190,22 @@ const indexRoute = createRoute({ component: LandingRoute, }); +/** + * Opens the chosen workspace if it is set up, and otherwise one that is. One + * still being set up opens when asked for by its address or the switcher, or + * here when there is no other. + */ function LandingRoute() { - const { workspace, isPending, error, refetch } = useWorkspace(); + const { workspace: chosen, isPending, error, refetch } = useWorkspace(); + const workspaces = useWorkspaces(); if (isPending) return
; if (error) return ; - if (!workspace) return ; - return ; + if (!chosen) return ; + const landing = + chosen.setupCompletedAt !== null + ? chosen + : (workspaces.data?.find((one) => one.setupCompletedAt !== null) ?? chosen); + return ; } interface LoginSearch { @@ -413,138 +415,84 @@ function InviteRoute() { ); } -const onboardingRoute = createRoute({ - getParentRoute: () => rootRoute, - path: "/onboarding", - beforeLoad: requireUser, - component: OnboardingRoute, -}); - -function OnboardingRoute() { - const session = useSession(); - const onboarding = useOnboarding(); - const workspace = useWorkspace(); - - if (onboarding.isPending || workspace.isPending) return
; - if (onboarding.error || workspace.error) { - return ( - Promise.all([onboarding.refetch(), workspace.refetch()])} - /> - ); - } - if (onboarding.data?.completed) { - // The first run is done once, so somebody who has deleted every workspace since sets up another. - return workspace.workspace ? ( - - ) : ( - - ); - } - return ; +interface OnboardingSearch { + /** + * The workspace being set up, by its address. Written before the workspace + * is saved, so a reload while it saves picks up where it got to. + */ + workspace?: string; } /** - * Setting up another workspace, with the same steps as the first. Under - * `/onboarding` because any new top-level path would hide a workspace whose - * address it is. + * Setting up a workspace, the first or another, all at this one path: every + * top-level path hides the workspace whose address matches it. */ -interface NewWorkspaceSearch { - /** The workspace this has made, by its address, so a reload picks up where it got to. */ - workspace?: string; -} - -const newWorkspaceRoute = createRoute({ +const onboardingRoute = createRoute({ getParentRoute: () => rootRoute, - path: "/onboarding/new", - validateSearch: (search: Record): NewWorkspaceSearch => ({ + path: "/onboarding", + validateSearch: (search: Record): OnboardingSearch => ({ workspace: typeof search.workspace === "string" ? search.workspace : undefined, }), beforeLoad: requireUser, - component: NewWorkspaceRoute, + component: OnboardingRoute, }); -function NewWorkspaceRoute() { +function OnboardingRoute() { const session = useSession(); - const { workspace: madeSlug } = newWorkspaceRoute.useSearch(); - const { workspace, isPending, error, refetch } = useWorkspace(); + const { workspace: madeSlug } = onboardingRoute.useSearch(); + const { isPending, error, refetch } = useWorkspace(); const workspaces = useWorkspaces(); if (isPending) return
; if (error) return ; return ( - one.slug === madeSlug)} - hasAnother={workspaces.data?.some((one) => one.slug !== madeSlug) ?? false} /> ); } /** - * Making the workspace chooses it, and the steps after work in the one chosen, - * so a reload chooses it again. Cancel chooses again the one this was opened - * from and goes back to the page it was opened on. + * Cancel chooses the workspace setup was opened from, or another when that is + * the one being set up, and goes back to the page it was opened on. Without + * one there is nowhere to go back to, and no Cancel. * - * Cancel deletes only a workspace this page made. One the address names from - * before, as after a reload or on coming back to setup by Back, may have been - * finished and shared since, and stays. + * Cancel deletes the workspace being set up while its setup is unfinished, if + * the caller may delete it, as its owner may. One whose setup is finished stays. */ -function NewWorkspaceOnboarding({ - session, - cameFrom, - made, - hasAnother, -}: { - session: Session; - cameFrom: Workspace | undefined; - made: Workspace | undefined; - /** Whether there is a workspace besides the one being made to go back to. */ - hasAnother: boolean; -}) { +function WorkspaceSetup({ session, made }: { session: Session; made: Workspace | undefined }) { const router = useRouter(); - const [returnTo] = useState(cameFrom); - const chosen = useWorkspace().workspace; + const openedFrom = useWorkspace().workspace; + const workspaces = useWorkspaces().data; + // Settled when setup opens, because making the workspace chooses it. + const [returnTo] = useState(() => + openedFrom && openedFrom.id !== made?.id + ? openedFrom + : workspaces?.find((one) => one.id !== made?.id), + ); const deleteWorkspace = useDeleteWorkspace(); - const [leaving, setLeaving] = useState(false); - const madeHere = useRef(undefined); - const choosingMade = !leaving && made !== undefined && chosen?.id !== made.id; - - useEffect(() => { - if (choosingMade) chooseWorkspace(made.id); - }, [choosingMade, made]); + // Setup shows its steps only once the workspace being set up is chosen, so this is its standing. + const standing = useWorkspaceStanding(); async function cancel() { - setLeaving(true); - // One that cannot be deleted now stays, for its settings to delete; leaving still goes ahead. - if (made && made.id === madeHere.current) { + if (made?.setupCompletedAt === null && standing.data?.permissions.deleteWorkspace) { + // One that cannot be deleted now stays; leaving still goes ahead. await deleteWorkspace.mutateAsync(made.id).catch(() => undefined); } - // After a reload the one this came from is not known, and deleting this one leaves another chosen. - if (returnTo && returnTo.id !== made?.id) chooseWorkspace(returnTo.id); + if (returnTo) chooseWorkspace(returnTo.id); if (router.history.canGoBack()) router.history.back(); else void router.navigate({ to: "/" }); } - if (choosingMade) return
; return ( { - // Before the address names one, saving it was making it. - if (!made) madeHere.current = saved.id; - void router.navigate({ - to: "/onboarding/new", - search: { workspace: saved.slug }, - replace: true, - }); - }, - onCancel: hasAnother ? () => void cancel() : undefined, - }} + workspace={made} + onAddress={(slug) => + void router.navigate({ to: "/onboarding", search: { workspace: slug }, replace: true }) + } + onCancel={returnTo ? () => void cancel() : undefined} /> ); } @@ -566,18 +514,13 @@ const workspaceIndexRoute = createRoute({ }); function ShellRoute() { - const onboarding = useOnboarding(); const workspace = useWorkspace(); const workspaces = useWorkspaces(); + const standing = useWorkspaceStanding(); - if (onboarding.isPending || workspace.isPending) return
; - if (onboarding.error || workspace.error) { - return ( - Promise.all([onboarding.refetch(), workspace.refetch()])} - /> - ); + if (workspace.isPending) return
; + if (workspace.error) { + return ; } if (!workspace.workspace) { // Somebody in no workspace at all has one to make, not a wrong address. @@ -587,8 +530,18 @@ function ShellRoute() { ); } - if (!onboarding.data?.completed) { - return ; + if (workspace.workspace.setupCompletedAt === null) { + // Setup left part way is finished before the workspace is used, by whoever may finish it. + // Anybody invited part way through uses it as it is. + if (standing.isPending) return
; + if (standing.error) { + return ; + } + // Finishing setup takes `workspace.providers.manage`. + const mayFinishSetup = standing.data?.permissions.manageProviders; + if (mayFinishSetup) { + return ; + } } return ; } @@ -1146,7 +1099,6 @@ const routeTree = rootRoute.addChildren([ inviteRoute, signInReturnRoute, onboardingRoute, - newWorkspaceRoute, shellRoute.addChildren([ workspaceIndexRoute, settingsRoute, diff --git a/packages/web/src/screens/ChatThreadPanel.stories.tsx b/packages/web/src/screens/ChatThreadPanel.stories.tsx index 18b6af2a..a39c191b 100644 --- a/packages/web/src/screens/ChatThreadPanel.stories.tsx +++ b/packages/web/src/screens/ChatThreadPanel.stories.tsx @@ -155,7 +155,15 @@ function Preview({ children, run }: { children: React.ReactNode; run?: ThreadDet // The run's link to its routine is found through the bot's pod. client.setQueryData( ["workspaces"], - [{ id: revenue.workspaceId, name: "Nitric", slug: "nitric", timeZone: "UTC" }], + [ + { + id: revenue.workspaceId, + name: "Nitric", + slug: "nitric", + timeZone: "UTC", + setupCompletedAt: "2026-09-01T00:00:00.000Z", + }, + ], ); client.setQueryData(["pods", revenue.workspaceId], [revenue]); client.setQueryData(["agents", revenue.workspaceId], [growthDesk, linearHandler]); diff --git a/packages/web/src/screens/DetailsSidebar.stories.tsx b/packages/web/src/screens/DetailsSidebar.stories.tsx index 52f49bee..37040eb2 100644 --- a/packages/web/src/screens/DetailsSidebar.stories.tsx +++ b/packages/web/src/screens/DetailsSidebar.stories.tsx @@ -122,7 +122,15 @@ const meta = preview.meta({ client.setQueryData(["connections", revenue.id], connections); client.setQueryData( ["workspaces"], - [{ id: WORKSPACE, name: "Nitric", slug: "nitric", timeZone: "UTC" }], + [ + { + id: WORKSPACE, + name: "Nitric", + slug: "nitric", + timeZone: "UTC", + setupCompletedAt: "2026-09-01T00:00:00.000Z", + }, + ], ); client.setQueryData( ["built-in-agents", WORKSPACE], diff --git a/packages/web/src/screens/Invite.tsx b/packages/web/src/screens/Invite.tsx index 37252a2d..ac94a578 100644 --- a/packages/web/src/screens/Invite.tsx +++ b/packages/web/src/screens/Invite.tsx @@ -88,7 +88,6 @@ export function Invite({ id, onDone }: { id: string; onDone: () => Promise setAccepted(true); chooseWorkspace(result.workspaceId); await queries.invalidateQueries({ queryKey: ["workspaces"] }); - await queries.invalidateQueries({ queryKey: ["onboarding"] }); await onDone(); return true; } catch (failure) { diff --git a/packages/web/src/screens/Onboarding.stories.tsx b/packages/web/src/screens/Onboarding.stories.tsx index c3256c1a..baf5c81a 100644 --- a/packages/web/src/screens/Onboarding.stories.tsx +++ b/packages/web/src/screens/Onboarding.stories.tsx @@ -14,6 +14,8 @@ const workspace = { id: WORKSPACE, name: "Nitric", slug: "nitric", + timeZone: "UTC", + setupCompletedAt: "2026-09-01T00:00:00.000Z", createdAt: "2026-09-01T00:00:00.000Z", }; const user = { @@ -88,7 +90,7 @@ const meta = preview.meta({ component: Onboarding, tags: ["ai-generated"], parameters: { layout: "fullscreen" }, - args: { session }, + args: { session, workspace, onAddress: fn() }, }); /** The welcome, before signing in: a bot of every colour, and the way in. */ @@ -106,8 +108,9 @@ export const Welcome = meta.story({ }, }); -/** Naming the workspace, which Continue waits on. */ +/** Naming the first workspace, which Continue waits on. With none to go back to, there is no Cancel. */ export const Workspace = meta.story({ + args: { workspace: undefined }, render: (args) => (
@@ -120,6 +123,7 @@ export const Workspace = meta.story({ await canvas.findByRole("heading", { name: "Name your workspace" }), ).toBeInTheDocument(); await expect(canvas.getByRole("button", { name: "Continue" })).toBeDisabled(); + await expect(canvas.queryByRole("button", { name: "Cancel" })).toBeNull(); await expect(canvas.getByLabelText("Workspace name")).toHaveAttribute( "placeholder", "e.g. Ryan's bots", @@ -129,7 +133,7 @@ export const Workspace = meta.story({ /** Naming another workspace, from a workspace already set up, which Cancel returns to. */ export const AnotherWorkspace = meta.story({ - args: { newWorkspace: { made: undefined, onMade: fn(), onCancel: fn() } }, + args: { workspace: undefined, onCancel: fn() }, render: (args) => (
@@ -144,7 +148,7 @@ export const AnotherWorkspace = meta.story({ await expect(canvas.getByLabelText("Workspace name")).toHaveValue(""); await expect(canvas.queryByRole("button", { name: "Back" })).toBeNull(); await userEvent.click(canvas.getByRole("button", { name: "Cancel" })); - await expect(args.newWorkspace?.onCancel).toHaveBeenCalled(); + await expect(args.onCancel).toHaveBeenCalled(); }, }); diff --git a/packages/web/src/screens/Onboarding.tsx b/packages/web/src/screens/Onboarding.tsx index e8c24f5e..199fe069 100644 --- a/packages/web/src/screens/Onboarding.tsx +++ b/packages/web/src/screens/Onboarding.tsx @@ -20,6 +20,7 @@ import { type KeyboardEvent, type ReactNode, type RefObject, + useEffect, useId, useRef, useState, @@ -35,6 +36,7 @@ import { import { useEnsurePersonalPod, usePods } from "@/lib/pods.ts"; import type { Session } from "@/lib/session.ts"; import { + chooseWorkspace, useCreateWorkspace, useInviteWorkspaceMember, useUpdateWorkspace, @@ -53,14 +55,10 @@ import { ProviderTile } from "./ProviderSettings.tsx"; import { ProviderSignInRow } from "./ProviderSignIn.tsx"; /* - * The first run: name the workspace, connect a model, make the first bot, - * invite people, then meet the bot. Signing in comes before, on the login - * page, which is the welcome. The first bot is the Personal pod's own, which - * the workspace made with it, so making it here is giving it a face and a name. - * - * Another workspace goes through the same steps, and can be left part way. - * Until it is made, the one already chosen is the one to go back to, not the - * one being set up. + * Setting up a workspace: name it, connect a model, make the first bot, invite + * people, then meet the bot. Signing in comes before, on the login page, which + * is the welcome. The first bot is the Personal pod's own, which the workspace + * made with it, so making it here is giving it a face and a name. */ type Step = "workspace" | "model" | "bot" | "invite" | "ready"; @@ -68,24 +66,39 @@ type Step = "workspace" | "model" | "bot" | "invite" | "ready"; /** The steps the dots count. Ready has none: there is nothing left to go back to. */ const counted: readonly Step[] = ["workspace", "model", "bot", "invite"]; -export function Onboarding({ - session, - newWorkspace, -}: { +interface SetupProps { session: Session; + /** The workspace being set up, once the first step has made it. */ + workspace: Workspace | undefined; /** - * Sets up another workspace rather than the first: `made` once the first - * step has made it, and chosen. `onMade` hears of it, and of its new - * address if it is renamed. Without `onCancel` there is nowhere to go back to. + * Hears the address the first step is about to save the workspace at, + * before it saves, so a reload while it saves comes back to the same + * workspace; and the address it keeps if saving fails. */ - newWorkspace?: { - made: Workspace | undefined; - onMade: (saved: { id: string; slug: string }) => void; - onCancel?: () => void; - }; -}) { - const chosenWorkspace = useWorkspace().workspace; - const workspace = newWorkspace ? newWorkspace.made : chosenWorkspace; + onAddress: (slug: string | undefined) => void; + /** Leaves setup. Without it there is nowhere to go back to. */ + onCancel?: () => void; +} + +/** + * The steps read the chosen workspace, so the one being set up is chosen + * before they show: once each time it changes, as on making it or after a + * reload. Choosing it no more often than that lets Cancel choose another on + * the way out. + */ +export function Onboarding(props: SetupProps) { + const chosen = useWorkspace().workspace; + const workspaceId = props.workspace?.id; + + useEffect(() => { + if (workspaceId) chooseWorkspace(workspaceId); + }, [workspaceId]); + + if (workspaceId && chosen?.id !== workspaceId) return
; + return ; +} + +function SetupSteps({ session, workspace, onAddress, onCancel }: SetupProps) { const cancelling = useRef(false); const pods = usePods(); const models = useModels(Boolean(workspace)); @@ -102,7 +115,7 @@ export function Onboarding({ // Coming back part way through picks up where the workspace says it got to. const inferred: Step = !workspace ? "workspace" : botHasModel ? "bot" : "model"; const step = chosen ?? inferred; - // Between making the workspace and the address saying so, there is none to set up yet. + // Made, but not yet among the workspaces read back, so there is none to set up yet. const loading = (!workspace && step !== "workspace") || (Boolean(workspace) && (pods.isPending || models.isPending || agentsPending)); @@ -115,16 +128,16 @@ export function Onboarding({ current={index === -1 ? undefined : index} onBack={back ? () => setChosen(back) : undefined} onCancel={ - newWorkspace?.onCancel && step !== "ready" + onCancel && step !== "ready" ? () => { cancelling.current = true; - newWorkspace.onCancel?.(); + onCancel(); } : undefined } > - {newWorkspace?.made && step !== "ready" && ( - + {workspace && step !== "ready" && ( + )} {loading ? (

Loading your setup…

@@ -142,10 +155,8 @@ export function Onboarding({ { - newWorkspace?.onMade(saved); - setChosen("model"); - }} + onAddress={onAddress} + onContinue={() => setChosen("model")} /> ) : step === "model" ? ( setChosen("bot")} /> @@ -164,7 +175,7 @@ export function Onboarding({ /** * Asks before a link, Back or closing the tab leaves a workspace made but not - * set up. Cancel is let through: it deletes the workspace. + * set up. Cancel is let through: it settles what becomes of the workspace. */ function LeavingSetupGuard({ workspaceName, @@ -185,7 +196,7 @@ function LeavingSetupGuard({ if (!open) blocker.reset?.(); }} title="Leave setup?" - description={`${workspaceName} has been made but isn't set up yet. It stays in your workspaces, where you can finish setting it up or delete it from its settings.`} + description={`${workspaceName} has been made but isn't set up yet. It stays in your workspaces, and opening it picks up setup where you left off.`} pending={false} confirmLabel="Leave" onDelete={() => blocker.proceed?.()} @@ -289,12 +300,13 @@ const fieldClass = function WorkspaceStep({ workspace, firstName, + onAddress, onContinue, }: { workspace?: { id: string; name: string; slug: string }; firstName?: string; - /** Given the workspace as it was saved. */ - onContinue: (saved: { id: string; slug: string }) => void; + onAddress: SetupProps["onAddress"]; + onContinue: () => void; }) { const create = useCreateWorkspace(); const update = useUpdateWorkspace(workspace?.id); @@ -304,19 +316,18 @@ function WorkspaceStep({ async function submit(event: FormEvent) { event.preventDefault(); - if (!slugify(trimmed)) return; - const input = { name: trimmed, slug: slugify(trimmed) }; - let saved: { id: string; slug: string }; + const slug = slugify(trimmed); + if (!slug) return; + if (workspace && trimmed === workspace.name) return onContinue(); + onAddress(slug); try { - saved = !workspace - ? await create.mutateAsync(input) - : trimmed !== workspace.name - ? await update.mutateAsync(input) - : workspace; + if (workspace) await update.mutateAsync({ name: trimmed, slug }); + else await create.mutateAsync({ name: trimmed, slug }); } catch { + onAddress(workspace?.slug); return; } - onContinue(saved); + onContinue(); } return ( diff --git a/packages/web/src/screens/SandboxSettings.stories.tsx b/packages/web/src/screens/SandboxSettings.stories.tsx index 3e359c7d..5fe3e137 100644 --- a/packages/web/src/screens/SandboxSettings.stories.tsx +++ b/packages/web/src/screens/SandboxSettings.stories.tsx @@ -12,6 +12,7 @@ const workspace = { name: "Suga Workspace", slug: "suga", timeZone: "UTC", + setupCompletedAt: "2026-09-01T00:00:00.000Z", createdAt: "2026-09-01T00:00:00.000Z", }; diff --git a/packages/web/src/screens/UsageSettings.stories.tsx b/packages/web/src/screens/UsageSettings.stories.tsx index e69f0121..bb058fef 100644 --- a/packages/web/src/screens/UsageSettings.stories.tsx +++ b/packages/web/src/screens/UsageSettings.stories.tsx @@ -11,6 +11,7 @@ const workspace = { name: "Suga Workspace", slug: "suga", timeZone: "UTC", + setupCompletedAt: "2026-09-01T00:00:00.000Z", createdAt: "2026-09-01T00:00:00.000Z", }; diff --git a/packages/web/src/screens/WebSearchSettings.stories.tsx b/packages/web/src/screens/WebSearchSettings.stories.tsx index e4e3d2b8..a25d0d94 100644 --- a/packages/web/src/screens/WebSearchSettings.stories.tsx +++ b/packages/web/src/screens/WebSearchSettings.stories.tsx @@ -12,6 +12,7 @@ const workspace = { name: "Suga Workspace", slug: "suga", timeZone: "UTC", + setupCompletedAt: "2026-09-01T00:00:00.000Z", createdAt: "2026-09-01T00:00:00.000Z", }; diff --git a/packages/web/src/shell/Rail.stories.tsx b/packages/web/src/shell/Rail.stories.tsx index 7288e74e..52edf6bd 100644 --- a/packages/web/src/shell/Rail.stories.tsx +++ b/packages/web/src/shell/Rail.stories.tsx @@ -8,18 +8,21 @@ const suga = { name: "Suga", slug: "suga", timeZone: "UTC", + setupCompletedAt: "2026-09-01T00:00:00.000Z", }; const nitric = { id: "0199a3a0-0000-7000-8000-000000000002", name: "Nitric", slug: "nitric", timeZone: "UTC", + setupCompletedAt: "2026-09-01T00:00:00.000Z", }; const longName = { id: "0199a3a0-0000-7000-8000-000000000003", name: "The Extremely Thorough Research and Development Collective", slug: "research", timeZone: "UTC", + setupCompletedAt: "2026-09-01T00:00:00.000Z", }; const meta = preview.meta({ @@ -114,7 +117,7 @@ export const OneWorkspace = meta.story({ await expect(screen.getAllByRole("menuitem")).toHaveLength(3); await expect(screen.getByRole("menuitem", { name: "New workspace" })).toHaveAttribute( "href", - "/onboarding/new", + "/onboarding", ); await expect(screen.getByRole("menuitem", { name: "Suga" })).toHaveAttribute( "aria-current", diff --git a/packages/web/src/shell/WorkspaceSwitcher.tsx b/packages/web/src/shell/WorkspaceSwitcher.tsx index fc5731f9..aa8accdf 100644 --- a/packages/web/src/shell/WorkspaceSwitcher.tsx +++ b/packages/web/src/shell/WorkspaceSwitcher.tsx @@ -63,7 +63,7 @@ export function WorkspaceSwitcher({ ), )} - }> + }> New workspace diff --git a/packages/web/src/shell/shell.test.tsx b/packages/web/src/shell/shell.test.tsx index 708788fe..9dc6c009 100644 --- a/packages/web/src/shell/shell.test.tsx +++ b/packages/web/src/shell/shell.test.tsx @@ -64,6 +64,7 @@ describe("the workspace choice", () => { name: "Nitric", slug: "nitric", timeZone: "UTC", + setupCompletedAt: "2026-09-01T00:00:00.000Z", }; it("keeps a workspace choice in memory when storage is unavailable", async () => { @@ -129,7 +130,7 @@ describe("the workspace choice", () => { expect(router.state.location.search).toEqual({ workspace: "nitric" }); }); - it("sets up every step of a workspace for somebody who deleted all of theirs", async () => { + it("keeps somebody with no workspace out of the shell, setting one up with nowhere to cancel to", async () => { client.api.workspaces.list.mockReturnValue(Effect.succeed([])); client.api.workspaces.create.mockImplementation(() => { client.api.workspaces.list.mockReturnValue(Effect.succeed([other])); @@ -138,20 +139,133 @@ describe("the workspace choice", () => { const router = mount(linearPage); const name = await screen.findByLabelText("Workspace name"); - await waitFor(() => expect(router.state.location.pathname).toBe("/onboarding/new")); - // There is no workspace to go back to. + await waitFor(() => expect(router.state.location.pathname).toBe("/onboarding")); + expect(screen.queryByRole("navigation", { name: "Workspace" })).toBeNull(); expect(screen.queryByRole("button", { name: "Cancel" })).toBeNull(); fireEvent.change(name, { target: { value: "Nitric" } }); fireEvent.click(screen.getByRole("button", { name: "Continue" })); expect(await screen.findByRole("heading", { name: "Connect a provider" })).toBeDefined(); - expect(router.state.location.pathname).toBe("/onboarding/new"); expect(screen.queryByRole("button", { name: "Cancel" })).toBeNull(); }); + it("takes somebody who may finish an unfinished workspace's setup back into it", async () => { + client.api.workspaces.list.mockReturnValue( + Effect.succeed([{ ...workspace, setupCompletedAt: null }]), + ); + client.api.pods.list.mockReturnValue(Effect.succeed([personalPod])); + client.api.agents.list.mockReturnValue( + Effect.succeed([{ ...personalAssistant, model: "switched-off" }]), + ); + const router = mount("/"); + + expect(await screen.findByRole("heading", { name: "Connect a provider" })).toBeDefined(); + expect(router.state.location.pathname).toBe("/onboarding"); + expect(router.state.location.search).toEqual({ workspace: workspace.slug }); + }); + + it("finishes an unfinished workspace's setup into its chat, not back into setup", async () => { + client.api.workspaces.list.mockReturnValue( + Effect.succeed([{ ...workspace, setupCompletedAt: null }]), + ); + client.api.pods.list.mockReturnValue(Effect.succeed([personalPod])); + client.api.agents.list.mockReturnValue(Effect.succeed([personalAssistant])); + const named = { ...personalAssistant, name: "Chief", handle: "chief" }; + client.api.agents.update.mockImplementation(() => { + client.api.agents.list.mockReturnValue(Effect.succeed([named])); + return Effect.succeed(named); + }); + client.api.onboarding.complete.mockImplementation(() => { + client.api.workspaces.list.mockReturnValue( + Effect.succeed([{ ...workspace, setupCompletedAt: "2026-10-09T00:00:00.000Z" }]), + ); + return Effect.void; + }); + const router = mount("/"); + + fireEvent.change(await screen.findByLabelText("Name"), { target: { value: "Chief" } }); + fireEvent.click(screen.getByRole("button", { name: "Create bot" })); + fireEvent.click(await screen.findByRole("button", { name: "Skip for now" })); + fireEvent.click(await screen.findByRole("button", { name: "Start chatting" })); + + expect(await screen.findByRole("navigation", { name: "Pods" })).toBeDefined(); + expect(router.state.location.pathname).toBe("/suga/pods/personal/agents/chief"); + }); + + it("says so, rather than opening an unfinished workspace, when it cannot tell who may finish it", async () => { + client.api.workspaces.list.mockReturnValue( + Effect.succeed([{ ...workspace, setupCompletedAt: null }]), + ); + client.api.workspaceAccess.mockReturnValueOnce( + Effect.fail(new Forbidden({ message: "Unavailable" })), + ); + client.api.pods.list.mockReturnValue(Effect.succeed([personalPod])); + client.api.agents.list.mockReturnValue( + Effect.succeed([{ ...personalAssistant, model: "switched-off" }]), + ); + const router = mount("/"); + + fireEvent.click(await screen.findByRole("button", { name: "Try again" })); + expect(screen.queryByRole("navigation", { name: "Pods" })).toBeNull(); + + expect(await screen.findByRole("heading", { name: "Connect a provider" })).toBeDefined(); + expect(router.state.location.pathname).toBe("/onboarding"); + }); + + it("opens an unfinished workspace as it is for somebody invited part way", async () => { + apiAnswers({ role: "member" }); + client.api.workspaces.list.mockReturnValue( + Effect.succeed([{ ...workspace, setupCompletedAt: null }]), + ); + const router = mount(linearPage); + + expect(await screen.findByRole("navigation", { name: "Pods" })).toBeDefined(); + expect(router.state.location.pathname).toBe(linearPage); + }); + + it("cancels an unfinished workspace's setup into another workspace, keeping it for an admin, who may not delete it", async () => { + const unfinished = { ...other, setupCompletedAt: null }; + client.api.workspaces.list.mockReturnValue(Effect.succeed([workspace, unfinished])); + const router = mount(`/${unfinished.slug}/agents`); + + fireEvent.click(await screen.findByRole("button", { name: "Cancel" })); + + await waitFor(() => expect(router.state.location.pathname).toMatch(/^\/suga\//)); + expect(client.api.workspaces.delete).not.toHaveBeenCalled(); + }); + + it("opens a workspace that is set up from the root, rather than the one being set up", async () => { + const unfinished = { ...other, setupCompletedAt: null }; + client.api.workspaces.list.mockReturnValue(Effect.succeed([workspace, unfinished])); + chooseWorkspace(unfinished.id); + const router = mount("/"); + + expect(await screen.findByRole("navigation", { name: "Pods" })).toBeDefined(); + expect(router.state.location.pathname).toMatch(/^\/suga\//); + }); + + it("comes back to a workspace whose save a reload interrupted", async () => { + client.api.workspaces.list.mockReturnValue(Effect.succeed([])); + client.api.workspaces.create.mockReturnValue(Effect.never); + const router = mount("/onboarding"); + fireEvent.change(await screen.findByLabelText("Workspace name"), { + target: { value: "Nitric" }, + }); + fireEvent.click(screen.getByRole("button", { name: "Continue" })); + await waitFor(() => expect(router.state.location.search).toEqual({ workspace: "nitric" })); + + // The server saves it while the page reloads. + cleanup(); + client.api.workspaces.list.mockReturnValue(Effect.succeed([other])); + mount(router.state.location.href); + + expect(await screen.findByRole("heading", { name: "Connect a provider" })).toBeDefined(); + expect(client.api.workspaces.create).toHaveBeenCalledOnce(); + }); + it("picks setting up a new workspace back up after a reload", async () => { client.api.workspaces.list.mockReturnValue(Effect.succeed([workspace, other])); - mount("/onboarding/new?workspace=nitric"); + mount("/onboarding?workspace=nitric"); expect(await screen.findByRole("button", { name: "Cancel" })).toBeDefined(); await waitFor(() => expect(localStorage.getItem("sugabots-workspace")).toBe(other.id)); @@ -159,27 +273,30 @@ describe("the workspace choice", () => { expect(client.api.workspaces.create).not.toHaveBeenCalled(); }); - it("keeps a workspace it did not make itself when cancelled", async () => { - // Setup reached again by its address may be for a workspace finished and shared since. + it("keeps a workspace whose setup is finished when its owner cancels setting it up", async () => { + // Setup reached by its address, from a bookmark or Back, for a workspace set up since. + apiAnswers({ role: "owner" }); client.api.workspaces.list.mockReturnValue(Effect.succeed([workspace, other])); - const router = mount("/onboarding/new?workspace=nitric"); + const router = mount("/onboarding?workspace=nitric"); fireEvent.click(await screen.findByRole("button", { name: "Cancel" })); - await waitFor(() => expect(router.state.location.pathname).not.toBe("/onboarding/new")); + await waitFor(() => expect(router.state.location.pathname).not.toBe("/onboarding")); expect(client.api.workspaces.delete).not.toHaveBeenCalled(); }); it("asks before leaving a workspace made but not set up", async () => { - client.api.workspaces.list.mockReturnValue(Effect.succeed([workspace, other])); - const router = mount("/onboarding/new?workspace=nitric"); + client.api.workspaces.list.mockReturnValue( + Effect.succeed([workspace, { ...other, setupCompletedAt: null }]), + ); + const router = mount("/onboarding?workspace=nitric"); await screen.findByRole("button", { name: "Cancel" }); void router.navigate({ to: linearPage }); const dialog = await screen.findByRole("dialog", { name: "Leave setup?" }); fireEvent.click(within(dialog).getByRole("button", { name: "Cancel" })); await waitFor(() => expect(screen.queryByRole("dialog", { name: "Leave setup?" })).toBeNull()); - expect(router.state.location.pathname).toBe("/onboarding/new"); + expect(router.state.location.pathname).toBe("/onboarding"); void router.navigate({ to: linearPage }); fireEvent.click( @@ -202,10 +319,12 @@ describe("the workspace choice", () => { expect(client.api.workspaces.create).not.toHaveBeenCalled(); }); - it("deletes the workspace it made when cancelled, and goes back to where it came from", async () => { + it("deletes the workspace it made when its owner cancels, and goes back to where it came from", async () => { + apiAnswers({ role: "owner" }); + const made = { ...other, setupCompletedAt: null }; client.api.workspaces.create.mockImplementation(() => { - client.api.workspaces.list.mockReturnValue(Effect.succeed([workspace, other])); - return Effect.succeed(other); + client.api.workspaces.list.mockReturnValue(Effect.succeed([workspace, made])); + return Effect.succeed(made); }); client.api.workspaces.delete.mockReturnValue(Effect.void); const router = mount(linearPage); @@ -220,6 +339,21 @@ describe("the workspace choice", () => { expect(localStorage.getItem("sugabots-workspace")).toBe(workspace.id); }); + it("deletes an unfinished workspace when its owner cancels setting it up after a reload", async () => { + apiAnswers({ role: "owner" }); + client.api.workspaces.list.mockReturnValue( + Effect.succeed([workspace, { ...other, setupCompletedAt: null }]), + ); + client.api.workspaces.delete.mockReturnValue(Effect.void); + const router = mount("/onboarding?workspace=nitric"); + + await screen.findByRole("heading", { name: "Connect a provider" }); + fireEvent.click(screen.getByRole("button", { name: "Cancel" })); + + await waitFor(() => expect(router.state.location.pathname).not.toBe("/onboarding")); + expect(client.api.workspaces.delete).toHaveBeenCalledWith({ params: { workspace: other.id } }); + }); + it("lets the owner delete a workspace, then opens another", async () => { apiAnswers({ role: "owner" }); client.api.workspaces.list.mockReturnValue(Effect.succeed([workspace, other])); @@ -246,7 +380,7 @@ describe("the workspace choice", () => { expect(screen.queryByRole("button", { name: "Delete workspace" })).toBeNull(); }); - it("says so when the address is already taken, and stays on the step", async () => { + it("says so when the address is already taken, and stays on the step without it", async () => { client.api.workspaces.create.mockReturnValue( Effect.fail(new Conflict({ message: "already exists" })), ); @@ -257,6 +391,7 @@ describe("the workspace choice", () => { expect(await screen.findByText("That workspace address is already taken.")).toBeDefined(); expect(screen.getByRole("heading", { name: "Name your workspace" })).toBeDefined(); + expect(router.state.location.search).toEqual({}); }); async function startNewWorkspace(router: ReturnType) { @@ -265,7 +400,7 @@ describe("the workspace choice", () => { await within(rail).findByRole("button", { name: `Workspace: ${workspace.name}` }), ); fireEvent.click(await screen.findByRole("menuitem", { name: "New workspace" })); - await waitFor(() => expect(router.state.location.pathname).toBe("/onboarding/new")); + await waitFor(() => expect(router.state.location.pathname).toBe("/onboarding")); await screen.findByRole("heading", { name: "Name your workspace" }); return screen.getByLabelText("Workspace name"); } @@ -630,18 +765,7 @@ describe("routes", () => { await waitFor(() => expect(router.state.location.pathname).toBe("/suga/agents")); }); - it("keeps a signed-in user without a workspace out of the shell", async () => { - client.api.workspaces.list.mockReturnValue(Effect.succeed([])); - client.api.onboarding.status.mockReturnValue(Effect.succeed({ completed: false })); - const router = mount(linearPage); - - expect(await screen.findByRole("heading", { name: "Name your workspace" })).toBeDefined(); - await waitFor(() => expect(router.state.location.pathname).toBe("/onboarding")); - expect(screen.queryByRole("navigation", { name: "Workspace" })).toBeNull(); - }); - it("walks a new workspace from its first bot to its chat", async () => { - client.api.onboarding.status.mockReturnValue(Effect.succeed({ completed: false })); client.api.pods.list.mockReturnValue(Effect.succeed([personalPod])); client.api.agents.list.mockReturnValue(Effect.succeed([personalAssistant])); const named = { @@ -654,8 +778,8 @@ describe("routes", () => { client.api.agents.list.mockReturnValue(Effect.succeed([named])); return Effect.succeed(named); }); - client.api.onboarding.complete.mockReturnValue(Effect.succeed({ completed: true })); - const router = mount(linearPage); + client.api.onboarding.complete.mockReturnValue(Effect.void); + const router = mount(`/onboarding?workspace=${workspace.slug}`); expect(await screen.findByRole("heading", { name: "Make your first bot" })).toBeDefined(); await waitFor(() => expect(router.state.location.pathname).toBe("/onboarding")); @@ -679,7 +803,6 @@ describe("routes", () => { fireEvent.click(screen.getByRole("button", { name: "Skip for now" })); expect(await screen.findByRole("heading", { name: "Chief is ready" })).toBeDefined(); - client.api.onboarding.status.mockReturnValue(Effect.succeed({ completed: true })); fireEvent.click(screen.getByRole("button", { name: "Start chatting" })); await waitFor(() => @@ -688,14 +811,13 @@ describe("routes", () => { }); it("invites the addresses typed, and says how many it will send", async () => { - client.api.onboarding.status.mockReturnValue(Effect.succeed({ completed: false })); client.api.pods.list.mockReturnValue(Effect.succeed([personalPod])); client.api.agents.list.mockReturnValue( Effect.succeed([{ ...personalAssistant, name: "Chief" }]), ); client.api.agents.update.mockReturnValue(Effect.succeed(personalAssistant)); client.api.workspaces.invite.mockReturnValue(Effect.succeed({ id: "an-invitation" })); - mount(linearPage); + mount(`/onboarding?workspace=${workspace.slug}`); fireEvent.click(await screen.findByRole("button", { name: "Create bot" })); const emails = await screen.findByLabelText("Email addresses"); @@ -718,19 +840,17 @@ describe("routes", () => { }); it("starts at the model step while the first bot has no model, and will not skip it", async () => { - client.api.onboarding.status.mockReturnValue(Effect.succeed({ completed: false })); client.api.pods.list.mockReturnValue(Effect.succeed([personalPod])); client.api.agents.list.mockReturnValue( Effect.succeed([{ ...personalAssistant, model: "switched-off" }]), ); - mount(linearPage); + mount(`/onboarding?workspace=${workspace.slug}`); expect(await screen.findByRole("heading", { name: "Connect a provider" })).toBeDefined(); expect(screen.queryByRole("button", { name: "Skip for now" })).toBeNull(); }); it("adds the first provider from the Models picker, then switches on the model chosen for the first bot", async () => { - client.api.onboarding.status.mockReturnValue(Effect.succeed({ completed: false })); client.api.pods.list.mockReturnValue(Effect.succeed([personalPod])); client.api.agents.list.mockReturnValue(Effect.succeed([personalAssistant])); client.api.modelProviders.listEnabledModels.mockReturnValue( @@ -766,7 +886,7 @@ describe("routes", () => { client.api.agents.update.mockReturnValue( Effect.succeed({ ...personalAssistant, model: "mistral/large" }), ); - mount(linearPage); + mount(`/onboarding?workspace=${workspace.slug}`); fireEvent.click(await screen.findByRole("button", { name: "Choose a provider" })); const picker = await screen.findByRole("dialog", { name: "Add provider" }); @@ -1748,7 +1868,6 @@ describe("workspace settings", () => { .mockReturnValue({ ...browserOptions, timeZone: "Australia/Sydney" }); onTestFinished(() => browser.mockRestore()); client.api.workspaces.list.mockReturnValue(Effect.succeed([])); - client.api.onboarding.status.mockReturnValue(Effect.succeed({ completed: false })); client.api.workspaces.create.mockReturnValue( Effect.succeed({ id: "0199a3a0-0000-7000-8000-0000000000f9", @@ -1774,7 +1893,6 @@ describe("workspace settings", () => { it("keeps a rejected workspace name and creation form open", async () => { client.api.workspaces.list.mockReturnValue(Effect.succeed([])); - client.api.onboarding.status.mockReturnValue(Effect.succeed({ completed: false })); client.api.workspaces.create.mockReturnValue(Effect.die(new Error("Offline"))); mount("/suga/settings"); const name = await screen.findByLabelText("Workspace name"); diff --git a/packages/web/src/story-app.tsx b/packages/web/src/story-app.tsx index 6ce40814..7d9004df 100644 --- a/packages/web/src/story-app.tsx +++ b/packages/web/src/story-app.tsx @@ -52,6 +52,7 @@ export const storyWorkspace = { name: "Nitric", slug: "nitric", timeZone: "UTC", + setupCompletedAt: "2026-09-01T00:00:00.000Z", }; /** Every pod, with the viewer as the Personal pod's owner, and every bot. */ @@ -198,7 +199,6 @@ export interface StoryAppData { messages?: Record; providers?: ModelProvider[]; models?: (typeof storyModel)[]; - onboarded?: boolean; /** The viewer's referral link, where the installation signs people up by referral. */ referralLink?: string; } @@ -230,7 +230,6 @@ export function appHandlers(data: StoryAppData = {}): RequestHandler[] { ]), ), http.get(api("/workspaces/:workspace/invitations"), () => HttpResponse.json([])), - http.get(api("/onboarding"), () => HttpResponse.json({ completed: data.onboarded ?? true })), http.get(api("/referral-link"), () => HttpResponse.json({ url: data.referralLink ?? null })), http.get(api("/workspaces/:workspace/me"), () => HttpResponse.json({ diff --git a/packages/web/src/test-api.tsx b/packages/web/src/test-api.tsx index b9ca2e4b..85dc04e2 100644 --- a/packages/web/src/test-api.tsx +++ b/packages/web/src/test-api.tsx @@ -64,7 +64,13 @@ export const jye: SessionUser = { const WORKSPACE = "0199a3a0-0000-7000-8000-000000000001"; -export const workspace = { id: WORKSPACE, name: "Suga Workspace", slug: "suga", timeZone: "UTC" }; +export const workspace = { + id: WORKSPACE, + name: "Suga Workspace", + slug: "suga", + timeZone: "UTC", + setupCompletedAt: "2026-09-01T00:00:00.000Z", +}; /** Every pod permission, as an admin gets them. */ const ADMIN_IN_POD: PodPermissions = { @@ -361,7 +367,6 @@ export function apiAnswers({ role = "admin" }: { role?: WorkspaceRole } = {}): v }, ]), ); - client.api.onboarding.status.mockReturnValue(Effect.succeed({ completed: true })); client.api.onboarding.completeInvite.mockImplementation(() => client.api.workspaces.acceptInvitation.mock.calls.length > 0 ? Effect.succeed({ workspaceId: WORKSPACE }) diff --git a/packages/web/src/test-client.ts b/packages/web/src/test-client.ts index b18efc0e..4d075b8d 100644 --- a/packages/web/src/test-client.ts +++ b/packages/web/src/test-client.ts @@ -60,7 +60,6 @@ export const client = { resetLink: vi.fn(), }, onboarding: { - status: vi.fn(), complete: vi.fn(), completeInvite: vi.fn(), },