From d359572ab303af8067005aa5fe63a0f0943311b4 Mon Sep 17 00:00:00 2001 From: Evgeny Shurakov Date: Tue, 9 Jun 2026 10:17:19 +0200 Subject: [PATCH] feat(cloud-agent): expand Kilo facade session API Add repository-scoped visible sessions with bootstrap state and stream persisted metadata updates so public session reads stay current. --- .../src/migrations/0163_busy_edwin_jarvis.sql | 3 + .../db/src/migrations/meta/0163_snapshot.json | 30739 ++++++++++++++++ packages/db/src/migrations/meta/_journal.json | 7 + packages/db/src/schema.ts | 6 + .../src/rpc-contract.ts | 110 + .../cloud-agent-next/src/execution/types.ts | 2 + .../src/kilo-facade/basic-command.test.ts | 112 + .../src/kilo-facade/basic-command.ts | 123 + .../src/kilo-facade/contracts.ts | 88 + .../src/kilo-facade/handlers/abort/handler.ts | 34 + .../handlers/command-list/handler.test.ts | 130 + .../handlers/command-list/handler.ts | 57 + .../handlers/command/handler.test.ts | 369 + .../kilo-facade/handlers/command/handler.ts | 232 + .../handlers/global-event/handler.ts | 18 + .../handlers/global-feed-upgrade/handler.ts | 103 + .../handlers/global-feed-upgrade/message.ts | 105 + .../handlers/permission/handler.test.ts | 337 + .../handlers/permission/handler.ts | 120 + .../handlers/prompt-async/handler.ts | 186 + .../handlers/question/handler.test.ts | 219 + .../kilo-facade/handlers/question/handler.ts | 150 + .../handlers/session-detail/handler.ts | 142 + .../handlers/session-event/handler.ts | 49 + .../handlers/session-list/handler.ts | 82 + .../handlers/session-message/handler.test.ts | 324 + .../handlers/session-message/handler.ts | 132 + .../handlers/session-messages/handler.ts | 246 + .../handlers/session-status/handler.ts | 42 + .../handlers/session-status/tracking.test.ts | 85 + .../handlers/session-status/tracking.ts | 89 + .../handlers/suggestion/handler.test.ts | 308 + .../handlers/suggestion/handler.ts | 123 + .../src/kilo-facade/http-contract.ts | 163 + .../src/kilo-facade/live-interaction.test.ts | 325 + .../src/kilo-facade/live-interaction.ts | 199 + .../src/kilo-facade/metadata-feed.test.ts | 127 + .../src/kilo-facade/metadata-feed.ts | 166 + .../kilo-facade/metadata-projector.test.ts | 197 + .../src/kilo-facade/metadata-projector.ts | 379 + .../src/kilo-facade/ownership.ts | 46 + .../kilo-facade/request-dispatcher.test.ts | 330 + .../src/kilo-facade/request-dispatcher.ts | 161 + .../src/kilo-facade/session-proxy.ts | 20 - .../src/kilo-facade/session-read-source.ts | 62 + .../stored-message-validation.test.ts | 106 + .../kilo-facade/stored-message-validation.ts | 54 + .../src/kilo-facade/user-kilo-facade.test.ts | 874 +- .../src/kilo-facade/user-kilo-facade.ts | 1544 +- .../src/kilo/wrapper-client.test.ts | 2 + .../src/persistence/CloudAgentSession.ts | 13 + .../src/session-prepare.test.ts | 34 +- .../src/session-service.test.ts | 85 + .../cloud-agent-next/src/session-service.ts | 7 +- .../src/session/pending-messages.test.ts | 19 + .../src/session/pending-messages.ts | 29 +- .../src/session/session-message-queue.test.ts | 105 + .../src/session/session-message-queue.ts | 11 +- .../src/session/session-message-state.test.ts | 19 + .../src/session/session-message-state.ts | 3 + .../src/session/session-registration.ts | 9 +- .../cloud-agent-next/src/shared/protocol.ts | 15 +- .../src/shared/wrapper-bootstrap.ts | 1 + .../test/e2e/sdk-basic-chat.ts | 133 + .../integration/kilo-facade-runtime.test.ts | 87 +- .../session/derive-queued-messages.test.ts | 35 + services/cloud-agent-next/test/test-worker.ts | 2 +- .../test/unit/wrapper/server.test.ts | 53 + .../wrapper/src/global-feed.test.ts | 85 +- .../wrapper/src/global-feed.ts | 32 +- .../cloud-agent-next/wrapper/src/kilo-api.ts | 43 +- .../cloud-agent-next/wrapper/src/server.ts | 13 +- services/session-ingest/src/app.ts | 35 +- .../session-ingest/src/dos/SessionIngestDO.ts | 5 + .../src/dos/kilo-sdk-materialization.ts | 201 +- services/session-ingest/src/index.test.ts | 155 + services/session-ingest/src/internal-auth.ts | 17 + .../session-ingest/src/internal-routes.ts | 24 + .../src/internal-user-events-entrypoint.ts | 13 + .../src/internal-user-events.ts | 32 + .../session-ingest/src/queue-consumer.test.ts | 237 + services/session-ingest/src/queue-consumer.ts | 53 +- .../src/session-ingest-rpc.test.ts | 432 +- .../session-ingest/src/session-ingest-rpc.ts | 90 +- .../types/user-connection-protocol.test.ts | 63 + .../src/types/user-connection-protocol.ts | 35 +- .../integration/session-ingest-do.test.ts | 387 +- .../session-ingest-rpc-websocket.test.ts | 74 + services/session-ingest/test/test-worker.ts | 7 +- services/session-ingest/wrangler.test.jsonc | 24 + 90 files changed, 40757 insertions(+), 1587 deletions(-) create mode 100644 packages/db/src/migrations/0163_busy_edwin_jarvis.sql create mode 100644 packages/db/src/migrations/meta/0163_snapshot.json create mode 100644 services/cloud-agent-next/src/kilo-facade/basic-command.test.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/basic-command.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/contracts.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/handlers/abort/handler.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/handlers/command-list/handler.test.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/handlers/command-list/handler.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/handlers/command/handler.test.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/handlers/command/handler.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/handlers/global-event/handler.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/handlers/global-feed-upgrade/handler.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/handlers/global-feed-upgrade/message.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/handlers/permission/handler.test.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/handlers/permission/handler.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/handlers/prompt-async/handler.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/handlers/question/handler.test.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/handlers/question/handler.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/handlers/session-detail/handler.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/handlers/session-event/handler.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/handlers/session-list/handler.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/handlers/session-message/handler.test.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/handlers/session-message/handler.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/handlers/session-messages/handler.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/handlers/session-status/handler.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/handlers/session-status/tracking.test.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/handlers/session-status/tracking.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/handlers/suggestion/handler.test.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/handlers/suggestion/handler.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/http-contract.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/live-interaction.test.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/live-interaction.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/metadata-feed.test.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/metadata-feed.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/metadata-projector.test.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/metadata-projector.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/ownership.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/request-dispatcher.test.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/request-dispatcher.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/session-read-source.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/stored-message-validation.test.ts create mode 100644 services/cloud-agent-next/src/kilo-facade/stored-message-validation.ts create mode 100644 services/session-ingest/src/internal-auth.ts create mode 100644 services/session-ingest/src/internal-routes.ts create mode 100644 services/session-ingest/src/internal-user-events-entrypoint.ts create mode 100644 services/session-ingest/src/internal-user-events.ts create mode 100644 services/session-ingest/test/integration/session-ingest-rpc-websocket.test.ts diff --git a/packages/db/src/migrations/0163_busy_edwin_jarvis.sql b/packages/db/src/migrations/0163_busy_edwin_jarvis.sql new file mode 100644 index 0000000000..87b0e2905a --- /dev/null +++ b/packages/db/src/migrations/0163_busy_edwin_jarvis.sql @@ -0,0 +1,3 @@ +COMMIT;--> statement-breakpoint +CREATE INDEX CONCURRENTLY IF NOT EXISTS "IDX_cli_sessions_v2_user_git_url_updated" ON "cli_sessions_v2" USING btree ("kilo_user_id","git_url","updated_at") WHERE "cli_sessions_v2"."git_url" IS NOT NULL AND "cli_sessions_v2"."parent_session_id" IS NULL AND "cli_sessions_v2"."cloud_agent_session_id" IS NOT NULL;--> statement-breakpoint +BEGIN; diff --git a/packages/db/src/migrations/meta/0163_snapshot.json b/packages/db/src/migrations/meta/0163_snapshot.json new file mode 100644 index 0000000000..af97a6fe8b --- /dev/null +++ b/packages/db/src/migrations/meta/0163_snapshot.json @@ -0,0 +1,30739 @@ +{ + "id": "457ddab0-c15b-441e-9dbb-91c7527f162f", + "prevId": "534bd4b9-fae7-404b-bf1d-b91383cd388c", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.agent_configs": { + "name": "agent_configs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "agent_type": { + "name": "agent_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "runtime_state": { + "name": "runtime_state", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_agent_configs_org_id": { + "name": "IDX_agent_configs_org_id", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_agent_configs_owned_by_user_id": { + "name": "IDX_agent_configs_owned_by_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_agent_configs_agent_type": { + "name": "IDX_agent_configs_agent_type", + "columns": [ + { + "expression": "agent_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_agent_configs_platform": { + "name": "IDX_agent_configs_platform", + "columns": [ + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_configs_owned_by_organization_id_organizations_id_fk": { + "name": "agent_configs_owned_by_organization_id_organizations_id_fk", + "tableFrom": "agent_configs", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "agent_configs_owned_by_user_id_kilocode_users_id_fk": { + "name": "agent_configs_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "agent_configs", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_agent_configs_org_agent_platform": { + "name": "UQ_agent_configs_org_agent_platform", + "nullsNotDistinct": false, + "columns": [ + "owned_by_organization_id", + "agent_type", + "platform" + ] + }, + "UQ_agent_configs_user_agent_platform": { + "name": "UQ_agent_configs_user_agent_platform", + "nullsNotDistinct": false, + "columns": [ + "owned_by_user_id", + "agent_type", + "platform" + ] + } + }, + "policies": {}, + "checkConstraints": { + "agent_configs_owner_check": { + "name": "agent_configs_owner_check", + "value": "(\n (\"agent_configs\".\"owned_by_user_id\" IS NOT NULL AND \"agent_configs\".\"owned_by_organization_id\" IS NULL) OR\n (\"agent_configs\".\"owned_by_user_id\" IS NULL AND \"agent_configs\".\"owned_by_organization_id\" IS NOT NULL)\n )" + }, + "agent_configs_agent_type_check": { + "name": "agent_configs_agent_type_check", + "value": "\"agent_configs\".\"agent_type\" IN ('code_review', 'auto_triage', 'auto_fix', 'security_scan')" + } + }, + "isRLSEnabled": false + }, + "public.agent_environment_profile_agents": { + "name": "agent_environment_profile_agents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "profile_id": { + "name": "profile_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_agent_env_profile_agents_profile_id": { + "name": "IDX_agent_env_profile_agents_profile_id", + "columns": [ + { + "expression": "profile_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_environment_profile_agents_profile_id_agent_environment_profiles_id_fk": { + "name": "agent_environment_profile_agents_profile_id_agent_environment_profiles_id_fk", + "tableFrom": "agent_environment_profile_agents", + "tableTo": "agent_environment_profiles", + "columnsFrom": [ + "profile_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_agent_env_profile_agents_profile_slug": { + "name": "UQ_agent_env_profile_agents_profile_slug", + "nullsNotDistinct": false, + "columns": [ + "profile_id", + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_environment_profile_commands": { + "name": "agent_environment_profile_commands", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "profile_id": { + "name": "profile_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sequence": { + "name": "sequence", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_agent_env_profile_commands_profile_id": { + "name": "IDX_agent_env_profile_commands_profile_id", + "columns": [ + { + "expression": "profile_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_environment_profile_commands_profile_id_agent_environment_profiles_id_fk": { + "name": "agent_environment_profile_commands_profile_id_agent_environment_profiles_id_fk", + "tableFrom": "agent_environment_profile_commands", + "tableTo": "agent_environment_profiles", + "columnsFrom": [ + "profile_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_agent_env_profile_commands_profile_sequence": { + "name": "UQ_agent_env_profile_commands_profile_sequence", + "nullsNotDistinct": false, + "columns": [ + "profile_id", + "sequence" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_environment_profile_kilo_commands": { + "name": "agent_environment_profile_kilo_commands", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "profile_id": { + "name": "profile_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "template": { + "name": "template", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "agent": { + "name": "agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subtask": { + "name": "subtask", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_agent_env_profile_kilo_cmds_profile_id": { + "name": "IDX_agent_env_profile_kilo_cmds_profile_id", + "columns": [ + { + "expression": "profile_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_environment_profile_kilo_commands_profile_id_agent_environment_profiles_id_fk": { + "name": "agent_environment_profile_kilo_commands_profile_id_agent_environment_profiles_id_fk", + "tableFrom": "agent_environment_profile_kilo_commands", + "tableTo": "agent_environment_profiles", + "columnsFrom": [ + "profile_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_agent_env_profile_kilo_cmds_profile_name": { + "name": "UQ_agent_env_profile_kilo_cmds_profile_name", + "nullsNotDistinct": false, + "columns": [ + "profile_id", + "name" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_environment_profile_mcp_servers": { + "name": "agent_environment_profile_mcp_servers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "profile_id": { + "name": "profile_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "timeout": { + "name": "timeout", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_agent_env_profile_mcp_servers_profile_id": { + "name": "IDX_agent_env_profile_mcp_servers_profile_id", + "columns": [ + { + "expression": "profile_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_environment_profile_mcp_servers_profile_id_agent_environment_profiles_id_fk": { + "name": "agent_environment_profile_mcp_servers_profile_id_agent_environment_profiles_id_fk", + "tableFrom": "agent_environment_profile_mcp_servers", + "tableTo": "agent_environment_profiles", + "columnsFrom": [ + "profile_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_agent_env_profile_mcp_servers_profile_name": { + "name": "UQ_agent_env_profile_mcp_servers_profile_name", + "nullsNotDistinct": false, + "columns": [ + "profile_id", + "name" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_environment_profile_repo_bindings": { + "name": "agent_environment_profile_repo_bindings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "repo_full_name": { + "name": "repo_full_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "profile_id": { + "name": "profile_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_agent_env_profile_repo_bindings_user": { + "name": "UQ_agent_env_profile_repo_bindings_user", + "columns": [ + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"agent_environment_profile_repo_bindings\".\"owned_by_user_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_agent_env_profile_repo_bindings_org": { + "name": "UQ_agent_env_profile_repo_bindings_org", + "columns": [ + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"agent_environment_profile_repo_bindings\".\"owned_by_organization_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_environment_profile_repo_bindings_profile_id_agent_environment_profiles_id_fk": { + "name": "agent_environment_profile_repo_bindings_profile_id_agent_environment_profiles_id_fk", + "tableFrom": "agent_environment_profile_repo_bindings", + "tableTo": "agent_environment_profiles", + "columnsFrom": [ + "profile_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "agent_environment_profile_repo_bindings_owned_by_organization_id_organizations_id_fk": { + "name": "agent_environment_profile_repo_bindings_owned_by_organization_id_organizations_id_fk", + "tableFrom": "agent_environment_profile_repo_bindings", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "agent_environment_profile_repo_bindings_owned_by_user_id_kilocode_users_id_fk": { + "name": "agent_environment_profile_repo_bindings_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "agent_environment_profile_repo_bindings", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "agent_env_profile_repo_bindings_owner_check": { + "name": "agent_env_profile_repo_bindings_owner_check", + "value": "(\n (\"agent_environment_profile_repo_bindings\".\"owned_by_user_id\" IS NOT NULL AND \"agent_environment_profile_repo_bindings\".\"owned_by_organization_id\" IS NULL) OR\n (\"agent_environment_profile_repo_bindings\".\"owned_by_user_id\" IS NULL AND \"agent_environment_profile_repo_bindings\".\"owned_by_organization_id\" IS NOT NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.agent_environment_profile_skills": { + "name": "agent_environment_profile_skills", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "profile_id": { + "name": "profile_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_type": { + "name": "source_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_url": { + "name": "source_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "raw_markdown": { + "name": "raw_markdown", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "files": { + "name": "files", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_agent_env_profile_skills_profile_id": { + "name": "IDX_agent_env_profile_skills_profile_id", + "columns": [ + { + "expression": "profile_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_environment_profile_skills_profile_id_agent_environment_profiles_id_fk": { + "name": "agent_environment_profile_skills_profile_id_agent_environment_profiles_id_fk", + "tableFrom": "agent_environment_profile_skills", + "tableTo": "agent_environment_profiles", + "columnsFrom": [ + "profile_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_agent_env_profile_skills_profile_name": { + "name": "UQ_agent_env_profile_skills_profile_name", + "nullsNotDistinct": false, + "columns": [ + "profile_id", + "name" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_environment_profile_vars": { + "name": "agent_environment_profile_vars", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "profile_id": { + "name": "profile_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_secret": { + "name": "is_secret", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_agent_env_profile_vars_profile_id": { + "name": "IDX_agent_env_profile_vars_profile_id", + "columns": [ + { + "expression": "profile_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_environment_profile_vars_profile_id_agent_environment_profiles_id_fk": { + "name": "agent_environment_profile_vars_profile_id_agent_environment_profiles_id_fk", + "tableFrom": "agent_environment_profile_vars", + "tableTo": "agent_environment_profiles", + "columnsFrom": [ + "profile_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_agent_env_profile_vars_profile_key": { + "name": "UQ_agent_env_profile_vars_profile_key", + "nullsNotDistinct": false, + "columns": [ + "profile_id", + "key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_environment_profiles": { + "name": "agent_environment_profiles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_agent_env_profiles_org_name": { + "name": "UQ_agent_env_profiles_org_name", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"agent_environment_profiles\".\"owned_by_organization_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_agent_env_profiles_user_name": { + "name": "UQ_agent_env_profiles_user_name", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"agent_environment_profiles\".\"owned_by_user_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_agent_env_profiles_org_default": { + "name": "UQ_agent_env_profiles_org_default", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"agent_environment_profiles\".\"is_default\" = true AND \"agent_environment_profiles\".\"owned_by_organization_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_agent_env_profiles_user_default": { + "name": "UQ_agent_env_profiles_user_default", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"agent_environment_profiles\".\"is_default\" = true AND \"agent_environment_profiles\".\"owned_by_user_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_agent_env_profiles_org_id": { + "name": "IDX_agent_env_profiles_org_id", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_agent_env_profiles_user_id": { + "name": "IDX_agent_env_profiles_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_agent_env_profiles_created_by_user_id": { + "name": "IDX_agent_env_profiles_created_by_user_id", + "columns": [ + { + "expression": "created_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_environment_profiles_owned_by_organization_id_organizations_id_fk": { + "name": "agent_environment_profiles_owned_by_organization_id_organizations_id_fk", + "tableFrom": "agent_environment_profiles", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "agent_environment_profiles_owned_by_user_id_kilocode_users_id_fk": { + "name": "agent_environment_profiles_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "agent_environment_profiles", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "agent_env_profiles_owner_check": { + "name": "agent_env_profiles_owner_check", + "value": "(\n (\"agent_environment_profiles\".\"owned_by_user_id\" IS NOT NULL AND \"agent_environment_profiles\".\"owned_by_organization_id\" IS NULL) OR\n (\"agent_environment_profiles\".\"owned_by_user_id\" IS NULL AND \"agent_environment_profiles\".\"owned_by_organization_id\" IS NOT NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.api_kind": { + "name": "api_kind", + "schema": "", + "columns": { + "api_kind_id": { + "name": "api_kind_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "api_kind": { + "name": "api_kind", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UQ_api_kind": { + "name": "UQ_api_kind", + "columns": [ + { + "expression": "api_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.api_request_log": { + "name": "api_request_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status_code": { + "name": "status_code", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "request": { + "name": "request", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "response": { + "name": "response", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_api_request_log_created_at": { + "name": "idx_api_request_log_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.app_builder_feedback": { + "name": "app_builder_feedback", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "preview_status": { + "name": "preview_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_streaming": { + "name": "is_streaming", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "message_count": { + "name": "message_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "feedback_text": { + "name": "feedback_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "recent_messages": { + "name": "recent_messages", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_app_builder_feedback_created_at": { + "name": "IDX_app_builder_feedback_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_app_builder_feedback_kilo_user_id": { + "name": "IDX_app_builder_feedback_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_app_builder_feedback_project_id": { + "name": "IDX_app_builder_feedback_project_id", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "app_builder_feedback_kilo_user_id_kilocode_users_id_fk": { + "name": "app_builder_feedback_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "app_builder_feedback", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "app_builder_feedback_project_id_app_builder_projects_id_fk": { + "name": "app_builder_feedback_project_id_app_builder_projects_id_fk", + "tableFrom": "app_builder_feedback", + "tableTo": "app_builder_projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.app_builder_project_sessions": { + "name": "app_builder_project_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "cloud_agent_session_id": { + "name": "cloud_agent_session_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "worker_version": { + "name": "worker_version", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'v2'" + } + }, + "indexes": { + "IDX_app_builder_project_sessions_project_id": { + "name": "IDX_app_builder_project_sessions_project_id", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "app_builder_project_sessions_project_id_app_builder_projects_id_fk": { + "name": "app_builder_project_sessions_project_id_app_builder_projects_id_fk", + "tableFrom": "app_builder_project_sessions", + "tableTo": "app_builder_projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_app_builder_project_sessions_cloud_agent_session_id": { + "name": "UQ_app_builder_project_sessions_cloud_agent_session_id", + "nullsNotDistinct": false, + "columns": [ + "cloud_agent_session_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.app_builder_projects": { + "name": "app_builder_projects", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model_id": { + "name": "model_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "template": { + "name": "template", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deployment_id": { + "name": "deployment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "last_message_at": { + "name": "last_message_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "git_repo_full_name": { + "name": "git_repo_full_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "git_platform_integration_id": { + "name": "git_platform_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "migrated_at": { + "name": "migrated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_app_builder_projects_created_by_user_id": { + "name": "IDX_app_builder_projects_created_by_user_id", + "columns": [ + { + "expression": "created_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_app_builder_projects_owned_by_user_id": { + "name": "IDX_app_builder_projects_owned_by_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_app_builder_projects_owned_by_organization_id": { + "name": "IDX_app_builder_projects_owned_by_organization_id", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_app_builder_projects_created_at": { + "name": "IDX_app_builder_projects_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_app_builder_projects_last_message_at": { + "name": "IDX_app_builder_projects_last_message_at", + "columns": [ + { + "expression": "last_message_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_app_builder_projects_git_repo_integration": { + "name": "IDX_app_builder_projects_git_repo_integration", + "columns": [ + { + "expression": "git_repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "git_platform_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"app_builder_projects\".\"git_repo_full_name\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "app_builder_projects_owned_by_user_id_kilocode_users_id_fk": { + "name": "app_builder_projects_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "app_builder_projects", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "app_builder_projects_owned_by_organization_id_organizations_id_fk": { + "name": "app_builder_projects_owned_by_organization_id_organizations_id_fk", + "tableFrom": "app_builder_projects", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "app_builder_projects_deployment_id_deployments_id_fk": { + "name": "app_builder_projects_deployment_id_deployments_id_fk", + "tableFrom": "app_builder_projects", + "tableTo": "deployments", + "columnsFrom": [ + "deployment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "app_builder_projects_git_platform_integration_id_platform_integrations_id_fk": { + "name": "app_builder_projects_git_platform_integration_id_platform_integrations_id_fk", + "tableFrom": "app_builder_projects", + "tableTo": "platform_integrations", + "columnsFrom": [ + "git_platform_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "app_builder_projects_owner_check": { + "name": "app_builder_projects_owner_check", + "value": "(\n (\"app_builder_projects\".\"owned_by_user_id\" IS NOT NULL AND \"app_builder_projects\".\"owned_by_organization_id\" IS NULL) OR\n (\"app_builder_projects\".\"owned_by_user_id\" IS NULL AND \"app_builder_projects\".\"owned_by_organization_id\" IS NOT NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.app_min_versions": { + "name": "app_min_versions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "ios_min_version": { + "name": "ios_min_version", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'1.0.0'" + }, + "android_min_version": { + "name": "android_min_version", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'1.0.0'" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.app_reported_messages": { + "name": "app_reported_messages", + "schema": "", + "columns": { + "report_id": { + "name": "report_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "report_type": { + "name": "report_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "signature": { + "name": "signature", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "cli_session_id": { + "name": "cli_session_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "app_reported_messages_cli_session_id_cli_sessions_session_id_fk": { + "name": "app_reported_messages_cli_session_id_cli_sessions_session_id_fk", + "tableFrom": "app_reported_messages", + "tableTo": "cli_sessions", + "columnsFrom": [ + "cli_session_id" + ], + "columnsTo": [ + "session_id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auto_fix_tickets": { + "name": "auto_fix_tickets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "platform_integration_id": { + "name": "platform_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "triage_ticket_id": { + "name": "triage_ticket_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "repo_full_name": { + "name": "repo_full_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issue_number": { + "name": "issue_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "issue_url": { + "name": "issue_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issue_title": { + "name": "issue_title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issue_body": { + "name": "issue_body", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "issue_author": { + "name": "issue_author", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issue_labels": { + "name": "issue_labels", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "trigger_source": { + "name": "trigger_source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'label'" + }, + "review_comment_id": { + "name": "review_comment_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "review_comment_body": { + "name": "review_comment_body", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "line_number": { + "name": "line_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "diff_hunk": { + "name": "diff_hunk", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pr_head_ref": { + "name": "pr_head_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "classification": { + "name": "classification", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confidence": { + "name": "confidence", + "type": "numeric(3, 2)", + "primaryKey": false, + "notNull": false + }, + "intent_summary": { + "name": "intent_summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "related_files": { + "name": "related_files", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cli_session_id": { + "name": "cli_session_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "pr_number": { + "name": "pr_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "pr_url": { + "name": "pr_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pr_branch": { + "name": "pr_branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_auto_fix_tickets_repo_issue": { + "name": "UQ_auto_fix_tickets_repo_issue", + "columns": [ + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"auto_fix_tickets\".\"trigger_source\" = 'label'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_auto_fix_tickets_repo_review_comment": { + "name": "UQ_auto_fix_tickets_repo_review_comment", + "columns": [ + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "review_comment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"auto_fix_tickets\".\"review_comment_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_fix_tickets_owned_by_org": { + "name": "IDX_auto_fix_tickets_owned_by_org", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_fix_tickets_owned_by_user": { + "name": "IDX_auto_fix_tickets_owned_by_user", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_fix_tickets_status": { + "name": "IDX_auto_fix_tickets_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_fix_tickets_created_at": { + "name": "IDX_auto_fix_tickets_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_fix_tickets_triage_ticket_id": { + "name": "IDX_auto_fix_tickets_triage_ticket_id", + "columns": [ + { + "expression": "triage_ticket_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_fix_tickets_session_id": { + "name": "IDX_auto_fix_tickets_session_id", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auto_fix_tickets_owned_by_organization_id_organizations_id_fk": { + "name": "auto_fix_tickets_owned_by_organization_id_organizations_id_fk", + "tableFrom": "auto_fix_tickets", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "auto_fix_tickets_owned_by_user_id_kilocode_users_id_fk": { + "name": "auto_fix_tickets_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "auto_fix_tickets", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "auto_fix_tickets_platform_integration_id_platform_integrations_id_fk": { + "name": "auto_fix_tickets_platform_integration_id_platform_integrations_id_fk", + "tableFrom": "auto_fix_tickets", + "tableTo": "platform_integrations", + "columnsFrom": [ + "platform_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "auto_fix_tickets_triage_ticket_id_auto_triage_tickets_id_fk": { + "name": "auto_fix_tickets_triage_ticket_id_auto_triage_tickets_id_fk", + "tableFrom": "auto_fix_tickets", + "tableTo": "auto_triage_tickets", + "columnsFrom": [ + "triage_ticket_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "auto_fix_tickets_cli_session_id_cli_sessions_session_id_fk": { + "name": "auto_fix_tickets_cli_session_id_cli_sessions_session_id_fk", + "tableFrom": "auto_fix_tickets", + "tableTo": "cli_sessions", + "columnsFrom": [ + "cli_session_id" + ], + "columnsTo": [ + "session_id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "auto_fix_tickets_owner_check": { + "name": "auto_fix_tickets_owner_check", + "value": "(\n (\"auto_fix_tickets\".\"owned_by_user_id\" IS NOT NULL AND \"auto_fix_tickets\".\"owned_by_organization_id\" IS NULL) OR\n (\"auto_fix_tickets\".\"owned_by_user_id\" IS NULL AND \"auto_fix_tickets\".\"owned_by_organization_id\" IS NOT NULL)\n )" + }, + "auto_fix_tickets_status_check": { + "name": "auto_fix_tickets_status_check", + "value": "\"auto_fix_tickets\".\"status\" IN ('pending', 'running', 'completed', 'failed', 'cancelled')" + }, + "auto_fix_tickets_classification_check": { + "name": "auto_fix_tickets_classification_check", + "value": "\"auto_fix_tickets\".\"classification\" IN ('bug', 'feature', 'question', 'unclear')" + }, + "auto_fix_tickets_confidence_check": { + "name": "auto_fix_tickets_confidence_check", + "value": "\"auto_fix_tickets\".\"confidence\" >= 0 AND \"auto_fix_tickets\".\"confidence\" <= 1" + }, + "auto_fix_tickets_trigger_source_check": { + "name": "auto_fix_tickets_trigger_source_check", + "value": "\"auto_fix_tickets\".\"trigger_source\" IN ('label', 'review_comment')" + } + }, + "isRLSEnabled": false + }, + "public.auto_model": { + "name": "auto_model", + "schema": "", + "columns": { + "auto_model_id": { + "name": "auto_model_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "auto_model": { + "name": "auto_model", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UQ_auto_model": { + "name": "UQ_auto_model", + "columns": [ + { + "expression": "auto_model", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auto_top_up_configs": { + "name": "auto_top_up_configs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_payment_method_id": { + "name": "stripe_payment_method_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount_cents": { + "name": "amount_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5000 + }, + "last_auto_top_up_at": { + "name": "last_auto_top_up_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "attempt_started_at": { + "name": "attempt_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "disabled_reason": { + "name": "disabled_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_auto_top_up_configs_owned_by_user_id": { + "name": "UQ_auto_top_up_configs_owned_by_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"auto_top_up_configs\".\"owned_by_user_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_auto_top_up_configs_owned_by_organization_id": { + "name": "UQ_auto_top_up_configs_owned_by_organization_id", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"auto_top_up_configs\".\"owned_by_organization_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auto_top_up_configs_owned_by_user_id_kilocode_users_id_fk": { + "name": "auto_top_up_configs_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "auto_top_up_configs", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "auto_top_up_configs_owned_by_organization_id_organizations_id_fk": { + "name": "auto_top_up_configs_owned_by_organization_id_organizations_id_fk", + "tableFrom": "auto_top_up_configs", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "auto_top_up_configs_exactly_one_owner": { + "name": "auto_top_up_configs_exactly_one_owner", + "value": "(\"auto_top_up_configs\".\"owned_by_user_id\" IS NOT NULL AND \"auto_top_up_configs\".\"owned_by_organization_id\" IS NULL) OR (\"auto_top_up_configs\".\"owned_by_user_id\" IS NULL AND \"auto_top_up_configs\".\"owned_by_organization_id\" IS NOT NULL)" + } + }, + "isRLSEnabled": false + }, + "public.auto_triage_tickets": { + "name": "auto_triage_tickets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "platform_integration_id": { + "name": "platform_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "repo_full_name": { + "name": "repo_full_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issue_number": { + "name": "issue_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "issue_url": { + "name": "issue_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issue_title": { + "name": "issue_title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issue_body": { + "name": "issue_body", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "issue_author": { + "name": "issue_author", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issue_type": { + "name": "issue_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issue_labels": { + "name": "issue_labels", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "classification": { + "name": "classification", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confidence": { + "name": "confidence", + "type": "numeric(3, 2)", + "primaryKey": false, + "notNull": false + }, + "intent_summary": { + "name": "intent_summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "related_files": { + "name": "related_files", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "is_duplicate": { + "name": "is_duplicate", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "duplicate_of_ticket_id": { + "name": "duplicate_of_ticket_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "similarity_score": { + "name": "similarity_score", + "type": "numeric(3, 2)", + "primaryKey": false, + "notNull": false + }, + "qdrant_point_id": { + "name": "qdrant_point_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "should_auto_fix": { + "name": "should_auto_fix", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "action_taken": { + "name": "action_taken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action_metadata": { + "name": "action_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_auto_triage_tickets_repo_issue": { + "name": "UQ_auto_triage_tickets_repo_issue", + "columns": [ + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_triage_tickets_owned_by_org": { + "name": "IDX_auto_triage_tickets_owned_by_org", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_triage_tickets_owned_by_user": { + "name": "IDX_auto_triage_tickets_owned_by_user", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_triage_tickets_status": { + "name": "IDX_auto_triage_tickets_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_triage_tickets_created_at": { + "name": "IDX_auto_triage_tickets_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_triage_tickets_qdrant_point_id": { + "name": "IDX_auto_triage_tickets_qdrant_point_id", + "columns": [ + { + "expression": "qdrant_point_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_triage_tickets_owner_status_created": { + "name": "IDX_auto_triage_tickets_owner_status_created", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_triage_tickets_user_status_created": { + "name": "IDX_auto_triage_tickets_user_status_created", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_triage_tickets_repo_classification": { + "name": "IDX_auto_triage_tickets_repo_classification", + "columns": [ + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "classification", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auto_triage_tickets_owned_by_organization_id_organizations_id_fk": { + "name": "auto_triage_tickets_owned_by_organization_id_organizations_id_fk", + "tableFrom": "auto_triage_tickets", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "auto_triage_tickets_owned_by_user_id_kilocode_users_id_fk": { + "name": "auto_triage_tickets_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "auto_triage_tickets", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "auto_triage_tickets_platform_integration_id_platform_integrations_id_fk": { + "name": "auto_triage_tickets_platform_integration_id_platform_integrations_id_fk", + "tableFrom": "auto_triage_tickets", + "tableTo": "platform_integrations", + "columnsFrom": [ + "platform_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "auto_triage_tickets_duplicate_of_ticket_id_auto_triage_tickets_id_fk": { + "name": "auto_triage_tickets_duplicate_of_ticket_id_auto_triage_tickets_id_fk", + "tableFrom": "auto_triage_tickets", + "tableTo": "auto_triage_tickets", + "columnsFrom": [ + "duplicate_of_ticket_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "auto_triage_tickets_owner_check": { + "name": "auto_triage_tickets_owner_check", + "value": "(\n (\"auto_triage_tickets\".\"owned_by_user_id\" IS NOT NULL AND \"auto_triage_tickets\".\"owned_by_organization_id\" IS NULL) OR\n (\"auto_triage_tickets\".\"owned_by_user_id\" IS NULL AND \"auto_triage_tickets\".\"owned_by_organization_id\" IS NOT NULL)\n )" + }, + "auto_triage_tickets_issue_type_check": { + "name": "auto_triage_tickets_issue_type_check", + "value": "\"auto_triage_tickets\".\"issue_type\" IN ('issue', 'pull_request')" + }, + "auto_triage_tickets_classification_check": { + "name": "auto_triage_tickets_classification_check", + "value": "\"auto_triage_tickets\".\"classification\" IN ('bug', 'feature', 'question', 'duplicate', 'unclear')" + }, + "auto_triage_tickets_confidence_check": { + "name": "auto_triage_tickets_confidence_check", + "value": "\"auto_triage_tickets\".\"confidence\" >= 0 AND \"auto_triage_tickets\".\"confidence\" <= 1" + }, + "auto_triage_tickets_similarity_score_check": { + "name": "auto_triage_tickets_similarity_score_check", + "value": "\"auto_triage_tickets\".\"similarity_score\" >= 0 AND \"auto_triage_tickets\".\"similarity_score\" <= 1" + }, + "auto_triage_tickets_status_check": { + "name": "auto_triage_tickets_status_check", + "value": "\"auto_triage_tickets\".\"status\" IN ('pending', 'analyzing', 'actioned', 'failed', 'skipped')" + }, + "auto_triage_tickets_action_taken_check": { + "name": "auto_triage_tickets_action_taken_check", + "value": "\"auto_triage_tickets\".\"action_taken\" IN ('pr_created', 'comment_posted', 'closed_duplicate', 'needs_clarification')" + } + }, + "isRLSEnabled": false + }, + "public.bot_request_cloud_agent_sessions": { + "name": "bot_request_cloud_agent_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "bot_request_id": { + "name": "bot_request_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "spawn_group_id": { + "name": "spawn_group_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "cloud_agent_session_id": { + "name": "cloud_agent_session_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kilo_session_id": { + "name": "kilo_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'running'" + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "github_repo": { + "name": "github_repo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlab_project": { + "name": "gitlab_project", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "callback_step": { + "name": "callback_step", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "final_message": { + "name": "final_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "final_message_fetched_at": { + "name": "final_message_fetched_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "final_message_error": { + "name": "final_message_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "terminal_at": { + "name": "terminal_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "continuation_started_at": { + "name": "continuation_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_bot_request_cas_cloud_agent_session_id": { + "name": "UQ_bot_request_cas_cloud_agent_session_id", + "columns": [ + { + "expression": "cloud_agent_session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_bot_request_cas_bot_request_id": { + "name": "IDX_bot_request_cas_bot_request_id", + "columns": [ + { + "expression": "bot_request_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_bot_request_cas_bot_request_id_spawn_group_id": { + "name": "IDX_bot_request_cas_bot_request_id_spawn_group_id", + "columns": [ + { + "expression": "bot_request_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "spawn_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_bot_request_cas_bot_request_id_spawn_group_id_status": { + "name": "IDX_bot_request_cas_bot_request_id_spawn_group_id_status", + "columns": [ + { + "expression": "bot_request_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "spawn_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "bot_request_cloud_agent_sessions_bot_request_id_bot_requests_id_fk": { + "name": "bot_request_cloud_agent_sessions_bot_request_id_bot_requests_id_fk", + "tableFrom": "bot_request_cloud_agent_sessions", + "tableTo": "bot_requests", + "columnsFrom": [ + "bot_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bot_requests": { + "name": "bot_requests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "platform_integration_id": { + "name": "platform_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform_thread_id": { + "name": "platform_thread_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform_message_id": { + "name": "platform_message_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_message": { + "name": "user_message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model_used": { + "name": "model_used", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "steps": { + "name": "steps", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "cloud_agent_session_id": { + "name": "cloud_agent_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "response_time_ms": { + "name": "response_time_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_bot_requests_created_at": { + "name": "IDX_bot_requests_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_bot_requests_created_by": { + "name": "IDX_bot_requests_created_by", + "columns": [ + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_bot_requests_organization_id": { + "name": "IDX_bot_requests_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_bot_requests_platform_integration_id": { + "name": "IDX_bot_requests_platform_integration_id", + "columns": [ + { + "expression": "platform_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_bot_requests_status": { + "name": "IDX_bot_requests_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "bot_requests_created_by_kilocode_users_id_fk": { + "name": "bot_requests_created_by_kilocode_users_id_fk", + "tableFrom": "bot_requests", + "tableTo": "kilocode_users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "bot_requests_organization_id_organizations_id_fk": { + "name": "bot_requests_organization_id_organizations_id_fk", + "tableFrom": "bot_requests", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "bot_requests_platform_integration_id_platform_integrations_id_fk": { + "name": "bot_requests_platform_integration_id_platform_integrations_id_fk", + "tableFrom": "bot_requests", + "tableTo": "platform_integrations", + "columnsFrom": [ + "platform_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.byok_api_keys": { + "name": "byok_api_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "encrypted_api_key": { + "name": "encrypted_api_key", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "management_source": { + "name": "management_source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "IDX_byok_api_keys_organization_id": { + "name": "IDX_byok_api_keys_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_byok_api_keys_kilo_user_id": { + "name": "IDX_byok_api_keys_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_byok_api_keys_provider_id": { + "name": "IDX_byok_api_keys_provider_id", + "columns": [ + { + "expression": "provider_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "byok_api_keys_organization_id_organizations_id_fk": { + "name": "byok_api_keys_organization_id_organizations_id_fk", + "tableFrom": "byok_api_keys", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "byok_api_keys_kilo_user_id_kilocode_users_id_fk": { + "name": "byok_api_keys_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "byok_api_keys", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_byok_api_keys_org_provider": { + "name": "UQ_byok_api_keys_org_provider", + "nullsNotDistinct": false, + "columns": [ + "organization_id", + "provider_id" + ] + }, + "UQ_byok_api_keys_user_provider": { + "name": "UQ_byok_api_keys_user_provider", + "nullsNotDistinct": false, + "columns": [ + "kilo_user_id", + "provider_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "byok_api_keys_management_source_check": { + "name": "byok_api_keys_management_source_check", + "value": "\"byok_api_keys\".\"management_source\" IN ('user', 'coding_plan')" + }, + "byok_api_keys_owner_check": { + "name": "byok_api_keys_owner_check", + "value": "(\n (\"byok_api_keys\".\"kilo_user_id\" IS NOT NULL AND \"byok_api_keys\".\"organization_id\" IS NULL) OR\n (\"byok_api_keys\".\"kilo_user_id\" IS NULL AND \"byok_api_keys\".\"organization_id\" IS NOT NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.cli_sessions": { + "name": "cli_sessions", + "schema": "", + "columns": { + "session_id": { + "name": "session_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_on_platform": { + "name": "created_on_platform", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "api_conversation_history_blob_url": { + "name": "api_conversation_history_blob_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "task_metadata_blob_url": { + "name": "task_metadata_blob_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ui_messages_blob_url": { + "name": "ui_messages_blob_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "git_state_blob_url": { + "name": "git_state_blob_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "git_url": { + "name": "git_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "forked_from": { + "name": "forked_from", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "parent_session_id": { + "name": "parent_session_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "cloud_agent_session_id": { + "name": "cloud_agent_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "last_mode": { + "name": "last_mode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_model": { + "name": "last_model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_cli_sessions_kilo_user_id": { + "name": "IDX_cli_sessions_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cli_sessions_created_at": { + "name": "IDX_cli_sessions_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cli_sessions_updated_at": { + "name": "IDX_cli_sessions_updated_at", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cli_sessions_organization_id": { + "name": "IDX_cli_sessions_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cli_sessions_user_updated": { + "name": "IDX_cli_sessions_user_updated", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "cli_sessions_kilo_user_id_kilocode_users_id_fk": { + "name": "cli_sessions_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "cli_sessions", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "cli_sessions_forked_from_cli_sessions_session_id_fk": { + "name": "cli_sessions_forked_from_cli_sessions_session_id_fk", + "tableFrom": "cli_sessions", + "tableTo": "cli_sessions", + "columnsFrom": [ + "forked_from" + ], + "columnsTo": [ + "session_id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "cli_sessions_parent_session_id_cli_sessions_session_id_fk": { + "name": "cli_sessions_parent_session_id_cli_sessions_session_id_fk", + "tableFrom": "cli_sessions", + "tableTo": "cli_sessions", + "columnsFrom": [ + "parent_session_id" + ], + "columnsTo": [ + "session_id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "cli_sessions_organization_id_organizations_id_fk": { + "name": "cli_sessions_organization_id_organizations_id_fk", + "tableFrom": "cli_sessions", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cli_sessions_cloud_agent_session_id_unique": { + "name": "cli_sessions_cloud_agent_session_id_unique", + "nullsNotDistinct": false, + "columns": [ + "cloud_agent_session_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cli_sessions_v2": { + "name": "cli_sessions_v2", + "schema": "", + "columns": { + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "public_id": { + "name": "public_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "parent_session_id": { + "name": "parent_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "cloud_agent_session_id": { + "name": "cloud_agent_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_on_platform": { + "name": "created_on_platform", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "git_url": { + "name": "git_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "git_branch": { + "name": "git_branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status_updated_at": { + "name": "status_updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_cli_sessions_v2_parent_session_id_kilo_user_id": { + "name": "IDX_cli_sessions_v2_parent_session_id_kilo_user_id", + "columns": [ + { + "expression": "parent_session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_cli_sessions_v2_public_id": { + "name": "UQ_cli_sessions_v2_public_id", + "columns": [ + { + "expression": "public_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"cli_sessions_v2\".\"public_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_cli_sessions_v2_cloud_agent_session_id": { + "name": "UQ_cli_sessions_v2_cloud_agent_session_id", + "columns": [ + { + "expression": "cloud_agent_session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"cli_sessions_v2\".\"cloud_agent_session_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cli_sessions_v2_organization_id": { + "name": "IDX_cli_sessions_v2_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cli_sessions_v2_kilo_user_id": { + "name": "IDX_cli_sessions_v2_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cli_sessions_v2_created_at": { + "name": "IDX_cli_sessions_v2_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cli_sessions_v2_user_updated": { + "name": "IDX_cli_sessions_v2_user_updated", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cli_sessions_v2_user_git_url_updated": { + "name": "IDX_cli_sessions_v2_user_git_url_updated", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "git_url", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"cli_sessions_v2\".\"git_url\" IS NOT NULL AND \"cli_sessions_v2\".\"parent_session_id\" IS NULL AND \"cli_sessions_v2\".\"cloud_agent_session_id\" IS NOT NULL", + "concurrently": true, + "method": "btree", + "with": {} + }, + "cli_sessions_v2_git_url_branch_idx": { + "name": "cli_sessions_v2_git_url_branch_idx", + "columns": [ + { + "expression": "git_url", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "git_branch", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "cli_sessions_v2_kilo_user_id_kilocode_users_id_fk": { + "name": "cli_sessions_v2_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "cli_sessions_v2", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "cli_sessions_v2_organization_id_organizations_id_fk": { + "name": "cli_sessions_v2_organization_id_organizations_id_fk", + "tableFrom": "cli_sessions_v2", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "cli_sessions_v2_parent_session_id_kilo_user_id_fk": { + "name": "cli_sessions_v2_parent_session_id_kilo_user_id_fk", + "tableFrom": "cli_sessions_v2", + "tableTo": "cli_sessions_v2", + "columnsFrom": [ + "parent_session_id", + "kilo_user_id" + ], + "columnsTo": [ + "session_id", + "kilo_user_id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "cli_sessions_v2_session_id_kilo_user_id_pk": { + "name": "cli_sessions_v2_session_id_kilo_user_id_pk", + "columns": [ + "session_id", + "kilo_user_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cloud_agent_code_review_attempts": { + "name": "cloud_agent_code_review_attempts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "code_review_id": { + "name": "code_review_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "attempt_number": { + "name": "attempt_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "retry_of_attempt_id": { + "name": "retry_of_attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "retry_reason": { + "name": "retry_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cli_session_id": { + "name": "cli_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "terminal_reason": { + "name": "terminal_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_cloud_agent_code_review_attempts_review_attempt_number": { + "name": "UQ_cloud_agent_code_review_attempts_review_attempt_number", + "columns": [ + { + "expression": "code_review_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "attempt_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_review_attempts_code_review_id": { + "name": "idx_cloud_agent_code_review_attempts_code_review_id", + "columns": [ + { + "expression": "code_review_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_review_attempts_session_id": { + "name": "idx_cloud_agent_code_review_attempts_session_id", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_review_attempts_cli_session_id": { + "name": "idx_cloud_agent_code_review_attempts_cli_session_id", + "columns": [ + { + "expression": "cli_session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_review_attempts_status": { + "name": "idx_cloud_agent_code_review_attempts_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_review_attempts_retry_reason": { + "name": "idx_cloud_agent_code_review_attempts_retry_reason", + "columns": [ + { + "expression": "retry_reason", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "cloud_agent_code_review_attempts_code_review_id_cloud_agent_code_reviews_id_fk": { + "name": "cloud_agent_code_review_attempts_code_review_id_cloud_agent_code_reviews_id_fk", + "tableFrom": "cloud_agent_code_review_attempts", + "tableTo": "cloud_agent_code_reviews", + "columnsFrom": [ + "code_review_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "cloud_agent_code_review_attempts_retry_of_attempt_id_cloud_agent_code_review_attempts_id_fk": { + "name": "cloud_agent_code_review_attempts_retry_of_attempt_id_cloud_agent_code_review_attempts_id_fk", + "tableFrom": "cloud_agent_code_review_attempts", + "tableTo": "cloud_agent_code_review_attempts", + "columnsFrom": [ + "retry_of_attempt_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "cloud_agent_code_review_attempts_attempt_number_check": { + "name": "cloud_agent_code_review_attempts_attempt_number_check", + "value": "\"cloud_agent_code_review_attempts\".\"attempt_number\" >= 1" + } + }, + "isRLSEnabled": false + }, + "public.cloud_agent_code_reviews": { + "name": "cloud_agent_code_reviews", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "platform_integration_id": { + "name": "platform_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "repo_full_name": { + "name": "repo_full_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pr_number": { + "name": "pr_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "pr_url": { + "name": "pr_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pr_title": { + "name": "pr_title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pr_author": { + "name": "pr_author", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pr_author_github_id": { + "name": "pr_author_github_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "base_ref": { + "name": "base_ref", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "head_ref": { + "name": "head_ref", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "head_sha": { + "name": "head_sha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "platform_project_id": { + "name": "platform_project_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cli_session_id": { + "name": "cli_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "dispatch_reservation_id": { + "name": "dispatch_reservation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "terminal_reason": { + "name": "terminal_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "agent_version": { + "name": "agent_version", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'v1'" + }, + "check_run_id": { + "name": "check_run_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "repository_review_instructions_used": { + "name": "repository_review_instructions_used", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "repository_review_instructions_ref": { + "name": "repository_review_instructions_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "repository_review_instructions_truncated": { + "name": "repository_review_instructions_truncated", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "total_tokens_in": { + "name": "total_tokens_in", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_tokens_out": { + "name": "total_tokens_out", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_cost_musd": { + "name": "total_cost_musd", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_cloud_agent_code_reviews_repo_pr_sha": { + "name": "UQ_cloud_agent_code_reviews_repo_pr_sha", + "columns": [ + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "pr_number", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "head_sha", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_reviews_owned_by_org_id": { + "name": "idx_cloud_agent_code_reviews_owned_by_org_id", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_reviews_owned_by_user_id": { + "name": "idx_cloud_agent_code_reviews_owned_by_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_reviews_session_id": { + "name": "idx_cloud_agent_code_reviews_session_id", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_reviews_cli_session_id": { + "name": "idx_cloud_agent_code_reviews_cli_session_id", + "columns": [ + { + "expression": "cli_session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_reviews_status": { + "name": "idx_cloud_agent_code_reviews_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_reviews_repo": { + "name": "idx_cloud_agent_code_reviews_repo", + "columns": [ + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_reviews_pr_number": { + "name": "idx_cloud_agent_code_reviews_pr_number", + "columns": [ + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "pr_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_reviews_created_at": { + "name": "idx_cloud_agent_code_reviews_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_reviews_pr_author_github_id": { + "name": "idx_cloud_agent_code_reviews_pr_author_github_id", + "columns": [ + { + "expression": "pr_author_github_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "cloud_agent_code_reviews_owned_by_organization_id_organizations_id_fk": { + "name": "cloud_agent_code_reviews_owned_by_organization_id_organizations_id_fk", + "tableFrom": "cloud_agent_code_reviews", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "cloud_agent_code_reviews_owned_by_user_id_kilocode_users_id_fk": { + "name": "cloud_agent_code_reviews_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "cloud_agent_code_reviews", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "cloud_agent_code_reviews_platform_integration_id_platform_integrations_id_fk": { + "name": "cloud_agent_code_reviews_platform_integration_id_platform_integrations_id_fk", + "tableFrom": "cloud_agent_code_reviews", + "tableTo": "platform_integrations", + "columnsFrom": [ + "platform_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "cloud_agent_code_reviews_owner_check": { + "name": "cloud_agent_code_reviews_owner_check", + "value": "(\n (\"cloud_agent_code_reviews\".\"owned_by_user_id\" IS NOT NULL AND \"cloud_agent_code_reviews\".\"owned_by_organization_id\" IS NULL) OR\n (\"cloud_agent_code_reviews\".\"owned_by_user_id\" IS NULL AND \"cloud_agent_code_reviews\".\"owned_by_organization_id\" IS NOT NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.cloud_agent_feedback": { + "name": "cloud_agent_feedback", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cloud_agent_session_id": { + "name": "cloud_agent_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_streaming": { + "name": "is_streaming", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "message_count": { + "name": "message_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "feedback_text": { + "name": "feedback_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "recent_messages": { + "name": "recent_messages", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_cloud_agent_feedback_created_at": { + "name": "IDX_cloud_agent_feedback_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cloud_agent_feedback_kilo_user_id": { + "name": "IDX_cloud_agent_feedback_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cloud_agent_feedback_cloud_agent_session_id": { + "name": "IDX_cloud_agent_feedback_cloud_agent_session_id", + "columns": [ + { + "expression": "cloud_agent_session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "cloud_agent_feedback_kilo_user_id_kilocode_users_id_fk": { + "name": "cloud_agent_feedback_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "cloud_agent_feedback", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "cloud_agent_feedback_organization_id_organizations_id_fk": { + "name": "cloud_agent_feedback_organization_id_organizations_id_fk", + "tableFrom": "cloud_agent_feedback", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cloud_agent_session_runs": { + "name": "cloud_agent_session_runs", + "schema": "", + "columns": { + "cloud_agent_session_id": { + "name": "cloud_agent_session_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "wrapper_run_id": { + "name": "wrapper_run_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "queued_at": { + "name": "queued_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "dispatch_accepted_at": { + "name": "dispatch_accepted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "agent_activity_observed_at": { + "name": "agent_activity_observed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "terminal_at": { + "name": "terminal_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "failure_stage": { + "name": "failure_stage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "failure_code": { + "name": "failure_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_message_redacted": { + "name": "error_message_redacted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_expires_at": { + "name": "error_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "IDX_cloud_agent_session_runs_wrapper_run_id": { + "name": "IDX_cloud_agent_session_runs_wrapper_run_id", + "columns": [ + { + "expression": "wrapper_run_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"cloud_agent_session_runs\".\"wrapper_run_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cloud_agent_session_runs_session_queued": { + "name": "IDX_cloud_agent_session_runs_session_queued", + "columns": [ + { + "expression": "cloud_agent_session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "queued_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cloud_agent_session_runs_queued_at": { + "name": "IDX_cloud_agent_session_runs_queued_at", + "columns": [ + { + "expression": "queued_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cloud_agent_session_runs_terminal_at": { + "name": "IDX_cloud_agent_session_runs_terminal_at", + "columns": [ + { + "expression": "terminal_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cloud_agent_session_runs_status_terminal": { + "name": "IDX_cloud_agent_session_runs_status_terminal", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "terminal_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cloud_agent_session_runs_failure_terminal": { + "name": "IDX_cloud_agent_session_runs_failure_terminal", + "columns": [ + { + "expression": "failure_stage", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "failure_code", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "terminal_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cloud_agent_session_runs_error_expires_at": { + "name": "IDX_cloud_agent_session_runs_error_expires_at", + "columns": [ + { + "expression": "error_expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"cloud_agent_session_runs\".\"error_expires_at\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "cloud_agent_session_runs_cloud_agent_session_id_cloud_agent_sessions_cloud_agent_session_id_fk": { + "name": "cloud_agent_session_runs_cloud_agent_session_id_cloud_agent_sessions_cloud_agent_session_id_fk", + "tableFrom": "cloud_agent_session_runs", + "tableTo": "cloud_agent_sessions", + "columnsFrom": [ + "cloud_agent_session_id" + ], + "columnsTo": [ + "cloud_agent_session_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "cloud_agent_session_runs_cloud_agent_session_id_message_id_pk": { + "name": "cloud_agent_session_runs_cloud_agent_session_id_message_id_pk", + "columns": [ + "cloud_agent_session_id", + "message_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "cloud_agent_session_runs_status_check": { + "name": "cloud_agent_session_runs_status_check", + "value": "\"cloud_agent_session_runs\".\"status\" IN ('queued', 'accepted', 'completed', 'failed', 'interrupted')" + }, + "cloud_agent_session_runs_failure_classification_check": { + "name": "cloud_agent_session_runs_failure_classification_check", + "value": "(\"cloud_agent_session_runs\".\"failure_stage\" IS NULL AND \"cloud_agent_session_runs\".\"failure_code\" IS NULL) OR\n (\"cloud_agent_session_runs\".\"failure_stage\" = 'pre_dispatch' AND \"cloud_agent_session_runs\".\"failure_code\" IN ('sandbox_connect_failed', 'workspace_setup_failed', 'kilo_server_failed', 'wrapper_start_failed', 'invalid_delivery_request', 'session_metadata_missing', 'model_missing', 'delivery_failure_unknown')) OR\n (\"cloud_agent_session_runs\".\"failure_stage\" = 'post_dispatch_no_activity' AND \"cloud_agent_session_runs\".\"failure_code\" IN ('wrapper_disconnected', 'wrapper_no_output', 'wrapper_ping_timeout', 'wrapper_error_before_activity', 'missing_assistant_reply')) OR\n (\"cloud_agent_session_runs\".\"failure_stage\" = 'agent_activity' AND \"cloud_agent_session_runs\".\"failure_code\" IN ('assistant_error', 'wrapper_error_after_activity')) OR\n (\"cloud_agent_session_runs\".\"failure_stage\" = 'interruption' AND \"cloud_agent_session_runs\".\"failure_code\" IN ('user_interrupt', 'container_shutdown', 'system_interrupt')) OR\n (\"cloud_agent_session_runs\".\"failure_stage\" = 'unknown' AND \"cloud_agent_session_runs\".\"failure_code\" = 'unclassified')" + }, + "cloud_agent_session_runs_error_message_bounded_check": { + "name": "cloud_agent_session_runs_error_message_bounded_check", + "value": "\"cloud_agent_session_runs\".\"error_message_redacted\" IS NULL OR char_length(\"cloud_agent_session_runs\".\"error_message_redacted\") <= 4096" + }, + "cloud_agent_session_runs_error_expiry_check": { + "name": "cloud_agent_session_runs_error_expiry_check", + "value": "(\"cloud_agent_session_runs\".\"error_message_redacted\" IS NULL AND \"cloud_agent_session_runs\".\"error_expires_at\" IS NULL) OR\n (\"cloud_agent_session_runs\".\"error_message_redacted\" IS NOT NULL AND \"cloud_agent_session_runs\".\"error_expires_at\" IS NOT NULL)" + } + }, + "isRLSEnabled": false + }, + "public.cloud_agent_sessions": { + "name": "cloud_agent_sessions", + "schema": "", + "columns": { + "cloud_agent_session_id": { + "name": "cloud_agent_session_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "kilo_session_id": { + "name": "kilo_session_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "initial_message_id": { + "name": "initial_message_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sandbox_id": { + "name": "sandbox_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "failure_at": { + "name": "failure_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "failure_stage": { + "name": "failure_stage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "failure_code": { + "name": "failure_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_message_redacted": { + "name": "error_message_redacted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_expires_at": { + "name": "error_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "UQ_cloud_agent_sessions_kilo_session_id": { + "name": "UQ_cloud_agent_sessions_kilo_session_id", + "columns": [ + { + "expression": "kilo_session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_cloud_agent_sessions_initial_message_id": { + "name": "UQ_cloud_agent_sessions_initial_message_id", + "columns": [ + { + "expression": "initial_message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cloud_agent_sessions_sandbox_id": { + "name": "IDX_cloud_agent_sessions_sandbox_id", + "columns": [ + { + "expression": "sandbox_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"cloud_agent_sessions\".\"sandbox_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cloud_agent_sessions_created_at": { + "name": "IDX_cloud_agent_sessions_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cloud_agent_sessions_failure_created": { + "name": "IDX_cloud_agent_sessions_failure_created", + "columns": [ + { + "expression": "failure_stage", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "failure_code", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cloud_agent_sessions_failure_at": { + "name": "IDX_cloud_agent_sessions_failure_at", + "columns": [ + { + "expression": "failure_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"cloud_agent_sessions\".\"failure_at\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cloud_agent_sessions_failure_classification_at": { + "name": "IDX_cloud_agent_sessions_failure_classification_at", + "columns": [ + { + "expression": "failure_stage", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "failure_code", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "failure_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"cloud_agent_sessions\".\"failure_at\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cloud_agent_sessions_error_expires_at": { + "name": "IDX_cloud_agent_sessions_error_expires_at", + "columns": [ + { + "expression": "error_expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"cloud_agent_sessions\".\"error_expires_at\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "cloud_agent_sessions_failure_classification_check": { + "name": "cloud_agent_sessions_failure_classification_check", + "value": "(\"cloud_agent_sessions\".\"failure_at\" IS NULL AND \"cloud_agent_sessions\".\"failure_stage\" IS NULL AND \"cloud_agent_sessions\".\"failure_code\" IS NULL) OR\n (\"cloud_agent_sessions\".\"failure_at\" IS NOT NULL AND \"cloud_agent_sessions\".\"failure_stage\" = 'sandbox_identity' AND \"cloud_agent_sessions\".\"failure_code\" = 'sandbox_id_derivation_failed') OR\n (\"cloud_agent_sessions\".\"failure_at\" IS NOT NULL AND \"cloud_agent_sessions\".\"failure_stage\" = 'registration' AND \"cloud_agent_sessions\".\"failure_code\" = 'do_registration_rejected') OR\n (\"cloud_agent_sessions\".\"failure_at\" IS NOT NULL AND \"cloud_agent_sessions\".\"failure_stage\" = 'initial_admission' AND \"cloud_agent_sessions\".\"failure_code\" IN ('initial_admission_rejected', 'initial_queue_full', 'invalid_initial_intent')) OR\n (\"cloud_agent_sessions\".\"failure_at\" IS NOT NULL AND \"cloud_agent_sessions\".\"failure_stage\" = 'transport' AND \"cloud_agent_sessions\".\"failure_code\" = 'do_rpc_outcome_unknown')" + }, + "cloud_agent_sessions_error_message_bounded_check": { + "name": "cloud_agent_sessions_error_message_bounded_check", + "value": "\"cloud_agent_sessions\".\"error_message_redacted\" IS NULL OR char_length(\"cloud_agent_sessions\".\"error_message_redacted\") <= 4096" + }, + "cloud_agent_sessions_error_expiry_check": { + "name": "cloud_agent_sessions_error_expiry_check", + "value": "(\"cloud_agent_sessions\".\"error_message_redacted\" IS NULL AND \"cloud_agent_sessions\".\"error_expires_at\" IS NULL) OR\n (\"cloud_agent_sessions\".\"error_message_redacted\" IS NOT NULL AND \"cloud_agent_sessions\".\"error_expires_at\" IS NOT NULL)" + } + }, + "isRLSEnabled": false + }, + "public.cloud_agent_webhook_triggers": { + "name": "cloud_agent_webhook_triggers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "trigger_id": { + "name": "trigger_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "target_type": { + "name": "target_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'cloud_agent'" + }, + "kiloclaw_instance_id": { + "name": "kiloclaw_instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "activation_mode": { + "name": "activation_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'webhook'" + }, + "cron_expression": { + "name": "cron_expression", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cron_timezone": { + "name": "cron_timezone", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'UTC'" + }, + "github_repo": { + "name": "github_repo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "profile_id": { + "name": "profile_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_cloud_agent_webhook_triggers_user_trigger": { + "name": "UQ_cloud_agent_webhook_triggers_user_trigger", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "trigger_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"cloud_agent_webhook_triggers\".\"user_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_cloud_agent_webhook_triggers_org_trigger": { + "name": "UQ_cloud_agent_webhook_triggers_org_trigger", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "trigger_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"cloud_agent_webhook_triggers\".\"organization_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cloud_agent_webhook_triggers_user": { + "name": "IDX_cloud_agent_webhook_triggers_user", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cloud_agent_webhook_triggers_org": { + "name": "IDX_cloud_agent_webhook_triggers_org", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cloud_agent_webhook_triggers_active": { + "name": "IDX_cloud_agent_webhook_triggers_active", + "columns": [ + { + "expression": "is_active", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cloud_agent_webhook_triggers_profile": { + "name": "IDX_cloud_agent_webhook_triggers_profile", + "columns": [ + { + "expression": "profile_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "cloud_agent_webhook_triggers_user_id_kilocode_users_id_fk": { + "name": "cloud_agent_webhook_triggers_user_id_kilocode_users_id_fk", + "tableFrom": "cloud_agent_webhook_triggers", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "cloud_agent_webhook_triggers_organization_id_organizations_id_fk": { + "name": "cloud_agent_webhook_triggers_organization_id_organizations_id_fk", + "tableFrom": "cloud_agent_webhook_triggers", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "cloud_agent_webhook_triggers_kiloclaw_instance_id_kiloclaw_instances_id_fk": { + "name": "cloud_agent_webhook_triggers_kiloclaw_instance_id_kiloclaw_instances_id_fk", + "tableFrom": "cloud_agent_webhook_triggers", + "tableTo": "kiloclaw_instances", + "columnsFrom": [ + "kiloclaw_instance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "cloud_agent_webhook_triggers_profile_id_agent_environment_profiles_id_fk": { + "name": "cloud_agent_webhook_triggers_profile_id_agent_environment_profiles_id_fk", + "tableFrom": "cloud_agent_webhook_triggers", + "tableTo": "agent_environment_profiles", + "columnsFrom": [ + "profile_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "CHK_cloud_agent_webhook_triggers_owner": { + "name": "CHK_cloud_agent_webhook_triggers_owner", + "value": "(\n (\"cloud_agent_webhook_triggers\".\"user_id\" IS NOT NULL AND \"cloud_agent_webhook_triggers\".\"organization_id\" IS NULL) OR\n (\"cloud_agent_webhook_triggers\".\"user_id\" IS NULL AND \"cloud_agent_webhook_triggers\".\"organization_id\" IS NOT NULL)\n )" + }, + "CHK_cloud_agent_webhook_triggers_cloud_agent_fields": { + "name": "CHK_cloud_agent_webhook_triggers_cloud_agent_fields", + "value": "(\n \"cloud_agent_webhook_triggers\".\"target_type\" != 'cloud_agent' OR\n (\"cloud_agent_webhook_triggers\".\"github_repo\" IS NOT NULL AND \"cloud_agent_webhook_triggers\".\"profile_id\" IS NOT NULL)\n )" + }, + "CHK_cloud_agent_webhook_triggers_kiloclaw_fields": { + "name": "CHK_cloud_agent_webhook_triggers_kiloclaw_fields", + "value": "(\n \"cloud_agent_webhook_triggers\".\"target_type\" != 'kiloclaw_chat' OR\n \"cloud_agent_webhook_triggers\".\"kiloclaw_instance_id\" IS NOT NULL\n )" + }, + "CHK_cloud_agent_webhook_triggers_scheduled_fields": { + "name": "CHK_cloud_agent_webhook_triggers_scheduled_fields", + "value": "(\n \"cloud_agent_webhook_triggers\".\"activation_mode\" != 'scheduled' OR\n \"cloud_agent_webhook_triggers\".\"cron_expression\" IS NOT NULL\n )" + } + }, + "isRLSEnabled": false + }, + "public.code_indexing_manifest": { + "name": "code_indexing_manifest", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "git_branch": { + "name": "git_branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_hash": { + "name": "file_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chunk_count": { + "name": "chunk_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "total_lines": { + "name": "total_lines", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_ai_lines": { + "name": "total_ai_lines", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_code_indexing_manifest_organization_id": { + "name": "IDX_code_indexing_manifest_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_code_indexing_manifest_kilo_user_id": { + "name": "IDX_code_indexing_manifest_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_code_indexing_manifest_project_id": { + "name": "IDX_code_indexing_manifest_project_id", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_code_indexing_manifest_git_branch": { + "name": "IDX_code_indexing_manifest_git_branch", + "columns": [ + { + "expression": "git_branch", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_code_indexing_manifest_created_at": { + "name": "IDX_code_indexing_manifest_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "code_indexing_manifest_kilo_user_id_kilocode_users_id_fk": { + "name": "code_indexing_manifest_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "code_indexing_manifest", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_code_indexing_manifest_org_user_project_hash_branch": { + "name": "UQ_code_indexing_manifest_org_user_project_hash_branch", + "nullsNotDistinct": true, + "columns": [ + "organization_id", + "kilo_user_id", + "project_id", + "file_path", + "git_branch" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.code_indexing_search": { + "name": "code_indexing_search", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "query": { + "name": "query", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_code_indexing_search_organization_id": { + "name": "IDX_code_indexing_search_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_code_indexing_search_kilo_user_id": { + "name": "IDX_code_indexing_search_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_code_indexing_search_project_id": { + "name": "IDX_code_indexing_search_project_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_code_indexing_search_created_at": { + "name": "IDX_code_indexing_search_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "code_indexing_search_kilo_user_id_kilocode_users_id_fk": { + "name": "code_indexing_search_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "code_indexing_search", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.code_review_feedback_events": { + "name": "code_review_feedback_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "repo_full_name": { + "name": "repo_full_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pr_number": { + "name": "pr_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "kilo_comment_id": { + "name": "kilo_comment_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reply_excerpt": { + "name": "reply_excerpt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kilo_comment_excerpt": { + "name": "kilo_comment_excerpt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dedupe_hash": { + "name": "dedupe_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_code_review_feedback_events_owned_by_org_id": { + "name": "idx_code_review_feedback_events_owned_by_org_id", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_feedback_events_owned_by_user_id": { + "name": "idx_code_review_feedback_events_owned_by_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_feedback_events_platform_repo": { + "name": "idx_code_review_feedback_events_platform_repo", + "columns": [ + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_feedback_events_created_at": { + "name": "idx_code_review_feedback_events_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "code_review_feedback_events_owned_by_organization_id_organizations_id_fk": { + "name": "code_review_feedback_events_owned_by_organization_id_organizations_id_fk", + "tableFrom": "code_review_feedback_events", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "code_review_feedback_events_owned_by_user_id_kilocode_users_id_fk": { + "name": "code_review_feedback_events_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "code_review_feedback_events", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_code_review_feedback_events_dedupe_hash": { + "name": "UQ_code_review_feedback_events_dedupe_hash", + "nullsNotDistinct": false, + "columns": [ + "dedupe_hash" + ] + } + }, + "policies": {}, + "checkConstraints": { + "code_review_feedback_events_owner_check": { + "name": "code_review_feedback_events_owner_check", + "value": "(\n (\"code_review_feedback_events\".\"owned_by_user_id\" IS NOT NULL AND \"code_review_feedback_events\".\"owned_by_organization_id\" IS NULL) OR\n (\"code_review_feedback_events\".\"owned_by_user_id\" IS NULL AND \"code_review_feedback_events\".\"owned_by_organization_id\" IS NOT NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.code_review_memory_proposals": { + "name": "code_review_memory_proposals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "repo_full_name": { + "name": "repo_full_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rationale": { + "name": "rationale", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "proposed_markdown": { + "name": "proposed_markdown", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "evidence": { + "name": "evidence", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "positive_count": { + "name": "positive_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "negative_count": { + "name": "negative_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "neutral_count": { + "name": "neutral_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "change_request_url": { + "name": "change_request_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_code_review_memory_proposals_owned_by_org_id": { + "name": "idx_code_review_memory_proposals_owned_by_org_id", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_memory_proposals_owned_by_user_id": { + "name": "idx_code_review_memory_proposals_owned_by_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_memory_proposals_platform_repo_status": { + "name": "idx_code_review_memory_proposals_platform_repo_status", + "columns": [ + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_memory_proposals_updated_at": { + "name": "idx_code_review_memory_proposals_updated_at", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_code_review_memory_proposals_org_active_scope": { + "name": "UQ_code_review_memory_proposals_org_active_scope", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"code_review_memory_proposals\".\"owned_by_organization_id\" IS NOT NULL AND \"code_review_memory_proposals\".\"status\" IN ('open', 'edited', 'opening_change_request')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_code_review_memory_proposals_user_active_scope": { + "name": "UQ_code_review_memory_proposals_user_active_scope", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"code_review_memory_proposals\".\"owned_by_user_id\" IS NOT NULL AND \"code_review_memory_proposals\".\"status\" IN ('open', 'edited', 'opening_change_request')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "code_review_memory_proposals_owned_by_organization_id_organizations_id_fk": { + "name": "code_review_memory_proposals_owned_by_organization_id_organizations_id_fk", + "tableFrom": "code_review_memory_proposals", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "code_review_memory_proposals_owned_by_user_id_kilocode_users_id_fk": { + "name": "code_review_memory_proposals_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "code_review_memory_proposals", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "code_review_memory_proposals_owner_check": { + "name": "code_review_memory_proposals_owner_check", + "value": "(\n (\"code_review_memory_proposals\".\"owned_by_user_id\" IS NOT NULL AND \"code_review_memory_proposals\".\"owned_by_organization_id\" IS NULL) OR\n (\"code_review_memory_proposals\".\"owned_by_user_id\" IS NULL AND \"code_review_memory_proposals\".\"owned_by_organization_id\" IS NOT NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.coding_plan_availability_intents": { + "name": "coding_plan_availability_intents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plan_id": { + "name": "plan_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_coding_plan_availability_intents_user_plan": { + "name": "UQ_coding_plan_availability_intents_user_plan", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "plan_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_coding_plan_availability_intents_plan": { + "name": "IDX_coding_plan_availability_intents_plan", + "columns": [ + { + "expression": "plan_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "coding_plan_availability_intents_user_id_kilocode_users_id_fk": { + "name": "coding_plan_availability_intents_user_id_kilocode_users_id_fk", + "tableFrom": "coding_plan_availability_intents", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.coding_plan_key_inventory": { + "name": "coding_plan_key_inventory", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "plan_id": { + "name": "plan_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "upstream_plan_id": { + "name": "upstream_plan_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "encrypted_api_key": { + "name": "encrypted_api_key", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "credential_fingerprint": { + "name": "credential_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'available'" + }, + "assigned_to_user_id": { + "name": "assigned_to_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "assigned_at": { + "name": "assigned_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revocation_requested_at": { + "name": "revocation_requested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revocation_attempt_count": { + "name": "revocation_attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_revocation_error": { + "name": "last_revocation_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_coding_plan_key_inv_fingerprint": { + "name": "UQ_coding_plan_key_inv_fingerprint", + "columns": [ + { + "expression": "credential_fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_coding_plan_key_inv_plan_status": { + "name": "IDX_coding_plan_key_inv_plan_status", + "columns": [ + { + "expression": "plan_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_coding_plan_key_inv_available": { + "name": "IDX_coding_plan_key_inv_available", + "columns": [ + { + "expression": "plan_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"coding_plan_key_inventory\".\"status\" = 'available'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "coding_plan_key_inventory_assigned_to_user_id_kilocode_users_id_fk": { + "name": "coding_plan_key_inventory_assigned_to_user_id_kilocode_users_id_fk", + "tableFrom": "coding_plan_key_inventory", + "tableTo": "kilocode_users", + "columnsFrom": [ + "assigned_to_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "coding_plan_key_inventory_status_check": { + "name": "coding_plan_key_inventory_status_check", + "value": "\"coding_plan_key_inventory\".\"status\" IN ('available', 'assigned', 'revocation_pending', 'revoked', 'revocation_failed')" + } + }, + "isRLSEnabled": false + }, + "public.coding_plan_subscriptions": { + "name": "coding_plan_subscriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plan_id": { + "name": "plan_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key_inventory_id": { + "name": "key_inventory_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "installed_byok_key_id": { + "name": "installed_byok_key_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cost_microdollars": { + "name": "cost_microdollars", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "billing_period_days": { + "name": "billing_period_days", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "current_period_start": { + "name": "current_period_start", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "current_period_end": { + "name": "current_period_end", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "credit_renewal_at": { + "name": "credit_renewal_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "cancel_at_period_end": { + "name": "cancel_at_period_end", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "past_due_started_at": { + "name": "past_due_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "payment_grace_expires_at": { + "name": "payment_grace_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "auto_top_up_attempted_for_due": { + "name": "auto_top_up_attempted_for_due", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "canceled_at": { + "name": "canceled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "cancellation_reason": { + "name": "cancellation_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_coding_plan_sub_live_user_plan": { + "name": "UQ_coding_plan_sub_live_user_plan", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "plan_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"coding_plan_subscriptions\".\"status\" IN ('active', 'past_due')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_coding_plan_sub_status": { + "name": "IDX_coding_plan_sub_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_coding_plan_sub_renewal": { + "name": "IDX_coding_plan_sub_renewal", + "columns": [ + { + "expression": "credit_renewal_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_coding_plan_sub_inventory": { + "name": "IDX_coding_plan_sub_inventory", + "columns": [ + { + "expression": "key_inventory_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "coding_plan_subscriptions_user_id_kilocode_users_id_fk": { + "name": "coding_plan_subscriptions_user_id_kilocode_users_id_fk", + "tableFrom": "coding_plan_subscriptions", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "coding_plan_subscriptions_key_inventory_id_coding_plan_key_inventory_id_fk": { + "name": "coding_plan_subscriptions_key_inventory_id_coding_plan_key_inventory_id_fk", + "tableFrom": "coding_plan_subscriptions", + "tableTo": "coding_plan_key_inventory", + "columnsFrom": [ + "key_inventory_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "coding_plan_subscriptions_installed_byok_key_id_byok_api_keys_id_fk": { + "name": "coding_plan_subscriptions_installed_byok_key_id_byok_api_keys_id_fk", + "tableFrom": "coding_plan_subscriptions", + "tableTo": "byok_api_keys", + "columnsFrom": [ + "installed_byok_key_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "coding_plan_subscriptions_status_check": { + "name": "coding_plan_subscriptions_status_check", + "value": "\"coding_plan_subscriptions\".\"status\" IN ('active', 'past_due', 'canceled')" + }, + "coding_plan_subscriptions_live_access_check": { + "name": "coding_plan_subscriptions_live_access_check", + "value": "\"coding_plan_subscriptions\".\"status\" = 'canceled' OR \"coding_plan_subscriptions\".\"key_inventory_id\" IS NOT NULL" + } + }, + "isRLSEnabled": false + }, + "public.coding_plan_terms": { + "name": "coding_plan_terms", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "subscription_id": { + "name": "subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plan_id": { + "name": "plan_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "period_start": { + "name": "period_start", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "period_end": { + "name": "period_end", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "cost_microdollars": { + "name": "cost_microdollars", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "credit_transaction_id": { + "name": "credit_transaction_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_coding_plan_terms_request": { + "name": "UQ_coding_plan_terms_request", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "plan_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_coding_plan_terms_subscription": { + "name": "IDX_coding_plan_terms_subscription", + "columns": [ + { + "expression": "subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "coding_plan_terms_subscription_id_coding_plan_subscriptions_id_fk": { + "name": "coding_plan_terms_subscription_id_coding_plan_subscriptions_id_fk", + "tableFrom": "coding_plan_terms", + "tableTo": "coding_plan_subscriptions", + "columnsFrom": [ + "subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "coding_plan_terms_user_id_kilocode_users_id_fk": { + "name": "coding_plan_terms_user_id_kilocode_users_id_fk", + "tableFrom": "coding_plan_terms", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "coding_plan_terms_credit_transaction_id_credit_transactions_id_fk": { + "name": "coding_plan_terms_credit_transaction_id_credit_transactions_id_fk", + "tableFrom": "coding_plan_terms", + "tableTo": "credit_transactions", + "columnsFrom": [ + "credit_transaction_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "coding_plan_terms_kind_check": { + "name": "coding_plan_terms_kind_check", + "value": "\"coding_plan_terms\".\"kind\" IN ('activation', 'extension', 'renewal')" + } + }, + "isRLSEnabled": false + }, + "public.contributor_champion_contributors": { + "name": "contributor_champion_contributors", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "github_login": { + "name": "github_login", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "github_profile_url": { + "name": "github_profile_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "github_user_id": { + "name": "github_user_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "first_contribution_at": { + "name": "first_contribution_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_contribution_at": { + "name": "last_contribution_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "all_time_contributions": { + "name": "all_time_contributions", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "manual_email": { + "name": "manual_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_contributor_champion_contributors_last_contribution_at": { + "name": "IDX_contributor_champion_contributors_last_contribution_at", + "columns": [ + { + "expression": "last_contribution_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_contributor_champion_contributors_manual_email": { + "name": "IDX_contributor_champion_contributors_manual_email", + "columns": [ + { + "expression": "manual_email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_contributor_champion_contributors_github_login": { + "name": "UQ_contributor_champion_contributors_github_login", + "nullsNotDistinct": false, + "columns": [ + "github_login" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.contributor_champion_events": { + "name": "contributor_champion_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "contributor_id": { + "name": "contributor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "repo_full_name": { + "name": "repo_full_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "github_pr_number": { + "name": "github_pr_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "github_pr_url": { + "name": "github_pr_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "github_pr_title": { + "name": "github_pr_title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "github_author_login": { + "name": "github_author_login", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "github_author_email": { + "name": "github_author_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "merged_at": { + "name": "merged_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_contributor_champion_events_contributor_id": { + "name": "IDX_contributor_champion_events_contributor_id", + "columns": [ + { + "expression": "contributor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_contributor_champion_events_merged_at": { + "name": "IDX_contributor_champion_events_merged_at", + "columns": [ + { + "expression": "merged_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_contributor_champion_events_author_email": { + "name": "IDX_contributor_champion_events_author_email", + "columns": [ + { + "expression": "github_author_email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "contributor_champion_events_contributor_id_contributor_champion_contributors_id_fk": { + "name": "contributor_champion_events_contributor_id_contributor_champion_contributors_id_fk", + "tableFrom": "contributor_champion_events", + "tableTo": "contributor_champion_contributors", + "columnsFrom": [ + "contributor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_contributor_champion_events_repo_pr": { + "name": "UQ_contributor_champion_events_repo_pr", + "nullsNotDistinct": false, + "columns": [ + "repo_full_name", + "github_pr_number" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.contributor_champion_memberships": { + "name": "contributor_champion_memberships", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "contributor_id": { + "name": "contributor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "selected_tier": { + "name": "selected_tier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enrolled_tier": { + "name": "enrolled_tier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enrolled_at": { + "name": "enrolled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "credit_amount_microdollars": { + "name": "credit_amount_microdollars", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "credits_last_granted_at": { + "name": "credits_last_granted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "linked_kilo_user_id": { + "name": "linked_kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_contributor_champion_memberships_credits_due": { + "name": "IDX_contributor_champion_memberships_credits_due", + "columns": [ + { + "expression": "credits_last_granted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"contributor_champion_memberships\".\"enrolled_tier\" IS NOT NULL AND \"contributor_champion_memberships\".\"credit_amount_microdollars\" > 0", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_contributor_champion_memberships_linked_kilo_user_id": { + "name": "IDX_contributor_champion_memberships_linked_kilo_user_id", + "columns": [ + { + "expression": "linked_kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "contributor_champion_memberships_contributor_id_contributor_champion_contributors_id_fk": { + "name": "contributor_champion_memberships_contributor_id_contributor_champion_contributors_id_fk", + "tableFrom": "contributor_champion_memberships", + "tableTo": "contributor_champion_contributors", + "columnsFrom": [ + "contributor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "contributor_champion_memberships_linked_kilo_user_id_kilocode_users_id_fk": { + "name": "contributor_champion_memberships_linked_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "contributor_champion_memberships", + "tableTo": "kilocode_users", + "columnsFrom": [ + "linked_kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_contributor_champion_memberships_contributor_id": { + "name": "UQ_contributor_champion_memberships_contributor_id", + "nullsNotDistinct": false, + "columns": [ + "contributor_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "contributor_champion_memberships_selected_tier_check": { + "name": "contributor_champion_memberships_selected_tier_check", + "value": "\"contributor_champion_memberships\".\"selected_tier\" IS NULL OR \"contributor_champion_memberships\".\"selected_tier\" IN ('contributor', 'ambassador', 'champion')" + }, + "contributor_champion_memberships_enrolled_tier_check": { + "name": "contributor_champion_memberships_enrolled_tier_check", + "value": "\"contributor_champion_memberships\".\"enrolled_tier\" IS NULL OR \"contributor_champion_memberships\".\"enrolled_tier\" IN ('contributor', 'ambassador', 'champion')" + } + }, + "isRLSEnabled": false + }, + "public.contributor_champion_sync_state": { + "name": "contributor_champion_sync_state", + "schema": "", + "columns": { + "repo_full_name": { + "name": "repo_full_name", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "last_merged_at": { + "name": "last_merged_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.credit_campaigns": { + "name": "credit_campaigns", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credit_category": { + "name": "credit_category", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount_microdollars": { + "name": "amount_microdollars", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "credit_expiry_hours": { + "name": "credit_expiry_hours", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "campaign_ends_at": { + "name": "campaign_ends_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "total_redemptions_allowed": { + "name": "total_redemptions_allowed", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by_kilo_user_id": { + "name": "created_by_kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_credit_campaigns_slug": { + "name": "UQ_credit_campaigns_slug", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_credit_campaigns_credit_category": { + "name": "UQ_credit_campaigns_credit_category", + "columns": [ + { + "expression": "credit_category", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "credit_campaigns_slug_format_check": { + "name": "credit_campaigns_slug_format_check", + "value": "\"credit_campaigns\".\"slug\" ~ '^[a-z0-9-]{5,40}$'" + }, + "credit_campaigns_amount_positive_check": { + "name": "credit_campaigns_amount_positive_check", + "value": "\"credit_campaigns\".\"amount_microdollars\" > 0" + }, + "credit_campaigns_credit_expiry_hours_positive_check": { + "name": "credit_campaigns_credit_expiry_hours_positive_check", + "value": "\"credit_campaigns\".\"credit_expiry_hours\" IS NULL OR \"credit_campaigns\".\"credit_expiry_hours\" > 0" + }, + "credit_campaigns_total_redemptions_allowed_positive_check": { + "name": "credit_campaigns_total_redemptions_allowed_positive_check", + "value": "\"credit_campaigns\".\"total_redemptions_allowed\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.credit_transactions": { + "name": "credit_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount_microdollars": { + "name": "amount_microdollars", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "expiration_baseline_microdollars_used": { + "name": "expiration_baseline_microdollars_used", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "original_baseline_microdollars_used": { + "name": "original_baseline_microdollars_used", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "is_free": { + "name": "is_free", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "original_transaction_id": { + "name": "original_transaction_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "stripe_payment_id": { + "name": "stripe_payment_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "coinbase_credit_block_id": { + "name": "coinbase_credit_block_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "credit_category": { + "name": "credit_category", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expiry_date": { + "name": "expiry_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "check_category_uniqueness": { + "name": "check_category_uniqueness", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "IDX_credit_transactions_created_at": { + "name": "IDX_credit_transactions_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_credit_transactions_is_free": { + "name": "IDX_credit_transactions_is_free", + "columns": [ + { + "expression": "is_free", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_credit_transactions_kilo_user_id": { + "name": "IDX_credit_transactions_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_credit_transactions_credit_category": { + "name": "IDX_credit_transactions_credit_category", + "columns": [ + { + "expression": "credit_category", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_credit_transactions_stripe_payment_id": { + "name": "IDX_credit_transactions_stripe_payment_id", + "columns": [ + { + "expression": "stripe_payment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_credit_transactions_original_transaction_id": { + "name": "IDX_credit_transactions_original_transaction_id", + "columns": [ + { + "expression": "original_transaction_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_credit_transactions_coinbase_credit_block_id": { + "name": "IDX_credit_transactions_coinbase_credit_block_id", + "columns": [ + { + "expression": "coinbase_credit_block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_credit_transactions_organization_id": { + "name": "IDX_credit_transactions_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_credit_transactions_unique_category": { + "name": "IDX_credit_transactions_unique_category", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "credit_category", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"credit_transactions\".\"check_category_uniqueness\" = TRUE", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.custom_llm2": { + "name": "custom_llm2", + "schema": "", + "columns": { + "public_id": { + "name": "public_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "definition": { + "name": "definition", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deleted_user_email_tombstones": { + "name": "deleted_user_email_tombstones", + "schema": "", + "columns": { + "normalized_email_hash": { + "name": "normalized_email_hash", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment_builds": { + "name": "deployment_builds", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "deployment_id": { + "name": "deployment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_deployment_builds_deployment_id": { + "name": "idx_deployment_builds_deployment_id", + "columns": [ + { + "expression": "deployment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_deployment_builds_status": { + "name": "idx_deployment_builds_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "deployment_builds_deployment_id_deployments_id_fk": { + "name": "deployment_builds_deployment_id_deployments_id_fk", + "tableFrom": "deployment_builds", + "tableTo": "deployments", + "columnsFrom": [ + "deployment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment_env_vars": { + "name": "deployment_env_vars", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "deployment_id": { + "name": "deployment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_secret": { + "name": "is_secret", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_deployment_env_vars_deployment_id": { + "name": "idx_deployment_env_vars_deployment_id", + "columns": [ + { + "expression": "deployment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "deployment_env_vars_deployment_id_deployments_id_fk": { + "name": "deployment_env_vars_deployment_id_deployments_id_fk", + "tableFrom": "deployment_env_vars", + "tableTo": "deployments", + "columnsFrom": [ + "deployment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_deployment_env_vars_deployment_key": { + "name": "UQ_deployment_env_vars_deployment_key", + "nullsNotDistinct": false, + "columns": [ + "deployment_id", + "key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment_events": { + "name": "deployment_events", + "schema": "", + "columns": { + "build_id": { + "name": "build_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "event_id": { + "name": "event_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'log'" + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "idx_deployment_events_build_id": { + "name": "idx_deployment_events_build_id", + "columns": [ + { + "expression": "build_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_deployment_events_timestamp": { + "name": "idx_deployment_events_timestamp", + "columns": [ + { + "expression": "timestamp", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_deployment_events_type": { + "name": "idx_deployment_events_type", + "columns": [ + { + "expression": "event_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "deployment_events_build_id_deployment_builds_id_fk": { + "name": "deployment_events_build_id_deployment_builds_id_fk", + "tableFrom": "deployment_events", + "tableTo": "deployment_builds", + "columnsFrom": [ + "build_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "deployment_events_build_id_event_id_pk": { + "name": "deployment_events_build_id_event_id_pk", + "columns": [ + "build_id", + "event_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment_threat_detections": { + "name": "deployment_threat_detections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "deployment_id": { + "name": "deployment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "build_id": { + "name": "build_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "threat_type": { + "name": "threat_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_deployment_threat_detections_deployment_id": { + "name": "idx_deployment_threat_detections_deployment_id", + "columns": [ + { + "expression": "deployment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_deployment_threat_detections_created_at": { + "name": "idx_deployment_threat_detections_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "deployment_threat_detections_deployment_id_deployments_id_fk": { + "name": "deployment_threat_detections_deployment_id_deployments_id_fk", + "tableFrom": "deployment_threat_detections", + "tableTo": "deployments", + "columnsFrom": [ + "deployment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_threat_detections_build_id_deployment_builds_id_fk": { + "name": "deployment_threat_detections_build_id_deployment_builds_id_fk", + "tableFrom": "deployment_threat_detections", + "tableTo": "deployment_builds", + "columnsFrom": [ + "build_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployments": { + "name": "deployments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "deployment_slug": { + "name": "deployment_slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_worker_name": { + "name": "internal_worker_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "repository_source": { + "name": "repository_source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deployment_url": { + "name": "deployment_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform_integration_id": { + "name": "platform_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "source_type": { + "name": "source_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "git_auth_token": { + "name": "git_auth_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_deployed_at": { + "name": "last_deployed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_build_id": { + "name": "last_build_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "threat_status": { + "name": "threat_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_from": { + "name": "created_from", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_deployments_owned_by_user_id": { + "name": "idx_deployments_owned_by_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_deployments_owned_by_organization_id": { + "name": "idx_deployments_owned_by_organization_id", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_deployments_platform_integration_id": { + "name": "idx_deployments_platform_integration_id", + "columns": [ + { + "expression": "platform_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_deployments_repository_source_branch": { + "name": "idx_deployments_repository_source_branch", + "columns": [ + { + "expression": "repository_source", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "branch", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_deployments_threat_status_pending": { + "name": "idx_deployments_threat_status_pending", + "columns": [ + { + "expression": "threat_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"deployments\".\"threat_status\" = 'pending_scan'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "deployments_owned_by_user_id_kilocode_users_id_fk": { + "name": "deployments_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "deployments", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "deployments_owned_by_organization_id_organizations_id_fk": { + "name": "deployments_owned_by_organization_id_organizations_id_fk", + "tableFrom": "deployments", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_deployments_deployment_slug": { + "name": "UQ_deployments_deployment_slug", + "nullsNotDistinct": false, + "columns": [ + "deployment_slug" + ] + } + }, + "policies": {}, + "checkConstraints": { + "deployments_owner_check": { + "name": "deployments_owner_check", + "value": "(\n (\"deployments\".\"owned_by_user_id\" IS NOT NULL AND \"deployments\".\"owned_by_organization_id\" IS NULL) OR\n (\"deployments\".\"owned_by_user_id\" IS NULL AND \"deployments\".\"owned_by_organization_id\" IS NOT NULL)\n )" + }, + "deployments_source_type_check": { + "name": "deployments_source_type_check", + "value": "\"deployments\".\"source_type\" IN ('github', 'git', 'app-builder')" + } + }, + "isRLSEnabled": false + }, + "public.deployments_ephemeral": { + "name": "deployments_ephemeral", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_type": { + "name": "source_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_worker_name": { + "name": "internal_worker_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deployment_slug": { + "name": "deployment_slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "next_cleanup_at": { + "name": "next_cleanup_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "cleanup_claim_token": { + "name": "cleanup_claim_token", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "cleanup_claimed_until": { + "name": "cleanup_claimed_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_deployments_ephemeral_owned_by_user_id": { + "name": "idx_deployments_ephemeral_owned_by_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_deployments_ephemeral_next_cleanup_at": { + "name": "idx_deployments_ephemeral_next_cleanup_at", + "columns": [ + { + "expression": "next_cleanup_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "deployments_ephemeral_owned_by_user_id_kilocode_users_id_fk": { + "name": "deployments_ephemeral_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "deployments_ephemeral", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_deployments_ephemeral_internal_worker_name": { + "name": "UQ_deployments_ephemeral_internal_worker_name", + "nullsNotDistinct": false, + "columns": [ + "internal_worker_name" + ] + }, + "UQ_deployments_ephemeral_deployment_slug": { + "name": "UQ_deployments_ephemeral_deployment_slug", + "nullsNotDistinct": false, + "columns": [ + "deployment_slug" + ] + } + }, + "policies": {}, + "checkConstraints": { + "deployments_ephemeral_source_type_check": { + "name": "deployments_ephemeral_source_type_check", + "value": "\"deployments_ephemeral\".\"source_type\" IN ('html')" + }, + "deployments_ephemeral_status_check": { + "name": "deployments_ephemeral_status_check", + "value": "\"deployments_ephemeral\".\"status\" IN ('pending', 'active', 'cleanup_retry')" + }, + "deployments_ephemeral_claim_fields_check": { + "name": "deployments_ephemeral_claim_fields_check", + "value": "(\"deployments_ephemeral\".\"cleanup_claim_token\" IS NULL) = (\"deployments_ephemeral\".\"cleanup_claimed_until\" IS NULL)" + }, + "deployments_ephemeral_active_fields_check": { + "name": "deployments_ephemeral_active_fields_check", + "value": "\"deployments_ephemeral\".\"status\" <> 'active' OR (\"deployments_ephemeral\".\"deployment_slug\" IS NOT NULL AND \"deployments_ephemeral\".\"expires_at\" IS NOT NULL)" + } + }, + "isRLSEnabled": false + }, + "public.device_auth_requests": { + "name": "device_auth_requests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "approved_at": { + "name": "approved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_device_auth_requests_code": { + "name": "UQ_device_auth_requests_code", + "columns": [ + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_device_auth_requests_status": { + "name": "IDX_device_auth_requests_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_device_auth_requests_expires_at": { + "name": "IDX_device_auth_requests_expires_at", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_device_auth_requests_kilo_user_id": { + "name": "IDX_device_auth_requests_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "device_auth_requests_kilo_user_id_kilocode_users_id_fk": { + "name": "device_auth_requests_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "device_auth_requests", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.discord_gateway_listener": { + "name": "discord_gateway_listener", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "default": 1 + }, + "listener_id": { + "name": "listener_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.editor_name": { + "name": "editor_name", + "schema": "", + "columns": { + "editor_name_id": { + "name": "editor_name_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "editor_name": { + "name": "editor_name", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UQ_editor_name": { + "name": "UQ_editor_name", + "columns": [ + { + "expression": "editor_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.enrichment_data": { + "name": "enrichment_data", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "github_enrichment_data": { + "name": "github_enrichment_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "linkedin_enrichment_data": { + "name": "linkedin_enrichment_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "clay_enrichment_data": { + "name": "clay_enrichment_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_enrichment_data_user_id": { + "name": "IDX_enrichment_data_user_id", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "enrichment_data_user_id_kilocode_users_id_fk": { + "name": "enrichment_data_user_id_kilocode_users_id_fk", + "tableFrom": "enrichment_data", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_enrichment_data_user_id": { + "name": "UQ_enrichment_data_user_id", + "nullsNotDistinct": false, + "columns": [ + "user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.exa_monthly_usage": { + "name": "exa_monthly_usage", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "month": { + "name": "month", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "total_cost_microdollars": { + "name": "total_cost_microdollars", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_charged_microdollars": { + "name": "total_charged_microdollars", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "request_count": { + "name": "request_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "free_allowance_microdollars": { + "name": "free_allowance_microdollars", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 10000000 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_exa_monthly_usage_personal": { + "name": "idx_exa_monthly_usage_personal", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "month", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"exa_monthly_usage\".\"organization_id\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_exa_monthly_usage_org": { + "name": "idx_exa_monthly_usage_org", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "month", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"exa_monthly_usage\".\"organization_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.exa_usage_log": { + "name": "exa_usage_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cost_microdollars": { + "name": "cost_microdollars", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "charged_to_balance": { + "name": "charged_to_balance", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "feature_id": { + "name": "feature_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_exa_usage_log_user_created": { + "name": "idx_exa_usage_log_user_created", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "exa_usage_log_id_created_at_pk": { + "name": "exa_usage_log_id_created_at_pk", + "columns": [ + "id", + "created_at" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.feature": { + "name": "feature", + "schema": "", + "columns": { + "feature_id": { + "name": "feature_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "feature": { + "name": "feature", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UQ_feature": { + "name": "UQ_feature", + "columns": [ + { + "expression": "feature", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.finish_reason": { + "name": "finish_reason", + "schema": "", + "columns": { + "finish_reason_id": { + "name": "finish_reason_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "finish_reason": { + "name": "finish_reason", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UQ_finish_reason": { + "name": "UQ_finish_reason", + "columns": [ + { + "expression": "finish_reason", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.free_model_usage": { + "name": "free_model_usage", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_free_model_usage_ip_created_at": { + "name": "idx_free_model_usage_ip_created_at", + "columns": [ + { + "expression": "ip_address", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_free_model_usage_created_at": { + "name": "idx_free_model_usage_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github_branch_pull_requests": { + "name": "github_branch_pull_requests", + "schema": "", + "columns": { + "git_url": { + "name": "git_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "git_branch": { + "name": "git_branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pr_url": { + "name": "pr_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pr_number": { + "name": "pr_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "pr_state": { + "name": "pr_state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pr_title": { + "name": "pr_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pr_head_sha": { + "name": "pr_head_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pr_review_decision": { + "name": "pr_review_decision", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "review_decision_pending": { + "name": "review_decision_pending", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "review_decision_fetching_at": { + "name": "review_decision_fetching_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "pr_last_synced_at": { + "name": "pr_last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_github_branch_prs_org": { + "name": "UQ_github_branch_prs_org", + "columns": [ + { + "expression": "git_url", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "git_branch", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"github_branch_pull_requests\".\"owned_by_organization_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_github_branch_prs_user": { + "name": "UQ_github_branch_prs_user", + "columns": [ + { + "expression": "git_url", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "git_branch", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"github_branch_pull_requests\".\"owned_by_user_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "github_branch_pull_requests_owned_by_organization_id_organizations_id_fk": { + "name": "github_branch_pull_requests_owned_by_organization_id_organizations_id_fk", + "tableFrom": "github_branch_pull_requests", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "github_branch_pull_requests_owned_by_user_id_kilocode_users_id_fk": { + "name": "github_branch_pull_requests_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "github_branch_pull_requests", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "github_branch_pull_requests_owner_check": { + "name": "github_branch_pull_requests_owner_check", + "value": "(\n (\"github_branch_pull_requests\".\"owned_by_organization_id\" IS NOT NULL AND \"github_branch_pull_requests\".\"owned_by_user_id\" IS NULL) OR\n (\"github_branch_pull_requests\".\"owned_by_organization_id\" IS NULL AND \"github_branch_pull_requests\".\"owned_by_user_id\" IS NOT NULL)\n )" + }, + "github_branch_pull_requests_review_decision_check": { + "name": "github_branch_pull_requests_review_decision_check", + "value": "\"github_branch_pull_requests\".\"pr_review_decision\" IS NULL OR \"github_branch_pull_requests\".\"pr_review_decision\" IN ('approved', 'changes_requested', 'review_required')" + } + }, + "isRLSEnabled": false + }, + "public.http_ip": { + "name": "http_ip", + "schema": "", + "columns": { + "http_ip_id": { + "name": "http_ip_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "http_ip": { + "name": "http_ip", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UQ_http_ip": { + "name": "UQ_http_ip", + "columns": [ + { + "expression": "http_ip", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.http_user_agent": { + "name": "http_user_agent", + "schema": "", + "columns": { + "http_user_agent_id": { + "name": "http_user_agent_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "http_user_agent": { + "name": "http_user_agent", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UQ_http_user_agent": { + "name": "UQ_http_user_agent", + "columns": [ + { + "expression": "http_user_agent", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.impact_advocate_participants": { + "name": "impact_advocate_participants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "program_key": { + "name": "program_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'kiloclaw'" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "advocate_id": { + "name": "advocate_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "advocate_account_id": { + "name": "advocate_account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "opaque_referral_identifier": { + "name": "opaque_referral_identifier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "contact_email": { + "name": "contact_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "locale": { + "name": "locale", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "country_code": { + "name": "country_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registration_state": { + "name": "registration_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "registered_at": { + "name": "registered_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_registration_attempt_at": { + "name": "last_registration_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_error_code": { + "name": "last_error_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_error_message": { + "name": "last_error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_impact_advocate_participants_program_referral_identifier": { + "name": "UQ_impact_advocate_participants_program_referral_identifier", + "columns": [ + { + "expression": "program_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "opaque_referral_identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"impact_advocate_participants\".\"opaque_referral_identifier\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_impact_advocate_participants_registration_state": { + "name": "IDX_impact_advocate_participants_registration_state", + "columns": [ + { + "expression": "registration_state", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "impact_advocate_participants_user_id_kilocode_users_id_fk": { + "name": "impact_advocate_participants_user_id_kilocode_users_id_fk", + "tableFrom": "impact_advocate_participants", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_impact_advocate_participants_program_user": { + "name": "UQ_impact_advocate_participants_program_user", + "nullsNotDistinct": false, + "columns": [ + "program_key", + "user_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "impact_advocate_participants_program_key_check": { + "name": "impact_advocate_participants_program_key_check", + "value": "\"impact_advocate_participants\".\"program_key\" IN ('kiloclaw', 'kilo_pass')" + }, + "impact_advocate_participants_registration_state_check": { + "name": "impact_advocate_participants_registration_state_check", + "value": "\"impact_advocate_participants\".\"registration_state\" IN ('pending', 'retrying', 'registered', 'failed')" + } + }, + "isRLSEnabled": false + }, + "public.impact_advocate_registration_attempts": { + "name": "impact_advocate_registration_attempts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "program_key": { + "name": "program_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'kiloclaw'" + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "dedupe_key": { + "name": "dedupe_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "opaque_cookie_value": { + "name": "opaque_cookie_value", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cookie_value_length": { + "name": "cookie_value_length", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "delivery_state": { + "name": "delivery_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "request_payload": { + "name": "request_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "response_payload": { + "name": "response_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "response_status_code": { + "name": "response_status_code", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_retry_at": { + "name": "next_retry_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_impact_advocate_registration_attempts_participant_id": { + "name": "IDX_impact_advocate_registration_attempts_participant_id", + "columns": [ + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_impact_advocate_registration_attempts_delivery_state": { + "name": "IDX_impact_advocate_registration_attempts_delivery_state", + "columns": [ + { + "expression": "delivery_state", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "impact_advocate_registration_attempts_participant_id_impact_advocate_participants_id_fk": { + "name": "impact_advocate_registration_attempts_participant_id_impact_advocate_participants_id_fk", + "tableFrom": "impact_advocate_registration_attempts", + "tableTo": "impact_advocate_participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_impact_advocate_registration_attempts_dedupe_key": { + "name": "UQ_impact_advocate_registration_attempts_dedupe_key", + "nullsNotDistinct": false, + "columns": [ + "dedupe_key" + ] + } + }, + "policies": {}, + "checkConstraints": { + "impact_advocate_registration_attempts_program_key_check": { + "name": "impact_advocate_registration_attempts_program_key_check", + "value": "\"impact_advocate_registration_attempts\".\"program_key\" IN ('kiloclaw', 'kilo_pass')" + }, + "impact_advocate_registration_attempts_delivery_state_check": { + "name": "impact_advocate_registration_attempts_delivery_state_check", + "value": "\"impact_advocate_registration_attempts\".\"delivery_state\" IN ('queued', 'sending', 'succeeded', 'failed')" + }, + "impact_advocate_registration_attempts_cookie_value_length_non_negative_check": { + "name": "impact_advocate_registration_attempts_cookie_value_length_non_negative_check", + "value": "\"impact_advocate_registration_attempts\".\"cookie_value_length\" >= 0" + }, + "impact_advocate_registration_attempts_attempt_count_non_negative_check": { + "name": "impact_advocate_registration_attempts_attempt_count_non_negative_check", + "value": "\"impact_advocate_registration_attempts\".\"attempt_count\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.impact_advocate_reward_redemptions": { + "name": "impact_advocate_reward_redemptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "reward_id": { + "name": "reward_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "dedupe_key": { + "name": "dedupe_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "beneficiary_user_id": { + "name": "beneficiary_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "impact_reward_id": { + "name": "impact_reward_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "request_payload": { + "name": "request_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "lookup_response_payload": { + "name": "lookup_response_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "redeem_response_payload": { + "name": "redeem_response_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "response_status_code": { + "name": "response_status_code", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_retry_at": { + "name": "next_retry_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "redeemed_at": { + "name": "redeemed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_impact_advocate_reward_redemptions_beneficiary_user_id": { + "name": "IDX_impact_advocate_reward_redemptions_beneficiary_user_id", + "columns": [ + { + "expression": "beneficiary_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_impact_advocate_reward_redemptions_state": { + "name": "IDX_impact_advocate_reward_redemptions_state", + "columns": [ + { + "expression": "state", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "impact_advocate_reward_redemptions_reward_id_impact_referral_rewards_id_fk": { + "name": "impact_advocate_reward_redemptions_reward_id_impact_referral_rewards_id_fk", + "tableFrom": "impact_advocate_reward_redemptions", + "tableTo": "impact_referral_rewards", + "columnsFrom": [ + "reward_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "impact_advocate_reward_redemptions_beneficiary_user_id_kilocode_users_id_fk": { + "name": "impact_advocate_reward_redemptions_beneficiary_user_id_kilocode_users_id_fk", + "tableFrom": "impact_advocate_reward_redemptions", + "tableTo": "kilocode_users", + "columnsFrom": [ + "beneficiary_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_impact_advocate_reward_redemptions_reward_id": { + "name": "UQ_impact_advocate_reward_redemptions_reward_id", + "nullsNotDistinct": false, + "columns": [ + "reward_id" + ] + }, + "UQ_impact_advocate_reward_redemptions_dedupe_key": { + "name": "UQ_impact_advocate_reward_redemptions_dedupe_key", + "nullsNotDistinct": false, + "columns": [ + "dedupe_key" + ] + } + }, + "policies": {}, + "checkConstraints": { + "impact_advocate_reward_redemptions_state_check": { + "name": "impact_advocate_reward_redemptions_state_check", + "value": "\"impact_advocate_reward_redemptions\".\"state\" IN ('queued', 'retrying', 'redeemed', 'failed')" + }, + "impact_advocate_reward_redemptions_attempt_count_non_negative_check": { + "name": "impact_advocate_reward_redemptions_attempt_count_non_negative_check", + "value": "\"impact_advocate_reward_redemptions\".\"attempt_count\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.impact_attribution_touches": { + "name": "impact_attribution_touches", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "product": { + "name": "product", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'kiloclaw'" + }, + "program_key": { + "name": "program_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'kiloclaw'" + }, + "dedupe_key": { + "name": "dedupe_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "anonymous_id": { + "name": "anonymous_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "touch_type": { + "name": "touch_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "opaque_tracking_value": { + "name": "opaque_tracking_value", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tracking_value_length": { + "name": "tracking_value_length", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "is_tracking_value_accepted": { + "name": "is_tracking_value_accepted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "rs_code": { + "name": "rs_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rs_share_medium": { + "name": "rs_share_medium", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rs_engagement_medium": { + "name": "rs_engagement_medium", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "im_ref": { + "name": "im_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "landing_path": { + "name": "landing_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "utm_source": { + "name": "utm_source", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "utm_medium": { + "name": "utm_medium", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "utm_campaign": { + "name": "utm_campaign", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "utm_term": { + "name": "utm_term", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "utm_content": { + "name": "utm_content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "touched_at": { + "name": "touched_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "sale_attributed_at": { + "name": "sale_attributed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_impact_attribution_touches_product_user_id": { + "name": "IDX_impact_attribution_touches_product_user_id", + "columns": [ + { + "expression": "product", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_impact_attribution_touches_user_id": { + "name": "IDX_impact_attribution_touches_user_id", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_impact_attribution_touches_anonymous_id": { + "name": "IDX_impact_attribution_touches_anonymous_id", + "columns": [ + { + "expression": "anonymous_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_impact_attribution_touches_expires_at": { + "name": "IDX_impact_attribution_touches_expires_at", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_impact_attribution_touches_sale_attributed_at": { + "name": "IDX_impact_attribution_touches_sale_attributed_at", + "columns": [ + { + "expression": "sale_attributed_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "impact_attribution_touches_user_id_kilocode_users_id_fk": { + "name": "impact_attribution_touches_user_id_kilocode_users_id_fk", + "tableFrom": "impact_attribution_touches", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_impact_attribution_touches_dedupe_key": { + "name": "UQ_impact_attribution_touches_dedupe_key", + "nullsNotDistinct": false, + "columns": [ + "dedupe_key" + ] + } + }, + "policies": {}, + "checkConstraints": { + "impact_attribution_touches_product_check": { + "name": "impact_attribution_touches_product_check", + "value": "\"impact_attribution_touches\".\"product\" IN ('kiloclaw', 'kilo_pass')" + }, + "impact_attribution_touches_program_key_check": { + "name": "impact_attribution_touches_program_key_check", + "value": "\"impact_attribution_touches\".\"program_key\" IN ('kiloclaw', 'kilo_pass')" + }, + "impact_attribution_touches_touch_type_check": { + "name": "impact_attribution_touches_touch_type_check", + "value": "\"impact_attribution_touches\".\"touch_type\" IN ('affiliate', 'referral')" + }, + "impact_attribution_touches_provider_check": { + "name": "impact_attribution_touches_provider_check", + "value": "\"impact_attribution_touches\".\"provider\" IN ('impact_performance', 'impact_advocate')" + }, + "impact_attribution_touches_tracking_value_length_non_negative_check": { + "name": "impact_attribution_touches_tracking_value_length_non_negative_check", + "value": "\"impact_attribution_touches\".\"tracking_value_length\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.impact_conversion_reports": { + "name": "impact_conversion_reports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "conversion_id": { + "name": "conversion_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "dedupe_key": { + "name": "dedupe_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "action_tracker_id": { + "name": "action_tracker_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "order_id": { + "name": "order_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "request_payload": { + "name": "request_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "response_payload": { + "name": "response_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "response_status_code": { + "name": "response_status_code", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_retry_at": { + "name": "next_retry_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "delivered_at": { + "name": "delivered_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_impact_conversion_reports_conversion_id": { + "name": "IDX_impact_conversion_reports_conversion_id", + "columns": [ + { + "expression": "conversion_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_impact_conversion_reports_state": { + "name": "IDX_impact_conversion_reports_state", + "columns": [ + { + "expression": "state", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "impact_conversion_reports_conversion_id_impact_referral_conversions_id_fk": { + "name": "impact_conversion_reports_conversion_id_impact_referral_conversions_id_fk", + "tableFrom": "impact_conversion_reports", + "tableTo": "impact_referral_conversions", + "columnsFrom": [ + "conversion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_impact_conversion_reports_dedupe_key": { + "name": "UQ_impact_conversion_reports_dedupe_key", + "nullsNotDistinct": false, + "columns": [ + "dedupe_key" + ] + } + }, + "policies": {}, + "checkConstraints": { + "impact_conversion_reports_state_check": { + "name": "impact_conversion_reports_state_check", + "value": "\"impact_conversion_reports\".\"state\" IN ('queued', 'retrying', 'delivered', 'failed')" + }, + "impact_conversion_reports_attempt_count_non_negative_check": { + "name": "impact_conversion_reports_attempt_count_non_negative_check", + "value": "\"impact_conversion_reports\".\"attempt_count\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.impact_referral_conversions": { + "name": "impact_referral_conversions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "product": { + "name": "product", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'kiloclaw'" + }, + "referee_user_id": { + "name": "referee_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "referrer_user_id": { + "name": "referrer_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_touch_id": { + "name": "source_touch_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "winning_touch_type": { + "name": "winning_touch_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payment_provider": { + "name": "payment_provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'credits'" + }, + "source_payment_id": { + "name": "source_payment_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "qualified": { + "name": "qualified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "disqualification_reason": { + "name": "disqualification_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "converted_at": { + "name": "converted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_impact_referral_conversions_referee_user_id": { + "name": "IDX_impact_referral_conversions_referee_user_id", + "columns": [ + { + "expression": "referee_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_impact_referral_conversions_referrer_user_id": { + "name": "IDX_impact_referral_conversions_referrer_user_id", + "columns": [ + { + "expression": "referrer_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "impact_referral_conversions_referee_user_id_kilocode_users_id_fk": { + "name": "impact_referral_conversions_referee_user_id_kilocode_users_id_fk", + "tableFrom": "impact_referral_conversions", + "tableTo": "kilocode_users", + "columnsFrom": [ + "referee_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "impact_referral_conversions_referrer_user_id_kilocode_users_id_fk": { + "name": "impact_referral_conversions_referrer_user_id_kilocode_users_id_fk", + "tableFrom": "impact_referral_conversions", + "tableTo": "kilocode_users", + "columnsFrom": [ + "referrer_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "impact_referral_conversions_source_touch_id_impact_attribution_touches_id_fk": { + "name": "impact_referral_conversions_source_touch_id_impact_attribution_touches_id_fk", + "tableFrom": "impact_referral_conversions", + "tableTo": "impact_attribution_touches", + "columnsFrom": [ + "source_touch_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_impact_referral_conversions_product_payment_source": { + "name": "UQ_impact_referral_conversions_product_payment_source", + "nullsNotDistinct": false, + "columns": [ + "product", + "payment_provider", + "source_payment_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "impact_referral_conversions_product_check": { + "name": "impact_referral_conversions_product_check", + "value": "\"impact_referral_conversions\".\"product\" IN ('kiloclaw', 'kilo_pass')" + }, + "impact_referral_conversions_winning_touch_type_check": { + "name": "impact_referral_conversions_winning_touch_type_check", + "value": "\"impact_referral_conversions\".\"winning_touch_type\" IN ('referral', 'affiliate', 'none')" + }, + "impact_referral_conversions_payment_provider_check": { + "name": "impact_referral_conversions_payment_provider_check", + "value": "\"impact_referral_conversions\".\"payment_provider\" IN ('stripe', 'credits', 'app_store', 'google_play')" + } + }, + "isRLSEnabled": false + }, + "public.impact_referral_reward_applications": { + "name": "impact_referral_reward_applications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "product": { + "name": "product", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'kiloclaw'" + }, + "reward_id": { + "name": "reward_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "beneficiary_user_id": { + "name": "beneficiary_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subscription_id": { + "name": "subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "previous_renewal_boundary": { + "name": "previous_renewal_boundary", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "new_renewal_boundary": { + "name": "new_renewal_boundary", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "local_operation_id": { + "name": "local_operation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_operation_id": { + "name": "stripe_operation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_idempotency_key": { + "name": "stripe_idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applied_at": { + "name": "applied_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_impact_referral_reward_applications_reward_id": { + "name": "IDX_impact_referral_reward_applications_reward_id", + "columns": [ + { + "expression": "reward_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_impact_referral_reward_applications_beneficiary_user_id": { + "name": "IDX_impact_referral_reward_applications_beneficiary_user_id", + "columns": [ + { + "expression": "beneficiary_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "impact_referral_reward_applications_reward_id_impact_referral_rewards_id_fk": { + "name": "impact_referral_reward_applications_reward_id_impact_referral_rewards_id_fk", + "tableFrom": "impact_referral_reward_applications", + "tableTo": "impact_referral_rewards", + "columnsFrom": [ + "reward_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "impact_referral_reward_applications_beneficiary_user_id_kilocode_users_id_fk": { + "name": "impact_referral_reward_applications_beneficiary_user_id_kilocode_users_id_fk", + "tableFrom": "impact_referral_reward_applications", + "tableTo": "kilocode_users", + "columnsFrom": [ + "beneficiary_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "impact_referral_reward_applications_product_check": { + "name": "impact_referral_reward_applications_product_check", + "value": "\"impact_referral_reward_applications\".\"product\" IN ('kiloclaw', 'kilo_pass')" + } + }, + "isRLSEnabled": false + }, + "public.impact_referral_reward_decisions": { + "name": "impact_referral_reward_decisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "product": { + "name": "product", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'kiloclaw'" + }, + "conversion_id": { + "name": "conversion_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "beneficiary_user_id": { + "name": "beneficiary_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "beneficiary_role": { + "name": "beneficiary_role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "outcome": { + "name": "outcome", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reward_kind": { + "name": "reward_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'kiloclaw_free_month'" + }, + "months_granted": { + "name": "months_granted", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "reward_percent": { + "name": "reward_percent", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": false + }, + "source_tier": { + "name": "source_tier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reward_amount_usd": { + "name": "reward_amount_usd", + "type": "numeric(12, 2)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_impact_referral_reward_decisions_beneficiary_user_id": { + "name": "IDX_impact_referral_reward_decisions_beneficiary_user_id", + "columns": [ + { + "expression": "beneficiary_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "impact_referral_reward_decisions_conversion_id_impact_referral_conversions_id_fk": { + "name": "impact_referral_reward_decisions_conversion_id_impact_referral_conversions_id_fk", + "tableFrom": "impact_referral_reward_decisions", + "tableTo": "impact_referral_conversions", + "columnsFrom": [ + "conversion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "impact_referral_reward_decisions_beneficiary_user_id_kilocode_users_id_fk": { + "name": "impact_referral_reward_decisions_beneficiary_user_id_kilocode_users_id_fk", + "tableFrom": "impact_referral_reward_decisions", + "tableTo": "kilocode_users", + "columnsFrom": [ + "beneficiary_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_impact_referral_reward_decisions_conversion_role": { + "name": "UQ_impact_referral_reward_decisions_conversion_role", + "nullsNotDistinct": false, + "columns": [ + "conversion_id", + "beneficiary_role" + ] + } + }, + "policies": {}, + "checkConstraints": { + "impact_referral_reward_decisions_product_check": { + "name": "impact_referral_reward_decisions_product_check", + "value": "\"impact_referral_reward_decisions\".\"product\" IN ('kiloclaw', 'kilo_pass')" + }, + "impact_referral_reward_decisions_beneficiary_role_check": { + "name": "impact_referral_reward_decisions_beneficiary_role_check", + "value": "\"impact_referral_reward_decisions\".\"beneficiary_role\" IN ('referrer', 'referee')" + }, + "impact_referral_reward_decisions_outcome_check": { + "name": "impact_referral_reward_decisions_outcome_check", + "value": "\"impact_referral_reward_decisions\".\"outcome\" IN ('granted', 'cap_limited', 'disqualified')" + }, + "impact_referral_reward_decisions_reward_kind_check": { + "name": "impact_referral_reward_decisions_reward_kind_check", + "value": "\"impact_referral_reward_decisions\".\"reward_kind\" IN ('kiloclaw_free_month', 'kilo_pass_bonus')" + }, + "impact_referral_reward_decisions_months_granted_non_negative_check": { + "name": "impact_referral_reward_decisions_months_granted_non_negative_check", + "value": "\"impact_referral_reward_decisions\".\"months_granted\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.impact_referral_rewards": { + "name": "impact_referral_rewards", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "product": { + "name": "product", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'kiloclaw'" + }, + "conversion_id": { + "name": "conversion_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "decision_id": { + "name": "decision_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "beneficiary_user_id": { + "name": "beneficiary_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "beneficiary_role": { + "name": "beneficiary_role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reward_kind": { + "name": "reward_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'kiloclaw_free_month'" + }, + "months_granted": { + "name": "months_granted", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "reward_percent": { + "name": "reward_percent", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": false + }, + "source_tier": { + "name": "source_tier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reward_amount_usd": { + "name": "reward_amount_usd", + "type": "numeric(12, 2)", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "applies_to_subscription_id": { + "name": "applies_to_subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "applies_to_kilo_pass_subscription_id": { + "name": "applies_to_kilo_pass_subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "consumed_kilo_pass_issuance_id": { + "name": "consumed_kilo_pass_issuance_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "consumed_kilo_pass_issuance_item_id": { + "name": "consumed_kilo_pass_issuance_item_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "earned_at": { + "name": "earned_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "applied_at": { + "name": "applied_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "reversed_at": { + "name": "reversed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "review_reason": { + "name": "review_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_impact_referral_rewards_beneficiary_user_id": { + "name": "IDX_impact_referral_rewards_beneficiary_user_id", + "columns": [ + { + "expression": "beneficiary_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_impact_referral_rewards_status": { + "name": "IDX_impact_referral_rewards_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "impact_referral_rewards_conversion_id_impact_referral_conversions_id_fk": { + "name": "impact_referral_rewards_conversion_id_impact_referral_conversions_id_fk", + "tableFrom": "impact_referral_rewards", + "tableTo": "impact_referral_conversions", + "columnsFrom": [ + "conversion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "impact_referral_rewards_decision_id_impact_referral_reward_decisions_id_fk": { + "name": "impact_referral_rewards_decision_id_impact_referral_reward_decisions_id_fk", + "tableFrom": "impact_referral_rewards", + "tableTo": "impact_referral_reward_decisions", + "columnsFrom": [ + "decision_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "impact_referral_rewards_beneficiary_user_id_kilocode_users_id_fk": { + "name": "impact_referral_rewards_beneficiary_user_id_kilocode_users_id_fk", + "tableFrom": "impact_referral_rewards", + "tableTo": "kilocode_users", + "columnsFrom": [ + "beneficiary_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "FK_impact_referral_rewards_kilo_pass_subscription": { + "name": "FK_impact_referral_rewards_kilo_pass_subscription", + "tableFrom": "impact_referral_rewards", + "tableTo": "kilo_pass_subscriptions", + "columnsFrom": [ + "applies_to_kilo_pass_subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "FK_impact_referral_rewards_kilo_pass_issuance": { + "name": "FK_impact_referral_rewards_kilo_pass_issuance", + "tableFrom": "impact_referral_rewards", + "tableTo": "kilo_pass_issuances", + "columnsFrom": [ + "consumed_kilo_pass_issuance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "FK_impact_referral_rewards_kilo_pass_issuance_item": { + "name": "FK_impact_referral_rewards_kilo_pass_issuance_item", + "tableFrom": "impact_referral_rewards", + "tableTo": "kilo_pass_issuance_items", + "columnsFrom": [ + "consumed_kilo_pass_issuance_item_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_impact_referral_rewards_conversion_role": { + "name": "UQ_impact_referral_rewards_conversion_role", + "nullsNotDistinct": false, + "columns": [ + "conversion_id", + "beneficiary_role" + ] + }, + "UQ_impact_referral_rewards_decision_id": { + "name": "UQ_impact_referral_rewards_decision_id", + "nullsNotDistinct": false, + "columns": [ + "decision_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "impact_referral_rewards_product_check": { + "name": "impact_referral_rewards_product_check", + "value": "\"impact_referral_rewards\".\"product\" IN ('kiloclaw', 'kilo_pass')" + }, + "impact_referral_rewards_beneficiary_role_check": { + "name": "impact_referral_rewards_beneficiary_role_check", + "value": "\"impact_referral_rewards\".\"beneficiary_role\" IN ('referrer', 'referee')" + }, + "impact_referral_rewards_reward_kind_check": { + "name": "impact_referral_rewards_reward_kind_check", + "value": "\"impact_referral_rewards\".\"reward_kind\" IN ('kiloclaw_free_month', 'kilo_pass_bonus')" + }, + "impact_referral_rewards_status_check": { + "name": "impact_referral_rewards_status_check", + "value": "\"impact_referral_rewards\".\"status\" IN ('pending', 'earned', 'applied', 'reversed', 'expired', 'canceled', 'review_required')" + }, + "impact_referral_rewards_months_granted_non_negative_check": { + "name": "impact_referral_rewards_months_granted_non_negative_check", + "value": "\"impact_referral_rewards\".\"months_granted\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.impact_referrals": { + "name": "impact_referrals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "product": { + "name": "product", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'kiloclaw'" + }, + "referee_user_id": { + "name": "referee_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "referrer_user_id": { + "name": "referrer_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_touch_id": { + "name": "source_touch_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "impact_referral_id": { + "name": "impact_referral_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_impact_referrals_referrer_user_id": { + "name": "IDX_impact_referrals_referrer_user_id", + "columns": [ + { + "expression": "referrer_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_impact_referrals_source_touch_id": { + "name": "IDX_impact_referrals_source_touch_id", + "columns": [ + { + "expression": "source_touch_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "impact_referrals_referee_user_id_kilocode_users_id_fk": { + "name": "impact_referrals_referee_user_id_kilocode_users_id_fk", + "tableFrom": "impact_referrals", + "tableTo": "kilocode_users", + "columnsFrom": [ + "referee_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "impact_referrals_referrer_user_id_kilocode_users_id_fk": { + "name": "impact_referrals_referrer_user_id_kilocode_users_id_fk", + "tableFrom": "impact_referrals", + "tableTo": "kilocode_users", + "columnsFrom": [ + "referrer_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "impact_referrals_source_touch_id_impact_attribution_touches_id_fk": { + "name": "impact_referrals_source_touch_id_impact_attribution_touches_id_fk", + "tableFrom": "impact_referrals", + "tableTo": "impact_attribution_touches", + "columnsFrom": [ + "source_touch_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_impact_referrals_product_referee_user_id": { + "name": "UQ_impact_referrals_product_referee_user_id", + "nullsNotDistinct": false, + "columns": [ + "product", + "referee_user_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "impact_referrals_product_check": { + "name": "impact_referrals_product_check", + "value": "\"impact_referrals\".\"product\" IN ('kiloclaw', 'kilo_pass')" + } + }, + "isRLSEnabled": false + }, + "public.ja4_digest": { + "name": "ja4_digest", + "schema": "", + "columns": { + "ja4_digest_id": { + "name": "ja4_digest_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "ja4_digest": { + "name": "ja4_digest", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UQ_ja4_digest": { + "name": "UQ_ja4_digest", + "columns": [ + { + "expression": "ja4_digest", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kilo_pass_audit_log": { + "name": "kilo_pass_audit_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "kilo_pass_subscription_id": { + "name": "kilo_pass_subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "result": { + "name": "result", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_event_id": { + "name": "stripe_event_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_invoice_id": { + "name": "stripe_invoice_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_subscription_id": { + "name": "stripe_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "related_credit_transaction_id": { + "name": "related_credit_transaction_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "related_monthly_issuance_id": { + "name": "related_monthly_issuance_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "payload_json": { + "name": "payload_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + } + }, + "indexes": { + "IDX_kilo_pass_audit_log_created_at": { + "name": "IDX_kilo_pass_audit_log_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_audit_log_kilo_user_id": { + "name": "IDX_kilo_pass_audit_log_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_audit_log_kilo_pass_subscription_id": { + "name": "IDX_kilo_pass_audit_log_kilo_pass_subscription_id", + "columns": [ + { + "expression": "kilo_pass_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_audit_log_action": { + "name": "IDX_kilo_pass_audit_log_action", + "columns": [ + { + "expression": "action", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_audit_log_result": { + "name": "IDX_kilo_pass_audit_log_result", + "columns": [ + { + "expression": "result", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_audit_log_idempotency_key": { + "name": "IDX_kilo_pass_audit_log_idempotency_key", + "columns": [ + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_audit_log_stripe_event_id": { + "name": "IDX_kilo_pass_audit_log_stripe_event_id", + "columns": [ + { + "expression": "stripe_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_audit_log_stripe_invoice_id": { + "name": "IDX_kilo_pass_audit_log_stripe_invoice_id", + "columns": [ + { + "expression": "stripe_invoice_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_audit_log_stripe_subscription_id": { + "name": "IDX_kilo_pass_audit_log_stripe_subscription_id", + "columns": [ + { + "expression": "stripe_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_audit_log_related_credit_transaction_id": { + "name": "IDX_kilo_pass_audit_log_related_credit_transaction_id", + "columns": [ + { + "expression": "related_credit_transaction_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_audit_log_related_monthly_issuance_id": { + "name": "IDX_kilo_pass_audit_log_related_monthly_issuance_id", + "columns": [ + { + "expression": "related_monthly_issuance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kilo_pass_audit_log_kilo_user_id_kilocode_users_id_fk": { + "name": "kilo_pass_audit_log_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "kilo_pass_audit_log", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "kilo_pass_audit_log_kilo_pass_subscription_id_kilo_pass_subscriptions_id_fk": { + "name": "kilo_pass_audit_log_kilo_pass_subscription_id_kilo_pass_subscriptions_id_fk", + "tableFrom": "kilo_pass_audit_log", + "tableTo": "kilo_pass_subscriptions", + "columnsFrom": [ + "kilo_pass_subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "kilo_pass_audit_log_related_credit_transaction_id_credit_transactions_id_fk": { + "name": "kilo_pass_audit_log_related_credit_transaction_id_credit_transactions_id_fk", + "tableFrom": "kilo_pass_audit_log", + "tableTo": "credit_transactions", + "columnsFrom": [ + "related_credit_transaction_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "kilo_pass_audit_log_related_monthly_issuance_id_kilo_pass_issuances_id_fk": { + "name": "kilo_pass_audit_log_related_monthly_issuance_id_kilo_pass_issuances_id_fk", + "tableFrom": "kilo_pass_audit_log", + "tableTo": "kilo_pass_issuances", + "columnsFrom": [ + "related_monthly_issuance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kilo_pass_audit_log_action_check": { + "name": "kilo_pass_audit_log_action_check", + "value": "\"kilo_pass_audit_log\".\"action\" IN ('stripe_webhook_received', 'kilo_pass_invoice_paid_handled', 'store_purchase_completed', 'store_notification_received', 'store_subscription_renewed', 'store_subscription_canceled', 'store_subscription_expired', 'store_subscription_refunded', 'base_credits_issued', 'bonus_credits_issued', 'bonus_credits_skipped_idempotent', 'first_month_50pct_promo_issued', 'yearly_monthly_base_cron_started', 'yearly_monthly_base_cron_completed', 'issue_yearly_remaining_credits', 'duplicate_card_subscription_canceled', 'yearly_monthly_bonus_cron_started', 'yearly_monthly_bonus_cron_completed')" + }, + "kilo_pass_audit_log_result_check": { + "name": "kilo_pass_audit_log_result_check", + "value": "\"kilo_pass_audit_log\".\"result\" IN ('success', 'skipped_idempotent', 'failed')" + } + }, + "isRLSEnabled": false + }, + "public.kilo_pass_issuance_items": { + "name": "kilo_pass_issuance_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_pass_issuance_id": { + "name": "kilo_pass_issuance_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credit_transaction_id": { + "name": "credit_transaction_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "amount_usd": { + "name": "amount_usd", + "type": "numeric(12, 2)", + "primaryKey": false, + "notNull": true + }, + "bonus_percent_applied": { + "name": "bonus_percent_applied", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_kilo_pass_issuance_items_issuance_id": { + "name": "IDX_kilo_pass_issuance_items_issuance_id", + "columns": [ + { + "expression": "kilo_pass_issuance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_issuance_items_credit_transaction_id": { + "name": "IDX_kilo_pass_issuance_items_credit_transaction_id", + "columns": [ + { + "expression": "credit_transaction_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kilo_pass_issuance_items_kilo_pass_issuance_id_kilo_pass_issuances_id_fk": { + "name": "kilo_pass_issuance_items_kilo_pass_issuance_id_kilo_pass_issuances_id_fk", + "tableFrom": "kilo_pass_issuance_items", + "tableTo": "kilo_pass_issuances", + "columnsFrom": [ + "kilo_pass_issuance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "kilo_pass_issuance_items_credit_transaction_id_credit_transactions_id_fk": { + "name": "kilo_pass_issuance_items_credit_transaction_id_credit_transactions_id_fk", + "tableFrom": "kilo_pass_issuance_items", + "tableTo": "credit_transactions", + "columnsFrom": [ + "credit_transaction_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "kilo_pass_issuance_items_credit_transaction_id_unique": { + "name": "kilo_pass_issuance_items_credit_transaction_id_unique", + "nullsNotDistinct": false, + "columns": [ + "credit_transaction_id" + ] + }, + "UQ_kilo_pass_issuance_items_issuance_kind": { + "name": "UQ_kilo_pass_issuance_items_issuance_kind", + "nullsNotDistinct": false, + "columns": [ + "kilo_pass_issuance_id", + "kind" + ] + } + }, + "policies": {}, + "checkConstraints": { + "kilo_pass_issuance_items_bonus_percent_applied_range_check": { + "name": "kilo_pass_issuance_items_bonus_percent_applied_range_check", + "value": "\"kilo_pass_issuance_items\".\"bonus_percent_applied\" IS NULL OR (\"kilo_pass_issuance_items\".\"bonus_percent_applied\" >= 0 AND \"kilo_pass_issuance_items\".\"bonus_percent_applied\" <= 1)" + }, + "kilo_pass_issuance_items_amount_usd_non_negative_check": { + "name": "kilo_pass_issuance_items_amount_usd_non_negative_check", + "value": "\"kilo_pass_issuance_items\".\"amount_usd\" >= 0" + }, + "kilo_pass_issuance_items_kind_check": { + "name": "kilo_pass_issuance_items_kind_check", + "value": "\"kilo_pass_issuance_items\".\"kind\" IN ('base', 'bonus', 'promo_first_month_50pct', 'referral_bonus')" + } + }, + "isRLSEnabled": false + }, + "public.kilo_pass_issuances": { + "name": "kilo_pass_issuances", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_pass_subscription_id": { + "name": "kilo_pass_subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_month": { + "name": "issue_month", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_invoice_id": { + "name": "stripe_invoice_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "initial_welcome_promo_eligibility_reason": { + "name": "initial_welcome_promo_eligibility_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_kilo_pass_issuances_stripe_invoice_id": { + "name": "UQ_kilo_pass_issuances_stripe_invoice_id", + "columns": [ + { + "expression": "stripe_invoice_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kilo_pass_issuances\".\"stripe_invoice_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_issuances_subscription_id": { + "name": "IDX_kilo_pass_issuances_subscription_id", + "columns": [ + { + "expression": "kilo_pass_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_issuances_issue_month": { + "name": "IDX_kilo_pass_issuances_issue_month", + "columns": [ + { + "expression": "issue_month", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kilo_pass_issuances_kilo_pass_subscription_id_kilo_pass_subscriptions_id_fk": { + "name": "kilo_pass_issuances_kilo_pass_subscription_id_kilo_pass_subscriptions_id_fk", + "tableFrom": "kilo_pass_issuances", + "tableTo": "kilo_pass_subscriptions", + "columnsFrom": [ + "kilo_pass_subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_kilo_pass_issuances_subscription_issue_month": { + "name": "UQ_kilo_pass_issuances_subscription_issue_month", + "nullsNotDistinct": false, + "columns": [ + "kilo_pass_subscription_id", + "issue_month" + ] + } + }, + "policies": {}, + "checkConstraints": { + "kilo_pass_issuances_issue_month_day_one_check": { + "name": "kilo_pass_issuances_issue_month_day_one_check", + "value": "EXTRACT(DAY FROM \"kilo_pass_issuances\".\"issue_month\") = 1" + }, + "kilo_pass_issuances_source_check": { + "name": "kilo_pass_issuances_source_check", + "value": "\"kilo_pass_issuances\".\"source\" IN ('stripe_invoice', 'app_store_transaction', 'google_play_transaction', 'cron')" + }, + "kilo_pass_issuances_initial_welcome_promo_reason_check": { + "name": "kilo_pass_issuances_initial_welcome_promo_reason_check", + "value": "\"kilo_pass_issuances\".\"initial_welcome_promo_eligibility_reason\" IN ('first_payment_fingerprint_claim', 'fingerprint_previously_claimed', 'missing_fingerprint', 'no_supported_fingerprint', 'no_positive_settlement', 'settlement_unresolved')" + } + }, + "isRLSEnabled": false + }, + "public.kilo_pass_pause_events": { + "name": "kilo_pass_pause_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_pass_subscription_id": { + "name": "kilo_pass_subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "paused_at": { + "name": "paused_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "resumes_at": { + "name": "resumes_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "resumed_at": { + "name": "resumed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_kilo_pass_pause_events_subscription_id": { + "name": "IDX_kilo_pass_pause_events_subscription_id", + "columns": [ + { + "expression": "kilo_pass_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kilo_pass_pause_events_one_open_per_sub": { + "name": "UQ_kilo_pass_pause_events_one_open_per_sub", + "columns": [ + { + "expression": "kilo_pass_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kilo_pass_pause_events\".\"resumed_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kilo_pass_pause_events_kilo_pass_subscription_id_kilo_pass_subscriptions_id_fk": { + "name": "kilo_pass_pause_events_kilo_pass_subscription_id_kilo_pass_subscriptions_id_fk", + "tableFrom": "kilo_pass_pause_events", + "tableTo": "kilo_pass_subscriptions", + "columnsFrom": [ + "kilo_pass_subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kilo_pass_pause_events_resumed_at_after_paused_at_check": { + "name": "kilo_pass_pause_events_resumed_at_after_paused_at_check", + "value": "\"kilo_pass_pause_events\".\"resumed_at\" IS NULL OR \"kilo_pass_pause_events\".\"resumed_at\" >= \"kilo_pass_pause_events\".\"paused_at\"" + } + }, + "isRLSEnabled": false + }, + "public.kilo_pass_scheduled_changes": { + "name": "kilo_pass_scheduled_changes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_subscription_id": { + "name": "stripe_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "from_tier": { + "name": "from_tier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "from_cadence": { + "name": "from_cadence", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "to_tier": { + "name": "to_tier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "to_cadence": { + "name": "to_cadence", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_schedule_id": { + "name": "stripe_schedule_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "effective_at": { + "name": "effective_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_kilo_pass_scheduled_changes_kilo_user_id": { + "name": "IDX_kilo_pass_scheduled_changes_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_scheduled_changes_status": { + "name": "IDX_kilo_pass_scheduled_changes_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_scheduled_changes_stripe_subscription_id": { + "name": "IDX_kilo_pass_scheduled_changes_stripe_subscription_id", + "columns": [ + { + "expression": "stripe_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kilo_pass_scheduled_changes_active_stripe_subscription_id": { + "name": "UQ_kilo_pass_scheduled_changes_active_stripe_subscription_id", + "columns": [ + { + "expression": "stripe_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kilo_pass_scheduled_changes\".\"deleted_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_scheduled_changes_effective_at": { + "name": "IDX_kilo_pass_scheduled_changes_effective_at", + "columns": [ + { + "expression": "effective_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_scheduled_changes_deleted_at": { + "name": "IDX_kilo_pass_scheduled_changes_deleted_at", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kilo_pass_scheduled_changes_kilo_user_id_kilocode_users_id_fk": { + "name": "kilo_pass_scheduled_changes_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "kilo_pass_scheduled_changes", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "kilo_pass_scheduled_changes_stripe_subscription_id_kilo_pass_subscriptions_stripe_subscription_id_fk": { + "name": "kilo_pass_scheduled_changes_stripe_subscription_id_kilo_pass_subscriptions_stripe_subscription_id_fk", + "tableFrom": "kilo_pass_scheduled_changes", + "tableTo": "kilo_pass_subscriptions", + "columnsFrom": [ + "stripe_subscription_id" + ], + "columnsTo": [ + "stripe_subscription_id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kilo_pass_scheduled_changes_from_tier_check": { + "name": "kilo_pass_scheduled_changes_from_tier_check", + "value": "\"kilo_pass_scheduled_changes\".\"from_tier\" IN ('tier_19', 'tier_49', 'tier_199')" + }, + "kilo_pass_scheduled_changes_from_cadence_check": { + "name": "kilo_pass_scheduled_changes_from_cadence_check", + "value": "\"kilo_pass_scheduled_changes\".\"from_cadence\" IN ('monthly', 'yearly')" + }, + "kilo_pass_scheduled_changes_to_tier_check": { + "name": "kilo_pass_scheduled_changes_to_tier_check", + "value": "\"kilo_pass_scheduled_changes\".\"to_tier\" IN ('tier_19', 'tier_49', 'tier_199')" + }, + "kilo_pass_scheduled_changes_to_cadence_check": { + "name": "kilo_pass_scheduled_changes_to_cadence_check", + "value": "\"kilo_pass_scheduled_changes\".\"to_cadence\" IN ('monthly', 'yearly')" + }, + "kilo_pass_scheduled_changes_status_check": { + "name": "kilo_pass_scheduled_changes_status_check", + "value": "\"kilo_pass_scheduled_changes\".\"status\" IN ('not_started', 'active', 'completed', 'released', 'canceled')" + } + }, + "isRLSEnabled": false + }, + "public.kilo_pass_store_events": { + "name": "kilo_pass_store_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "payment_provider": { + "name": "payment_provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_id": { + "name": "event_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_subscription_id": { + "name": "provider_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_transaction_id": { + "name": "provider_transaction_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "app_account_token": { + "name": "app_account_token", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "product_id": { + "name": "product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environment": { + "name": "environment", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload_json": { + "name": "payload_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "processing_started_at": { + "name": "processing_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_kilo_pass_store_events_provider_event": { + "name": "UQ_kilo_pass_store_events_provider_event", + "columns": [ + { + "expression": "payment_provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_store_events_provider_subscription": { + "name": "IDX_kilo_pass_store_events_provider_subscription", + "columns": [ + { + "expression": "payment_provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_store_events_app_account_token": { + "name": "IDX_kilo_pass_store_events_app_account_token", + "columns": [ + { + "expression": "app_account_token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kilo_pass_store_events_payment_provider_check": { + "name": "kilo_pass_store_events_payment_provider_check", + "value": "\"kilo_pass_store_events\".\"payment_provider\" IN ('stripe', 'app_store', 'google_play')" + } + }, + "isRLSEnabled": false + }, + "public.kilo_pass_store_purchases": { + "name": "kilo_pass_store_purchases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_pass_subscription_id": { + "name": "kilo_pass_subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payment_provider": { + "name": "payment_provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "product_id": { + "name": "product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_subscription_id": { + "name": "provider_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_transaction_id": { + "name": "provider_transaction_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_original_transaction_id": { + "name": "provider_original_transaction_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "app_account_token": { + "name": "app_account_token", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "purchase_token": { + "name": "purchase_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "environment": { + "name": "environment", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "purchased_at": { + "name": "purchased_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "raw_payload_json": { + "name": "raw_payload_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_kilo_pass_store_purchases_provider_transaction": { + "name": "UQ_kilo_pass_store_purchases_provider_transaction", + "columns": [ + { + "expression": "payment_provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_transaction_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_store_purchases_subscription_id": { + "name": "IDX_kilo_pass_store_purchases_subscription_id", + "columns": [ + { + "expression": "kilo_pass_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_store_purchases_user_id": { + "name": "IDX_kilo_pass_store_purchases_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_store_purchases_app_account_token": { + "name": "IDX_kilo_pass_store_purchases_app_account_token", + "columns": [ + { + "expression": "app_account_token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_store_purchases_latest_subscription_purchase": { + "name": "IDX_kilo_pass_store_purchases_latest_subscription_purchase", + "columns": [ + { + "expression": "payment_provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "purchased_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kilo_pass_store_purchases_kilo_pass_subscription_id_kilo_pass_subscriptions_id_fk": { + "name": "kilo_pass_store_purchases_kilo_pass_subscription_id_kilo_pass_subscriptions_id_fk", + "tableFrom": "kilo_pass_store_purchases", + "tableTo": "kilo_pass_subscriptions", + "columnsFrom": [ + "kilo_pass_subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "kilo_pass_store_purchases_kilo_user_id_kilocode_users_id_fk": { + "name": "kilo_pass_store_purchases_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "kilo_pass_store_purchases", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "FK_kilo_pass_store_purchases_subscription_owner_provider": { + "name": "FK_kilo_pass_store_purchases_subscription_owner_provider", + "tableFrom": "kilo_pass_store_purchases", + "tableTo": "kilo_pass_subscriptions", + "columnsFrom": [ + "kilo_pass_subscription_id", + "kilo_user_id", + "payment_provider", + "provider_subscription_id" + ], + "columnsTo": [ + "id", + "kilo_user_id", + "payment_provider", + "provider_subscription_id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kilo_pass_store_purchases_store_provider_check": { + "name": "kilo_pass_store_purchases_store_provider_check", + "value": "\"kilo_pass_store_purchases\".\"payment_provider\" IN ('app_store', 'google_play')" + }, + "kilo_pass_store_purchases_payment_provider_check": { + "name": "kilo_pass_store_purchases_payment_provider_check", + "value": "\"kilo_pass_store_purchases\".\"payment_provider\" IN ('stripe', 'app_store', 'google_play')" + } + }, + "isRLSEnabled": false + }, + "public.kilo_pass_subscriptions": { + "name": "kilo_pass_subscriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payment_provider": { + "name": "payment_provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'stripe'" + }, + "provider_subscription_id": { + "name": "provider_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_subscription_id": { + "name": "stripe_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tier": { + "name": "tier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cadence": { + "name": "cadence", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cancel_at_period_end": { + "name": "cancel_at_period_end", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "current_streak_months": { + "name": "current_streak_months", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_yearly_issue_at": { + "name": "next_yearly_issue_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_kilo_pass_subscriptions_kilo_user_id": { + "name": "IDX_kilo_pass_subscriptions_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_subscriptions_payment_provider": { + "name": "IDX_kilo_pass_subscriptions_payment_provider", + "columns": [ + { + "expression": "payment_provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_subscriptions_status": { + "name": "IDX_kilo_pass_subscriptions_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_subscriptions_cadence": { + "name": "IDX_kilo_pass_subscriptions_cadence", + "columns": [ + { + "expression": "cadence", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kilo_pass_subscriptions_provider_subscription": { + "name": "UQ_kilo_pass_subscriptions_provider_subscription", + "columns": [ + { + "expression": "payment_provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kilo_pass_subscriptions\".\"provider_subscription_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kilo_pass_subscriptions_store_purchase_reference": { + "name": "UQ_kilo_pass_subscriptions_store_purchase_reference", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "payment_provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kilo_pass_subscriptions_kilo_user_id_kilocode_users_id_fk": { + "name": "kilo_pass_subscriptions_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "kilo_pass_subscriptions", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "kilo_pass_subscriptions_stripe_subscription_id_unique": { + "name": "kilo_pass_subscriptions_stripe_subscription_id_unique", + "nullsNotDistinct": false, + "columns": [ + "stripe_subscription_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "kilo_pass_subscriptions_current_streak_months_non_negative_check": { + "name": "kilo_pass_subscriptions_current_streak_months_non_negative_check", + "value": "\"kilo_pass_subscriptions\".\"current_streak_months\" >= 0" + }, + "kilo_pass_subscriptions_provider_ids_check": { + "name": "kilo_pass_subscriptions_provider_ids_check", + "value": "(\n \"kilo_pass_subscriptions\".\"payment_provider\" = 'stripe'\n AND \"kilo_pass_subscriptions\".\"provider_subscription_id\" IS NOT NULL\n AND \"kilo_pass_subscriptions\".\"stripe_subscription_id\" IS NOT NULL\n AND \"kilo_pass_subscriptions\".\"provider_subscription_id\" = \"kilo_pass_subscriptions\".\"stripe_subscription_id\"\n ) OR (\n \"kilo_pass_subscriptions\".\"payment_provider\" IN ('app_store', 'google_play')\n AND \"kilo_pass_subscriptions\".\"provider_subscription_id\" IS NOT NULL\n AND \"kilo_pass_subscriptions\".\"stripe_subscription_id\" IS NULL\n )" + }, + "kilo_pass_subscriptions_payment_provider_check": { + "name": "kilo_pass_subscriptions_payment_provider_check", + "value": "\"kilo_pass_subscriptions\".\"payment_provider\" IN ('stripe', 'app_store', 'google_play')" + }, + "kilo_pass_subscriptions_tier_check": { + "name": "kilo_pass_subscriptions_tier_check", + "value": "\"kilo_pass_subscriptions\".\"tier\" IN ('tier_19', 'tier_49', 'tier_199')" + }, + "kilo_pass_subscriptions_cadence_check": { + "name": "kilo_pass_subscriptions_cadence_check", + "value": "\"kilo_pass_subscriptions\".\"cadence\" IN ('monthly', 'yearly')" + } + }, + "isRLSEnabled": false + }, + "public.kilo_pass_welcome_promo_payment_fingerprint_claims": { + "name": "kilo_pass_welcome_promo_payment_fingerprint_claims", + "schema": "", + "columns": { + "stripe_payment_method_type": { + "name": "stripe_payment_method_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_fingerprint": { + "name": "stripe_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_stripe_invoice_id": { + "name": "source_stripe_invoice_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "kilo_pass_welcome_promo_payment_fingerprint_claims_stripe_payment_method_type_stripe_fingerprint_pk": { + "name": "kilo_pass_welcome_promo_payment_fingerprint_claims_stripe_payment_method_type_stripe_fingerprint_pk", + "columns": [ + "stripe_payment_method_type", + "stripe_fingerprint" + ] + } + }, + "uniqueConstraints": { + "UQ_kilo_pass_welcome_promo_payment_fingerprint_claims_source_invoice_id": { + "name": "UQ_kilo_pass_welcome_promo_payment_fingerprint_claims_source_invoice_id", + "nullsNotDistinct": false, + "columns": [ + "source_stripe_invoice_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "kilo_pass_welcome_promo_payment_fingerprint_claims_type_check": { + "name": "kilo_pass_welcome_promo_payment_fingerprint_claims_type_check", + "value": "\"kilo_pass_welcome_promo_payment_fingerprint_claims\".\"stripe_payment_method_type\" IN ('card', 'sepa_debit', 'us_bank_account', 'bacs_debit', 'au_becs_debit')" + } + }, + "isRLSEnabled": false + }, + "public.kiloclaw_access_codes": { + "name": "kiloclaw_access_codes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "redeemed_at": { + "name": "redeemed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_kiloclaw_access_codes_code": { + "name": "UQ_kiloclaw_access_codes_code", + "columns": [ + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_access_codes_user_status": { + "name": "IDX_kiloclaw_access_codes_user_status", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kiloclaw_access_codes_one_active_per_user": { + "name": "UQ_kiloclaw_access_codes_one_active_per_user", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "status = 'active'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_access_codes_kilo_user_id_kilocode_users_id_fk": { + "name": "kiloclaw_access_codes_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_access_codes", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_admin_audit_logs": { + "name": "kiloclaw_admin_audit_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_email": { + "name": "actor_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_name": { + "name": "actor_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_user_id": { + "name": "target_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_kiloclaw_admin_audit_logs_target_user_id": { + "name": "IDX_kiloclaw_admin_audit_logs_target_user_id", + "columns": [ + { + "expression": "target_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_admin_audit_logs_action": { + "name": "IDX_kiloclaw_admin_audit_logs_action", + "columns": [ + { + "expression": "action", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_admin_audit_logs_created_at": { + "name": "IDX_kiloclaw_admin_audit_logs_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_cli_runs": { + "name": "kiloclaw_cli_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "instance_id": { + "name": "instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "initiated_by_admin_id": { + "name": "initiated_by_admin_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'running'" + }, + "exit_code": { + "name": "exit_code", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "output": { + "name": "output", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "IDX_kiloclaw_cli_runs_user_id": { + "name": "IDX_kiloclaw_cli_runs_user_id", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_cli_runs_started_at": { + "name": "IDX_kiloclaw_cli_runs_started_at", + "columns": [ + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_cli_runs_instance_id": { + "name": "IDX_kiloclaw_cli_runs_instance_id", + "columns": [ + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_cli_runs_user_id_kilocode_users_id_fk": { + "name": "kiloclaw_cli_runs_user_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_cli_runs", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "kiloclaw_cli_runs_instance_id_kiloclaw_instances_id_fk": { + "name": "kiloclaw_cli_runs_instance_id_kiloclaw_instances_id_fk", + "tableFrom": "kiloclaw_cli_runs", + "tableTo": "kiloclaw_instances", + "columnsFrom": [ + "instance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "kiloclaw_cli_runs_initiated_by_admin_id_kilocode_users_id_fk": { + "name": "kiloclaw_cli_runs_initiated_by_admin_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_cli_runs", + "tableTo": "kilocode_users", + "columnsFrom": [ + "initiated_by_admin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_earlybird_purchases": { + "name": "kiloclaw_earlybird_purchases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_charge_id": { + "name": "stripe_charge_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "manual_payment_id": { + "name": "manual_payment_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "amount_cents": { + "name": "amount_cents", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "kiloclaw_earlybird_purchases_user_id_kilocode_users_id_fk": { + "name": "kiloclaw_earlybird_purchases_user_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_earlybird_purchases", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "kiloclaw_earlybird_purchases_user_id_unique": { + "name": "kiloclaw_earlybird_purchases_user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id" + ] + }, + "kiloclaw_earlybird_purchases_stripe_charge_id_unique": { + "name": "kiloclaw_earlybird_purchases_stripe_charge_id_unique", + "nullsNotDistinct": false, + "columns": [ + "stripe_charge_id" + ] + }, + "kiloclaw_earlybird_purchases_manual_payment_id_unique": { + "name": "kiloclaw_earlybird_purchases_manual_payment_id_unique", + "nullsNotDistinct": false, + "columns": [ + "manual_payment_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_email_log": { + "name": "kiloclaw_email_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "instance_id": { + "name": "instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "email_type": { + "name": "email_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "period_start": { + "name": "period_start", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "'epoch'" + }, + "sent_at": { + "name": "sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_kiloclaw_email_log_user_type_global": { + "name": "UQ_kiloclaw_email_log_user_type_global", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "email_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kiloclaw_email_log\".\"instance_id\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kiloclaw_email_log_user_instance_type_period": { + "name": "UQ_kiloclaw_email_log_user_instance_type_period", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "email_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "period_start", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kiloclaw_email_log\".\"instance_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_email_log_type_sent_instance": { + "name": "IDX_kiloclaw_email_log_type_sent_instance", + "columns": [ + { + "expression": "email_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sent_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_email_log\".\"instance_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_email_log_user_id_kilocode_users_id_fk": { + "name": "kiloclaw_email_log_user_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_email_log", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "kiloclaw_email_log_instance_id_kiloclaw_instances_id_fk": { + "name": "kiloclaw_email_log_instance_id_kiloclaw_instances_id_fk", + "tableFrom": "kiloclaw_email_log", + "tableTo": "kiloclaw_instances", + "columnsFrom": [ + "instance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_google_oauth_connections": { + "name": "kiloclaw_google_oauth_connections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "instance_id": { + "name": "instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'google'" + }, + "account_email": { + "name": "account_email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "account_subject": { + "name": "account_subject", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "oauth_client_id": { + "name": "oauth_client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "oauth_client_secret_encrypted": { + "name": "oauth_client_secret_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "credential_profile": { + "name": "credential_profile", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'kilo_owned'" + }, + "refresh_token_encrypted": { + "name": "refresh_token_encrypted", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "grants_by_source": { + "name": "grants_by_source", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "capabilities": { + "name": "capabilities", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_error_at": { + "name": "last_error_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "connected_at": { + "name": "connected_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_kiloclaw_google_oauth_connections_instance": { + "name": "UQ_kiloclaw_google_oauth_connections_instance", + "columns": [ + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_google_oauth_connections_status": { + "name": "IDX_kiloclaw_google_oauth_connections_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_google_oauth_connections_provider": { + "name": "IDX_kiloclaw_google_oauth_connections_provider", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_google_oauth_connections_instance_id_kiloclaw_instances_id_fk": { + "name": "kiloclaw_google_oauth_connections_instance_id_kiloclaw_instances_id_fk", + "tableFrom": "kiloclaw_google_oauth_connections", + "tableTo": "kiloclaw_instances", + "columnsFrom": [ + "instance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kiloclaw_google_oauth_connections_status_check": { + "name": "kiloclaw_google_oauth_connections_status_check", + "value": "\"kiloclaw_google_oauth_connections\".\"status\" IN ('active', 'action_required', 'disconnected')" + }, + "kiloclaw_google_oauth_connections_credential_profile_check": { + "name": "kiloclaw_google_oauth_connections_credential_profile_check", + "value": "\"kiloclaw_google_oauth_connections\".\"credential_profile\" IN ('legacy', 'kilo_owned')" + } + }, + "isRLSEnabled": false + }, + "public.kiloclaw_image_catalog": { + "name": "kiloclaw_image_catalog", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "openclaw_version": { + "name": "openclaw_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "variant": { + "name": "variant", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'default'" + }, + "image_tag": { + "name": "image_tag", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image_digest": { + "name": "image_digest", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'available'" + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "published_at": { + "name": "published_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "synced_at": { + "name": "synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "rollout_percent": { + "name": "rollout_percent", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "is_latest": { + "name": "is_latest", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "IDX_kiloclaw_image_catalog_status": { + "name": "IDX_kiloclaw_image_catalog_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_image_catalog_variant": { + "name": "IDX_kiloclaw_image_catalog_variant", + "columns": [ + { + "expression": "variant", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kiloclaw_image_catalog_one_latest_per_variant": { + "name": "UQ_kiloclaw_image_catalog_one_latest_per_variant", + "columns": [ + { + "expression": "variant", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kiloclaw_image_catalog\".\"is_latest\" = true", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kiloclaw_image_catalog_one_candidate_per_variant": { + "name": "UQ_kiloclaw_image_catalog_one_candidate_per_variant", + "columns": [ + { + "expression": "variant", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kiloclaw_image_catalog\".\"is_latest\" = false AND \"kiloclaw_image_catalog\".\"rollout_percent\" > 0 AND \"kiloclaw_image_catalog\".\"status\" = 'available'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "kiloclaw_image_catalog_image_tag_unique": { + "name": "kiloclaw_image_catalog_image_tag_unique", + "nullsNotDistinct": false, + "columns": [ + "image_tag" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_inbound_email_aliases": { + "name": "kiloclaw_inbound_email_aliases", + "schema": "", + "columns": { + "alias": { + "name": "alias", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "instance_id": { + "name": "instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "retired_at": { + "name": "retired_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "IDX_kiloclaw_inbound_email_aliases_instance_id": { + "name": "IDX_kiloclaw_inbound_email_aliases_instance_id", + "columns": [ + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kiloclaw_inbound_email_aliases_active_instance": { + "name": "UQ_kiloclaw_inbound_email_aliases_active_instance", + "columns": [ + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kiloclaw_inbound_email_aliases\".\"retired_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_inbound_email_aliases_instance_id_kiloclaw_instances_id_fk": { + "name": "kiloclaw_inbound_email_aliases_instance_id_kiloclaw_instances_id_fk", + "tableFrom": "kiloclaw_inbound_email_aliases", + "tableTo": "kiloclaw_instances", + "columnsFrom": [ + "instance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_inbound_email_reserved_aliases": { + "name": "kiloclaw_inbound_email_reserved_aliases", + "schema": "", + "columns": { + "alias": { + "name": "alias", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_instances": { + "name": "kiloclaw_instances", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sandbox_id": { + "name": "sandbox_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'fly'" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "inbound_email_enabled": { + "name": "inbound_email_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "inactive_trial_stopped_at": { + "name": "inactive_trial_stopped_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "destroyed_at": { + "name": "destroyed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "tracked_image_tag": { + "name": "tracked_image_tag", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "instance_type": { + "name": "instance_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "admin_size_override": { + "name": "admin_size_override", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "UQ_kiloclaw_instances_active": { + "name": "UQ_kiloclaw_instances_active", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sandbox_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kiloclaw_instances\".\"destroyed_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_instances_active_personal_by_user": { + "name": "IDX_kiloclaw_instances_active_personal_by_user", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_instances\".\"organization_id\" IS NULL AND \"kiloclaw_instances\".\"destroyed_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_instances_active_org_by_user_org": { + "name": "IDX_kiloclaw_instances_active_org_by_user_org", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_instances\".\"organization_id\" IS NOT NULL AND \"kiloclaw_instances\".\"destroyed_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_instances_active_org_by_org_created": { + "name": "IDX_kiloclaw_instances_active_org_by_org_created", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_instances\".\"organization_id\" IS NOT NULL AND \"kiloclaw_instances\".\"destroyed_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_instances_user_id_created_at": { + "name": "IDX_kiloclaw_instances_user_id_created_at", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_instances_tracked_image_tag": { + "name": "IDX_kiloclaw_instances_tracked_image_tag", + "columns": [ + { + "expression": "tracked_image_tag", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_instances\".\"destroyed_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_instances_instance_type": { + "name": "IDX_kiloclaw_instances_instance_type", + "columns": [ + { + "expression": "instance_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_instances\".\"destroyed_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_instances_admin_size_override": { + "name": "IDX_kiloclaw_instances_admin_size_override", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_instances\".\"admin_size_override\" IS NOT NULL AND \"kiloclaw_instances\".\"destroyed_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_instances_user_id_kilocode_users_id_fk": { + "name": "kiloclaw_instances_user_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_instances", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "kiloclaw_instances_organization_id_organizations_id_fk": { + "name": "kiloclaw_instances_organization_id_organizations_id_fk", + "tableFrom": "kiloclaw_instances", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "CHK_kiloclaw_instances_instance_type": { + "name": "CHK_kiloclaw_instances_instance_type", + "value": "\"kiloclaw_instances\".\"instance_type\" IS NULL OR \"kiloclaw_instances\".\"instance_type\" IN ('perf-1-3', 'perf-4-8', 'perf-4-16', 'shared-2-3', 'shared-2-4', 'custom')" + } + }, + "isRLSEnabled": false + }, + "public.kiloclaw_morning_briefing_configs": { + "name": "kiloclaw_morning_briefing_configs", + "schema": "", + "columns": { + "instance_id": { + "name": "instance_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cron": { + "name": "cron", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'0 7 * * *'" + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'UTC'" + }, + "interest_topics": { + "name": "interest_topics", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_kiloclaw_morning_briefing_configs_enabled": { + "name": "IDX_kiloclaw_morning_briefing_configs_enabled", + "columns": [ + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_morning_briefing_configs\".\"enabled\" = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_morning_briefing_configs_instance_id_kiloclaw_instances_id_fk": { + "name": "kiloclaw_morning_briefing_configs_instance_id_kiloclaw_instances_id_fk", + "tableFrom": "kiloclaw_morning_briefing_configs", + "tableTo": "kiloclaw_instances", + "columnsFrom": [ + "instance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_scheduled_action_notifications": { + "name": "kiloclaw_scheduled_action_notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "target_id": { + "name": "target_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'notice'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "sent_at": { + "name": "sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "UQ_kiloclaw_scheduled_action_notifications_target_kind_channel": { + "name": "UQ_kiloclaw_scheduled_action_notifications_target_kind_channel", + "columns": [ + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "channel", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_scheduled_action_notifications_pending": { + "name": "IDX_kiloclaw_scheduled_action_notifications_pending", + "columns": [ + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_scheduled_action_notifications\".\"status\" = 'pending'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_scheduled_action_notifications_target_id_kiloclaw_scheduled_action_targets_id_fk": { + "name": "kiloclaw_scheduled_action_notifications_target_id_kiloclaw_scheduled_action_targets_id_fk", + "tableFrom": "kiloclaw_scheduled_action_notifications", + "tableTo": "kiloclaw_scheduled_action_targets", + "columnsFrom": [ + "target_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_scheduled_action_stages": { + "name": "kiloclaw_scheduled_action_stages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "scheduled_action_id": { + "name": "scheduled_action_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "stage_index": { + "name": "stage_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "scheduled_at": { + "name": "scheduled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "notice_sent_at": { + "name": "notice_sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "applied_count": { + "name": "applied_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "skipped_count": { + "name": "skipped_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "failed_count": { + "name": "failed_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "UQ_kiloclaw_scheduled_action_stages_parent_index": { + "name": "UQ_kiloclaw_scheduled_action_stages_parent_index", + "columns": [ + { + "expression": "scheduled_action_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "stage_index", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_scheduled_action_stages_notice_due": { + "name": "IDX_kiloclaw_scheduled_action_stages_notice_due", + "columns": [ + { + "expression": "scheduled_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_scheduled_action_stages\".\"notice_sent_at\" IS NULL AND \"kiloclaw_scheduled_action_stages\".\"status\" = 'pending'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_scheduled_action_stages_scheduled_action_id_kiloclaw_scheduled_actions_id_fk": { + "name": "kiloclaw_scheduled_action_stages_scheduled_action_id_kiloclaw_scheduled_actions_id_fk", + "tableFrom": "kiloclaw_scheduled_action_stages", + "tableTo": "kiloclaw_scheduled_actions", + "columnsFrom": [ + "scheduled_action_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_scheduled_action_targets": { + "name": "kiloclaw_scheduled_action_targets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "scheduled_action_id": { + "name": "scheduled_action_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "stage_id": { + "name": "stage_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "instance_id": { + "name": "instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_image_tag": { + "name": "source_image_tag", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_image_tag": { + "name": "target_image_tag", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applied_at": { + "name": "applied_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "skip_reason": { + "name": "skip_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "UQ_kiloclaw_scheduled_action_targets_parent_instance": { + "name": "UQ_kiloclaw_scheduled_action_targets_parent_instance", + "columns": [ + { + "expression": "scheduled_action_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_scheduled_action_targets_stage": { + "name": "IDX_kiloclaw_scheduled_action_targets_stage", + "columns": [ + { + "expression": "stage_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_scheduled_action_targets_pending_by_instance": { + "name": "IDX_kiloclaw_scheduled_action_targets_pending_by_instance", + "columns": [ + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_scheduled_action_targets\".\"status\" = 'pending'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_scheduled_action_targets_scheduled_action_id_kiloclaw_scheduled_actions_id_fk": { + "name": "kiloclaw_scheduled_action_targets_scheduled_action_id_kiloclaw_scheduled_actions_id_fk", + "tableFrom": "kiloclaw_scheduled_action_targets", + "tableTo": "kiloclaw_scheduled_actions", + "columnsFrom": [ + "scheduled_action_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "kiloclaw_scheduled_action_targets_stage_id_kiloclaw_scheduled_action_stages_id_fk": { + "name": "kiloclaw_scheduled_action_targets_stage_id_kiloclaw_scheduled_action_stages_id_fk", + "tableFrom": "kiloclaw_scheduled_action_targets", + "tableTo": "kiloclaw_scheduled_action_stages", + "columnsFrom": [ + "stage_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "kiloclaw_scheduled_action_targets_instance_id_kiloclaw_instances_id_fk": { + "name": "kiloclaw_scheduled_action_targets_instance_id_kiloclaw_instances_id_fk", + "tableFrom": "kiloclaw_scheduled_action_targets", + "tableTo": "kiloclaw_instances", + "columnsFrom": [ + "instance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "kiloclaw_scheduled_action_targets_user_id_kilocode_users_id_fk": { + "name": "kiloclaw_scheduled_action_targets_user_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_scheduled_action_targets", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_scheduled_actions": { + "name": "kiloclaw_scheduled_actions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "action_type": { + "name": "action_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_image_tag": { + "name": "target_image_tag", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "override_pins": { + "name": "override_pins", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notice_lead_hours": { + "name": "notice_lead_hours", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 24 + }, + "notice_subject": { + "name": "notice_subject", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "notice_body": { + "name": "notice_body", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'scheduled'" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "total_count": { + "name": "total_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "applied_count": { + "name": "applied_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "skipped_count": { + "name": "skipped_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "failed_count": { + "name": "failed_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "IDX_kiloclaw_scheduled_actions_status": { + "name": "IDX_kiloclaw_scheduled_actions_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_scheduled_actions_action_type": { + "name": "IDX_kiloclaw_scheduled_actions_action_type", + "columns": [ + { + "expression": "action_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_scheduled_actions_created_by": { + "name": "IDX_kiloclaw_scheduled_actions_created_by", + "columns": [ + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_scheduled_actions_target_image_tag_kiloclaw_image_catalog_image_tag_fk": { + "name": "kiloclaw_scheduled_actions_target_image_tag_kiloclaw_image_catalog_image_tag_fk", + "tableFrom": "kiloclaw_scheduled_actions", + "tableTo": "kiloclaw_image_catalog", + "columnsFrom": [ + "target_image_tag" + ], + "columnsTo": [ + "image_tag" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "kiloclaw_scheduled_actions_created_by_kilocode_users_id_fk": { + "name": "kiloclaw_scheduled_actions_created_by_kilocode_users_id_fk", + "tableFrom": "kiloclaw_scheduled_actions", + "tableTo": "kilocode_users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_subscription_change_log": { + "name": "kiloclaw_subscription_change_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "subscription_id": { + "name": "subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actor_type": { + "name": "actor_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "before_state": { + "name": "before_state", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "after_state": { + "name": "after_state", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "IDX_kiloclaw_subscription_change_log_subscription_created_at": { + "name": "IDX_kiloclaw_subscription_change_log_subscription_created_at", + "columns": [ + { + "expression": "subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_subscription_change_log_created_at": { + "name": "IDX_kiloclaw_subscription_change_log_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_subscription_change_log_subscription_id_kiloclaw_subscriptions_id_fk": { + "name": "kiloclaw_subscription_change_log_subscription_id_kiloclaw_subscriptions_id_fk", + "tableFrom": "kiloclaw_subscription_change_log", + "tableTo": "kiloclaw_subscriptions", + "columnsFrom": [ + "subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kiloclaw_subscription_change_log_actor_type_check": { + "name": "kiloclaw_subscription_change_log_actor_type_check", + "value": "\"kiloclaw_subscription_change_log\".\"actor_type\" IN ('user', 'system')" + }, + "kiloclaw_subscription_change_log_action_check": { + "name": "kiloclaw_subscription_change_log_action_check", + "value": "\"kiloclaw_subscription_change_log\".\"action\" IN ('created', 'status_changed', 'plan_switched', 'period_advanced', 'canceled', 'reactivated', 'suspended', 'destruction_scheduled', 'reassigned', 'backfilled', 'payment_source_changed', 'schedule_changed', 'admin_override')" + } + }, + "isRLSEnabled": false + }, + "public.kiloclaw_subscriptions": { + "name": "kiloclaw_subscriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_subscription_id": { + "name": "stripe_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_schedule_id": { + "name": "stripe_schedule_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "transferred_to_subscription_id": { + "name": "transferred_to_subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "instance_id": { + "name": "instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "access_origin": { + "name": "access_origin", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payment_source": { + "name": "payment_source", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "kiloclaw_price_version": { + "name": "kiloclaw_price_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scheduled_plan": { + "name": "scheduled_plan", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scheduled_by": { + "name": "scheduled_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cancel_at_period_end": { + "name": "cancel_at_period_end", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "pending_conversion": { + "name": "pending_conversion", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "trial_started_at": { + "name": "trial_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "trial_ends_at": { + "name": "trial_ends_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "current_period_start": { + "name": "current_period_start", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "current_period_end": { + "name": "current_period_end", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "credit_renewal_at": { + "name": "credit_renewal_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "commit_ends_at": { + "name": "commit_ends_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "past_due_since": { + "name": "past_due_since", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "suspended_at": { + "name": "suspended_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "destruction_deadline": { + "name": "destruction_deadline", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "auto_resume_requested_at": { + "name": "auto_resume_requested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "auto_resume_retry_after": { + "name": "auto_resume_retry_after", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "auto_resume_attempt_count": { + "name": "auto_resume_attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "auto_top_up_triggered_for_period": { + "name": "auto_top_up_triggered_for_period", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_kiloclaw_subscriptions_status": { + "name": "IDX_kiloclaw_subscriptions_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_subscriptions_user_id": { + "name": "IDX_kiloclaw_subscriptions_user_id", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_subscriptions_user_status": { + "name": "IDX_kiloclaw_subscriptions_user_status", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_subscriptions_price_version": { + "name": "IDX_kiloclaw_subscriptions_price_version", + "columns": [ + { + "expression": "kiloclaw_price_version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_subscriptions_transferred_to": { + "name": "IDX_kiloclaw_subscriptions_transferred_to", + "columns": [ + { + "expression": "transferred_to_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_subscriptions_stripe_schedule_id": { + "name": "IDX_kiloclaw_subscriptions_stripe_schedule_id", + "columns": [ + { + "expression": "stripe_schedule_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_subscriptions_auto_resume_retry_after": { + "name": "IDX_kiloclaw_subscriptions_auto_resume_retry_after", + "columns": [ + { + "expression": "auto_resume_retry_after", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kiloclaw_subscriptions_instance": { + "name": "UQ_kiloclaw_subscriptions_instance", + "columns": [ + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kiloclaw_subscriptions\".\"instance_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kiloclaw_subscriptions_transferred_to": { + "name": "UQ_kiloclaw_subscriptions_transferred_to", + "columns": [ + { + "expression": "transferred_to_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kiloclaw_subscriptions\".\"transferred_to_subscription_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_subscriptions_earlybird_origin": { + "name": "IDX_kiloclaw_subscriptions_earlybird_origin", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "access_origin", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_subscriptions\".\"access_origin\" = 'earlybird'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_subscriptions_user_id_kilocode_users_id_fk": { + "name": "kiloclaw_subscriptions_user_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_subscriptions", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "kiloclaw_subscriptions_transferred_to_subscription_id_kiloclaw_subscriptions_id_fk": { + "name": "kiloclaw_subscriptions_transferred_to_subscription_id_kiloclaw_subscriptions_id_fk", + "tableFrom": "kiloclaw_subscriptions", + "tableTo": "kiloclaw_subscriptions", + "columnsFrom": [ + "transferred_to_subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "kiloclaw_subscriptions_instance_id_kiloclaw_instances_id_fk": { + "name": "kiloclaw_subscriptions_instance_id_kiloclaw_instances_id_fk", + "tableFrom": "kiloclaw_subscriptions", + "tableTo": "kiloclaw_instances", + "columnsFrom": [ + "instance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "kiloclaw_subscriptions_stripe_subscription_id_unique": { + "name": "kiloclaw_subscriptions_stripe_subscription_id_unique", + "nullsNotDistinct": false, + "columns": [ + "stripe_subscription_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "kiloclaw_subscriptions_price_version_check": { + "name": "kiloclaw_subscriptions_price_version_check", + "value": "\"kiloclaw_subscriptions\".\"kiloclaw_price_version\" IN ('2026-03-19', '2026-05-10')" + }, + "kiloclaw_subscriptions_plan_check": { + "name": "kiloclaw_subscriptions_plan_check", + "value": "\"kiloclaw_subscriptions\".\"plan\" IN ('trial', 'commit', 'standard')" + }, + "kiloclaw_subscriptions_scheduled_plan_check": { + "name": "kiloclaw_subscriptions_scheduled_plan_check", + "value": "\"kiloclaw_subscriptions\".\"scheduled_plan\" IN ('commit', 'standard')" + }, + "kiloclaw_subscriptions_scheduled_by_check": { + "name": "kiloclaw_subscriptions_scheduled_by_check", + "value": "\"kiloclaw_subscriptions\".\"scheduled_by\" IN ('auto', 'user')" + }, + "kiloclaw_subscriptions_status_check": { + "name": "kiloclaw_subscriptions_status_check", + "value": "\"kiloclaw_subscriptions\".\"status\" IN ('trialing', 'active', 'past_due', 'canceled', 'unpaid')" + }, + "kiloclaw_subscriptions_access_origin_check": { + "name": "kiloclaw_subscriptions_access_origin_check", + "value": "\"kiloclaw_subscriptions\".\"access_origin\" IN ('earlybird')" + }, + "kiloclaw_subscriptions_payment_source_check": { + "name": "kiloclaw_subscriptions_payment_source_check", + "value": "\"kiloclaw_subscriptions\".\"payment_source\" IN ('stripe', 'credits')" + } + }, + "isRLSEnabled": false + }, + "public.kiloclaw_terminal_renewal_failures": { + "name": "kiloclaw_terminal_renewal_failures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "subscription_id": { + "name": "subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "renewal_boundary": { + "name": "renewal_boundary", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unresolved'" + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "first_failure_at": { + "name": "first_failure_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_failure_at": { + "name": "last_failure_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_failure_code": { + "name": "last_failure_code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_failure_message": { + "name": "last_failure_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resolution_actor_type": { + "name": "resolution_actor_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resolution_actor_id": { + "name": "resolution_actor_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resolution_at": { + "name": "resolution_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "resolution_reason": { + "name": "resolution_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_kiloclaw_terminal_renewal_failures_subscription_boundary": { + "name": "UQ_kiloclaw_terminal_renewal_failures_subscription_boundary", + "columns": [ + { + "expression": "subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "renewal_boundary", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_terminal_renewal_failures_unresolved": { + "name": "IDX_kiloclaw_terminal_renewal_failures_unresolved", + "columns": [ + { + "expression": "subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "renewal_boundary", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_terminal_renewal_failures\".\"status\" = 'unresolved'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_terminal_renewal_failures_status_last_failure_at": { + "name": "IDX_kiloclaw_terminal_renewal_failures_status_last_failure_at", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_failure_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_terminal_renewal_failures_subscription_id_kiloclaw_subscriptions_id_fk": { + "name": "kiloclaw_terminal_renewal_failures_subscription_id_kiloclaw_subscriptions_id_fk", + "tableFrom": "kiloclaw_terminal_renewal_failures", + "tableTo": "kiloclaw_subscriptions", + "columnsFrom": [ + "subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kiloclaw_terminal_renewal_failures_status_check": { + "name": "kiloclaw_terminal_renewal_failures_status_check", + "value": "\"kiloclaw_terminal_renewal_failures\".\"status\" IN ('unresolved', 'resolved', 'waived', 'superseded')" + }, + "kiloclaw_terminal_renewal_failures_last_failure_code_check": { + "name": "kiloclaw_terminal_renewal_failures_last_failure_code_check", + "value": "\"kiloclaw_terminal_renewal_failures\".\"last_failure_code\" IN ('credit_balance_read_failed', 'renewal_transaction_failed', 'auto_top_up_marker_write_failed', 'worker_timeout', 'poison_payload', 'queue_delivery_exhausted')" + }, + "kiloclaw_terminal_renewal_failures_resolution_actor_type_check": { + "name": "kiloclaw_terminal_renewal_failures_resolution_actor_type_check", + "value": "\"kiloclaw_terminal_renewal_failures\".\"resolution_actor_type\" IN ('operator', 'system')" + } + }, + "isRLSEnabled": false + }, + "public.kiloclaw_version_pins": { + "name": "kiloclaw_version_pins", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "instance_id": { + "name": "instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "image_tag": { + "name": "image_tag", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pinned_by": { + "name": "pinned_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "kiloclaw_version_pins_instance_id_kiloclaw_instances_id_fk": { + "name": "kiloclaw_version_pins_instance_id_kiloclaw_instances_id_fk", + "tableFrom": "kiloclaw_version_pins", + "tableTo": "kiloclaw_instances", + "columnsFrom": [ + "instance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "kiloclaw_version_pins_image_tag_kiloclaw_image_catalog_image_tag_fk": { + "name": "kiloclaw_version_pins_image_tag_kiloclaw_image_catalog_image_tag_fk", + "tableFrom": "kiloclaw_version_pins", + "tableTo": "kiloclaw_image_catalog", + "columnsFrom": [ + "image_tag" + ], + "columnsTo": [ + "image_tag" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "kiloclaw_version_pins_pinned_by_kilocode_users_id_fk": { + "name": "kiloclaw_version_pins_pinned_by_kilocode_users_id_fk", + "tableFrom": "kiloclaw_version_pins", + "tableTo": "kilocode_users", + "columnsFrom": [ + "pinned_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "kiloclaw_version_pins_instance_id_unique": { + "name": "kiloclaw_version_pins_instance_id_unique", + "nullsNotDistinct": false, + "columns": [ + "instance_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kilocode_users": { + "name": "kilocode_users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "google_user_email": { + "name": "google_user_email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "google_user_name": { + "name": "google_user_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "google_user_image_url": { + "name": "google_user_image_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "hosted_domain": { + "name": "hosted_domain", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "microdollars_used": { + "name": "microdollars_used", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "kilo_pass_threshold": { + "name": "kilo_pass_threshold", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "app_store_account_token": { + "name": "app_store_account_token", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "is_admin": { + "name": "is_admin", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "total_microdollars_acquired": { + "name": "total_microdollars_acquired", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "next_credit_expiration_at": { + "name": "next_credit_expiration_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "has_validation_stytch": { + "name": "has_validation_stytch", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "has_validation_novel_card_with_hold": { + "name": "has_validation_novel_card_with_hold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "blocked_reason": { + "name": "blocked_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "blocked_at": { + "name": "blocked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "blocked_by_kilo_user_id": { + "name": "blocked_by_kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "api_token_pepper": { + "name": "api_token_pepper", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "web_session_pepper": { + "name": "web_session_pepper", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "auto_top_up_enabled": { + "name": "auto_top_up_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_bot": { + "name": "is_bot", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "kiloclaw_early_access": { + "name": "kiloclaw_early_access", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "default_model": { + "name": "default_model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cohorts": { + "name": "cohorts", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "completed_welcome_form": { + "name": "completed_welcome_form", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "linkedin_url": { + "name": "linkedin_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "github_url": { + "name": "github_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discord_server_membership_verified_at": { + "name": "discord_server_membership_verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "openrouter_upstream_safety_identifier": { + "name": "openrouter_upstream_safety_identifier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "vercel_downstream_safety_identifier": { + "name": "vercel_downstream_safety_identifier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customer_source": { + "name": "customer_source", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "signup_ip": { + "name": "signup_ip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "account_deletion_requested_at": { + "name": "account_deletion_requested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "normalized_email": { + "name": "normalized_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email_domain": { + "name": "email_domain", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "IDX_kilocode_users_signup_ip_created_at": { + "name": "IDX_kilocode_users_signup_ip_created_at", + "columns": [ + { + "expression": "signup_ip", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilocode_users_blocked_at": { + "name": "IDX_kilocode_users_blocked_at", + "columns": [ + { + "expression": "blocked_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilocode_users_blocked_by_kilo_user_id": { + "name": "IDX_kilocode_users_blocked_by_kilo_user_id", + "columns": [ + { + "expression": "blocked_by_kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kilocode_users_openrouter_upstream_safety_identifier": { + "name": "UQ_kilocode_users_openrouter_upstream_safety_identifier", + "columns": [ + { + "expression": "openrouter_upstream_safety_identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kilocode_users\".\"openrouter_upstream_safety_identifier\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kilocode_users_vercel_downstream_safety_identifier": { + "name": "UQ_kilocode_users_vercel_downstream_safety_identifier", + "columns": [ + { + "expression": "vercel_downstream_safety_identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kilocode_users\".\"vercel_downstream_safety_identifier\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilocode_users_normalized_email": { + "name": "IDX_kilocode_users_normalized_email", + "columns": [ + { + "expression": "normalized_email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilocode_users_email_domain": { + "name": "IDX_kilocode_users_email_domain", + "columns": [ + { + "expression": "email_domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "kilocode_users_app_store_account_token_unique": { + "name": "kilocode_users_app_store_account_token_unique", + "nullsNotDistinct": false, + "columns": [ + "app_store_account_token" + ] + }, + "UQ_b1afacbcf43f2c7c4cb9f7e7faa": { + "name": "UQ_b1afacbcf43f2c7c4cb9f7e7faa", + "nullsNotDistinct": false, + "columns": [ + "google_user_email" + ] + } + }, + "policies": {}, + "checkConstraints": { + "blocked_reason_not_empty": { + "name": "blocked_reason_not_empty", + "value": "length(blocked_reason) > 0" + } + }, + "isRLSEnabled": false + }, + "public.magic_link_tokens": { + "name": "magic_link_tokens", + "schema": "", + "columns": { + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "consumed_at": { + "name": "consumed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_magic_link_tokens_email": { + "name": "idx_magic_link_tokens_email", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_magic_link_tokens_expires_at": { + "name": "idx_magic_link_tokens_expires_at", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "check_expires_at_future": { + "name": "check_expires_at_future", + "value": "\"magic_link_tokens\".\"expires_at\" > \"magic_link_tokens\".\"created_at\"" + } + }, + "isRLSEnabled": false + }, + "public.mcp_gateway_assignments": { + "name": "mcp_gateway_assignments", + "schema": "", + "columns": { + "assignment_id": { + "name": "assignment_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "config_id": { + "name": "config_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "assigned_by_kilo_user_id": { + "name": "assigned_by_kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "single_user_slot": { + "name": "single_user_slot", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_mcp_gateway_assignments_active": { + "name": "UQ_mcp_gateway_assignments_active", + "columns": [ + { + "expression": "config_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"mcp_gateway_assignments\".\"revoked_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_mcp_gateway_assignments_single_user_slot": { + "name": "UQ_mcp_gateway_assignments_single_user_slot", + "columns": [ + { + "expression": "config_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "single_user_slot", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"mcp_gateway_assignments\".\"revoked_at\" is null and \"mcp_gateway_assignments\".\"single_user_slot\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_mcp_gateway_assignments_config": { + "name": "IDX_mcp_gateway_assignments_config", + "columns": [ + { + "expression": "config_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_mcp_gateway_assignments_user": { + "name": "IDX_mcp_gateway_assignments_user", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "mcp_gateway_assignments_config_id_mcp_gateway_configs_config_id_fk": { + "name": "mcp_gateway_assignments_config_id_mcp_gateway_configs_config_id_fk", + "tableFrom": "mcp_gateway_assignments", + "tableTo": "mcp_gateway_configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "config_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mcp_gateway_assignments_kilo_user_id_kilocode_users_id_fk": { + "name": "mcp_gateway_assignments_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "mcp_gateway_assignments", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mcp_gateway_assignments_assigned_by_kilo_user_id_kilocode_users_id_fk": { + "name": "mcp_gateway_assignments_assigned_by_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "mcp_gateway_assignments", + "tableTo": "kilocode_users", + "columnsFrom": [ + "assigned_by_kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mcp_gateway_audit_events": { + "name": "mcp_gateway_audit_events", + "schema": "", + "columns": { + "audit_event_id": { + "name": "audit_event_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "actor_kilo_user_id": { + "name": "actor_kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_scope": { + "name": "owner_scope", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config_id": { + "name": "config_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "connect_resource_id": { + "name": "connect_resource_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "instance_id": { + "name": "instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "outcome": { + "name": "outcome", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "correlation_metadata": { + "name": "correlation_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_mcp_gateway_audit_events_config": { + "name": "IDX_mcp_gateway_audit_events_config", + "columns": [ + { + "expression": "config_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_mcp_gateway_audit_events_owner": { + "name": "IDX_mcp_gateway_audit_events_owner", + "columns": [ + { + "expression": "owner_scope", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owner_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_mcp_gateway_audit_events_created_at": { + "name": "IDX_mcp_gateway_audit_events_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "mcp_gateway_audit_events_actor_kilo_user_id_kilocode_users_id_fk": { + "name": "mcp_gateway_audit_events_actor_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "mcp_gateway_audit_events", + "tableTo": "kilocode_users", + "columnsFrom": [ + "actor_kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "mcp_gateway_audit_events_config_id_mcp_gateway_configs_config_id_fk": { + "name": "mcp_gateway_audit_events_config_id_mcp_gateway_configs_config_id_fk", + "tableFrom": "mcp_gateway_audit_events", + "tableTo": "mcp_gateway_configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "config_id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "mcp_gateway_audit_events_connect_resource_id_mcp_gateway_connect_resources_connect_resource_id_fk": { + "name": "mcp_gateway_audit_events_connect_resource_id_mcp_gateway_connect_resources_connect_resource_id_fk", + "tableFrom": "mcp_gateway_audit_events", + "tableTo": "mcp_gateway_connect_resources", + "columnsFrom": [ + "connect_resource_id" + ], + "columnsTo": [ + "connect_resource_id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "mcp_gateway_audit_events_instance_id_mcp_gateway_connection_instances_instance_id_fk": { + "name": "mcp_gateway_audit_events_instance_id_mcp_gateway_connection_instances_instance_id_fk", + "tableFrom": "mcp_gateway_audit_events", + "tableTo": "mcp_gateway_connection_instances", + "columnsFrom": [ + "instance_id" + ], + "columnsTo": [ + "instance_id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "mcp_gateway_audit_events_owner_scope": { + "name": "mcp_gateway_audit_events_owner_scope", + "value": "\"mcp_gateway_audit_events\".\"owner_scope\" IN ('personal', 'organization')" + }, + "mcp_gateway_audit_events_outcome": { + "name": "mcp_gateway_audit_events_outcome", + "value": "\"mcp_gateway_audit_events\".\"outcome\" IN ('success', 'failure', 'blocked')" + } + }, + "isRLSEnabled": false + }, + "public.mcp_gateway_authorization_codes": { + "name": "mcp_gateway_authorization_codes", + "schema": "", + "columns": { + "authorization_code_id": { + "name": "authorization_code_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "code_hash": { + "name": "code_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "authorization_request_id": { + "name": "authorization_request_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "oauth_client_id": { + "name": "oauth_client_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_scope": { + "name": "owner_scope", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config_id": { + "name": "config_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "route_key": { + "name": "route_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "canonical_resource_url": { + "name": "canonical_resource_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "granted_scopes": { + "name": "granted_scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "code_challenge": { + "name": "code_challenge", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "code_challenge_method": { + "name": "code_challenge_method", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'S256'" + }, + "execution_context": { + "name": "execution_context", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "instance_id": { + "name": "instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "consumed_at": { + "name": "consumed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_mcp_gateway_authorization_codes_code_hash": { + "name": "UQ_mcp_gateway_authorization_codes_code_hash", + "columns": [ + { + "expression": "code_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_mcp_gateway_authorization_codes_expires_at": { + "name": "IDX_mcp_gateway_authorization_codes_expires_at", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_mcp_gateway_authorization_codes_client": { + "name": "IDX_mcp_gateway_authorization_codes_client", + "columns": [ + { + "expression": "oauth_client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "mcp_gateway_authorization_codes_authorization_request_id_mcp_gateway_authorization_requests_authorization_request_id_fk": { + "name": "mcp_gateway_authorization_codes_authorization_request_id_mcp_gateway_authorization_requests_authorization_request_id_fk", + "tableFrom": "mcp_gateway_authorization_codes", + "tableTo": "mcp_gateway_authorization_requests", + "columnsFrom": [ + "authorization_request_id" + ], + "columnsTo": [ + "authorization_request_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mcp_gateway_authorization_codes_oauth_client_id_mcp_gateway_oauth_clients_oauth_client_id_fk": { + "name": "mcp_gateway_authorization_codes_oauth_client_id_mcp_gateway_oauth_clients_oauth_client_id_fk", + "tableFrom": "mcp_gateway_authorization_codes", + "tableTo": "mcp_gateway_oauth_clients", + "columnsFrom": [ + "oauth_client_id" + ], + "columnsTo": [ + "oauth_client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mcp_gateway_authorization_codes_config_id_mcp_gateway_configs_config_id_fk": { + "name": "mcp_gateway_authorization_codes_config_id_mcp_gateway_configs_config_id_fk", + "tableFrom": "mcp_gateway_authorization_codes", + "tableTo": "mcp_gateway_configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "config_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mcp_gateway_authorization_codes_kilo_user_id_kilocode_users_id_fk": { + "name": "mcp_gateway_authorization_codes_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "mcp_gateway_authorization_codes", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mcp_gateway_authorization_codes_instance_id_mcp_gateway_connection_instances_instance_id_fk": { + "name": "mcp_gateway_authorization_codes_instance_id_mcp_gateway_connection_instances_instance_id_fk", + "tableFrom": "mcp_gateway_authorization_codes", + "tableTo": "mcp_gateway_connection_instances", + "columnsFrom": [ + "instance_id" + ], + "columnsTo": [ + "instance_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "mcp_gateway_authorization_codes_owner_scope": { + "name": "mcp_gateway_authorization_codes_owner_scope", + "value": "\"mcp_gateway_authorization_codes\".\"owner_scope\" IN ('personal', 'organization')" + } + }, + "isRLSEnabled": false + }, + "public.mcp_gateway_authorization_requests": { + "name": "mcp_gateway_authorization_requests", + "schema": "", + "columns": { + "authorization_request_id": { + "name": "authorization_request_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "request_state_hash": { + "name": "request_state_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "oauth_client_id": { + "name": "oauth_client_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_scope": { + "name": "owner_scope", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config_id": { + "name": "config_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "route_key": { + "name": "route_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "canonical_resource_url": { + "name": "canonical_resource_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requested_scopes": { + "name": "requested_scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "granted_scopes": { + "name": "granted_scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "oauth_state": { + "name": "oauth_state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "code_challenge": { + "name": "code_challenge", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "code_challenge_method": { + "name": "code_challenge_method", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'S256'" + }, + "execution_context": { + "name": "execution_context", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "instance_id": { + "name": "instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "request_status": { + "name": "request_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "consumed_at": { + "name": "consumed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_mcp_gateway_authorization_requests_state_hash": { + "name": "UQ_mcp_gateway_authorization_requests_state_hash", + "columns": [ + { + "expression": "request_state_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_mcp_gateway_authorization_requests_config": { + "name": "IDX_mcp_gateway_authorization_requests_config", + "columns": [ + { + "expression": "config_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_mcp_gateway_authorization_requests_user": { + "name": "IDX_mcp_gateway_authorization_requests_user", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_mcp_gateway_authorization_requests_expires_at": { + "name": "IDX_mcp_gateway_authorization_requests_expires_at", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "mcp_gateway_authorization_requests_oauth_client_id_mcp_gateway_oauth_clients_oauth_client_id_fk": { + "name": "mcp_gateway_authorization_requests_oauth_client_id_mcp_gateway_oauth_clients_oauth_client_id_fk", + "tableFrom": "mcp_gateway_authorization_requests", + "tableTo": "mcp_gateway_oauth_clients", + "columnsFrom": [ + "oauth_client_id" + ], + "columnsTo": [ + "oauth_client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mcp_gateway_authorization_requests_config_id_mcp_gateway_configs_config_id_fk": { + "name": "mcp_gateway_authorization_requests_config_id_mcp_gateway_configs_config_id_fk", + "tableFrom": "mcp_gateway_authorization_requests", + "tableTo": "mcp_gateway_configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "config_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mcp_gateway_authorization_requests_kilo_user_id_kilocode_users_id_fk": { + "name": "mcp_gateway_authorization_requests_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "mcp_gateway_authorization_requests", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mcp_gateway_authorization_requests_instance_id_mcp_gateway_connection_instances_instance_id_fk": { + "name": "mcp_gateway_authorization_requests_instance_id_mcp_gateway_connection_instances_instance_id_fk", + "tableFrom": "mcp_gateway_authorization_requests", + "tableTo": "mcp_gateway_connection_instances", + "columnsFrom": [ + "instance_id" + ], + "columnsTo": [ + "instance_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "mcp_gateway_authorization_requests_owner_scope": { + "name": "mcp_gateway_authorization_requests_owner_scope", + "value": "\"mcp_gateway_authorization_requests\".\"owner_scope\" IN ('personal', 'organization')" + }, + "mcp_gateway_authorization_requests_status": { + "name": "mcp_gateway_authorization_requests_status", + "value": "\"mcp_gateway_authorization_requests\".\"request_status\" IN ('pending', 'completed', 'error')" + } + }, + "isRLSEnabled": false + }, + "public.mcp_gateway_config_secrets": { + "name": "mcp_gateway_config_secrets", + "schema": "", + "columns": { + "config_secret_id": { + "name": "config_secret_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "config_id": { + "name": "config_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "secret_kind": { + "name": "secret_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "encrypted_secret": { + "name": "encrypted_secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secret_version": { + "name": "secret_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_mcp_gateway_config_secrets_active_kind": { + "name": "UQ_mcp_gateway_config_secrets_active_kind", + "columns": [ + { + "expression": "config_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "secret_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"mcp_gateway_config_secrets\".\"revoked_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_mcp_gateway_config_secrets_config": { + "name": "IDX_mcp_gateway_config_secrets_config", + "columns": [ + { + "expression": "config_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "mcp_gateway_config_secrets_config_id_mcp_gateway_configs_config_id_fk": { + "name": "mcp_gateway_config_secrets_config_id_mcp_gateway_configs_config_id_fk", + "tableFrom": "mcp_gateway_config_secrets", + "tableTo": "mcp_gateway_configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "config_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "mcp_gateway_config_secrets_version_positive": { + "name": "mcp_gateway_config_secrets_version_positive", + "value": "\"mcp_gateway_config_secrets\".\"secret_version\" > 0" + }, + "mcp_gateway_config_secrets_kind": { + "name": "mcp_gateway_config_secrets_kind", + "value": "\"mcp_gateway_config_secrets\".\"secret_kind\" IN ('static_provider_credentials', 'dynamic_registration', 'static_headers')" + } + }, + "isRLSEnabled": false + }, + "public.mcp_gateway_configs": { + "name": "mcp_gateway_configs", + "schema": "", + "columns": { + "config_id": { + "name": "config_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owner_scope": { + "name": "owner_scope", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "remote_url": { + "name": "remote_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "auth_mode": { + "name": "auth_mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sharing_mode": { + "name": "sharing_mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_scopes": { + "name": "provider_scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "provider_scope_source": { + "name": "provider_scope_source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "provider_resource": { + "name": "provider_resource", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "path_passthrough": { + "name": "path_passthrough", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "config_version": { + "name": "config_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "discovered_provider_metadata": { + "name": "discovered_provider_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "registry_metadata": { + "name": "registry_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "auxiliary_headers": { + "name": "auxiliary_headers", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_by_kilo_user_id": { + "name": "created_by_kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_mcp_gateway_configs_owner": { + "name": "IDX_mcp_gateway_configs_owner", + "columns": [ + { + "expression": "owner_scope", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owner_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_mcp_gateway_configs_enabled": { + "name": "IDX_mcp_gateway_configs_enabled", + "columns": [ + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_mcp_gateway_configs_remote_url": { + "name": "IDX_mcp_gateway_configs_remote_url", + "columns": [ + { + "expression": "remote_url", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "mcp_gateway_configs_created_by_kilo_user_id_kilocode_users_id_fk": { + "name": "mcp_gateway_configs_created_by_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "mcp_gateway_configs", + "tableTo": "kilocode_users", + "columnsFrom": [ + "created_by_kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "mcp_gateway_configs_name_not_empty": { + "name": "mcp_gateway_configs_name_not_empty", + "value": "length(trim(\"mcp_gateway_configs\".\"name\")) > 0" + }, + "mcp_gateway_configs_config_version_positive": { + "name": "mcp_gateway_configs_config_version_positive", + "value": "\"mcp_gateway_configs\".\"config_version\" > 0" + }, + "mcp_gateway_configs_personal_single_user": { + "name": "mcp_gateway_configs_personal_single_user", + "value": "\"mcp_gateway_configs\".\"owner_scope\" <> 'personal' OR \"mcp_gateway_configs\".\"sharing_mode\" = 'single_user'" + }, + "mcp_gateway_configs_owner_scope": { + "name": "mcp_gateway_configs_owner_scope", + "value": "\"mcp_gateway_configs\".\"owner_scope\" IN ('personal', 'organization')" + }, + "mcp_gateway_configs_auth_mode": { + "name": "mcp_gateway_configs_auth_mode", + "value": "\"mcp_gateway_configs\".\"auth_mode\" IN ('none', 'static_headers', 'oauth_dynamic', 'oauth_static')" + }, + "mcp_gateway_configs_sharing_mode": { + "name": "mcp_gateway_configs_sharing_mode", + "value": "\"mcp_gateway_configs\".\"sharing_mode\" IN ('single_user', 'multi_user')" + }, + "mcp_gateway_configs_provider_scope_source": { + "name": "mcp_gateway_configs_provider_scope_source", + "value": "\"mcp_gateway_configs\".\"provider_scope_source\" IN ('none', 'discovered', 'override')" + } + }, + "isRLSEnabled": false + }, + "public.mcp_gateway_connect_resources": { + "name": "mcp_gateway_connect_resources", + "schema": "", + "columns": { + "connect_resource_id": { + "name": "connect_resource_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "config_id": { + "name": "config_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "owner_scope": { + "name": "owner_scope", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "route_key": { + "name": "route_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "canonical_url": { + "name": "canonical_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "route_status": { + "name": "route_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "route_version": { + "name": "route_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "rotated_at": { + "name": "rotated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_mcp_gateway_connect_resources_route_key": { + "name": "UQ_mcp_gateway_connect_resources_route_key", + "columns": [ + { + "expression": "route_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_mcp_gateway_connect_resources_active_config": { + "name": "UQ_mcp_gateway_connect_resources_active_config", + "columns": [ + { + "expression": "config_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"mcp_gateway_connect_resources\".\"route_status\" = 'active'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_mcp_gateway_connect_resources_config": { + "name": "IDX_mcp_gateway_connect_resources_config", + "columns": [ + { + "expression": "config_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_mcp_gateway_connect_resources_canonical_url": { + "name": "IDX_mcp_gateway_connect_resources_canonical_url", + "columns": [ + { + "expression": "canonical_url", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "mcp_gateway_connect_resources_config_id_mcp_gateway_configs_config_id_fk": { + "name": "mcp_gateway_connect_resources_config_id_mcp_gateway_configs_config_id_fk", + "tableFrom": "mcp_gateway_connect_resources", + "tableTo": "mcp_gateway_configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "config_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "mcp_gateway_connect_resources_route_key_format": { + "name": "mcp_gateway_connect_resources_route_key_format", + "value": "\"mcp_gateway_connect_resources\".\"route_key\" ~ '^[A-Za-z0-9_-]{32,}$'" + }, + "mcp_gateway_connect_resources_route_version_positive": { + "name": "mcp_gateway_connect_resources_route_version_positive", + "value": "\"mcp_gateway_connect_resources\".\"route_version\" > 0" + }, + "mcp_gateway_connect_resources_owner_scope": { + "name": "mcp_gateway_connect_resources_owner_scope", + "value": "\"mcp_gateway_connect_resources\".\"owner_scope\" IN ('personal', 'organization')" + }, + "mcp_gateway_connect_resources_route_status": { + "name": "mcp_gateway_connect_resources_route_status", + "value": "\"mcp_gateway_connect_resources\".\"route_status\" IN ('active', 'rotated', 'revoked')" + } + }, + "isRLSEnabled": false + }, + "public.mcp_gateway_connection_instances": { + "name": "mcp_gateway_connection_instances", + "schema": "", + "columns": { + "instance_id": { + "name": "instance_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "config_id": { + "name": "config_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "owner_scope": { + "name": "owner_scope", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "instance_status": { + "name": "instance_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "instance_version": { + "name": "instance_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "removed_at": { + "name": "removed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_mcp_gateway_connection_instances_non_terminal": { + "name": "UQ_mcp_gateway_connection_instances_non_terminal", + "columns": [ + { + "expression": "owner_scope", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owner_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "config_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"mcp_gateway_connection_instances\".\"instance_status\" IN ('active', 'needs_reauth')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_mcp_gateway_connection_instances_config": { + "name": "IDX_mcp_gateway_connection_instances_config", + "columns": [ + { + "expression": "config_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_mcp_gateway_connection_instances_user": { + "name": "IDX_mcp_gateway_connection_instances_user", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "mcp_gateway_connection_instances_config_id_mcp_gateway_configs_config_id_fk": { + "name": "mcp_gateway_connection_instances_config_id_mcp_gateway_configs_config_id_fk", + "tableFrom": "mcp_gateway_connection_instances", + "tableTo": "mcp_gateway_configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "config_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mcp_gateway_connection_instances_kilo_user_id_kilocode_users_id_fk": { + "name": "mcp_gateway_connection_instances_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "mcp_gateway_connection_instances", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "mcp_gateway_connection_instances_version_positive": { + "name": "mcp_gateway_connection_instances_version_positive", + "value": "\"mcp_gateway_connection_instances\".\"instance_version\" > 0" + }, + "mcp_gateway_connection_instances_owner_scope": { + "name": "mcp_gateway_connection_instances_owner_scope", + "value": "\"mcp_gateway_connection_instances\".\"owner_scope\" IN ('personal', 'organization')" + }, + "mcp_gateway_connection_instances_status": { + "name": "mcp_gateway_connection_instances_status", + "value": "\"mcp_gateway_connection_instances\".\"instance_status\" IN ('active', 'needs_reauth', 'revoked', 'removed')" + } + }, + "isRLSEnabled": false + }, + "public.mcp_gateway_oauth_clients": { + "name": "mcp_gateway_oauth_clients", + "schema": "", + "columns": { + "oauth_client_id": { + "name": "oauth_client_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_name": { + "name": "client_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registration_token_hash": { + "name": "registration_token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_secret_hash": { + "name": "client_secret_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token_endpoint_auth_method": { + "name": "token_endpoint_auth_method", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "redirect_uris": { + "name": "redirect_uris", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "grant_types": { + "name": "grant_types", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "response_types": { + "name": "response_types", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "declared_scopes": { + "name": "declared_scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "registration_access_token_expires_at": { + "name": "registration_access_token_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_mcp_gateway_oauth_clients_client_id": { + "name": "UQ_mcp_gateway_oauth_clients_client_id", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_mcp_gateway_oauth_clients_registration_token_hash": { + "name": "UQ_mcp_gateway_oauth_clients_registration_token_hash", + "columns": [ + { + "expression": "registration_token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_mcp_gateway_oauth_clients_deleted_at": { + "name": "IDX_mcp_gateway_oauth_clients_deleted_at", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "mcp_gateway_oauth_clients_client_id_format": { + "name": "mcp_gateway_oauth_clients_client_id_format", + "value": "\"mcp_gateway_oauth_clients\".\"client_id\" ~ '^[A-Za-z0-9._-]+:[A-Za-z0-9._-]+$'" + }, + "mcp_gateway_oauth_clients_auth_method": { + "name": "mcp_gateway_oauth_clients_auth_method", + "value": "\"mcp_gateway_oauth_clients\".\"token_endpoint_auth_method\" IN ('none', 'client_secret_post', 'client_secret_basic')" + } + }, + "isRLSEnabled": false + }, + "public.mcp_gateway_pending_provider_authorizations": { + "name": "mcp_gateway_pending_provider_authorizations", + "schema": "", + "columns": { + "pending_provider_authorization_id": { + "name": "pending_provider_authorization_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "state_hash": { + "name": "state_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "authorization_request_id": { + "name": "authorization_request_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "config_id": { + "name": "config_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "instance_id": { + "name": "instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "owner_scope": { + "name": "owner_scope", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "route_key": { + "name": "route_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "canonical_resource_url": { + "name": "canonical_resource_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "remote_url": { + "name": "remote_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "auth_mode": { + "name": "auth_mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_authorization_endpoint": { + "name": "provider_authorization_endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_token_endpoint": { + "name": "provider_token_endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "encrypted_state": { + "name": "encrypted_state", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "execution_context": { + "name": "execution_context", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "config_version": { + "name": "config_version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "pending_status": { + "name": "pending_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "consumed_at": { + "name": "consumed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_mcp_gateway_pending_provider_authorizations_state_hash": { + "name": "UQ_mcp_gateway_pending_provider_authorizations_state_hash", + "columns": [ + { + "expression": "state_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_mcp_gateway_pending_provider_authorizations_config": { + "name": "IDX_mcp_gateway_pending_provider_authorizations_config", + "columns": [ + { + "expression": "config_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_mcp_gateway_pending_provider_authorizations_expires_at": { + "name": "IDX_mcp_gateway_pending_provider_authorizations_expires_at", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "mcp_gateway_pending_provider_authorizations_authorization_request_id_mcp_gateway_authorization_requests_authorization_request_id_fk": { + "name": "mcp_gateway_pending_provider_authorizations_authorization_request_id_mcp_gateway_authorization_requests_authorization_request_id_fk", + "tableFrom": "mcp_gateway_pending_provider_authorizations", + "tableTo": "mcp_gateway_authorization_requests", + "columnsFrom": [ + "authorization_request_id" + ], + "columnsTo": [ + "authorization_request_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mcp_gateway_pending_provider_authorizations_config_id_mcp_gateway_configs_config_id_fk": { + "name": "mcp_gateway_pending_provider_authorizations_config_id_mcp_gateway_configs_config_id_fk", + "tableFrom": "mcp_gateway_pending_provider_authorizations", + "tableTo": "mcp_gateway_configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "config_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mcp_gateway_pending_provider_authorizations_instance_id_mcp_gateway_connection_instances_instance_id_fk": { + "name": "mcp_gateway_pending_provider_authorizations_instance_id_mcp_gateway_connection_instances_instance_id_fk", + "tableFrom": "mcp_gateway_pending_provider_authorizations", + "tableTo": "mcp_gateway_connection_instances", + "columnsFrom": [ + "instance_id" + ], + "columnsTo": [ + "instance_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mcp_gateway_pending_provider_authorizations_kilo_user_id_kilocode_users_id_fk": { + "name": "mcp_gateway_pending_provider_authorizations_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "mcp_gateway_pending_provider_authorizations", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "mcp_gateway_pending_provider_authorizations_config_version_positive": { + "name": "mcp_gateway_pending_provider_authorizations_config_version_positive", + "value": "\"mcp_gateway_pending_provider_authorizations\".\"config_version\" > 0" + }, + "mcp_gateway_pending_provider_authorizations_owner_scope": { + "name": "mcp_gateway_pending_provider_authorizations_owner_scope", + "value": "\"mcp_gateway_pending_provider_authorizations\".\"owner_scope\" IN ('personal', 'organization')" + }, + "mcp_gateway_pending_provider_authorizations_auth_mode": { + "name": "mcp_gateway_pending_provider_authorizations_auth_mode", + "value": "\"mcp_gateway_pending_provider_authorizations\".\"auth_mode\" IN ('none', 'static_headers', 'oauth_dynamic', 'oauth_static')" + }, + "mcp_gateway_pending_provider_authorizations_status": { + "name": "mcp_gateway_pending_provider_authorizations_status", + "value": "\"mcp_gateway_pending_provider_authorizations\".\"pending_status\" IN ('pending', 'completed', 'error')" + } + }, + "isRLSEnabled": false + }, + "public.mcp_gateway_provider_grants": { + "name": "mcp_gateway_provider_grants", + "schema": "", + "columns": { + "provider_grant_id": { + "name": "provider_grant_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "instance_id": { + "name": "instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "encrypted_grant": { + "name": "encrypted_grant", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_subject": { + "name": "provider_subject", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "grant_scope": { + "name": "grant_scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "grant_status": { + "name": "grant_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "grant_version": { + "name": "grant_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_mcp_gateway_provider_grants_active_instance": { + "name": "UQ_mcp_gateway_provider_grants_active_instance", + "columns": [ + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"mcp_gateway_provider_grants\".\"grant_status\" = 'active'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_mcp_gateway_provider_grants_instance": { + "name": "IDX_mcp_gateway_provider_grants_instance", + "columns": [ + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "mcp_gateway_provider_grants_instance_id_mcp_gateway_connection_instances_instance_id_fk": { + "name": "mcp_gateway_provider_grants_instance_id_mcp_gateway_connection_instances_instance_id_fk", + "tableFrom": "mcp_gateway_provider_grants", + "tableTo": "mcp_gateway_connection_instances", + "columnsFrom": [ + "instance_id" + ], + "columnsTo": [ + "instance_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "mcp_gateway_provider_grants_version_positive": { + "name": "mcp_gateway_provider_grants_version_positive", + "value": "\"mcp_gateway_provider_grants\".\"grant_version\" > 0" + }, + "mcp_gateway_provider_grants_status": { + "name": "mcp_gateway_provider_grants_status", + "value": "\"mcp_gateway_provider_grants\".\"grant_status\" IN ('active', 'revoked')" + } + }, + "isRLSEnabled": false + }, + "public.mcp_gateway_rate_limit_windows": { + "name": "mcp_gateway_rate_limit_windows", + "schema": "", + "columns": { + "rate_limit_window_id": { + "name": "rate_limit_window_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "ip_hash": { + "name": "ip_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "window_started_at": { + "name": "window_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_mcp_gateway_rate_limit_windows_ip_window": { + "name": "UQ_mcp_gateway_rate_limit_windows_ip_window", + "columns": [ + { + "expression": "ip_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "window_started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_mcp_gateway_rate_limit_windows_window": { + "name": "IDX_mcp_gateway_rate_limit_windows_window", + "columns": [ + { + "expression": "window_started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "mcp_gateway_rate_limit_windows_attempt_count_non_negative": { + "name": "mcp_gateway_rate_limit_windows_attempt_count_non_negative", + "value": "\"mcp_gateway_rate_limit_windows\".\"attempt_count\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.mcp_gateway_refresh_tokens": { + "name": "mcp_gateway_refresh_tokens", + "schema": "", + "columns": { + "refresh_token_id": { + "name": "refresh_token_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rotated_from_refresh_token_id": { + "name": "rotated_from_refresh_token_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "oauth_client_id": { + "name": "oauth_client_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_scope": { + "name": "owner_scope", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config_id": { + "name": "config_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "route_key": { + "name": "route_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "canonical_resource_url": { + "name": "canonical_resource_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "granted_scopes": { + "name": "granted_scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "execution_context": { + "name": "execution_context", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "instance_id": { + "name": "instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "consumed_at": { + "name": "consumed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_mcp_gateway_refresh_tokens_token_hash": { + "name": "UQ_mcp_gateway_refresh_tokens_token_hash", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_mcp_gateway_refresh_tokens_user": { + "name": "IDX_mcp_gateway_refresh_tokens_user", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_mcp_gateway_refresh_tokens_config": { + "name": "IDX_mcp_gateway_refresh_tokens_config", + "columns": [ + { + "expression": "config_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_mcp_gateway_refresh_tokens_consumed_at": { + "name": "IDX_mcp_gateway_refresh_tokens_consumed_at", + "columns": [ + { + "expression": "consumed_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "mcp_gateway_refresh_tokens_oauth_client_id_mcp_gateway_oauth_clients_oauth_client_id_fk": { + "name": "mcp_gateway_refresh_tokens_oauth_client_id_mcp_gateway_oauth_clients_oauth_client_id_fk", + "tableFrom": "mcp_gateway_refresh_tokens", + "tableTo": "mcp_gateway_oauth_clients", + "columnsFrom": [ + "oauth_client_id" + ], + "columnsTo": [ + "oauth_client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mcp_gateway_refresh_tokens_config_id_mcp_gateway_configs_config_id_fk": { + "name": "mcp_gateway_refresh_tokens_config_id_mcp_gateway_configs_config_id_fk", + "tableFrom": "mcp_gateway_refresh_tokens", + "tableTo": "mcp_gateway_configs", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "config_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mcp_gateway_refresh_tokens_kilo_user_id_kilocode_users_id_fk": { + "name": "mcp_gateway_refresh_tokens_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "mcp_gateway_refresh_tokens", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mcp_gateway_refresh_tokens_instance_id_mcp_gateway_connection_instances_instance_id_fk": { + "name": "mcp_gateway_refresh_tokens_instance_id_mcp_gateway_connection_instances_instance_id_fk", + "tableFrom": "mcp_gateway_refresh_tokens", + "tableTo": "mcp_gateway_connection_instances", + "columnsFrom": [ + "instance_id" + ], + "columnsTo": [ + "instance_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "mcp_gateway_refresh_tokens_owner_scope": { + "name": "mcp_gateway_refresh_tokens_owner_scope", + "value": "\"mcp_gateway_refresh_tokens\".\"owner_scope\" IN ('personal', 'organization')" + } + }, + "isRLSEnabled": false + }, + "public.microdollar_usage": { + "name": "microdollar_usage", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cost": { + "name": "cost", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "input_tokens": { + "name": "input_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "output_tokens": { + "name": "output_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "cache_write_tokens": { + "name": "cache_write_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "cache_hit_tokens": { + "name": "cache_hit_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "requested_model": { + "name": "requested_model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cache_discount": { + "name": "cache_discount", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "has_error": { + "name": "has_error", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "abuse_classification": { + "name": "abuse_classification", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "inference_provider": { + "name": "inference_provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_created_at": { + "name": "idx_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_abuse_classification": { + "name": "idx_abuse_classification", + "columns": [ + { + "expression": "abuse_classification", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_kilo_user_id_created_at2": { + "name": "idx_kilo_user_id_created_at2", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_microdollar_usage_organization_id": { + "name": "idx_microdollar_usage_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"microdollar_usage\".\"organization_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.microdollar_usage_daily": { + "name": "microdollar_usage_daily", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "usage_date": { + "name": "usage_date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "total_cost_microdollars": { + "name": "total_cost_microdollars", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_microdollar_usage_daily_personal": { + "name": "idx_microdollar_usage_daily_personal", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "usage_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"microdollar_usage_daily\".\"organization_id\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_microdollar_usage_daily_org": { + "name": "idx_microdollar_usage_daily_org", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "usage_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"microdollar_usage_daily\".\"organization_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.microdollar_usage_metadata": { + "name": "microdollar_usage_metadata", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "message_id": { + "name": "message_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "http_user_agent_id": { + "name": "http_user_agent_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "http_ip_id": { + "name": "http_ip_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "vercel_ip_city_id": { + "name": "vercel_ip_city_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "vercel_ip_country_id": { + "name": "vercel_ip_country_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "vercel_ip_latitude": { + "name": "vercel_ip_latitude", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "vercel_ip_longitude": { + "name": "vercel_ip_longitude", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "ja4_digest_id": { + "name": "ja4_digest_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "user_prompt_prefix": { + "name": "user_prompt_prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "system_prompt_prefix_id": { + "name": "system_prompt_prefix_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "system_prompt_length": { + "name": "system_prompt_length", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "max_tokens": { + "name": "max_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "has_middle_out_transform": { + "name": "has_middle_out_transform", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "status_code": { + "name": "status_code", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "upstream_id": { + "name": "upstream_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "finish_reason_id": { + "name": "finish_reason_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "latency": { + "name": "latency", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "moderation_latency": { + "name": "moderation_latency", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "generation_time": { + "name": "generation_time", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "is_byok": { + "name": "is_byok", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "is_user_byok": { + "name": "is_user_byok", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "streamed": { + "name": "streamed", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "cancelled": { + "name": "cancelled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "editor_name_id": { + "name": "editor_name_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "api_kind_id": { + "name": "api_kind_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "has_tools": { + "name": "has_tools", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "machine_id": { + "name": "machine_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "feature_id": { + "name": "feature_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mode_id": { + "name": "mode_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "auto_model_id": { + "name": "auto_model_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "market_cost": { + "name": "market_cost", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "is_free": { + "name": "is_free", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "abuse_delay": { + "name": "abuse_delay", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "abuse_downgraded_from": { + "name": "abuse_downgraded_from", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_microdollar_usage_metadata_created_at": { + "name": "idx_microdollar_usage_metadata_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_microdollar_usage_metadata_session_id": { + "name": "idx_microdollar_usage_metadata_session_id", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"microdollar_usage_metadata\".\"session_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "microdollar_usage_metadata_http_user_agent_id_http_user_agent_http_user_agent_id_fk": { + "name": "microdollar_usage_metadata_http_user_agent_id_http_user_agent_http_user_agent_id_fk", + "tableFrom": "microdollar_usage_metadata", + "tableTo": "http_user_agent", + "columnsFrom": [ + "http_user_agent_id" + ], + "columnsTo": [ + "http_user_agent_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "microdollar_usage_metadata_http_ip_id_http_ip_http_ip_id_fk": { + "name": "microdollar_usage_metadata_http_ip_id_http_ip_http_ip_id_fk", + "tableFrom": "microdollar_usage_metadata", + "tableTo": "http_ip", + "columnsFrom": [ + "http_ip_id" + ], + "columnsTo": [ + "http_ip_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "microdollar_usage_metadata_vercel_ip_city_id_vercel_ip_city_vercel_ip_city_id_fk": { + "name": "microdollar_usage_metadata_vercel_ip_city_id_vercel_ip_city_vercel_ip_city_id_fk", + "tableFrom": "microdollar_usage_metadata", + "tableTo": "vercel_ip_city", + "columnsFrom": [ + "vercel_ip_city_id" + ], + "columnsTo": [ + "vercel_ip_city_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "microdollar_usage_metadata_vercel_ip_country_id_vercel_ip_country_vercel_ip_country_id_fk": { + "name": "microdollar_usage_metadata_vercel_ip_country_id_vercel_ip_country_vercel_ip_country_id_fk", + "tableFrom": "microdollar_usage_metadata", + "tableTo": "vercel_ip_country", + "columnsFrom": [ + "vercel_ip_country_id" + ], + "columnsTo": [ + "vercel_ip_country_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "microdollar_usage_metadata_ja4_digest_id_ja4_digest_ja4_digest_id_fk": { + "name": "microdollar_usage_metadata_ja4_digest_id_ja4_digest_ja4_digest_id_fk", + "tableFrom": "microdollar_usage_metadata", + "tableTo": "ja4_digest", + "columnsFrom": [ + "ja4_digest_id" + ], + "columnsTo": [ + "ja4_digest_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "microdollar_usage_metadata_system_prompt_prefix_id_system_prompt_prefix_system_prompt_prefix_id_fk": { + "name": "microdollar_usage_metadata_system_prompt_prefix_id_system_prompt_prefix_system_prompt_prefix_id_fk", + "tableFrom": "microdollar_usage_metadata", + "tableTo": "system_prompt_prefix", + "columnsFrom": [ + "system_prompt_prefix_id" + ], + "columnsTo": [ + "system_prompt_prefix_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mode": { + "name": "mode", + "schema": "", + "columns": { + "mode_id": { + "name": "mode_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UQ_mode": { + "name": "UQ_mode", + "columns": [ + { + "expression": "mode", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.model_stats": { + "name": "model_stats", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "is_featured": { + "name": "is_featured", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_stealth": { + "name": "is_stealth", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_recommended": { + "name": "is_recommended", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "openrouter_id": { + "name": "openrouter_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "aa_slug": { + "name": "aa_slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model_creator": { + "name": "model_creator", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "creator_slug": { + "name": "creator_slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "release_date": { + "name": "release_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "price_input": { + "name": "price_input", + "type": "numeric(10, 6)", + "primaryKey": false, + "notNull": false + }, + "price_output": { + "name": "price_output", + "type": "numeric(10, 6)", + "primaryKey": false, + "notNull": false + }, + "coding_index": { + "name": "coding_index", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": false + }, + "speed_tokens_per_sec": { + "name": "speed_tokens_per_sec", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": false + }, + "context_length": { + "name": "context_length", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "max_output_tokens": { + "name": "max_output_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "input_modalities": { + "name": "input_modalities", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "openrouter_data": { + "name": "openrouter_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "benchmarks": { + "name": "benchmarks", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "chart_data": { + "name": "chart_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_model_stats_openrouter_id": { + "name": "IDX_model_stats_openrouter_id", + "columns": [ + { + "expression": "openrouter_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_model_stats_slug": { + "name": "IDX_model_stats_slug", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_model_stats_is_active": { + "name": "IDX_model_stats_is_active", + "columns": [ + { + "expression": "is_active", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_model_stats_creator_slug": { + "name": "IDX_model_stats_creator_slug", + "columns": [ + { + "expression": "creator_slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_model_stats_price_input": { + "name": "IDX_model_stats_price_input", + "columns": [ + { + "expression": "price_input", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_model_stats_coding_index": { + "name": "IDX_model_stats_coding_index", + "columns": [ + { + "expression": "coding_index", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_model_stats_context_length": { + "name": "IDX_model_stats_context_length", + "columns": [ + { + "expression": "context_length", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "model_stats_openrouter_id_unique": { + "name": "model_stats_openrouter_id_unique", + "nullsNotDistinct": false, + "columns": [ + "openrouter_id" + ] + }, + "model_stats_slug_unique": { + "name": "model_stats_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.model_eval_ingestions": { + "name": "model_eval_ingestions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "bench_eval_name": { + "name": "bench_eval_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bench_eval_url": { + "name": "bench_eval_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model_stats_id": { + "name": "model_stats_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "variant": { + "name": "variant", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "task_source": { + "name": "task_source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "n_total_trials": { + "name": "n_total_trials", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "n_attempts": { + "name": "n_attempts", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_score": { + "name": "total_score", + "type": "numeric(14, 6)", + "primaryKey": false, + "notNull": true + }, + "overall_score": { + "name": "overall_score", + "type": "numeric(12, 8)", + "primaryKey": false, + "notNull": true + }, + "n_errored": { + "name": "n_errored", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "avg_cost_microdollars": { + "name": "avg_cost_microdollars", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "total_cost_microdollars": { + "name": "total_cost_microdollars", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "avg_input_tokens": { + "name": "avg_input_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_input_tokens": { + "name": "total_input_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "avg_output_tokens": { + "name": "avg_output_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_output_tokens": { + "name": "total_output_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "avg_cache_read_tokens": { + "name": "avg_cache_read_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_cache_read_tokens": { + "name": "total_cache_read_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "avg_execution_ms": { + "name": "avg_execution_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "promoted_at": { + "name": "promoted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "promoted_by_email": { + "name": "promoted_by_email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "promotion_note": { + "name": "promotion_note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_model_eval_ingestions_lookup": { + "name": "IDX_model_eval_ingestions_lookup", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "model", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "variant", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "task_source", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "promoted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_model_eval_ingestions_model_stats": { + "name": "IDX_model_eval_ingestions_model_stats", + "columns": [ + { + "expression": "model_stats_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_model_eval_ingestions_promoted_by_email_lower": { + "name": "IDX_model_eval_ingestions_promoted_by_email_lower", + "columns": [ + { + "expression": "LOWER(\"promoted_by_email\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "model_eval_ingestions_model_stats_id_model_stats_id_fk": { + "name": "model_eval_ingestions_model_stats_id_model_stats_id_fk", + "tableFrom": "model_eval_ingestions", + "tableTo": "model_stats", + "columnsFrom": [ + "model_stats_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "model_eval_ingestions_bench_eval_name_unique": { + "name": "model_eval_ingestions_bench_eval_name_unique", + "nullsNotDistinct": false, + "columns": [ + "bench_eval_name" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.model_experiment": { + "name": "model_experiment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "public_model_id": { + "name": "public_model_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "is_archived": { + "name": "is_archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "UQ_model_experiment_public_model_id_routing": { + "name": "UQ_model_experiment_public_model_id_routing", + "columns": [ + { + "expression": "public_model_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"model_experiment\".\"status\" IN ('active', 'paused')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_model_experiment_status": { + "name": "IDX_model_experiment_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "model_experiment_created_by_user_id_kilocode_users_id_fk": { + "name": "model_experiment_created_by_user_id_kilocode_users_id_fk", + "tableFrom": "model_experiment", + "tableTo": "kilocode_users", + "columnsFrom": [ + "created_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "model_experiment_status_valid": { + "name": "model_experiment_status_valid", + "value": "\"model_experiment\".\"status\" IN ('draft', 'active', 'paused', 'completed')" + }, + "model_experiment_active_not_archived": { + "name": "model_experiment_active_not_archived", + "value": "\"model_experiment\".\"status\" <> 'active' OR \"model_experiment\".\"is_archived\" = false" + } + }, + "isRLSEnabled": false + }, + "public.model_experiment_request": { + "name": "model_experiment_request", + "schema": "", + "columns": { + "usage_id": { + "name": "usage_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "variant_version_id": { + "name": "variant_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "allocation_subject": { + "name": "allocation_subject", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_request_id": { + "name": "client_request_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "request_kind": { + "name": "request_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "request_body_sha256": { + "name": "request_body_sha256", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "was_truncated": { + "name": "was_truncated", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_model_experiment_request_variant_version_created_at": { + "name": "IDX_model_experiment_request_variant_version_created_at", + "columns": [ + { + "expression": "variant_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_model_experiment_request_client_request_id": { + "name": "IDX_model_experiment_request_client_request_id", + "columns": [ + { + "expression": "client_request_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"model_experiment_request\".\"client_request_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "model_experiment_request_usage_id_microdollar_usage_id_fk": { + "name": "model_experiment_request_usage_id_microdollar_usage_id_fk", + "tableFrom": "model_experiment_request", + "tableTo": "microdollar_usage", + "columnsFrom": [ + "usage_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "model_experiment_request_variant_version_id_model_experiment_variant_version_id_fk": { + "name": "model_experiment_request_variant_version_id_model_experiment_variant_version_id_fk", + "tableFrom": "model_experiment_request", + "tableTo": "model_experiment_variant_version", + "columnsFrom": [ + "variant_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "model_experiment_request_usage_id_created_at_pk": { + "name": "model_experiment_request_usage_id_created_at_pk", + "columns": [ + "usage_id", + "created_at" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "model_experiment_request_allocation_subject_valid": { + "name": "model_experiment_request_allocation_subject_valid", + "value": "\"model_experiment_request\".\"allocation_subject\" IN ('user', 'machine', 'ip')" + }, + "model_experiment_request_request_kind_valid": { + "name": "model_experiment_request_request_kind_valid", + "value": "\"model_experiment_request\".\"request_kind\" IN ('chat_completions', 'messages', 'responses')" + }, + "model_experiment_request_request_body_sha256_format": { + "name": "model_experiment_request_request_body_sha256_format", + "value": "\"model_experiment_request\".\"request_body_sha256\" ~ '^[0-9a-f]{64}$' OR \"model_experiment_request\".\"request_body_sha256\" IN ('__failed__', '__deleted__')" + } + }, + "isRLSEnabled": false + }, + "public.model_experiment_variant": { + "name": "model_experiment_variant", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "experiment_id": { + "name": "experiment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "weight": { + "name": "weight", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_model_experiment_variant_experiment_id": { + "name": "IDX_model_experiment_variant_experiment_id", + "columns": [ + { + "expression": "experiment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "model_experiment_variant_experiment_id_model_experiment_id_fk": { + "name": "model_experiment_variant_experiment_id_model_experiment_id_fk", + "tableFrom": "model_experiment_variant", + "tableTo": "model_experiment", + "columnsFrom": [ + "experiment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_model_experiment_variant_experiment_label": { + "name": "UQ_model_experiment_variant_experiment_label", + "nullsNotDistinct": false, + "columns": [ + "experiment_id", + "label" + ] + } + }, + "policies": {}, + "checkConstraints": { + "model_experiment_variant_weight_positive": { + "name": "model_experiment_variant_weight_positive", + "value": "\"model_experiment_variant\".\"weight\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.model_experiment_variant_version": { + "name": "model_experiment_variant_version", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "variant_id": { + "name": "variant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "upstream": { + "name": "upstream", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "encrypted_api_key": { + "name": "encrypted_api_key", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "effective_at": { + "name": "effective_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_model_experiment_variant_version_variant_effective": { + "name": "IDX_model_experiment_variant_version_variant_effective", + "columns": [ + { + "expression": "variant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "effective_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "model_experiment_variant_version_variant_id_model_experiment_variant_id_fk": { + "name": "model_experiment_variant_version_variant_id_model_experiment_variant_id_fk", + "tableFrom": "model_experiment_variant_version", + "tableTo": "model_experiment_variant", + "columnsFrom": [ + "variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "model_experiment_variant_version_created_by_kilocode_users_id_fk": { + "name": "model_experiment_variant_version_created_by_kilocode_users_id_fk", + "tableFrom": "model_experiment_variant_version", + "tableTo": "kilocode_users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.models_by_provider": { + "name": "models_by_provider", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "openrouter": { + "name": "openrouter", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "vercel": { + "name": "vercel", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_audit_logs": { + "name": "organization_audit_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_email": { + "name": "actor_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_name": { + "name": "actor_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_organization_audit_logs_organization_id": { + "name": "IDX_organization_audit_logs_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_audit_logs_action": { + "name": "IDX_organization_audit_logs_action", + "columns": [ + { + "expression": "action", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_audit_logs_actor_id": { + "name": "IDX_organization_audit_logs_actor_id", + "columns": [ + { + "expression": "actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_audit_logs_created_at": { + "name": "IDX_organization_audit_logs_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_invitations": { + "name": "organization_invitations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invited_by": { + "name": "invited_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "accepted_at": { + "name": "accepted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_organization_invitations_token": { + "name": "UQ_organization_invitations_token", + "columns": [ + { + "expression": "token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_invitations_org_id": { + "name": "IDX_organization_invitations_org_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_invitations_email": { + "name": "IDX_organization_invitations_email", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_invitations_expires_at": { + "name": "IDX_organization_invitations_expires_at", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_membership_removals": { + "name": "organization_membership_removals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "removed_at": { + "name": "removed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "removed_by": { + "name": "removed_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previous_role": { + "name": "previous_role", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "IDX_org_membership_removals_org_id": { + "name": "IDX_org_membership_removals_org_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_org_membership_removals_user_id": { + "name": "IDX_org_membership_removals_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_org_membership_removals_org_user": { + "name": "UQ_org_membership_removals_org_user", + "nullsNotDistinct": false, + "columns": [ + "organization_id", + "kilo_user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_memberships": { + "name": "organization_memberships", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "joined_at": { + "name": "joined_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "invited_by": { + "name": "invited_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_organization_memberships_org_id": { + "name": "IDX_organization_memberships_org_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_memberships_user_id": { + "name": "IDX_organization_memberships_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_organization_memberships_org_user": { + "name": "UQ_organization_memberships_org_user", + "nullsNotDistinct": false, + "columns": [ + "organization_id", + "kilo_user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_seats_purchases": { + "name": "organization_seats_purchases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "subscription_stripe_id": { + "name": "subscription_stripe_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "seat_count": { + "name": "seat_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "amount_usd": { + "name": "amount_usd", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "subscription_status": { + "name": "subscription_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "starts_at": { + "name": "starts_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "billing_cycle": { + "name": "billing_cycle", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'monthly'" + } + }, + "indexes": { + "IDX_organization_seats_org_id": { + "name": "IDX_organization_seats_org_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_seats_expires_at": { + "name": "IDX_organization_seats_expires_at", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_seats_created_at": { + "name": "IDX_organization_seats_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_seats_updated_at": { + "name": "IDX_organization_seats_updated_at", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_seats_starts_at": { + "name": "IDX_organization_seats_starts_at", + "columns": [ + { + "expression": "starts_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_organization_seats_idempotency_key": { + "name": "UQ_organization_seats_idempotency_key", + "nullsNotDistinct": false, + "columns": [ + "idempotency_key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_user_limits": { + "name": "organization_user_limits", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "limit_type": { + "name": "limit_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "microdollar_limit": { + "name": "microdollar_limit", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_organization_user_limits_org_id": { + "name": "IDX_organization_user_limits_org_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_user_limits_user_id": { + "name": "IDX_organization_user_limits_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_organization_user_limits_org_user": { + "name": "UQ_organization_user_limits_org_user", + "nullsNotDistinct": false, + "columns": [ + "organization_id", + "kilo_user_id", + "limit_type" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_user_usage": { + "name": "organization_user_usage", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "usage_date": { + "name": "usage_date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "limit_type": { + "name": "limit_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "microdollar_usage": { + "name": "microdollar_usage", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_organization_user_daily_usage_org_id": { + "name": "IDX_organization_user_daily_usage_org_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_user_daily_usage_user_id": { + "name": "IDX_organization_user_daily_usage_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_organization_user_daily_usage_org_user_date": { + "name": "UQ_organization_user_daily_usage_org_user_date", + "nullsNotDistinct": false, + "columns": [ + "organization_id", + "kilo_user_id", + "limit_type", + "usage_date" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organizations": { + "name": "organizations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "microdollars_used": { + "name": "microdollars_used", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "microdollars_balance": { + "name": "microdollars_balance", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "total_microdollars_acquired": { + "name": "total_microdollars_acquired", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "next_credit_expiration_at": { + "name": "next_credit_expiration_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "auto_top_up_enabled": { + "name": "auto_top_up_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "settings": { + "name": "settings", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "seat_count": { + "name": "seat_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "require_seats": { + "name": "require_seats", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_by_kilo_user_id": { + "name": "created_by_kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "sso_domain": { + "name": "sso_domain", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'teams'" + }, + "free_trial_end_at": { + "name": "free_trial_end_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "company_domain": { + "name": "company_domain", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "IDX_organizations_sso_domain": { + "name": "IDX_organizations_sso_domain", + "columns": [ + { + "expression": "sso_domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "organizations_name_not_empty_check": { + "name": "organizations_name_not_empty_check", + "value": "length(trim(\"organizations\".\"name\")) > 0" + } + }, + "isRLSEnabled": false + }, + "public.organization_modes": { + "name": "organization_modes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + } + }, + "indexes": { + "IDX_organization_modes_organization_id": { + "name": "IDX_organization_modes_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_organization_modes_org_id_slug": { + "name": "UQ_organization_modes_org_id_slug", + "nullsNotDistinct": false, + "columns": [ + "organization_id", + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payment_methods": { + "name": "payment_methods", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "stripe_fingerprint": { + "name": "stripe_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_id": { + "name": "stripe_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last4": { + "name": "last4", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "brand": { + "name": "brand", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_line1": { + "name": "address_line1", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_line2": { + "name": "address_line2", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_city": { + "name": "address_city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_state": { + "name": "address_state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_zip": { + "name": "address_zip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_country": { + "name": "address_country", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "three_d_secure_supported": { + "name": "three_d_secure_supported", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "funding": { + "name": "funding", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "regulated_status": { + "name": "regulated_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_line1_check_status": { + "name": "address_line1_check_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postal_code_check_status": { + "name": "postal_code_check_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "http_x_forwarded_for": { + "name": "http_x_forwarded_for", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ip_city": { + "name": "http_x_vercel_ip_city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ip_country": { + "name": "http_x_vercel_ip_country", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ip_latitude": { + "name": "http_x_vercel_ip_latitude", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ip_longitude": { + "name": "http_x_vercel_ip_longitude", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ja4_digest": { + "name": "http_x_vercel_ja4_digest", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "eligible_for_free_credits": { + "name": "eligible_for_free_credits", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "stripe_data": { + "name": "stripe_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "IDX_d7d7fb15569674aaadcfbc0428": { + "name": "IDX_d7d7fb15569674aaadcfbc0428", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_e1feb919d0ab8a36381d5d5138": { + "name": "IDX_e1feb919d0ab8a36381d5d5138", + "columns": [ + { + "expression": "stripe_fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_payment_methods_organization_id": { + "name": "IDX_payment_methods_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_29df1b0403df5792c96bbbfdbe6": { + "name": "UQ_29df1b0403df5792c96bbbfdbe6", + "nullsNotDistinct": false, + "columns": [ + "user_id", + "stripe_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pending_impact_sale_reversals": { + "name": "pending_impact_sale_reversals", + "schema": "", + "columns": { + "stripe_charge_id": { + "name": "stripe_charge_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "dispute_id": { + "name": "dispute_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_date": { + "name": "event_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_attempt_at": { + "name": "last_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "pending_impact_sale_reversals_attempt_count_non_negative_check": { + "name": "pending_impact_sale_reversals_attempt_count_non_negative_check", + "value": "\"pending_impact_sale_reversals\".\"attempt_count\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.platform_integrations": { + "name": "platform_integrations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "integration_type": { + "name": "integration_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform_installation_id": { + "name": "platform_installation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "platform_account_id": { + "name": "platform_account_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "platform_account_login": { + "name": "platform_account_login", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "permissions": { + "name": "permissions", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "repository_access": { + "name": "repository_access", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "repositories": { + "name": "repositories", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "repositories_synced_at": { + "name": "repositories_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "auth_invalid_at": { + "name": "auth_invalid_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "auth_invalid_reason": { + "name": "auth_invalid_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "kilo_requester_user_id": { + "name": "kilo_requester_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "platform_requester_account_id": { + "name": "platform_requester_account_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "integration_status": { + "name": "integration_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "suspended_at": { + "name": "suspended_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "suspended_by": { + "name": "suspended_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "github_app_type": { + "name": "github_app_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'standard'" + }, + "installed_at": { + "name": "installed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_platform_integrations_owned_by_org_platform_inst": { + "name": "UQ_platform_integrations_owned_by_org_platform_inst", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform_installation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"platform_integrations\".\"owned_by_organization_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_platform_integrations_owned_by_user_platform_inst": { + "name": "UQ_platform_integrations_owned_by_user_platform_inst", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform_installation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"platform_integrations\".\"owned_by_user_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_platform_integrations_slack_platform_inst": { + "name": "UQ_platform_integrations_slack_platform_inst", + "columns": [ + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform_installation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"platform_integrations\".\"platform\" = 'slack' AND \"platform_integrations\".\"platform_installation_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_platform_integrations_linear_platform_inst": { + "name": "UQ_platform_integrations_linear_platform_inst", + "columns": [ + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform_installation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"platform_integrations\".\"platform\" = 'linear' AND \"platform_integrations\".\"platform_installation_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_platform_integrations_owned_by_org_id": { + "name": "IDX_platform_integrations_owned_by_org_id", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_platform_integrations_owned_by_user_id": { + "name": "IDX_platform_integrations_owned_by_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_platform_integrations_platform_inst_id": { + "name": "IDX_platform_integrations_platform_inst_id", + "columns": [ + { + "expression": "platform_installation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_platform_integrations_platform": { + "name": "IDX_platform_integrations_platform", + "columns": [ + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_platform_integrations_owned_by_org_platform": { + "name": "IDX_platform_integrations_owned_by_org_platform", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_platform_integrations_owned_by_user_platform": { + "name": "IDX_platform_integrations_owned_by_user_platform", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_platform_integrations_integration_status": { + "name": "IDX_platform_integrations_integration_status", + "columns": [ + { + "expression": "integration_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_platform_integrations_kilo_requester": { + "name": "IDX_platform_integrations_kilo_requester", + "columns": [ + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kilo_requester_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "integration_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_platform_integrations_platform_requester": { + "name": "IDX_platform_integrations_platform_requester", + "columns": [ + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform_requester_account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "integration_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "platform_integrations_owned_by_organization_id_organizations_id_fk": { + "name": "platform_integrations_owned_by_organization_id_organizations_id_fk", + "tableFrom": "platform_integrations", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "platform_integrations_owned_by_user_id_kilocode_users_id_fk": { + "name": "platform_integrations_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "platform_integrations", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "platform_integrations_owner_check": { + "name": "platform_integrations_owner_check", + "value": "(\n (\"platform_integrations\".\"owned_by_user_id\" IS NOT NULL AND \"platform_integrations\".\"owned_by_organization_id\" IS NULL) OR\n (\"platform_integrations\".\"owned_by_user_id\" IS NULL AND \"platform_integrations\".\"owned_by_organization_id\" IS NOT NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.referral_code_usages": { + "name": "referral_code_usages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "referring_kilo_user_id": { + "name": "referring_kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "redeeming_kilo_user_id": { + "name": "redeeming_kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount_usd": { + "name": "amount_usd", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "paid_at": { + "name": "paid_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_referral_code_usages_redeeming_kilo_user_id": { + "name": "IDX_referral_code_usages_redeeming_kilo_user_id", + "columns": [ + { + "expression": "redeeming_kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_referral_code_usages_redeeming_user_id_code": { + "name": "UQ_referral_code_usages_redeeming_user_id_code", + "nullsNotDistinct": false, + "columns": [ + "redeeming_kilo_user_id", + "referring_kilo_user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.referral_codes": { + "name": "referral_codes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "max_redemptions": { + "name": "max_redemptions", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 10 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_referral_codes_kilo_user_id": { + "name": "UQ_referral_codes_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_referral_codes_code": { + "name": "IDX_referral_codes_code", + "columns": [ + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security_advisor_check_catalog": { + "name": "security_advisor_check_catalog", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "check_id": { + "name": "check_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "severity": { + "name": "severity", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "explanation": { + "name": "explanation", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "risk": { + "name": "risk", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_advisor_check_catalog_check_id_unique": { + "name": "security_advisor_check_catalog_check_id_unique", + "nullsNotDistinct": false, + "columns": [ + "check_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "security_advisor_check_catalog_severity_check": { + "name": "security_advisor_check_catalog_severity_check", + "value": "\"security_advisor_check_catalog\".\"severity\" in ('critical', 'warn', 'info')" + } + }, + "isRLSEnabled": false + }, + "public.security_advisor_content": { + "name": "security_advisor_content", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_advisor_content_key_unique": { + "name": "security_advisor_content_key_unique", + "nullsNotDistinct": false, + "columns": [ + "key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security_advisor_kiloclaw_coverage": { + "name": "security_advisor_kiloclaw_coverage", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "area": { + "name": "area", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "detail": { + "name": "detail", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "match_check_ids": { + "name": "match_check_ids", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_advisor_kiloclaw_coverage_area_unique": { + "name": "security_advisor_kiloclaw_coverage_area_unique", + "nullsNotDistinct": false, + "columns": [ + "area" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security_advisor_scans": { + "name": "security_advisor_scans", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_platform": { + "name": "source_platform", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_method": { + "name": "source_method", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plugin_version": { + "name": "plugin_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "openclaw_version": { + "name": "openclaw_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "public_ip": { + "name": "public_ip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "findings_critical": { + "name": "findings_critical", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "findings_warn": { + "name": "findings_warn", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "findings_info": { + "name": "findings_info", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_security_advisor_scans_user_created_at": { + "name": "idx_security_advisor_scans_user_created_at", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_advisor_scans_created_at": { + "name": "idx_security_advisor_scans_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_advisor_scans_platform": { + "name": "idx_security_advisor_scans_platform", + "columns": [ + { + "expression": "source_platform", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security_agent_commands": { + "name": "security_agent_commands", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "command_type": { + "name": "command_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "origin": { + "name": "origin", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "finding_id": { + "name": "finding_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "repo_full_name": { + "name": "repo_full_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'accepted'" + }, + "result_code": { + "name": "result_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "result_metadata": { + "name": "result_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "last_error_redacted": { + "name": "last_error_redacted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accepted_at": { + "name": "accepted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_security_agent_commands_org_created": { + "name": "idx_security_agent_commands_org_created", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_agent_commands_user_created": { + "name": "idx_security_agent_commands_user_created", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_agent_commands_status_updated": { + "name": "idx_security_agent_commands_status_updated", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_agent_commands_finding_created": { + "name": "idx_security_agent_commands_finding_created", + "columns": [ + { + "expression": "finding_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "security_agent_commands_owned_by_organization_id_organizations_id_fk": { + "name": "security_agent_commands_owned_by_organization_id_organizations_id_fk", + "tableFrom": "security_agent_commands", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "security_agent_commands_owned_by_user_id_kilocode_users_id_fk": { + "name": "security_agent_commands_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "security_agent_commands", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "security_agent_commands_finding_id_security_findings_id_fk": { + "name": "security_agent_commands_finding_id_security_findings_id_fk", + "tableFrom": "security_agent_commands", + "tableTo": "security_findings", + "columnsFrom": [ + "finding_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "security_agent_commands_owner_check": { + "name": "security_agent_commands_owner_check", + "value": "(\n (\"security_agent_commands\".\"owned_by_user_id\" IS NOT NULL AND \"security_agent_commands\".\"owned_by_organization_id\" IS NULL) OR\n (\"security_agent_commands\".\"owned_by_user_id\" IS NULL AND \"security_agent_commands\".\"owned_by_organization_id\" IS NOT NULL)\n )" + }, + "security_agent_commands_type_check": { + "name": "security_agent_commands_type_check", + "value": "\"security_agent_commands\".\"command_type\" IN ('sync', 'dismiss_finding', 'start_analysis', 'apply_auto_remediation')" + }, + "security_agent_commands_origin_check": { + "name": "security_agent_commands_origin_check", + "value": "\"security_agent_commands\".\"origin\" IN ('manual', 'dashboard_refresh', 'enable_initial_sync', 'settings_include_existing')" + }, + "security_agent_commands_status_check": { + "name": "security_agent_commands_status_check", + "value": "\"security_agent_commands\".\"status\" IN ('accepted', 'running', 'succeeded', 'failed', 'no_op')" + } + }, + "isRLSEnabled": false + }, + "public.security_agent_repository_sync_state": { + "name": "security_agent_repository_sync_state", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "repo_full_name": { + "name": "repo_full_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_attempted_at": { + "name": "last_attempted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_succeeded_at": { + "name": "last_succeeded_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_failure_code": { + "name": "last_failure_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_security_agent_repository_sync_state_org_repo": { + "name": "UQ_security_agent_repository_sync_state_org_repo", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"security_agent_repository_sync_state\".\"owned_by_organization_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_security_agent_repository_sync_state_user_repo": { + "name": "UQ_security_agent_repository_sync_state_user_repo", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"security_agent_repository_sync_state\".\"owned_by_user_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "security_agent_repository_sync_state_owned_by_organization_id_organizations_id_fk": { + "name": "security_agent_repository_sync_state_owned_by_organization_id_organizations_id_fk", + "tableFrom": "security_agent_repository_sync_state", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "security_agent_repository_sync_state_owned_by_user_id_kilocode_users_id_fk": { + "name": "security_agent_repository_sync_state_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "security_agent_repository_sync_state", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "security_agent_repository_sync_state_owner_check": { + "name": "security_agent_repository_sync_state_owner_check", + "value": "(\n (\"security_agent_repository_sync_state\".\"owned_by_user_id\" IS NOT NULL AND \"security_agent_repository_sync_state\".\"owned_by_organization_id\" IS NULL) OR\n (\"security_agent_repository_sync_state\".\"owned_by_user_id\" IS NULL AND \"security_agent_repository_sync_state\".\"owned_by_organization_id\" IS NOT NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.security_analysis_owner_state": { + "name": "security_analysis_owner_state", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "auto_analysis_enabled_at": { + "name": "auto_analysis_enabled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "blocked_until": { + "name": "blocked_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "block_reason": { + "name": "block_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "consecutive_actor_resolution_failures": { + "name": "consecutive_actor_resolution_failures", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_actor_resolution_failure_at": { + "name": "last_actor_resolution_failure_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_security_analysis_owner_state_org_owner": { + "name": "UQ_security_analysis_owner_state_org_owner", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"security_analysis_owner_state\".\"owned_by_organization_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_security_analysis_owner_state_user_owner": { + "name": "UQ_security_analysis_owner_state_user_owner", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"security_analysis_owner_state\".\"owned_by_user_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "security_analysis_owner_state_owned_by_organization_id_organizations_id_fk": { + "name": "security_analysis_owner_state_owned_by_organization_id_organizations_id_fk", + "tableFrom": "security_analysis_owner_state", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "security_analysis_owner_state_owned_by_user_id_kilocode_users_id_fk": { + "name": "security_analysis_owner_state_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "security_analysis_owner_state", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "security_analysis_owner_state_owner_check": { + "name": "security_analysis_owner_state_owner_check", + "value": "(\n (\"security_analysis_owner_state\".\"owned_by_user_id\" IS NOT NULL AND \"security_analysis_owner_state\".\"owned_by_organization_id\" IS NULL) OR\n (\"security_analysis_owner_state\".\"owned_by_user_id\" IS NULL AND \"security_analysis_owner_state\".\"owned_by_organization_id\" IS NOT NULL)\n )" + }, + "security_analysis_owner_state_block_reason_check": { + "name": "security_analysis_owner_state_block_reason_check", + "value": "\"security_analysis_owner_state\".\"block_reason\" IS NULL OR \"security_analysis_owner_state\".\"block_reason\" IN ('INSUFFICIENT_CREDITS', 'ACTOR_RESOLUTION_FAILED', 'OPERATOR_PAUSE')" + } + }, + "isRLSEnabled": false + }, + "public.security_analysis_queue": { + "name": "security_analysis_queue", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "finding_id": { + "name": "finding_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "queue_status": { + "name": "queue_status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "severity_rank": { + "name": "severity_rank", + "type": "smallint", + "primaryKey": false, + "notNull": true + }, + "queued_at": { + "name": "queued_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "claimed_by_job_id": { + "name": "claimed_by_job_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "claim_token": { + "name": "claim_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "reopen_requeue_count": { + "name": "reopen_requeue_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_retry_at": { + "name": "next_retry_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "failure_code": { + "name": "failure_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_error_redacted": { + "name": "last_error_redacted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_security_analysis_queue_finding_id": { + "name": "UQ_security_analysis_queue_finding_id", + "columns": [ + { + "expression": "finding_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_analysis_queue_claim_path_org": { + "name": "idx_security_analysis_queue_claim_path_org", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"next_retry_at\", '-infinity'::timestamptz)", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "severity_rank", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "queued_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"security_analysis_queue\".\"queue_status\" = 'queued'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_analysis_queue_claim_path_user": { + "name": "idx_security_analysis_queue_claim_path_user", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"next_retry_at\", '-infinity'::timestamptz)", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "severity_rank", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "queued_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"security_analysis_queue\".\"queue_status\" = 'queued'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_analysis_queue_in_flight_org": { + "name": "idx_security_analysis_queue_in_flight_org", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "queue_status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "claimed_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"security_analysis_queue\".\"queue_status\" IN ('pending', 'running')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_analysis_queue_in_flight_user": { + "name": "idx_security_analysis_queue_in_flight_user", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "queue_status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "claimed_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"security_analysis_queue\".\"queue_status\" IN ('pending', 'running')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_analysis_queue_lag_dashboards": { + "name": "idx_security_analysis_queue_lag_dashboards", + "columns": [ + { + "expression": "queued_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"security_analysis_queue\".\"queue_status\" = 'queued'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_analysis_queue_pending_reconciliation": { + "name": "idx_security_analysis_queue_pending_reconciliation", + "columns": [ + { + "expression": "claimed_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"security_analysis_queue\".\"queue_status\" = 'pending'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_analysis_queue_running_reconciliation": { + "name": "idx_security_analysis_queue_running_reconciliation", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"security_analysis_queue\".\"queue_status\" = 'running'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_analysis_queue_failure_trend": { + "name": "idx_security_analysis_queue_failure_trend", + "columns": [ + { + "expression": "failure_code", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"security_analysis_queue\".\"failure_code\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "security_analysis_queue_finding_id_security_findings_id_fk": { + "name": "security_analysis_queue_finding_id_security_findings_id_fk", + "tableFrom": "security_analysis_queue", + "tableTo": "security_findings", + "columnsFrom": [ + "finding_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "security_analysis_queue_owned_by_organization_id_organizations_id_fk": { + "name": "security_analysis_queue_owned_by_organization_id_organizations_id_fk", + "tableFrom": "security_analysis_queue", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "security_analysis_queue_owned_by_user_id_kilocode_users_id_fk": { + "name": "security_analysis_queue_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "security_analysis_queue", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "security_analysis_queue_owner_check": { + "name": "security_analysis_queue_owner_check", + "value": "(\n (\"security_analysis_queue\".\"owned_by_user_id\" IS NOT NULL AND \"security_analysis_queue\".\"owned_by_organization_id\" IS NULL) OR\n (\"security_analysis_queue\".\"owned_by_user_id\" IS NULL AND \"security_analysis_queue\".\"owned_by_organization_id\" IS NOT NULL)\n )" + }, + "security_analysis_queue_status_check": { + "name": "security_analysis_queue_status_check", + "value": "\"security_analysis_queue\".\"queue_status\" IN ('queued', 'pending', 'running', 'failed', 'completed')" + }, + "security_analysis_queue_claim_token_required_check": { + "name": "security_analysis_queue_claim_token_required_check", + "value": "\"security_analysis_queue\".\"queue_status\" NOT IN ('pending', 'running') OR \"security_analysis_queue\".\"claim_token\" IS NOT NULL" + }, + "security_analysis_queue_attempt_count_non_negative_check": { + "name": "security_analysis_queue_attempt_count_non_negative_check", + "value": "\"security_analysis_queue\".\"attempt_count\" >= 0" + }, + "security_analysis_queue_reopen_requeue_count_non_negative_check": { + "name": "security_analysis_queue_reopen_requeue_count_non_negative_check", + "value": "\"security_analysis_queue\".\"reopen_requeue_count\" >= 0" + }, + "security_analysis_queue_severity_rank_check": { + "name": "security_analysis_queue_severity_rank_check", + "value": "\"security_analysis_queue\".\"severity_rank\" IN (0, 1, 2, 3)" + }, + "security_analysis_queue_failure_code_check": { + "name": "security_analysis_queue_failure_code_check", + "value": "\"security_analysis_queue\".\"failure_code\" IS NULL OR \"security_analysis_queue\".\"failure_code\" IN (\n 'NETWORK_TIMEOUT',\n 'UPSTREAM_5XX',\n 'TEMP_TOKEN_FAILURE',\n 'START_CALL_AMBIGUOUS',\n 'REQUEUE_TEMPORARY_PRECONDITION',\n 'ACTOR_RESOLUTION_FAILED',\n 'GITHUB_TOKEN_UNAVAILABLE',\n 'INVALID_CONFIG',\n 'MISSING_OWNERSHIP',\n 'PERMISSION_DENIED_PERMANENT',\n 'UNSUPPORTED_SEVERITY',\n 'INSUFFICIENT_CREDITS',\n 'STATE_GUARD_REJECTED',\n 'SKIPPED_ALREADY_IN_PROGRESS',\n 'SKIPPED_NO_LONGER_ELIGIBLE',\n 'REOPEN_LOOP_GUARD',\n 'RUN_LOST'\n )" + } + }, + "isRLSEnabled": false + }, + "public.security_audit_log": { + "name": "security_audit_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_email": { + "name": "actor_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_name": { + "name": "actor_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_type": { + "name": "resource_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resource_id": { + "name": "resource_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "before_state": { + "name": "before_state", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "after_state": { + "name": "after_state", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_security_audit_log_org_created": { + "name": "IDX_security_audit_log_org_created", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_security_audit_log_user_created": { + "name": "IDX_security_audit_log_user_created", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_security_audit_log_resource": { + "name": "IDX_security_audit_log_resource", + "columns": [ + { + "expression": "resource_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_security_audit_log_actor": { + "name": "IDX_security_audit_log_actor", + "columns": [ + { + "expression": "actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_security_audit_log_action": { + "name": "IDX_security_audit_log_action", + "columns": [ + { + "expression": "action", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "security_audit_log_owned_by_organization_id_organizations_id_fk": { + "name": "security_audit_log_owned_by_organization_id_organizations_id_fk", + "tableFrom": "security_audit_log", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "security_audit_log_owned_by_user_id_kilocode_users_id_fk": { + "name": "security_audit_log_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "security_audit_log", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "security_audit_log_owner_check": { + "name": "security_audit_log_owner_check", + "value": "(\"security_audit_log\".\"owned_by_user_id\" IS NOT NULL AND \"security_audit_log\".\"owned_by_organization_id\" IS NULL) OR (\"security_audit_log\".\"owned_by_user_id\" IS NULL AND \"security_audit_log\".\"owned_by_organization_id\" IS NOT NULL)" + }, + "security_audit_log_action_check": { + "name": "security_audit_log_action_check", + "value": "\"security_audit_log\".\"action\" IN ('security.finding.created', 'security.finding.status_change', 'security.finding.dismissed', 'security.finding.auto_dismissed', 'security.finding.analysis_started', 'security.finding.analysis_completed', 'security.remediation.queued', 'security.remediation.started', 'security.remediation.pr_opened', 'security.remediation.failed', 'security.remediation.blocked', 'security.remediation.no_changes_needed', 'security.remediation.cancelled', 'security.remediation.retried', 'security.finding.deleted', 'security.config.enabled', 'security.config.disabled', 'security.config.updated', 'security.sync.triggered', 'security.sync.completed', 'security.audit_log.exported')" + } + }, + "isRLSEnabled": false + }, + "public.security_findings": { + "name": "security_findings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "platform_integration_id": { + "name": "platform_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "repo_full_name": { + "name": "repo_full_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_id": { + "name": "source_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "severity": { + "name": "severity", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ghsa_id": { + "name": "ghsa_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cve_id": { + "name": "cve_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "package_name": { + "name": "package_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "package_ecosystem": { + "name": "package_ecosystem", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "vulnerable_version_range": { + "name": "vulnerable_version_range", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "patched_version": { + "name": "patched_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "manifest_path": { + "name": "manifest_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "ignored_reason": { + "name": "ignored_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ignored_by": { + "name": "ignored_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fixed_at": { + "name": "fixed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "sla_due_at": { + "name": "sla_due_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "dependabot_html_url": { + "name": "dependabot_html_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cwe_ids": { + "name": "cwe_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "cvss_score": { + "name": "cvss_score", + "type": "numeric(3, 1)", + "primaryKey": false, + "notNull": false + }, + "dependency_scope": { + "name": "dependency_scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cli_session_id": { + "name": "cli_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "analysis_status": { + "name": "analysis_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "analysis_started_at": { + "name": "analysis_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "analysis_completed_at": { + "name": "analysis_completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "analysis_error": { + "name": "analysis_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "analysis": { + "name": "analysis", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "raw_data": { + "name": "raw_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "first_detected_at": { + "name": "first_detected_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_security_findings_org_id": { + "name": "idx_security_findings_org_id", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_findings_user_id": { + "name": "idx_security_findings_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_findings_repo": { + "name": "idx_security_findings_repo", + "columns": [ + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_findings_severity": { + "name": "idx_security_findings_severity", + "columns": [ + { + "expression": "severity", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_findings_status": { + "name": "idx_security_findings_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_findings_package": { + "name": "idx_security_findings_package", + "columns": [ + { + "expression": "package_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_findings_sla_due_at": { + "name": "idx_security_findings_sla_due_at", + "columns": [ + { + "expression": "sla_due_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_findings_session_id": { + "name": "idx_security_findings_session_id", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_findings_cli_session_id": { + "name": "idx_security_findings_cli_session_id", + "columns": [ + { + "expression": "cli_session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_findings_analysis_status": { + "name": "idx_security_findings_analysis_status", + "columns": [ + { + "expression": "analysis_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_findings_org_analysis_in_flight": { + "name": "idx_security_findings_org_analysis_in_flight", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "analysis_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"security_findings\".\"analysis_status\" IN ('pending', 'running')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_findings_user_analysis_in_flight": { + "name": "idx_security_findings_user_analysis_in_flight", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "analysis_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"security_findings\".\"analysis_status\" IN ('pending', 'running')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "security_findings_owned_by_organization_id_organizations_id_fk": { + "name": "security_findings_owned_by_organization_id_organizations_id_fk", + "tableFrom": "security_findings", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "security_findings_owned_by_user_id_kilocode_users_id_fk": { + "name": "security_findings_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "security_findings", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "security_findings_platform_integration_id_platform_integrations_id_fk": { + "name": "security_findings_platform_integration_id_platform_integrations_id_fk", + "tableFrom": "security_findings", + "tableTo": "platform_integrations", + "columnsFrom": [ + "platform_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "uq_security_findings_source": { + "name": "uq_security_findings_source", + "nullsNotDistinct": false, + "columns": [ + "repo_full_name", + "source", + "source_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "security_findings_owner_check": { + "name": "security_findings_owner_check", + "value": "(\n (\"security_findings\".\"owned_by_user_id\" IS NOT NULL AND \"security_findings\".\"owned_by_organization_id\" IS NULL) OR\n (\"security_findings\".\"owned_by_user_id\" IS NULL AND \"security_findings\".\"owned_by_organization_id\" IS NOT NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.security_remediation_attempts": { + "name": "security_remediation_attempts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "remediation_id": { + "name": "remediation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "finding_id": { + "name": "finding_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "repo_full_name": { + "name": "repo_full_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "origin": { + "name": "origin", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "attempt_number": { + "name": "attempt_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "retry_of_attempt_id": { + "name": "retry_of_attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "requested_by_user_id": { + "name": "requested_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "analysis_fingerprint": { + "name": "analysis_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "analysis_completed_at": { + "name": "analysis_completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "remediation_model_slug": { + "name": "remediation_model_slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "branch_name": { + "name": "branch_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cloud_agent_session_id": { + "name": "cloud_agent_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "kilo_session_id": { + "name": "kilo_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "priority": { + "name": "priority", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 50 + }, + "claim_token": { + "name": "claim_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "claimed_by_job_id": { + "name": "claimed_by_job_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "launch_attempt_count": { + "name": "launch_attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_retry_at": { + "name": "next_retry_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "callback_attempt_token_hash": { + "name": "callback_attempt_token_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "failure_code": { + "name": "failure_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "blocked_reason": { + "name": "blocked_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_error_redacted": { + "name": "last_error_redacted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "structured_result": { + "name": "structured_result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "final_assistant_message": { + "name": "final_assistant_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "validation_evidence": { + "name": "validation_evidence", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "risk_notes": { + "name": "risk_notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "draft_reason": { + "name": "draft_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pr_url": { + "name": "pr_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pr_number": { + "name": "pr_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "pr_draft": { + "name": "pr_draft", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "pr_head_branch": { + "name": "pr_head_branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pr_base_branch": { + "name": "pr_base_branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cancellation_requested_at": { + "name": "cancellation_requested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "cancellation_requested_by_user_id": { + "name": "cancellation_requested_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "queued_at": { + "name": "queued_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "launched_at": { + "name": "launched_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_security_remediation_attempts_number": { + "name": "UQ_security_remediation_attempts_number", + "columns": [ + { + "expression": "remediation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "attempt_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_security_remediation_attempts_active_finding": { + "name": "UQ_security_remediation_attempts_active_finding", + "columns": [ + { + "expression": "finding_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"security_remediation_attempts\".\"status\" IN ('queued', 'launching', 'running')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_security_remediation_attempts_active_remediation": { + "name": "UQ_security_remediation_attempts_active_remediation", + "columns": [ + { + "expression": "remediation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"security_remediation_attempts\".\"status\" IN ('queued', 'launching', 'running')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_security_remediation_attempts_finding_fingerprint_terminal": { + "name": "UQ_security_remediation_attempts_finding_fingerprint_terminal", + "columns": [ + { + "expression": "finding_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "analysis_fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"security_remediation_attempts\".\"status\" IN ('queued', 'launching', 'running', 'pr_opened')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_remediation_attempts_org_claim": { + "name": "idx_security_remediation_attempts_org_claim", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"next_retry_at\", '-infinity'::timestamptz)", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "priority", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "queued_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"security_remediation_attempts\".\"status\" = 'queued'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_remediation_attempts_user_claim": { + "name": "idx_security_remediation_attempts_user_claim", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"next_retry_at\", '-infinity'::timestamptz)", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "priority", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "queued_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"security_remediation_attempts\".\"status\" = 'queued'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_remediation_attempts_repo_claim": { + "name": "idx_security_remediation_attempts_repo_claim", + "columns": [ + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"next_retry_at\", '-infinity'::timestamptz)", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "priority", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "queued_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"security_remediation_attempts\".\"status\" = 'queued'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_remediation_attempts_org_inflight": { + "name": "idx_security_remediation_attempts_org_inflight", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "claimed_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"security_remediation_attempts\".\"status\" IN ('launching', 'running')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_remediation_attempts_user_inflight": { + "name": "idx_security_remediation_attempts_user_inflight", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "claimed_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"security_remediation_attempts\".\"status\" IN ('launching', 'running')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_remediation_attempts_repo_inflight": { + "name": "idx_security_remediation_attempts_repo_inflight", + "columns": [ + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "claimed_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"security_remediation_attempts\".\"status\" IN ('launching', 'running')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_remediation_attempts_cloud_agent_session": { + "name": "idx_security_remediation_attempts_cloud_agent_session", + "columns": [ + { + "expression": "cloud_agent_session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_remediation_attempts_finding_fingerprint": { + "name": "idx_security_remediation_attempts_finding_fingerprint", + "columns": [ + { + "expression": "finding_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "analysis_fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "security_remediation_attempts_remediation_id_security_remediations_id_fk": { + "name": "security_remediation_attempts_remediation_id_security_remediations_id_fk", + "tableFrom": "security_remediation_attempts", + "tableTo": "security_remediations", + "columnsFrom": [ + "remediation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "security_remediation_attempts_finding_id_security_findings_id_fk": { + "name": "security_remediation_attempts_finding_id_security_findings_id_fk", + "tableFrom": "security_remediation_attempts", + "tableTo": "security_findings", + "columnsFrom": [ + "finding_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "security_remediation_attempts_owned_by_organization_id_organizations_id_fk": { + "name": "security_remediation_attempts_owned_by_organization_id_organizations_id_fk", + "tableFrom": "security_remediation_attempts", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "security_remediation_attempts_owned_by_user_id_kilocode_users_id_fk": { + "name": "security_remediation_attempts_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "security_remediation_attempts", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "security_remediation_attempts_requested_by_user_id_kilocode_users_id_fk": { + "name": "security_remediation_attempts_requested_by_user_id_kilocode_users_id_fk", + "tableFrom": "security_remediation_attempts", + "tableTo": "kilocode_users", + "columnsFrom": [ + "requested_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "security_remediation_attempts_cancellation_requested_by_user_id_kilocode_users_id_fk": { + "name": "security_remediation_attempts_cancellation_requested_by_user_id_kilocode_users_id_fk", + "tableFrom": "security_remediation_attempts", + "tableTo": "kilocode_users", + "columnsFrom": [ + "cancellation_requested_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "security_remediation_attempts_owner_check": { + "name": "security_remediation_attempts_owner_check", + "value": "(\n (\"security_remediation_attempts\".\"owned_by_user_id\" IS NOT NULL AND \"security_remediation_attempts\".\"owned_by_organization_id\" IS NULL) OR\n (\"security_remediation_attempts\".\"owned_by_user_id\" IS NULL AND \"security_remediation_attempts\".\"owned_by_organization_id\" IS NOT NULL)\n )" + }, + "security_remediation_attempts_status_check": { + "name": "security_remediation_attempts_status_check", + "value": "\"security_remediation_attempts\".\"status\" IN ('queued', 'launching', 'running', 'pr_opened', 'failed', 'blocked', 'no_changes_needed', 'cancelled')" + }, + "security_remediation_attempts_origin_check": { + "name": "security_remediation_attempts_origin_check", + "value": "\"security_remediation_attempts\".\"origin\" IN ('auto_policy', 'bulk_existing', 'manual')" + }, + "security_remediation_attempts_attempt_number_check": { + "name": "security_remediation_attempts_attempt_number_check", + "value": "\"security_remediation_attempts\".\"attempt_number\" >= 1" + }, + "security_remediation_attempts_launch_attempt_count_check": { + "name": "security_remediation_attempts_launch_attempt_count_check", + "value": "\"security_remediation_attempts\".\"launch_attempt_count\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.security_remediations": { + "name": "security_remediations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "finding_id": { + "name": "finding_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "repo_full_name": { + "name": "repo_full_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "latest_attempt_id": { + "name": "latest_attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "latest_analysis_fingerprint": { + "name": "latest_analysis_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "latest_analysis_completed_at": { + "name": "latest_analysis_completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "pr_url": { + "name": "pr_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pr_number": { + "name": "pr_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "pr_draft": { + "name": "pr_draft", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "pr_head_branch": { + "name": "pr_head_branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pr_base_branch": { + "name": "pr_base_branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "failure_code": { + "name": "failure_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "blocked_reason": { + "name": "blocked_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "outcome_summary": { + "name": "outcome_summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_security_remediations_finding_id": { + "name": "UQ_security_remediations_finding_id", + "columns": [ + { + "expression": "finding_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_remediations_org_status": { + "name": "idx_security_remediations_org_status", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_remediations_user_status": { + "name": "idx_security_remediations_user_status", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_remediations_repo_status": { + "name": "idx_security_remediations_repo_status", + "columns": [ + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_remediations_latest_attempt": { + "name": "idx_security_remediations_latest_attempt", + "columns": [ + { + "expression": "latest_attempt_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "security_remediations_owned_by_organization_id_organizations_id_fk": { + "name": "security_remediations_owned_by_organization_id_organizations_id_fk", + "tableFrom": "security_remediations", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "security_remediations_owned_by_user_id_kilocode_users_id_fk": { + "name": "security_remediations_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "security_remediations", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "security_remediations_finding_id_security_findings_id_fk": { + "name": "security_remediations_finding_id_security_findings_id_fk", + "tableFrom": "security_remediations", + "tableTo": "security_findings", + "columnsFrom": [ + "finding_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "security_remediations_owner_check": { + "name": "security_remediations_owner_check", + "value": "(\n (\"security_remediations\".\"owned_by_user_id\" IS NOT NULL AND \"security_remediations\".\"owned_by_organization_id\" IS NULL) OR\n (\"security_remediations\".\"owned_by_user_id\" IS NULL AND \"security_remediations\".\"owned_by_organization_id\" IS NOT NULL)\n )" + }, + "security_remediations_status_check": { + "name": "security_remediations_status_check", + "value": "\"security_remediations\".\"status\" IN ('queued', 'running', 'pr_opened', 'failed', 'blocked', 'no_changes_needed', 'cancelled')" + } + }, + "isRLSEnabled": false + }, + "public.shared_cli_sessions": { + "name": "shared_cli_sessions", + "schema": "", + "columns": { + "share_id": { + "name": "share_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "session_id": { + "name": "session_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "shared_state": { + "name": "shared_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'public'" + }, + "api_conversation_history_blob_url": { + "name": "api_conversation_history_blob_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "task_metadata_blob_url": { + "name": "task_metadata_blob_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ui_messages_blob_url": { + "name": "ui_messages_blob_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "git_state_blob_url": { + "name": "git_state_blob_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_shared_cli_sessions_session_id": { + "name": "IDX_shared_cli_sessions_session_id", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_shared_cli_sessions_created_at": { + "name": "IDX_shared_cli_sessions_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "shared_cli_sessions_session_id_cli_sessions_session_id_fk": { + "name": "shared_cli_sessions_session_id_cli_sessions_session_id_fk", + "tableFrom": "shared_cli_sessions", + "tableTo": "cli_sessions", + "columnsFrom": [ + "session_id" + ], + "columnsTo": [ + "session_id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "shared_cli_sessions_kilo_user_id_kilocode_users_id_fk": { + "name": "shared_cli_sessions_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "shared_cli_sessions", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "shared_cli_sessions_shared_state_check": { + "name": "shared_cli_sessions_shared_state_check", + "value": "\"shared_cli_sessions\".\"shared_state\" IN ('public', 'organization')" + } + }, + "isRLSEnabled": false + }, + "public.slack_bot_requests": { + "name": "slack_bot_requests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "platform_integration_id": { + "name": "platform_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "slack_team_id": { + "name": "slack_team_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slack_team_name": { + "name": "slack_team_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "slack_channel_id": { + "name": "slack_channel_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slack_user_id": { + "name": "slack_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slack_thread_ts": { + "name": "slack_thread_ts", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_message": { + "name": "user_message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_message_truncated": { + "name": "user_message_truncated", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "response_time_ms": { + "name": "response_time_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "model_used": { + "name": "model_used", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tool_calls_made": { + "name": "tool_calls_made", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "cloud_agent_session_id": { + "name": "cloud_agent_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_slack_bot_requests_created_at": { + "name": "idx_slack_bot_requests_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_slack_bot_requests_slack_team_id": { + "name": "idx_slack_bot_requests_slack_team_id", + "columns": [ + { + "expression": "slack_team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_slack_bot_requests_owned_by_org_id": { + "name": "idx_slack_bot_requests_owned_by_org_id", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_slack_bot_requests_owned_by_user_id": { + "name": "idx_slack_bot_requests_owned_by_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_slack_bot_requests_status": { + "name": "idx_slack_bot_requests_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_slack_bot_requests_event_type": { + "name": "idx_slack_bot_requests_event_type", + "columns": [ + { + "expression": "event_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_slack_bot_requests_team_created": { + "name": "idx_slack_bot_requests_team_created", + "columns": [ + { + "expression": "slack_team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "slack_bot_requests_owned_by_organization_id_organizations_id_fk": { + "name": "slack_bot_requests_owned_by_organization_id_organizations_id_fk", + "tableFrom": "slack_bot_requests", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "slack_bot_requests_owned_by_user_id_kilocode_users_id_fk": { + "name": "slack_bot_requests_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "slack_bot_requests", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "slack_bot_requests_platform_integration_id_platform_integrations_id_fk": { + "name": "slack_bot_requests_platform_integration_id_platform_integrations_id_fk", + "tableFrom": "slack_bot_requests", + "tableTo": "platform_integrations", + "columnsFrom": [ + "platform_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "slack_bot_requests_owner_check": { + "name": "slack_bot_requests_owner_check", + "value": "(\n (\"slack_bot_requests\".\"owned_by_user_id\" IS NOT NULL AND \"slack_bot_requests\".\"owned_by_organization_id\" IS NULL) OR\n (\"slack_bot_requests\".\"owned_by_user_id\" IS NULL AND \"slack_bot_requests\".\"owned_by_organization_id\" IS NOT NULL) OR\n (\"slack_bot_requests\".\"owned_by_user_id\" IS NULL AND \"slack_bot_requests\".\"owned_by_organization_id\" IS NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.source_embeddings": { + "name": "source_embeddings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": true + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_hash": { + "name": "file_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "start_line": { + "name": "start_line", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "end_line": { + "name": "end_line", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "git_branch": { + "name": "git_branch", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'main'" + }, + "is_base_branch": { + "name": "is_base_branch", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_source_embeddings_organization_id": { + "name": "IDX_source_embeddings_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_source_embeddings_kilo_user_id": { + "name": "IDX_source_embeddings_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_source_embeddings_project_id": { + "name": "IDX_source_embeddings_project_id", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_source_embeddings_created_at": { + "name": "IDX_source_embeddings_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_source_embeddings_updated_at": { + "name": "IDX_source_embeddings_updated_at", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_source_embeddings_file_path_lower": { + "name": "IDX_source_embeddings_file_path_lower", + "columns": [ + { + "expression": "LOWER(\"file_path\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_source_embeddings_git_branch": { + "name": "IDX_source_embeddings_git_branch", + "columns": [ + { + "expression": "git_branch", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_source_embeddings_org_project_branch": { + "name": "IDX_source_embeddings_org_project_branch", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "git_branch", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "source_embeddings_organization_id_organizations_id_fk": { + "name": "source_embeddings_organization_id_organizations_id_fk", + "tableFrom": "source_embeddings", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "source_embeddings_kilo_user_id_kilocode_users_id_fk": { + "name": "source_embeddings_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "source_embeddings", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_source_embeddings_org_project_branch_file_lines": { + "name": "UQ_source_embeddings_org_project_branch_file_lines", + "nullsNotDistinct": false, + "columns": [ + "organization_id", + "project_id", + "git_branch", + "file_path", + "start_line", + "end_line" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.stripe_dispute_actions": { + "name": "stripe_dispute_actions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "case_id": { + "name": "case_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "action_type": { + "name": "action_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_key": { + "name": "target_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_retry_at": { + "name": "next_retry_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_attempt_at": { + "name": "last_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "terminal_at": { + "name": "terminal_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "result_code": { + "name": "result_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "result_reference_id": { + "name": "result_reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "failure_context": { + "name": "failure_context", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_stripe_dispute_actions_case_id": { + "name": "IDX_stripe_dispute_actions_case_id", + "columns": [ + { + "expression": "case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_stripe_dispute_actions_claim_path": { + "name": "IDX_stripe_dispute_actions_claim_path", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"next_retry_at\", '-infinity'::timestamptz)", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "stripe_dispute_actions_case_id_stripe_dispute_cases_id_fk": { + "name": "stripe_dispute_actions_case_id_stripe_dispute_cases_id_fk", + "tableFrom": "stripe_dispute_actions", + "tableTo": "stripe_dispute_cases", + "columnsFrom": [ + "case_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_stripe_dispute_actions_case_type_target": { + "name": "UQ_stripe_dispute_actions_case_type_target", + "nullsNotDistinct": false, + "columns": [ + "case_id", + "action_type", + "target_key" + ] + } + }, + "policies": {}, + "checkConstraints": { + "stripe_dispute_actions_action_type_check": { + "name": "stripe_dispute_actions_action_type_check", + "value": "\"stripe_dispute_actions\".\"action_type\" IN ('stripe_acceptance', 'user_block', 'auto_top_up_disable', 'credit_balance_reset', 'subscription_cancellation', 'access_termination', 'kiloclaw_suspension')" + }, + "stripe_dispute_actions_status_check": { + "name": "stripe_dispute_actions_status_check", + "value": "\"stripe_dispute_actions\".\"status\" IN ('queued', 'processing', 'completed', 'failed', 'skipped')" + }, + "stripe_dispute_actions_attempt_count_non_negative_check": { + "name": "stripe_dispute_actions_attempt_count_non_negative_check", + "value": "\"stripe_dispute_actions\".\"attempt_count\" >= 0" + }, + "stripe_dispute_actions_target_key_not_empty_check": { + "name": "stripe_dispute_actions_target_key_not_empty_check", + "value": "length(\"stripe_dispute_actions\".\"target_key\") > 0" + } + }, + "isRLSEnabled": false + }, + "public.stripe_dispute_cases": { + "name": "stripe_dispute_cases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "stripe_dispute_id": { + "name": "stripe_dispute_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_event_id": { + "name": "stripe_event_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_event_created_at": { + "name": "stripe_event_created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "stripe_charge_id": { + "name": "stripe_charge_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_payment_intent_id": { + "name": "stripe_payment_intent_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "amount_minor_units": { + "name": "amount_minor_units", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dispute_reason": { + "name": "dispute_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_status": { + "name": "stripe_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_classification": { + "name": "owner_classification", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'needs_action'" + }, + "status_reason": { + "name": "status_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "failure_context": { + "name": "failure_context", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_created_at": { + "name": "stripe_created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "evidence_due_by": { + "name": "evidence_due_by", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "synced_at": { + "name": "synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "accepted_by_kilo_user_id": { + "name": "accepted_by_kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "acceptance_started_at": { + "name": "acceptance_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "next_retry_at": { + "name": "next_retry_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "accepted_at": { + "name": "accepted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "enforcement_completed_at": { + "name": "enforcement_completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "review_required_at": { + "name": "review_required_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "closed_at": { + "name": "closed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_stripe_dispute_cases_event_id": { + "name": "IDX_stripe_dispute_cases_event_id", + "columns": [ + { + "expression": "stripe_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_stripe_dispute_cases_charge_id": { + "name": "IDX_stripe_dispute_cases_charge_id", + "columns": [ + { + "expression": "stripe_charge_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_stripe_dispute_cases_payment_intent_id": { + "name": "IDX_stripe_dispute_cases_payment_intent_id", + "columns": [ + { + "expression": "stripe_payment_intent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_stripe_dispute_cases_customer_id": { + "name": "IDX_stripe_dispute_cases_customer_id", + "columns": [ + { + "expression": "stripe_customer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_stripe_dispute_cases_kilo_user_id": { + "name": "IDX_stripe_dispute_cases_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_stripe_dispute_cases_organization_id": { + "name": "IDX_stripe_dispute_cases_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_stripe_dispute_cases_status_due_by": { + "name": "IDX_stripe_dispute_cases_status_due_by", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "evidence_due_by", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "stripe_created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "stripe_dispute_cases_kilo_user_id_kilocode_users_id_fk": { + "name": "stripe_dispute_cases_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "stripe_dispute_cases", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "stripe_dispute_cases_organization_id_organizations_id_fk": { + "name": "stripe_dispute_cases_organization_id_organizations_id_fk", + "tableFrom": "stripe_dispute_cases", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "stripe_dispute_cases_accepted_by_kilo_user_id_kilocode_users_id_fk": { + "name": "stripe_dispute_cases_accepted_by_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "stripe_dispute_cases", + "tableTo": "kilocode_users", + "columnsFrom": [ + "accepted_by_kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_stripe_dispute_cases_dispute_id": { + "name": "UQ_stripe_dispute_cases_dispute_id", + "nullsNotDistinct": false, + "columns": [ + "stripe_dispute_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "stripe_dispute_cases_owner_classification_check": { + "name": "stripe_dispute_cases_owner_classification_check", + "value": "\"stripe_dispute_cases\".\"owner_classification\" IN ('personal', 'organization', 'ambiguous', 'unmatched')" + }, + "stripe_dispute_cases_status_check": { + "name": "stripe_dispute_cases_status_check", + "value": "\"stripe_dispute_cases\".\"status\" IN ('needs_action', 'processing', 'accepted', 'acceptance_failed', 'enforcement_failed', 'review_required', 'closed')" + }, + "stripe_dispute_cases_amount_minor_units_non_negative_check": { + "name": "stripe_dispute_cases_amount_minor_units_non_negative_check", + "value": "\"stripe_dispute_cases\".\"amount_minor_units\" IS NULL OR \"stripe_dispute_cases\".\"amount_minor_units\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.stripe_early_fraud_warning_actions": { + "name": "stripe_early_fraud_warning_actions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "case_id": { + "name": "case_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "action_type": { + "name": "action_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_key": { + "name": "target_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_retry_at": { + "name": "next_retry_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_attempt_at": { + "name": "last_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "terminal_at": { + "name": "terminal_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "result_code": { + "name": "result_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "result_reference_id": { + "name": "result_reference_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "failure_context": { + "name": "failure_context", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_stripe_early_fraud_warning_actions_case_id": { + "name": "IDX_stripe_early_fraud_warning_actions_case_id", + "columns": [ + { + "expression": "case_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_stripe_early_fraud_warning_actions_claim_path": { + "name": "IDX_stripe_early_fraud_warning_actions_claim_path", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"next_retry_at\", '-infinity'::timestamptz)", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "stripe_early_fraud_warning_actions_case_id_stripe_early_fraud_warning_cases_id_fk": { + "name": "stripe_early_fraud_warning_actions_case_id_stripe_early_fraud_warning_cases_id_fk", + "tableFrom": "stripe_early_fraud_warning_actions", + "tableTo": "stripe_early_fraud_warning_cases", + "columnsFrom": [ + "case_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_stripe_early_fraud_warning_actions_case_type_target": { + "name": "UQ_stripe_early_fraud_warning_actions_case_type_target", + "nullsNotDistinct": false, + "columns": [ + "case_id", + "action_type", + "target_key" + ] + } + }, + "policies": {}, + "checkConstraints": { + "stripe_early_fraud_warning_actions_action_type_check": { + "name": "stripe_early_fraud_warning_actions_action_type_check", + "value": "\"stripe_early_fraud_warning_actions\".\"action_type\" IN ('containment', 'refund', 'payment_value_clawback', 'subscription_termination', 'access_termination', 'kiloclaw_suspension', 'affiliate_payout_reversal', 'referral_reward_reversal', 'user_notice')" + }, + "stripe_early_fraud_warning_actions_status_check": { + "name": "stripe_early_fraud_warning_actions_status_check", + "value": "\"stripe_early_fraud_warning_actions\".\"status\" IN ('queued', 'processing', 'completed', 'failed', 'review_required', 'dismissed')" + }, + "stripe_early_fraud_warning_actions_attempt_count_non_negative_check": { + "name": "stripe_early_fraud_warning_actions_attempt_count_non_negative_check", + "value": "\"stripe_early_fraud_warning_actions\".\"attempt_count\" >= 0" + }, + "stripe_early_fraud_warning_actions_target_key_not_empty_check": { + "name": "stripe_early_fraud_warning_actions_target_key_not_empty_check", + "value": "length(\"stripe_early_fraud_warning_actions\".\"target_key\") > 0" + } + }, + "isRLSEnabled": false + }, + "public.stripe_early_fraud_warning_cases": { + "name": "stripe_early_fraud_warning_cases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "stripe_early_fraud_warning_id": { + "name": "stripe_early_fraud_warning_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_event_id": { + "name": "stripe_event_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_charge_id": { + "name": "stripe_charge_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_payment_intent_id": { + "name": "stripe_payment_intent_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "amount_minor_units": { + "name": "amount_minor_units", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_classification": { + "name": "owner_classification", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "failure_context": { + "name": "failure_context", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "warning_created_at": { + "name": "warning_created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "contained_at": { + "name": "contained_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "processing_started_at": { + "name": "processing_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "review_required_at": { + "name": "review_required_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "remediated_at": { + "name": "remediated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "dismissed_at": { + "name": "dismissed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_stripe_early_fraud_warning_cases_event_id": { + "name": "IDX_stripe_early_fraud_warning_cases_event_id", + "columns": [ + { + "expression": "stripe_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_stripe_early_fraud_warning_cases_charge_id": { + "name": "IDX_stripe_early_fraud_warning_cases_charge_id", + "columns": [ + { + "expression": "stripe_charge_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_stripe_early_fraud_warning_cases_payment_intent_id": { + "name": "IDX_stripe_early_fraud_warning_cases_payment_intent_id", + "columns": [ + { + "expression": "stripe_payment_intent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_stripe_early_fraud_warning_cases_customer_id": { + "name": "IDX_stripe_early_fraud_warning_cases_customer_id", + "columns": [ + { + "expression": "stripe_customer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_stripe_early_fraud_warning_cases_kilo_user_id": { + "name": "IDX_stripe_early_fraud_warning_cases_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_stripe_early_fraud_warning_cases_organization_id": { + "name": "IDX_stripe_early_fraud_warning_cases_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_stripe_early_fraud_warning_cases_status_created_at": { + "name": "IDX_stripe_early_fraud_warning_cases_status_created_at", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "stripe_early_fraud_warning_cases_kilo_user_id_kilocode_users_id_fk": { + "name": "stripe_early_fraud_warning_cases_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "stripe_early_fraud_warning_cases", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "stripe_early_fraud_warning_cases_organization_id_organizations_id_fk": { + "name": "stripe_early_fraud_warning_cases_organization_id_organizations_id_fk", + "tableFrom": "stripe_early_fraud_warning_cases", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_stripe_early_fraud_warning_cases_warning_id": { + "name": "UQ_stripe_early_fraud_warning_cases_warning_id", + "nullsNotDistinct": false, + "columns": [ + "stripe_early_fraud_warning_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "stripe_early_fraud_warning_cases_owner_classification_check": { + "name": "stripe_early_fraud_warning_cases_owner_classification_check", + "value": "\"stripe_early_fraud_warning_cases\".\"owner_classification\" IN ('personal', 'organization', 'ambiguous', 'unmatched')" + }, + "stripe_early_fraud_warning_cases_status_check": { + "name": "stripe_early_fraud_warning_cases_status_check", + "value": "\"stripe_early_fraud_warning_cases\".\"status\" IN ('queued', 'contained', 'processing', 'completed', 'review_required', 'failed', 'remediated', 'dismissed')" + }, + "stripe_early_fraud_warning_cases_amount_minor_units_non_negative_check": { + "name": "stripe_early_fraud_warning_cases_amount_minor_units_non_negative_check", + "value": "\"stripe_early_fraud_warning_cases\".\"amount_minor_units\" IS NULL OR \"stripe_early_fraud_warning_cases\".\"amount_minor_units\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.stytch_fingerprints": { + "name": "stytch_fingerprints", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "visitor_fingerprint": { + "name": "visitor_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "browser_fingerprint": { + "name": "browser_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "browser_id": { + "name": "browser_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "hardware_fingerprint": { + "name": "hardware_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "network_fingerprint": { + "name": "network_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "visitor_id": { + "name": "visitor_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "verdict_action": { + "name": "verdict_action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "detected_device_type": { + "name": "detected_device_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_authentic_device": { + "name": "is_authentic_device", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "reasons": { + "name": "reasons", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{\"\"}'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "status_code": { + "name": "status_code", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "fingerprint_data": { + "name": "fingerprint_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "kilo_free_tier_allowed": { + "name": "kilo_free_tier_allowed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "http_x_forwarded_for": { + "name": "http_x_forwarded_for", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ip_city": { + "name": "http_x_vercel_ip_city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ip_country": { + "name": "http_x_vercel_ip_country", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ip_latitude": { + "name": "http_x_vercel_ip_latitude", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ip_longitude": { + "name": "http_x_vercel_ip_longitude", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ja4_digest": { + "name": "http_x_vercel_ja4_digest", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "http_user_agent": { + "name": "http_user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_hardware_fingerprint": { + "name": "idx_hardware_fingerprint", + "columns": [ + { + "expression": "hardware_fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_kilo_user_id": { + "name": "idx_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_stytch_fingerprints_reasons_gin": { + "name": "idx_stytch_fingerprints_reasons_gin", + "columns": [ + { + "expression": "reasons", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "idx_verdict_action": { + "name": "idx_verdict_action", + "columns": [ + { + "expression": "verdict_action", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_visitor_fingerprint": { + "name": "idx_visitor_fingerprint", + "columns": [ + { + "expression": "visitor_fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.system_prompt_prefix": { + "name": "system_prompt_prefix", + "schema": "", + "columns": { + "system_prompt_prefix_id": { + "name": "system_prompt_prefix_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "system_prompt_prefix": { + "name": "system_prompt_prefix", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UQ_system_prompt_prefix": { + "name": "UQ_system_prompt_prefix", + "columns": [ + { + "expression": "system_prompt_prefix", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.transactional_email_log": { + "name": "transactional_email_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "email_type": { + "name": "email_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sent_at": { + "name": "sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_transactional_email_log_type_idempotency_key": { + "name": "UQ_transactional_email_log_type_idempotency_key", + "columns": [ + { + "expression": "email_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_transactional_email_log_user_id": { + "name": "IDX_transactional_email_log_user_id", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_transactional_email_log_organization_id": { + "name": "IDX_transactional_email_log_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "transactional_email_log_user_id_kilocode_users_id_fk": { + "name": "transactional_email_log_user_id_kilocode_users_id_fk", + "tableFrom": "transactional_email_log", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "transactional_email_log_organization_id_organizations_id_fk": { + "name": "transactional_email_log_organization_id_organizations_id_fk", + "tableFrom": "transactional_email_log", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "CHK_transactional_email_log_owner": { + "name": "CHK_transactional_email_log_owner", + "value": "\"transactional_email_log\".\"user_id\" IS NOT NULL OR \"transactional_email_log\".\"organization_id\" IS NOT NULL" + } + }, + "isRLSEnabled": false + }, + "public.user_admin_notes": { + "name": "user_admin_notes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "note_content": { + "name": "note_content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "admin_kilo_user_id": { + "name": "admin_kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_34517df0b385234babc38fe81b": { + "name": "IDX_34517df0b385234babc38fe81b", + "columns": [ + { + "expression": "admin_kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_ccbde98c4c14046daa5682ec4f": { + "name": "IDX_ccbde98c4c14046daa5682ec4f", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_d0270eb24ef6442d65a0b7853c": { + "name": "IDX_d0270eb24ef6442d65a0b7853c", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_affiliate_attributions": { + "name": "user_affiliate_attributions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tracking_id": { + "name": "tracking_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_user_affiliate_attributions_user_id": { + "name": "IDX_user_affiliate_attributions_user_id", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_affiliate_attributions_user_id_kilocode_users_id_fk": { + "name": "user_affiliate_attributions_user_id_kilocode_users_id_fk", + "tableFrom": "user_affiliate_attributions", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_user_affiliate_attributions_user_provider": { + "name": "UQ_user_affiliate_attributions_user_provider", + "nullsNotDistinct": false, + "columns": [ + "user_id", + "provider" + ] + } + }, + "policies": {}, + "checkConstraints": { + "user_affiliate_attributions_provider_check": { + "name": "user_affiliate_attributions_provider_check", + "value": "\"user_affiliate_attributions\".\"provider\" IN ('impact')" + } + }, + "isRLSEnabled": false + }, + "public.user_affiliate_events": { + "name": "user_affiliate_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dedupe_key": { + "name": "dedupe_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_event_id": { + "name": "parent_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "delivery_state": { + "name": "delivery_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "payload_json": { + "name": "payload_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "stripe_charge_id": { + "name": "stripe_charge_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "impact_action_id": { + "name": "impact_action_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "impact_submission_uri": { + "name": "impact_submission_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_retry_at": { + "name": "next_retry_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_user_affiliate_events_claim_path": { + "name": "IDX_user_affiliate_events_claim_path", + "columns": [ + { + "expression": "delivery_state", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"next_retry_at\", '-infinity'::timestamptz)", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_user_affiliate_events_parent_event_id": { + "name": "IDX_user_affiliate_events_parent_event_id", + "columns": [ + { + "expression": "parent_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_user_affiliate_events_provider_event_type_charge": { + "name": "IDX_user_affiliate_events_provider_event_type_charge", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "event_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "stripe_charge_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_affiliate_events_user_id_kilocode_users_id_fk": { + "name": "user_affiliate_events_user_id_kilocode_users_id_fk", + "tableFrom": "user_affiliate_events", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "user_affiliate_events_parent_event_id_fk": { + "name": "user_affiliate_events_parent_event_id_fk", + "tableFrom": "user_affiliate_events", + "tableTo": "user_affiliate_events", + "columnsFrom": [ + "parent_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_user_affiliate_events_dedupe_key": { + "name": "UQ_user_affiliate_events_dedupe_key", + "nullsNotDistinct": false, + "columns": [ + "dedupe_key" + ] + } + }, + "policies": {}, + "checkConstraints": { + "user_affiliate_events_provider_check": { + "name": "user_affiliate_events_provider_check", + "value": "\"user_affiliate_events\".\"provider\" IN ('impact')" + }, + "user_affiliate_events_event_type_check": { + "name": "user_affiliate_events_event_type_check", + "value": "\"user_affiliate_events\".\"event_type\" IN ('signup', 'trial_start', 'trial_end', 'sale', 'sale_reversal')" + }, + "user_affiliate_events_delivery_state_check": { + "name": "user_affiliate_events_delivery_state_check", + "value": "\"user_affiliate_events\".\"delivery_state\" IN ('queued', 'blocked', 'sending', 'delivered', 'failed')" + }, + "user_affiliate_events_attempt_count_non_negative_check": { + "name": "user_affiliate_events_attempt_count_non_negative_check", + "value": "\"user_affiliate_events\".\"attempt_count\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.user_auth_provider": { + "name": "user_auth_provider", + "schema": "", + "columns": { + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_account_id": { + "name": "provider_account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "hosted_domain": { + "name": "hosted_domain", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_user_auth_provider_kilo_user_id": { + "name": "IDX_user_auth_provider_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_user_auth_provider_hosted_domain": { + "name": "IDX_user_auth_provider_hosted_domain", + "columns": [ + { + "expression": "hosted_domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "user_auth_provider_provider_provider_account_id_pk": { + "name": "user_auth_provider_provider_provider_account_id_pk", + "columns": [ + "provider", + "provider_account_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_feedback": { + "name": "user_feedback", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "feedback_text": { + "name": "feedback_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "feedback_for": { + "name": "feedback_for", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "feedback_batch": { + "name": "feedback_batch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "context_json": { + "name": "context_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_user_feedback_created_at": { + "name": "IDX_user_feedback_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_user_feedback_kilo_user_id": { + "name": "IDX_user_feedback_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_user_feedback_feedback_for": { + "name": "IDX_user_feedback_feedback_for", + "columns": [ + { + "expression": "feedback_for", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_user_feedback_feedback_batch": { + "name": "IDX_user_feedback_feedback_batch", + "columns": [ + { + "expression": "feedback_batch", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_user_feedback_source": { + "name": "IDX_user_feedback_source", + "columns": [ + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_feedback_kilo_user_id_kilocode_users_id_fk": { + "name": "user_feedback_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "user_feedback", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_github_app_tokens": { + "name": "user_github_app_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "github_app_type": { + "name": "github_app_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'standard'" + }, + "github_user_id": { + "name": "github_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "github_login": { + "name": "github_login", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token_encrypted": { + "name": "access_token_encrypted", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "refresh_token_encrypted": { + "name": "refresh_token_encrypted", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "credential_version": { + "name": "credential_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revocation_reason": { + "name": "revocation_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_user_github_app_tokens_user_app": { + "name": "UQ_user_github_app_tokens_user_app", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "github_app_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_user_github_app_tokens_github_user_app": { + "name": "UQ_user_github_app_tokens_github_user_app", + "columns": [ + { + "expression": "github_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "github_app_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_github_app_tokens_kilo_user_id_kilocode_users_id_fk": { + "name": "user_github_app_tokens_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "user_github_app_tokens", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "user_github_app_tokens_app_type_check": { + "name": "user_github_app_tokens_app_type_check", + "value": "\"user_github_app_tokens\".\"github_app_type\" IN ('standard', 'lite')" + } + }, + "isRLSEnabled": false + }, + "public.user_period_cache": { + "name": "user_period_cache", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cache_type": { + "name": "cache_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "period_type": { + "name": "period_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "period_key": { + "name": "period_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "computed_at": { + "name": "computed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "shared_url_token": { + "name": "shared_url_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shared_at": { + "name": "shared_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "IDX_user_period_cache_kilo_user_id": { + "name": "IDX_user_period_cache_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_user_period_cache": { + "name": "UQ_user_period_cache", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "cache_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "period_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "period_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_user_period_cache_lookup": { + "name": "IDX_user_period_cache_lookup", + "columns": [ + { + "expression": "cache_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "period_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "period_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_user_period_cache_share_token": { + "name": "UQ_user_period_cache_share_token", + "columns": [ + { + "expression": "shared_url_token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"user_period_cache\".\"shared_url_token\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_period_cache_kilo_user_id_kilocode_users_id_fk": { + "name": "user_period_cache_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "user_period_cache", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "user_period_cache_period_type_check": { + "name": "user_period_cache_period_type_check", + "value": "\"user_period_cache\".\"period_type\" IN ('year', 'quarter', 'month', 'week', 'custom')" + } + }, + "isRLSEnabled": false + }, + "public.user_push_tokens": { + "name": "user_push_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_user_push_tokens_token": { + "name": "UQ_user_push_tokens_token", + "columns": [ + { + "expression": "token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_user_push_tokens_user_id": { + "name": "IDX_user_push_tokens_user_id", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_push_tokens_user_id_kilocode_users_id_fk": { + "name": "user_push_tokens_user_id_kilocode_users_id_fk", + "tableFrom": "user_push_tokens", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.vercel_ip_city": { + "name": "vercel_ip_city", + "schema": "", + "columns": { + "vercel_ip_city_id": { + "name": "vercel_ip_city_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "vercel_ip_city": { + "name": "vercel_ip_city", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UQ_vercel_ip_city": { + "name": "UQ_vercel_ip_city", + "columns": [ + { + "expression": "vercel_ip_city", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.vercel_ip_country": { + "name": "vercel_ip_country", + "schema": "", + "columns": { + "vercel_ip_country_id": { + "name": "vercel_ip_country_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "vercel_ip_country": { + "name": "vercel_ip_country", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UQ_vercel_ip_country": { + "name": "UQ_vercel_ip_country", + "columns": [ + { + "expression": "vercel_ip_country", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.webhook_events": { + "name": "webhook_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_action": { + "name": "event_action", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "headers": { + "name": "headers", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "processed": { + "name": "processed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "handlers_triggered": { + "name": "handlers_triggered", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "errors": { + "name": "errors", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "event_signature": { + "name": "event_signature", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_webhook_events_owned_by_org_id": { + "name": "IDX_webhook_events_owned_by_org_id", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_webhook_events_owned_by_user_id": { + "name": "IDX_webhook_events_owned_by_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_webhook_events_platform": { + "name": "IDX_webhook_events_platform", + "columns": [ + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_webhook_events_event_type": { + "name": "IDX_webhook_events_event_type", + "columns": [ + { + "expression": "event_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_webhook_events_created_at": { + "name": "IDX_webhook_events_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "webhook_events_owned_by_organization_id_organizations_id_fk": { + "name": "webhook_events_owned_by_organization_id_organizations_id_fk", + "tableFrom": "webhook_events", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "webhook_events_owned_by_user_id_kilocode_users_id_fk": { + "name": "webhook_events_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "webhook_events", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_webhook_events_signature": { + "name": "UQ_webhook_events_signature", + "nullsNotDistinct": false, + "columns": [ + "event_signature" + ] + } + }, + "policies": {}, + "checkConstraints": { + "webhook_events_owner_check": { + "name": "webhook_events_owner_check", + "value": "(\n (\"webhook_events\".\"owned_by_user_id\" IS NOT NULL AND \"webhook_events\".\"owned_by_organization_id\" IS NULL) OR\n (\"webhook_events\".\"owned_by_user_id\" IS NULL AND \"webhook_events\".\"owned_by_organization_id\" IS NOT NULL)\n )" + } + }, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": { + "public.microdollar_usage_view": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cost": { + "name": "cost", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "input_tokens": { + "name": "input_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "output_tokens": { + "name": "output_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "cache_write_tokens": { + "name": "cache_write_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "cache_hit_tokens": { + "name": "cache_hit_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "http_x_forwarded_for": { + "name": "http_x_forwarded_for", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ip_city": { + "name": "http_x_vercel_ip_city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ip_country": { + "name": "http_x_vercel_ip_country", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ip_latitude": { + "name": "http_x_vercel_ip_latitude", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ip_longitude": { + "name": "http_x_vercel_ip_longitude", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ja4_digest": { + "name": "http_x_vercel_ja4_digest", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "requested_model": { + "name": "requested_model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_prompt_prefix": { + "name": "user_prompt_prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "system_prompt_prefix": { + "name": "system_prompt_prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "system_prompt_length": { + "name": "system_prompt_length", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "http_user_agent": { + "name": "http_user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cache_discount": { + "name": "cache_discount", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "max_tokens": { + "name": "max_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "has_middle_out_transform": { + "name": "has_middle_out_transform", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "has_error": { + "name": "has_error", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "abuse_classification": { + "name": "abuse_classification", + "type": "smallint", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "inference_provider": { + "name": "inference_provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status_code": { + "name": "status_code", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "upstream_id": { + "name": "upstream_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "finish_reason": { + "name": "finish_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "latency": { + "name": "latency", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "moderation_latency": { + "name": "moderation_latency", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "generation_time": { + "name": "generation_time", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "is_byok": { + "name": "is_byok", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "is_user_byok": { + "name": "is_user_byok", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "streamed": { + "name": "streamed", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "cancelled": { + "name": "cancelled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "editor_name": { + "name": "editor_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "api_kind": { + "name": "api_kind", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "has_tools": { + "name": "has_tools", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "machine_id": { + "name": "machine_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "feature": { + "name": "feature", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "auto_model": { + "name": "auto_model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "market_cost": { + "name": "market_cost", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "is_free": { + "name": "is_free", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "abuse_delay": { + "name": "abuse_delay", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "abuse_downgraded_from": { + "name": "abuse_downgraded_from", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "definition": "\n SELECT\n mu.id,\n mu.kilo_user_id,\n meta.message_id,\n mu.cost,\n mu.input_tokens,\n mu.output_tokens,\n mu.cache_write_tokens,\n mu.cache_hit_tokens,\n mu.created_at,\n ip.http_ip AS http_x_forwarded_for,\n city.vercel_ip_city AS http_x_vercel_ip_city,\n country.vercel_ip_country AS http_x_vercel_ip_country,\n meta.vercel_ip_latitude AS http_x_vercel_ip_latitude,\n meta.vercel_ip_longitude AS http_x_vercel_ip_longitude,\n ja4.ja4_digest AS http_x_vercel_ja4_digest,\n mu.provider,\n mu.model,\n mu.requested_model,\n meta.user_prompt_prefix,\n spp.system_prompt_prefix,\n meta.system_prompt_length,\n ua.http_user_agent,\n mu.cache_discount,\n meta.max_tokens,\n meta.has_middle_out_transform,\n mu.has_error,\n mu.abuse_classification,\n mu.organization_id,\n mu.inference_provider,\n mu.project_id,\n meta.status_code,\n meta.upstream_id,\n frfr.finish_reason,\n meta.latency,\n meta.moderation_latency,\n meta.generation_time,\n meta.is_byok,\n meta.is_user_byok,\n meta.streamed,\n meta.cancelled,\n edit.editor_name,\n ak.api_kind,\n meta.has_tools,\n meta.machine_id,\n feat.feature,\n meta.session_id,\n md.mode,\n am.auto_model,\n meta.market_cost,\n meta.is_free,\n meta.abuse_delay,\n meta.abuse_downgraded_from\n FROM \"microdollar_usage\" mu\n LEFT JOIN \"microdollar_usage_metadata\" meta ON mu.id = meta.id\n LEFT JOIN \"http_ip\" ip ON meta.http_ip_id = ip.http_ip_id\n LEFT JOIN \"vercel_ip_city\" city ON meta.vercel_ip_city_id = city.vercel_ip_city_id\n LEFT JOIN \"vercel_ip_country\" country ON meta.vercel_ip_country_id = country.vercel_ip_country_id\n LEFT JOIN \"ja4_digest\" ja4 ON meta.ja4_digest_id = ja4.ja4_digest_id\n LEFT JOIN \"system_prompt_prefix\" spp ON meta.system_prompt_prefix_id = spp.system_prompt_prefix_id\n LEFT JOIN \"http_user_agent\" ua ON meta.http_user_agent_id = ua.http_user_agent_id\n LEFT JOIN \"finish_reason\" frfr ON meta.finish_reason_id = frfr.finish_reason_id\n LEFT JOIN \"editor_name\" edit ON meta.editor_name_id = edit.editor_name_id\n LEFT JOIN \"api_kind\" ak ON meta.api_kind_id = ak.api_kind_id\n LEFT JOIN \"feature\" feat ON meta.feature_id = feat.feature_id\n LEFT JOIN \"mode\" md ON meta.mode_id = md.mode_id\n LEFT JOIN \"auto_model\" am ON meta.auto_model_id = am.auto_model_id\n", + "name": "microdollar_usage_view", + "schema": "public", + "isExisting": false, + "materialized": false + } + }, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/packages/db/src/migrations/meta/_journal.json b/packages/db/src/migrations/meta/_journal.json index eef09c7f7d..271ae2769e 100644 --- a/packages/db/src/migrations/meta/_journal.json +++ b/packages/db/src/migrations/meta/_journal.json @@ -1142,6 +1142,13 @@ "when": 1781157223936, "tag": "0162_long_marvel_apes", "breakpoints": true + }, + { + "idx": 163, + "version": "7", + "when": 1781183556285, + "tag": "0163_busy_edwin_jarvis", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/db/src/schema.ts b/packages/db/src/schema.ts index bf87dea3c6..0f9fc98c14 100644 --- a/packages/db/src/schema.ts +++ b/packages/db/src/schema.ts @@ -4250,6 +4250,12 @@ export const cli_sessions_v2 = pgTable( index('IDX_cli_sessions_v2_kilo_user_id').on(table.kilo_user_id), index('IDX_cli_sessions_v2_created_at').on(table.created_at), index('IDX_cli_sessions_v2_user_updated').on(table.kilo_user_id, table.updated_at), + index('IDX_cli_sessions_v2_user_git_url_updated') + .on(table.kilo_user_id, table.git_url, table.updated_at) + .concurrently() + .where( + sql`${table.git_url} IS NOT NULL AND ${table.parent_session_id} IS NULL AND ${table.cloud_agent_session_id} IS NOT NULL` + ), // Supports joins from github_branch_pull_requests on (git_url, git_branch). index('cli_sessions_v2_git_url_branch_idx').on(table.git_url, table.git_branch), ] diff --git a/packages/session-ingest-contracts/src/rpc-contract.ts b/packages/session-ingest-contracts/src/rpc-contract.ts index 95bb454f06..72febff32c 100644 --- a/packages/session-ingest-contracts/src/rpc-contract.ts +++ b/packages/session-ingest-contracts/src/rpc-contract.ts @@ -1,6 +1,42 @@ import { z } from 'zod'; export const sessionIdSchema = z.string().startsWith('ses_').length(30); + +export const sessionStatusSchema = z.enum(['idle', 'busy', 'question', 'permission', 'retry']); + +export const sessionEventV2RowSchema = z.object({ + source: z.literal('v2'), + sessionId: z.string(), + createdAt: z.string(), + updatedAt: z.string(), + title: z.string().nullable(), + createdOnPlatform: z.string().nullable(), + organizationId: z.string().nullable(), + gitUrl: z.string().nullable(), + gitBranch: z.string().nullable(), + parentSessionId: z.string().nullable(), + status: sessionStatusSchema.nullable(), + statusUpdatedAt: z.string().nullable(), +}); +export type SessionEventV2Row = z.infer; + +export const sessionRowEventPayloadSchema = z.object({ + source: z.literal('v2'), + session: sessionEventV2RowSchema, + changedAt: z.string(), +}); +export type SessionRowEventPayload = z.infer; + +const sessionUpdatedMetadataPayloadSchema = sessionRowEventPayloadSchema.extend({ + session: sessionEventV2RowSchema.extend({ sessionId: sessionIdSchema }), +}); + +export const sessionUpdatedMetadataMessageSchema = z.object({ + type: z.literal('system'), + event: z.literal('session.updated'), + data: sessionUpdatedMetadataPayloadSchema, +}); +export type SessionUpdatedMetadataMessage = z.infer; export const messageIdSchema = z .string() .startsWith('msg') @@ -14,6 +50,28 @@ export const partIdSchema = z message: 'part IDs must not contain / or U+0000', }); const sdkMetadataSchema = z.record(z.string(), z.unknown()); +export const safeRepositoryUrlSchema = z + .string() + .trim() + .min(1) + .max(2048) + .refine(value => { + try { + const url = new URL(value); + const pathSegments = url.pathname.split('/').filter(Boolean); + const minimumPathSegments = ['github.com', 'gitlab.com'].includes(url.hostname) ? 2 : 1; + return ( + url.protocol === 'https:' && + url.username === '' && + url.password === '' && + url.search === '' && + url.hash === '' && + pathSegments.length >= minimumPathSegments + ); + } catch { + return false; + } + }, 'repository URL must be a safe HTTPS repository URL'); export const createSessionForCloudAgentSchema = z.object({ sessionId: sessionIdSchema, @@ -22,6 +80,7 @@ export const createSessionForCloudAgentSchema = z.object({ organizationId: z.string().optional(), createdOnPlatform: z.string().min(1), title: z.string().optional(), + gitUrl: safeRepositoryUrlSchema.optional(), }); export type CreateSessionForCloudAgentParams = z.input; @@ -136,6 +195,12 @@ export const listCloudAgentRootSessionsSchema = z.object({ start: z.number().int().nonnegative().max(8_640_000_000_000_000).optional(), }); export type ListCloudAgentRootSessionsParams = z.input; +export const listCloudAgentRootSessionsByGitUrlSchema = listCloudAgentRootSessionsSchema.extend({ + gitUrl: safeRepositoryUrlSchema, +}); +export type ListCloudAgentRootSessionsByGitUrlParams = z.input< + typeof listCloudAgentRootSessionsByGitUrlSchema +>; export type CloudAgentRootSessionSummary = { kiloSessionId: string; cloudAgentSessionId: string; @@ -433,6 +498,22 @@ export const kiloSdkStoredMessageSchema = z.object({ }); export type KiloSdkStoredMessage = z.infer; +export const kiloSdkExactMessageReadSchema = z.discriminatedUnion('kind', [ + z.object({ kind: z.literal('value'), message: kiloSdkStoredMessageSchema }), + z.object({ kind: z.literal('not_found') }), + z.object({ + kind: z.literal('too_large'), + maximumBytes: z.number().int().positive(), + phase: z.enum(['message_scan', 'page_parts']), + }), + z.object({ + kind: z.literal('retryable_failure'), + phase: z.enum(['message_scan', 'page_parts']), + }), + z.object({ kind: z.literal('invalid_data') }), +]); +export type KiloSdkExactMessageRead = z.infer; + export const kiloSdkMessageHistoryPageSchema = z.object({ messages: z.array(kiloSdkStoredMessageSchema), nextCursor: z.string().nullable(), @@ -558,6 +639,16 @@ function normalizePersistedKiloSdkMessageHistory(value: unknown): unknown { }; } +function normalizePersistedKiloSdkExactMessageRead(value: unknown): unknown { + if (!isRecord(value) || value.kind !== 'value') return value; + return { ...value, message: normalizePersistedKiloSdkStoredMessage(value.message).message }; +} + +export const persistedKiloSdkExactMessageReadSchema = z.preprocess( + normalizePersistedKiloSdkExactMessageRead, + kiloSdkExactMessageReadSchema +); + export const persistedKiloSdkMessageHistorySchema = z.preprocess( normalizePersistedKiloSdkMessageHistory, kiloSdkMessageHistorySchema @@ -634,6 +725,19 @@ export type CloudAgentRootSessionMessages = { }; export type GetCloudAgentRootSessionMessagesResult = CloudAgentRootSessionMessages | null; +export const getCloudAgentRootSessionMessageSchema = resolveCloudAgentRootSessionSchema.extend({ + messageId: messageIdSchema, +}); +export type GetCloudAgentRootSessionMessageParams = z.input< + typeof getCloudAgentRootSessionMessageSchema +>; +export type CloudAgentRootSessionMessage = { + kiloSessionId: string; + cloudAgentSessionId: string; + message: KiloSdkExactMessageRead; +}; +export type GetCloudAgentRootSessionMessageResult = CloudAgentRootSessionMessage | null; + export type SessionIngestRpcMethods = { createSessionForCloudAgent: (params: CreateSessionForCloudAgentParams) => Promise; deleteSessionForCloudAgent: (params: DeleteSessionForCloudAgentParams) => Promise; @@ -646,7 +750,13 @@ export type SessionIngestRpcMethods = { listCloudAgentRootSessions: ( params: ListCloudAgentRootSessionsParams ) => Promise; + listCloudAgentRootSessionsByGitUrl: ( + params: ListCloudAgentRootSessionsByGitUrlParams + ) => Promise; getCloudAgentRootSessionMessages: ( params: GetCloudAgentRootSessionMessagesParams ) => Promise; + getCloudAgentRootSessionMessage: ( + params: GetCloudAgentRootSessionMessageParams + ) => Promise; }; diff --git a/services/cloud-agent-next/src/execution/types.ts b/services/cloud-agent-next/src/execution/types.ts index de490346b5..655f3cbc2d 100644 --- a/services/cloud-agent-next/src/execution/types.ts +++ b/services/cloud-agent-next/src/execution/types.ts @@ -54,6 +54,7 @@ export type CommandExecutionTurnSubmission = { id?: string | null; command: string; arguments: string; + snapshotInitialization?: 'wait'; attachments?: Attachments; }; @@ -72,6 +73,7 @@ export type AcceptedCommandTurn = { messageId: string; command: string; arguments: string; + snapshotInitialization?: 'wait'; }; export type AcceptedExecutionTurn = AcceptedPromptTurn | AcceptedCommandTurn; diff --git a/services/cloud-agent-next/src/kilo-facade/basic-command.test.ts b/services/cloud-agent-next/src/kilo-facade/basic-command.test.ts new file mode 100644 index 0000000000..927513dfdc --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/basic-command.test.ts @@ -0,0 +1,112 @@ +import type { SessionCommandResponse } from '@kilocode/sdk/v2'; +import { describe, expect, it } from 'vitest'; +import { buildSyntheticCommandResponse, parseBasicKiloCommand } from './basic-command.js'; + +const messageId = 'msg_00000000000000000000000001'; + +describe('parseBasicKiloCommand', () => { + it('projects a native command body into a durable command request', () => { + expect( + parseBasicKiloCommand({ + messageID: messageId, + command: 'review/security', + arguments: ' --strict ', + agent: 'code', + model: 'kilo/anthropic/claude-sonnet-4', + variant: 'high', + snapshotInitialization: 'wait', + parts: [], + }) + ).toEqual({ + success: true, + command: { + messageId, + command: 'review/security', + arguments: ' --strict ', + agent: { + mode: 'code', + model: 'anthropic/claude-sonnet-4', + variant: 'high', + }, + snapshotInitialization: 'wait', + }, + }); + }); + + it('accepts the minimal command body without resolving durable defaults', () => { + expect(parseBasicKiloCommand({ command: 'init', arguments: '' })).toEqual({ + success: true, + command: { messageId: undefined, command: 'init', arguments: '' }, + }); + }); + + it('reports non-empty file parts as explicitly unsupported', () => { + expect( + parseBasicKiloCommand({ + command: 'init', + arguments: '', + parts: [{ type: 'file', mime: 'text/plain', url: 'file:///tmp/a.txt' }], + }) + ).toEqual({ success: false, reason: 'parts-unsupported' }); + }); + + it.each([ + [{ command: 'init' }], + [{ command: '', arguments: '' }], + [{ command: ' '.repeat(2), arguments: '' }], + [{ command: 'x'.repeat(101), arguments: '' }], + [{ command: 'init', arguments: 'x'.repeat(100_001) }], + [{ command: 'init', arguments: '', messageID: 'msg_invalid' }], + [{ command: 'init', arguments: '', agent: 'Build' }], + [{ command: 'init', arguments: '', model: 'anthropic/claude' }], + [{ command: 'init', arguments: '', model: 'kilo/' }], + [{ command: 'init', arguments: '', snapshotInitialization: 'continue' }], + [{ command: 'init', arguments: '', parts: 'not-an-array' }], + [{ command: 'init', arguments: '', unknown: true }], + ])('rejects unsupported native command bodies', body => { + expect(parseBasicKiloCommand(body)).toEqual({ success: false, reason: 'invalid' }); + }); +}); + +describe('buildSyntheticCommandResponse', () => { + it('builds an SDK-valid acknowledgement without claiming command completion', () => { + const response = buildSyntheticCommandResponse({ + now: 1_750_000_000_000, + assistantMessageId: 'msg_00000000000000000000000002', + admittedMessageId: messageId, + kiloSessionId: 'ses_12345678901234567890123456', + publicDirectory: '/cloud-agent/sessions/ses_12345678901234567890123456', + requestedAgent: 'code', + requestedModel: 'anthropic/claude-sonnet-4', + variant: 'high', + }); + const sdkResponse: SessionCommandResponse = response; + + expect(sdkResponse).toEqual({ + info: { + id: 'msg_00000000000000000000000002', + sessionID: 'ses_12345678901234567890123456', + role: 'assistant', + time: { created: 1_750_000_000_000 }, + parentID: messageId, + modelID: 'anthropic/claude-sonnet-4', + providerID: 'kilo', + mode: 'code', + agent: 'code', + path: { + cwd: '/cloud-agent/sessions/ses_12345678901234567890123456', + root: '/cloud-agent/sessions/ses_12345678901234567890123456', + }, + cost: 0, + tokens: { + input: 0, + output: 0, + reasoning: 0, + cache: { read: 0, write: 0 }, + }, + variant: 'high', + }, + parts: [], + }); + }); +}); diff --git a/services/cloud-agent-next/src/kilo-facade/basic-command.ts b/services/cloud-agent-next/src/kilo-facade/basic-command.ts new file mode 100644 index 0000000000..92cc02644d --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/basic-command.ts @@ -0,0 +1,123 @@ +import type { SessionCommandResponse } from '@kilocode/sdk/v2'; +import * as z from 'zod'; +import { MessageIdSchema, ModeSlugSchema, modelIdSchema } from '../router/schemas.js'; +import { Limits } from '../schema.js'; + +const commandBodySchema = z + .object({ + messageID: MessageIdSchema.optional(), + command: z + .string() + .min(1) + .max(Limits.MAX_RUNTIME_KILO_COMMAND_NAME_LENGTH) + .refine(command => command.trim().length > 0), + arguments: z.string().max(Limits.MAX_PROMPT_LENGTH), + agent: ModeSlugSchema.optional(), + model: z.string().optional(), + variant: z.string().min(1).max(255).optional(), + snapshotInitialization: z.literal('wait').optional(), + parts: z.array(z.never()).optional(), + }) + .strict(); + +export type BasicKiloCommand = { + messageId?: string; + command: string; + arguments: string; + agent?: { + mode?: string; + model?: string; + variant?: string; + }; + snapshotInitialization?: 'wait'; +}; + +export type BasicKiloCommandParseResult = + | { success: true; command: BasicKiloCommand } + | { success: false; reason: 'invalid' | 'parts-unsupported' }; + +function parseKiloModel(value: string | undefined): string | undefined | null { + if (value === undefined) return undefined; + const separator = value.indexOf('/'); + if (separator < 0 || value.slice(0, separator) !== 'kilo') return null; + const model = value.slice(separator + 1); + return modelIdSchema.safeParse(model).success ? model : null; +} + +export function parseBasicKiloCommand(value: unknown): BasicKiloCommandParseResult { + if ( + typeof value === 'object' && + value !== null && + 'parts' in value && + Array.isArray(value.parts) && + value.parts.length > 0 + ) { + return { success: false, reason: 'parts-unsupported' }; + } + + const result = commandBodySchema.safeParse(value); + if (!result.success) return { success: false, reason: 'invalid' }; + const model = parseKiloModel(result.data.model); + if (model === null) return { success: false, reason: 'invalid' }; + + const mode = result.data.agent; + const variant = result.data.variant; + return { + success: true, + command: { + messageId: result.data.messageID, + command: result.data.command, + arguments: result.data.arguments, + ...(mode !== undefined || model !== undefined || variant !== undefined + ? { + agent: { + ...(mode !== undefined ? { mode } : {}), + ...(model !== undefined ? { model } : {}), + ...(variant !== undefined ? { variant } : {}), + }, + } + : {}), + ...(result.data.snapshotInitialization !== undefined + ? { snapshotInitialization: result.data.snapshotInitialization } + : {}), + }, + }; +} + +export function buildSyntheticCommandResponse(params: { + now: number; + assistantMessageId: string; + admittedMessageId: string; + kiloSessionId: string; + publicDirectory: string; + requestedAgent?: string; + requestedModel?: string; + variant?: string; +}): SessionCommandResponse { + const agent = params.requestedAgent ?? 'unknown'; + return { + info: { + id: params.assistantMessageId, + sessionID: params.kiloSessionId, + role: 'assistant', + time: { created: params.now }, + parentID: params.admittedMessageId, + modelID: params.requestedModel ?? 'unknown', + providerID: 'kilo', + mode: agent, + agent, + path: { cwd: params.publicDirectory, root: params.publicDirectory }, + cost: 0, + tokens: { + input: 0, + output: 0, + reasoning: 0, + cache: { read: 0, write: 0 }, + }, + ...(params.variant !== undefined ? { variant: params.variant } : {}), + }, + // Temporary VS Code compatibility acknowledgement; real command output arrives through + // events/messages until Kilo exposes an async command API or Cloud Agent has a durable waiter. + parts: [], + }; +} diff --git a/services/cloud-agent-next/src/kilo-facade/contracts.ts b/services/cloud-agent-next/src/kilo-facade/contracts.ts new file mode 100644 index 0000000000..4f1fa8ec62 --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/contracts.ts @@ -0,0 +1,88 @@ +import type { BalanceOnlyResult } from '../balance-validation.js'; +import type { + SessionMessageAdmissionResult, + SubmittedSessionMessageRequest, +} from '../execution/types.js'; +import type { CloudAgentSession } from '../persistence/CloudAgentSession.js'; +import type { SessionStatus } from '../shared/protocol.js'; +import type { SlashCommandInfo } from '../shared/slash-commands.js'; +import type { Env } from '../types.js'; +import type { + LiveWrapperTarget, + SessionKiloFacadeDecision, + SessionKiloFacadePolicyInput, +} from './session-proxy.js'; + +export type KiloFacadeGlobalEvents = { + getPublicSessionStatuses?(kiloSessionId?: string): Record; + openPublicGlobalEventStream(userId: string): Response; + openPublicSessionEventStream(userId: string, kiloSessionId: string): Response; +}; + +export type KiloFacadeRequestDeps = { + resolveRootSessionForKiloSession?: (params: { + env: Env; + userId: string; + kiloSessionId: string; + }) => Promise<{ cloudAgentSessionId: string } | null>; + decideSessionRoute?: (input: SessionKiloFacadePolicyInput) => SessionKiloFacadeDecision; + resolveLiveWrapper?: (params: { + env: Env; + userId: string; + cloudAgentSessionId: string; + }) => Promise; + admitPrompt?: (params: { + env: Env; + userId: string; + cloudAgentSessionId: string; + request: SubmittedSessionMessageRequest; + }) => Promise; + validatePromptBalance?: (params: { + env: Env; + authToken: string; + userId: string; + cloudAgentSessionId: string; + }) => Promise; + getAvailableCommands?: (params: { + env: Env; + userId: string; + cloudAgentSessionId: string; + }) => Promise; + admitCommand?: (params: { + env: Env; + userId: string; + cloudAgentSessionId: string; + request: SubmittedSessionMessageRequest; + }) => Promise; + validateCommandBalance?: (params: { + env: Env; + authToken: string; + userId: string; + cloudAgentSessionId: string; + }) => Promise; + interruptPrompt?: (params: { + env: Env; + userId: string; + cloudAgentSessionId: string; + }) => Promise>>; + globalEvents?: KiloFacadeGlobalEvents; +}; + +export type KiloFacadeHandlerContext = { + request: Request; + env: Env; + userId: string; + authToken?: string; + deps?: KiloFacadeRequestDeps; + url: URL; + kiloPath: string; +}; + +export type SelectedOwnedRootHandlerContext = KiloFacadeHandlerContext & { + kiloSessionId: string; + cloudAgentSessionId: string; +}; + +export type OwnedRootHandlerContext = SelectedOwnedRootHandlerContext & { + encodedKiloSessionId: string; +}; diff --git a/services/cloud-agent-next/src/kilo-facade/handlers/abort/handler.ts b/services/cloud-agent-next/src/kilo-facade/handlers/abort/handler.ts new file mode 100644 index 0000000000..3324ceb6ee --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/handlers/abort/handler.ts @@ -0,0 +1,34 @@ +import type { CloudAgentSession } from '../../../persistence/CloudAgentSession.js'; +import { withDORetry } from '../../../utils/do-retry.js'; +import type { OwnedRootHandlerContext } from '../../contracts.js'; +import { validateIdScopedSelectors } from '../../http-contract.js'; + +async function interruptPrompt( + context: OwnedRootHandlerContext +): Promise>> { + if (context.deps?.interruptPrompt) { + return context.deps.interruptPrompt({ + env: context.env, + userId: context.userId, + cloudAgentSessionId: context.cloudAgentSessionId, + }); + } + const id = context.env.CLOUD_AGENT_SESSION.idFromName( + `${context.userId}:${context.cloudAgentSessionId}` + ); + return withDORetry< + DurableObjectStub, + Awaited> + >( + () => context.env.CLOUD_AGENT_SESSION.get(id), + stub => stub.interruptExecution(), + 'interruptExecution' + ); +} + +export async function handleAbort(context: OwnedRootHandlerContext): Promise { + const selectorResponse = validateIdScopedSelectors(context.url, context.kiloSessionId, new Set()); + if (selectorResponse) return selectorResponse; + await interruptPrompt(context); + return Response.json(true); +} diff --git a/services/cloud-agent-next/src/kilo-facade/handlers/command-list/handler.test.ts b/services/cloud-agent-next/src/kilo-facade/handlers/command-list/handler.test.ts new file mode 100644 index 0000000000..5c5cb17f9b --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/handlers/command-list/handler.test.ts @@ -0,0 +1,130 @@ +import { describe, expect, it, vi } from 'vitest'; +import { commandsOrDefault } from '../../../shared/slash-commands.js'; +import type { KiloFacadeHandlerContext } from '../../contracts.js'; +import { handleCommandList } from './handler.js'; + +const kiloSessionId = 'ses_12345678901234567890123456'; +const directory = `/cloud-agent/sessions/${kiloSessionId}`; + +function commandListContext(search = `?directory=${directory}`): { + context: KiloFacadeHandlerContext; + getAvailableCommands: ReturnType; + resolveRoot: ReturnType; +} { + const request = new Request(`https://facade.test/kilo/command${search}`); + const getAvailableCommands = vi.fn().mockResolvedValue([ + { + name: 'init', + description: 'initialize repository instructions', + source: 'command', + hints: ['optional focus'], + agent: 'code', + model: 'anthropic/claude-sonnet-4', + subtask: false, + }, + ]); + const resolveRoot = vi.fn().mockResolvedValue({ cloudAgentSessionId: 'cloud-root' }); + return { + context: { + request, + env: {} as KiloFacadeHandlerContext['env'], + userId: 'user-1', + url: new URL(request.url), + kiloPath: '/command', + deps: { resolveRootSessionForKiloSession: resolveRoot, getAvailableCommands }, + }, + getAvailableCommands, + resolveRoot, + }; +} + +describe('command list handler', () => { + it('returns cached command metadata with an empty compatibility template', async () => { + const { context, getAvailableCommands } = commandListContext(); + + const response = await handleCommandList(context); + + expect(response.status).toBe(200); + await expect(response.json()).resolves.toEqual([ + { + name: 'init', + description: 'initialize repository instructions', + source: 'command', + hints: ['optional focus'], + agent: 'code', + model: 'anthropic/claude-sonnet-4', + subtask: false, + template: '', + }, + ]); + expect(getAvailableCommands).toHaveBeenCalledWith({ + env: context.env, + userId: 'user-1', + cloudAgentSessionId: 'cloud-root', + }); + }); + + it('reads cold generated defaults through the production session DO seam', async () => { + const { context } = commandListContext(); + const getAvailableCommands = vi.fn().mockResolvedValue(commandsOrDefault(undefined)); + context.deps = { + resolveRootSessionForKiloSession: async () => ({ cloudAgentSessionId: 'cloud-root' }), + }; + context.env = { + CLOUD_AGENT_SESSION: { + idFromName: vi.fn(() => 'session-do-id'), + get: vi.fn(() => ({ getAvailableCommands })), + }, + } as unknown as KiloFacadeHandlerContext['env']; + + const response = await handleCommandList(context); + const commands = (await response.json()) as Array<{ name: string; template: string }>; + + expect(response.status).toBe(200); + expect(commands.some(command => command.name === 'init')).toBe(true); + expect(commands.some(command => command.name === 'compact')).toBe(true); + expect(commands.every(command => command.template === '')).toBe(true); + expect(getAvailableCommands).toHaveBeenCalledOnce(); + }); + + it.each([ + '', + '?workspace=/private/workspace', + '?directory=/private/workspace', + '?unknown=x', + `?directory=${encodeURIComponent(directory)}&directory=${encodeURIComponent(directory)}`, + ])('requires exactly one public directory selector for %s', async search => { + const { context, getAvailableCommands } = commandListContext(search); + + const response = await handleCommandList(context); + + expect(response.status).toBe(400); + await expect(response.json()).resolves.toMatchObject({ + error: 'KILO_SESSION_SELECTOR_UNSUPPORTED', + }); + expect(getAvailableCommands).not.toHaveBeenCalled(); + }); + + it('returns public not-found for an unowned selected root', async () => { + const { context, resolveRoot, getAvailableCommands } = commandListContext(); + resolveRoot.mockResolvedValue(null); + + const response = await handleCommandList(context); + + expect(response.status).toBe(404); + expect(getAvailableCommands).not.toHaveBeenCalled(); + }); + + it('sanitizes command catalog read failures', async () => { + const { context, getAvailableCommands } = commandListContext(); + getAvailableCommands.mockRejectedValue(new Error('private durable object details')); + + const response = await handleCommandList(context); + + expect(response.status).toBe(500); + await expect(response.json()).resolves.toEqual({ + error: 'KILO_COMMAND_LIST_FAILED', + message: 'Kilo command catalog is temporarily unavailable', + }); + }); +}); diff --git a/services/cloud-agent-next/src/kilo-facade/handlers/command-list/handler.ts b/services/cloud-agent-next/src/kilo-facade/handlers/command-list/handler.ts new file mode 100644 index 0000000000..5343425be1 --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/handlers/command-list/handler.ts @@ -0,0 +1,57 @@ +import type { Command } from '@kilocode/sdk/v2'; +import type { CloudAgentSession } from '../../../persistence/CloudAgentSession.js'; +import type { SlashCommandInfo } from '../../../shared/slash-commands.js'; +import { withDORetry } from '../../../utils/do-retry.js'; +import type { KiloFacadeHandlerContext } from '../../contracts.js'; +import { facadeError } from '../../http-contract.js'; +import { resolveSelectedOwnedRoot } from '../../ownership.js'; + +function projectCommand(command: SlashCommandInfo): Command { + return { + name: command.name, + ...(command.description !== undefined ? { description: command.description } : {}), + ...(command.agent !== undefined ? { agent: command.agent } : {}), + ...(command.model !== undefined ? { model: command.model } : {}), + ...(command.source !== undefined ? { source: command.source } : {}), + ...(command.subtask !== undefined ? { subtask: command.subtask } : {}), + hints: command.hints, + // The runtime cache is intentionally metadata-only; Kilo expands commands by name. + // This empty field satisfies the SDK shape and is not an executable public template. + template: '', + }; +} + +function readAvailableCommands( + context: KiloFacadeHandlerContext, + cloudAgentSessionId: string +): Promise { + if (context.deps?.getAvailableCommands) { + return context.deps.getAvailableCommands({ + env: context.env, + userId: context.userId, + cloudAgentSessionId, + }); + } + const id = context.env.CLOUD_AGENT_SESSION.idFromName(`${context.userId}:${cloudAgentSessionId}`); + return withDORetry, SlashCommandInfo[]>( + () => context.env.CLOUD_AGENT_SESSION.get(id), + stub => stub.getAvailableCommands(), + 'getAvailableCommands' + ); +} + +export async function handleCommandList(context: KiloFacadeHandlerContext): Promise { + const owned = await resolveSelectedOwnedRoot(context); + if (owned instanceof Response) return owned; + + try { + const commands = await readAvailableCommands(context, owned.cloudAgentSessionId); + return Response.json(commands.map(projectCommand)); + } catch { + return facadeError( + 500, + 'KILO_COMMAND_LIST_FAILED', + 'Kilo command catalog is temporarily unavailable' + ); + } +} diff --git a/services/cloud-agent-next/src/kilo-facade/handlers/command/handler.test.ts b/services/cloud-agent-next/src/kilo-facade/handlers/command/handler.test.ts new file mode 100644 index 0000000000..5fdf72ec89 --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/handlers/command/handler.test.ts @@ -0,0 +1,369 @@ +import type { SessionCommandResponse } from '@kilocode/sdk/v2'; +import { TRPCError } from '@trpc/server'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +const { preflightExistingPromptModelMock } = vi.hoisted(() => ({ + preflightExistingPromptModelMock: vi.fn(), +})); + +vi.mock('../../../session/model-preflight.js', () => ({ + preflightExistingPromptModel: preflightExistingPromptModelMock, +})); + +import type { OwnedRootHandlerContext } from '../../contracts.js'; +import { publicCloudAgentDirectory } from '../../public-sdk-projection.js'; +import { handleCommand } from './handler.js'; + +const kiloSessionId = 'ses_12345678901234567890123456'; +const cloudAgentSessionId = 'agent_command'; +const messageId = 'msg_018f1e2d3c4bCommandFacadeA'; + +function commandContext(params: { + body?: unknown; + rawBody?: string; + search?: string; + authToken?: string | null; + headers?: Record; + admission?: unknown; +}) { + const directory = encodeURIComponent(publicCloudAgentDirectory(kiloSessionId)); + const request = new Request( + `https://facade.test/kilo/session/${kiloSessionId}/command${params.search ?? `?directory=${directory}`}`, + { + method: 'POST', + headers: { 'content-type': 'application/json', ...params.headers }, + body: params.rawBody ?? JSON.stringify(params.body ?? { command: 'init', arguments: '' }), + } + ); + const admitCommand = vi.fn().mockResolvedValue( + params.admission ?? { + success: true, + outcome: 'queued', + messageId, + compatibilityDelivery: 'queued', + } + ); + const validateCommandBalance = vi.fn().mockResolvedValue({ success: true }); + const hasMessageAdmission = vi.fn().mockResolvedValue(false); + const context: OwnedRootHandlerContext = { + request, + env: { + CLOUD_AGENT_SESSION: { + idFromName: vi.fn(() => 'session-do-id'), + get: vi.fn(() => ({ hasMessageAdmission })), + }, + } as unknown as OwnedRootHandlerContext['env'], + userId: 'user-1', + authToken: params.authToken === null ? undefined : (params.authToken ?? 'validated-token'), + url: new URL(request.url), + kiloPath: `/session/${kiloSessionId}/command`, + kiloSessionId, + encodedKiloSessionId: kiloSessionId, + cloudAgentSessionId, + deps: { admitCommand, validateCommandBalance }, + }; + return { context, admitCommand, validateCommandBalance, hasMessageAdmission }; +} + +describe('command handler', () => { + beforeEach(() => { + vi.clearAllMocks(); + preflightExistingPromptModelMock.mockResolvedValue(undefined); + }); + + it('durably admits a command and returns an immediate synthetic acknowledgement', async () => { + const { context, admitCommand } = commandContext({ + body: { + messageID: messageId, + command: 'review/security', + arguments: ' --strict ', + agent: 'plan', + model: 'kilo/anthropic/claude-sonnet-4', + variant: 'high', + snapshotInitialization: 'wait', + parts: [], + }, + }); + + const response = await handleCommand(context); + const body = (await response.json()) as SessionCommandResponse; + + expect(response.status).toBe(200); + expect(admitCommand).toHaveBeenCalledWith({ + env: context.env, + userId: 'user-1', + cloudAgentSessionId, + request: { + userId: 'user-1', + turn: { + type: 'command', + id: messageId, + command: 'review/security', + arguments: ' --strict ', + snapshotInitialization: 'wait', + }, + agent: { + mode: 'plan', + model: 'anthropic/claude-sonnet-4', + variant: 'high', + }, + }, + }); + expect(preflightExistingPromptModelMock).toHaveBeenCalledWith({ + env: context.env, + userId: 'user-1', + cloudAgentSessionId, + requestedModel: 'anthropic/claude-sonnet-4', + procedure: 'kilo.session.command', + }); + expect(body).toMatchObject({ + info: { + sessionID: kiloSessionId, + role: 'assistant', + parentID: messageId, + modelID: 'anthropic/claude-sonnet-4', + providerID: 'kilo', + mode: 'plan', + agent: 'plan', + variant: 'high', + path: { + cwd: publicCloudAgentDirectory(kiloSessionId), + root: publicCloudAgentDirectory(kiloSessionId), + }, + cost: 0, + }, + parts: [], + }); + expect(body.info.id).not.toBe(messageId); + expect(body.info.time).not.toHaveProperty('completed'); + expect(body.info).not.toHaveProperty('finish'); + }); + + it.each([ + ['', 'KILO_SESSION_SELECTOR_UNSUPPORTED'], + ['?workspace=/private/workspace', 'KILO_SESSION_SELECTOR_UNSUPPORTED'], + [ + `?directory=${encodeURIComponent(publicCloudAgentDirectory('ses_22222222222222222222222222'))}`, + 'KILO_SESSION_SELECTOR_UNSUPPORTED', + ], + [ + `?directory=${encodeURIComponent(publicCloudAgentDirectory(kiloSessionId))}&directory=${encodeURIComponent(publicCloudAgentDirectory(kiloSessionId))}`, + 'KILO_QUERY_INVALID', + ], + ])( + 'rejects missing or unsupported selectors before mutation side effects', + async (search, code) => { + const { context, admitCommand, validateCommandBalance } = commandContext({ search }); + + const response = await handleCommand(context); + + expect(response.status).toBe(400); + await expect(response.json()).resolves.toMatchObject({ error: code }); + expect(validateCommandBalance).not.toHaveBeenCalled(); + expect(admitCommand).not.toHaveBeenCalled(); + } + ); + + it('rejects non-empty command parts explicitly before mutation side effects', async () => { + const { context, admitCommand, validateCommandBalance } = commandContext({ + body: { + command: 'init', + arguments: '', + parts: [{ type: 'file', mime: 'text/plain', url: 'file:///tmp/a.txt' }], + }, + }); + + const response = await handleCommand(context); + + expect(response.status).toBe(400); + await expect(response.json()).resolves.toEqual({ + error: 'KILO_COMMAND_PARTS_UNSUPPORTED', + message: 'File attachments are not supported for public Kilo commands', + }); + expect(validateCommandBalance).not.toHaveBeenCalled(); + expect(admitCommand).not.toHaveBeenCalled(); + }); + + it.each([ + { rawBody: '{' }, + { body: { command: 'init', arguments: '', unknown: true } }, + { body: { command: 'init', arguments: '', model: 'anthropic/claude' } }, + { + body: { command: 'init', arguments: '' }, + headers: { 'content-length': String(256 * 1024 + 1) }, + }, + ])('rejects invalid command bodies before mutation side effects', async params => { + const { context, admitCommand, validateCommandBalance } = commandContext(params); + + const response = await handleCommand(context); + + expect(response.status).toBe(400); + await expect(response.json()).resolves.toMatchObject({ error: 'KILO_COMMAND_BODY_INVALID' }); + expect(validateCommandBalance).not.toHaveBeenCalled(); + expect(admitCommand).not.toHaveBeenCalled(); + }); + + it('requires facade authentication and rejects balance bypass', async () => { + const missingAuth = commandContext({ authToken: null }); + const missingAuthResponse = await handleCommand(missingAuth.context); + expect(missingAuthResponse.status).toBe(500); + await expect(missingAuthResponse.json()).resolves.toMatchObject({ + error: 'KILO_FACADE_UNAVAILABLE', + }); + + const bypass = commandContext({ headers: { 'x-skip-balance-check': 'true' } }); + const bypassResponse = await handleCommand(bypass.context); + expect(bypassResponse.status).toBe(400); + await expect(bypassResponse.json()).resolves.toMatchObject({ + error: 'KILO_BALANCE_BYPASS_UNSUPPORTED', + }); + expect(bypass.validateCommandBalance).not.toHaveBeenCalled(); + expect(bypass.admitCommand).not.toHaveBeenCalled(); + }); + + it('preserves balance validation failure status and prevents admission', async () => { + const { context, validateCommandBalance, admitCommand } = commandContext({}); + validateCommandBalance.mockResolvedValue({ + success: false, + status: 402, + message: 'Insufficient credits', + }); + + const response = await handleCommand(context); + + expect(response.status).toBe(402); + await expect(response.json()).resolves.toEqual({ + error: 'KILO_BALANCE_VALIDATION_FAILED', + message: 'Insufficient credits', + }); + expect(admitCommand).not.toHaveBeenCalled(); + }); + + it('uses the admitted generated message identity as the acknowledgement parent', async () => { + const generatedMessageId = 'msg_018f1e2d3c4bGeneratedCmdAB'; + const { context } = commandContext({ + body: { command: 'init', arguments: '' }, + admission: { + success: true, + outcome: 'queued', + messageId: generatedMessageId, + compatibilityDelivery: 'queued', + }, + }); + + const response = await handleCommand(context); + + await expect(response.json()).resolves.toMatchObject({ + info: { parentID: generatedMessageId, modelID: 'unknown', agent: 'unknown' }, + }); + }); + + it('skips model preflight for an idempotently replayed durable command', async () => { + const { context, hasMessageAdmission, admitCommand } = commandContext({ + body: { messageID: messageId, command: 'init', arguments: '' }, + }); + hasMessageAdmission.mockResolvedValue(true); + preflightExistingPromptModelMock.mockRejectedValue(new Error('model is unavailable')); + + const response = await handleCommand(context); + + expect(response.status).toBe(200); + expect(preflightExistingPromptModelMock).not.toHaveBeenCalled(); + expect(admitCommand).toHaveBeenCalledOnce(); + }); + + it.each([ + [ + new TRPCError({ code: 'BAD_REQUEST', message: 'private invalid model details' }), + 400, + 'KILO_COMMAND_ADMISSION_REJECTED', + 'Kilo command model selection is not available', + ], + [ + new TRPCError({ code: 'FORBIDDEN', message: 'private authorization details' }), + 403, + 'KILO_COMMAND_ADMISSION_REJECTED', + 'Kilo command model selection is not available', + ], + [ + new TRPCError({ code: 'SERVICE_UNAVAILABLE', message: 'private provider details' }), + 503, + 'MODEL_VALIDATION_UNAVAILABLE', + 'Command model validation is temporarily unavailable', + ], + [ + new TRPCError({ code: 'NOT_FOUND', message: 'private session details' }), + 404, + 'KILO_SESSION_NOT_FOUND', + 'Cloud Agent root Kilo session was not found', + ], + [ + new Error('private unexpected preflight details'), + 500, + 'KILO_COMMAND_ADMISSION_FAILED', + 'Kilo command admission failed', + ], + ])('maps model preflight failures without admission', async (error, status, code, message) => { + const { context, admitCommand } = commandContext({}); + preflightExistingPromptModelMock.mockRejectedValue(error); + + const response = await handleCommand(context); + + expect(response.status).toBe(status); + await expect(response.json()).resolves.toEqual({ error: code, message }); + expect(admitCommand).not.toHaveBeenCalled(); + }); + + it.each([ + [ + { success: false, code: 'BAD_REQUEST', error: 'invalid intent' }, + 400, + 'KILO_COMMAND_ADMISSION_REJECTED', + 'Kilo command admission was rejected', + ], + [ + { success: false, code: 'NOT_FOUND', error: 'missing' }, + 404, + 'KILO_SESSION_NOT_FOUND', + 'Cloud Agent root Kilo session was not found', + ], + [ + { success: false, code: 'PENDING_QUEUE_FULL', error: 'full' }, + 429, + 'KILO_COMMAND_QUEUE_FULL', + 'Kilo command queue is full', + ], + [ + { success: false, code: 'WORKSPACE_SETUP_FAILED', error: 'retry' }, + 503, + 'WORKSPACE_SETUP_FAILED', + 'Kilo command runtime is temporarily unavailable', + ], + [ + { success: false, code: 'INTERNAL', error: 'private details' }, + 500, + 'KILO_COMMAND_ADMISSION_FAILED', + 'Kilo command admission failed', + ], + ])('maps durable admission failures', async (admission, status, code, message) => { + const { context } = commandContext({ admission }); + + const response = await handleCommand(context); + + expect(response.status).toBe(status); + await expect(response.json()).resolves.toEqual({ error: code, message }); + }); + + it('sanitizes unexpected durable admission failures', async () => { + const { context, admitCommand } = commandContext({}); + admitCommand.mockRejectedValue(new Error('private durable object details')); + + const response = await handleCommand(context); + + expect(response.status).toBe(500); + await expect(response.json()).resolves.toEqual({ + error: 'KILO_COMMAND_ADMISSION_FAILED', + message: 'Kilo command admission failed', + }); + }); +}); diff --git a/services/cloud-agent-next/src/kilo-facade/handlers/command/handler.ts b/services/cloud-agent-next/src/kilo-facade/handlers/command/handler.ts new file mode 100644 index 0000000000..cc0ba487d8 --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/handlers/command/handler.ts @@ -0,0 +1,232 @@ +import { TRPCError } from '@trpc/server'; +import { fetchOrgIdForSession, validateBalanceOnly } from '../../../balance-validation.js'; +import type { + QueueExecutionTurnCommand, + SessionMessageAdmissionResult, + SubmittedSessionMessageRequest, +} from '../../../execution/types.js'; +import type { CloudAgentSession } from '../../../persistence/CloudAgentSession.js'; +import { createMessageId } from '../../../session/message-id.js'; +import { preflightAndAdmitPromptMessage } from '../../../session/queue-message.js'; +import type { UserId } from '../../../types/ids.js'; +import { withDORetry } from '../../../utils/do-retry.js'; +import { + buildSyntheticCommandResponse, + parseBasicKiloCommand, + type BasicKiloCommand, +} from '../../basic-command.js'; +import type { OwnedRootHandlerContext } from '../../contracts.js'; +import { + facadeError, + missingRootKiloSessionResponse, + readBoundedBody, + unsupportedSessionSelectorResponse, + validateIdScopedSelectors, +} from '../../http-contract.js'; +import { publicCloudAgentDirectory } from '../../public-sdk-projection.js'; + +const MAX_KILO_COMMAND_JSON_BYTES = 256 * 1024; + +async function readCommandBody(request: Request): Promise { + const declaredLength = request.headers.get('content-length'); + if (declaredLength !== null) { + const bodyBytes = Number(declaredLength); + if ( + !Number.isSafeInteger(bodyBytes) || + bodyBytes < 0 || + bodyBytes > MAX_KILO_COMMAND_JSON_BYTES + ) { + return facadeError(400, 'KILO_COMMAND_BODY_INVALID', 'Kilo command body is not valid'); + } + } + + const bytes = await readBoundedBody(new Response(request.body), MAX_KILO_COMMAND_JSON_BYTES); + if (!bytes) { + return facadeError(400, 'KILO_COMMAND_BODY_INVALID', 'Kilo command body is not valid'); + } + + let value: unknown; + try { + value = JSON.parse(new TextDecoder().decode(bytes)); + } catch { + return facadeError(400, 'KILO_COMMAND_BODY_INVALID', 'Kilo command body is not valid'); + } + const parsed = parseBasicKiloCommand(value); + if (!parsed.success) { + return parsed.reason === 'parts-unsupported' + ? facadeError( + 400, + 'KILO_COMMAND_PARTS_UNSUPPORTED', + 'File attachments are not supported for public Kilo commands' + ) + : facadeError(400, 'KILO_COMMAND_BODY_INVALID', 'Kilo command body is not valid'); + } + return parsed.command; +} + +function commandAdmissionError( + result: Extract +): Response { + switch (result.code) { + case 'BAD_REQUEST': + return facadeError( + 400, + 'KILO_COMMAND_ADMISSION_REJECTED', + 'Kilo command admission was rejected' + ); + case 'NOT_FOUND': + return missingRootKiloSessionResponse(); + case 'PENDING_QUEUE_FULL': + return facadeError(429, 'KILO_COMMAND_QUEUE_FULL', 'Kilo command queue is full'); + case 'SANDBOX_CONNECT_FAILED': + case 'WORKSPACE_SETUP_FAILED': + case 'KILO_SERVER_FAILED': + case 'WRAPPER_START_FAILED': + case 'WRAPPER_FINALIZING': + return facadeError(503, result.code, 'Kilo command runtime is temporarily unavailable'); + case 'INTERNAL': + return facadeError(500, 'KILO_COMMAND_ADMISSION_FAILED', 'Kilo command admission failed'); + } +} + +function commandPreflightError(error: unknown): Response { + if (!(error instanceof TRPCError)) { + return facadeError(500, 'KILO_COMMAND_ADMISSION_FAILED', 'Kilo command admission failed'); + } + switch (error.code) { + case 'BAD_REQUEST': + return facadeError( + 400, + 'KILO_COMMAND_ADMISSION_REJECTED', + 'Kilo command model selection is not available' + ); + case 'NOT_FOUND': + return missingRootKiloSessionResponse(); + case 'FORBIDDEN': + return facadeError( + 403, + 'KILO_COMMAND_ADMISSION_REJECTED', + 'Kilo command model selection is not available' + ); + case 'SERVICE_UNAVAILABLE': + return facadeError( + 503, + 'MODEL_VALIDATION_UNAVAILABLE', + 'Command model validation is temporarily unavailable' + ); + default: + return facadeError(500, 'KILO_COMMAND_ADMISSION_FAILED', 'Kilo command admission failed'); + } +} + +async function validateCommandMutation( + context: OwnedRootHandlerContext +): Promise { + const command = await readCommandBody(context.request); + if (command instanceof Response) return command; + if (!context.authToken) { + return facadeError(500, 'KILO_FACADE_UNAVAILABLE', 'Durable command admission is unavailable'); + } + if (context.request.headers.get('x-skip-balance-check') !== null) { + return facadeError( + 400, + 'KILO_BALANCE_BYPASS_UNSUPPORTED', + 'Balance bypass is not supported for public Kilo command mutations' + ); + } + + const balance = context.deps?.validateCommandBalance + ? await context.deps.validateCommandBalance({ + env: context.env, + authToken: context.authToken, + userId: context.userId, + cloudAgentSessionId: context.cloudAgentSessionId, + }) + : await validateBalanceOnly( + context.authToken, + await fetchOrgIdForSession(context.env, context.userId, context.cloudAgentSessionId), + context.env + ); + if (!balance.success) { + return facadeError(balance.status, 'KILO_BALANCE_VALIDATION_FAILED', balance.message); + } + return command; +} + +async function admitCommand( + context: OwnedRootHandlerContext, + parsed: BasicKiloCommand +): Promise { + const command = { + turn: { + type: 'command', + id: parsed.messageId, + command: parsed.command, + arguments: parsed.arguments, + snapshotInitialization: parsed.snapshotInitialization, + }, + ...(parsed.agent ? { agent: parsed.agent } : {}), + } satisfies QueueExecutionTurnCommand; + const request: SubmittedSessionMessageRequest = { userId: context.userId as UserId, ...command }; + const injectedAdmission = context.deps?.admitCommand; + const admit = injectedAdmission + ? () => + injectedAdmission({ + env: context.env, + userId: context.userId, + cloudAgentSessionId: context.cloudAgentSessionId, + request, + }) + : () => { + const id = context.env.CLOUD_AGENT_SESSION.idFromName( + `${context.userId}:${context.cloudAgentSessionId}` + ); + return withDORetry, SessionMessageAdmissionResult>( + () => context.env.CLOUD_AGENT_SESSION.get(id), + stub => stub.admitSubmittedMessage(request), + 'admitSubmittedMessage' + ); + }; + + try { + return await preflightAndAdmitPromptMessage( + { cloudAgentSessionId: context.cloudAgentSessionId, ...command }, + { env: context.env, userId: context.userId }, + 'kilo.session.command', + admit + ); + } catch (error) { + return commandPreflightError(error); + } +} + +export async function handleCommand(context: OwnedRootHandlerContext): Promise { + const selectorResponse = validateIdScopedSelectors(context.url, context.kiloSessionId, new Set()); + if (selectorResponse) return selectorResponse; + if (context.url.searchParams.get('directory') === null) { + return unsupportedSessionSelectorResponse(); + } + + const parsed = await validateCommandMutation(context); + if (parsed instanceof Response) return parsed; + const admission = await admitCommand(context, parsed); + if (admission instanceof Response) return admission; + if (!admission.success) return commandAdmissionError(admission); + + const now = Date.now(); + const generatedId = createMessageId(now); + const assistantMessageId = + generatedId === admission.messageId ? createMessageId(now + 1) : generatedId; + return Response.json( + buildSyntheticCommandResponse({ + now, + assistantMessageId, + admittedMessageId: admission.messageId, + kiloSessionId: context.kiloSessionId, + publicDirectory: publicCloudAgentDirectory(context.kiloSessionId), + requestedAgent: parsed.agent?.mode, + requestedModel: parsed.agent?.model, + variant: parsed.agent?.variant, + }) + ); +} diff --git a/services/cloud-agent-next/src/kilo-facade/handlers/global-event/handler.ts b/services/cloud-agent-next/src/kilo-facade/handlers/global-event/handler.ts new file mode 100644 index 0000000000..479e3a30da --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/handlers/global-event/handler.ts @@ -0,0 +1,18 @@ +import type { KiloFacadeHandlerContext } from '../../contracts.js'; +import { facadeError, unsupportedRouteResponse } from '../../http-contract.js'; + +export function handleGlobalEvent(context: KiloFacadeHandlerContext): Response { + if (context.request.method !== 'GET') return unsupportedRouteResponse(); + if (context.url.search.length > 0) { + return facadeError( + 400, + 'KILO_EVENT_SELECTOR_UNSUPPORTED', + 'Global event selectors are not supported' + ); + } + const globalEvents = context.deps?.globalEvents; + if (!globalEvents) { + return facadeError(500, 'KILO_FACADE_UNAVAILABLE', 'Kilo facade stream is unavailable'); + } + return globalEvents.openPublicGlobalEventStream(context.userId); +} diff --git a/services/cloud-agent-next/src/kilo-facade/handlers/global-feed-upgrade/handler.ts b/services/cloud-agent-next/src/kilo-facade/handlers/global-feed-upgrade/handler.ts new file mode 100644 index 0000000000..e0f8b84efa --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/handlers/global-feed-upgrade/handler.ts @@ -0,0 +1,103 @@ +import type { SessionStatus } from '../../../shared/protocol.js'; +import { hasDuplicateQueryParameters } from '../../../shared/http-query.js'; +import { facadeError } from '../../http-contract.js'; + +export type GlobalFeedSource = { + userId: string; + cloudAgentSessionId: string; + kiloSessionId: string; + wrapperRunId: string; + wrapperGeneration: number; + wrapperConnectionId: string; + sessionStatus?: SessionStatus; +}; + +type ProducerValidation = { success: true } | { success: false; status: number; message: string }; + +function parseGlobalFeedSource(request: Request): GlobalFeedSource | Response { + const url = new URL(request.url); + if (hasDuplicateQueryParameters(url.searchParams)) { + return facadeError(400, 'INVALID_GLOBAL_FEED_SOURCE', 'Invalid global feed source'); + } + const userId = url.searchParams.get('userId'); + const cloudAgentSessionId = url.searchParams.get('cloudAgentSessionId'); + const kiloSessionId = url.searchParams.get('kiloSessionId'); + const wrapperRunId = url.searchParams.get('wrapperRunId'); + const wrapperGenerationParam = url.searchParams.get('wrapperGeneration'); + const wrapperConnectionId = url.searchParams.get('wrapperConnectionId'); + const wrapperGeneration = wrapperGenerationParam ? Number(wrapperGenerationParam) : NaN; + if ( + !userId || + !cloudAgentSessionId || + !kiloSessionId || + !wrapperRunId || + !Number.isInteger(wrapperGeneration) || + wrapperGeneration < 0 || + !wrapperConnectionId + ) { + return facadeError(400, 'INVALID_GLOBAL_FEED_SOURCE', 'Invalid global feed source'); + } + return { + userId, + cloudAgentSessionId, + kiloSessionId, + wrapperRunId, + wrapperGeneration, + wrapperConnectionId, + }; +} + +function producerTag(source: Pick): string { + return `kilo-global:${source.cloudAgentSessionId}`; +} + +function isSameProducer(left: GlobalFeedSource, right: GlobalFeedSource): boolean { + return ( + left.wrapperRunId === right.wrapperRunId && + left.wrapperGeneration === right.wrapperGeneration && + left.wrapperConnectionId === right.wrapperConnectionId + ); +} + +function mayReplaceProducer(existing: GlobalFeedSource, candidate: GlobalFeedSource): boolean { + if (candidate.wrapperGeneration > existing.wrapperGeneration) return true; + if (candidate.wrapperGeneration < existing.wrapperGeneration) return false; + return isSameProducer(existing, candidate); +} + +export async function handleGlobalFeedUpgrade(params: { + request: Request; + ctx: DurableObjectState; + validateProducer: (source: GlobalFeedSource) => Promise; +}): Promise { + if (params.request.headers.get('Upgrade')?.toLowerCase() !== 'websocket') { + return facadeError(426, 'WEBSOCKET_REQUIRED', 'Expected WebSocket upgrade'); + } + const source = parseGlobalFeedSource(params.request); + if (source instanceof Response) return source; + const validation = await params.validateProducer(source); + if (!validation.success) return new Response(validation.message, { status: validation.status }); + + const tag = producerTag(source); + const existingSockets = params.ctx.getWebSockets(tag); + for (const existing of existingSockets) { + const existingSource = existing.deserializeAttachment() as GlobalFeedSource | null; + if (existingSource && !mayReplaceProducer(existingSource, source)) { + return new Response('A newer global feed producer is already connected', { status: 409 }); + } + } + for (const existing of existingSockets) { + try { + existing.close(1000, 'Replaced by newer global feed'); + } catch { + // Ignore already-closed producer sockets. + } + } + + const pair = new WebSocketPair(); + const client = pair[0]; + const server = pair[1]; + params.ctx.acceptWebSocket(server, [tag]); + server.serializeAttachment(source); + return new Response(null, { status: 101, webSocket: client }); +} diff --git a/services/cloud-agent-next/src/kilo-facade/handlers/global-feed-upgrade/message.ts b/services/cloud-agent-next/src/kilo-facade/handlers/global-feed-upgrade/message.ts new file mode 100644 index 0000000000..7b2a70a65c --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/handlers/global-feed-upgrade/message.ts @@ -0,0 +1,105 @@ +import { isPublicCloudAgentExtensionSourceType } from '../../cloud-agent-extension-events.js'; +import { isRecord } from '../../http-contract.js'; +import { publicCloudAgentDirectory } from '../../public-sdk-projection.js'; +import { sessionStatusUpdateFromEvent } from '../session-status/tracking.js'; +import type { GlobalFeedSource } from './handler.js'; + +export type KiloEventPayload = Record & { + id?: string; + type: string; + properties: Record; +}; + +export type KiloGlobalEventEnvelope = Record & { + directory?: string; + project?: unknown; + workspace?: unknown; + payload?: KiloEventPayload; +}; + +export type GlobalFeedMessageResult = + | { kind: 'send-error'; error: string } + | { kind: 'close'; code: number; reason: string } + | { kind: 'drop' } + | { + kind: 'broadcast'; + event: KiloGlobalEventEnvelope; + kiloSessionId: string; + nextSource?: GlobalFeedSource; + }; + +function isKiloEventPayload(value: unknown): value is KiloEventPayload { + return isRecord(value) && typeof value.type === 'string' && isRecord(value.properties); +} + +function isKiloGlobalEventEnvelope(value: unknown): value is KiloGlobalEventEnvelope { + return isRecord(value) && isKiloEventPayload(value.payload); +} + +function isSubstantiveKiloGlobalEventEnvelope( + value: unknown +): value is KiloGlobalEventEnvelope & { directory: string; payload: KiloEventPayload } { + return ( + isKiloGlobalEventEnvelope(value) && + typeof value.directory === 'string' && + value.directory.length > 0 + ); +} + +export function isSyntheticGlobalEvent(event: KiloGlobalEventEnvelope): boolean { + const type = event.payload?.type; + return type === 'server.connected' || type === 'server.heartbeat'; +} + +export function rewriteGlobalEventDirectory( + event: KiloGlobalEventEnvelope, + kiloSessionId: string +): KiloGlobalEventEnvelope { + return { ...event, directory: publicCloudAgentDirectory(kiloSessionId) }; +} + +export function interpretGlobalFeedMessage( + source: GlobalFeedSource | null, + message: string | ArrayBuffer +): GlobalFeedMessageResult { + if (typeof message !== 'string') { + return { kind: 'send-error', error: 'Binary global feed messages are not supported' }; + } + if (!source) return { kind: 'close', code: 1011, reason: 'Missing global feed source' }; + + let parsed: unknown; + try { + parsed = JSON.parse(message); + } catch { + return { kind: 'send-error', error: 'Invalid global feed JSON' }; + } + if (!isKiloGlobalEventEnvelope(parsed)) { + return { kind: 'send-error', error: 'Invalid global event envelope' }; + } + if (isSyntheticGlobalEvent(parsed)) return { kind: 'drop' }; + if (!isSubstantiveKiloGlobalEventEnvelope(parsed)) { + return { kind: 'send-error', error: 'Invalid global event envelope' }; + } + if (isPublicCloudAgentExtensionSourceType(parsed.payload.type)) return { kind: 'drop' }; + if (parsed.payload.properties.sessionID !== source.kiloSessionId) return { kind: 'drop' }; + + const statusUpdate = sessionStatusUpdateFromEvent(parsed.payload, source.kiloSessionId); + if ( + (parsed.payload.type === 'session.status' || parsed.payload.type === 'session.idle') && + !statusUpdate + ) { + return { kind: 'drop' }; + } + let nextSource: GlobalFeedSource | undefined; + if (statusUpdate) { + nextSource = { ...source }; + if (statusUpdate.kind === 'set') nextSource.sessionStatus = statusUpdate.status; + else delete nextSource.sessionStatus; + } + return { + kind: 'broadcast', + event: rewriteGlobalEventDirectory(parsed, source.kiloSessionId), + kiloSessionId: source.kiloSessionId, + ...(nextSource ? { nextSource } : {}), + }; +} diff --git a/services/cloud-agent-next/src/kilo-facade/handlers/permission/handler.test.ts b/services/cloud-agent-next/src/kilo-facade/handlers/permission/handler.test.ts new file mode 100644 index 0000000000..28fae8cfdf --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/handlers/permission/handler.test.ts @@ -0,0 +1,337 @@ +import { describe, expect, it, vi } from 'vitest'; +import type { KiloFacadeHandlerContext } from '../../contracts.js'; +import { handlePermission, parsePermissionRoute } from './handler.js'; + +vi.mock('@cloudflare/sandbox', () => ({ + getSandbox: vi.fn(), + Sandbox: class Sandbox {}, +})); + +const rootSessionID = 'ses_12345678901234567890123456'; +const childSessionID = 'ses_22222222222222222222222222'; +const foreignSessionID = 'ses_33333333333333333333333333'; + +function permissionContext(params: { + method?: string; + path?: string; + body?: string; + directory?: string | null; + responses: Response[]; + containerFetch?: ReturnType; +}): { context: KiloFacadeHandlerContext; containerFetch: ReturnType } { + const path = params.path ?? '/permission'; + const directory = + params.directory === undefined ? `/cloud-agent/sessions/${rootSessionID}` : params.directory; + const requestUrl = new URL(`https://facade.test/kilo${path}`); + if (directory !== null) requestUrl.searchParams.set('directory', directory); + const request = new Request(requestUrl, { + method: params.method ?? 'GET', + ...(params.body !== undefined + ? { body: params.body, headers: { 'content-type': 'application/json' } } + : {}), + }); + const containerFetch = params.containerFetch ?? vi.fn(); + for (const response of params.responses) containerFetch.mockResolvedValueOnce(response); + return { + context: { + request, + env: {} as KiloFacadeHandlerContext['env'], + userId: 'user-1', + url: new URL(request.url), + kiloPath: path, + deps: { + resolveRootSessionForKiloSession: vi + .fn() + .mockResolvedValue({ cloudAgentSessionId: 'cloud-1' }), + resolveLiveWrapper: vi.fn().mockResolvedValue({ sandbox: { containerFetch }, port: 4312 }), + }, + }, + containerFetch, + }; +} + +const rootPermission = { + id: 'per_root', + sessionID: rootSessionID, + permission: 'bash', + patterns: ['git status', 'git diff *'], + metadata: { command: 'git status' }, + always: ['git status'], + tool: { messageID: 'message-root', callID: 'call-root' }, + nativeExtra: true, +}; +const childPermission = { + id: 'per_child', + sessionID: childSessionID, + permission: 'read', + patterns: ['/workspace/*'], + metadata: {}, + always: [], +}; +const foreignPermission = { + id: 'per_foreign', + sessionID: foreignSessionID, + permission: 'edit', + patterns: ['/workspace/file'], + metadata: {}, + always: [], +}; + +describe('permission handler', () => { + it('recognizes only the allowlisted permission routes', () => { + expect(parsePermissionRoute('GET', '/permission')).toEqual({ kind: 'list' }); + expect(parsePermissionRoute('POST', '/permission/request-1/reply')).toEqual({ + kind: 'reply', + requestID: 'request-1', + }); + expect(parsePermissionRoute('POST', '/permission/request-1/always-rules')).toEqual({ + kind: 'always-rules', + requestID: 'request-1', + }); + expect(parsePermissionRoute('POST', '/permission/request%201/reply')).toEqual({ + kind: 'reply', + requestID: 'request 1', + }); + expect(parsePermissionRoute('DELETE', '/permission/request-1/reply')).toBeNull(); + expect(parsePermissionRoute('POST', '/permission/request-1/reject')).toBeNull(); + expect(parsePermissionRoute('POST', '/permission/%FF/reply')).toBeNull(); + }); + + it('preserves valid native permission entries while filtering non-root permissions', async () => { + const { context, containerFetch } = permissionContext({ + responses: [Response.json([rootPermission, childPermission, foreignPermission])], + }); + + const response = await handlePermission(context); + + expect(response.status).toBe(200); + await expect(response.json()).resolves.toEqual([rootPermission]); + const request = containerFetch.mock.calls[0]?.[0]; + expect(request).toBeInstanceOf(Request); + expect(new URL(request.url).pathname).toBe('/kilo-proxy/permission'); + expect(new URL(request.url).search).toBe(''); + }); + + it('requires a selected owned root before listing permissions', async () => { + const { context, containerFetch } = permissionContext({ directory: null, responses: [] }); + + const response = await handlePermission(context); + + expect(response.status).toBe(400); + await expect(response.json()).resolves.toMatchObject({ + error: 'KILO_SESSION_SELECTOR_UNSUPPORTED', + }); + expect(containerFetch).not.toHaveBeenCalled(); + }); + + it('rejects malformed native permission lists', async () => { + const malformedEntries = [ + { ...rootPermission, id: 'wrong-permission-root' }, + { ...rootPermission, patterns: ['valid', 1] }, + { ...rootPermission, metadata: [] }, + { ...rootPermission, always: [false] }, + { ...rootPermission, tool: { messageID: 'message-root' } }, + ]; + + for (const malformed of malformedEntries) { + const { context } = permissionContext({ responses: [Response.json([malformed])] }); + const response = await handlePermission(context); + expect(response.status).toBe(502); + await expect(response.json()).resolves.toMatchObject({ + error: 'KILO_UPSTREAM_RESPONSE_INVALID', + }); + } + }); + + it('preflights and forwards every native permission reply with its native response', async () => { + const bodies = [ + { reply: 'once' }, + { reply: 'always' }, + { reply: 'reject', message: 'Use the existing file instead' }, + ]; + + for (const body of bodies) { + const nativeResponse = new Response(JSON.stringify({ handled: body.reply }), { + status: 202, + headers: { 'content-type': 'application/json', 'x-native': 'preserved' }, + }); + const { context, containerFetch } = permissionContext({ + method: 'POST', + path: '/permission/per_root/reply', + body: JSON.stringify(body), + responses: [Response.json([rootPermission]), nativeResponse], + }); + + const response = await handlePermission(context); + + expect(response.status).toBe(202); + expect(response.headers.get('x-native')).toBe('preserved'); + await expect(response.json()).resolves.toEqual({ handled: body.reply }); + expect(containerFetch).toHaveBeenCalledTimes(2); + const mutationRequest = containerFetch.mock.calls[1]?.[0]; + expect(new URL(mutationRequest.url).pathname).toBe('/kilo-proxy/permission/per_root/reply'); + await expect(mutationRequest.json()).resolves.toEqual(body); + } + }); + + it('rejects malformed reply bodies before resolving or mutating a wrapper', async () => { + const malformedBodies = [ + {}, + { reply: 'later' }, + { reply: 'once', message: 1 }, + { reply: 'once', extra: true }, + ]; + + for (const body of malformedBodies) { + const { context, containerFetch } = permissionContext({ + method: 'POST', + path: '/permission/per_root/reply', + body: JSON.stringify(body), + responses: [], + }); + const response = await handlePermission(context); + expect(response.status).toBe(400); + await expect(response.json()).resolves.toMatchObject({ + error: 'KILO_INTERACTION_BODY_INVALID', + }); + expect(containerFetch).not.toHaveBeenCalled(); + } + }); + + it('forwards always rules followed by a compatible reply against the same pending request', async () => { + const alwaysRulesBody = { approvedAlways: ['git status'], deniedAlways: ['git push *'] }; + const containerFetch = vi + .fn() + .mockResolvedValueOnce(Response.json([rootPermission])) + .mockResolvedValueOnce(Response.json(true)) + .mockResolvedValueOnce(Response.json([rootPermission])) + .mockResolvedValueOnce(Response.json(true)); + const alwaysRules = permissionContext({ + method: 'POST', + path: '/permission/per_root/always-rules', + body: JSON.stringify(alwaysRulesBody), + responses: [], + containerFetch, + }); + + const alwaysRulesResponse = await handlePermission(alwaysRules.context); + + expect(alwaysRulesResponse.status).toBe(200); + await expect(alwaysRulesResponse.json()).resolves.toBe(true); + const alwaysRulesRequest = containerFetch.mock.calls[1]?.[0]; + expect(new URL(alwaysRulesRequest.url).pathname).toBe( + '/kilo-proxy/permission/per_root/always-rules' + ); + await expect(alwaysRulesRequest.json()).resolves.toEqual(alwaysRulesBody); + + const reply = permissionContext({ + method: 'POST', + path: '/permission/per_root/reply', + body: JSON.stringify({ reply: 'always' }), + responses: [], + containerFetch, + }); + const replyResponse = await handlePermission(reply.context); + + expect(replyResponse.status).toBe(200); + await expect(replyResponse.json()).resolves.toBe(true); + const replyRequest = containerFetch.mock.calls[3]?.[0]; + expect(new URL(replyRequest.url).pathname).toBe('/kilo-proxy/permission/per_root/reply'); + await expect(replyRequest.json()).resolves.toEqual({ reply: 'always' }); + }); + + it('rejects an omitted always-rules body before resolving or mutating a wrapper', async () => { + const { context, containerFetch } = permissionContext({ + method: 'POST', + path: '/permission/per_root/always-rules', + responses: [], + }); + + const response = await handlePermission(context); + + expect(response.status).toBe(400); + await expect(response.json()).resolves.toMatchObject({ + error: 'KILO_INTERACTION_BODY_INVALID', + }); + expect(containerFetch).not.toHaveBeenCalled(); + }); + + it('rejects malformed always-rules bodies before resolving or mutating a wrapper', async () => { + const malformedBodies = [ + { approvedAlways: ['git status', 1] }, + { deniedAlways: 'git push *' }, + { approvedAlways: [], extra: true }, + ]; + + for (const body of malformedBodies) { + const { context, containerFetch } = permissionContext({ + method: 'POST', + path: '/permission/per_root/always-rules', + body: JSON.stringify(body), + responses: [], + }); + const response = await handlePermission(context); + expect(response.status).toBe(400); + await expect(response.json()).resolves.toMatchObject({ + error: 'KILO_INTERACTION_BODY_INVALID', + }); + expect(containerFetch).not.toHaveBeenCalled(); + } + }); + + it('accepts a native no-underscore ID and uses it for same-root mutation preflight', async () => { + const nativePermission = { ...rootPermission, id: 'permission-root' }; + const { context, containerFetch } = permissionContext({ + method: 'POST', + path: '/permission/permission-root/reply', + body: JSON.stringify({ reply: 'once' }), + responses: [Response.json([nativePermission]), Response.json(true)], + }); + + const response = await handlePermission(context); + + expect(response.status).toBe(200); + await expect(response.json()).resolves.toBe(true); + expect(containerFetch).toHaveBeenCalledTimes(2); + expect(new URL(containerFetch.mock.calls[1]?.[0].url).pathname).toBe( + '/kilo-proxy/permission/permission-root/reply' + ); + }); + + it('does not authorize a mutation from a malformed upstream permission ID', async () => { + const malformedPermission = { ...rootPermission, id: 'wrong-permission-root' }; + const { context, containerFetch } = permissionContext({ + method: 'POST', + path: '/permission/permission-root/reply', + body: JSON.stringify({ reply: 'once' }), + responses: [Response.json([malformedPermission])], + }); + + const response = await handlePermission(context); + + expect(response.status).toBe(502); + await expect(response.json()).resolves.toMatchObject({ + error: 'KILO_UPSTREAM_RESPONSE_INVALID', + }); + expect(containerFetch).toHaveBeenCalledTimes(1); + }); + + it('returns facade 404 without mutation for stale, child, and foreign permission IDs', async () => { + for (const requestID of ['per_stale', 'per_child', 'per_foreign']) { + const { context, containerFetch } = permissionContext({ + method: 'POST', + path: `/permission/${requestID}/reply`, + body: JSON.stringify({ reply: 'reject' }), + responses: [Response.json([childPermission, foreignPermission])], + }); + + const response = await handlePermission(context); + + expect(response.status).toBe(404); + await expect(response.json()).resolves.toMatchObject({ + error: 'KILO_INTERACTION_REQUEST_NOT_FOUND', + }); + expect(containerFetch).toHaveBeenCalledTimes(1); + } + }); +}); diff --git a/services/cloud-agent-next/src/kilo-facade/handlers/permission/handler.ts b/services/cloud-agent-next/src/kilo-facade/handlers/permission/handler.ts new file mode 100644 index 0000000000..5d817756b5 --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/handlers/permission/handler.ts @@ -0,0 +1,120 @@ +import type { KiloFacadeHandlerContext } from '../../contracts.js'; +import { isRecord } from '../../http-contract.js'; +import { + openSelectedRootLiveInteraction, + readInteractionRequestBody, +} from '../../live-interaction.js'; + +export type PermissionRequest = Record & { + id: string; + sessionID: string; + permission: string; + patterns: string[]; + metadata: Record; + always: string[]; + tool?: { messageID: string; callID: string }; +}; + +export type PermissionRoute = + | { kind: 'list' } + | { kind: 'reply'; requestID: string } + | { kind: 'always-rules'; requestID: string }; + +export function parsePermissionRoute(method: string, kiloPath: string): PermissionRoute | null { + if (method === 'GET' && kiloPath === '/permission') return { kind: 'list' }; + const match = /^\/permission\/([^/]+)\/(reply|always-rules)$/.exec(kiloPath); + if (!match?.[1] || !match[2] || method !== 'POST') return null; + let requestID: string; + try { + requestID = decodeURIComponent(match[1]); + } catch { + return null; + } + if (!requestID) return null; + return match[2] === 'reply' ? { kind: 'reply', requestID } : { kind: 'always-rules', requestID }; +} + +function isStringArray(value: unknown): value is string[] { + return Array.isArray(value) && value.every(item => typeof item === 'string'); +} + +function isPermissionTool(value: unknown): value is { messageID: string; callID: string } { + return isRecord(value) && typeof value.messageID === 'string' && typeof value.callID === 'string'; +} + +export function isPermissionRequest(value: unknown): value is PermissionRequest { + return ( + isRecord(value) && + typeof value.id === 'string' && + value.id.startsWith('per') && + typeof value.sessionID === 'string' && + typeof value.permission === 'string' && + isStringArray(value.patterns) && + isRecord(value.metadata) && + !Array.isArray(value.metadata) && + isStringArray(value.always) && + (value.tool === undefined || isPermissionTool(value.tool)) + ); +} + +function hasOnlyKeys(value: Record, allowedKeys: Set): boolean { + return Object.keys(value).every(key => allowedKeys.has(key)); +} + +function isPermissionReplyBody(value: unknown): boolean { + return ( + isRecord(value) && + hasOnlyKeys(value, new Set(['reply', 'message'])) && + (value.reply === 'once' || value.reply === 'always' || value.reply === 'reject') && + (value.message === undefined || typeof value.message === 'string') + ); +} + +function isPermissionAlwaysRulesBody(value: unknown): boolean { + return ( + isRecord(value) && + hasOnlyKeys(value, new Set(['approvedAlways', 'deniedAlways'])) && + (value.approvedAlways === undefined || isStringArray(value.approvedAlways)) && + (value.deniedAlways === undefined || isStringArray(value.deniedAlways)) + ); +} + +export async function handlePermission( + context: KiloFacadeHandlerContext, + route = parsePermissionRoute(context.request.method, context.kiloPath) +): Promise { + if (!route) throw new Error('Permission handler called for unsupported route'); + + let body: Uint8Array | undefined; + if (route.kind === 'reply') { + const parsedBody = await readInteractionRequestBody(context.request, isPermissionReplyBody); + if (parsedBody instanceof Response) return parsedBody; + body = parsedBody; + } else if (route.kind === 'always-rules') { + const parsedBody = await readInteractionRequestBody( + context.request, + isPermissionAlwaysRulesBody + ); + if (parsedBody instanceof Response) return parsedBody; + body = parsedBody; + } + + const interaction = await openSelectedRootLiveInteraction(context); + if (interaction instanceof Response) return interaction; + + if (route.kind === 'list') { + const permissions = await interaction.list({ + path: '/permission', + isEntry: isPermissionRequest, + }); + return permissions instanceof Response ? permissions : Response.json(permissions); + } + + return interaction.mutate({ + listPath: '/permission', + mutationPath: `/permission/${encodeURIComponent(route.requestID)}/${route.kind === 'reply' ? 'reply' : 'always-rules'}`, + requestID: route.requestID, + isEntry: isPermissionRequest, + ...(body ? { body } : {}), + }); +} diff --git a/services/cloud-agent-next/src/kilo-facade/handlers/prompt-async/handler.ts b/services/cloud-agent-next/src/kilo-facade/handlers/prompt-async/handler.ts new file mode 100644 index 0000000000..3cb5c8fe22 --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/handlers/prompt-async/handler.ts @@ -0,0 +1,186 @@ +import { TRPCError } from '@trpc/server'; +import { fetchOrgIdForSession, validateBalanceOnly } from '../../../balance-validation.js'; +import type { + QueueExecutionTurnCommand, + SessionMessageAdmissionResult, + SubmittedSessionMessageRequest, +} from '../../../execution/types.js'; +import type { CloudAgentSession } from '../../../persistence/CloudAgentSession.js'; +import { preflightAndAdmitPromptMessage } from '../../../session/queue-message.js'; +import type { UserId } from '../../../types/ids.js'; +import { withDORetry } from '../../../utils/do-retry.js'; +import { parseBasicKiloPrompt } from '../../basic-prompt.js'; +import type { OwnedRootHandlerContext } from '../../contracts.js'; +import { + facadeError, + missingRootKiloSessionResponse, + readBoundedBody, + validateIdScopedSelectors, +} from '../../http-contract.js'; + +const MAX_KILO_PROMPT_JSON_BYTES = 256 * 1024; + +type ReadRequestJsonResult = + | { success: true; value: unknown } + | { success: false; response: Response }; + +async function readRequestJson(request: Request): Promise { + const declaredLength = request.headers.get('content-length'); + if (declaredLength !== null) { + const bodyBytes = Number(declaredLength); + if (!Number.isSafeInteger(bodyBytes) || bodyBytes > MAX_KILO_PROMPT_JSON_BYTES) { + return { + success: false, + response: facadeError( + 400, + 'KILO_BASIC_PROMPT_UNSUPPORTED', + 'Basic Kilo prompt body is not supported' + ), + }; + } + } + const bytes = await readBoundedBody(new Response(request.body), MAX_KILO_PROMPT_JSON_BYTES); + if (!bytes) { + return { + success: false, + response: facadeError( + 400, + 'KILO_BASIC_PROMPT_UNSUPPORTED', + 'Basic Kilo prompt body is not supported' + ), + }; + } + try { + return { success: true, value: JSON.parse(new TextDecoder().decode(bytes)) }; + } catch { + return { + success: false, + response: facadeError( + 400, + 'KILO_BASIC_PROMPT_UNSUPPORTED', + 'Basic Kilo prompt body is not supported' + ), + }; + } +} + +function promptAdmissionError( + result: Extract +): Response { + switch (result.code) { + case 'BAD_REQUEST': + return facadeError(400, 'KILO_PROMPT_ADMISSION_REJECTED', result.error); + case 'NOT_FOUND': + return missingRootKiloSessionResponse(); + case 'PENDING_QUEUE_FULL': + return facadeError(429, 'KILO_PROMPT_QUEUE_FULL', result.error); + case 'SANDBOX_CONNECT_FAILED': + case 'WORKSPACE_SETUP_FAILED': + case 'KILO_SERVER_FAILED': + case 'WRAPPER_START_FAILED': + case 'WRAPPER_FINALIZING': + return facadeError(503, result.code, result.error); + case 'INTERNAL': + return facadeError(500, 'KILO_PROMPT_ADMISSION_FAILED', result.error); + } +} + +function promptPreflightError(error: unknown): Response { + if (!(error instanceof TRPCError)) throw error; + switch (error.code) { + case 'BAD_REQUEST': + return facadeError(400, 'KILO_PROMPT_ADMISSION_REJECTED', error.message); + case 'NOT_FOUND': + return missingRootKiloSessionResponse(); + case 'FORBIDDEN': + return facadeError(403, 'KILO_PROMPT_ADMISSION_REJECTED', error.message); + case 'SERVICE_UNAVAILABLE': + return facadeError(503, 'MODEL_VALIDATION_UNAVAILABLE', error.message); + default: + throw error; + } +} + +async function admitBasicPrompt( + context: OwnedRootHandlerContext +): Promise { + const body = await readRequestJson(context.request); + if (!body.success) return body.response; + const parsed = parseBasicKiloPrompt(body.value); + if (!parsed.success) { + return facadeError( + 400, + 'KILO_BASIC_PROMPT_UNSUPPORTED', + 'Basic Kilo prompt body is not supported' + ); + } + if (!context.authToken) { + return facadeError(500, 'KILO_FACADE_UNAVAILABLE', 'Durable prompt admission is unavailable'); + } + if (context.request.headers.get('x-skip-balance-check') !== null) { + return facadeError( + 400, + 'KILO_BALANCE_BYPASS_UNSUPPORTED', + 'Balance bypass is not supported for public Kilo prompt mutations' + ); + } + const balance = context.deps?.validatePromptBalance + ? await context.deps.validatePromptBalance({ + env: context.env, + authToken: context.authToken, + userId: context.userId, + cloudAgentSessionId: context.cloudAgentSessionId, + }) + : await validateBalanceOnly( + context.authToken, + await fetchOrgIdForSession(context.env, context.userId, context.cloudAgentSessionId), + context.env + ); + if (!balance.success) { + return facadeError(balance.status, 'KILO_BALANCE_VALIDATION_FAILED', balance.message); + } + + const command = { + turn: { type: 'prompt', id: parsed.prompt.messageId, prompt: parsed.prompt.prompt }, + ...(parsed.prompt.agent ? { agent: parsed.prompt.agent } : {}), + } satisfies QueueExecutionTurnCommand; + const request: SubmittedSessionMessageRequest = { userId: context.userId as UserId, ...command }; + const injectedAdmitPrompt = context.deps?.admitPrompt; + const admitPrompt = injectedAdmitPrompt + ? () => + injectedAdmitPrompt({ + env: context.env, + userId: context.userId, + cloudAgentSessionId: context.cloudAgentSessionId, + request, + }) + : () => { + const id = context.env.CLOUD_AGENT_SESSION.idFromName( + `${context.userId}:${context.cloudAgentSessionId}` + ); + return withDORetry, SessionMessageAdmissionResult>( + () => context.env.CLOUD_AGENT_SESSION.get(id), + stub => stub.admitSubmittedMessage(request), + 'admitSubmittedMessage' + ); + }; + try { + return await preflightAndAdmitPromptMessage( + { cloudAgentSessionId: context.cloudAgentSessionId, ...command }, + { env: context.env, userId: context.userId }, + 'kilo.prompt_async', + admitPrompt + ); + } catch (error) { + return promptPreflightError(error); + } +} + +export async function handlePromptAsync(context: OwnedRootHandlerContext): Promise { + const selectorResponse = validateIdScopedSelectors(context.url, context.kiloSessionId, new Set()); + if (selectorResponse) return selectorResponse; + const admission = await admitBasicPrompt(context); + if (admission instanceof Response) return admission; + if (!admission.success) return promptAdmissionError(admission); + return new Response(null, { status: 204 }); +} diff --git a/services/cloud-agent-next/src/kilo-facade/handlers/question/handler.test.ts b/services/cloud-agent-next/src/kilo-facade/handlers/question/handler.test.ts new file mode 100644 index 0000000000..8da2e6ef45 --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/handlers/question/handler.test.ts @@ -0,0 +1,219 @@ +import { describe, expect, it, vi } from 'vitest'; +import type { KiloFacadeHandlerContext } from '../../contracts.js'; +import { handleQuestion, parseQuestionRoute } from './handler.js'; + +vi.mock('@cloudflare/sandbox', () => ({ + getSandbox: vi.fn(), + Sandbox: class Sandbox {}, +})); + +const rootSessionID = 'ses_12345678901234567890123456'; +const childSessionID = 'ses_22222222222222222222222222'; + +function questionContext(params: { + method?: string; + path?: string; + body?: string; + responses: Response[]; +}): { context: KiloFacadeHandlerContext; containerFetch: ReturnType } { + const path = params.path ?? '/question'; + const request = new Request( + `https://facade.test/kilo${path}?directory=/cloud-agent/sessions/${rootSessionID}`, + { + method: params.method ?? 'GET', + ...(params.body !== undefined + ? { body: params.body, headers: { 'content-type': 'application/json' } } + : {}), + } + ); + const containerFetch = vi.fn(); + for (const response of params.responses) containerFetch.mockResolvedValueOnce(response); + return { + context: { + request, + env: {} as KiloFacadeHandlerContext['env'], + userId: 'user-1', + url: new URL(request.url), + kiloPath: path, + deps: { + resolveRootSessionForKiloSession: vi + .fn() + .mockResolvedValue({ cloudAgentSessionId: 'cloud-1' }), + resolveLiveWrapper: vi.fn().mockResolvedValue({ sandbox: { containerFetch }, port: 4312 }), + }, + }, + containerFetch, + }; +} + +const rootQuestion = { + id: 'que_root', + sessionID: rootSessionID, + questions: [ + { + question: 'Proceed?', + header: 'Proceed', + options: [ + { + label: 'Yes', + description: 'Proceed with the operation', + labelKey: 'question.yes.label', + descriptionKey: 'question.yes.description', + mode: 'build', + }, + ], + multiple: false, + questionKey: 'question.proceed.question', + headerKey: 'question.proceed.header', + custom: true, + }, + ], + blocking: true, + tool: { messageID: 'msg_root', callID: 'call_root' }, +}; +const childQuestion = { + id: 'que_child', + sessionID: childSessionID, + questions: [ + { + question: 'Child?', + header: 'Child', + options: [{ label: 'Yes', description: 'Proceed with the operation' }], + }, + ], +}; + +describe('question handler', () => { + it('recognizes only the allowlisted question routes', () => { + expect(parseQuestionRoute('GET', '/question')).toEqual({ kind: 'list' }); + expect(parseQuestionRoute('POST', '/question/request-1/reply')).toEqual({ + kind: 'reply', + requestID: 'request-1', + }); + expect(parseQuestionRoute('POST', '/question/request-1/reject')).toEqual({ + kind: 'reject', + requestID: 'request-1', + }); + expect(parseQuestionRoute('DELETE', '/question/request-1/reject')).toBeNull(); + }); + + it('preserves native question entries while filtering child questions', async () => { + const { context, containerFetch } = questionContext({ + responses: [Response.json([rootQuestion, childQuestion])], + }); + const response = await handleQuestion(context); + expect(response.status).toBe(200); + await expect(response.json()).resolves.toEqual([rootQuestion]); + const request = containerFetch.mock.calls[0]?.[0]; + expect(request).toBeInstanceOf(Request); + expect(new URL(request.url).search).toBe(''); + }); + + it('rejects malformed native question lists', async () => { + const malformedEntries = [ + { ...rootQuestion, id: 'wrong-question-root' }, + { ...rootQuestion, questions: [{ question: 'Proceed?', header: 'Proceed' }] }, + { + ...rootQuestion, + questions: [ + { + question: 'Proceed?', + header: 'Proceed', + options: [{ label: 'Yes' }], + }, + ], + }, + { ...rootQuestion, questions: [{ ...rootQuestion.questions[0], multiple: 'yes' }] }, + { ...rootQuestion, blocking: 'yes' }, + { ...rootQuestion, tool: { messageID: 'msg_1' } }, + ]; + + for (const malformed of malformedEntries) { + const { context } = questionContext({ responses: [Response.json([malformed])] }); + const response = await handleQuestion(context); + expect(response.status).toBe(502); + await expect(response.json()).resolves.toMatchObject({ + error: 'KILO_UPSTREAM_RESPONSE_INVALID', + }); + } + }); + + it('preflights and forwards a valid native reply body', async () => { + const nativeResult = { accepted: true }; + const { context, containerFetch } = questionContext({ + method: 'POST', + path: '/question/que_root/reply', + body: JSON.stringify({ answers: [['Yes'], ['Other']] }), + responses: [Response.json([rootQuestion]), Response.json(nativeResult)], + }); + const response = await handleQuestion(context); + expect(response.status).toBe(200); + await expect(response.json()).resolves.toEqual(nativeResult); + expect(containerFetch).toHaveBeenCalledTimes(2); + const mutationRequest = containerFetch.mock.calls[1]?.[0]; + await expect(mutationRequest.json()).resolves.toEqual({ answers: [['Yes'], ['Other']] }); + }); + + it('accepts a native no-underscore ID and uses it for same-root mutation preflight', async () => { + const nativeQuestion = { ...rootQuestion, id: 'question-root' }; + const { context, containerFetch } = questionContext({ + method: 'POST', + path: '/question/question-root/reject', + responses: [Response.json([nativeQuestion]), Response.json(true)], + }); + + const response = await handleQuestion(context); + + expect(response.status).toBe(200); + await expect(response.json()).resolves.toBe(true); + expect(containerFetch).toHaveBeenCalledTimes(2); + expect(new URL(containerFetch.mock.calls[1]?.[0].url).pathname).toBe( + '/kilo-proxy/question/question-root/reject' + ); + }); + + it('rejects malformed reply bodies before resolving or mutating a wrapper', async () => { + const { context, containerFetch } = questionContext({ + method: 'POST', + path: '/question/que_root/reply', + body: JSON.stringify({ answers: ['Yes'] }), + responses: [], + }); + const response = await handleQuestion(context); + expect(response.status).toBe(400); + expect(containerFetch).not.toHaveBeenCalled(); + }); + + it('does not authorize a mutation from a malformed upstream question ID', async () => { + const malformedQuestion = { ...rootQuestion, id: 'wrong-question-root' }; + const { context, containerFetch } = questionContext({ + method: 'POST', + path: '/question/question-root/reject', + responses: [Response.json([malformedQuestion])], + }); + + const response = await handleQuestion(context); + + expect(response.status).toBe(502); + await expect(response.json()).resolves.toMatchObject({ + error: 'KILO_UPSTREAM_RESPONSE_INVALID', + }); + expect(containerFetch).toHaveBeenCalledTimes(1); + }); + + it('returns public 404 without mutation for stale and child question IDs', async () => { + for (const requestID of ['que_stale', 'que_child']) { + const { context, containerFetch } = questionContext({ + method: 'POST', + path: `/question/${requestID}/reject`, + responses: [Response.json([childQuestion])], + }); + const response = await handleQuestion(context); + expect(response.status).toBe(404); + await expect(response.json()).resolves.toMatchObject({ + error: 'KILO_INTERACTION_REQUEST_NOT_FOUND', + }); + expect(containerFetch).toHaveBeenCalledTimes(1); + } + }); +}); diff --git a/services/cloud-agent-next/src/kilo-facade/handlers/question/handler.ts b/services/cloud-agent-next/src/kilo-facade/handlers/question/handler.ts new file mode 100644 index 0000000000..0ef41cdb27 --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/handlers/question/handler.ts @@ -0,0 +1,150 @@ +import type { KiloFacadeHandlerContext } from '../../contracts.js'; +import { isRecord } from '../../http-contract.js'; +import { + openSelectedRootLiveInteraction, + readInteractionRequestBody, +} from '../../live-interaction.js'; + +export type QuestionOption = Record & { + label: string; + description: string; + labelKey?: string; + descriptionKey?: string; + mode?: string; +}; + +export type QuestionInfo = Record & { + question: string; + header: string; + options: QuestionOption[]; + multiple?: boolean; + questionKey?: string; + headerKey?: string; + custom?: boolean; +}; + +export type QuestionRequest = Record & { + id: string; + sessionID: string; + questions: QuestionInfo[]; + blocking?: boolean; + tool?: { messageID: string; callID: string }; +}; + +export type QuestionRoute = + | { kind: 'list' } + | { kind: 'reply'; requestID: string } + | { kind: 'reject'; requestID: string }; + +export function parseQuestionRoute(method: string, kiloPath: string): QuestionRoute | null { + if (method === 'GET' && kiloPath === '/question') return { kind: 'list' }; + const match = /^\/question\/([^/]+)\/(reply|reject)$/.exec(kiloPath); + if (!match?.[1] || !match[2] || method !== 'POST') return null; + let requestID: string; + try { + requestID = decodeURIComponent(match[1]); + } catch { + return null; + } + if (!requestID) return null; + return match[2] === 'reply' ? { kind: 'reply', requestID } : { kind: 'reject', requestID }; +} + +function isOptionalString(value: unknown): boolean { + return value === undefined || typeof value === 'string'; +} + +function isOptionalBoolean(value: unknown): boolean { + return value === undefined || typeof value === 'boolean'; +} + +function isQuestionOption(value: unknown): value is QuestionOption { + return ( + isRecord(value) && + typeof value.label === 'string' && + typeof value.description === 'string' && + isOptionalString(value.labelKey) && + isOptionalString(value.descriptionKey) && + isOptionalString(value.mode) + ); +} + +function isQuestionInfo(value: unknown): value is QuestionInfo { + return ( + isRecord(value) && + typeof value.question === 'string' && + typeof value.header === 'string' && + Array.isArray(value.options) && + value.options.every(isQuestionOption) && + isOptionalBoolean(value.multiple) && + isOptionalString(value.questionKey) && + isOptionalString(value.headerKey) && + isOptionalBoolean(value.custom) + ); +} + +function isQuestionTool(value: unknown): value is QuestionRequest['tool'] { + return isRecord(value) && typeof value.messageID === 'string' && typeof value.callID === 'string'; +} + +export function isQuestionRequest(value: unknown): value is QuestionRequest { + return ( + isRecord(value) && + typeof value.id === 'string' && + value.id.startsWith('que') && + typeof value.sessionID === 'string' && + Array.isArray(value.questions) && + value.questions.every(isQuestionInfo) && + isOptionalBoolean(value.blocking) && + (value.tool === undefined || isQuestionTool(value.tool)) + ); +} + +function isQuestionReplyBody(value: unknown): boolean { + return ( + isRecord(value) && + Object.keys(value).length === 1 && + Array.isArray(value.answers) && + value.answers.every( + answer => Array.isArray(answer) && answer.every(item => typeof item === 'string') + ) + ); +} + +export async function handleQuestion( + context: KiloFacadeHandlerContext, + route = parseQuestionRoute(context.request.method, context.kiloPath) +): Promise { + if (!route) throw new Error('Question handler called for unsupported route'); + let body: Uint8Array | undefined; + if (route.kind === 'reply') { + const parsedBody = await readInteractionRequestBody(context.request, isQuestionReplyBody); + if (parsedBody instanceof Response) return parsedBody; + body = parsedBody; + } + + const interaction = await openSelectedRootLiveInteraction(context); + if (interaction instanceof Response) return interaction; + + if (route.kind === 'list') { + const questions = await interaction.list({ path: '/question', isEntry: isQuestionRequest }); + return questions instanceof Response ? questions : Response.json(questions); + } + + if (route.kind === 'reply') { + return interaction.mutate({ + listPath: '/question', + mutationPath: `/question/${encodeURIComponent(route.requestID)}/reply`, + requestID: route.requestID, + isEntry: isQuestionRequest, + body, + }); + } + + return interaction.mutate({ + listPath: '/question', + mutationPath: `/question/${encodeURIComponent(route.requestID)}/reject`, + requestID: route.requestID, + isEntry: isQuestionRequest, + }); +} diff --git a/services/cloud-agent-next/src/kilo-facade/handlers/session-detail/handler.ts b/services/cloud-agent-next/src/kilo-facade/handlers/session-detail/handler.ts new file mode 100644 index 0000000000..53bc6a3e9c --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/handlers/session-detail/handler.ts @@ -0,0 +1,142 @@ +import type { KiloSdkSessionInfo } from '../../../session-ingest-binding.js'; +import type { CloudAgentSession } from '../../../persistence/CloudAgentSession.js'; +import { withDORetry } from '../../../utils/do-retry.js'; +import type { QueuedMessageSnapshot } from '../../../websocket/stream.js'; +import type { OwnedRootHandlerContext } from '../../contracts.js'; +import { + facadeError, + invalidPersistedSessionDataResponse, + isRecord, + missingRootKiloSessionResponse, + pendingSessionSnapshotResponse, + readBoundedBody, + retryableSessionReadResponse, + validateIdScopedSelectors, +} from '../../http-contract.js'; +import { projectPublicListedSession, projectPublicSession } from '../../public-sdk-projection.js'; +import { handleLiveFirstSessionRead } from '../../session-read-source.js'; + +const MAX_KILO_SESSION_JSON_BYTES = 8 * 1024 * 1024; + +function isKiloSdkSessionInfo(value: unknown, kiloSessionId: string): value is KiloSdkSessionInfo { + return ( + isRecord(value) && + value.id === kiloSessionId && + typeof value.slug === 'string' && + typeof value.projectID === 'string' && + typeof value.directory === 'string' && + typeof value.title === 'string' && + typeof value.version === 'string' && + isRecord(value.time) && + typeof value.time.created === 'number' && + typeof value.time.updated === 'number' + ); +} + +async function rewriteLiveSessionDetailResponse( + response: Response, + context: OwnedRootHandlerContext +): Promise { + if (response.status === 404) return persistedSessionDetailResponse(context); + if (!response.ok) return response; + const kiloSessionId = context.kiloSessionId; + const declaredLength = response.headers.get('content-length'); + if (declaredLength !== null) { + const bodyBytes = Number(declaredLength); + if (!Number.isSafeInteger(bodyBytes) || bodyBytes > MAX_KILO_SESSION_JSON_BYTES) { + return facadeError( + 502, + 'KILO_UPSTREAM_RESPONSE_INVALID', + 'Kilo session response exceeds supported size' + ); + } + } + const bytes = await readBoundedBody(response, MAX_KILO_SESSION_JSON_BYTES); + if (!bytes) { + return facadeError( + 502, + 'KILO_UPSTREAM_RESPONSE_INVALID', + 'Kilo session response exceeds supported size' + ); + } + let parsed: unknown; + try { + parsed = JSON.parse(new TextDecoder().decode(bytes)); + } catch { + return facadeError( + 502, + 'KILO_UPSTREAM_RESPONSE_INVALID', + 'Kilo session response is not valid JSON' + ); + } + if (!isKiloSdkSessionInfo(parsed, kiloSessionId)) { + return facadeError(502, 'KILO_UPSTREAM_RESPONSE_INVALID', 'Kilo session response is not valid'); + } + const headers = new Headers(response.headers); + headers.delete('content-length'); + headers.delete('content-encoding'); + headers.set('content-type', 'application/json'); + return new Response(JSON.stringify(projectPublicSession(parsed, kiloSessionId)), { + status: response.status, + statusText: response.statusText, + headers, + }); +} + +async function pendingSessionDetailResponse(context: OwnedRootHandlerContext): Promise { + const id = context.env.CLOUD_AGENT_SESSION.idFromName( + `${context.userId}:${context.cloudAgentSessionId}` + ); + const initial = await withDORetry< + DurableObjectStub, + QueuedMessageSnapshot | null + >( + () => context.env.CLOUD_AGENT_SESSION.get(id), + stub => stub.getInitialMessageSnapshot(), + 'getInitialMessageSnapshot' + ).catch(() => null); + if (!initial) return pendingSessionSnapshotResponse(); + return Response.json( + projectPublicListedSession({ + kiloSessionId: context.kiloSessionId, + cloudAgentSessionId: context.cloudAgentSessionId, + title: null, + created: initial.timestamp, + updated: initial.timestamp, + }) + ); +} + +async function persistedSessionDetailResponse(context: OwnedRootHandlerContext): Promise { + const snapshot = await context.env.SESSION_INGEST.getCloudAgentRootSessionSnapshot({ + kiloUserId: context.userId, + kiloSessionId: context.kiloSessionId, + }); + if (!snapshot) return missingRootKiloSessionResponse(); + switch (snapshot.snapshot.kind) { + case 'pending': + return pendingSessionDetailResponse(context); + case 'too_large': + return facadeError( + 413, + 'KILO_SESSION_SNAPSHOT_TOO_LARGE', + 'Persisted Kilo session snapshot exceeds the safe cold-read budget' + ); + case 'retryable_failure': + return retryableSessionReadResponse(); + case 'invalid_data': + return invalidPersistedSessionDataResponse('session'); + case 'value': + return Response.json(projectPublicSession(snapshot.snapshot.info, snapshot.kiloSessionId)); + } +} + +export async function handleSessionDetail(context: OwnedRootHandlerContext): Promise { + const selectorResponse = validateIdScopedSelectors(context.url, context.kiloSessionId, new Set()); + if (selectorResponse) return selectorResponse; + return handleLiveFirstSessionRead({ + context, + persistedFallback: () => persistedSessionDetailResponse(context), + rewriteLiveResponse: response => rewriteLiveSessionDetailResponse(response, context), + }); +} diff --git a/services/cloud-agent-next/src/kilo-facade/handlers/session-event/handler.ts b/services/cloud-agent-next/src/kilo-facade/handlers/session-event/handler.ts new file mode 100644 index 0000000000..8ec625168d --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/handlers/session-event/handler.ts @@ -0,0 +1,49 @@ +import { hasDuplicateQueryParameters } from '../../../shared/http-query.js'; +import type { KiloFacadeHandlerContext } from '../../contracts.js'; +import { + duplicateQueryParametersResponse, + facadeError, + isSessionIngestKiloSessionId, + missingRootKiloSessionResponse, + parsePublicSessionDirectory, + unsupportedRouteResponse, +} from '../../http-contract.js'; +import { resolveRootSessionForKiloSession } from '../../ownership.js'; + +export async function handleSessionEvent(context: KiloFacadeHandlerContext): Promise { + if (context.request.method !== 'GET') return unsupportedRouteResponse(); + if ([...context.url.searchParams.keys()].some(key => key !== 'directory')) { + return facadeError( + 400, + 'KILO_EVENT_SELECTOR_UNSUPPORTED', + 'Only the Cloud Agent session directory selector is supported' + ); + } + if (hasDuplicateQueryParameters(context.url.searchParams)) { + return duplicateQueryParametersResponse(); + } + const directory = context.url.searchParams.get('directory'); + if (!directory) { + return facadeError( + 400, + 'KILO_EVENT_DIRECTORY_REQUIRED', + 'A Cloud Agent session directory selector is required' + ); + } + const kiloSessionId = parsePublicSessionDirectory(directory); + if (!kiloSessionId) { + return facadeError( + 400, + 'KILO_EVENT_SELECTOR_UNSUPPORTED', + 'Only a Cloud Agent root session directory selector is supported' + ); + } + if (!isSessionIngestKiloSessionId(kiloSessionId)) return missingRootKiloSessionResponse(); + const root = await resolveRootSessionForKiloSession(context, kiloSessionId); + if (!root) return missingRootKiloSessionResponse(); + const globalEvents = context.deps?.globalEvents; + if (!globalEvents) { + return facadeError(500, 'KILO_FACADE_UNAVAILABLE', 'Kilo facade stream is unavailable'); + } + return globalEvents.openPublicSessionEventStream(context.userId, kiloSessionId); +} diff --git a/services/cloud-agent-next/src/kilo-facade/handlers/session-list/handler.ts b/services/cloud-agent-next/src/kilo-facade/handlers/session-list/handler.ts new file mode 100644 index 0000000000..e150752e68 --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/handlers/session-list/handler.ts @@ -0,0 +1,82 @@ +import { safeRepositoryUrlSchema } from '@kilocode/session-ingest-contracts'; +import type { + ListCloudAgentRootSessionsByGitUrlParams, + ListCloudAgentRootSessionsParams, +} from '../../../session-ingest-binding.js'; +import type { KiloFacadeHandlerContext } from '../../contracts.js'; +import { duplicateQueryParametersResponse, facadeError } from '../../http-contract.js'; +import { projectPublicListedSession } from '../../public-sdk-projection.js'; +import { hasDuplicateQueryParameters } from '../../../shared/http-query.js'; + +const SUPPORTED_QUERY_PARAMS = new Set(['limit', 'start', 'gitUrl']); +const MAX_TIMESTAMP_MILLISECONDS = 8_640_000_000_000_000; + +function parseSessionListQuery( + url: URL +): + | (Omit & { gitUrl?: string }) + | Response { + for (const key of url.searchParams.keys()) { + if (!SUPPORTED_QUERY_PARAMS.has(key)) { + return facadeError( + 400, + 'KILO_SESSION_LIST_SELECTOR_UNSUPPORTED', + `Session list query parameter is not supported: ${key}` + ); + } + } + if (hasDuplicateQueryParameters(url.searchParams)) { + return duplicateQueryParametersResponse(); + } + + const params: Omit & { gitUrl?: string } = {}; + const gitUrlParam = url.searchParams.get('gitUrl'); + if (gitUrlParam !== null) { + const gitUrl = safeRepositoryUrlSchema.safeParse(gitUrlParam); + if (!gitUrl.success || gitUrl.data !== gitUrlParam) { + return facadeError(400, 'KILO_QUERY_INVALID', 'Session list gitUrl must be a safe HTTPS URL'); + } + params.gitUrl = gitUrl.data; + } + const limitParam = url.searchParams.get('limit'); + if (limitParam !== null) { + const limit = Number(limitParam); + if (!Number.isInteger(limit) || limit < 1 || limit > 100) { + return facadeError( + 400, + 'KILO_QUERY_INVALID', + 'Session list limit must be an integer from 1 to 100' + ); + } + params.limit = limit; + } + const startParam = url.searchParams.get('start'); + if (startParam !== null) { + const start = Number(startParam); + if (!Number.isSafeInteger(start) || start < 0 || start > MAX_TIMESTAMP_MILLISECONDS) { + return facadeError( + 400, + 'KILO_QUERY_INVALID', + 'Session list start must be a non-negative integer' + ); + } + params.start = start; + } + return params; +} + +export async function handleSessionList(context: KiloFacadeHandlerContext): Promise { + const query = parseSessionListQuery(context.url); + if (query instanceof Response) return query; + const sessions = query.gitUrl + ? await context.env.SESSION_INGEST.listCloudAgentRootSessionsByGitUrl({ + ...query, + gitUrl: query.gitUrl, + kiloUserId: context.userId, + }) + : await context.env.SESSION_INGEST.listCloudAgentRootSessions({ + ...query, + kiloUserId: context.userId, + }); + return Response.json(sessions.map(projectPublicListedSession)); +} diff --git a/services/cloud-agent-next/src/kilo-facade/handlers/session-message/handler.test.ts b/services/cloud-agent-next/src/kilo-facade/handlers/session-message/handler.test.ts new file mode 100644 index 0000000000..3cb32d44df --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/handlers/session-message/handler.test.ts @@ -0,0 +1,324 @@ +import { describe, expect, it, vi } from 'vitest'; + +vi.mock('../../session-proxy.js', () => ({ + buildWrapperKiloProxyUrl: (params: { + wrapperPort: number; + kiloRelativePath: string; + search: string; + }) => + `http://localhost:${params.wrapperPort}/kilo-proxy${params.kiloRelativePath}${params.search}`, + resolveLiveWrapperTarget: vi.fn(), +})); + +import type { Env } from '../../../types.js'; +import type { KiloSdkStoredMessage } from '../../../session-ingest-binding.js'; +import type { OwnedRootHandlerContext } from '../../contracts.js'; +import { publicCloudAgentDirectory } from '../../public-sdk-projection.js'; +import { handleSessionMessage } from './handler.js'; + +const kiloSessionId = 'ses_12345678901234567890123456'; +const messageId = 'msg_exact_01'; +const storedMessage: KiloSdkStoredMessage = { + info: { + id: messageId, + sessionID: kiloSessionId, + role: 'assistant', + time: { created: 100 }, + parentID: 'msg_parent_01', + modelID: 'model', + providerID: 'provider', + mode: 'build', + agent: 'build', + path: { cwd: '/private/workspace', root: '/private/workspace' }, + cost: 0, + tokens: { input: 1, output: 1, reasoning: 0, cache: { read: 0, write: 0 } }, + }, + parts: [ + { + id: 'prt_exact_01', + sessionID: kiloSessionId, + messageID: messageId, + type: 'file', + mime: 'text/plain', + url: 'file:///private/workspace/secret.txt', + source: { + type: 'file', + text: { value: 'secret.txt', start: 0, end: 10 }, + path: '/private/workspace/secret.txt', + }, + }, + ], +}; + +function makeContext(params?: { + url?: string; + persisted?: unknown; + liveResponse?: Response; +}): OwnedRootHandlerContext { + const getCloudAgentRootSessionMessage = vi.fn(async () => + params?.persisted === undefined + ? { + kiloSessionId, + cloudAgentSessionId: 'agent_root', + message: { kind: 'value', message: storedMessage }, + } + : params.persisted + ); + const liveWrapper = params?.liveResponse + ? { + port: 4096, + sandbox: { containerFetch: vi.fn(async () => params.liveResponse) }, + } + : null; + const request = new Request( + params?.url ?? `https://example.test/kilo/session/${kiloSessionId}/message/${messageId}` + ); + const url = new URL(request.url); + return { + request, + url, + kiloPath: url.pathname.slice('/kilo'.length), + env: { SESSION_INGEST: { getCloudAgentRootSessionMessage } } as unknown as Env, + userId: 'usr_owner', + encodedKiloSessionId: kiloSessionId, + kiloSessionId, + cloudAgentSessionId: 'agent_root', + deps: { resolveLiveWrapper: vi.fn(async () => liveWrapper as never) }, + }; +} + +describe('handleSessionMessage', () => { + it('uses a valid warm exact response first and projects public message paths', async () => { + const context = makeContext({ liveResponse: Response.json(storedMessage) }); + + const response = await handleSessionMessage(context, messageId); + + expect(response.status).toBe(200); + const body = (await response.json()) as KiloSdkStoredMessage; + expect(body.info).toMatchObject({ + id: messageId, + path: { + cwd: publicCloudAgentDirectory(kiloSessionId), + root: publicCloudAgentDirectory(kiloSessionId), + }, + }); + expect(body.parts[0]).toMatchObject({ + url: '', + source: { path: publicCloudAgentDirectory(kiloSessionId) }, + }); + expect(context.env.SESSION_INGEST.getCloudAgentRootSessionMessage).not.toHaveBeenCalled(); + }); + + it.each([ + { + name: 'non-JSON content type', + response: () => + new Response(JSON.stringify(storedMessage), { headers: { 'content-type': 'text/plain' } }), + }, + { + name: 'malformed JSON', + response: () => new Response('{', { headers: { 'content-type': 'application/json' } }), + }, + { + name: 'oversized declared content length', + response: () => + new Response(JSON.stringify(storedMessage), { + headers: { + 'content-type': 'application/json', + 'content-length': String(8 * 1024 * 1024 + 1), + }, + }), + }, + { + name: 'oversized streamed body', + response: () => + new Response( + new ReadableStream({ + start(controller) { + controller.enqueue(new Uint8Array(8 * 1024 * 1024 + 1)); + controller.close(); + }, + }), + { headers: { 'content-type': 'application/json' } } + ), + }, + ])( + 'maps a live $name response to stable invalid-upstream', + async ({ response: liveResponse }) => { + const context = makeContext({ liveResponse: liveResponse() }); + + const response = await handleSessionMessage(context, messageId); + + expect(response.status).toBe(502); + await expect(response.json()).resolves.toMatchObject({ + error: 'KILO_UPSTREAM_RESPONSE_INVALID', + }); + expect(context.env.SESSION_INGEST.getCloudAgentRootSessionMessage).not.toHaveBeenCalled(); + } + ); + + it('rejects warm responses for another message instead of accepting a nearby result', async () => { + const context = makeContext({ + liveResponse: Response.json({ + ...storedMessage, + info: { ...storedMessage.info, id: 'msg_other_02' }, + }), + }); + + const response = await handleSessionMessage(context, messageId); + + expect(response.status).toBe(502); + await expect(response.json()).resolves.toMatchObject({ + error: 'KILO_UPSTREAM_RESPONSE_INVALID', + }); + expect(context.env.SESSION_INGEST.getCloudAgentRootSessionMessage).not.toHaveBeenCalled(); + }); + + it('falls back cold and distinguishes exact not-found from owned-root missing', async () => { + const exactMissing = makeContext({ + persisted: { + kiloSessionId, + cloudAgentSessionId: 'agent_root', + message: { kind: 'not_found' }, + }, + }); + const rootMissing = makeContext({ persisted: null }); + + const exactMissingResponse = await handleSessionMessage(exactMissing, messageId); + const rootMissingResponse = await handleSessionMessage(rootMissing, messageId); + + expect(exactMissingResponse.status).toBe(404); + await expect(exactMissingResponse.json()).resolves.toMatchObject({ + error: 'KILO_MESSAGE_NOT_FOUND', + }); + expect(rootMissingResponse.status).toBe(404); + await expect(rootMissingResponse.json()).resolves.toMatchObject({ + error: 'KILO_SESSION_NOT_FOUND', + }); + }); + + it('validates the persisted message belongs to the selected root and requested ID', async () => { + const wrongMapping = makeContext({ + persisted: { + kiloSessionId, + cloudAgentSessionId: 'agent_other_root', + message: { kind: 'value', message: storedMessage }, + }, + }); + const wrongRoot = makeContext({ + persisted: { + kiloSessionId, + cloudAgentSessionId: 'agent_root', + message: { + kind: 'value', + message: { + ...storedMessage, + info: { ...storedMessage.info, sessionID: 'ses_abcdefghijklmnopqrstuvwxyz' }, + }, + }, + }, + }); + const wrongMessage = makeContext({ + persisted: { + kiloSessionId, + cloudAgentSessionId: 'agent_root', + message: { + kind: 'value', + message: { ...storedMessage, info: { ...storedMessage.info, id: 'msg_other_02' } }, + }, + }, + }); + + for (const context of [wrongMapping, wrongRoot, wrongMessage]) { + const response = await handleSessionMessage(context, messageId); + expect(response.status).toBe(502); + await expect(response.json()).resolves.toMatchObject({ + error: 'KILO_UPSTREAM_RESPONSE_INVALID', + }); + } + }); + + it('returns exact not-found for invalid message IDs before accessing the selected root', async () => { + const context = makeContext(); + + const response = await handleSessionMessage(context, 'not-a-message'); + + expect(response.status).toBe(404); + await expect(response.json()).resolves.toMatchObject({ error: 'KILO_MESSAGE_NOT_FOUND' }); + expect(context.deps?.resolveLiveWrapper).not.toHaveBeenCalled(); + expect(context.env.SESSION_INGEST.getCloudAgentRootSessionMessage).not.toHaveBeenCalled(); + }); + + it('maps persisted too_large and invalid_data outcomes', async () => { + const tooLarge = makeContext({ + persisted: { + kiloSessionId, + cloudAgentSessionId: 'agent_root', + message: { kind: 'too_large', maximumBytes: 8 * 1024 * 1024, phase: 'page_parts' }, + }, + }); + const invalid = makeContext({ + persisted: { + kiloSessionId, + cloudAgentSessionId: 'agent_root', + message: { kind: 'invalid_data' }, + }, + }); + + const tooLargeResponse = await handleSessionMessage(tooLarge, messageId); + expect(tooLargeResponse.status).toBe(413); + await expect(tooLargeResponse.json()).resolves.toMatchObject({ + error: 'KILO_TRANSCRIPT_TOO_LARGE', + }); + const invalidResponse = await handleSessionMessage(invalid, messageId); + expect(invalidResponse.status).toBe(502); + await expect(invalidResponse.json()).resolves.toMatchObject({ + error: 'KILO_UPSTREAM_RESPONSE_INVALID', + }); + }); + + it('returns invalid-upstream instead of throwing for malformed discriminated parts', async () => { + const context = makeContext({ + liveResponse: Response.json({ + ...storedMessage, + parts: [ + { + id: 'prt_malformed_01', + sessionID: kiloSessionId, + messageID: messageId, + type: 'tool', + state: { status: 'unknown' }, + }, + ], + }), + }); + + const response = await handleSessionMessage(context, messageId); + + expect(response.status).toBe(502); + await expect(response.json()).resolves.toMatchObject({ + error: 'KILO_UPSTREAM_RESPONSE_INVALID', + }); + }); + + it('matches owned-root selector validation and maps persisted read failures', async () => { + const selected = makeContext({ + url: `https://example.test/kilo/session/${kiloSessionId}/message/${messageId}?directory=${encodeURIComponent(publicCloudAgentDirectory(kiloSessionId))}`, + persisted: { + kiloSessionId, + cloudAgentSessionId: 'agent_root', + message: { kind: 'retryable_failure', phase: 'page_parts' }, + }, + }); + const retryableResponse = await handleSessionMessage(selected, messageId); + expect(retryableResponse.status).toBe(503); + expect(retryableResponse.headers.get('Retry-After')).toBe('1'); + + const unsupported = makeContext({ + url: `https://example.test/kilo/session/${kiloSessionId}/message/${messageId}?directory=/tmp/private`, + }); + const unsupportedResponse = await handleSessionMessage(unsupported, messageId); + expect(unsupportedResponse.status).toBe(400); + expect(unsupported.env.SESSION_INGEST.getCloudAgentRootSessionMessage).not.toHaveBeenCalled(); + }); +}); diff --git a/services/cloud-agent-next/src/kilo-facade/handlers/session-message/handler.ts b/services/cloud-agent-next/src/kilo-facade/handlers/session-message/handler.ts new file mode 100644 index 0000000000..b59c84821b --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/handlers/session-message/handler.ts @@ -0,0 +1,132 @@ +import type { OwnedRootHandlerContext } from '../../contracts.js'; +import { + facadeError, + invalidPersistedSessionDataResponse, + isRecord, + missingRootKiloSessionResponse, + readBoundedBody, + retryableSessionReadResponse, + validateIdScopedSelectors, +} from '../../http-contract.js'; +import { projectPublicStoredMessage } from '../../public-sdk-projection.js'; +import { handleLiveFirstSessionRead } from '../../session-read-source.js'; +import { parseOwnedKiloSdkStoredMessage } from '../../stored-message-validation.js'; + +const MAX_KILO_SESSION_JSON_BYTES = 8 * 1024 * 1024; + +function exactMessageNotFoundResponse(): Response { + return facadeError(404, 'KILO_MESSAGE_NOT_FOUND', 'Kilo session message was not found'); +} + +async function rewriteLiveSessionMessageResponse( + response: Response, + kiloSessionId: string, + messageId: string +): Promise { + if (!response.ok) return response; + const contentType = response.headers.get('content-type'); + if (!contentType?.toLowerCase().includes('application/json')) { + return facadeError(502, 'KILO_UPSTREAM_RESPONSE_INVALID', 'Kilo message response is not valid'); + } + const declaredLength = response.headers.get('content-length'); + if (declaredLength !== null) { + const bodyBytes = Number(declaredLength); + if (!Number.isSafeInteger(bodyBytes) || bodyBytes > MAX_KILO_SESSION_JSON_BYTES) { + return facadeError( + 502, + 'KILO_UPSTREAM_RESPONSE_INVALID', + 'Kilo message response exceeds supported size' + ); + } + } + const bytes = await readBoundedBody(response, MAX_KILO_SESSION_JSON_BYTES); + if (!bytes) { + return facadeError( + 502, + 'KILO_UPSTREAM_RESPONSE_INVALID', + 'Kilo message response exceeds supported size' + ); + } + let parsed: unknown; + try { + parsed = JSON.parse(new TextDecoder().decode(bytes)); + } catch { + return facadeError( + 502, + 'KILO_UPSTREAM_RESPONSE_INVALID', + 'Kilo message response is not valid JSON' + ); + } + const message = parseOwnedKiloSdkStoredMessage(parsed, kiloSessionId, messageId); + if (!message) { + return facadeError(502, 'KILO_UPSTREAM_RESPONSE_INVALID', 'Kilo message response is not valid'); + } + const headers = new Headers(response.headers); + headers.delete('content-length'); + headers.delete('content-encoding'); + headers.set('content-type', 'application/json'); + return new Response(JSON.stringify(projectPublicStoredMessage(message, kiloSessionId)), { + status: response.status, + statusText: response.statusText, + headers, + }); +} + +async function persistedSessionMessageResponse( + context: OwnedRootHandlerContext, + messageId: string +): Promise { + const result = await context.env.SESSION_INGEST.getCloudAgentRootSessionMessage({ + kiloUserId: context.userId, + kiloSessionId: context.kiloSessionId, + messageId, + }); + if (!result) return missingRootKiloSessionResponse(); + if ( + result.kiloSessionId !== context.kiloSessionId || + result.cloudAgentSessionId !== context.cloudAgentSessionId || + !isRecord(result.message) + ) { + return invalidPersistedSessionDataResponse('messages'); + } + switch (result.message.kind) { + case 'not_found': + return exactMessageNotFoundResponse(); + case 'too_large': + return facadeError( + 413, + 'KILO_TRANSCRIPT_TOO_LARGE', + 'Persisted Kilo message exceeds the safe cold-read budget; retry while a live runtime is available' + ); + case 'retryable_failure': + return retryableSessionReadResponse(); + case 'invalid_data': + return invalidPersistedSessionDataResponse('messages'); + case 'value': { + const message = parseOwnedKiloSdkStoredMessage( + result.message.message, + context.kiloSessionId, + messageId + ); + if (!message) return invalidPersistedSessionDataResponse('messages'); + return Response.json(projectPublicStoredMessage(message, context.kiloSessionId)); + } + } +} + +export async function handleSessionMessage( + context: OwnedRootHandlerContext, + messageId: string +): Promise { + if (messageId.includes('/') || messageId.includes('\u0000') || !messageId.startsWith('msg')) { + return exactMessageNotFoundResponse(); + } + const selectorResponse = validateIdScopedSelectors(context.url, context.kiloSessionId, new Set()); + if (selectorResponse) return selectorResponse; + return handleLiveFirstSessionRead({ + context, + persistedFallback: () => persistedSessionMessageResponse(context, messageId), + rewriteLiveResponse: (response, kiloSessionId) => + rewriteLiveSessionMessageResponse(response, kiloSessionId, messageId), + }); +} diff --git a/services/cloud-agent-next/src/kilo-facade/handlers/session-messages/handler.ts b/services/cloud-agent-next/src/kilo-facade/handlers/session-messages/handler.ts new file mode 100644 index 0000000000..51fc1d80da --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/handlers/session-messages/handler.ts @@ -0,0 +1,246 @@ +import { + MAX_KILO_SDK_MESSAGE_HISTORY_PAGE_SIZE, + validateKiloSdkMessagesCursor, +} from '@kilocode/session-ingest-contracts'; +import type { + GetCloudAgentRootSessionMessagesParams, + KiloSdkStoredMessage, +} from '../../../session-ingest-binding.js'; +import type { CloudAgentSession } from '../../../persistence/CloudAgentSession.js'; +import { withDORetry } from '../../../utils/do-retry.js'; +import type { QueuedMessageSnapshot } from '../../../websocket/stream.js'; +import type { OwnedRootHandlerContext } from '../../contracts.js'; +import { + facadeError, + invalidPersistedSessionDataResponse, + missingRootKiloSessionResponse, + pendingSessionSnapshotResponse, + readBoundedBody, + retryableSessionReadResponse, + unsupportedSessionSelectorResponse, + validateIdScopedSelectors, +} from '../../http-contract.js'; +import { projectPublicStoredMessages } from '../../public-sdk-projection.js'; +import { handleLiveFirstSessionRead } from '../../session-read-source.js'; +import { parseOwnedKiloSdkStoredMessages } from '../../stored-message-validation.js'; + +const MAX_KILO_SESSION_JSON_BYTES = 8 * 1024 * 1024; +const SUPPORTED_QUERY_PARAMS = new Set(['directory', 'limit', 'before']); + +function parseSessionMessagesQuery( + url: URL +): Pick | Response { + for (const key of url.searchParams.keys()) { + if (!SUPPORTED_QUERY_PARAMS.has(key)) return unsupportedSessionSelectorResponse(); + } + const params: Pick = {}; + const limitParam = url.searchParams.get('limit'); + if (limitParam !== null) { + const limit = Number(limitParam); + if (!Number.isInteger(limit) || limit < 0 || limit > MAX_KILO_SDK_MESSAGE_HISTORY_PAGE_SIZE) { + return facadeError( + 400, + 'KILO_QUERY_INVALID', + `Session messages limit must be an integer from 0 to ${MAX_KILO_SDK_MESSAGE_HISTORY_PAGE_SIZE}` + ); + } + params.limit = limit; + } + const before = url.searchParams.get('before'); + if (before !== null) { + if (before.length === 0 || params.limit === undefined || params.limit === 0) { + return facadeError( + 400, + 'KILO_QUERY_INVALID', + 'Session messages before requires a positive limit' + ); + } + if (!validateKiloSdkMessagesCursor(before)) { + return facadeError( + 400, + 'KILO_QUERY_INVALID', + 'Session messages before is not a valid cursor' + ); + } + params.before = before; + } + return params; +} + +async function rewriteLiveMessagesResponse( + response: Response, + context: OwnedRootHandlerContext, + query: Pick +): Promise { + if (response.status === 404) return persistedSessionMessagesResponse(context, query); + if (!response.ok) return response; + const declaredLength = response.headers.get('content-length'); + if (declaredLength !== null) { + const bodyBytes = Number(declaredLength); + if (!Number.isSafeInteger(bodyBytes) || bodyBytes > MAX_KILO_SESSION_JSON_BYTES) { + return facadeError( + 502, + 'KILO_UPSTREAM_RESPONSE_INVALID', + 'Kilo messages response exceeds supported size' + ); + } + } + const bytes = await readBoundedBody(response, MAX_KILO_SESSION_JSON_BYTES); + if (!bytes) { + return facadeError( + 502, + 'KILO_UPSTREAM_RESPONSE_INVALID', + 'Kilo messages response exceeds supported size' + ); + } + let parsed: unknown; + try { + parsed = JSON.parse(new TextDecoder().decode(bytes)); + } catch { + return facadeError( + 502, + 'KILO_UPSTREAM_RESPONSE_INVALID', + 'Kilo messages response is not valid JSON' + ); + } + const messages = parseOwnedKiloSdkStoredMessages(parsed, context.kiloSessionId); + if (!messages) { + return facadeError( + 502, + 'KILO_UPSTREAM_RESPONSE_INVALID', + 'Kilo messages response is not valid' + ); + } + const output = + messages.length > 0 + ? projectPublicStoredMessages(messages, context.kiloSessionId) + : await initialStoredMessages(context, query); + const headers = new Headers(response.headers); + headers.delete('content-length'); + headers.delete('content-encoding'); + headers.set('content-type', 'application/json'); + return new Response(JSON.stringify(output), { + status: response.status, + statusText: response.statusText, + headers, + }); +} + +function initialStoredMessage( + snapshot: QueuedMessageSnapshot, + kiloSessionId: string +): KiloSdkStoredMessage { + return { + info: { + id: snapshot.messageId, + sessionID: kiloSessionId, + role: 'user', + time: { created: snapshot.timestamp }, + agent: '', + model: { providerID: '', modelID: '' }, + }, + parts: [ + { + id: `prt_${snapshot.messageId}`, + sessionID: kiloSessionId, + messageID: snapshot.messageId, + type: 'text', + text: snapshot.content, + }, + ], + }; +} + +async function initialStoredMessages( + context: OwnedRootHandlerContext, + query: Pick +): Promise { + if (query.before !== undefined) return []; + const id = context.env.CLOUD_AGENT_SESSION.idFromName( + `${context.userId}:${context.cloudAgentSessionId}` + ); + const snapshot = await withDORetry< + DurableObjectStub, + QueuedMessageSnapshot | null + >( + () => context.env.CLOUD_AGENT_SESSION.get(id), + stub => stub.getInitialMessageSnapshot(), + 'getInitialMessageSnapshot' + ).catch(() => null); + return snapshot?.content ? [initialStoredMessage(snapshot, context.kiloSessionId)] : []; +} + +function messagesPageResponse( + requestUrl: URL, + messages: unknown, + nextCursor: string | null, + omittedItemCount: number +): Response { + const headers = new Headers({ + 'content-type': 'application/json', + 'X-Kilo-Omitted-Item-Count': String(omittedItemCount), + }); + if (nextCursor !== null) { + const nextUrl = new URL(requestUrl); + nextUrl.searchParams.set('before', nextCursor); + headers.set('Link', `<${nextUrl.toString()}>; rel="next"`); + headers.set('X-Next-Cursor', nextCursor); + } + return new Response(JSON.stringify(messages), { headers }); +} + +async function persistedSessionMessagesResponse( + context: OwnedRootHandlerContext, + query: Pick +): Promise { + const result = await context.env.SESSION_INGEST.getCloudAgentRootSessionMessages({ + kiloUserId: context.userId, + kiloSessionId: context.kiloSessionId, + ...query, + }); + if (!result) return missingRootKiloSessionResponse(); + if (result.history === null) { + const initial = await initialStoredMessages(context, query); + return initial.length > 0 + ? messagesPageResponse(context.url, initial, null, 0) + : pendingSessionSnapshotResponse(); + } + if ('kind' in result.history) { + switch (result.history.kind) { + case 'too_large': + return facadeError( + 413, + 'KILO_TRANSCRIPT_TOO_LARGE', + 'Persisted Kilo transcript exceeds the safe cold-read budget; use smaller bounded history when possible, or retry while a live runtime is available' + ); + case 'retryable_failure': + return retryableSessionReadResponse(); + case 'invalid_data': + return invalidPersistedSessionDataResponse('messages'); + } + } + const messages = projectPublicStoredMessages(result.history.messages, context.kiloSessionId); + const output = messages.length > 0 ? messages : await initialStoredMessages(context, query); + return messagesPageResponse( + context.url, + output, + result.history.nextCursor, + result.history.omittedItemCount ?? 0 + ); +} + +export async function handleSessionMessages(context: OwnedRootHandlerContext): Promise { + const selectorResponse = validateIdScopedSelectors( + context.url, + context.kiloSessionId, + new Set(['limit', 'before']) + ); + if (selectorResponse) return selectorResponse; + const query = parseSessionMessagesQuery(context.url); + if (query instanceof Response) return query; + return handleLiveFirstSessionRead({ + context, + persistedFallback: () => persistedSessionMessagesResponse(context, query), + rewriteLiveResponse: response => rewriteLiveMessagesResponse(response, context, query), + }); +} diff --git a/services/cloud-agent-next/src/kilo-facade/handlers/session-status/handler.ts b/services/cloud-agent-next/src/kilo-facade/handlers/session-status/handler.ts new file mode 100644 index 0000000000..ad71c7e09f --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/handlers/session-status/handler.ts @@ -0,0 +1,42 @@ +import { hasDuplicateQueryParameters } from '../../../shared/http-query.js'; +import type { KiloFacadeHandlerContext } from '../../contracts.js'; +import { + duplicateQueryParametersResponse, + facadeError, + isSessionIngestKiloSessionId, + missingRootKiloSessionResponse, + parsePublicSessionDirectory, + unsupportedSessionSelectorResponse, +} from '../../http-contract.js'; +import { resolveRootSessionForKiloSession } from '../../ownership.js'; + +export async function handleSessionStatus(context: KiloFacadeHandlerContext): Promise { + if ([...context.url.searchParams.keys()].some(key => key !== 'directory')) { + return unsupportedSessionSelectorResponse(); + } + if (hasDuplicateQueryParameters(context.url.searchParams)) { + return duplicateQueryParametersResponse(); + } + + let selectedKiloSessionId: string | undefined; + const directory = context.url.searchParams.get('directory'); + if (directory !== null) { + selectedKiloSessionId = parsePublicSessionDirectory(directory) ?? undefined; + if (!selectedKiloSessionId) return unsupportedSessionSelectorResponse(); + if (!isSessionIngestKiloSessionId(selectedKiloSessionId)) { + return missingRootKiloSessionResponse(); + } + const root = await resolveRootSessionForKiloSession(context, selectedKiloSessionId); + if (!root) return missingRootKiloSessionResponse(); + } + + const globalEvents = context.deps?.globalEvents; + if (!globalEvents?.getPublicSessionStatuses) { + return facadeError(500, 'KILO_FACADE_UNAVAILABLE', 'Kilo facade status is unavailable'); + } + return Response.json( + selectedKiloSessionId + ? globalEvents.getPublicSessionStatuses(selectedKiloSessionId) + : globalEvents.getPublicSessionStatuses() + ); +} diff --git a/services/cloud-agent-next/src/kilo-facade/handlers/session-status/tracking.test.ts b/services/cloud-agent-next/src/kilo-facade/handlers/session-status/tracking.test.ts new file mode 100644 index 0000000000..f7152e795c --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/handlers/session-status/tracking.test.ts @@ -0,0 +1,85 @@ +import { describe, expect, it } from 'vitest'; +import { publicSessionStatusesFromAttachments, sessionStatusUpdateFromEvent } from './tracking.js'; + +const rootSessionId = 'ses_12345678901234567890123456'; + +describe('session status tracking', () => { + it('keeps the latest producer generation and only exposes non-idle root statuses', () => { + expect( + publicSessionStatusesFromAttachments([ + { kiloSessionId: rootSessionId, wrapperGeneration: 1, sessionStatus: { type: 'busy' } }, + { kiloSessionId: rootSessionId, wrapperGeneration: 2 }, + { + kiloSessionId: 'ses_22222222222222222222222222', + wrapperGeneration: 1, + sessionStatus: { type: 'offline', requestID: 'request-1', message: 'Waiting' }, + }, + ]) + ).toEqual({ + ses_22222222222222222222222222: { + type: 'offline', + requestID: 'request-1', + message: 'Waiting', + }, + }); + }); + + it('accepts the current SDK retry action shape and clears idle', () => { + expect( + sessionStatusUpdateFromEvent( + { + type: 'session.status', + properties: { + sessionID: rootSessionId, + status: { + type: 'retry', + attempt: 1, + message: 'Retrying', + action: { + reason: 'rate_limit', + provider: 'kilo', + title: 'Try again', + message: 'Waiting', + label: 'Retry', + link: 'https://example.com/retry', + }, + next: 123, + }, + }, + }, + rootSessionId + ) + ).toEqual({ + kind: 'set', + status: { + type: 'retry', + attempt: 1, + message: 'Retrying', + action: { + reason: 'rate_limit', + provider: 'kilo', + title: 'Try again', + message: 'Waiting', + label: 'Retry', + link: 'https://example.com/retry', + }, + next: 123, + }, + }); + expect( + sessionStatusUpdateFromEvent( + { + type: 'session.status', + properties: { sessionID: rootSessionId, status: { type: 'idle' } }, + }, + rootSessionId + ) + ).toEqual({ kind: 'clear' }); + expect( + sessionStatusUpdateFromEvent( + { type: 'session.idle', properties: { sessionID: rootSessionId } }, + rootSessionId + ) + ).toEqual({ kind: 'clear' }); + }); +}); diff --git a/services/cloud-agent-next/src/kilo-facade/handlers/session-status/tracking.ts b/services/cloud-agent-next/src/kilo-facade/handlers/session-status/tracking.ts new file mode 100644 index 0000000000..28ee3fbb8f --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/handlers/session-status/tracking.ts @@ -0,0 +1,89 @@ +import { z } from 'zod'; +import type { SessionStatus } from '../../../shared/protocol.js'; + +const nonNegativeInteger = z.number().int().nonnegative(); +const sessionStatusSchema: z.ZodType = z.discriminatedUnion('type', [ + z.object({ type: z.literal('idle') }), + z.object({ type: z.literal('busy') }), + z.object({ + type: z.literal('retry'), + attempt: z.number(), + message: z.string(), + action: z + .object({ + reason: z.string(), + provider: z.string(), + title: z.string(), + message: z.string(), + label: z.string(), + link: z.string().optional(), + }) + .optional(), + next: z.number(), + }), + z.object({ + type: z.literal('offline'), + requestID: z.string(), + message: z.string(), + }), +]); + +const sessionStatusEventSchema = z.object({ + type: z.literal('session.status'), + properties: z.object({ sessionID: z.string(), status: sessionStatusSchema }), +}); + +const sessionIdleEventSchema = z.object({ + type: z.literal('session.idle'), + properties: z.object({ sessionID: z.string() }), +}); + +const statusAttachmentSchema = z.object({ + kiloSessionId: z.string(), + wrapperGeneration: nonNegativeInteger, + sessionStatus: sessionStatusSchema.optional(), +}); + +export type SessionStatusUpdate = { kind: 'clear' } | { kind: 'set'; status: SessionStatus }; + +export function sessionStatusUpdateFromEvent( + payload: unknown, + kiloSessionId: string +): SessionStatusUpdate | null { + const idle = sessionIdleEventSchema.safeParse(payload); + if (idle.success) { + return idle.data.properties.sessionID === kiloSessionId ? { kind: 'clear' } : null; + } + const parsed = sessionStatusEventSchema.safeParse(payload); + if (!parsed.success || parsed.data.properties.sessionID !== kiloSessionId) return null; + const status = parsed.data.properties.status; + return status.type === 'idle' ? { kind: 'clear' } : { kind: 'set', status }; +} + +export function publicSessionStatusesFromAttachments( + attachments: Iterable, + selectedKiloSessionId?: string +): Record { + const latestBySession = new Map< + string, + { wrapperGeneration: number; sessionStatus?: SessionStatus } + >(); + + for (const attachment of attachments) { + const parsed = statusAttachmentSchema.safeParse(attachment); + if (!parsed.success) continue; + const source = parsed.data; + if (selectedKiloSessionId && source.kiloSessionId !== selectedKiloSessionId) continue; + const current = latestBySession.get(source.kiloSessionId); + if (current && current.wrapperGeneration > source.wrapperGeneration) continue; + latestBySession.set(source.kiloSessionId, source); + } + + const statuses: Record = {}; + for (const [kiloSessionId, source] of latestBySession) { + if (source.sessionStatus && source.sessionStatus.type !== 'idle') { + statuses[kiloSessionId] = source.sessionStatus; + } + } + return statuses; +} diff --git a/services/cloud-agent-next/src/kilo-facade/handlers/suggestion/handler.test.ts b/services/cloud-agent-next/src/kilo-facade/handlers/suggestion/handler.test.ts new file mode 100644 index 0000000000..3586ed5188 --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/handlers/suggestion/handler.test.ts @@ -0,0 +1,308 @@ +import { describe, expect, it, vi } from 'vitest'; +import type { KiloFacadeHandlerContext } from '../../contracts.js'; +import { handleSuggestion, isSuggestionRequest, parseSuggestionRoute } from './handler.js'; + +vi.mock('@cloudflare/sandbox', () => ({ + getSandbox: vi.fn(), + Sandbox: class Sandbox {}, +})); + +const rootSessionID = 'ses_12345678901234567890123456'; +const childSessionID = 'ses_22222222222222222222222222'; + +function suggestionContext(params: { + method?: string; + path?: string; + body?: string; + responses: Response[]; +}): { + context: KiloFacadeHandlerContext; + containerFetch: ReturnType; + resolveRootSessionForKiloSession: ReturnType; +} { + const path = params.path ?? '/suggestion'; + const request = new Request( + `https://facade.test/kilo${path}?directory=/cloud-agent/sessions/${rootSessionID}`, + { + method: params.method ?? 'GET', + ...(params.body !== undefined + ? { body: params.body, headers: { 'content-type': 'application/json' } } + : {}), + } + ); + const containerFetch = vi.fn(); + for (const response of params.responses) containerFetch.mockResolvedValueOnce(response); + const resolveRootSessionForKiloSession = vi + .fn() + .mockResolvedValue({ cloudAgentSessionId: 'cloud-1' }); + return { + context: { + request, + env: {} as KiloFacadeHandlerContext['env'], + userId: 'user-1', + url: new URL(request.url), + kiloPath: path, + deps: { + resolveRootSessionForKiloSession, + resolveLiveWrapper: vi.fn().mockResolvedValue({ sandbox: { containerFetch }, port: 4312 }), + }, + }, + containerFetch, + resolveRootSessionForKiloSession, + }; +} + +const rootSuggestion = { + id: 'sug_root', + sessionID: rootSessionID, + text: 'Choose an approach', + actions: [ + { label: 'First', prompt: 'Use the first approach' }, + { label: 'Second', description: 'Use the alternative', prompt: 'Use the second approach' }, + ], + blocking: false, + tool: { messageID: 'message-1', callID: 'call-1' }, +}; +const childSuggestion = { + id: 'sug_child', + sessionID: childSessionID, + text: 'Choose a child approach', + actions: [{ label: 'Child', prompt: 'Use the child approach' }], +}; + +describe('suggestion handler', () => { + it('recognizes only the allowlisted suggestion routes', () => { + expect(parseSuggestionRoute('GET', '/suggestion')).toEqual({ kind: 'list' }); + expect(parseSuggestionRoute('POST', '/suggestion/request-1/accept')).toEqual({ + kind: 'accept', + requestID: 'request-1', + }); + expect(parseSuggestionRoute('POST', '/suggestion/request-1/dismiss')).toEqual({ + kind: 'dismiss', + requestID: 'request-1', + }); + expect(parseSuggestionRoute('POST', '/suggestion/request%2F1/accept')).toEqual({ + kind: 'accept', + requestID: 'request/1', + }); + expect(parseSuggestionRoute('DELETE', '/suggestion/request-1/dismiss')).toBeNull(); + expect(parseSuggestionRoute('GET', '/suggestion/request-1/accept')).toBeNull(); + expect(parseSuggestionRoute('POST', '/suggestion//accept')).toBeNull(); + expect(parseSuggestionRoute('POST', '/suggestion/%E0%A4%A/accept')).toBeNull(); + }); + + it('validates the native SuggestionRequest shape', () => { + expect(isSuggestionRequest(rootSuggestion)).toBe(true); + expect(isSuggestionRequest({ ...rootSuggestion, id: 'wrong-suggestion-root' })).toBe(false); + expect(isSuggestionRequest({ ...rootSuggestion, text: undefined })).toBe(false); + expect(isSuggestionRequest({ ...rootSuggestion, actions: [{ label: 'Missing prompt' }] })).toBe( + false + ); + expect(isSuggestionRequest({ ...rootSuggestion, blocking: 'false' })).toBe(false); + expect(isSuggestionRequest({ ...rootSuggestion, tool: { messageID: 'message-1' } })).toBe( + false + ); + }); + + it('requires a selected owned root before opening a live interaction', async () => { + const { context, containerFetch, resolveRootSessionForKiloSession } = suggestionContext({ + responses: [], + }); + context.url.search = ''; + context.request = new Request(context.url); + + const response = await handleSuggestion(context); + + expect(response.status).toBe(400); + await expect(response.json()).resolves.toMatchObject({ + error: 'KILO_SESSION_SELECTOR_UNSUPPORTED', + }); + expect(resolveRootSessionForKiloSession).not.toHaveBeenCalled(); + expect(containerFetch).not.toHaveBeenCalled(); + }); + + it('preserves native suggestion entries while filtering child suggestions', async () => { + const { context, containerFetch, resolveRootSessionForKiloSession } = suggestionContext({ + responses: [Response.json([rootSuggestion, childSuggestion])], + }); + + const response = await handleSuggestion(context); + + expect(response.status).toBe(200); + await expect(response.json()).resolves.toEqual([rootSuggestion]); + expect(resolveRootSessionForKiloSession).toHaveBeenCalledWith( + expect.objectContaining({ kiloSessionId: rootSessionID }) + ); + const request = containerFetch.mock.calls[0]?.[0]; + expect(request).toBeInstanceOf(Request); + expect(request.method).toBe('GET'); + expect(new URL(request.url).pathname).toBe('/kilo-proxy/suggestion'); + expect(new URL(request.url).search).toBe(''); + }); + + it('rejects invalid upstream suggestion entries', async () => { + const { context } = suggestionContext({ + responses: [Response.json([{ ...rootSuggestion, actions: [{ label: 'Missing prompt' }] }])], + }); + + const response = await handleSuggestion(context); + + expect(response.status).toBe(502); + await expect(response.json()).resolves.toMatchObject({ + error: 'KILO_UPSTREAM_RESPONSE_INVALID', + }); + }); + + it('preflights and forwards a valid accept index to the same live target', async () => { + const { context, containerFetch } = suggestionContext({ + method: 'POST', + path: '/suggestion/sug_root/accept', + body: JSON.stringify({ index: 1 }), + responses: [Response.json([rootSuggestion]), Response.json(true)], + }); + + const response = await handleSuggestion(context); + + expect(response.status).toBe(200); + await expect(response.json()).resolves.toBe(true); + expect(containerFetch).toHaveBeenCalledTimes(2); + const mutationRequest = containerFetch.mock.calls[1]?.[0]; + expect(new URL(mutationRequest.url).pathname).toBe('/kilo-proxy/suggestion/sug_root/accept'); + expect(new URL(mutationRequest.url).search).toBe(''); + await expect(mutationRequest.json()).resolves.toEqual({ index: 1 }); + }); + + it('rejects accept bodies that are not exactly one non-negative integer index', async () => { + for (const body of [ + {}, + { index: -1 }, + { index: 1.5 }, + { index: 0, extra: true }, + { index: '0' }, + ]) { + const { context, containerFetch } = suggestionContext({ + method: 'POST', + path: '/suggestion/sug_root/accept', + body: JSON.stringify(body), + responses: [], + }); + + const response = await handleSuggestion(context); + + expect(response.status).toBe(400); + await expect(response.json()).resolves.toMatchObject({ + error: 'KILO_INTERACTION_BODY_INVALID', + }); + expect(containerFetch).not.toHaveBeenCalled(); + } + }); + + it('preserves native invalid-index response semantics after preflight', async () => { + const nativeInvalidIndex = Response.json( + { name: 'BadRequestError', data: { message: 'Invalid action index' } }, + { status: 400, headers: { 'x-native-suggestion': 'invalid-index' } } + ); + const { context, containerFetch } = suggestionContext({ + method: 'POST', + path: '/suggestion/sug_root/accept', + body: JSON.stringify({ index: 99 }), + responses: [Response.json([rootSuggestion]), nativeInvalidIndex], + }); + + const response = await handleSuggestion(context); + + expect(response.status).toBe(400); + expect(response.headers.get('x-native-suggestion')).toBe('invalid-index'); + await expect(response.json()).resolves.toEqual({ + name: 'BadRequestError', + data: { message: 'Invalid action index' }, + }); + expect(containerFetch).toHaveBeenCalledTimes(2); + }); + + it('preflights and forwards dismiss while preserving its native response', async () => { + const nativeResult = Response.json(false, { status: 404, headers: { 'x-native': 'dismiss' } }); + const { context, containerFetch } = suggestionContext({ + method: 'POST', + path: '/suggestion/sug_root/dismiss', + responses: [Response.json([rootSuggestion]), nativeResult], + }); + + const response = await handleSuggestion(context); + + expect(response.status).toBe(404); + expect(response.headers.get('x-native')).toBe('dismiss'); + await expect(response.json()).resolves.toBe(false); + expect(containerFetch).toHaveBeenCalledTimes(2); + const mutationRequest = containerFetch.mock.calls[1]?.[0]; + expect(new URL(mutationRequest.url).pathname).toBe('/kilo-proxy/suggestion/sug_root/dismiss'); + expect(await mutationRequest.text()).toBe(''); + }); + + it('accepts a native no-underscore ID and uses it for same-root mutation preflight', async () => { + const nativeSuggestion = { ...rootSuggestion, id: 'suggestion-root' }; + const { context, containerFetch } = suggestionContext({ + method: 'POST', + path: '/suggestion/suggestion-root/dismiss', + responses: [Response.json([nativeSuggestion]), Response.json(true)], + }); + + const response = await handleSuggestion(context); + + expect(response.status).toBe(200); + await expect(response.json()).resolves.toBe(true); + expect(containerFetch).toHaveBeenCalledTimes(2); + expect(new URL(containerFetch.mock.calls[1]?.[0].url).pathname).toBe( + '/kilo-proxy/suggestion/suggestion-root/dismiss' + ); + }); + + it('does not authorize a mutation from a malformed upstream suggestion ID', async () => { + const malformedSuggestion = { ...rootSuggestion, id: 'wrong-suggestion-root' }; + const { context, containerFetch } = suggestionContext({ + method: 'POST', + path: '/suggestion/suggestion-root/dismiss', + responses: [Response.json([malformedSuggestion])], + }); + + const response = await handleSuggestion(context); + + expect(response.status).toBe(502); + await expect(response.json()).resolves.toMatchObject({ + error: 'KILO_UPSTREAM_RESPONSE_INVALID', + }); + expect(containerFetch).toHaveBeenCalledTimes(1); + }); + + it('returns facade 404 without mutation for stale, child, and foreign suggestion IDs', async () => { + for (const requestID of ['sug_stale', 'sug_child', 'sug_foreign']) { + const foreignSuggestion = { ...childSuggestion, id: 'sug_foreign' }; + const { context, containerFetch } = suggestionContext({ + method: 'POST', + path: `/suggestion/${requestID}/dismiss`, + responses: [Response.json([childSuggestion, foreignSuggestion])], + }); + + const response = await handleSuggestion(context); + + expect(response.status).toBe(404); + await expect(response.json()).resolves.toMatchObject({ + error: 'KILO_INTERACTION_REQUEST_NOT_FOUND', + }); + expect(containerFetch).toHaveBeenCalledTimes(1); + } + }); + + it('returns facade 404 before live-wrapper resolution for an unowned selected root', async () => { + const { context, containerFetch, resolveRootSessionForKiloSession } = suggestionContext({ + responses: [], + }); + resolveRootSessionForKiloSession.mockResolvedValue(null); + + const response = await handleSuggestion(context); + + expect(response.status).toBe(404); + await expect(response.json()).resolves.toMatchObject({ error: 'KILO_SESSION_NOT_FOUND' }); + expect(containerFetch).not.toHaveBeenCalled(); + }); +}); diff --git a/services/cloud-agent-next/src/kilo-facade/handlers/suggestion/handler.ts b/services/cloud-agent-next/src/kilo-facade/handlers/suggestion/handler.ts new file mode 100644 index 0000000000..06c89476ae --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/handlers/suggestion/handler.ts @@ -0,0 +1,123 @@ +import type { KiloFacadeHandlerContext } from '../../contracts.js'; +import { isRecord } from '../../http-contract.js'; +import { + openSelectedRootLiveInteraction, + readInteractionRequestBody, +} from '../../live-interaction.js'; + +export type SuggestionAction = Record & { + label: string; + prompt: string; + description?: string; +}; + +export type SuggestionRequest = Record & { + id: string; + sessionID: string; + text: string; + actions: SuggestionAction[]; + blocking?: boolean; + tool?: { + messageID: string; + callID: string; + }; +}; + +export type SuggestionRoute = + | { kind: 'list' } + | { kind: 'accept'; requestID: string } + | { kind: 'dismiss'; requestID: string }; + +export function parseSuggestionRoute(method: string, kiloPath: string): SuggestionRoute | null { + if (method === 'GET' && kiloPath === '/suggestion') return { kind: 'list' }; + const match = /^\/suggestion\/([^/]+)\/(accept|dismiss)$/.exec(kiloPath); + if (!match?.[1] || !match[2] || method !== 'POST') return null; + let requestID: string; + try { + requestID = decodeURIComponent(match[1]); + } catch { + return null; + } + if (!requestID) return null; + return match[2] === 'accept' ? { kind: 'accept', requestID } : { kind: 'dismiss', requestID }; +} + +function isSuggestionAction(value: unknown): value is SuggestionAction { + return ( + isRecord(value) && + typeof value.label === 'string' && + typeof value.prompt === 'string' && + (value.description === undefined || typeof value.description === 'string') + ); +} + +function isSuggestionTool(value: unknown): value is SuggestionRequest['tool'] { + return isRecord(value) && typeof value.messageID === 'string' && typeof value.callID === 'string'; +} + +export function isSuggestionRequest(value: unknown): value is SuggestionRequest { + return ( + isRecord(value) && + typeof value.id === 'string' && + value.id.startsWith('sug') && + typeof value.sessionID === 'string' && + typeof value.text === 'string' && + Array.isArray(value.actions) && + value.actions.length >= 1 && + value.actions.length <= 2 && + value.actions.every(isSuggestionAction) && + (value.blocking === undefined || typeof value.blocking === 'boolean') && + (value.tool === undefined || isSuggestionTool(value.tool)) + ); +} + +function isSuggestionAcceptBody(value: unknown): boolean { + return ( + isRecord(value) && + Object.keys(value).length === 1 && + typeof value.index === 'number' && + Number.isInteger(value.index) && + value.index >= 0 + ); +} + +export async function handleSuggestion( + context: KiloFacadeHandlerContext, + route = parseSuggestionRoute(context.request.method, context.kiloPath) +): Promise { + if (!route) throw new Error('Suggestion handler called for unsupported route'); + let body: Uint8Array | undefined; + if (route.kind === 'accept') { + const parsedBody = await readInteractionRequestBody(context.request, isSuggestionAcceptBody); + if (parsedBody instanceof Response) return parsedBody; + body = parsedBody; + } + + const interaction = await openSelectedRootLiveInteraction(context); + if (interaction instanceof Response) return interaction; + + if (route.kind === 'list') { + const suggestions = await interaction.list({ + path: '/suggestion', + isEntry: isSuggestionRequest, + }); + return suggestions instanceof Response ? suggestions : Response.json(suggestions); + } + + if (route.kind === 'accept') { + return interaction.mutate({ + listPath: '/suggestion', + mutationPath: `/suggestion/${encodeURIComponent(route.requestID)}/accept`, + requestID: route.requestID, + isEntry: isSuggestionRequest, + body, + }); + } + + return interaction.mutate({ + listPath: '/suggestion', + mutationPath: `/suggestion/${encodeURIComponent(route.requestID)}/dismiss`, + requestID: route.requestID, + isEntry: isSuggestionRequest, + }); +} diff --git a/services/cloud-agent-next/src/kilo-facade/http-contract.ts b/services/cloud-agent-next/src/kilo-facade/http-contract.ts new file mode 100644 index 0000000000..0dae3475e6 --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/http-contract.ts @@ -0,0 +1,163 @@ +import { hasDuplicateQueryParameters } from '../shared/http-query.js'; +import { publicCloudAgentDirectory } from './public-sdk-projection.js'; + +export function isRecord(value: unknown): value is Record { + return typeof value === 'object' && value !== null; +} + +export function facadeError(status: number, code: string, message: string): Response { + return Response.json({ error: code, message }, { status }); +} + +export function unsupportedRouteResponse(): Response { + return facadeError(501, 'KILO_ROUTE_UNSUPPORTED', 'Kilo facade route is not supported'); +} + +export function missingRootKiloSessionResponse(): Response { + return facadeError(404, 'KILO_SESSION_NOT_FOUND', 'Cloud Agent root Kilo session was not found'); +} + +export function pendingSessionSnapshotResponse(): Response { + const response = facadeError( + 503, + 'KILO_SESSION_SNAPSHOT_PENDING', + 'Cloud Agent Kilo session snapshot is not available yet' + ); + response.headers.set('Retry-After', '1'); + return response; +} + +export function retryableSessionReadResponse(): Response { + const response = facadeError( + 503, + 'KILO_SESSION_READ_RETRYABLE', + 'Persisted Kilo session data is temporarily unavailable; retry the request' + ); + response.headers.set('Retry-After', '1'); + return response; +} + +export function invalidPersistedSessionDataResponse(entity: 'session' | 'messages'): Response { + return facadeError(502, 'KILO_UPSTREAM_RESPONSE_INVALID', `Kilo ${entity} response is not valid`); +} + +export function liveRuntimeUnavailableResponse(): Response { + const response = facadeError( + 503, + 'KILO_LIVE_RUNTIME_UNAVAILABLE', + 'Live Kilo runtime is not available for this session' + ); + response.headers.set('Retry-After', '1'); + return response; +} + +export function invalidUpstreamResponse(message = 'Kilo upstream response is not valid'): Response { + return facadeError(502, 'KILO_UPSTREAM_RESPONSE_INVALID', message); +} + +export function invalidInteractionRequestBodyResponse(): Response { + return facadeError( + 400, + 'KILO_INTERACTION_BODY_INVALID', + 'Kilo interaction request body is not valid' + ); +} + +export function missingInteractionRequestResponse(): Response { + return facadeError( + 404, + 'KILO_INTERACTION_REQUEST_NOT_FOUND', + 'Kilo interaction request was not found' + ); +} + +export function duplicateQueryParametersResponse(): Response { + return facadeError(400, 'KILO_QUERY_INVALID', 'Query parameters must be unique'); +} + +export function unsupportedSessionSelectorResponse(): Response { + return facadeError( + 400, + 'KILO_SESSION_SELECTOR_UNSUPPORTED', + 'Only the matching public Cloud Agent session directory selector is supported' + ); +} + +export function kiloRelativePath(pathname: string): string { + if (pathname === '/kilo') return '/'; + return pathname.startsWith('/kilo/') ? pathname.slice('/kilo'.length) : pathname; +} + +export function parseRootSessionRoute(kiloPath: string): { + encodedKiloSessionId: string; + kiloSessionId: string; +} | null { + const match = /^\/session\/([^/]+)(?:\/.*)?$/.exec(kiloPath); + if (!match?.[1]) return null; + try { + return { encodedKiloSessionId: match[1], kiloSessionId: decodeURIComponent(match[1]) }; + } catch { + return null; + } +} + +export function parsePublicSessionDirectory(directory: string): string | null { + const match = /^\/cloud-agent\/sessions\/([^/]+)$/.exec(directory); + if (!match?.[1]) return null; + try { + return decodeURIComponent(match[1]); + } catch { + return null; + } +} + +export function isSessionIngestKiloSessionId(kiloSessionId: string): boolean { + return kiloSessionId.startsWith('ses_') && kiloSessionId.length === 30; +} + +export function validateIdScopedSelectors( + url: URL, + kiloSessionId: string, + paginationKeys: Set +): Response | null { + for (const key of url.searchParams.keys()) { + if (paginationKeys.has(key)) continue; + if (key !== 'directory') return unsupportedSessionSelectorResponse(); + } + if (hasDuplicateQueryParameters(url.searchParams)) { + return duplicateQueryParametersResponse(); + } + const directory = url.searchParams.get('directory'); + if (directory !== null && directory !== publicCloudAgentDirectory(kiloSessionId)) { + return unsupportedSessionSelectorResponse(); + } + return null; +} + +export async function readBoundedBody( + response: Response, + maximumBytes: number +): Promise { + if (!response.body) return new Uint8Array(); + const reader = response.body.getReader(); + const chunks: Uint8Array[] = []; + let byteLength = 0; + for (;;) { + const chunk = await reader.read(); + if (chunk.done) break; + const value: unknown = chunk.value; + if (!(value instanceof Uint8Array) || byteLength + value.byteLength > maximumBytes) { + await reader.cancel().catch(() => undefined); + return null; + } + chunks.push(value); + byteLength += value.byteLength; + } + const bytes = new Uint8Array(byteLength); + let offset = 0; + for (const chunk of chunks) { + bytes.set(chunk, offset); + offset += chunk.byteLength; + } + return bytes; +} diff --git a/services/cloud-agent-next/src/kilo-facade/live-interaction.test.ts b/services/cloud-agent-next/src/kilo-facade/live-interaction.test.ts new file mode 100644 index 0000000000..33aaf3be12 --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/live-interaction.test.ts @@ -0,0 +1,325 @@ +import { describe, expect, it, vi } from 'vitest'; +import type { KiloFacadeHandlerContext } from './contracts.js'; +import { openSelectedRootLiveInteraction, readInteractionRequestBody } from './live-interaction.js'; + +vi.mock('@cloudflare/sandbox', () => ({ + getSandbox: vi.fn(), + Sandbox: class Sandbox {}, +})); + +const rootSessionID = 'ses_12345678901234567890123456'; +const otherSessionID = 'ses_22222222222222222222222222'; +const maximumInteractionBodyBytes = 512 * 1024; + +function context(params: { + url?: string; + root?: { cloudAgentSessionId: string } | null; + target?: { sandbox: { containerFetch: ReturnType }; port: number } | null; +}): KiloFacadeHandlerContext { + const request = new Request( + params.url ?? + `https://facade.test/kilo/question?directory=/cloud-agent/sessions/${rootSessionID}` + ); + return { + request, + env: {} as KiloFacadeHandlerContext['env'], + userId: 'user-1', + url: new URL(request.url), + kiloPath: '/question', + deps: { + resolveRootSessionForKiloSession: vi + .fn() + .mockResolvedValue(params.root ?? { cloudAgentSessionId: 'cloud-1' }), + resolveLiveWrapper: vi.fn().mockResolvedValue(params.target ?? null), + }, + }; +} + +const isEntry = ( + value: unknown +): value is Record & { id: string; sessionID: string } => + typeof value === 'object' && + value !== null && + typeof Reflect.get(value, 'id') === 'string' && + typeof Reflect.get(value, 'sessionID') === 'string'; + +describe('interaction request body', () => { + const validBody = (value: unknown): boolean => + typeof value === 'object' && value !== null && Reflect.get(value, 'accepted') === true; + + it('rejects an oversized declared content length before ownership or wrapper resolution', async () => { + const testContext = context({}); + testContext.request = new Request(testContext.request.url, { + method: 'POST', + headers: { 'content-length': String(maximumInteractionBodyBytes + 1) }, + body: JSON.stringify({ accepted: true }), + }); + + const response = await readInteractionRequestBody(testContext.request, validBody); + + expect(response).toBeInstanceOf(Response); + expect((response as Response).status).toBe(400); + await expect((response as Response).json()).resolves.toMatchObject({ + error: 'KILO_INTERACTION_BODY_INVALID', + }); + expect(testContext.deps?.resolveRootSessionForKiloSession).not.toHaveBeenCalled(); + expect(testContext.deps?.resolveLiveWrapper).not.toHaveBeenCalled(); + }); + + it('rejects a streamed body exceeding the limit before ownership or wrapper resolution', async () => { + const testContext = context({}); + const oversizedChunk = new Uint8Array(maximumInteractionBodyBytes + 1); + const requestInit: RequestInit & { duplex: 'half' } = { + method: 'POST', + body: new ReadableStream({ + start(controller) { + controller.enqueue(oversizedChunk); + controller.close(); + }, + }), + duplex: 'half', + }; + testContext.request = new Request(testContext.request.url, requestInit); + + expect(testContext.request.headers.has('content-length')).toBe(false); + const response = await readInteractionRequestBody(testContext.request, validBody); + + expect(response).toBeInstanceOf(Response); + expect((response as Response).status).toBe(400); + await expect((response as Response).json()).resolves.toMatchObject({ + error: 'KILO_INTERACTION_BODY_INVALID', + }); + expect(testContext.deps?.resolveRootSessionForKiloSession).not.toHaveBeenCalled(); + expect(testContext.deps?.resolveLiveWrapper).not.toHaveBeenCalled(); + }); +}); + +describe('selected-root live interaction', () => { + it('requires exactly one public directory selector before resolving a wrapper', async () => { + for (const url of [ + 'https://facade.test/kilo/question', + `https://facade.test/kilo/question?directory=/cloud-agent/sessions/${rootSessionID}&directory=/cloud-agent/sessions/${rootSessionID}`, + `https://facade.test/kilo/question?directory=/cloud-agent/sessions/${rootSessionID}&limit=1`, + ]) { + const testContext = context({ url }); + const result = await openSelectedRootLiveInteraction(testContext); + expect(result).toBeInstanceOf(Response); + expect((result as Response).status).toBe(400); + expect(testContext.deps?.resolveLiveWrapper).not.toHaveBeenCalled(); + } + }); + + it('resolves ownership before returning the stable cold-runtime error', async () => { + const testContext = context({ target: null }); + const result = await openSelectedRootLiveInteraction(testContext); + expect(testContext.deps?.resolveRootSessionForKiloSession).toHaveBeenCalledWith( + expect.objectContaining({ kiloSessionId: rootSessionID }) + ); + expect(result).toBeInstanceOf(Response); + expect((result as Response).status).toBe(503); + await expect((result as Response).json()).resolves.toMatchObject({ + error: 'KILO_LIVE_RUNTIME_UNAVAILABLE', + }); + }); + + it('filters lists and guards mutations on one resolved target', async () => { + const containerFetch = vi + .fn() + .mockResolvedValueOnce( + Response.json([ + { id: 'root-request', sessionID: rootSessionID }, + { id: 'foreign-request', sessionID: otherSessionID }, + ]) + ) + .mockResolvedValueOnce(Response.json([{ id: 'foreign-request', sessionID: otherSessionID }])); + const testContext = context({ target: { sandbox: { containerFetch }, port: 4312 } }); + const result = await openSelectedRootLiveInteraction(testContext); + expect(result).not.toBeInstanceOf(Response); + if (result instanceof Response) return; + + await expect(result.list({ path: '/question', isEntry })).resolves.toEqual([ + { id: 'root-request', sessionID: rootSessionID }, + ]); + const mutation = await result.mutate({ + listPath: '/question', + mutationPath: '/question/foreign-request/reject', + requestID: 'foreign-request', + isEntry, + }); + expect(mutation.status).toBe(404); + expect(containerFetch).toHaveBeenCalledTimes(2); + expect(testContext.deps?.resolveLiveWrapper).toHaveBeenCalledTimes(1); + }); + + it('returns stable unavailable when a resolved target list fetch rejects', async () => { + const containerFetch = vi.fn().mockRejectedValue(new Error('container unavailable')); + const result = await openSelectedRootLiveInteraction( + context({ target: { sandbox: { containerFetch }, port: 4312 } }) + ); + expect(result).not.toBeInstanceOf(Response); + if (result instanceof Response) return; + + const list = await result.list({ path: '/question', isEntry }); + + expect(list).toBeInstanceOf(Response); + expect((list as Response).status).toBe(503); + expect((list as Response).headers.get('Retry-After')).toBe('1'); + await expect((list as Response).json()).resolves.toMatchObject({ + error: 'KILO_LIVE_RUNTIME_UNAVAILABLE', + }); + }); + + it('maps wrapper runtime and proxy list failures to stable unavailable responses', async () => { + for (const [wrapperError, status] of [ + ['KILO_RUNTIME_UNAVAILABLE', 503], + ['KILO_PROXY_ERROR', 502], + ] as const) { + const containerFetch = vi + .fn() + .mockResolvedValue(Response.json({ error: wrapperError }, { status })); + const result = await openSelectedRootLiveInteraction( + context({ target: { sandbox: { containerFetch }, port: 4312 } }) + ); + expect(result).not.toBeInstanceOf(Response); + if (result instanceof Response) continue; + + const list = await result.list({ path: '/question', isEntry }); + expect(list).toBeInstanceOf(Response); + expect((list as Response).status).toBe(503); + expect((list as Response).headers.get('Retry-After')).toBe('1'); + await expect((list as Response).json()).resolves.toMatchObject({ + error: 'KILO_LIVE_RUNTIME_UNAVAILABLE', + }); + } + }); + + it('returns stable unavailable when mutation preflight succeeds but mutation fetch rejects', async () => { + const containerFetch = vi + .fn() + .mockResolvedValueOnce(Response.json([{ id: 'root-request', sessionID: rootSessionID }])) + .mockRejectedValueOnce(new Error('container unavailable')); + const result = await openSelectedRootLiveInteraction( + context({ target: { sandbox: { containerFetch }, port: 4312 } }) + ); + expect(result).not.toBeInstanceOf(Response); + if (result instanceof Response) return; + + const mutation = await result.mutate({ + listPath: '/question', + mutationPath: '/question/root-request/reject', + requestID: 'root-request', + isEntry, + }); + + expect(mutation.status).toBe(503); + expect(mutation.headers.get('Retry-After')).toBe('1'); + await expect(mutation.json()).resolves.toMatchObject({ + error: 'KILO_LIVE_RUNTIME_UNAVAILABLE', + }); + expect(containerFetch).toHaveBeenCalledTimes(2); + }); + + it('maps wrapper runtime and proxy mutation failures to stable unavailable responses', async () => { + for (const [wrapperError, status] of [ + ['KILO_RUNTIME_UNAVAILABLE', 503], + ['KILO_PROXY_ERROR', 502], + ] as const) { + const containerFetch = vi + .fn() + .mockResolvedValueOnce(Response.json([{ id: 'root-request', sessionID: rootSessionID }])) + .mockResolvedValueOnce(Response.json({ error: wrapperError }, { status })); + const result = await openSelectedRootLiveInteraction( + context({ target: { sandbox: { containerFetch }, port: 4312 } }) + ); + expect(result).not.toBeInstanceOf(Response); + if (result instanceof Response) continue; + + const mutation = await result.mutate({ + listPath: '/question', + mutationPath: '/question/root-request/reject', + requestID: 'root-request', + isEntry, + }); + expect(mutation.status).toBe(503); + await expect(mutation.json()).resolves.toMatchObject({ + error: 'KILO_LIVE_RUNTIME_UNAVAILABLE', + }); + } + }); + + it('preserves genuine native mutation responses', async () => { + const nativeResponse = Response.json( + { name: 'BadRequestError' }, + { status: 502, headers: { 'x-native': 'preserved' } } + ); + const containerFetch = vi + .fn() + .mockResolvedValueOnce(Response.json([{ id: 'root-request', sessionID: rootSessionID }])) + .mockResolvedValueOnce(nativeResponse); + const result = await openSelectedRootLiveInteraction( + context({ target: { sandbox: { containerFetch }, port: 4312 } }) + ); + expect(result).not.toBeInstanceOf(Response); + if (result instanceof Response) return; + + const mutation = await result.mutate({ + listPath: '/question', + mutationPath: '/question/root-request/reject', + requestID: 'root-request', + isEntry, + }); + expect(mutation.status).toBe(502); + expect(mutation.headers.get('x-native')).toBe('preserved'); + await expect(mutation.json()).resolves.toEqual({ name: 'BadRequestError' }); + }); + + it('rejects more than 1,000 individually valid entries within the byte limit', async () => { + const entries = Array.from({ length: 1_001 }, (_, index) => ({ + id: `request-${index}`, + sessionID: rootSessionID, + })); + const responseBody = JSON.stringify(entries); + expect(new TextEncoder().encode(responseBody).byteLength).toBeLessThan( + maximumInteractionBodyBytes + ); + const containerFetch = vi.fn().mockResolvedValue( + new Response(responseBody, { + headers: { 'content-type': 'application/json' }, + }) + ); + const result = await openSelectedRootLiveInteraction( + context({ target: { sandbox: { containerFetch }, port: 4312 } }) + ); + expect(result).not.toBeInstanceOf(Response); + if (result instanceof Response) return; + + const list = await result.list({ path: '/question', isEntry }); + + expect(list).toBeInstanceOf(Response); + expect((list as Response).status).toBe(502); + await expect((list as Response).json()).resolves.toMatchObject({ + error: 'KILO_UPSTREAM_RESPONSE_INVALID', + }); + }); + + it('returns a stable invalid-upstream error for malformed or oversized lists', async () => { + for (const response of [ + new Response('{not json', { headers: { 'content-type': 'application/json' } }), + Response.json([{ id: 'missing-session' }]), + new Response('[]', { headers: { 'content-length': String(512 * 1024 + 1) } }), + ]) { + const containerFetch = vi.fn().mockResolvedValue(response); + const result = await openSelectedRootLiveInteraction( + context({ target: { sandbox: { containerFetch }, port: 4312 } }) + ); + expect(result).not.toBeInstanceOf(Response); + if (result instanceof Response) continue; + const list = await result.list({ path: '/question', isEntry }); + expect(list).toBeInstanceOf(Response); + expect((list as Response).status).toBe(502); + await expect((list as Response).json()).resolves.toMatchObject({ + error: 'KILO_UPSTREAM_RESPONSE_INVALID', + }); + } + }); +}); diff --git a/services/cloud-agent-next/src/kilo-facade/live-interaction.ts b/services/cloud-agent-next/src/kilo-facade/live-interaction.ts new file mode 100644 index 0000000000..54491ee3be --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/live-interaction.ts @@ -0,0 +1,199 @@ +import { createProxyRequest } from '../shared/http-proxy.js'; +import type { KiloFacadeHandlerContext, SelectedOwnedRootHandlerContext } from './contracts.js'; +import { + invalidInteractionRequestBodyResponse, + invalidUpstreamResponse, + liveRuntimeUnavailableResponse, + missingInteractionRequestResponse, + readBoundedBody, +} from './http-contract.js'; +import { resolveSelectedOwnedRoot } from './ownership.js'; +import { + buildWrapperKiloProxyUrl, + resolveLiveWrapperTarget, + type LiveWrapperTarget, +} from './session-proxy.js'; + +const MAX_INTERACTION_JSON_BYTES = 512 * 1024; +const MAX_INTERACTION_ENTRIES = 1_000; + +export type LiveInteractionRequest = Record & { id: string; sessionID: string }; +export type LiveInteractionValidator = ( + value: unknown +) => value is T; + +export type SelectedRootLiveInteraction = { + context: SelectedOwnedRootHandlerContext; + list: (params: { + path: string; + isEntry: LiveInteractionValidator; + }) => Promise; + mutate: (params: { + listPath: string; + mutationPath: string; + requestID: string; + isEntry: LiveInteractionValidator; + body?: Uint8Array; + }) => Promise; +}; + +export type OpenSelectedRootLiveInteractionResult = SelectedRootLiveInteraction | Response; + +function isBoundedContentLength(headers: Headers): boolean { + const declaredLength = headers.get('content-length'); + if (declaredLength === null) return true; + const byteLength = Number(declaredLength); + return ( + Number.isSafeInteger(byteLength) && byteLength >= 0 && byteLength <= MAX_INTERACTION_JSON_BYTES + ); +} + +async function readBoundedJson(response: Response): Promise { + if (!isBoundedContentLength(response.headers)) return invalidUpstreamResponse(); + const bytes = await readBoundedBody(response, MAX_INTERACTION_JSON_BYTES); + if (!bytes) return invalidUpstreamResponse(); + try { + return JSON.parse(new TextDecoder().decode(bytes)); + } catch { + return invalidUpstreamResponse(); + } +} + +function listIsValid( + value: unknown, + isEntry: LiveInteractionValidator +): value is T[] { + return Array.isArray(value) && value.length <= MAX_INTERACTION_ENTRIES && value.every(isEntry); +} + +async function fetchOnTarget(params: { + target: LiveWrapperTarget; + sourceRequest: Request; + path: string; + body?: Uint8Array; +}): Promise { + const targetUrl = buildWrapperKiloProxyUrl({ + wrapperPort: params.target.port, + kiloRelativePath: params.path, + search: '', + }); + const sourceRequest = new Request(params.sourceRequest.url, { + method: params.sourceRequest.method, + headers: params.sourceRequest.headers, + ...(params.body ? { body: params.body } : {}), + }); + try { + return await params.target.sandbox.containerFetch( + createProxyRequest(sourceRequest, new URL(targetUrl)), + params.target.port + ); + } catch { + return null; + } +} + +async function isUnavailableRuntimeResponse(response: Response): Promise { + if (response.status !== 502 && response.status !== 503) return false; + const parsed = await readBoundedJson(response.clone()); + return ( + !(parsed instanceof Response) && + typeof parsed === 'object' && + parsed !== null && + (Reflect.get(parsed, 'error') === 'KILO_RUNTIME_UNAVAILABLE' || + Reflect.get(parsed, 'error') === 'KILO_PROXY_ERROR') + ); +} + +async function listSelectedRoot(params: { + target: LiveWrapperTarget; + sourceRequest: Request; + path: string; + kiloSessionId: string; + isEntry: LiveInteractionValidator; +}): Promise { + const response = await fetchOnTarget({ + target: params.target, + sourceRequest: new Request(params.sourceRequest.url, { method: 'GET' }), + path: params.path, + }); + if (!response) return liveRuntimeUnavailableResponse(); + if (!response.ok) + return (await isUnavailableRuntimeResponse(response)) + ? liveRuntimeUnavailableResponse() + : response; + const parsed = await readBoundedJson(response); + if (parsed instanceof Response) return parsed; + if (!listIsValid(parsed, params.isEntry)) return invalidUpstreamResponse(); + return parsed.filter(entry => entry.sessionID === params.kiloSessionId); +} + +export async function readInteractionRequestBody( + request: Request, + validate: (value: unknown) => boolean +): Promise { + if (!isBoundedContentLength(request.headers)) return invalidInteractionRequestBodyResponse(); + const bytes = await readBoundedBody(new Response(request.body), MAX_INTERACTION_JSON_BYTES); + if (!bytes) return invalidInteractionRequestBodyResponse(); + let parsed: unknown; + try { + parsed = JSON.parse(new TextDecoder().decode(bytes)); + } catch { + return invalidInteractionRequestBodyResponse(); + } + return validate(parsed) ? bytes : invalidInteractionRequestBodyResponse(); +} + +async function openOwnedRootLiveInteraction( + selected: SelectedOwnedRootHandlerContext +): Promise { + let target: LiveWrapperTarget | null; + try { + target = await (selected.deps?.resolveLiveWrapper ?? resolveLiveWrapperTarget)({ + env: selected.env, + userId: selected.userId, + cloudAgentSessionId: selected.cloudAgentSessionId, + }); + } catch { + return liveRuntimeUnavailableResponse(); + } + if (!target) return liveRuntimeUnavailableResponse(); + + const resolvedTarget = target; + const list: SelectedRootLiveInteraction['list'] = params => + listSelectedRoot({ + target: resolvedTarget, + sourceRequest: selected.request, + path: params.path, + kiloSessionId: selected.kiloSessionId, + isEntry: params.isEntry, + }); + + return { + context: selected, + list, + mutate: async params => { + const pending = await list({ path: params.listPath, isEntry: params.isEntry }); + if (pending instanceof Response) return pending; + if (!pending.some(entry => entry.id === params.requestID)) { + return missingInteractionRequestResponse(); + } + const response = await fetchOnTarget({ + target, + sourceRequest: selected.request, + path: params.mutationPath, + ...(params.body ? { body: params.body } : {}), + }); + if (!response || (await isUnavailableRuntimeResponse(response))) { + return liveRuntimeUnavailableResponse(); + } + return response; + }, + }; +} + +export async function openSelectedRootLiveInteraction( + context: KiloFacadeHandlerContext +): Promise { + const selected = await resolveSelectedOwnedRoot(context); + return selected instanceof Response ? selected : openOwnedRootLiveInteraction(selected); +} diff --git a/services/cloud-agent-next/src/kilo-facade/metadata-feed.test.ts b/services/cloud-agent-next/src/kilo-facade/metadata-feed.test.ts new file mode 100644 index 0000000000..7b85ca37ee --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/metadata-feed.test.ts @@ -0,0 +1,127 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; +import type { Env } from '../types'; +import { FacadeMetadataFeed } from './metadata-feed'; + +class ClientSocket extends EventTarget { + accept = vi.fn(); + close = vi.fn(); +} + +function callbacks() { + return { + onMessage: vi.fn(), + onConnected: vi.fn(), + onStopped: vi.fn(), + }; +} + +afterEach(() => { + vi.useRealTimers(); +}); + +describe('FacadeMetadataFeed', () => { + it('cancels reconnect work when final demand ends', async () => { + vi.useFakeTimers(); + const fetch = vi.fn().mockRejectedValue(new Error('temporarily unavailable')); + const env = { + INTERNAL_API_SECRET_PROD: { get: vi.fn().mockResolvedValue('secret') }, + SESSION_INGEST: { fetch }, + } as unknown as Env; + const feed = new FacadeMetadataFeed(env, callbacks()); + + feed.addDemand('usr_1'); + await vi.advanceTimersByTimeAsync(0); + expect(fetch).toHaveBeenCalledOnce(); + + feed.removeDemand(); + await vi.advanceTimersByTimeAsync(10_000); + expect(fetch).toHaveBeenCalledOnce(); + }); + + it('reconnects after an active socket disconnects while demand remains', async () => { + vi.useFakeTimers(); + const firstSocket = new ClientSocket(); + const secondSocket = new ClientSocket(); + const fetch = vi + .fn() + .mockResolvedValueOnce({ status: 101, webSocket: firstSocket }) + .mockResolvedValueOnce({ status: 101, webSocket: secondSocket }); + const env = { + INTERNAL_API_SECRET_PROD: { get: vi.fn().mockResolvedValue('secret') }, + SESSION_INGEST: { fetch }, + } as unknown as Env; + const connected = callbacks(); + const feed = new FacadeMetadataFeed(env, connected); + + feed.addDemand('usr_1'); + await vi.advanceTimersByTimeAsync(0); + expect(firstSocket.accept).toHaveBeenCalledOnce(); + expect(connected.onConnected).toHaveBeenCalledOnce(); + + firstSocket.dispatchEvent(new Event('close')); + await vi.advanceTimersByTimeAsync(250); + + expect(fetch).toHaveBeenCalledTimes(2); + expect(secondSocket.accept).toHaveBeenCalledOnce(); + expect(connected.onConnected).toHaveBeenCalledTimes(2); + feed.removeDemand(); + }); + + it('keeps backing off when successful sockets close before becoming stable', async () => { + vi.useFakeTimers(); + const firstSocket = new ClientSocket(); + const secondSocket = new ClientSocket(); + const thirdSocket = new ClientSocket(); + const fetch = vi + .fn() + .mockResolvedValueOnce({ status: 101, webSocket: firstSocket }) + .mockResolvedValueOnce({ status: 101, webSocket: secondSocket }) + .mockResolvedValueOnce({ status: 101, webSocket: thirdSocket }); + const env = { + INTERNAL_API_SECRET_PROD: { get: vi.fn().mockResolvedValue('secret') }, + SESSION_INGEST: { fetch }, + } as unknown as Env; + const feed = new FacadeMetadataFeed(env, callbacks()); + + feed.addDemand('usr_1'); + await vi.advanceTimersByTimeAsync(0); + firstSocket.dispatchEvent(new Event('close')); + await vi.advanceTimersByTimeAsync(250); + expect(fetch).toHaveBeenCalledTimes(2); + + secondSocket.dispatchEvent(new Event('close')); + await vi.advanceTimersByTimeAsync(499); + expect(fetch).toHaveBeenCalledTimes(2); + await vi.advanceTimersByTimeAsync(1); + expect(fetch).toHaveBeenCalledTimes(3); + + feed.removeDemand(); + }); + + it('closes a late successful socket after demand has ended', async () => { + const socket = new ClientSocket(); + let resolveFetch: ((response: { status: number; webSocket: ClientSocket }) => void) | undefined; + const fetch = vi.fn( + () => + new Promise<{ status: number; webSocket: ClientSocket }>( + resolve => (resolveFetch = resolve) + ) + ); + const env = { + INTERNAL_API_SECRET_PROD: { get: vi.fn().mockResolvedValue('secret') }, + SESSION_INGEST: { fetch }, + } as unknown as Env; + const connected = callbacks(); + const feed = new FacadeMetadataFeed(env, connected); + + feed.addDemand('usr_1'); + await vi.waitFor(() => expect(fetch).toHaveBeenCalledOnce()); + feed.removeDemand(); + resolveFetch?.({ status: 101, webSocket: socket }); + await Promise.resolve(); + + expect(socket.accept).not.toHaveBeenCalled(); + expect(socket.close).toHaveBeenCalledWith(1000, 'Facade metadata demand ended'); + expect(connected.onConnected).not.toHaveBeenCalled(); + }); +}); diff --git a/services/cloud-agent-next/src/kilo-facade/metadata-feed.ts b/services/cloud-agent-next/src/kilo-facade/metadata-feed.ts new file mode 100644 index 0000000000..18e9bbf715 --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/metadata-feed.ts @@ -0,0 +1,166 @@ +import { + sessionUpdatedMetadataMessageSchema, + type SessionUpdatedMetadataMessage, +} from '@kilocode/session-ingest-contracts'; +import type { Env } from '../types.js'; + +const INITIAL_RECONNECT_DELAY_MS = 250; +const MAX_RECONNECT_DELAY_MS = 4_000; +const RECONNECT_STABILITY_MS = 10_000; + +type MetadataFeedCallbacks = { + onMessage(message: SessionUpdatedMetadataMessage, generation: number): void; + onConnected(generation: number): void; + onStopped(): void; +}; + +type SocketListeners = { + message: EventListener; + close: EventListener; + error: EventListener; +}; + +type MetadataDemand = { + generation: number; + started: boolean; +}; + +export class FacadeMetadataFeed { + private demand = 0; + private generation = 0; + private userId?: string; + private connectionId?: string; + private socket?: WebSocket; + private listeners?: SocketListeners; + private retryTimer?: ReturnType; + private stabilityTimer?: ReturnType; + private reconnectDelayMs = INITIAL_RECONNECT_DELAY_MS; + + constructor( + private readonly env: Env, + private readonly callbacks: MetadataFeedCallbacks + ) {} + + addDemand(userId: string): MetadataDemand { + this.demand += 1; + if (this.demand !== 1) return { generation: this.generation, started: false }; + this.userId = userId; + this.connectionId = `facade-${crypto.randomUUID()}`; + this.generation += 1; + void this.connect(this.generation); + return { generation: this.generation, started: true }; + } + + removeDemand(): void { + if (this.demand === 0) return; + this.demand -= 1; + if (this.demand > 0) return; + this.stop(); + } + + isCurrent(generation: number): boolean { + return this.demand > 0 && this.generation === generation; + } + + userIdForCurrentDemand(): string { + return this.userId ?? ''; + } + + private async connect(generation: number): Promise { + const userId = this.userId; + const connectionId = this.connectionId; + if (!userId || !connectionId || !this.isCurrent(generation)) return; + try { + const secret = await this.env.INTERNAL_API_SECRET_PROD.get(); + if (!this.isCurrent(generation)) return; + const response = await this.env.SESSION_INGEST.fetch( + `https://session-ingest/internal/user/events?connectionId=${encodeURIComponent(connectionId)}`, + { + headers: { + Upgrade: 'websocket', + 'X-Internal-Secret': secret, + 'X-Kilo-User-Id': userId, + }, + } + ); + if (!this.isCurrent(generation)) { + response.webSocket?.close(1000, 'Facade metadata demand ended'); + return; + } + if (response.status !== 101 || !response.webSocket) { + response.webSocket?.close(1002, 'Session metadata upgrade failed'); + this.scheduleReconnect(generation); + return; + } + const socket = response.webSocket; + socket.accept(); + const message: EventListener = event => { + if (!this.isCurrent(generation) || !(event instanceof MessageEvent)) return; + if (typeof event.data !== 'string') return; + try { + const parsed = sessionUpdatedMetadataMessageSchema.safeParse(JSON.parse(event.data)); + if (parsed.success) this.callbacks.onMessage(parsed.data, generation); + } catch { + // Malformed metadata is non-fatal and ignored. + } + }; + const disconnected: EventListener = () => { + if (!this.isCurrent(generation) || this.socket !== socket) return; + this.detachSocket(); + socket.close(1001, 'Session metadata connection ended'); + this.scheduleReconnect(generation); + }; + this.socket = socket; + this.listeners = { message, close: disconnected, error: disconnected }; + socket.addEventListener('message', message); + socket.addEventListener('close', disconnected); + socket.addEventListener('error', disconnected); + this.stabilityTimer = setTimeout(() => { + this.stabilityTimer = undefined; + if (this.isCurrent(generation) && this.socket === socket) { + this.reconnectDelayMs = INITIAL_RECONNECT_DELAY_MS; + } + }, RECONNECT_STABILITY_MS); + this.callbacks.onConnected(generation); + } catch { + if (this.isCurrent(generation)) this.scheduleReconnect(generation); + } + } + + private scheduleReconnect(generation: number): void { + if (!this.isCurrent(generation) || this.retryTimer) return; + const delay = this.reconnectDelayMs; + this.reconnectDelayMs = Math.min(this.reconnectDelayMs * 2, MAX_RECONNECT_DELAY_MS); + this.retryTimer = setTimeout(() => { + this.retryTimer = undefined; + void this.connect(generation); + }, delay); + } + + private detachSocket(): void { + if (this.stabilityTimer) clearTimeout(this.stabilityTimer); + this.stabilityTimer = undefined; + const socket = this.socket; + const listeners = this.listeners; + if (socket && listeners) { + socket.removeEventListener('message', listeners.message); + socket.removeEventListener('close', listeners.close); + socket.removeEventListener('error', listeners.error); + } + this.socket = undefined; + this.listeners = undefined; + } + + private stop(): void { + this.generation += 1; + if (this.retryTimer) clearTimeout(this.retryTimer); + this.retryTimer = undefined; + const socket = this.socket; + this.detachSocket(); + socket?.close(1000, 'Facade metadata demand ended'); + this.userId = undefined; + this.connectionId = undefined; + this.reconnectDelayMs = INITIAL_RECONNECT_DELAY_MS; + this.callbacks.onStopped(); + } +} diff --git a/services/cloud-agent-next/src/kilo-facade/metadata-projector.test.ts b/services/cloud-agent-next/src/kilo-facade/metadata-projector.test.ts new file mode 100644 index 0000000000..3f0945411d --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/metadata-projector.test.ts @@ -0,0 +1,197 @@ +import type { SessionUpdatedMetadataMessage } from '@kilocode/session-ingest-contracts'; +import { describe, expect, it, vi } from 'vitest'; +import type { Env } from '../types'; +import type { KiloGlobalEventEnvelope } from './handlers/global-feed-upgrade/message'; +import { FacadeMetadataProjector } from './metadata-projector'; + +const kiloSessionId = 'ses_12345678901234567890123456'; + +function metadataMessage(): SessionUpdatedMetadataMessage { + return { + type: 'system', + event: 'session.updated', + data: { + source: 'v2', + changedAt: '2026-06-08T20:00:00.000Z', + session: { + source: 'v2', + sessionId: kiloSessionId, + createdAt: '2026-06-08T19:00:00.000Z', + updatedAt: '2026-06-08T20:00:00.000Z', + title: 'notification only', + createdOnPlatform: null, + organizationId: null, + gitUrl: null, + gitBranch: null, + parentSessionId: null, + status: null, + statusUpdatedAt: null, + }, + }, + }; +} + +function sessionInfo(title: string, updated: number) { + return { + id: kiloSessionId, + slug: 'session', + projectID: 'project', + directory: '/private/workspace', + path: '/private/workspace', + title, + version: '1.0.0', + time: { created: 100, updated }, + }; +} + +function snapshot(title: string, updated: number) { + return { + kiloSessionId, + cloudAgentSessionId: 'agent_1', + snapshot: { kind: 'value' as const, info: sessionInfo(title, updated), byteLength: 100 }, + }; +} + +function wrapperEvent(title: string, updated: number): KiloGlobalEventEnvelope { + return { + directory: '/private/workspace', + payload: { + type: 'session.updated', + properties: { sessionID: kiloSessionId, info: sessionInfo(title, updated) }, + }, + }; +} + +function setup(getSnapshot: ReturnType) { + const broadcast = vi.fn(); + const env = { + SESSION_INGEST: { getCloudAgentRootSessionSnapshot: getSnapshot }, + } as unknown as Env; + const projector = new FacadeMetadataProjector(env, broadcast, generation => generation === 1); + projector.start(1, 'usr_1'); + return { broadcast, projector }; +} + +describe('FacadeMetadataProjector', () => { + it('coalesces a newer metadata notification while a snapshot read is in flight', async () => { + let resolveFirst: ((value: ReturnType) => void) | undefined; + const getSnapshot = vi + .fn() + .mockImplementationOnce(() => new Promise(resolve => (resolveFirst = resolve))) + .mockResolvedValueOnce(snapshot('newest', 300)); + const { broadcast, projector } = setup(getSnapshot); + + projector.notify(metadataMessage(), 1); + projector.notify(metadataMessage(), 1); + resolveFirst?.(snapshot('stale', 200)); + + await vi.waitFor(() => expect(getSnapshot).toHaveBeenCalledTimes(2)); + await vi.waitFor(() => expect(broadcast).toHaveBeenCalledOnce()); + expect(broadcast.mock.calls[0]?.[0]).toMatchObject({ + payload: { properties: { info: { title: 'newest' } } }, + }); + }); + + it('reruns after a wrapper update invalidates an in-flight read without regressing it', async () => { + let resolveFirst: ((value: ReturnType) => void) | undefined; + const getSnapshot = vi + .fn() + .mockImplementationOnce(() => new Promise(resolve => (resolveFirst = resolve))) + .mockResolvedValueOnce(snapshot('persisted older', 200)); + const { broadcast, projector } = setup(getSnapshot); + + projector.notify(metadataMessage(), 1); + projector.recordWrapperEvent(wrapperEvent('wrapper newer', 300), kiloSessionId); + resolveFirst?.(snapshot('stale', 200)); + + await vi.waitFor(() => expect(getSnapshot).toHaveBeenCalledTimes(2)); + await new Promise(resolve => setTimeout(resolve, 0)); + expect(broadcast).not.toHaveBeenCalled(); + }); + + it('allows one differing equal-version canonical replacement and suppresses duplicates', async () => { + const getSnapshot = vi.fn().mockResolvedValue(snapshot('canonical', 200)); + const { broadcast, projector } = setup(getSnapshot); + + expect(projector.recordWrapperEvent(wrapperEvent('wrapper', 200), kiloSessionId)).toBe(true); + projector.notify(metadataMessage(), 1); + await vi.waitFor(() => expect(broadcast).toHaveBeenCalledOnce()); + + expect(projector.recordWrapperEvent(wrapperEvent('equal wrapper', 200), kiloSessionId)).toBe( + false + ); + expect(projector.recordWrapperEvent(wrapperEvent('older wrapper', 100), kiloSessionId)).toBe( + false + ); + projector.notify(metadataMessage(), 1); + await vi.waitFor(() => expect(getSnapshot).toHaveBeenCalledTimes(2)); + await new Promise(resolve => setTimeout(resolve, 0)); + expect(broadcast).toHaveBeenCalledOnce(); + }); + + it('bounds concurrent and pending refresh work across many sessions', () => { + const getSnapshot = vi.fn(() => new Promise(() => {})); + const { projector } = setup(getSnapshot); + const sessionIds = Array.from( + { length: 300 }, + (_, index) => `ses_${index.toString().padStart(26, '0')}` + ); + + projector.reconcile(sessionIds, 1); + + const state = projector as unknown as { refreshes: Map }; + expect(getSnapshot.mock.calls.length).toBeLessThanOrEqual(8); + expect(state.refreshes.size).toBeLessThanOrEqual(256); + }); + + it('starts a pending refresh when an active refresh completes', async () => { + let resolveFirst: ((value: null) => void) | undefined; + const getSnapshot = vi.fn(() => { + if (getSnapshot.mock.calls.length === 1) { + return new Promise(resolve => (resolveFirst = resolve)); + } + return new Promise(() => {}); + }); + const { projector } = setup(getSnapshot); + const sessionIds = Array.from( + { length: 9 }, + (_, index) => `ses_${index.toString().padStart(26, '0')}` + ); + + projector.reconcile(sessionIds, 1); + expect(getSnapshot).toHaveBeenCalledTimes(8); + resolveFirst?.(null); + + await vi.waitFor(() => expect(getSnapshot).toHaveBeenCalledTimes(9)); + }); + + it('bounds retained version state for long-lived global streams', () => { + const { projector } = setup(vi.fn()); + + for (let index = 0; index < 300; index += 1) { + const sessionId = `ses_${index.toString().padStart(26, '0')}`; + const event = wrapperEvent(`session ${index}`, index); + if (event.payload?.properties && typeof event.payload.properties === 'object') { + event.payload.properties.sessionID = sessionId; + event.payload.properties.info = { + ...sessionInfo(`session ${index}`, index), + id: sessionId, + }; + } + projector.recordWrapperEvent(event, sessionId); + } + + const state = projector as unknown as { versions: Map }; + expect(state.versions.size).toBeLessThanOrEqual(256); + }); + + it('ignores a notification when the owned-root snapshot is unavailable', async () => { + const getSnapshot = vi.fn().mockResolvedValue(null); + const { broadcast, projector } = setup(getSnapshot); + + projector.notify(metadataMessage(), 1); + + await vi.waitFor(() => expect(getSnapshot).toHaveBeenCalledOnce()); + expect(broadcast).not.toHaveBeenCalled(); + }); +}); diff --git a/services/cloud-agent-next/src/kilo-facade/metadata-projector.ts b/services/cloud-agent-next/src/kilo-facade/metadata-projector.ts new file mode 100644 index 0000000000..ab5c67f14b --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/metadata-projector.ts @@ -0,0 +1,379 @@ +import type { SessionUpdatedMetadataMessage } from '@kilocode/session-ingest-contracts'; +import type { Env } from '../types.js'; +import type { KiloGlobalEventEnvelope } from './handlers/global-feed-upgrade/message.js'; +import { projectPublicSession, publicCloudAgentDirectory } from './public-sdk-projection.js'; + +const SNAPSHOT_RETRY_DELAY_MS = 100; +const MAX_TRACKED_SESSION_VERSIONS = 256; +const MAX_TRACKED_SESSION_REFRESHES = 256; +const MAX_CONCURRENT_SESSION_REFRESHES = 8; + +type Broadcast = (event: KiloGlobalEventEnvelope, kiloSessionId: string) => void; +type IsCurrent = (generation: number) => boolean; + +type SessionVersion = { + updated: number; + fingerprint: string; + source: 'wrapper' | 'canonical'; +}; + +type RefreshState = { + running: boolean; + dirty: boolean; + hasSlot: boolean; + generation: number; + retries: number; + timer?: ReturnType; +}; + +function sessionUpdatedInfo( + event: KiloGlobalEventEnvelope +): { updated: number; info: unknown } | null { + const payload = event.payload; + if (payload?.type !== 'session.updated') return null; + const properties = payload.properties; + if (!properties || typeof properties !== 'object') return null; + const info: unknown = Reflect.get(properties, 'info'); + if (!info || typeof info !== 'object') return null; + const time: unknown = Reflect.get(info, 'time'); + if (!time || typeof time !== 'object') return null; + const updated: unknown = Reflect.get(time, 'updated'); + return typeof updated === 'number' && Number.isFinite(updated) ? { updated, info } : null; +} + +export class FacadeMetadataProjector { + private versions = new Map(); + private refreshes = new Map(); + private pendingRefreshes = new Set(); + private activeRefreshes = 0; + private lifecycleGeneration = 0; + private currentUserId = ''; + + constructor( + private readonly env: Env, + private readonly broadcast: Broadcast, + private readonly isCurrent: IsCurrent + ) {} + + start(generation: number, userId: string): void { + this.lifecycleGeneration = generation; + this.currentUserId = userId; + } + + stop(): void { + this.lifecycleGeneration += 1; + this.currentUserId = ''; + for (const refresh of this.refreshes.values()) { + if (refresh.timer) clearTimeout(refresh.timer); + } + this.refreshes.clear(); + this.pendingRefreshes.clear(); + this.activeRefreshes = 0; + this.versions.clear(); + } + + recordWrapperEvent(event: KiloGlobalEventEnvelope, kiloSessionId: string): boolean { + if (!this.isActive(this.lifecycleGeneration)) return true; + const update = sessionUpdatedInfo(event); + if (!update) return true; + + const refresh = this.refreshes.get(kiloSessionId); + if (refresh) { + this.supersedeRefresh( + kiloSessionId, + this.lifecycleGeneration, + refresh, + refresh.generation + 1 + ); + } + + const fingerprint = JSON.stringify(update.info); + const latest = this.versions.get(kiloSessionId); + if (latest && update.updated < latest.updated) { + this.rememberVersion(kiloSessionId, latest); + return false; + } + if ( + latest && + update.updated === latest.updated && + (latest.fingerprint === fingerprint || latest.source === 'canonical') + ) { + this.rememberVersion(kiloSessionId, latest); + return false; + } + + this.rememberVersion(kiloSessionId, { + updated: update.updated, + fingerprint, + source: 'wrapper', + }); + return true; + } + + notify(message: SessionUpdatedMetadataMessage, generation: number): void { + this.requestRefresh(message.data.session.sessionId, generation); + } + + reconcile(kiloSessionIds: Iterable, generation: number): void { + for (const kiloSessionId of new Set(kiloSessionIds)) { + this.requestRefresh(kiloSessionId, generation); + } + } + + private requestRefresh(kiloSessionId: string, lifecycleGeneration: number): void { + if (!this.isActive(lifecycleGeneration)) return; + const current = this.refreshes.get(kiloSessionId); + if (current) { + this.supersedeRefresh(kiloSessionId, lifecycleGeneration, current, current.generation + 1); + return; + } + if (this.refreshes.size >= MAX_TRACKED_SESSION_REFRESHES) return; + + const refresh: RefreshState = { + running: false, + dirty: true, + hasSlot: false, + generation: 1, + retries: 0, + }; + this.refreshes.set(kiloSessionId, refresh); + this.scheduleRefresh(kiloSessionId, lifecycleGeneration, refresh); + } + + private supersedeRefresh( + kiloSessionId: string, + lifecycleGeneration: number, + refresh: RefreshState, + generation: number + ): void { + refresh.generation = generation; + refresh.dirty = true; + refresh.retries = 0; + if (refresh.timer) { + clearTimeout(refresh.timer); + refresh.timer = undefined; + } + if (!refresh.running) this.scheduleRefresh(kiloSessionId, lifecycleGeneration, refresh); + } + + private scheduleRefresh( + kiloSessionId: string, + lifecycleGeneration: number, + refresh: RefreshState + ): void { + if (refresh.hasSlot) { + this.startNextRefresh(kiloSessionId, lifecycleGeneration, refresh); + return; + } + if (this.activeRefreshes >= MAX_CONCURRENT_SESSION_REFRESHES) { + this.pendingRefreshes.add(kiloSessionId); + return; + } + refresh.hasSlot = true; + this.activeRefreshes += 1; + this.startNextRefresh(kiloSessionId, lifecycleGeneration, refresh); + } + + private startNextRefresh( + kiloSessionId: string, + lifecycleGeneration: number, + refresh: RefreshState + ): void { + if (!this.isActive(lifecycleGeneration) || this.refreshes.get(kiloSessionId) !== refresh) { + this.removeRefresh(kiloSessionId, refresh); + return; + } + if (refresh.running || refresh.timer) return; + refresh.running = true; + refresh.dirty = false; + const capturedGeneration = refresh.generation; + void this.runRefresh(kiloSessionId, lifecycleGeneration, refresh, capturedGeneration); + } + + private async runRefresh( + kiloSessionId: string, + lifecycleGeneration: number, + refresh: RefreshState, + capturedGeneration: number + ): Promise { + let result; + try { + result = await this.env.SESSION_INGEST.getCloudAgentRootSessionSnapshot({ + kiloUserId: this.currentUserId, + kiloSessionId, + }); + } catch { + if (!this.isRefreshCurrent(kiloSessionId, lifecycleGeneration, refresh, capturedGeneration)) { + this.finishStaleRefresh(kiloSessionId, lifecycleGeneration, refresh, capturedGeneration); + return; + } + this.scheduleSnapshotRetry(kiloSessionId, lifecycleGeneration, refresh, capturedGeneration); + return; + } + + if (!this.isRefreshCurrent(kiloSessionId, lifecycleGeneration, refresh, capturedGeneration)) { + this.finishStaleRefresh(kiloSessionId, lifecycleGeneration, refresh, capturedGeneration); + return; + } + if (!result) { + this.finishCurrentRefresh(kiloSessionId, lifecycleGeneration, refresh); + return; + } + if (result.snapshot.kind === 'pending' || result.snapshot.kind === 'retryable_failure') { + this.scheduleSnapshotRetry(kiloSessionId, lifecycleGeneration, refresh, capturedGeneration); + return; + } + if (result.snapshot.kind !== 'value') { + this.finishCurrentRefresh(kiloSessionId, lifecycleGeneration, refresh); + return; + } + + const info = projectPublicSession(result.snapshot.info, kiloSessionId); + const updated = info.time.updated; + const fingerprint = JSON.stringify(info); + const latest = this.versions.get(kiloSessionId); + if (latest && updated < latest.updated) { + this.finishCurrentRefresh(kiloSessionId, lifecycleGeneration, refresh); + return; + } + if ( + latest && + updated === latest.updated && + (latest.fingerprint === fingerprint || latest.source === 'canonical') + ) { + this.finishCurrentRefresh(kiloSessionId, lifecycleGeneration, refresh); + return; + } + this.rememberVersion(kiloSessionId, { updated, fingerprint, source: 'canonical' }); + this.broadcast( + { + directory: publicCloudAgentDirectory(kiloSessionId), + payload: { + type: 'session.updated', + properties: { sessionID: kiloSessionId, info }, + }, + }, + kiloSessionId + ); + this.finishCurrentRefresh(kiloSessionId, lifecycleGeneration, refresh); + } + + private scheduleSnapshotRetry( + kiloSessionId: string, + lifecycleGeneration: number, + refresh: RefreshState, + capturedGeneration: number + ): void { + if (refresh.retries >= 1) { + this.finishCurrentRefresh(kiloSessionId, lifecycleGeneration, refresh); + return; + } + refresh.retries += 1; + refresh.running = false; + refresh.timer = setTimeout(() => { + refresh.timer = undefined; + if (!this.isRefreshCurrent(kiloSessionId, lifecycleGeneration, refresh, capturedGeneration)) { + this.finishStaleRefresh(kiloSessionId, lifecycleGeneration, refresh, capturedGeneration); + return; + } + this.startNextRefresh(kiloSessionId, lifecycleGeneration, refresh); + }, SNAPSHOT_RETRY_DELAY_MS); + } + + private finishCurrentRefresh( + kiloSessionId: string, + lifecycleGeneration: number, + refresh: RefreshState + ): void { + if (this.refreshes.get(kiloSessionId) !== refresh) return; + refresh.running = false; + if (refresh.dirty && this.isActive(lifecycleGeneration)) { + this.startNextRefresh(kiloSessionId, lifecycleGeneration, refresh); + return; + } + this.removeRefresh(kiloSessionId, refresh); + } + + private finishStaleRefresh( + kiloSessionId: string, + lifecycleGeneration: number, + refresh: RefreshState, + capturedGeneration: number + ): void { + if (this.refreshes.get(kiloSessionId) !== refresh) return; + refresh.running = false; + if ( + this.isActive(lifecycleGeneration) && + (refresh.dirty || refresh.generation !== capturedGeneration) + ) { + this.startNextRefresh(kiloSessionId, lifecycleGeneration, refresh); + return; + } + this.removeRefresh(kiloSessionId, refresh); + } + + private removeRefresh(kiloSessionId: string, refresh: RefreshState): void { + if (this.refreshes.get(kiloSessionId) !== refresh) return; + this.refreshes.delete(kiloSessionId); + this.pendingRefreshes.delete(kiloSessionId); + if (refresh.hasSlot) { + refresh.hasSlot = false; + this.activeRefreshes -= 1; + } + this.pruneVersions(); + this.drainPendingRefreshes(); + } + + private drainPendingRefreshes(): void { + while ( + this.activeRefreshes < MAX_CONCURRENT_SESSION_REFRESHES && + this.pendingRefreshes.size > 0 + ) { + const kiloSessionId = this.pendingRefreshes.values().next().value; + if (typeof kiloSessionId !== 'string') return; + this.pendingRefreshes.delete(kiloSessionId); + const refresh = this.refreshes.get(kiloSessionId); + if (!refresh) continue; + refresh.hasSlot = true; + this.activeRefreshes += 1; + this.startNextRefresh(kiloSessionId, this.lifecycleGeneration, refresh); + } + } + + private isRefreshCurrent( + kiloSessionId: string, + lifecycleGeneration: number, + refresh: RefreshState, + capturedGeneration: number + ): boolean { + return ( + this.isActive(lifecycleGeneration) && + this.refreshes.get(kiloSessionId) === refresh && + refresh.generation === capturedGeneration + ); + } + + private isActive(generation: number): boolean { + return generation > 0 && this.lifecycleGeneration === generation && this.isCurrent(generation); + } + + private rememberVersion(kiloSessionId: string, version: SessionVersion): void { + this.versions.delete(kiloSessionId); + this.versions.set(kiloSessionId, version); + this.pruneVersions(); + } + + private pruneVersions(): void { + while (this.versions.size > MAX_TRACKED_SESSION_VERSIONS) { + let candidate: string | undefined; + for (const kiloSessionId of this.versions.keys()) { + if (!this.refreshes.has(kiloSessionId)) { + candidate = kiloSessionId; + break; + } + } + if (!candidate) return; + this.versions.delete(candidate); + } + } +} diff --git a/services/cloud-agent-next/src/kilo-facade/ownership.ts b/services/cloud-agent-next/src/kilo-facade/ownership.ts new file mode 100644 index 0000000000..6cab8a6777 --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/ownership.ts @@ -0,0 +1,46 @@ +import type { KiloFacadeHandlerContext, SelectedOwnedRootHandlerContext } from './contracts.js'; +import { + isSessionIngestKiloSessionId, + missingRootKiloSessionResponse, + parsePublicSessionDirectory, + unsupportedSessionSelectorResponse, +} from './http-contract.js'; +import { hasDuplicateQueryParameters } from '../shared/http-query.js'; + +export function resolveRootSessionForKiloSession( + context: KiloFacadeHandlerContext, + kiloSessionId: string +): Promise<{ cloudAgentSessionId: string } | null> { + if (context.deps?.resolveRootSessionForKiloSession) { + return context.deps.resolveRootSessionForKiloSession({ + env: context.env, + userId: context.userId, + kiloSessionId, + }); + } + return context.env.SESSION_INGEST.resolveCloudAgentRootSessionForKiloSession({ + kiloUserId: context.userId, + kiloSessionId, + }); +} + +export async function resolveSelectedOwnedRoot( + context: KiloFacadeHandlerContext +): Promise { + if ( + [...context.url.searchParams.keys()].some(key => key !== 'directory') || + hasDuplicateQueryParameters(context.url.searchParams) + ) { + return unsupportedSessionSelectorResponse(); + } + + const directory = context.url.searchParams.get('directory'); + if (directory === null) return unsupportedSessionSelectorResponse(); + const kiloSessionId = parsePublicSessionDirectory(directory); + if (!kiloSessionId) return unsupportedSessionSelectorResponse(); + if (!isSessionIngestKiloSessionId(kiloSessionId)) return missingRootKiloSessionResponse(); + + const root = await resolveRootSessionForKiloSession(context, kiloSessionId); + if (!root) return missingRootKiloSessionResponse(); + return { ...context, kiloSessionId, cloudAgentSessionId: root.cloudAgentSessionId }; +} diff --git a/services/cloud-agent-next/src/kilo-facade/request-dispatcher.test.ts b/services/cloud-agent-next/src/kilo-facade/request-dispatcher.test.ts new file mode 100644 index 0000000000..f073e0b843 --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/request-dispatcher.test.ts @@ -0,0 +1,330 @@ +import type { KiloSdkStoredMessage } from '../session-ingest-binding.js'; +import type { Env } from '../types.js'; +import { describe, expect, it, vi } from 'vitest'; +import type { KiloFacadeRequestDeps } from './contracts.js'; +import { handleKiloFacadeRequest } from './request-dispatcher.js'; + +vi.mock('@cloudflare/sandbox', () => ({ + getSandbox: vi.fn(), + Sandbox: class Sandbox {}, +})); + +vi.mock('../session/model-preflight.js', () => ({ + preflightExistingPromptModel: vi.fn().mockResolvedValue(undefined), +})); + +const kiloSessionId = 'ses_12345678901234567890123456'; +const directory = `/cloud-agent/sessions/${kiloSessionId}`; +const messageId = 'msg_exact_01'; + +const exactMessage: KiloSdkStoredMessage = { + info: { + id: messageId, + sessionID: kiloSessionId, + role: 'user', + time: { created: 100 }, + agent: 'build', + model: { providerID: 'test', modelID: 'fake' }, + }, + parts: [ + { + id: 'prt_exact_01', + sessionID: kiloSessionId, + messageID: messageId, + type: 'text', + text: 'existing message', + }, + ], +}; + +const question = { + id: 'que_root', + sessionID: kiloSessionId, + questions: [ + { + question: 'Proceed?', + header: 'Proceed', + options: [{ label: 'Yes', description: 'Proceed with the operation' }], + }, + ], +}; +const permission = { + id: 'per_root', + sessionID: kiloSessionId, + permission: 'bash', + patterns: ['git status'], + metadata: { command: 'git status' }, + always: ['git status'], +}; +const suggestion = { + id: 'sug_root', + sessionID: kiloSessionId, + text: 'Choose an approach', + actions: [{ label: 'First', prompt: 'Use the first approach' }], +}; + +const interactionLists = new Map([ + ['/kilo-proxy/question', [question]], + ['/kilo-proxy/permission', [permission]], + ['/kilo-proxy/suggestion', [suggestion]], +]); + +type WrapperDispatch = { + method: string; + path: string; + search: string; + body?: unknown; +}; + +function request(path: string, init?: RequestInit, includeDirectory = true): Request { + const url = new URL(`https://facade.test/kilo${path}`); + if (includeDirectory) url.searchParams.set('directory', directory); + return new Request(url, init); +} + +function jsonRequest(path: string, body: unknown): Request { + return request(path, { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify(body), + }); +} + +function acceptanceHarness(): { + dispatch: (request: Request) => Promise; + wrapperDispatches: WrapperDispatch[]; + resolveRoot: ReturnType; + resolveLiveWrapper: ReturnType; + getAvailableCommands: ReturnType; + admitCommand: ReturnType; +} { + const wrapperDispatches: WrapperDispatch[] = []; + const containerFetch = vi.fn(async (upstreamRequest: Request) => { + const url = new URL(upstreamRequest.url); + const bodyText = await upstreamRequest.clone().text(); + wrapperDispatches.push({ + method: upstreamRequest.method, + path: url.pathname, + search: url.search, + ...(bodyText ? { body: JSON.parse(bodyText) } : {}), + }); + if (url.pathname === `/kilo-proxy/session/${kiloSessionId}/message/${messageId}`) { + return Response.json(exactMessage); + } + const list = interactionLists.get(url.pathname); + return list ? Response.json(list) : Response.json(true); + }); + const resolveRoot = vi.fn().mockResolvedValue({ cloudAgentSessionId: 'cloud-root' }); + const resolveLiveWrapper = vi.fn().mockResolvedValue({ + port: 4312, + sandbox: { containerFetch }, + }); + const getAvailableCommands = vi.fn().mockResolvedValue([ + { + name: 'init', + description: 'initialize repository instructions', + source: 'command', + hints: [], + }, + ]); + const admitCommand = vi.fn().mockResolvedValue({ + success: true, + outcome: 'queued', + messageId: 'msg_018f1e2d3c4bDispatchCmdAAA', + compatibilityDelivery: 'queued', + }); + const deps: KiloFacadeRequestDeps = { + resolveRootSessionForKiloSession: resolveRoot, + resolveLiveWrapper, + getAvailableCommands, + validateCommandBalance: vi.fn().mockResolvedValue({ success: true }), + admitCommand, + }; + const env = { + CLOUD_AGENT_SESSION: { + idFromName: vi.fn(() => 'session-do-id'), + get: vi.fn(() => ({ hasMessageAdmission: vi.fn().mockResolvedValue(false) })), + }, + } as unknown as Env; + + return { + dispatch: facadeRequest => + handleKiloFacadeRequest({ + request: facadeRequest, + env, + userId: 'user-1', + authToken: 'validated-token', + deps, + }), + wrapperDispatches, + resolveRoot, + resolveLiveWrapper, + getAvailableCommands, + admitCommand, + }; +} + +describe('Kilo facade request dispatcher acceptance', () => { + it('dispatches exact-message and selected-root interaction lists to the live wrapper', async () => { + const harness = acceptanceHarness(); + + const exactResponse = await harness.dispatch( + request(`/session/${kiloSessionId}/message/${messageId}`) + ); + expect(exactResponse.status).toBe(200); + await expect(exactResponse.json()).resolves.toEqual(exactMessage); + + for (const [path, expected] of [ + ['/question', question], + ['/permission', permission], + ['/suggestion', suggestion], + ] as const) { + const response = await harness.dispatch(request(path)); + expect(response.status).toBe(200); + await expect(response.json()).resolves.toEqual([expected]); + } + + expect(harness.wrapperDispatches).toEqual([ + { + method: 'GET', + path: `/kilo-proxy/session/${kiloSessionId}/message/${messageId}`, + search: '', + }, + { method: 'GET', path: '/kilo-proxy/question', search: '' }, + { method: 'GET', path: '/kilo-proxy/permission', search: '' }, + { method: 'GET', path: '/kilo-proxy/suggestion', search: '' }, + ]); + }); + + it('preflights and dispatches every supported interaction mutation', async () => { + const harness = acceptanceHarness(); + const cases = [ + { + request: jsonRequest('/question/que_root/reply', { answers: [['Yes']] }), + listPath: '/kilo-proxy/question', + mutationPath: '/kilo-proxy/question/que_root/reply', + body: { answers: [['Yes']] }, + }, + { + request: request('/question/que_root/reject', { method: 'POST' }), + listPath: '/kilo-proxy/question', + mutationPath: '/kilo-proxy/question/que_root/reject', + }, + { + request: jsonRequest('/permission/per_root/reply', { reply: 'once' }), + listPath: '/kilo-proxy/permission', + mutationPath: '/kilo-proxy/permission/per_root/reply', + body: { reply: 'once' }, + }, + { + request: jsonRequest('/permission/per_root/always-rules', { + approvedAlways: ['git status'], + }), + listPath: '/kilo-proxy/permission', + mutationPath: '/kilo-proxy/permission/per_root/always-rules', + body: { approvedAlways: ['git status'] }, + }, + { + request: jsonRequest('/suggestion/sug_root/accept', { index: 0 }), + listPath: '/kilo-proxy/suggestion', + mutationPath: '/kilo-proxy/suggestion/sug_root/accept', + body: { index: 0 }, + }, + { + request: request('/suggestion/sug_root/dismiss', { method: 'POST' }), + listPath: '/kilo-proxy/suggestion', + mutationPath: '/kilo-proxy/suggestion/sug_root/dismiss', + }, + ]; + + for (const testCase of cases) { + const dispatchOffset = harness.wrapperDispatches.length; + const response = await harness.dispatch(testCase.request); + expect(response.status).toBe(200); + await expect(response.json()).resolves.toBe(true); + expect(harness.wrapperDispatches.slice(dispatchOffset)).toEqual([ + { method: 'GET', path: testCase.listPath, search: '' }, + { + method: 'POST', + path: testCase.mutationPath, + search: '', + ...(testCase.body ? { body: testCase.body } : {}), + }, + ]); + } + }); + + it('serves command metadata and durably admits commands without wrapper dispatch', async () => { + const harness = acceptanceHarness(); + + const listResponse = await harness.dispatch(request('/command')); + expect(listResponse.status).toBe(200); + await expect(listResponse.json()).resolves.toEqual([ + { + name: 'init', + description: 'initialize repository instructions', + source: 'command', + hints: [], + template: '', + }, + ]); + + const commandResponse = await harness.dispatch( + jsonRequest(`/session/${kiloSessionId}/command`, { + messageID: 'msg_018f1e2d3c4bDispatchCmdAAA', + command: 'init', + arguments: '', + }) + ); + expect(commandResponse.status).toBe(200); + await expect(commandResponse.json()).resolves.toMatchObject({ + info: { + sessionID: kiloSessionId, + parentID: 'msg_018f1e2d3c4bDispatchCmdAAA', + role: 'assistant', + }, + parts: [], + }); + expect(harness.getAvailableCommands).toHaveBeenCalledOnce(); + expect(harness.admitCommand).toHaveBeenCalledOnce(); + expect(harness.wrapperDispatches).toEqual([]); + }); + + it('rejects wrong methods as unsupported without dispatching to a wrapper', async () => { + const harness = acceptanceHarness(); + const wrongMethods = [ + request(`/session/${kiloSessionId}/message/${messageId}`, { method: 'POST' }), + request('/command', { method: 'POST' }), + request(`/session/${kiloSessionId}/command`), + request('/question', { method: 'POST' }), + request('/question/que_root/reply'), + request('/permission', { method: 'POST' }), + request('/permission/per_root/always-rules'), + request('/suggestion', { method: 'POST' }), + request('/suggestion/sug_root/dismiss'), + ]; + + for (const wrongMethod of wrongMethods) { + const response = await harness.dispatch(wrongMethod); + expect(response.status).toBe(501); + await expect(response.json()).resolves.toMatchObject({ error: 'KILO_ROUTE_UNSUPPORTED' }); + } + expect(harness.resolveLiveWrapper).not.toHaveBeenCalled(); + expect(harness.wrapperDispatches).toEqual([]); + }); + + it('requires a selected public directory for interaction routes before ownership resolution', async () => { + const harness = acceptanceHarness(); + + for (const path of ['/question', '/permission', '/suggestion']) { + const response = await harness.dispatch(request(path, undefined, false)); + expect(response.status).toBe(400); + await expect(response.json()).resolves.toMatchObject({ + error: 'KILO_SESSION_SELECTOR_UNSUPPORTED', + }); + } + expect(harness.resolveRoot).not.toHaveBeenCalled(); + expect(harness.resolveLiveWrapper).not.toHaveBeenCalled(); + expect(harness.wrapperDispatches).toEqual([]); + }); +}); diff --git a/services/cloud-agent-next/src/kilo-facade/request-dispatcher.ts b/services/cloud-agent-next/src/kilo-facade/request-dispatcher.ts new file mode 100644 index 0000000000..d0d13d28ea --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/request-dispatcher.ts @@ -0,0 +1,161 @@ +import type { Env } from '../types.js'; +import type { + KiloFacadeHandlerContext, + KiloFacadeRequestDeps, + OwnedRootHandlerContext, +} from './contracts.js'; +import { handleAbort } from './handlers/abort/handler.js'; +import { handleCommandList } from './handlers/command-list/handler.js'; +import { handleCommand } from './handlers/command/handler.js'; +import { handleGlobalEvent } from './handlers/global-event/handler.js'; +import { handlePermission, parsePermissionRoute } from './handlers/permission/handler.js'; +import { handlePromptAsync } from './handlers/prompt-async/handler.js'; +import { handleQuestion, parseQuestionRoute } from './handlers/question/handler.js'; +import { handleSessionDetail } from './handlers/session-detail/handler.js'; +import { handleSessionEvent } from './handlers/session-event/handler.js'; +import { handleSessionList } from './handlers/session-list/handler.js'; +import { handleSessionMessage } from './handlers/session-message/handler.js'; +import { handleSessionMessages } from './handlers/session-messages/handler.js'; +import { handleSessionStatus } from './handlers/session-status/handler.js'; +import { handleSuggestion, parseSuggestionRoute } from './handlers/suggestion/handler.js'; +import { + facadeError, + isSessionIngestKiloSessionId, + kiloRelativePath, + missingRootKiloSessionResponse, + parseRootSessionRoute, + unsupportedRouteResponse, +} from './http-contract.js'; +import { resolveRootSessionForKiloSession } from './ownership.js'; + +const KNOWN_UNSUPPORTED_ROUTES = new Set([ + 'POST /session/viewed', + 'POST /sync/history', + 'GET /config', + 'GET /provider', + 'GET /project/current', + 'GET /global/health', +]); + +type OwnedRootRoute = + | { kind: 'detail' } + | { kind: 'messages' } + | { kind: 'message'; messageId: string } + | { kind: 'prompt-async' } + | { kind: 'command' } + | { kind: 'abort' }; + +function classifyOwnedRootRoute( + method: string, + kiloPath: string, + encodedKiloSessionId: string +): OwnedRootRoute | null { + const exactRoute = `/session/${encodedKiloSessionId}`; + if (method === 'GET' && kiloPath === exactRoute) return { kind: 'detail' }; + if (method === 'GET' && kiloPath === `${exactRoute}/message`) return { kind: 'messages' }; + if (method === 'GET') { + const messageMatch = /^\/message\/([^/]+)$/.exec(kiloPath.slice(exactRoute.length)); + if (messageMatch?.[1]) { + try { + return { kind: 'message', messageId: decodeURIComponent(messageMatch[1]) }; + } catch { + return null; + } + } + } + if (method === 'POST' && kiloPath === `${exactRoute}/prompt_async`) + return { kind: 'prompt-async' }; + if (method === 'POST' && kiloPath === `${exactRoute}/command`) return { kind: 'command' }; + if (method === 'POST' && kiloPath === `${exactRoute}/abort`) return { kind: 'abort' }; + return null; +} + +export async function handleKiloFacadeRequest(params: { + request: Request; + env: Env; + userId: string; + authToken?: string; + deps?: KiloFacadeRequestDeps; +}): Promise { + const url = new URL(params.request.url); + const context: KiloFacadeHandlerContext = { + ...params, + url, + kiloPath: kiloRelativePath(url.pathname), + }; + + if (context.kiloPath === '/global/event') return handleGlobalEvent(context); + if (context.kiloPath === '/event') return handleSessionEvent(context); + if (context.kiloPath === '/session/status') { + return context.request.method === 'GET' + ? handleSessionStatus(context) + : unsupportedRouteResponse(); + } + if (context.kiloPath === '/command') { + return context.request.method === 'GET' + ? handleCommandList(context) + : unsupportedRouteResponse(); + } + if (context.kiloPath === '/session' && context.request.method === 'GET') { + return handleSessionList(context); + } + const questionRoute = parseQuestionRoute(context.request.method, context.kiloPath); + if (questionRoute) return handleQuestion(context, questionRoute); + const permissionRoute = parsePermissionRoute(context.request.method, context.kiloPath); + if (permissionRoute) return handlePermission(context, permissionRoute); + const suggestionRoute = parseSuggestionRoute(context.request.method, context.kiloPath); + if (suggestionRoute) return handleSuggestion(context, suggestionRoute); + if ( + context.kiloPath === '/session' || + KNOWN_UNSUPPORTED_ROUTES.has(`${context.request.method} ${context.kiloPath}`) + ) { + return unsupportedRouteResponse(); + } + + const route = parseRootSessionRoute(context.kiloPath); + if (!route) return unsupportedRouteResponse(); + if (!isSessionIngestKiloSessionId(route.kiloSessionId)) return missingRootKiloSessionResponse(); + + const root = await resolveRootSessionForKiloSession(context, route.kiloSessionId); + if (!root) return missingRootKiloSessionResponse(); + + const ownedRoute = classifyOwnedRootRoute( + context.request.method, + context.kiloPath, + route.encodedKiloSessionId + ); + if (context.deps?.decideSessionRoute) { + const decision = context.deps.decideSessionRoute({ + method: context.request.method, + kiloRelativePath: context.kiloPath, + search: context.url.search, + userId: context.userId, + kiloSessionId: route.kiloSessionId, + cloudAgentSessionId: root.cloudAgentSessionId, + }); + if (decision.kind === 'reject') { + return facadeError(decision.status, decision.code, decision.message); + } + } + if (!ownedRoute) return unsupportedRouteResponse(); + + const ownedContext: OwnedRootHandlerContext = { + ...context, + ...route, + cloudAgentSessionId: root.cloudAgentSessionId, + }; + switch (ownedRoute.kind) { + case 'detail': + return handleSessionDetail(ownedContext); + case 'messages': + return handleSessionMessages(ownedContext); + case 'message': + return handleSessionMessage(ownedContext, ownedRoute.messageId); + case 'prompt-async': + return handlePromptAsync(ownedContext); + case 'command': + return handleCommand(ownedContext); + case 'abort': + return handleAbort(ownedContext); + } +} diff --git a/services/cloud-agent-next/src/kilo-facade/session-proxy.ts b/services/cloud-agent-next/src/kilo-facade/session-proxy.ts index bcf731f402..55c37d4463 100644 --- a/services/cloud-agent-next/src/kilo-facade/session-proxy.ts +++ b/services/cloud-agent-next/src/kilo-facade/session-proxy.ts @@ -22,26 +22,6 @@ export type LiveWrapperTarget = { port: number; }; -export function decideSessionKiloFacadeRoute( - input: SessionKiloFacadePolicyInput -): SessionKiloFacadeDecision { - const suffix = input.kiloRelativePath.slice( - `/session/${encodeURIComponent(input.kiloSessionId)}`.length - ); - const supported = - (input.method === 'GET' && (suffix === '' || suffix === '/message')) || - (input.method === 'POST' && (suffix === '/prompt_async' || suffix === '/abort')); - if (!supported) { - return { - kind: 'reject', - status: 501, - code: 'KILO_ROUTE_UNSUPPORTED', - message: 'Kilo facade route is not supported', - }; - } - return { kind: 'proxy-live-wrapper' }; -} - export function buildWrapperKiloProxyUrl(params: { wrapperPort: number; kiloRelativePath: string; diff --git a/services/cloud-agent-next/src/kilo-facade/session-read-source.ts b/services/cloud-agent-next/src/kilo-facade/session-read-source.ts new file mode 100644 index 0000000000..5c58961c48 --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/session-read-source.ts @@ -0,0 +1,62 @@ +import { createProxyRequest } from '../shared/http-proxy.js'; +import type { OwnedRootHandlerContext } from './contracts.js'; +import { isRecord, readBoundedBody } from './http-contract.js'; +import { buildWrapperKiloProxyUrl, resolveLiveWrapperTarget } from './session-proxy.js'; + +const MAX_KILO_ERROR_JSON_BYTES = 64 * 1024; + +async function isUnavailableKiloRuntimeResponse(response: Response): Promise { + if (response.status !== 502 && response.status !== 503) return false; + const contentType = response.headers.get('content-type'); + if (!contentType?.toLowerCase().includes('application/json')) return false; + const bytes = await readBoundedBody(response.clone(), MAX_KILO_ERROR_JSON_BYTES); + if (!bytes) return false; + try { + const parsed: unknown = JSON.parse(new TextDecoder().decode(bytes)); + return ( + isRecord(parsed) && + (parsed.error === 'KILO_RUNTIME_UNAVAILABLE' || parsed.error === 'KILO_PROXY_ERROR') + ); + } catch { + return false; + } +} + +export async function handleLiveFirstSessionRead(params: { + context: OwnedRootHandlerContext; + persistedFallback: () => Promise; + rewriteLiveResponse: (response: Response, kiloSessionId: string) => Promise; +}): Promise { + const { context } = params; + let liveWrapper; + try { + liveWrapper = await (context.deps?.resolveLiveWrapper ?? resolveLiveWrapperTarget)({ + env: context.env, + userId: context.userId, + cloudAgentSessionId: context.cloudAgentSessionId, + }); + } catch { + return params.persistedFallback(); + } + if (!liveWrapper) return params.persistedFallback(); + + const upstreamSearchParams = new URLSearchParams(context.url.searchParams); + upstreamSearchParams.delete('directory'); + const upstreamSearch = upstreamSearchParams.size > 0 ? `?${upstreamSearchParams.toString()}` : ''; + const targetUrl = buildWrapperKiloProxyUrl({ + wrapperPort: liveWrapper.port, + kiloRelativePath: context.kiloPath, + search: upstreamSearch, + }); + let response: Response; + try { + response = await liveWrapper.sandbox.containerFetch( + createProxyRequest(context.request, targetUrl), + liveWrapper.port + ); + } catch { + return params.persistedFallback(); + } + if (await isUnavailableKiloRuntimeResponse(response)) return params.persistedFallback(); + return params.rewriteLiveResponse(response, context.kiloSessionId); +} diff --git a/services/cloud-agent-next/src/kilo-facade/stored-message-validation.test.ts b/services/cloud-agent-next/src/kilo-facade/stored-message-validation.test.ts new file mode 100644 index 0000000000..815d75f592 --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/stored-message-validation.test.ts @@ -0,0 +1,106 @@ +import { describe, expect, it } from 'vitest'; + +import { + parseOwnedKiloSdkStoredMessage, + parseOwnedKiloSdkStoredMessages, +} from './stored-message-validation.js'; + +const kiloSessionId = 'ses_12345678901234567890123456'; +const messageId = 'msg_valid_01'; +const message = { + info: { + id: messageId, + sessionID: kiloSessionId, + role: 'user' as const, + time: { created: 100 }, + agent: 'build', + model: { providerID: 'provider', modelID: 'model' }, + }, + parts: [ + { + id: 'prt_valid_01', + sessionID: kiloSessionId, + messageID: messageId, + type: 'text' as const, + text: 'hello', + }, + ], +}; + +describe('stored message validation', () => { + it('validates the shared stored-message schema and selected identities', () => { + expect(parseOwnedKiloSdkStoredMessage(message, kiloSessionId, messageId)).toEqual(message); + expect( + parseOwnedKiloSdkStoredMessage( + { + ...message, + parts: [{ ...message.parts[0], sessionID: 'ses_abcdefghijklmnopqrstuvwxyz' }], + }, + kiloSessionId, + messageId + ) + ).toBeNull(); + expect( + parseOwnedKiloSdkStoredMessage( + { ...message, parts: [{ ...message.parts[0], messageID: 'msg_other_02' }] }, + kiloSessionId, + messageId + ) + ).toBeNull(); + }); + + it('rejects nested tool attachments belonging to another message', () => { + expect( + parseOwnedKiloSdkStoredMessage( + { + ...message, + parts: [ + { + id: 'prt_tool_01', + sessionID: kiloSessionId, + messageID: messageId, + type: 'tool', + callID: 'call_01', + tool: 'read', + state: { + status: 'completed', + input: {}, + output: 'done', + title: 'Read', + metadata: {}, + time: { start: 1, end: 2 }, + attachments: [ + { + id: 'prt_attachment_01', + sessionID: kiloSessionId, + messageID: 'msg_other_02', + type: 'file', + mime: 'text/plain', + url: 'file:///tmp/value.txt', + }, + ], + }, + }, + ], + }, + kiloSessionId, + messageId + ) + ).toBeNull(); + }); + + it('returns null instead of throwing for malformed discriminated parts', () => { + expect( + parseOwnedKiloSdkStoredMessages( + [ + message, + { + ...message, + parts: [{ ...message.parts[0], type: 'tool', state: { status: 'unknown' } }], + }, + ], + kiloSessionId + ) + ).toBeNull(); + }); +}); diff --git a/services/cloud-agent-next/src/kilo-facade/stored-message-validation.ts b/services/cloud-agent-next/src/kilo-facade/stored-message-validation.ts new file mode 100644 index 0000000000..123b674725 --- /dev/null +++ b/services/cloud-agent-next/src/kilo-facade/stored-message-validation.ts @@ -0,0 +1,54 @@ +import { + kiloSdkStoredMessageSchema, + type KiloSdkPart, + type KiloSdkStoredMessage, +} from '@kilocode/session-ingest-contracts'; + +function isOwnedPart(part: KiloSdkPart, kiloSessionId: string, messageId: string): boolean { + if (part.sessionID !== kiloSessionId || part.messageID !== messageId) return false; + if (part.type !== 'tool' || part.state.status !== 'completed' || !part.state.attachments) + return true; + return part.state.attachments.every( + attachment => attachment.sessionID === kiloSessionId && attachment.messageID === messageId + ); +} + +function isOwnedStoredMessage( + message: KiloSdkStoredMessage, + kiloSessionId: string, + expectedMessageId?: string +): boolean { + if (message.info.sessionID !== kiloSessionId) return false; + if (expectedMessageId !== undefined && message.info.id !== expectedMessageId) return false; + return message.parts.every(part => isOwnedPart(part, kiloSessionId, message.info.id)); +} + +export function parseOwnedKiloSdkStoredMessage( + value: unknown, + kiloSessionId: string, + expectedMessageId?: string +): KiloSdkStoredMessage | null { + try { + const parsed = kiloSdkStoredMessageSchema.safeParse(value); + if (!parsed.success || !isOwnedStoredMessage(parsed.data, kiloSessionId, expectedMessageId)) { + return null; + } + return parsed.data; + } catch { + return null; + } +} + +export function parseOwnedKiloSdkStoredMessages( + value: unknown, + kiloSessionId: string +): KiloSdkStoredMessage[] | null { + if (!Array.isArray(value)) return null; + const messages: KiloSdkStoredMessage[] = []; + for (const entry of value) { + const message = parseOwnedKiloSdkStoredMessage(entry, kiloSessionId); + if (!message) return null; + messages.push(message); + } + return messages; +} diff --git a/services/cloud-agent-next/src/kilo-facade/user-kilo-facade.test.ts b/services/cloud-agent-next/src/kilo-facade/user-kilo-facade.test.ts index 39ae833945..cd19924f8d 100644 --- a/services/cloud-agent-next/src/kilo-facade/user-kilo-facade.test.ts +++ b/services/cloud-agent-next/src/kilo-facade/user-kilo-facade.test.ts @@ -50,6 +50,7 @@ function envStub(): Env { resolveCloudAgentRootSessionForKiloSession: vi.fn(), getCloudAgentRootSessionSnapshot: vi.fn(), listCloudAgentRootSessions: vi.fn(), + listCloudAgentRootSessionsByGitUrl: vi.fn(), getCloudAgentRootSessionMessages: vi.fn(), }, CLOUD_AGENT_SESSION: { @@ -58,6 +59,7 @@ function envStub(): Env { hasMessageAdmission: vi.fn().mockResolvedValue(false), admitSubmittedMessage: vi.fn(), interruptExecution: vi.fn(), + getInitialMessageSnapshot: vi.fn().mockResolvedValue(null), })), }, } as unknown as Env; @@ -392,7 +394,6 @@ describe('handleKiloFacadeRequest', () => { it('returns 501 for intentionally unsupported Kilo route families', async () => { for (const request of [ new Request('http://worker.test/kilo/session', { method: 'POST' }), - new Request('http://worker.test/kilo/session/status'), new Request('http://worker.test/kilo/session/viewed', { method: 'POST' }), new Request('http://worker.test/kilo/sync/history', { method: 'POST' }), new Request('http://worker.test/kilo/config'), @@ -417,6 +418,105 @@ describe('handleKiloFacadeRequest', () => { } }); + it('returns the sparse non-idle root status map', async () => { + const getPublicSessionStatuses = vi.fn(() => ({ [kiloSessionId]: { type: 'busy' as const } })); + const globalEvents = { + openPublicGlobalEventStream: vi.fn(), + openPublicSessionEventStream: vi.fn(), + getPublicSessionStatuses, + }; + + const response = await handleKiloFacadeRequest({ + request: new Request('http://worker.test/kilo/session/status'), + env: envStub(), + userId: 'usr_1', + deps: { globalEvents }, + }); + + expect(response.status).toBe(200); + await expect(response.json()).resolves.toEqual({ [kiloSessionId]: { type: 'busy' } }); + expect(getPublicSessionStatuses).toHaveBeenCalledWith(); + }); + + it('returns status only for an owned root selected by public directory', async () => { + const getPublicSessionStatuses = vi.fn(() => ({ [kiloSessionId]: { type: 'busy' as const } })); + const resolveRootSessionForKiloSession = vi.fn(async () => ({ + cloudAgentSessionId: 'agent_live', + })); + const globalEvents = { + openPublicGlobalEventStream: vi.fn(), + openPublicSessionEventStream: vi.fn(), + getPublicSessionStatuses, + }; + + const response = await handleKiloFacadeRequest({ + request: new Request( + `http://worker.test/kilo/session/status?directory=${encodeURIComponent(publicCloudAgentDirectory(kiloSessionId))}` + ), + env: envStub(), + userId: 'usr_1', + deps: { globalEvents, resolveRootSessionForKiloSession }, + }); + + expect(response.status).toBe(200); + await expect(response.json()).resolves.toEqual({ [kiloSessionId]: { type: 'busy' } }); + expect(resolveRootSessionForKiloSession).toHaveBeenCalledWith({ + env: expect.anything(), + userId: 'usr_1', + kiloSessionId, + }); + expect(getPublicSessionStatuses).toHaveBeenCalledWith(kiloSessionId); + }); + + it('rejects unsupported or repeated status selectors before ownership lookup', async () => { + for (const request of [ + new Request('http://worker.test/kilo/session/status?workspace=private'), + new Request( + `http://worker.test/kilo/session/status?directory=${encodeURIComponent(publicCloudAgentDirectory(kiloSessionId))}&directory=${encodeURIComponent(publicCloudAgentDirectory(kiloSessionId))}` + ), + ]) { + const resolveRootSessionForKiloSession = vi.fn(); + const response = await handleKiloFacadeRequest({ + request, + env: envStub(), + userId: 'usr_1', + deps: { + resolveRootSessionForKiloSession, + globalEvents: { + openPublicGlobalEventStream: vi.fn(), + openPublicSessionEventStream: vi.fn(), + getPublicSessionStatuses: vi.fn(), + }, + }, + }); + + expect(response.status).toBe(400); + expect(resolveRootSessionForKiloSession).not.toHaveBeenCalled(); + } + }); + + it('hides a foreign selected status root without reading its status', async () => { + const getPublicSessionStatuses = vi.fn(); + const response = await handleKiloFacadeRequest({ + request: new Request( + `http://worker.test/kilo/session/status?directory=${encodeURIComponent(publicCloudAgentDirectory(kiloSessionId))}` + ), + env: envStub(), + userId: 'usr_1', + deps: { + resolveRootSessionForKiloSession: vi.fn(async () => null), + globalEvents: { + openPublicGlobalEventStream: vi.fn(), + openPublicSessionEventStream: vi.fn(), + getPublicSessionStatuses, + }, + }, + }); + + expect(response.status).toBe(404); + expect(getPublicSessionStatuses).not.toHaveBeenCalled(); + }); + it('lists mapped Cloud Agent roots without requiring private SDK snapshots', async () => { const env = envStub(); const listCloudAgentRootSessions = vi.mocked(env.SESSION_INGEST.listCloudAgentRootSessions); @@ -445,6 +545,90 @@ describe('handleKiloFacadeRequest', () => { }); }); + it('lists mapped Cloud Agent roots through the dedicated repository RPC', async () => { + const env = envStub(); + const list = vi.mocked(env.SESSION_INGEST.listCloudAgentRootSessionsByGitUrl); + list.mockResolvedValue([]); + + const response = await handleKiloFacadeRequest({ + request: new Request( + 'http://worker.test/kilo/session?gitUrl=https%3A%2F%2Fgithub.com%2Facme%2Frepo&limit=15' + ), + env, + userId: 'usr_1', + }); + + expect(response.status).toBe(200); + expect(list).toHaveBeenCalledWith({ + kiloUserId: 'usr_1', + gitUrl: 'https://github.com/acme/repo', + limit: 15, + }); + expect(env.SESSION_INGEST.listCloudAgentRootSessions).not.toHaveBeenCalled(); + }); + + it('lists root-level generic HTTPS repositories', async () => { + const env = envStub(); + const list = vi.mocked(env.SESSION_INGEST.listCloudAgentRootSessionsByGitUrl); + list.mockResolvedValue([]); + + const response = await handleKiloFacadeRequest({ + request: new Request( + 'http://worker.test/kilo/session?gitUrl=https%3A%2F%2Fgit.example.com%2Fwidgets.git' + ), + env, + userId: 'usr_1', + }); + + expect(response.status).toBe(200); + expect(list).toHaveBeenCalledWith({ + kiloUserId: 'usr_1', + gitUrl: 'https://git.example.com/widgets.git', + }); + }); + + it('does not fall back to the unfiltered RPC when repository listing fails', async () => { + const env = envStub(); + vi.mocked(env.SESSION_INGEST.listCloudAgentRootSessionsByGitUrl).mockRejectedValue( + new Error('filtered RPC unavailable') + ); + + await expect( + handleKiloFacadeRequest({ + request: new Request( + 'http://worker.test/kilo/session?gitUrl=https%3A%2F%2Fgithub.com%2Facme%2Frepo' + ), + env, + userId: 'usr_1', + }) + ).rejects.toThrow('filtered RPC unavailable'); + + expect(env.SESSION_INGEST.listCloudAgentRootSessions).not.toHaveBeenCalled(); + }); + + it.each([ + 'gitUrl=', + 'gitUrl=not-a-url', + 'gitUrl=https%3A%2F%2Fgithub.com%2Facme', + 'gitUrl=https%3A%2F%2Ftoken%40github.com%2Facme%2Frepo', + 'gitUrl=https%3A%2F%2Fgithub.com%2Facme%2Frepo%3Ftoken%3Dsecret', + 'gitUrl=https%3A%2F%2Fgithub.com%2Facme%2Frepo%23readme', + 'gitUrl=https%3A%2F%2Fgithub.com%2Facme%2Frepo&gitUrl=https%3A%2F%2Fgithub.com%2Facme%2Frepo', + ])('rejects unsafe repository selector %s before listing roots', async query => { + const env = envStub(); + + const response = await handleKiloFacadeRequest({ + request: new Request(`http://worker.test/kilo/session?${query}`), + env, + userId: 'usr_1', + }); + + expect(response.status).toBe(400); + await expect(response.json()).resolves.toMatchObject({ error: 'KILO_QUERY_INVALID' }); + expect(env.SESSION_INGEST.listCloudAgentRootSessionsByGitUrl).not.toHaveBeenCalled(); + expect(env.SESSION_INGEST.listCloudAgentRootSessions).not.toHaveBeenCalled(); + }); + it('rejects repeated session list selectors before listing roots', async () => { const env = envStub(); @@ -493,7 +677,7 @@ describe('handleKiloFacadeRequest', () => { expect(response.status).toBe(200); expect(response.headers.get('content-type')).toContain('text/event-stream'); - expect(openPublicGlobalEventStream).toHaveBeenCalledOnce(); + expect(openPublicGlobalEventStream).toHaveBeenCalledWith('usr_1'); }); it('requires a directory selector for a scoped event subscription', async () => { @@ -624,7 +808,7 @@ describe('handleKiloFacadeRequest', () => { }); expect(response.status).toBe(200); - expect(openPublicSessionEventStream).toHaveBeenCalledWith(kiloSessionId); + expect(openPublicSessionEventStream).toHaveBeenCalledWith('usr_1', kiloSessionId); expect(resolveLiveWrapper).not.toHaveBeenCalled(); }); @@ -861,13 +1045,24 @@ describe('handleKiloFacadeRequest', () => { }); }); - it('returns a stable pending response for a cold detail read without a materialized snapshot', async () => { + it('returns bootstrap session detail while the live Kilo session is not materialized', async () => { const env = envStub(); + const containerFetch = vi.fn(async () => + Response.json({ error: 'not found' }, { status: 404 }) + ); vi.mocked(env.SESSION_INGEST.getCloudAgentRootSessionSnapshot).mockResolvedValue({ kiloSessionId, cloudAgentSessionId: 'agent_cold', snapshot: { kind: 'pending' }, }); + const getInitialMessageSnapshot = vi.fn().mockResolvedValue({ + messageId: 'msg_initial', + content: 'Fix the failing test', + timestamp: 123, + }); + vi.spyOn(env.CLOUD_AGENT_SESSION, 'get').mockReturnValue({ + getInitialMessageSnapshot, + } as never); const response = await handleKiloFacadeRequest({ request: new Request(`http://worker.test/kilo/session/${kiloSessionId}`), @@ -877,15 +1072,21 @@ describe('handleKiloFacadeRequest', () => { resolveRootSessionForKiloSession: vi.fn(async () => ({ cloudAgentSessionId: 'agent_cold', })), - resolveLiveWrapper: vi.fn(async () => null), + resolveLiveWrapper: vi.fn(async () => liveWrapperTarget(containerFetch)), }, }); - expect(response.status).toBe(503); - expect(response.headers.get('retry-after')).toBe('1'); - await expect(response.json()).resolves.toMatchObject({ - error: 'KILO_SESSION_SNAPSHOT_PENDING', + expect(response.status).toBe(200); + await expect(response.json()).resolves.toEqual({ + id: kiloSessionId, + slug: kiloSessionId, + projectID: 'cloud-agent', + directory: publicCloudAgentDirectory(kiloSessionId), + title: '', + version: 'cloud-agent', + time: { created: 123, updated: 123 }, }); + expect(getInitialMessageSnapshot).toHaveBeenCalledOnce(); }); it('maps an oversized cold session snapshot to stable 413', async () => { @@ -1439,6 +1640,95 @@ describe('handleKiloFacadeRequest', () => { await expect(response.json()).resolves.toEqual(projectedSdkMessageHistory()); }); + it('returns the initial user message while the live Kilo session is not materialized', async () => { + const env = envStub(); + vi.mocked(env.SESSION_INGEST.getCloudAgentRootSessionMessages).mockResolvedValue({ + kiloSessionId, + cloudAgentSessionId: 'agent_live', + history: null, + }); + const getInitialMessageSnapshot = vi.fn().mockResolvedValue({ + messageId: 'msg_initial', + content: 'Fix the failing test', + timestamp: 123, + }); + vi.spyOn(env.CLOUD_AGENT_SESSION, 'get').mockReturnValue({ + getInitialMessageSnapshot, + } as never); + const containerFetch = vi.fn(async () => + Response.json({ error: 'not found' }, { status: 404 }) + ); + + const response = await handleKiloFacadeRequest({ + request: new Request(`http://worker.test/kilo/session/${kiloSessionId}/message`), + env, + userId: 'usr_1', + deps: { + resolveRootSessionForKiloSession: vi.fn(async () => ({ + cloudAgentSessionId: 'agent_live', + })), + resolveLiveWrapper: vi.fn(async () => liveWrapperTarget(containerFetch)), + }, + }); + + expect(response.status).toBe(200); + await expect(response.json()).resolves.toEqual([ + expect.objectContaining({ + info: expect.objectContaining({ id: 'msg_initial', role: 'user' }), + parts: [expect.objectContaining({ type: 'text', text: 'Fix the failing test' })], + }), + ]); + }); + + it('returns a durable initial user message while the live Kilo transcript is empty', async () => { + const env = envStub(); + const getInitialMessageSnapshot = vi.fn().mockResolvedValue({ + messageId: 'msg_initial', + content: 'Fix the failing test', + timestamp: 123, + }); + vi.spyOn(env.CLOUD_AGENT_SESSION, 'get').mockReturnValue({ + getInitialMessageSnapshot, + } as never); + const containerFetch = vi.fn(async () => Response.json([])); + + const response = await handleKiloFacadeRequest({ + request: new Request(`http://worker.test/kilo/session/${kiloSessionId}/message`), + env, + userId: 'usr_1', + deps: { + resolveRootSessionForKiloSession: vi.fn(async () => ({ + cloudAgentSessionId: 'agent_live', + })), + resolveLiveWrapper: vi.fn(async () => liveWrapperTarget(containerFetch)), + }, + }); + + expect(response.status).toBe(200); + await expect(response.json()).resolves.toEqual([ + { + info: { + id: 'msg_initial', + sessionID: kiloSessionId, + role: 'user', + time: { created: 123 }, + agent: '', + model: { providerID: '', modelID: '' }, + }, + parts: [ + { + id: 'prt_msg_initial', + sessionID: kiloSessionId, + messageID: 'msg_initial', + type: 'text', + text: 'Fix the failing test', + }, + ], + }, + ]); + expect(getInitialMessageSnapshot).toHaveBeenCalledOnce(); + }); + it('continues forwarding live message reads for freshest available history without private selectors', async () => { const containerFetch = vi.fn(async () => Response.json(sdkMessageHistory())); @@ -1602,7 +1892,97 @@ describe('handleKiloFacadeRequest', () => { await expect(response.json()).resolves.toMatchObject({ error: 'KILO_SESSION_NOT_FOUND' }); }); - it('returns snapshot-pending for a listed root without persisted transcript state', async () => { + it('returns the durable initial user message for an unbounded empty persisted transcript', async () => { + const env = envStub(); + vi.mocked(env.SESSION_INGEST.getCloudAgentRootSessionMessages).mockResolvedValue({ + kiloSessionId, + cloudAgentSessionId: 'agent_pending', + history: { messages: [], nextCursor: null, omittedItemCount: 0 }, + }); + const getInitialMessageSnapshot = vi.fn().mockResolvedValue({ + messageId: 'msg_initial', + content: 'Fix the failing test', + timestamp: 123, + }); + vi.spyOn(env.CLOUD_AGENT_SESSION, 'get').mockReturnValue({ + getInitialMessageSnapshot, + } as never); + + const response = await handleKiloFacadeRequest({ + request: new Request(`http://worker.test/kilo/session/${kiloSessionId}/message?limit=0`), + env, + userId: 'usr_1', + deps: { + resolveRootSessionForKiloSession: vi.fn(async () => ({ + cloudAgentSessionId: 'agent_pending', + })), + resolveLiveWrapper: vi.fn(async () => null), + }, + }); + + expect(response.status).toBe(200); + await expect(response.json()).resolves.toEqual([ + expect.objectContaining({ + info: expect.objectContaining({ id: 'msg_initial', role: 'user' }), + parts: [expect.objectContaining({ type: 'text', text: 'Fix the failing test' })], + }), + ]); + }); + + it('returns a durable initial user message while the Kilo transcript is pending', async () => { + const env = envStub(); + vi.mocked(env.SESSION_INGEST.getCloudAgentRootSessionMessages).mockResolvedValue({ + kiloSessionId, + cloudAgentSessionId: 'agent_pending', + history: null, + }); + const getInitialMessageSnapshot = vi.fn().mockResolvedValue({ + messageId: 'msg_initial', + content: 'Fix the failing test', + timestamp: 123, + }); + vi.spyOn(env.CLOUD_AGENT_SESSION, 'get').mockReturnValue({ + getInitialMessageSnapshot, + } as never); + + const response = await handleKiloFacadeRequest({ + request: new Request(`http://worker.test/kilo/session/${kiloSessionId}/message`), + env, + userId: 'usr_1', + deps: { + resolveRootSessionForKiloSession: vi.fn(async () => ({ + cloudAgentSessionId: 'agent_pending', + })), + resolveLiveWrapper: vi.fn(async () => null), + }, + }); + + expect(response.status).toBe(200); + await expect(response.json()).resolves.toEqual([ + { + info: { + id: 'msg_initial', + sessionID: kiloSessionId, + role: 'user', + time: { created: 123 }, + agent: '', + model: { providerID: '', modelID: '' }, + }, + parts: [ + { + id: 'prt_msg_initial', + sessionID: kiloSessionId, + messageID: 'msg_initial', + type: 'text', + text: 'Fix the failing test', + }, + ], + }, + ]); + expect(getInitialMessageSnapshot).toHaveBeenCalledOnce(); + }); + + it('returns snapshot-pending for a listed root without persisted or queued transcript state', async () => { const env = envStub(); vi.mocked(env.SESSION_INGEST.getCloudAgentRootSessionMessages).mockResolvedValue({ kiloSessionId, @@ -2438,12 +2818,146 @@ describe('UserKiloFacade producer messages', () => { expect(close).not.toHaveBeenCalled(); }); + it('tracks non-idle root status on the hibernation-safe producer attachment and clears idle', async () => { + let attachment = { + userId: 'usr_1', + cloudAgentSessionId: 'agent_live', + kiloSessionId, + wrapperRunId: 'wr_current', + wrapperGeneration: 1, + wrapperConnectionId: 'conn_current', + }; + const ws = { + close: vi.fn(), + send: vi.fn(), + deserializeAttachment: () => attachment, + serializeAttachment: vi.fn(next => { + attachment = next; + }), + } as unknown as WebSocket; + const ctx = { getWebSockets: vi.fn(() => [ws]) } as unknown as DurableObjectState; + const env = { + CLOUD_AGENT_SESSION: { idFromName: vi.fn(), get: vi.fn() }, + } as unknown as Env; + const facade = new UserKiloFacade(ctx, env); + const globalBody = facade.openPublicGlobalEventStream('usr_1').body; + const scopedBody = facade.openPublicSessionEventStream('usr_1', kiloSessionId).body; + if (!globalBody || !scopedBody) throw new Error('Expected public event response bodies'); + const globalReader = globalBody.getReader(); + const scopedReader = scopedBody.getReader(); + + try { + await readSseFrame(globalReader); + await readSseFrame(scopedReader); + const busyEvent = { + type: 'session.status', + properties: { sessionID: kiloSessionId, status: { type: 'busy' } }, + }; + await facade.webSocketMessage( + ws, + JSON.stringify({ directory: '/workspace/private', payload: busyEvent }) + ); + expect(facade.getPublicSessionStatuses()).toEqual({ [kiloSessionId]: { type: 'busy' } }); + await expect(readSseFrame(scopedReader)).resolves.toEqual(busyEvent); + await expect(readSseFrame(globalReader)).resolves.toEqual({ + directory: publicCloudAgentDirectory(kiloSessionId), + payload: busyEvent, + }); + + const idleEvent = { type: 'session.idle', properties: { sessionID: kiloSessionId } }; + await facade.webSocketMessage( + ws, + JSON.stringify({ directory: '/workspace/private', payload: idleEvent }) + ); + expect(facade.getPublicSessionStatuses()).toEqual({}); + await expect(readSseFrame(scopedReader)).resolves.toEqual(idleEvent); + await expect(readSseFrame(globalReader)).resolves.toEqual({ + directory: publicCloudAgentDirectory(kiloSessionId), + payload: idleEvent, + }); + } finally { + await globalReader.cancel().catch(() => undefined); + await scopedReader.cancel().catch(() => undefined); + } + }); + + it('drops invalid session status producer events from public streams', async () => { + const facade = new UserKiloFacade({} as DurableObjectState, {} as Env); + const body = facade.openPublicSessionEventStream('usr_1', kiloSessionId).body; + if (!body) throw new Error('Expected public scoped event response body'); + const reader = body.getReader(); + const ws = { + send: vi.fn(), + close: vi.fn(), + deserializeAttachment: () => ({ + userId: 'usr_1', + cloudAgentSessionId: 'agent_live', + kiloSessionId, + wrapperRunId: 'wr_current', + wrapperGeneration: 1, + wrapperConnectionId: 'conn_current', + }), + } as unknown as WebSocket; + + try { + await readSseFrame(reader); + await facade.webSocketMessage( + ws, + JSON.stringify({ + directory: '/workspace/private', + payload: { + type: 'session.status', + properties: { + sessionID: kiloSessionId, + status: { type: 'retry', attempt: 'invalid', message: 'invalid', next: 0 }, + }, + }, + }) + ); + await facade.webSocketMessage( + ws, + JSON.stringify({ + directory: '/workspace/private', + payload: { + type: 'message.updated', + properties: { sessionID: kiloSessionId, id: 'after-invalid-status' }, + }, + }) + ); + await expect(readSseFrame<{ type: string }>(reader)).resolves.toMatchObject({ + type: 'message.updated', + }); + } finally { + await reader.cancel().catch(() => undefined); + } + }); + + it('filters the attachment-backed status map to a selected root', () => { + const otherKiloSessionId = 'ses_22222222222222222222222222'; + const socket = (id: string) => + ({ + deserializeAttachment: () => ({ + kiloSessionId: id, + wrapperGeneration: 1, + sessionStatus: { type: 'busy' }, + }), + }) as unknown as WebSocket; + const ctx = { + getWebSockets: vi.fn(() => [socket(kiloSessionId), socket(otherKiloSessionId)]), + } as unknown as DurableObjectState; + const facade = new UserKiloFacade(ctx, {} as Env); + + expect(facade.getPublicSessionStatuses(kiloSessionId)).toEqual({ + [kiloSessionId]: { type: 'busy' }, + }); + }); + it('emits a native-shaped global connection event in the public virtual-server directory', async () => { const env = { CLOUD_AGENT_SESSION: { idFromName: vi.fn(), get: vi.fn() }, } as unknown as Env; const facade = new UserKiloFacade({} as DurableObjectState, env); - const response = facade.openPublicGlobalEventStream(); + const response = facade.openPublicGlobalEventStream('usr_1'); const body = response.body; if (!body) throw new Error('Expected public global event response body'); const reader = body.getReader(); @@ -2470,7 +2984,7 @@ describe('UserKiloFacade producer messages', () => { }, } as unknown as Env; const facade = new UserKiloFacade({} as DurableObjectState, env); - const response = facade.openPublicGlobalEventStream(); + const response = facade.openPublicGlobalEventStream('usr_1'); const body = response.body; if (!body) throw new Error('Expected public global event response body'); const reader = body.getReader(); @@ -2520,7 +3034,7 @@ describe('UserKiloFacade producer messages', () => { }, } as unknown as Env; const facade = new UserKiloFacade({} as DurableObjectState, env); - const response = facade.openPublicGlobalEventStream(); + const response = facade.openPublicGlobalEventStream('usr_1'); const body = response.body; if (!body) throw new Error('Expected public global event response body'); const reader = body.getReader(); @@ -2565,7 +3079,7 @@ describe('UserKiloFacade producer messages', () => { }, } as unknown as Env; const facade = new UserKiloFacade({} as DurableObjectState, env); - const response = facade.openPublicGlobalEventStream(); + const response = facade.openPublicGlobalEventStream('usr_1'); const body = response.body; if (!body) throw new Error('Expected public global event response body'); const reader = body.getReader(); @@ -2619,7 +3133,7 @@ describe('UserKiloFacade producer messages', () => { }, } as unknown as Env; const facade = new UserKiloFacade({} as DurableObjectState, env); - const response = facade.openPublicGlobalEventStream(); + const response = facade.openPublicGlobalEventStream('usr_1'); const body = response.body; if (!body) throw new Error('Expected public global event response body'); const reader = body.getReader(); @@ -2662,7 +3176,7 @@ describe('UserKiloFacade producer messages', () => { CLOUD_AGENT_SESSION: { idFromName: vi.fn(), get: vi.fn() }, } as unknown as Env; const facade = new UserKiloFacade({} as DurableObjectState, env); - const body = facade.openPublicSessionEventStream(kiloSessionId).body; + const body = facade.openPublicSessionEventStream('usr_1', kiloSessionId).body; if (!body) throw new Error('Expected public scoped event response body'); const reader = body.getReader(); try { @@ -2682,7 +3196,7 @@ describe('UserKiloFacade producer messages', () => { CLOUD_AGENT_SESSION: { idFromName: vi.fn(), get: vi.fn() }, } as unknown as Env; const facade = new UserKiloFacade({} as DurableObjectState, env); - const response = facade.openPublicSessionEventStream(kiloSessionId); + const response = facade.openPublicSessionEventStream('usr_1', kiloSessionId); const body = response.body; if (!body) throw new Error('Expected public scoped event response body'); const reader = body.getReader(); @@ -2708,8 +3222,8 @@ describe('UserKiloFacade producer messages', () => { CLOUD_AGENT_SESSION: { idFromName: vi.fn(), get: vi.fn() }, } as unknown as Env; const facade = new UserKiloFacade({} as DurableObjectState, env); - const globalBody = facade.openPublicGlobalEventStream().body; - const scopedBody = facade.openPublicSessionEventStream(kiloSessionId).body; + const globalBody = facade.openPublicGlobalEventStream('usr_1').body; + const scopedBody = facade.openPublicSessionEventStream('usr_1', kiloSessionId).body; if (!globalBody || !scopedBody) throw new Error('Expected public event response bodies'); const globalReader = globalBody.getReader(); const scopedReader = scopedBody.getReader(); @@ -2749,7 +3263,7 @@ describe('UserKiloFacade producer messages', () => { CLOUD_AGENT_SESSION: { idFromName: vi.fn(), get: vi.fn() }, } as unknown as Env; const facade = new UserKiloFacade({} as DurableObjectState, env); - const body = facade.openPublicGlobalEventStream().body; + const body = facade.openPublicGlobalEventStream('usr_1').body; if (!body) throw new Error('Expected public global event response body'); const reader = body.getReader(); const deniedEvent = { @@ -2806,7 +3320,7 @@ describe('UserKiloFacade producer messages', () => { }, } as unknown as Env; const facade = new UserKiloFacade({} as DurableObjectState, env); - const response = facade.openPublicSessionEventStream(kiloSessionId); + const response = facade.openPublicSessionEventStream('usr_1', kiloSessionId); const body = response.body; if (!body) throw new Error('Expected public scoped event response body'); const reader = body.getReader(); @@ -2907,8 +3421,8 @@ describe('UserKiloFacade producer messages', () => { }, } as unknown as Env; const facade = new UserKiloFacade({} as DurableObjectState, env); - const globalBody = facade.openPublicGlobalEventStream().body; - const scopedBody = facade.openPublicSessionEventStream(kiloSessionId).body; + const globalBody = facade.openPublicGlobalEventStream('usr_1').body; + const scopedBody = facade.openPublicSessionEventStream('usr_1', kiloSessionId).body; if (!globalBody || !scopedBody) throw new Error('Expected public event response bodies'); const globalReader = globalBody.getReader(); const scopedReader = scopedBody.getReader(); @@ -3045,7 +3559,7 @@ describe('UserKiloFacade producer messages', () => { }, } as unknown as Env; const facade = new UserKiloFacade({} as DurableObjectState, env); - const body = facade.openPublicGlobalEventStream().body; + const body = facade.openPublicGlobalEventStream('usr_1').body; if (!body) throw new Error('Expected public global event response body'); const reader = body.getReader(); const ws = { @@ -3102,7 +3616,7 @@ describe('UserKiloFacade producer messages', () => { }, } as unknown as Env; const facade = new UserKiloFacade({} as DurableObjectState, env); - const response = facade.openPublicGlobalEventStream(); + const response = facade.openPublicGlobalEventStream('usr_1'); const body = response.body; if (!body) throw new Error('Expected public global event response body'); const reader = body.getReader(); @@ -3237,7 +3751,7 @@ describe('UserKiloFacade producer messages', () => { }, } as unknown as Env; const facade = new UserKiloFacade({} as DurableObjectState, env); - const response = facade.openPublicGlobalEventStream(); + const response = facade.openPublicGlobalEventStream('usr_1'); const body = response.body; if (!body) { throw new Error('Expected public global event response body'); @@ -3287,7 +3801,7 @@ describe('UserKiloFacade producer messages', () => { }, } as unknown as Env; const facade = new UserKiloFacade({} as DurableObjectState, env); - const response = facade.openPublicGlobalEventStream(); + const response = facade.openPublicGlobalEventStream('usr_1'); const body = response.body; if (!body) { throw new Error('Expected public global event response body'); @@ -3349,6 +3863,312 @@ describe('UserKiloFacade producer messages', () => { }); }); +describe('UserKiloFacade metadata convergence', () => { + class ClientSocket extends EventTarget { + accept = vi.fn(); + close = vi.fn(); + + message(data: string | ArrayBuffer): void { + this.dispatchEvent(new MessageEvent('message', { data })); + } + } + + function metadataMessage(id = kiloSessionId): string { + return JSON.stringify({ + type: 'system', + event: 'session.updated', + data: { + source: 'v2', + changedAt: '2026-06-08T20:00:00.000Z', + session: { + source: 'v2', + sessionId: id, + createdAt: '2026-06-08T19:00:00.000Z', + updatedAt: '2026-06-08T20:00:00.000Z', + title: 'notification only', + createdOnPlatform: null, + organizationId: null, + gitUrl: null, + gitBranch: null, + parentSessionId: null, + status: null, + statusUpdatedAt: null, + }, + }, + }); + } + + function metadataEnv(socket: ClientSocket, snapshot = sdkSessionInfo(kiloSessionId)) { + const getSecret = vi.fn(async () => 'internal-secret'); + const fetch = vi.fn(async (_input: RequestInfo | URL, _init?: RequestInit) => ({ + status: 101, + webSocket: socket, + })); + const getSnapshot = vi.fn<(...args: never[]) => Promise>(async () => ({ + kiloSessionId, + cloudAgentSessionId: 'agent_live', + snapshot: { kind: 'value' as const, info: snapshot, byteLength: 100 }, + })); + const env = { + INTERNAL_API_SECRET_PROD: { get: getSecret }, + SESSION_INGEST: { + fetch, + listCloudAgentRootSessions: vi.fn(async () => []), + getCloudAgentRootSessionSnapshot: getSnapshot, + }, + CLOUD_AGENT_SESSION: { idFromName: vi.fn(), get: vi.fn() }, + } as unknown as Env; + return { env, fetch, getSecret, getSnapshot }; + } + + it('shares one demand-driven metadata socket and closes it after final cancellation', async () => { + const socket = new ClientSocket(); + const { env, fetch, getSecret } = metadataEnv(socket); + const facade = new UserKiloFacade({} as DurableObjectState, env); + const globalReader = facade.openPublicGlobalEventStream('usr_1').body?.getReader(); + const scopedReader = facade + .openPublicSessionEventStream('usr_1', kiloSessionId) + .body?.getReader(); + if (!globalReader || !scopedReader) throw new Error('Expected event streams'); + + await vi.waitFor(() => expect(fetch).toHaveBeenCalledOnce()); + expect(getSecret).toHaveBeenCalledOnce(); + const request = fetch.mock.calls[0]?.[0]; + const init = fetch.mock.calls[0]?.[1]; + expect(request).toContain('/internal/user/events?connectionId=facade-'); + expect(new Headers(init?.headers).get('X-Kilo-User-Id')).toBe('usr_1'); + expect(new Headers(init?.headers).get('X-Internal-Secret')).toBe('internal-secret'); + expect(socket.accept).toHaveBeenCalledOnce(); + + await globalReader.cancel(); + expect(socket.close).not.toHaveBeenCalled(); + await scopedReader.cancel(); + expect(socket.close).toHaveBeenCalledOnce(); + }); + + it('rejects a subscriber identity mismatch without joining the shared feed', async () => { + const socket = new ClientSocket(); + const { env, fetch } = metadataEnv(socket); + const facade = new UserKiloFacade({} as DurableObjectState, env); + const reader = facade.openPublicGlobalEventStream('usr_1').body?.getReader(); + if (!reader) throw new Error('Expected event stream'); + + const mismatched = facade.openPublicGlobalEventStream('usr_2'); + + expect(mismatched.status).toBe(403); + await vi.waitFor(() => expect(fetch).toHaveBeenCalledOnce()); + await reader.cancel(); + }); + + it('ignores malformed metadata and projects a valid canonical snapshot', async () => { + const socket = new ClientSocket(); + const canonical = { ...sdkSessionInfo(kiloSessionId), title: 'Canonical title' }; + const { env, getSnapshot } = metadataEnv(socket, canonical); + const facade = new UserKiloFacade({} as DurableObjectState, env); + const globalReader = facade.openPublicGlobalEventStream('usr_1').body?.getReader(); + if (!globalReader) throw new Error('Expected event stream'); + await readSseFrame(globalReader); + await vi.waitFor(() => expect(socket.accept).toHaveBeenCalledOnce()); + + socket.message('{bad json'); + socket.message(new Uint8Array([1, 2]).buffer); + expect(getSnapshot).not.toHaveBeenCalled(); + socket.message(metadataMessage()); + + await expect(readSseFrame(globalReader)).resolves.toMatchObject({ + directory: publicCloudAgentDirectory(kiloSessionId), + payload: { + type: 'session.updated', + properties: { sessionID: kiloSessionId, info: { title: 'Canonical title' } }, + }, + }); + expect(getSnapshot).toHaveBeenCalledOnce(); + + await globalReader.cancel(); + }); + + it('suppresses an in-flight canonical snapshot after a newer wrapper session update', async () => { + const socket = new ClientSocket(); + let resolveSnapshot: ((value: unknown) => void) | undefined; + const { env, getSnapshot } = metadataEnv(socket); + getSnapshot.mockImplementation( + () => new Promise(resolve => (resolveSnapshot = resolve)) as never + ); + const facade = new UserKiloFacade({} as DurableObjectState, env); + const reader = facade.openPublicGlobalEventStream('usr_1').body?.getReader(); + if (!reader) throw new Error('Expected event stream'); + await readSseFrame(reader); + await vi.waitFor(() => expect(socket.accept).toHaveBeenCalledOnce()); + socket.message(metadataMessage()); + await vi.waitFor(() => expect(getSnapshot).toHaveBeenCalled()); + + const wrapperSocket = { + send: vi.fn(), + close: vi.fn(), + deserializeAttachment: () => ({ kiloSessionId, wrapperGeneration: 1 }), + } as unknown as WebSocket; + await facade.webSocketMessage( + wrapperSocket, + JSON.stringify({ + directory: '/private', + payload: { + type: 'session.updated', + properties: { + sessionID: kiloSessionId, + info: { + ...sdkSessionInfo(kiloSessionId), + title: 'newer', + time: { created: 100, updated: 300 }, + }, + }, + }, + }) + ); + resolveSnapshot?.({ + kiloSessionId, + cloudAgentSessionId: 'agent_live', + snapshot: { kind: 'value', info: sdkSessionInfo(kiloSessionId), byteLength: 100 }, + }); + await Promise.resolve(); + const event = await readSseFrame<{ payload: { properties: { info: { title: string } } } }>( + reader + ); + expect(event.payload.properties.info.title).toBe('newer'); + + await reader.cancel(); + }); + + it('reconciles the public list window after the metadata socket connects', async () => { + const socket = new ClientSocket(); + const canonical = { ...sdkSessionInfo(kiloSessionId), title: 'Committed before connect' }; + const { env, fetch, getSnapshot } = metadataEnv(socket, canonical); + let resolveFetch: ((response: { status: number; webSocket: ClientSocket }) => void) | undefined; + fetch.mockImplementation(() => new Promise(resolve => (resolveFetch = resolve)) as never); + vi.mocked(env.SESSION_INGEST.listCloudAgentRootSessions).mockResolvedValue([ + { + kiloSessionId, + cloudAgentSessionId: 'agent_live', + title: canonical.title, + created: 100, + updated: 200, + }, + ]); + const facade = new UserKiloFacade({} as DurableObjectState, env); + const reader = facade.openPublicGlobalEventStream('usr_1').body?.getReader(); + if (!reader) throw new Error('Expected event stream'); + await readSseFrame(reader); + expect(getSnapshot).not.toHaveBeenCalled(); + + resolveFetch?.({ status: 101, webSocket: socket }); + + await expect(readSseFrame(reader)).resolves.toMatchObject({ + payload: { + type: 'session.updated', + properties: { info: { title: 'Committed before connect' } }, + }, + }); + expect(getSnapshot).toHaveBeenCalledOnce(); + await reader.cancel(); + }); + + it('retries global reconciliation after a transient root listing failure', async () => { + vi.useFakeTimers(); + const socket = new ClientSocket(); + const canonical = { ...sdkSessionInfo(kiloSessionId), title: 'Recovered reconciliation' }; + const { env, getSnapshot } = metadataEnv(socket, canonical); + const listRootSessions = vi.mocked(env.SESSION_INGEST.listCloudAgentRootSessions); + listRootSessions + .mockRejectedValueOnce(new Error('temporary Session Ingest failure')) + .mockResolvedValueOnce([ + { + kiloSessionId, + cloudAgentSessionId: 'agent_live', + title: canonical.title, + created: 100, + updated: 200, + }, + ]); + const facade = new UserKiloFacade({} as DurableObjectState, env); + const reader = facade.openPublicGlobalEventStream('usr_1').body?.getReader(); + if (!reader) throw new Error('Expected event stream'); + + try { + await readSseFrame(reader); + await vi.waitFor(() => expect(listRootSessions).toHaveBeenCalledOnce()); + expect(getSnapshot).not.toHaveBeenCalled(); + + await vi.advanceTimersByTimeAsync(100); + + await expect(readSseFrame(reader)).resolves.toMatchObject({ + payload: { + type: 'session.updated', + properties: { info: { title: 'Recovered reconciliation' } }, + }, + }); + expect(listRootSessions).toHaveBeenCalledTimes(2); + expect(getSnapshot).toHaveBeenCalledOnce(); + } finally { + await reader.cancel().catch(() => undefined); + vi.useRealTimers(); + } + }); + + it('cancels a pending global reconciliation retry when only scoped demand remains', async () => { + vi.useFakeTimers(); + const socket = new ClientSocket(); + const { env } = metadataEnv(socket); + const listRootSessions = vi.mocked(env.SESSION_INGEST.listCloudAgentRootSessions); + listRootSessions.mockRejectedValue(new Error('temporary Session Ingest failure')); + const facade = new UserKiloFacade({} as DurableObjectState, env); + const globalReader = facade.openPublicGlobalEventStream('usr_1').body?.getReader(); + const scopedReader = facade + .openPublicSessionEventStream('usr_1', kiloSessionId) + .body?.getReader(); + if (!globalReader || !scopedReader) throw new Error('Expected event streams'); + + try { + await readSseFrame(globalReader); + await readSseFrame(scopedReader); + await vi.waitFor(() => expect(listRootSessions).toHaveBeenCalledOnce()); + + await globalReader.cancel(); + await vi.advanceTimersByTimeAsync(100); + + expect(listRootSessions).toHaveBeenCalledOnce(); + } finally { + await globalReader.cancel().catch(() => undefined); + await scopedReader.cancel().catch(() => undefined); + vi.useRealTimers(); + } + }); + + it('retries one pending canonical snapshot and emits the recovered value', async () => { + vi.useFakeTimers(); + const socket = new ClientSocket(); + const { env, getSnapshot } = metadataEnv(socket); + getSnapshot + .mockResolvedValueOnce({ + kiloSessionId, + cloudAgentSessionId: 'agent_live', + snapshot: { kind: 'pending' }, + }) + .mockResolvedValueOnce({ + kiloSessionId, + cloudAgentSessionId: 'agent_live', + snapshot: { kind: 'value', info: sdkSessionInfo(kiloSessionId), byteLength: 100 }, + }); + const facade = new UserKiloFacade({} as DurableObjectState, env); + const reader = facade.openPublicSessionEventStream('usr_1', kiloSessionId).body?.getReader(); + if (!reader) throw new Error('Expected event stream'); + await readSseFrame(reader); + await vi.advanceTimersByTimeAsync(100); + await expect(readSseFrame(reader)).resolves.toMatchObject({ type: 'session.updated' }); + expect(getSnapshot).toHaveBeenCalledTimes(2); + await reader.cancel(); + vi.useRealTimers(); + }); +}); + describe('global event helpers', () => { it('rewrites only directory to the public Cloud Agent session namespace', () => { const payload = { diff --git a/services/cloud-agent-next/src/kilo-facade/user-kilo-facade.ts b/services/cloud-agent-next/src/kilo-facade/user-kilo-facade.ts index 3ff7d2ba41..f9663e6184 100644 --- a/services/cloud-agent-next/src/kilo-facade/user-kilo-facade.ts +++ b/services/cloud-agent-next/src/kilo-facade/user-kilo-facade.ts @@ -1,97 +1,34 @@ -import { - MAX_KILO_SDK_MESSAGE_HISTORY_PAGE_SIZE, - validateKiloSdkMessagesCursor, -} from '@kilocode/session-ingest-contracts'; import { DurableObject } from 'cloudflare:workers'; -import { TRPCError } from '@trpc/server'; -import type { - GetCloudAgentRootSessionMessagesParams, - KiloSdkSessionInfo, - KiloSdkStoredMessage, - ListCloudAgentRootSessionsParams, -} from '../session-ingest-binding.js'; -import { - fetchOrgIdForSession, - validateBalanceOnly, - type BalanceOnlyResult, -} from '../balance-validation.js'; -import type { - QueueExecutionTurnCommand, - SubmittedSessionMessageRequest, - SessionMessageAdmissionResult, -} from '../execution/types.js'; -import type { CloudAgentSession } from '../persistence/CloudAgentSession.js'; -import type { UserId } from '../types/ids.js'; +import type { SessionStatus } from '../shared/protocol.js'; import type { Env } from '../types.js'; -import { withDORetry } from '../utils/do-retry.js'; -import { preflightAndAdmitPromptMessage } from '../session/queue-message.js'; -import { parseBasicKiloPrompt } from './basic-prompt.js'; -import { - isPublicCloudAgentExtensionSourceType, - type PublicCloudAgentExtensionEvent, -} from './cloud-agent-extension-events.js'; -import { createProxyRequest } from '../shared/http-proxy.js'; -import { hasDuplicateQueryParameters } from '../shared/http-query.js'; +import type { PublicCloudAgentExtensionEvent } from './cloud-agent-extension-events.js'; +import type { KiloFacadeGlobalEvents } from './contracts.js'; +import { facadeError } from './http-contract.js'; +import { publicCloudAgentDirectory } from './public-sdk-projection.js'; +import { handleKiloFacadeRequest } from './request-dispatcher.js'; import { - projectPublicListedSession, - projectPublicSession, - projectPublicStoredMessages, - publicCloudAgentDirectory, -} from './public-sdk-projection.js'; + handleGlobalFeedUpgrade, + type GlobalFeedSource, +} from './handlers/global-feed-upgrade/handler.js'; import { - buildWrapperKiloProxyUrl, - decideSessionKiloFacadeRoute, - resolveLiveWrapperTarget, - type LiveWrapperTarget, - type SessionKiloFacadeDecision, - type SessionKiloFacadePolicyInput, -} from './session-proxy.js'; + interpretGlobalFeedMessage, + type KiloEventPayload, + type KiloGlobalEventEnvelope, +} from './handlers/global-feed-upgrade/message.js'; +import { publicSessionStatusesFromAttachments } from './handlers/session-status/tracking.js'; +import { FacadeMetadataFeed } from './metadata-feed.js'; +import { FacadeMetadataProjector } from './metadata-projector.js'; export const KILO_FACADE_USER_ID_HEADER = 'x-kilo-facade-user-id'; export const KILO_FACADE_AUTH_TOKEN_HEADER = 'x-kilo-facade-auth-token'; - export const KILO_FACADE_GLOBAL_FEED_PATH = '/internal/kilo/global-feed'; + const HEARTBEAT_INTERVAL_MS = 10_000; +const METADATA_RECONCILIATION_RETRY_DELAY_MS = 100; const MAX_PUBLIC_GLOBAL_EVENT_QUEUE_SIZE = 64; const SLOW_SUBSCRIBER_ERROR = 'Global event subscriber fell behind'; -const SUPPORTED_SESSION_LIST_QUERY_PARAMS = new Set(['limit', 'start']); -const SUPPORTED_SESSION_MESSAGES_QUERY_PARAMS = new Set(['directory', 'limit', 'before']); -const KNOWN_UNSUPPORTED_ROUTES = new Set([ - 'GET /session/status', - 'POST /session/viewed', - 'POST /sync/history', - 'GET /config', - 'GET /provider', - 'GET /project/current', - 'GET /global/health', -]); -const MAX_KILO_SESSION_JSON_BYTES = 8 * 1024 * 1024; -const MAX_KILO_ERROR_JSON_BYTES = 64 * 1024; -const MAX_KILO_PROMPT_JSON_BYTES = 256 * 1024; -const MAX_TIMESTAMP_MILLISECONDS = 8_640_000_000_000_000; const PUBLIC_VIRTUAL_SERVER_DIRECTORY = '/cloud-agent'; - -type KiloEventPayload = Record & { - id?: string; - type: string; - properties: Record; -}; - -type KiloGlobalEventEnvelope = Record & { - directory?: string; - project?: unknown; - workspace?: unknown; - payload?: KiloEventPayload; -}; - -type GlobalFeedSource = { - userId: string; - cloudAgentSessionId: string; - kiloSessionId: string; - wrapperRunId: string; - wrapperGeneration: number; - wrapperConnectionId: string; -}; +const encoder = new TextEncoder(); type PublicSubscriber = { controller: ReadableStreamDefaultController; @@ -99,885 +36,8 @@ type PublicSubscriber = { scope: { kind: 'global' } | { kind: 'session'; kiloSessionId: string }; }; -export type KiloFacadeGlobalEvents = { - openPublicGlobalEventStream(): Response; - openPublicSessionEventStream(kiloSessionId: string): Response; -}; - -export type KiloFacadeRequestDeps = { - resolveRootSessionForKiloSession?: (params: { - env: Env; - userId: string; - kiloSessionId: string; - }) => Promise<{ cloudAgentSessionId: string } | null>; - decideSessionRoute?: (input: SessionKiloFacadePolicyInput) => SessionKiloFacadeDecision; - resolveLiveWrapper?: (params: { - env: Env; - userId: string; - cloudAgentSessionId: string; - }) => Promise; - admitPrompt?: (params: { - env: Env; - userId: string; - cloudAgentSessionId: string; - request: SubmittedSessionMessageRequest; - }) => Promise; - validatePromptBalance?: (params: { - env: Env; - authToken: string; - userId: string; - cloudAgentSessionId: string; - }) => Promise; - interruptPrompt?: (params: { - env: Env; - userId: string; - cloudAgentSessionId: string; - }) => Promise>>; - globalEvents?: KiloFacadeGlobalEvents; -}; - -const encoder = new TextEncoder(); - -function isRecord(value: unknown): value is Record { - return typeof value === 'object' && value !== null; -} - -function facadeError(status: number, code: string, message: string): Response { - return Response.json({ error: code, message }, { status }); -} - -function kiloRelativePath(pathname: string): string { - if (pathname === '/kilo') { - return '/'; - } - if (pathname.startsWith('/kilo/')) { - return pathname.slice('/kilo'.length); - } - return pathname; -} - -function parseRootSessionRoute(kiloPath: string): { - encodedKiloSessionId: string; - kiloSessionId: string; -} | null { - const match = /^\/session\/([^/]+)(?:\/.*)?$/.exec(kiloPath); - if (!match?.[1]) { - return null; - } - try { - return { - encodedKiloSessionId: match[1], - kiloSessionId: decodeURIComponent(match[1]), - }; - } catch { - return null; - } -} - -function isSessionIngestKiloSessionId(kiloSessionId: string): boolean { - return kiloSessionId.startsWith('ses_') && kiloSessionId.length === 30; -} - -function missingRootKiloSessionResponse(): Response { - return facadeError(404, 'KILO_SESSION_NOT_FOUND', 'Cloud Agent root Kilo session was not found'); -} - -function pendingSessionSnapshotResponse(): Response { - const response = facadeError( - 503, - 'KILO_SESSION_SNAPSHOT_PENDING', - 'Cloud Agent Kilo session snapshot is not available yet' - ); - response.headers.set('Retry-After', '1'); - return response; -} - -function sessionSnapshotTooLargeResponse(): Response { - return facadeError( - 413, - 'KILO_SESSION_SNAPSHOT_TOO_LARGE', - 'Persisted Kilo session snapshot exceeds the safe cold-read budget' - ); -} - -function retryableSessionReadResponse(): Response { - const response = facadeError( - 503, - 'KILO_SESSION_READ_RETRYABLE', - 'Persisted Kilo session data is temporarily unavailable; retry the request' - ); - response.headers.set('Retry-After', '1'); - return response; -} - -function invalidPersistedSessionDataResponse(entity: 'session' | 'messages'): Response { - return facadeError(502, 'KILO_UPSTREAM_RESPONSE_INVALID', `Kilo ${entity} response is not valid`); -} - -function transcriptTooLargeResponse(): Response { - return facadeError( - 413, - 'KILO_TRANSCRIPT_TOO_LARGE', - 'Persisted Kilo transcript exceeds the safe cold-read budget; use smaller bounded history when possible, or retry while a live runtime is available' - ); -} - -function isKiloEventPayload(value: unknown): value is KiloEventPayload { - return isRecord(value) && typeof value.type === 'string' && isRecord(value.properties); -} - -function isKiloGlobalEventEnvelope(value: unknown): value is KiloGlobalEventEnvelope { - return isRecord(value) && isKiloEventPayload(value.payload); -} - -function isSubstantiveKiloGlobalEventEnvelope( - value: unknown -): value is KiloGlobalEventEnvelope & { directory: string; payload: KiloEventPayload } { - return ( - isKiloGlobalEventEnvelope(value) && - typeof value.directory === 'string' && - value.directory.length > 0 - ); -} - function createPublicEventPayload(type: 'server.connected'): KiloEventPayload { - return { - id: `evt_${crypto.randomUUID()}`, - type, - properties: {}, - }; -} - -export { publicCloudAgentDirectory }; - -function isKiloSdkSessionInfo(value: unknown, kiloSessionId: string): value is KiloSdkSessionInfo { - return ( - isRecord(value) && - value.id === kiloSessionId && - typeof value.slug === 'string' && - typeof value.projectID === 'string' && - typeof value.directory === 'string' && - typeof value.title === 'string' && - typeof value.version === 'string' && - isRecord(value.time) && - typeof value.time.created === 'number' && - typeof value.time.updated === 'number' - ); -} - -async function readBoundedBody( - response: Response, - maximumBytes: number -): Promise { - if (!response.body) { - return new Uint8Array(); - } - const reader = response.body.getReader(); - const chunks: Uint8Array[] = []; - let byteLength = 0; - for (;;) { - const chunk = await reader.read(); - if (chunk.done) { - break; - } - const value: unknown = chunk.value; - if (!(value instanceof Uint8Array)) { - await reader.cancel().catch(() => undefined); - return null; - } - if (byteLength + value.byteLength > maximumBytes) { - await reader.cancel().catch(() => undefined); - return null; - } - chunks.push(value); - byteLength += value.byteLength; - } - const bytes = new Uint8Array(byteLength); - let offset = 0; - for (const chunk of chunks) { - bytes.set(chunk, offset); - offset += chunk.byteLength; - } - return bytes; -} - -async function isUnavailableKiloRuntimeResponse(response: Response): Promise { - if (response.status !== 502 && response.status !== 503) { - return false; - } - const contentType = response.headers.get('content-type'); - if (!contentType?.toLowerCase().includes('application/json')) { - return false; - } - const bytes = await readBoundedBody(response.clone(), MAX_KILO_ERROR_JSON_BYTES); - if (!bytes) { - return false; - } - try { - const parsed: unknown = JSON.parse(new TextDecoder().decode(bytes)); - return ( - isRecord(parsed) && - (parsed.error === 'KILO_RUNTIME_UNAVAILABLE' || parsed.error === 'KILO_PROXY_ERROR') - ); - } catch { - return false; - } -} - -async function parseLiveSessionDetail( - response: Response, - kiloSessionId: string -): Promise { - const declaredLength = response.headers.get('content-length'); - if (declaredLength !== null) { - const bodyBytes = Number(declaredLength); - if (!Number.isSafeInteger(bodyBytes) || bodyBytes > MAX_KILO_SESSION_JSON_BYTES) { - return facadeError( - 502, - 'KILO_UPSTREAM_RESPONSE_INVALID', - 'Kilo session response exceeds supported size' - ); - } - } - - const bytes = await readBoundedBody(response, MAX_KILO_SESSION_JSON_BYTES); - if (!bytes) { - return facadeError( - 502, - 'KILO_UPSTREAM_RESPONSE_INVALID', - 'Kilo session response exceeds supported size' - ); - } - let parsed: unknown; - try { - parsed = JSON.parse(new TextDecoder().decode(bytes)); - } catch { - return facadeError( - 502, - 'KILO_UPSTREAM_RESPONSE_INVALID', - 'Kilo session response is not valid JSON' - ); - } - if (!isKiloSdkSessionInfo(parsed, kiloSessionId)) { - return facadeError(502, 'KILO_UPSTREAM_RESPONSE_INVALID', 'Kilo session response is not valid'); - } - return parsed; -} - -async function rewriteLiveSessionDetailResponse( - response: Response, - kiloSessionId: string -): Promise { - if (!response.ok) { - return response; - } - const info = await parseLiveSessionDetail(response, kiloSessionId); - if (info instanceof Response) { - return info; - } - const publicInfo = projectPublicSession(info, kiloSessionId); - const headers = new Headers(response.headers); - headers.delete('content-length'); - headers.delete('content-encoding'); - headers.set('content-type', 'application/json'); - return new Response(JSON.stringify(publicInfo), { - status: response.status, - statusText: response.statusText, - headers, - }); -} - -function isKiloSdkMessageInfo(value: unknown, kiloSessionId: string): boolean { - if (!isRecord(value) || value.sessionID !== kiloSessionId || typeof value.id !== 'string') { - return false; - } - if (value.role === 'user') return true; - return ( - value.role === 'assistant' && - isRecord(value.path) && - typeof value.path.cwd === 'string' && - typeof value.path.root === 'string' - ); -} - -function isKiloSdkPart(value: unknown, kiloSessionId: string): boolean { - if ( - !isRecord(value) || - value.sessionID !== kiloSessionId || - typeof value.messageID !== 'string' || - typeof value.id !== 'string' || - typeof value.type !== 'string' - ) { - return false; - } - if (value.type === 'file' && value.source !== undefined) { - return ( - isRecord(value.source) && - (value.source.type === 'resource' || typeof value.source.path === 'string') - ); - } - if (value.type === 'tool' && isRecord(value.state) && Array.isArray(value.state.attachments)) { - return value.state.attachments.every(part => isKiloSdkPart(part, kiloSessionId)); - } - return true; -} - -function isKiloSdkStoredMessage( - value: unknown, - kiloSessionId: string -): value is KiloSdkStoredMessage { - return ( - isRecord(value) && - isKiloSdkMessageInfo(value.info, kiloSessionId) && - Array.isArray(value.parts) && - value.parts.every(part => isKiloSdkPart(part, kiloSessionId)) - ); -} - -async function rewriteLiveMessagesResponse( - response: Response, - kiloSessionId: string -): Promise { - if (!response.ok) return response; - const declaredLength = response.headers.get('content-length'); - if (declaredLength !== null) { - const bodyBytes = Number(declaredLength); - if (!Number.isSafeInteger(bodyBytes) || bodyBytes > MAX_KILO_SESSION_JSON_BYTES) { - return facadeError( - 502, - 'KILO_UPSTREAM_RESPONSE_INVALID', - 'Kilo messages response exceeds supported size' - ); - } - } - const bytes = await readBoundedBody(response, MAX_KILO_SESSION_JSON_BYTES); - if (!bytes) { - return facadeError( - 502, - 'KILO_UPSTREAM_RESPONSE_INVALID', - 'Kilo messages response exceeds supported size' - ); - } - let parsed: unknown; - try { - parsed = JSON.parse(new TextDecoder().decode(bytes)); - } catch { - return facadeError( - 502, - 'KILO_UPSTREAM_RESPONSE_INVALID', - 'Kilo messages response is not valid JSON' - ); - } - if ( - !Array.isArray(parsed) || - !parsed.every(message => isKiloSdkStoredMessage(message, kiloSessionId)) - ) { - return facadeError( - 502, - 'KILO_UPSTREAM_RESPONSE_INVALID', - 'Kilo messages response is not valid' - ); - } - const publicMessages = projectPublicStoredMessages(parsed, kiloSessionId); - const headers = new Headers(response.headers); - headers.delete('content-length'); - headers.delete('content-encoding'); - headers.set('content-type', 'application/json'); - return new Response(JSON.stringify(publicMessages), { - status: response.status, - statusText: response.statusText, - headers, - }); -} - -function duplicateQueryParametersResponse(): Response { - return facadeError(400, 'KILO_QUERY_INVALID', 'Query parameters must be unique'); -} - -function parseSessionListQuery( - url: URL -): Omit | Response { - for (const key of url.searchParams.keys()) { - if (!SUPPORTED_SESSION_LIST_QUERY_PARAMS.has(key)) { - return facadeError( - 400, - 'KILO_SESSION_LIST_SELECTOR_UNSUPPORTED', - `Session list query parameter is not supported: ${key}` - ); - } - } - if (hasDuplicateQueryParameters(url.searchParams)) { - return duplicateQueryParametersResponse(); - } - - const params: Omit = {}; - const limitParam = url.searchParams.get('limit'); - if (limitParam !== null) { - const limit = Number(limitParam); - if (!Number.isInteger(limit) || limit < 1 || limit > 100) { - return facadeError( - 400, - 'KILO_QUERY_INVALID', - 'Session list limit must be an integer from 1 to 100' - ); - } - params.limit = limit; - } - const startParam = url.searchParams.get('start'); - if (startParam !== null) { - const start = Number(startParam); - if (!Number.isSafeInteger(start) || start < 0 || start > MAX_TIMESTAMP_MILLISECONDS) { - return facadeError( - 400, - 'KILO_QUERY_INVALID', - 'Session list start must be a non-negative integer' - ); - } - params.start = start; - } - return params; -} - -function unsupportedSessionSelectorResponse(): Response { - return facadeError( - 400, - 'KILO_SESSION_SELECTOR_UNSUPPORTED', - 'Only the matching public Cloud Agent session directory selector is supported' - ); -} - -function validateIdScopedSelectors( - url: URL, - kiloSessionId: string, - paginationKeys: Set -): Response | null { - for (const key of url.searchParams.keys()) { - if (paginationKeys.has(key)) continue; - if (key !== 'directory') return unsupportedSessionSelectorResponse(); - } - if (hasDuplicateQueryParameters(url.searchParams)) { - return duplicateQueryParametersResponse(); - } - const directory = url.searchParams.get('directory'); - if (directory !== null && directory !== publicCloudAgentDirectory(kiloSessionId)) { - return unsupportedSessionSelectorResponse(); - } - return null; -} - -function parseSessionMessagesQuery( - url: URL -): Pick | Response { - for (const key of url.searchParams.keys()) { - if (!SUPPORTED_SESSION_MESSAGES_QUERY_PARAMS.has(key)) { - return unsupportedSessionSelectorResponse(); - } - } - const params: Pick = {}; - const limitParam = url.searchParams.get('limit'); - if (limitParam !== null) { - const limit = Number(limitParam); - if (!Number.isInteger(limit) || limit < 0 || limit > MAX_KILO_SDK_MESSAGE_HISTORY_PAGE_SIZE) { - return facadeError( - 400, - 'KILO_QUERY_INVALID', - `Session messages limit must be an integer from 0 to ${MAX_KILO_SDK_MESSAGE_HISTORY_PAGE_SIZE}` - ); - } - params.limit = limit; - } - const before = url.searchParams.get('before'); - if (before !== null) { - if (before.length === 0 || params.limit === undefined || params.limit === 0) { - return facadeError( - 400, - 'KILO_QUERY_INVALID', - 'Session messages before requires a positive limit' - ); - } - if (!validateKiloSdkMessagesCursor(before)) { - return facadeError( - 400, - 'KILO_QUERY_INVALID', - 'Session messages before is not a valid cursor' - ); - } - params.before = before; - } - return params; -} - -function isExactSessionDetailRead(method: string, kiloPath: string, routePath: string): boolean { - return method === 'GET' && kiloPath === `/session/${routePath}`; -} - -function isExactSessionMessagesRead(method: string, kiloPath: string, routePath: string): boolean { - return method === 'GET' && kiloPath === `/session/${routePath}/message`; -} - -function isExactSessionPromptAsync(method: string, kiloPath: string, routePath: string): boolean { - return method === 'POST' && kiloPath === `/session/${routePath}/prompt_async`; -} - -function isExactSessionAbort(method: string, kiloPath: string, routePath: string): boolean { - return method === 'POST' && kiloPath === `/session/${routePath}/abort`; -} - -function promptAdmissionError( - result: Extract -): Response { - switch (result.code) { - case 'BAD_REQUEST': - return facadeError(400, 'KILO_PROMPT_ADMISSION_REJECTED', result.error); - case 'NOT_FOUND': - return missingRootKiloSessionResponse(); - case 'PENDING_QUEUE_FULL': - return facadeError(429, 'KILO_PROMPT_QUEUE_FULL', result.error); - case 'SANDBOX_CONNECT_FAILED': - case 'WORKSPACE_SETUP_FAILED': - case 'KILO_SERVER_FAILED': - case 'WRAPPER_START_FAILED': - case 'WRAPPER_FINALIZING': - return facadeError(503, result.code, result.error); - case 'INTERNAL': - return facadeError(500, 'KILO_PROMPT_ADMISSION_FAILED', result.error); - } -} - -function promptPreflightError(error: unknown): Response { - if (!(error instanceof TRPCError)) throw error; - switch (error.code) { - case 'BAD_REQUEST': - return facadeError(400, 'KILO_PROMPT_ADMISSION_REJECTED', error.message); - case 'NOT_FOUND': - return missingRootKiloSessionResponse(); - case 'FORBIDDEN': - return facadeError(403, 'KILO_PROMPT_ADMISSION_REJECTED', error.message); - case 'SERVICE_UNAVAILABLE': - return facadeError(503, 'MODEL_VALIDATION_UNAVAILABLE', error.message); - default: - throw error; - } -} - -type ReadRequestJsonResult = - | { success: true; value: unknown } - | { success: false; response: Response }; - -async function readRequestJson(request: Request): Promise { - const declaredLength = request.headers.get('content-length'); - if (declaredLength !== null) { - const bodyBytes = Number(declaredLength); - if (!Number.isSafeInteger(bodyBytes) || bodyBytes > MAX_KILO_PROMPT_JSON_BYTES) { - return { - success: false, - response: facadeError( - 400, - 'KILO_BASIC_PROMPT_UNSUPPORTED', - 'Basic Kilo prompt body is not supported' - ), - }; - } - } - const response = new Response(request.body); - const bytes = await readBoundedBody(response, MAX_KILO_PROMPT_JSON_BYTES); - if (!bytes) { - return { - success: false, - response: facadeError( - 400, - 'KILO_BASIC_PROMPT_UNSUPPORTED', - 'Basic Kilo prompt body is not supported' - ), - }; - } - try { - return { success: true, value: JSON.parse(new TextDecoder().decode(bytes)) }; - } catch { - return { - success: false, - response: facadeError( - 400, - 'KILO_BASIC_PROMPT_UNSUPPORTED', - 'Basic Kilo prompt body is not supported' - ), - }; - } -} - -async function defaultValidatePromptBalance(params: { - env: Env; - authToken: string; - userId: string; - cloudAgentSessionId: string; -}): Promise { - const orgId = await fetchOrgIdForSession(params.env, params.userId, params.cloudAgentSessionId); - return validateBalanceOnly(params.authToken, orgId, params.env); -} - -async function defaultAdmitPrompt(params: { - env: Env; - userId: string; - cloudAgentSessionId: string; - request: SubmittedSessionMessageRequest; -}): Promise { - const id = params.env.CLOUD_AGENT_SESSION.idFromName( - `${params.userId}:${params.cloudAgentSessionId}` - ); - return withDORetry, SessionMessageAdmissionResult>( - () => params.env.CLOUD_AGENT_SESSION.get(id), - stub => stub.admitSubmittedMessage(params.request), - 'admitSubmittedMessage' - ); -} - -async function admitBasicPrompt(params: { - request: Request; - env: Env; - userId: string; - authToken?: string; - cloudAgentSessionId: string; - deps?: KiloFacadeRequestDeps; -}): Promise { - const body = await readRequestJson(params.request); - if (!body.success) { - return body.response; - } - const parsed = parseBasicKiloPrompt(body.value); - if (!parsed.success) { - return facadeError( - 400, - 'KILO_BASIC_PROMPT_UNSUPPORTED', - 'Basic Kilo prompt body is not supported' - ); - } - if (!params.authToken) { - return facadeError(500, 'KILO_FACADE_UNAVAILABLE', 'Durable prompt admission is unavailable'); - } - if (params.request.headers.get('x-skip-balance-check') !== null) { - return facadeError( - 400, - 'KILO_BALANCE_BYPASS_UNSUPPORTED', - 'Balance bypass is not supported for public Kilo prompt mutations' - ); - } - const balance = await (params.deps?.validatePromptBalance ?? defaultValidatePromptBalance)({ - env: params.env, - authToken: params.authToken, - userId: params.userId, - cloudAgentSessionId: params.cloudAgentSessionId, - }); - if (!balance.success) { - return facadeError(balance.status, 'KILO_BALANCE_VALIDATION_FAILED', balance.message); - } - const command = { - turn: { - type: 'prompt', - id: parsed.prompt.messageId, - prompt: parsed.prompt.prompt, - }, - ...(parsed.prompt.agent ? { agent: parsed.prompt.agent } : {}), - } satisfies QueueExecutionTurnCommand; - const request: SubmittedSessionMessageRequest = { - userId: params.userId as UserId, - ...command, - }; - const admitPrompt = params.deps?.admitPrompt ?? defaultAdmitPrompt; - try { - return await preflightAndAdmitPromptMessage( - { cloudAgentSessionId: params.cloudAgentSessionId, ...command }, - { env: params.env, userId: params.userId }, - 'kilo.prompt_async', - () => - admitPrompt({ - env: params.env, - userId: params.userId, - cloudAgentSessionId: params.cloudAgentSessionId, - request, - }) - ); - } catch (error) { - return promptPreflightError(error); - } -} - -async function handlePromptAsyncMutation(params: { - request: Request; - env: Env; - userId: string; - authToken?: string; - cloudAgentSessionId: string; - deps?: KiloFacadeRequestDeps; -}): Promise { - const admission = await admitBasicPrompt(params); - if (admission instanceof Response) { - return admission; - } - if (!admission.success) { - return promptAdmissionError(admission); - } - return new Response(null, { status: 204 }); -} - -async function defaultInterruptPrompt(params: { - env: Env; - userId: string; - cloudAgentSessionId: string; -}): Promise>> { - const id = params.env.CLOUD_AGENT_SESSION.idFromName( - `${params.userId}:${params.cloudAgentSessionId}` - ); - return withDORetry< - DurableObjectStub, - Awaited> - >( - () => params.env.CLOUD_AGENT_SESSION.get(id), - stub => stub.interruptExecution(), - 'interruptExecution' - ); -} - -async function handleAbortMutation(params: { - env: Env; - userId: string; - cloudAgentSessionId: string; - deps?: KiloFacadeRequestDeps; -}): Promise { - await (params.deps?.interruptPrompt ?? defaultInterruptPrompt)({ - env: params.env, - userId: params.userId, - cloudAgentSessionId: params.cloudAgentSessionId, - }); - return Response.json(true); -} - -function messagesPageResponse( - requestUrl: URL, - messages: unknown, - nextCursor: string | null, - omittedItemCount: number -): Response { - const headers = new Headers({ - 'content-type': 'application/json', - 'X-Kilo-Omitted-Item-Count': String(omittedItemCount), - }); - if (nextCursor !== null) { - const nextUrl = new URL(requestUrl); - nextUrl.searchParams.set('before', nextCursor); - headers.set('Link', `<${nextUrl.toString()}>; rel="next"`); - headers.set('X-Next-Cursor', nextCursor); - } - return new Response(JSON.stringify(messages), { headers }); -} - -async function persistedSessionDetailResponse(params: { - env: Env; - userId: string; - kiloSessionId: string; -}): Promise { - const snapshot = await params.env.SESSION_INGEST.getCloudAgentRootSessionSnapshot({ - kiloUserId: params.userId, - kiloSessionId: params.kiloSessionId, - }); - if (!snapshot) { - return missingRootKiloSessionResponse(); - } - switch (snapshot.snapshot.kind) { - case 'pending': - return pendingSessionSnapshotResponse(); - case 'too_large': - return sessionSnapshotTooLargeResponse(); - case 'retryable_failure': - return retryableSessionReadResponse(); - case 'invalid_data': - return invalidPersistedSessionDataResponse('session'); - case 'value': - return Response.json(projectPublicSession(snapshot.snapshot.info, snapshot.kiloSessionId)); - } -} - -async function persistedSessionMessagesResponse(params: { - env: Env; - userId: string; - kiloSessionId: string; - url: URL; - query: Pick; -}): Promise { - const result = await params.env.SESSION_INGEST.getCloudAgentRootSessionMessages({ - kiloUserId: params.userId, - kiloSessionId: params.kiloSessionId, - ...params.query, - }); - if (!result) { - return missingRootKiloSessionResponse(); - } - if (result.history === null) { - return pendingSessionSnapshotResponse(); - } - if ('kind' in result.history) { - switch (result.history.kind) { - case 'too_large': - return transcriptTooLargeResponse(); - case 'retryable_failure': - return retryableSessionReadResponse(); - case 'invalid_data': - return invalidPersistedSessionDataResponse('messages'); - } - } - const publicMessages = projectPublicStoredMessages(result.history.messages, params.kiloSessionId); - return messagesPageResponse( - params.url, - publicMessages, - result.history.nextCursor, - result.history.omittedItemCount ?? 0 - ); -} - -type PersistedSessionRead = - | { kind: 'detail' } - | { - kind: 'messages'; - query: Pick; - }; - -function persistedSessionReadResponse(params: { - env: Env; - userId: string; - kiloSessionId: string; - url: URL; - read: PersistedSessionRead; -}): Promise { - switch (params.read.kind) { - case 'detail': - return persistedSessionDetailResponse(params); - case 'messages': - return persistedSessionMessagesResponse({ ...params, query: params.read.query }); - } -} - -export function isSyntheticGlobalEvent(event: KiloGlobalEventEnvelope): boolean { - const type = event.payload?.type; - return type === 'server.connected' || type === 'server.heartbeat'; -} - -function parsePublicSessionDirectory(directory: string): string | null { - const match = /^\/cloud-agent\/sessions\/([^/]+)$/.exec(directory); - if (!match?.[1]) return null; - try { - return decodeURIComponent(match[1]); - } catch { - return null; - } -} - -export function rewriteGlobalEventDirectory( - event: KiloGlobalEventEnvelope, - kiloSessionId: string -): KiloGlobalEventEnvelope { - return { - ...event, - directory: publicCloudAgentDirectory(kiloSessionId), - }; + return { id: `evt_${crypto.randomUUID()}`, type, properties: {} }; } export function encodeSseData(data: unknown): Uint8Array { @@ -988,367 +48,52 @@ function encodeSseComment(comment: string): Uint8Array { return encoder.encode(`: ${comment}\n\n`); } -export async function defaultResolveRootSessionForKiloSession(params: { - env: Env; - userId: string; - kiloSessionId: string; -}): Promise<{ cloudAgentSessionId: string } | null> { - return params.env.SESSION_INGEST.resolveCloudAgentRootSessionForKiloSession({ - kiloUserId: params.userId, - kiloSessionId: params.kiloSessionId, - }); -} - -async function eventStreamResponse(params: { - request: Request; - env: Env; - userId: string; - deps?: KiloFacadeRequestDeps; - url: URL; - kiloPath: string; -}): Promise { - const { request, env, userId, deps, url, kiloPath } = params; - if (kiloPath === '/global/event') { - if (request.method !== 'GET') { - return facadeError(501, 'KILO_ROUTE_UNSUPPORTED', 'Kilo facade route is not supported'); - } - if (url.search.length > 0) { - return facadeError( - 400, - 'KILO_EVENT_SELECTOR_UNSUPPORTED', - 'Global event selectors are not supported' - ); - } - const globalEvents = deps?.globalEvents; - if (!globalEvents) { - return facadeError(500, 'KILO_FACADE_UNAVAILABLE', 'Kilo facade stream is unavailable'); - } - return globalEvents.openPublicGlobalEventStream(); - } +export { handleKiloFacadeRequest, publicCloudAgentDirectory }; +export { + isSyntheticGlobalEvent, + rewriteGlobalEventDirectory, +} from './handlers/global-feed-upgrade/message.js'; +export type { KiloFacadeGlobalEvents, KiloFacadeRequestDeps } from './contracts.js'; - if (kiloPath !== '/event') { - return null; - } - if (request.method !== 'GET') { - return facadeError(501, 'KILO_ROUTE_UNSUPPORTED', 'Kilo facade route is not supported'); - } - if ([...url.searchParams.keys()].some(key => key !== 'directory')) { - return facadeError( - 400, - 'KILO_EVENT_SELECTOR_UNSUPPORTED', - 'Only the Cloud Agent session directory selector is supported' - ); - } - if (hasDuplicateQueryParameters(url.searchParams)) { - return duplicateQueryParametersResponse(); - } - const directory = url.searchParams.get('directory'); - if (!directory) { - return facadeError( - 400, - 'KILO_EVENT_DIRECTORY_REQUIRED', - 'A Cloud Agent session directory selector is required' - ); - } - const kiloSessionId = parsePublicSessionDirectory(directory); - if (!kiloSessionId) { - return facadeError( - 400, - 'KILO_EVENT_SELECTOR_UNSUPPORTED', - 'Only a Cloud Agent root session directory selector is supported' - ); - } - if (!isSessionIngestKiloSessionId(kiloSessionId)) { - return missingRootKiloSessionResponse(); - } - const resolveRoot = - deps?.resolveRootSessionForKiloSession ?? defaultResolveRootSessionForKiloSession; - const root = await resolveRoot({ env, userId, kiloSessionId }); - if (!root) { - return missingRootKiloSessionResponse(); - } - const globalEvents = deps?.globalEvents; - if (!globalEvents) { - return facadeError(500, 'KILO_FACADE_UNAVAILABLE', 'Kilo facade stream is unavailable'); - } - return globalEvents.openPublicSessionEventStream(kiloSessionId); -} - -async function proxyOwnedKiloSessionRequest(params: { - request: Request; - env: Env; - userId: string; - deps?: KiloFacadeRequestDeps; - url: URL; - kiloPath: string; - kiloSessionId: string; - cloudAgentSessionId: string; - persistedRead: PersistedSessionRead | null; -}): Promise { - const { request, env, userId, deps, url, kiloPath, kiloSessionId, cloudAgentSessionId } = params; - const persistedFallback = () => - params.persistedRead - ? persistedSessionReadResponse({ - env, - userId, - kiloSessionId, - url, - read: params.persistedRead, - }) - : null; +export class UserKiloFacade extends DurableObject implements KiloFacadeGlobalEvents { + private subscribers = new Map(); + private subscriberUserId?: string; + private readonly metadataFeed: FacadeMetadataFeed; + private readonly metadataProjector: FacadeMetadataProjector; - let liveWrapper: LiveWrapperTarget | null; - try { - liveWrapper = await (deps?.resolveLiveWrapper ?? resolveLiveWrapperTarget)({ + constructor(ctx: DurableObjectState, env: Env) { + super(ctx, env); + this.metadataProjector = new FacadeMetadataProjector( env, - userId, - cloudAgentSessionId, - }); - } catch (error) { - const fallback = persistedFallback(); - if (fallback) return fallback; - throw error; - } - if (!liveWrapper) { - const fallback = persistedFallback(); - if (fallback) return fallback; - return facadeError( - 503, - 'KILO_LIVE_RUNTIME_UNAVAILABLE', - 'Cloud Agent Kilo runtime is not live' + (event, kiloSessionId) => this.broadcastGlobalEvent(event, kiloSessionId), + generation => this.metadataFeed.isCurrent(generation) ); - } - - const upstreamSearchParams = new URLSearchParams(url.searchParams); - upstreamSearchParams.delete('directory'); - const upstreamSearch = upstreamSearchParams.size > 0 ? `?${upstreamSearchParams.toString()}` : ''; - const targetUrl = buildWrapperKiloProxyUrl({ - wrapperPort: liveWrapper.port, - kiloRelativePath: kiloPath, - search: upstreamSearch, - }); - const proxyRequest = createProxyRequest(request, targetUrl); - let response: Response; - try { - response = await liveWrapper.sandbox.containerFetch(proxyRequest, liveWrapper.port); - } catch (error) { - const fallback = persistedFallback(); - if (fallback) return fallback; - throw error; - } - - if (!params.persistedRead) { - return response; - } - if (await isUnavailableKiloRuntimeResponse(response)) { - return persistedSessionReadResponse({ - env, - userId, - kiloSessionId, - url, - read: params.persistedRead, - }); - } - switch (params.persistedRead.kind) { - case 'detail': - return rewriteLiveSessionDetailResponse(response, kiloSessionId); - case 'messages': - return rewriteLiveMessagesResponse(response, kiloSessionId); - } -} - -export async function handleKiloFacadeRequest(params: { - request: Request; - env: Env; - userId: string; - authToken?: string; - deps?: KiloFacadeRequestDeps; -}): Promise { - const { request, env, userId, authToken, deps } = params; - const url = new URL(request.url); - const kiloPath = kiloRelativePath(url.pathname); - - const streamResponse = await eventStreamResponse({ request, env, userId, deps, url, kiloPath }); - if (streamResponse) { - return streamResponse; - } - - if (kiloPath === '/session' && request.method === 'GET') { - const query = parseSessionListQuery(url); - if (query instanceof Response) { - return query; - } - const sessions = await env.SESSION_INGEST.listCloudAgentRootSessions({ - ...query, - kiloUserId: userId, - }); - return Response.json(sessions.map(projectPublicListedSession)); - } - - if (kiloPath === '/session' || KNOWN_UNSUPPORTED_ROUTES.has(`${request.method} ${kiloPath}`)) { - return facadeError(501, 'KILO_ROUTE_UNSUPPORTED', 'Kilo facade route is not supported'); - } - - const route = parseRootSessionRoute(kiloPath); - if (!route) { - return facadeError(501, 'KILO_ROUTE_UNSUPPORTED', 'Kilo facade route is not supported'); - } - if (!isSessionIngestKiloSessionId(route.kiloSessionId)) { - return missingRootKiloSessionResponse(); - } - - const resolveRoot = - deps?.resolveRootSessionForKiloSession ?? defaultResolveRootSessionForKiloSession; - const root = await resolveRoot({ env, userId, kiloSessionId: route.kiloSessionId }); - if (!root) { - return missingRootKiloSessionResponse(); - } - - const policyInput: SessionKiloFacadePolicyInput = { - method: request.method, - kiloRelativePath: kiloPath, - search: url.search, - userId, - kiloSessionId: route.kiloSessionId, - cloudAgentSessionId: root.cloudAgentSessionId, - }; - const decision = (deps?.decideSessionRoute ?? decideSessionKiloFacadeRoute)(policyInput); - if (decision.kind === 'reject') { - return facadeError(decision.status, decision.code, decision.message); - } - - const routeClassification = { - detailRead: isExactSessionDetailRead(request.method, kiloPath, route.encodedKiloSessionId), - messagesRead: isExactSessionMessagesRead(request.method, kiloPath, route.encodedKiloSessionId), - promptAsync: isExactSessionPromptAsync(request.method, kiloPath, route.encodedKiloSessionId), - abort: isExactSessionAbort(request.method, kiloPath, route.encodedKiloSessionId), - }; - if ( - routeClassification.detailRead || - routeClassification.messagesRead || - routeClassification.promptAsync || - routeClassification.abort - ) { - const paginationKeys = routeClassification.messagesRead - ? new Set(['limit', 'before']) - : new Set(); - const selectorResponse = validateIdScopedSelectors(url, route.kiloSessionId, paginationKeys); - if (selectorResponse) return selectorResponse; - } - - if (routeClassification.promptAsync) { - return handlePromptAsyncMutation({ - request, - env, - userId, - authToken, - cloudAgentSessionId: root.cloudAgentSessionId, - deps, - }); - } - if (routeClassification.abort) { - return handleAbortMutation({ - env, - userId, - cloudAgentSessionId: root.cloudAgentSessionId, - deps, + this.metadataFeed = new FacadeMetadataFeed(env, { + onMessage: (message, generation) => { + this.metadataProjector.notify(message, generation); + }, + onConnected: generation => { + void this.reconcileMetadata(generation); + }, + onStopped: () => { + this.metadataProjector.stop(); + }, }); } - const messageQuery = routeClassification.messagesRead ? parseSessionMessagesQuery(url) : null; - if (messageQuery instanceof Response) { - return messageQuery; - } - const persistedRead: PersistedSessionRead | null = routeClassification.detailRead - ? { kind: 'detail' } - : routeClassification.messagesRead && messageQuery !== null - ? { kind: 'messages', query: messageQuery } - : null; - return proxyOwnedKiloSessionRequest({ - request, - env, - userId, - deps, - url, - kiloPath, - kiloSessionId: route.kiloSessionId, - cloudAgentSessionId: root.cloudAgentSessionId, - persistedRead, - }); -} - -function parseGlobalFeedSource(request: Request): GlobalFeedSource | Response { - const url = new URL(request.url); - if (hasDuplicateQueryParameters(url.searchParams)) { - return facadeError(400, 'INVALID_GLOBAL_FEED_SOURCE', 'Invalid global feed source'); - } - const userId = url.searchParams.get('userId'); - const cloudAgentSessionId = url.searchParams.get('cloudAgentSessionId'); - const kiloSessionId = url.searchParams.get('kiloSessionId'); - const wrapperRunId = url.searchParams.get('wrapperRunId'); - const wrapperGenerationParam = url.searchParams.get('wrapperGeneration'); - const wrapperConnectionId = url.searchParams.get('wrapperConnectionId'); - const wrapperGeneration = wrapperGenerationParam ? Number(wrapperGenerationParam) : NaN; - - if ( - !userId || - !cloudAgentSessionId || - !kiloSessionId || - !wrapperRunId || - !Number.isInteger(wrapperGeneration) || - wrapperGeneration < 0 || - !wrapperConnectionId - ) { - return facadeError(400, 'INVALID_GLOBAL_FEED_SOURCE', 'Invalid global feed source'); - } - - return { - userId, - cloudAgentSessionId, - kiloSessionId, - wrapperRunId, - wrapperGeneration, - wrapperConnectionId, - }; -} - -function producerTag(source: Pick): string { - return `kilo-global:${source.cloudAgentSessionId}`; -} - -function isSameGlobalFeedProducer(left: GlobalFeedSource, right: GlobalFeedSource): boolean { - return ( - left.wrapperRunId === right.wrapperRunId && - left.wrapperGeneration === right.wrapperGeneration && - left.wrapperConnectionId === right.wrapperConnectionId - ); -} - -function mayReplaceGlobalFeedProducer( - existing: GlobalFeedSource, - candidate: GlobalFeedSource -): boolean { - if (candidate.wrapperGeneration > existing.wrapperGeneration) return true; - if (candidate.wrapperGeneration < existing.wrapperGeneration) return false; - return isSameGlobalFeedProducer(existing, candidate); -} - -export class UserKiloFacade extends DurableObject implements KiloFacadeGlobalEvents { - private subscribers = new Map(); - async fetch(request: Request): Promise { const url = new URL(request.url); - if (url.pathname === KILO_FACADE_GLOBAL_FEED_PATH) { - return this.handleGlobalFeedRequest(request); + return handleGlobalFeedUpgrade({ + request, + ctx: this.ctx, + validateProducer: source => this.validateGlobalFeedProducer(source), + }); } const userId = request.headers.get(KILO_FACADE_USER_ID_HEADER); - if (!userId) { - return facadeError(401, 'UNAUTHENTICATED', 'Missing authenticated user context'); - } + if (!userId) return facadeError(401, 'UNAUTHENTICATED', 'Missing authenticated user context'); const authToken = request.headers.get(KILO_FACADE_AUTH_TOKEN_HEADER) ?? undefined; - return handleKiloFacadeRequest({ request, env: this.env, @@ -1358,12 +103,21 @@ export class UserKiloFacade extends DurableObject implements KiloFacadeGlob }); } - openPublicGlobalEventStream(): Response { - return this.openPublicEventStream({ kind: 'global' }); + getPublicSessionStatuses(kiloSessionId?: string): Record { + const attachments: unknown[] = []; + for (const socket of this.ctx.getWebSockets()) { + const attachment: unknown = socket.deserializeAttachment(); + attachments.push(attachment); + } + return publicSessionStatusesFromAttachments(attachments, kiloSessionId); + } + + openPublicGlobalEventStream(userId: string): Response { + return this.openPublicEventStream(userId, { kind: 'global' }); } - openPublicSessionEventStream(kiloSessionId: string): Response { - return this.openPublicEventStream({ kind: 'session', kiloSessionId }); + openPublicSessionEventStream(userId: string, kiloSessionId: string): Response { + return this.openPublicEventStream(userId, { kind: 'session', kiloSessionId }); } async publishCloudAgentExtensionEvent(input: { @@ -1388,7 +142,14 @@ export class UserKiloFacade extends DurableObject implements KiloFacadeGlob ); } - private openPublicEventStream(scope: PublicSubscriber['scope']): Response { + private openPublicEventStream(userId: string, scope: PublicSubscriber['scope']): Response { + if (this.subscriberUserId && this.subscriberUserId !== userId) { + return facadeError( + 403, + 'KILO_FACADE_IDENTITY_MISMATCH', + 'Authenticated user context does not match the active facade stream' + ); + } const subscriberId = crypto.randomUUID(); const publicConnectionEvent = () => scope.kind === 'global' @@ -1397,7 +158,6 @@ export class UserKiloFacade extends DurableObject implements KiloFacadeGlob payload: createPublicEventPayload('server.connected'), } : createPublicEventPayload('server.connected'); - const stream = new ReadableStream( { start: controller => { @@ -1409,6 +169,9 @@ export class UserKiloFacade extends DurableObject implements KiloFacadeGlob }, HEARTBEAT_INTERVAL_MS), }; this.subscribers.set(subscriberId, subscriber); + this.subscriberUserId ??= userId; + const demand = this.metadataFeed.addDemand(userId); + if (demand.started) this.metadataProjector.start(demand.generation, userId); this.sendToSubscriber(subscriberId, publicConnectionEvent()); }, cancel: () => { @@ -1417,7 +180,6 @@ export class UserKiloFacade extends DurableObject implements KiloFacadeGlob }, { highWaterMark: MAX_PUBLIC_GLOBAL_EVENT_QUEUE_SIZE, size: () => 1 } ); - return new Response(stream, { headers: { 'Content-Type': 'text/event-stream', @@ -1436,8 +198,7 @@ export class UserKiloFacade extends DurableObject implements KiloFacadeGlob const sessionDoId = this.env.CLOUD_AGENT_SESSION.idFromName( `${source.userId}:${source.cloudAgentSessionId}` ); - const sessionStub = this.env.CLOUD_AGENT_SESSION.get(sessionDoId); - return sessionStub.validateKiloGlobalFeedProducer({ + return this.env.CLOUD_AGENT_SESSION.get(sessionDoId).validateKiloGlobalFeedProducer({ kiloSessionId: source.kiloSessionId, wrapperRunId: source.wrapperRunId, wrapperGeneration: source.wrapperGeneration, @@ -1446,90 +207,27 @@ export class UserKiloFacade extends DurableObject implements KiloFacadeGlob } private handleGlobalFeedMessage(ws: WebSocket, message: string | ArrayBuffer): void { - if (typeof message !== 'string') { - ws.send(JSON.stringify({ error: 'Binary global feed messages are not supported' })); - return; - } - const source = ws.deserializeAttachment() as GlobalFeedSource | null; - if (!source) { - ws.close(1011, 'Missing global feed source'); - return; - } - - let parsed: unknown; - try { - parsed = JSON.parse(message); - } catch { - ws.send(JSON.stringify({ error: 'Invalid global feed JSON' })); - return; - } - - if (!isKiloGlobalEventEnvelope(parsed)) { - ws.send(JSON.stringify({ error: 'Invalid global event envelope' })); - return; - } - - if (isSyntheticGlobalEvent(parsed)) { - return; - } - if (!isSubstantiveKiloGlobalEventEnvelope(parsed)) { - ws.send(JSON.stringify({ error: 'Invalid global event envelope' })); - return; - } - if (isPublicCloudAgentExtensionSourceType(parsed.payload.type)) { - return; - } - - if (parsed.payload.properties.sessionID !== source.kiloSessionId) { - return; + const result = interpretGlobalFeedMessage(source, message); + switch (result.kind) { + case 'send-error': + ws.send(JSON.stringify({ error: result.error })); + return; + case 'close': + ws.close(result.code, result.reason); + return; + case 'drop': + return; + case 'broadcast': + if (result.nextSource) ws.serializeAttachment(result.nextSource); + if (this.metadataProjector.recordWrapperEvent(result.event, result.kiloSessionId)) { + this.broadcastGlobalEvent(result.event, result.kiloSessionId); + } } - const publicEnvelope = rewriteGlobalEventDirectory(parsed, source.kiloSessionId); - this.broadcastGlobalEvent(publicEnvelope, source.kiloSessionId); } webSocketClose(): void { - // Socket attachments expire with the socket; no persistent cleanup is needed. - } - - private async handleGlobalFeedRequest(request: Request): Promise { - if (request.headers.get('Upgrade')?.toLowerCase() !== 'websocket') { - return facadeError(426, 'WEBSOCKET_REQUIRED', 'Expected WebSocket upgrade'); - } - - const source = parseGlobalFeedSource(request); - if (source instanceof Response) { - return source; - } - - const validation = await this.validateGlobalFeedProducer(source); - if (!validation.success) { - return new Response(validation.message, { status: validation.status }); - } - - const tag = producerTag(source); - const existingSockets = this.ctx.getWebSockets(tag); - for (const existing of existingSockets) { - const existingSource = existing.deserializeAttachment() as GlobalFeedSource | null; - if (existingSource && !mayReplaceGlobalFeedProducer(existingSource, source)) { - return new Response('A newer global feed producer is already connected', { status: 409 }); - } - } - for (const existing of existingSockets) { - try { - existing.close(1000, 'Replaced by newer global feed'); - } catch { - // Ignore already-closed producer sockets. - } - } - - const pair = new WebSocketPair(); - const client = pair[0]; - const server = pair[1]; - this.ctx.acceptWebSocket(server, [tag]); - server.serializeAttachment(source); - - return new Response(null, { status: 101, webSocket: client }); + // A producer disconnect is not an idle transition; a reconnect sends an authoritative status. } private sendFrameToSubscriber(subscriberId: string, frame: Uint8Array): void { @@ -1559,9 +257,7 @@ export class UserKiloFacade extends DurableObject implements KiloFacadeGlob for (const [subscriberId, subscriber] of this.subscribers.entries()) { if (subscriber.scope.kind === 'global') { this.sendToSubscriber(subscriberId, event); - continue; - } - if (subscriber.scope.kiloSessionId === kiloSessionId && event.payload) { + } else if (subscriber.scope.kiloSessionId === kiloSessionId && event.payload) { this.sendToSubscriber(subscriberId, event.payload); } } @@ -1572,5 +268,57 @@ export class UserKiloFacade extends DurableObject implements KiloFacadeGlob if (!subscriber) return; clearInterval(subscriber.heartbeat); this.subscribers.delete(subscriberId); + this.metadataFeed.removeDemand(); + if (this.subscribers.size === 0) this.subscriberUserId = undefined; + } + + private async reconcileMetadata(generation: number): Promise { + const scopedIds = [...this.subscribers.values()].flatMap(subscriber => + subscriber.scope.kind === 'session' ? [subscriber.scope.kiloSessionId] : [] + ); + if (!this.hasGlobalSubscriber()) { + this.metadataProjector.reconcile(scopedIds, generation); + return; + } + + const roots = await this.listGlobalReconciliationRoots(generation); + if (!this.metadataFeed.isCurrent(generation)) return; + if (!roots || !this.hasGlobalSubscriber()) { + this.metadataProjector.reconcile(scopedIds, generation); + return; + } + this.metadataProjector.reconcile( + [...scopedIds, ...roots.map(root => root.kiloSessionId)], + generation + ); + } + + private async listGlobalReconciliationRoots(generation: number) { + try { + return await this.env.SESSION_INGEST.listCloudAgentRootSessions({ + kiloUserId: this.currentSubscriberUserId(), + limit: 100, + }); + } catch { + if (!this.metadataFeed.isCurrent(generation) || !this.hasGlobalSubscriber()) return null; + await new Promise(resolve => setTimeout(resolve, METADATA_RECONCILIATION_RETRY_DELAY_MS)); + if (!this.metadataFeed.isCurrent(generation) || !this.hasGlobalSubscriber()) return null; + try { + return await this.env.SESSION_INGEST.listCloudAgentRootSessions({ + kiloUserId: this.currentSubscriberUserId(), + limit: 100, + }); + } catch { + return null; + } + } + } + + private hasGlobalSubscriber(): boolean { + return [...this.subscribers.values()].some(subscriber => subscriber.scope.kind === 'global'); + } + + private currentSubscriberUserId(): string { + return this.metadataFeed.userIdForCurrentDemand(); } } diff --git a/services/cloud-agent-next/src/kilo/wrapper-client.test.ts b/services/cloud-agent-next/src/kilo/wrapper-client.test.ts index d044998c01..5b79c6e4b1 100644 --- a/services/cloud-agent-next/src/kilo/wrapper-client.test.ts +++ b/services/cloud-agent-next/src/kilo/wrapper-client.test.ts @@ -697,6 +697,7 @@ describe('WrapperClient', () => { command: 'compact', args: '--aggressive', messageId: 'msg_018f1e2d3c4bCommandWireAAA', + snapshotInitialization: 'wait', agent: { model: { modelID: 'anthropic/claude-sonnet-4-20250514' } }, autoCommit: true, condenseOnComplete: false, @@ -711,6 +712,7 @@ describe('WrapperClient', () => { const execCall = (session.exec as ReturnType).mock.calls[0][0] as string; expect(execCall).toContain('"messageId":"msg_018f1e2d3c4bCommandWireAAA"'); + expect(execCall).toContain('"snapshotInitialization":"wait"'); expect(execCall).toContain('"modelID":"anthropic/claude-sonnet-4-20250514"'); expect(execCall).toContain('"autoCommit":true'); expect(execCall).toContain('"condenseOnComplete":false'); diff --git a/services/cloud-agent-next/src/persistence/CloudAgentSession.ts b/services/cloud-agent-next/src/persistence/CloudAgentSession.ts index 7e97bf3e0b..3e825a1ead 100644 --- a/services/cloud-agent-next/src/persistence/CloudAgentSession.ts +++ b/services/cloud-agent-next/src/persistence/CloudAgentSession.ts @@ -1026,6 +1026,19 @@ export class CloudAgentSession extends DurableObject { return this.getSessionMessageQueue().snapshotForStreamConnect(); } + async getInitialMessageSnapshot(): Promise { + const metadata = await this.getMetadata(); + const messageId = metadata?.initialMessage?.id; + if (!messageId) return null; + const state = await getSessionMessageState(this.ctx.storage, messageId); + if (!state) return null; + return { + messageId, + content: state.prompt, + timestamp: state.queuedAt ?? state.createdAt, + }; + } + /** * Get count of connected stream clients. * diff --git a/services/cloud-agent-next/src/session-prepare.test.ts b/services/cloud-agent-next/src/session-prepare.test.ts index dde80b771a..72dcfd2249 100644 --- a/services/cloud-agent-next/src/session-prepare.test.ts +++ b/services/cloud-agent-next/src/session-prepare.test.ts @@ -455,7 +455,8 @@ describe('prepareSession endpoint', () => { expect.any(Object), 'f47ac10b-58cc-4372-a567-0e02b2c3d479', 'code-review', - expect.stringMatching(/^New session - /) + expect.stringMatching(/^New session - /), + 'https://github.com/acme/repo' ); expect(doStub.registerSession).toHaveBeenCalledWith( expect.objectContaining({ @@ -1226,6 +1227,16 @@ describe('start endpoint', () => { }); expect(steps).toEqual(['session-report', 'sandbox', 'sandbox-report', 'visible', 'admission']); + expect(createCliSessionMock).toHaveBeenCalledWith( + expect.any(String), + expect.any(String), + expect.any(String), + expect.any(Object), + undefined, + 'cloud-agent', + expect.any(String), + 'https://github.com/acme/repo' + ); expect(createSessionReportMock).toHaveBeenCalledWith( { cloudAgentSessionId: 'agent_12345678-1234-1234-1234-123456789abc', @@ -1244,6 +1255,27 @@ describe('start endpoint', () => { expect(recordSessionFailureMock).not.toHaveBeenCalled(); }); + it('sanitizes GitLab repository identity before visible ownership creation', async () => { + const caller = appRouter.createCaller(createInternalApiContext({})); + + await caller.start({ + message: { prompt: 'Create GitLab ownership' }, + agent: { mode: 'code', model: 'anthropic/claude-sonnet-4-20250514' }, + repository: { type: 'gitlab', url: 'https://token@gitlab.com/acme/repo.git?ref=main#readme' }, + }); + + expect(createCliSessionMock).toHaveBeenCalledWith( + expect.any(String), + expect.any(String), + expect.any(String), + expect.any(Object), + undefined, + 'cloud-agent', + expect.any(String), + 'https://gitlab.com/acme/repo.git' + ); + }); + it('admits canonical document attachments through one grouped creation operation', async () => { const initialMessageId = 'msg_018f1e2d3c4bAbCdEfGhIjKlMn'; const attachments = { diff --git a/services/cloud-agent-next/src/session-service.test.ts b/services/cloud-agent-next/src/session-service.test.ts index 751e2e9326..cb1a040d7c 100644 --- a/services/cloud-agent-next/src/session-service.test.ts +++ b/services/cloud-agent-next/src/session-service.test.ts @@ -1053,6 +1053,45 @@ describe('SessionService.buildWrapperSessionReadyAndPromptRequests', () => { }); } + it('preserves command snapshot policy in the wrapper request', async () => { + const service = new SessionService(); + const env = createEnv(); + env.WORKER_URL = 'https://cloud-agent.example.com'; + + const result = await service.buildWrapperSessionReadyAndPromptRequests({ + env, + plan: { + scope: { sessionId: 'agent_test', userId: 'user_test' }, + turn: { + type: 'command', + messageId: 'msg_018f1e2d3c4bCommandWaitAAA', + command: 'init', + arguments: '--strict', + snapshotInitialization: 'wait', + }, + agent: { mode: 'code', model: 'test-model', variant: 'thinking' }, + workspace: { sandboxId: 'usr-abcdef', metadata: createMetadata() }, + wrapper: { + fence: { + wrapperRunId: 'wr_command', + wrapperGeneration: 2, + wrapperConnectionId: 'conn_command', + }, + }, + } satisfies FencedWrapperDispatchRequest, + }); + + expect(result.type).toBe('command'); + if (result.type !== 'command') throw new Error('Expected command delivery request'); + expect(result.commandRequest).toMatchObject({ + command: 'init', + args: '--strict', + messageId: 'msg_018f1e2d3c4bCommandWaitAAA', + snapshotInitialization: 'wait', + agent: { mode: 'code', model: { modelID: 'test-model' }, variant: 'thinking' }, + }); + }); + it('passes persisted devcontainer intent to the active wrapper readiness request', async () => { const service = new SessionService(); const env = createEnv(); @@ -1299,6 +1338,32 @@ describe('SessionService.buildWrapperSessionReadyAndPromptRequests', () => { expect(result.promptRequest.message.attachments).toEqual(signedAttachments); }); + it('classifies no_installation_found as a permanent execution failure', async () => { + tokenMocks.resolveCloudAgentGitHubAuthForRepo.mockResolvedValueOnce({ + success: false, + error: { + reason: 'no_installation_found', + message: 'GitHub managed auth lookup failed (no_installation_found)', + }, + }); + + await expect( + buildPromptWrapperRequests( + createMetadata({ + githubRepo: 'acme/repo', + gitUrl: undefined, + gitToken: undefined, + platform: 'github', + }) + ) + ).rejects.toMatchObject({ + code: 'INVALID_REQUEST', + retryable: false, + message: + 'GitHub token or active app installation required for this repository (no_installation_found)', + }); + }); + it('uses selected user GitHub auth for the remote, author, and managed GH_TOKEN', async () => { tokenMocks.resolveCloudAgentGitHubAuthForRepo.mockResolvedValueOnce({ success: true, @@ -1620,6 +1685,26 @@ describe('SessionService.buildWrapperSessionReadyAndPromptRequests', () => { }); describe('SessionService session-ingest compatibility', () => { + it('creates a visible session with its repository identity', async () => { + const env = createEnv(); + const service = new SessionService(); + + await service.createCliSessionViaSessionIngest( + 'ses_12345678901234567890123456', + 'agent_12345678-1234-1234-1234-123456789abc', + 'user_test', + env, + undefined, + 'cloud-agent', + undefined, + 'https://GitHub.com/ACME/Widgets.git' + ); + + expect(env.SESSION_INGEST.createSessionForCloudAgent).toHaveBeenCalledWith( + expect.objectContaining({ gitUrl: 'https://GitHub.com/ACME/Widgets.git' }) + ); + }); + it('creates a visible session without projecting reporting milestones', async () => { const env = createEnv(); const service = new SessionService(); diff --git a/services/cloud-agent-next/src/session-service.ts b/services/cloud-agent-next/src/session-service.ts index d40e9e8644..7efc6634c9 100644 --- a/services/cloud-agent-next/src/session-service.ts +++ b/services/cloud-agent-next/src/session-service.ts @@ -1630,6 +1630,9 @@ export class SessionService { command: turn.command, ...(turn.arguments.length > 0 ? { args: turn.arguments } : {}), messageId: turn.messageId, + ...(turn.snapshotInitialization !== undefined + ? { snapshotInitialization: turn.snapshotInitialization } + : {}), agent: { mode: promptAgent, model: { modelID: agent.model }, @@ -2307,7 +2310,8 @@ export class SessionService { env: PersistenceEnv, organizationId: string | undefined, createdOnPlatform: string, - title?: string + title?: string, + gitUrl?: string ): Promise { try { await env.SESSION_INGEST.createSessionForCloudAgent({ @@ -2317,6 +2321,7 @@ export class SessionService { organizationId, createdOnPlatform, title, + gitUrl, }); } catch (error) { logger diff --git a/services/cloud-agent-next/src/session/pending-messages.test.ts b/services/cloud-agent-next/src/session/pending-messages.test.ts index 3ba2ffe34b..0cb7e28591 100644 --- a/services/cloud-agent-next/src/session/pending-messages.test.ts +++ b/services/cloud-agent-next/src/session/pending-messages.test.ts @@ -121,6 +121,25 @@ describe('createPendingSessionMessageFromIntent', () => { expect(stored).not.toHaveProperty('callbackUrl'); }); + it('round-trips command snapshot policy in current V2 storage', async () => { + const storage = createMemoryStorage(); + const intent: SessionMessageIntent = { + turn: { + type: 'command', + messageId: BASE_MSG_ID, + command: 'init', + arguments: '', + snapshotInitialization: 'wait', + }, + agent: { mode: 'code', model: 'claude' }, + }; + + await storePendingSessionMessage(storage, createPendingSessionMessageFromIntent(intent, 42)); + + const [listed] = await listPendingSessionMessages(storage); + expect(listed?.intent).toEqual(intent); + }); + it('creates a canonical document message from a session message intent', () => { const intent: SessionMessageIntent = { turn: { diff --git a/services/cloud-agent-next/src/session/pending-messages.ts b/services/cloud-agent-next/src/session/pending-messages.ts index 6114772836..c7c8702336 100644 --- a/services/cloud-agent-next/src/session/pending-messages.ts +++ b/services/cloud-agent-next/src/session/pending-messages.ts @@ -1,9 +1,5 @@ import * as z from 'zod'; -import type { - ExecutionMode, - RetryableResultCode, - SessionMessageIntent, -} from '../execution/types.js'; +import type { ExecutionMode, SessionMessageIntent } from '../execution/types.js'; import { renderExecutionTurnContent } from '../execution/types.js'; import { logger } from '../logger.js'; import { AttachmentsSchema, CallbackTargetSchema } from '../persistence/schemas.js'; @@ -57,6 +53,7 @@ const SessionMessageIntentSchema = z.object({ messageId: z.string().regex(MESSAGE_ID_PATTERN, MESSAGE_ID_FORMAT_DESCRIPTION), command: z.string().min(1), arguments: z.string(), + snapshotInitialization: z.literal('wait').optional(), }) .strict(), ]), @@ -78,6 +75,7 @@ const PendingFlushFailureCodeSchema = z.enum([ 'NOT_FOUND', 'BAD_REQUEST', 'INTERNAL', + 'PERMANENT_EXECUTION_FAILURE', 'PENDING_QUEUE_FULL', 'MODEL_MISSING', 'UNKNOWN', @@ -490,14 +488,7 @@ export async function recordPendingFlushFailure( now: number, options: { policy: PendingFlushPolicy; - code?: - | RetryableResultCode - | 'NOT_FOUND' - | 'BAD_REQUEST' - | 'INTERNAL' - | 'PENDING_QUEUE_FULL' - | 'MODEL_MISSING' - | 'UNKNOWN'; + code?: PendingFlushFailureCode; subtype?: WorkspaceFailureSubtype; safeFailureMessage?: string; retryable?: boolean; @@ -566,17 +557,7 @@ export async function recordPendingFlushFailure( } return { message: updated, attempts, exhausted, nextFlushAttemptAt }; } -function isRetryableFlushCode( - code: - | RetryableResultCode - | 'NOT_FOUND' - | 'BAD_REQUEST' - | 'INTERNAL' - | 'PENDING_QUEUE_FULL' - | 'MODEL_MISSING' - | 'UNKNOWN' - | undefined -): boolean { +function isRetryableFlushCode(code: PendingFlushFailureCode | undefined): boolean { return ( code === undefined || code === 'UNKNOWN' || diff --git a/services/cloud-agent-next/src/session/session-message-queue.test.ts b/services/cloud-agent-next/src/session/session-message-queue.test.ts index 55b746e71b..93ee4c82fe 100644 --- a/services/cloud-agent-next/src/session/session-message-queue.test.ts +++ b/services/cloud-agent-next/src/session/session-message-queue.test.ts @@ -1077,6 +1077,33 @@ describe('SessionMessageQueue', () => { expect(harness.events).toHaveLength(0); }); + it('persists command snapshot policy as immutable durable intent', async () => { + const harness = createQueueHarness(); + const request = { + userId: 'user_test' as UserId, + turn: { + type: 'command' as const, + id: FIRST_MESSAGE_ID, + command: 'init', + arguments: '', + snapshotInitialization: 'wait' as const, + }, + }; + + const admitted = await harness.queue.admitSubmittedMessage(request); + const [pending] = await listPendingSessionMessages(harness.storage); + const replay = await harness.queue.admitSubmittedMessage(request); + const changedReplay = await harness.queue.admitSubmittedMessage({ + ...request, + turn: { ...request.turn, snapshotInitialization: undefined }, + }); + + expect(admitted).toMatchObject({ success: true, messageId: FIRST_MESSAGE_ID }); + expect(pending?.intent?.turn).toMatchObject({ snapshotInitialization: 'wait' }); + expect(replay).toMatchObject({ success: true, messageId: FIRST_MESSAGE_ID }); + expect(changedReplay).toMatchObject({ success: false, code: 'BAD_REQUEST' }); + }); + it('admits prompt documents as canonical attachments in durable pending state', async () => { const harness = createQueueHarness(); const attachments = { @@ -1458,6 +1485,84 @@ describe('SessionMessageQueue', () => { }); }); + it.each([ + [ + 'invalid request', + ExecutionError.invalidRequest( + 'GitHub token or active app installation required for this repository (no_installation_found)' + ), + 'invalid_delivery_request', + ], + ['missing session', ExecutionError.sessionNotFound('agent_test'), 'session_metadata_missing'], + [ + 'wrapper job conflict', + ExecutionError.wrapperJobConflict('wrapper is busy'), + 'delivery_failure_unknown', + ], + ] as const)( + 'terminalizes thrown permanent %s without retrying', + async (_name, error, failureCode) => { + const harness = createQueueHarness({ + deliver: async () => Promise.reject(error), + }); + await harness.queue.admitSubmittedMessage({ + userId: 'user_test' as UserId, + turn: { type: 'prompt', id: FIRST_MESSAGE_ID, prompt: 'do not retry permanent failure' }, + }); + + const drain = await harness.queue.drainNextPendingMessage(); + + expect(harness.deliver).toHaveBeenCalledOnce(); + expect(drain).toEqual({ retryAt: undefined, remainingPendingCount: 0 }); + expect(await listPendingSessionMessages(harness.storage)).toHaveLength(0); + expect(harness.terminalizations).toHaveLength(1); + expect(harness.terminalizations[0]?.params).toMatchObject({ + kind: 'failed', + error: error.message, + completionSource: 'delivery_failure', + failureStage: 'pre_dispatch', + failureCode, + attempts: 1, + }); + } + ); + + it('replaces an earlier retryable cause with a later permanent failure', async () => { + const permanentError = ExecutionError.wrapperJobConflict('wrapper is busy'); + const deliver = vi + .fn<(_plan: MessageDeliveryRequest) => Promise>() + .mockRejectedValueOnce( + ExecutionError.workspaceSetupFailed('workspace temporarily unavailable') + ) + .mockRejectedValueOnce(permanentError); + const harness = createQueueHarness({ deliver }); + await harness.queue.admitSubmittedMessage({ + userId: 'user_test' as UserId, + turn: { type: 'prompt', id: FIRST_MESSAGE_ID, prompt: 'stop after permanent failure' }, + }); + + await harness.queue.drainNextPendingMessage(); + const [pending] = await listPendingSessionMessages(harness.storage); + expect(pending?.lastFlushFailureCode).toBe('WORKSPACE_SETUP_FAILED'); + if (pending?.nextFlushAttemptAt === undefined) { + throw new Error('Expected workspace setup failure to be retried'); + } + + vi.spyOn(Date, 'now').mockReturnValueOnce(pending.nextFlushAttemptAt); + const drain = await harness.queue.drainNextPendingMessage(); + vi.restoreAllMocks(); + + expect(harness.deliver).toHaveBeenCalledTimes(2); + expect(drain).toEqual({ retryAt: undefined, remainingPendingCount: 0 }); + expect(harness.terminalizations.at(-1)?.params).toMatchObject({ + kind: 'failed', + error: permanentError.message, + failureStage: 'pre_dispatch', + failureCode: 'delivery_failure_unknown', + attempts: 2, + }); + }); + it('preserves a thrown workspace setup failure through retry exhaustion', async () => { const error = 'Git clone failed: No space left on device'; const harness = createQueueHarness({ diff --git a/services/cloud-agent-next/src/session/session-message-queue.ts b/services/cloud-agent-next/src/session/session-message-queue.ts index 6efab7d068..ce0dfd1fb3 100644 --- a/services/cloud-agent-next/src/session/session-message-queue.ts +++ b/services/cloud-agent-next/src/session/session-message-queue.ts @@ -4,7 +4,6 @@ import type { AdmitAcceptedSessionMessageRequest, MessageDeliveryRequest, MessageDeliveryResult, - RetryableResultCode, SessionMessageAdmissionResult, SessionMessageIntent, SubmittedSessionMessageRequest, @@ -201,13 +200,14 @@ function classifyDeliveryFailure(code: PendingFlushFailureCode | undefined): { return { failureStage: 'pre_dispatch', failureCode: 'model_missing' }; case 'WRAPPER_FINALIZING': case 'INTERNAL': + case 'PERMANENT_EXECUTION_FAILURE': case 'UNKNOWN': case undefined: return { failureStage: 'pre_dispatch', failureCode: 'delivery_failure_unknown' }; } } -function knownPreDispatchExecutionFailureCode(error: unknown): RetryableResultCode | undefined { +function knownPreDispatchExecutionFailureCode(error: unknown): PendingFlushFailureCode | undefined { if (!isExecutionError(error)) return undefined; switch (error.code) { case 'SANDBOX_CONNECT_FAILED': @@ -217,8 +217,12 @@ function knownPreDispatchExecutionFailureCode(error: unknown): RetryableResultCo case 'WRAPPER_START_FAILED': // Orchestration also uses this code when prompt dispatch fails after wrapper readiness. return undefined; + case 'INVALID_REQUEST': + return error.retryable ? undefined : 'BAD_REQUEST'; + case 'SESSION_NOT_FOUND': + return error.retryable ? undefined : 'NOT_FOUND'; default: - return undefined; + return error.retryable ? undefined : 'PERMANENT_EXECUTION_FAILURE'; } } @@ -860,6 +864,7 @@ export function createSessionMessageQueue( messageId, command: explicitTurn.command, arguments: explicitTurn.arguments, + snapshotInitialization: explicitTurn.snapshotInitialization, }, agent: { mode: modeInput, diff --git a/services/cloud-agent-next/src/session/session-message-state.test.ts b/services/cloud-agent-next/src/session/session-message-state.test.ts index 0eab8b1ab6..5b6c43d54f 100644 --- a/services/cloud-agent-next/src/session/session-message-state.test.ts +++ b/services/cloud-agent-next/src/session/session-message-state.test.ts @@ -142,6 +142,25 @@ describe('getSessionMessageState / putSessionMessageState', () => { expect(loaded).not.toHaveProperty('finalization'); }); + it('round-trips command snapshot policy in an admission snapshot', async () => { + const storage = createFakeStorage(); + const state = createQueuedSessionMessageState({ + turn: { + type: 'command', + messageId: VALID_MESSAGE_ID, + command: 'init', + arguments: '', + snapshotInitialization: 'wait', + }, + agent: { mode: 'code', model: 'default-model' }, + }); + await putSessionMessageState(storage, state); + + const loaded = await getSessionMessageState(storage, VALID_MESSAGE_ID); + + expect(loaded?.admissionSnapshot?.turn).toMatchObject({ snapshotInitialization: 'wait' }); + }); + it('round-trips canonical attachments in an admission snapshot', async () => { const storage = createFakeStorage(); const attachments = { diff --git a/services/cloud-agent-next/src/session/session-message-state.ts b/services/cloud-agent-next/src/session/session-message-state.ts index 2f626491f0..cab1332c42 100644 --- a/services/cloud-agent-next/src/session/session-message-state.ts +++ b/services/cloud-agent-next/src/session/session-message-state.ts @@ -145,6 +145,7 @@ export const SessionMessageStateSchema = z messageId: z.string(), command: z.string(), arguments: z.string(), + snapshotInitialization: z.literal('wait').optional(), }), ]), agent: z.object({ mode: z.string(), model: z.string(), variant: z.string().optional() }), @@ -173,6 +174,7 @@ export const SessionMessageStateSchema = z messageId: z.string(), command: z.string(), arguments: z.string(), + snapshotInitialization: z.literal('wait').optional(), }), ]) .optional(), @@ -341,6 +343,7 @@ function normalizeParsedSessionMessageState( messageId: z.string(), command: z.string(), arguments: z.string(), + snapshotInitialization: z.literal('wait').optional(), }), ]) .safeParse(state.turn); diff --git a/services/cloud-agent-next/src/session/session-registration.ts b/services/cloud-agent-next/src/session/session-registration.ts index 3d474f6b6c..c847295ede 100644 --- a/services/cloud-agent-next/src/session/session-registration.ts +++ b/services/cloud-agent-next/src/session/session-registration.ts @@ -15,6 +15,7 @@ * metadata; generic git repositories may still carry an explicit token. */ import { TRPCError } from '@trpc/server'; +import { sanitizeGitUrl } from '@kilocode/worker-utils'; import type { Env } from '../types.js'; import type { CloudAgentSession } from '../persistence/CloudAgentSession.js'; @@ -60,6 +61,11 @@ export type StartedSessionResult = Omit; }; +function repositoryGitUrl(repository: SessionCreateRequest['repository']): string { + if (repository.type === 'github') return `https://github.com/${repository.repo}`; + return sanitizeGitUrl(repository.url); +} + function acceptInitialTurn(initialTurn: ExecutionTurnSubmission): AcceptedExecutionTurn { const messageId = initialTurn.id ?? createMessageId(); return initialTurn.type === 'prompt' @@ -185,7 +191,8 @@ async function allocateNewSession( ctx.env, input.options?.kilocodeOrganizationId, createdOnPlatform, - defaultTitle + defaultTitle, + repositoryGitUrl(input.repository) ); } catch (error) { await recordCloudAgentSessionFailure( diff --git a/services/cloud-agent-next/src/shared/protocol.ts b/services/cloud-agent-next/src/shared/protocol.ts index cae3a92484..0f34c6060b 100644 --- a/services/cloud-agent-next/src/shared/protocol.ts +++ b/services/cloud-agent-next/src/shared/protocol.ts @@ -139,7 +139,20 @@ export type CloudStatusData = { export type SessionStatus = | { type: 'busy' } | { type: 'idle' } - | { type: 'retry'; attempt: number; message: string; next: number } + | { + type: 'retry'; + attempt: number; + message: string; + action?: { + reason: string; + provider: string; + title: string; + message: string; + label: string; + link?: string; + }; + next: number; + } | { type: 'offline'; requestID: string; message: string }; /** Data included in 'connected' events. */ diff --git a/services/cloud-agent-next/src/shared/wrapper-bootstrap.ts b/services/cloud-agent-next/src/shared/wrapper-bootstrap.ts index 0ca7176a71..d552c7983f 100644 --- a/services/cloud-agent-next/src/shared/wrapper-bootstrap.ts +++ b/services/cloud-agent-next/src/shared/wrapper-bootstrap.ts @@ -110,6 +110,7 @@ export type WrapperCommandRequest = { command: string; args?: string; messageId: string; + snapshotInitialization?: 'wait'; agent?: WrapperPromptAgent; autoCommit?: boolean; condenseOnComplete?: boolean; diff --git a/services/cloud-agent-next/test/e2e/sdk-basic-chat.ts b/services/cloud-agent-next/test/e2e/sdk-basic-chat.ts index 3573b05ea8..6c99ff8ae3 100644 --- a/services/cloud-agent-next/test/e2e/sdk-basic-chat.ts +++ b/services/cloud-agent-next/test/e2e/sdk-basic-chat.ts @@ -99,6 +99,15 @@ function findAssistantWithText( ); } +function findAssistantByParentID( + messages: MessageEntry[], + parentID: string +): MessageEntry | undefined { + return messages.find( + entry => entry.info.role === 'assistant' && entry.info.parentID === parentID + ); +} + function assertProjectedAssistant(entry: MessageEntry, directory: string, action: string): void { assert.equal(entry.info.role, 'assistant', `${action} did not return an assistant message`); if (entry.info.role !== 'assistant') return; @@ -318,7 +327,38 @@ async function main(): Promise { assert.notEqual(warmBootstrapAssistant, undefined); if (warmBootstrapAssistant) { assertProjectedAssistant(warmBootstrapAssistant, rootDirectory, 'warm session.messages()'); + const warmBootstrapMessage = requireData( + await client.session.message({ + sessionID: rootSessionID, + messageID: warmBootstrapAssistant.info.id, + directory: rootDirectory, + }), + 'warm session.message()' + ); + assert.equal(warmBootstrapMessage.info.id, warmBootstrapAssistant.info.id); + assertProjectedAssistant(warmBootstrapMessage, rootDirectory, 'warm session.message()'); } + assert.deepEqual( + requireData(await client.question.list({ directory: rootDirectory }), 'warm question.list()'), + [], + 'warm root unexpectedly had pending questions' + ); + assert.deepEqual( + requireData( + await client.permission.list({ directory: rootDirectory }), + 'warm permission.list()' + ), + [], + 'warm root unexpectedly had pending permissions' + ); + assert.deepEqual( + requireData( + await client.suggestion.list({ directory: rootDirectory }), + 'warm suggestion.list()' + ), + [], + 'warm root unexpectedly had pending suggestions' + ); await stopOwnedSandboxFamilies(root.cloudAgentSessionId, ownedSandboxFamilies); @@ -339,7 +379,36 @@ async function main(): Promise { ); if (coldBootstrapAssistant) { assertProjectedAssistant(coldBootstrapAssistant, rootDirectory, 'cold session.messages()'); + const coldBootstrapMessage = requireData( + await client.session.message({ + sessionID: rootSessionID, + messageID: coldBootstrapAssistant.info.id, + directory: rootDirectory, + }), + 'cold session.message()' + ); + assert.deepEqual( + coldBootstrapMessage, + coldBootstrapAssistant, + 'cold session.message() returned a different persisted message' + ); + assertProjectedAssistant(coldBootstrapMessage, rootDirectory, 'cold session.message()'); } + const coldStatuses = requireData(await client.session.status(), 'cold session.status()'); + assert.equal( + coldStatuses[rootSessionID], + undefined, + 'cold root unexpectedly had a non-idle status' + ); + const selectedColdStatuses = requireData( + await client.session.status({ directory: rootDirectory }), + 'selected cold session.status()' + ); + assert.equal( + selectedColdStatuses[rootSessionID], + undefined, + 'selected cold root unexpectedly had a non-idle status' + ); const scopedEvents = await client.event.subscribe( { directory: rootDirectory }, @@ -408,6 +477,70 @@ async function main(): Promise { assertProjectedAssistant(asyncAssistant, rootDirectory, 'promptAsync result'); } + const commands = requireData( + await client.command.list({ directory: rootDirectory }), + 'warm command.list()' + ); + const initCommand = commands.find(command => command.name === 'init'); + assert.notEqual(initCommand, undefined, 'command.list() omitted the built-in init command'); + if (initCommand) { + assert.equal(initCommand.template, '', 'command.list() exposed an executable template'); + assert.equal(Array.isArray(initCommand.hints), true, 'command.list() omitted command hints'); + } + + const commandMessageID = createMessageId(); + const scopedCommandWakeEvent = waitForEvent( + scopedEvents.stream, + 'scoped command wake stream', + event => isCorrelatedAssistantWakeEvent(event, rootSessionID, commandMessageID) + ); + const globalCommandWakeEvent = waitForEvent( + globalEvents.stream, + 'global command wake stream', + event => isProjectedGlobalWakeEvent(event, rootSessionID, commandMessageID) + ); + const commandAcknowledgement = requireData( + await client.session.command({ + sessionID: rootSessionID, + directory: rootDirectory, + messageID: commandMessageID, + command: 'init', + arguments: '', + }), + 'session.command()' + ); + assert.equal(commandAcknowledgement.info.role, 'assistant'); + assert.equal(commandAcknowledgement.info.sessionID, rootSessionID); + assert.equal(commandAcknowledgement.info.parentID, commandMessageID); + assert.notEqual(commandAcknowledgement.info.id, commandMessageID); + assert.equal(commandAcknowledgement.info.time.completed, undefined); + assert.equal(commandAcknowledgement.info.finish, undefined); + assert.deepEqual(commandAcknowledgement.parts, []); + assertProjectedAssistant( + commandAcknowledgement, + rootDirectory, + 'session.command() acknowledgement' + ); + await Promise.all([scopedCommandWakeEvent, globalCommandWakeEvent]); + + const commandMessages = await pollFor(async () => { + const messages = requireData( + await client.session.messages({ sessionID: rootSessionID, limit: 50 }), + 'command result messages()' + ); + return findAssistantByParentID(messages, commandMessageID) ? messages : undefined; + }, 'command assistant result'); + const commandAssistant = findAssistantByParentID(commandMessages, commandMessageID); + assert.notEqual(commandAssistant, undefined, 'command transcript lost correlation'); + if (commandAssistant) { + assertProjectedAssistant(commandAssistant, rootDirectory, 'session.command() result'); + } + assert.equal( + commandMessages.some(entry => entry.info.id === commandAcknowledgement.info.id), + false, + 'synthetic command acknowledgement was persisted' + ); + const unsupportedSyncMessageID = createMessageId(); const unsupportedSyncPrompt = await client.session.prompt({ sessionID: rootSessionID, diff --git a/services/cloud-agent-next/test/integration/kilo-facade-runtime.test.ts b/services/cloud-agent-next/test/integration/kilo-facade-runtime.test.ts index ef3710faa5..3a09beb6ba 100644 --- a/services/cloud-agent-next/test/integration/kilo-facade-runtime.test.ts +++ b/services/cloud-agent-next/test/integration/kilo-facade-runtime.test.ts @@ -13,6 +13,18 @@ type WrapperIdentity = { wrapperConnectionId: string; }; +type ProducerSource = { + userId: string; + cloudAgentSessionId: string; + kiloSessionId: string; +}; + +const defaultProducerSource = { + userId, + cloudAgentSessionId, + kiloSessionId, +} satisfies ProducerSource; + type SseDataReader = { next(): Promise; cancel(): Promise; @@ -52,17 +64,20 @@ function createSseDataReader(response: Response): SseDataReader { }; } -async function configureCurrentProducer(identity: WrapperIdentity): Promise { +async function configureCurrentProducer( + identity: WrapperIdentity, + source: ProducerSource = defaultProducerSource +): Promise { const session = env.CLOUD_AGENT_SESSION.get( - env.CLOUD_AGENT_SESSION.idFromName(`${userId}:${cloudAgentSessionId}`) + env.CLOUD_AGENT_SESSION.idFromName(`${source.userId}:${source.cloudAgentSessionId}`) ); await runInDurableObject(session, async instance => { const metadata = await instance.getMetadata(); if (!metadata) { await registerReadySession(instance, { - sessionId: cloudAgentSessionId, - userId, - kiloSessionId, + sessionId: source.cloudAgentSessionId, + userId: source.userId, + kiloSessionId: source.kiloSessionId, prompt: 'runtime facade fixture', mode: 'code', model: 'test-model', @@ -73,18 +88,24 @@ async function configureCurrentProducer(identity: WrapperIdentity): Promise { +async function connectProducer( + identity: WrapperIdentity, + source: ProducerSource = defaultProducerSource +): Promise { const query = new URLSearchParams({ - userId, - cloudAgentSessionId, - kiloSessionId, + userId: source.userId, + cloudAgentSessionId: source.cloudAgentSessionId, + kiloSessionId: source.kiloSessionId, wrapperRunId: identity.wrapperRunId, wrapperGeneration: String(identity.wrapperGeneration), wrapperConnectionId: identity.wrapperConnectionId, }); - const response = await SELF.fetch(`http://worker.test/kilo-global-feed-test?${query}`, { - headers: { Upgrade: 'websocket' }, - }); + const response = await SELF.fetch( + `http://worker.test/kilo-global-feed-test?${query.toString()}`, + { + headers: { Upgrade: 'websocket' }, + } + ); if (response.status !== 101 || !response.webSocket) { throw new Error(`Unexpected producer upgrade response: ${response.status}`); } @@ -315,6 +336,46 @@ describe('UserKiloFacade in the Workers runtime', () => { } }); + it('serves the sparse root status map from producer WebSocket attachments', async () => { + const source = { + userId: 'user_facade_status', + cloudAgentSessionId: 'agent_facade_status', + kiloSessionId: 'ses_33333333333333333333333333', + } satisfies ProducerSource; + const identity = { + wrapperRunId: 'wr_facade_status_1', + wrapperGeneration: 1, + wrapperConnectionId: 'conn_facade_status_1', + } satisfies WrapperIdentity; + await configureCurrentProducer(identity, source); + const producer = await connectProducer(identity, source); + + try { + producer.send( + JSON.stringify({ + directory: '/workspace/private/session', + payload: { + type: 'session.status', + properties: { sessionID: source.kiloSessionId, status: { type: 'busy' } }, + }, + }) + ); + + let status: unknown; + for (let attempt = 0; attempt < 20; attempt += 1) { + const response = await SELF.fetch( + `http://worker.test/kilo-test/kilo/session/status?userId=${source.userId}` + ); + status = await response.json(); + if (JSON.stringify(status).includes('busy')) break; + await new Promise(resolve => setTimeout(resolve, 10)); + } + expect(status).toEqual({ [source.kiloSessionId]: { type: 'busy' } }); + } finally { + producer.close(1000, 'test complete'); + } + }); + it('replaces an older producer and rejects its stale reconnect', async () => { const firstIdentity = { wrapperRunId: 'wr_facade_replaced_1', @@ -346,7 +407,7 @@ describe('UserKiloFacade in the Workers runtime', () => { wrapperConnectionId: firstIdentity.wrapperConnectionId, }); const staleResponse = await SELF.fetch( - `http://worker.test/kilo-global-feed-test?${staleQuery}`, + `http://worker.test/kilo-global-feed-test?${staleQuery.toString()}`, { headers: { Upgrade: 'websocket' } } ); expect(staleResponse.status).toBe(409); diff --git a/services/cloud-agent-next/test/integration/session/derive-queued-messages.test.ts b/services/cloud-agent-next/test/integration/session/derive-queued-messages.test.ts index 2aea6c85f4..bfe195d4bd 100644 --- a/services/cloud-agent-next/test/integration/session/derive-queued-messages.test.ts +++ b/services/cloud-agent-next/test/integration/session/derive-queued-messages.test.ts @@ -65,6 +65,41 @@ describe('deriveQueuedMessages (/stream connect catch-up)', () => { expect(snapshots).toEqual([]); }); + it('retains the initial message snapshot after wrapper acceptance', async () => { + const sessionId = 'agent_initial_snapshot'; + const stub = env.CLOUD_AGENT_SESSION.get( + env.CLOUD_AGENT_SESSION.idFromName(`${userId}:${sessionId}`) + ); + + const snapshot = await runInDurableObject(stub, async instance => { + await instance.registerSession( + groupedRegisterSessionInput({ + sessionId, + userId, + prompt: 'Build me a CLI', + mode: 'code', + model: 'claude', + initialMessageId: MSG_INITIAL, + }) + ); + await putSessionMessageState(instance.ctx.storage, { + messageId: MSG_INITIAL, + status: 'accepted', + prompt: 'Build me a CLI', + createdAt: 1700000000000, + queuedAt: 1700000000100, + acceptedAt: 1700000000200, + }); + return instance.getInitialMessageSnapshot(); + }); + + expect(snapshot).toEqual({ + messageId: MSG_INITIAL, + content: 'Build me a CLI', + timestamp: 1700000000100, + }); + }); + it('returns every entry in the pending_message:* queue', async () => { const sessionId = 'agent_queued_derive_2'; const stub = env.CLOUD_AGENT_SESSION.get( diff --git a/services/cloud-agent-next/test/test-worker.ts b/services/cloud-agent-next/test/test-worker.ts index fc07f4b214..32e38686bf 100644 --- a/services/cloud-agent-next/test/test-worker.ts +++ b/services/cloud-agent-next/test/test-worker.ts @@ -131,7 +131,7 @@ export default { return new Response('Missing kiloSessionId parameter', { status: 400 }); } const facade = env.USER_KILO_FACADE.get(env.USER_KILO_FACADE.idFromName(userId)); - return facade.openPublicSessionEventStream(kiloSessionId); + return facade.openPublicSessionEventStream(userId, kiloSessionId); } if (url.pathname === '/kilo-global-feed-test') { diff --git a/services/cloud-agent-next/test/unit/wrapper/server.test.ts b/services/cloud-agent-next/test/unit/wrapper/server.test.ts index a81a1f0991..fe2aa1c707 100644 --- a/services/cloud-agent-next/test/unit/wrapper/server.test.ts +++ b/services/cloud-agent-next/test/unit/wrapper/server.test.ts @@ -40,6 +40,7 @@ function createMockKiloClient(): WrapperKiloClient { abortSession: vi.fn().mockResolvedValue(true), summarizeSession: vi.fn().mockResolvedValue(true), sendCommand: vi.fn().mockResolvedValue(undefined), + listCommands: vi.fn().mockResolvedValue([]), answerPermission: vi.fn().mockResolvedValue(true), answerQuestion: vi.fn().mockResolvedValue(true), rejectQuestion: vi.fn().mockResolvedValue(true), @@ -47,6 +48,11 @@ function createMockKiloClient(): WrapperKiloClient { getSessionStatuses: vi.fn().mockResolvedValue({}), getQuestions: vi.fn().mockResolvedValue([]), getPermissions: vi.fn().mockResolvedValue([]), + getNetworkWaits: vi.fn().mockResolvedValue([]), + resumeNetworkWait: vi.fn().mockResolvedValue(true), + createPty: vi.fn(), + resizePty: vi.fn(), + deletePty: vi.fn().mockResolvedValue(true), subscribeEvents: vi.fn().mockResolvedValue({ stream: undefined }), serverUrl: 'http://127.0.0.1:0', }; @@ -1019,6 +1025,53 @@ describe('createCommandHandler', () => { } ); + it('forwards snapshot initialization policy for regular commands', async () => { + const state = new WrapperState(); + const deps = createMockDeps(state); + const handler = createCommandHandler(defaultServerConfig, deps); + + const response = await handler( + jsonRequest({ + command: 'review/security', + args: '--strict', + messageId: 'msg_command_wait', + snapshotInitialization: 'wait', + session: completeBinding, + }) + ); + + expect(response.status).toBe(200); + expect(deps.kiloClient.sendCommand).toHaveBeenCalledWith({ + sessionId: 'kilo_sess_1', + command: 'review/security', + args: '--strict', + messageId: 'msg_command_wait', + snapshotInitialization: 'wait', + }); + }); + + it('rejects unsupported snapshot initialization policy at the wrapper boundary', async () => { + const state = new WrapperState(); + const deps = createMockDeps(state); + const handler = createCommandHandler(defaultServerConfig, deps); + + const response = await handler( + jsonRequest({ + command: 'init', + snapshotInitialization: 'continue', + session: completeBinding, + }) + ); + + expect(response.status).toBe(400); + await expect(readJson(response)).resolves.toEqual({ + error: 'INVALID_REQUEST', + message: 'snapshotInitialization must be wait when provided', + }); + expect(state.hasSession).toBe(false); + expect(deps.kiloClient.sendCommand).not.toHaveBeenCalled(); + }); + it('routes compact through session summarize with the selected model', async () => { const state = new WrapperState(); const sendToIngest = vi.fn(); diff --git a/services/cloud-agent-next/wrapper/src/global-feed.test.ts b/services/cloud-agent-next/wrapper/src/global-feed.test.ts index 9d84adc752..6767f0f848 100644 --- a/services/cloud-agent-next/wrapper/src/global-feed.test.ts +++ b/services/cloud-agent-next/wrapper/src/global-feed.test.ts @@ -79,8 +79,21 @@ function bindGlobalFeedSession(state: WrapperState): void { }); } -function makeKiloClient(serverUrl = 'http://127.0.0.1:4321'): WrapperKiloClient { - return { serverUrl } as WrapperKiloClient; +function makeKiloClient( + serverUrl = 'http://127.0.0.1:4321', + statuses: Record = {} +): WrapperKiloClient { + return { serverUrl, getSessionStatuses: async () => statuses } as WrapperKiloClient; +} + +function rootStatusFrame(status: { type: string; [key: string]: unknown }): string { + return JSON.stringify({ + directory: process.cwd(), + payload: { + type: 'session.status', + properties: { sessionID: 'kilo_root_1', status }, + }, + }); } afterEach(() => { @@ -184,6 +197,7 @@ describe('openKiloGlobalFeed', () => { Authorization: 'Bearer worker-token', }); expect(ws.sent).toEqual([ + rootStatusFrame({ type: 'idle' }), JSON.stringify({ directory: '/workspace/root', payload: { type: 'message.updated', properties: { id: 'msg_1' } }, @@ -195,6 +209,72 @@ describe('openKiloGlobalFeed', () => { ]); }); + it('bootstraps the current non-idle root status when the worker feed connects', async () => { + const state = new WrapperState(); + bindGlobalFeedSession(state); + const fetchImpl = asFetch(async () => new Response(streamFromChunks([]), { status: 200 })); + + const connection = openKiloGlobalFeed({ + state, + kiloClient: makeKiloClient('http://127.0.0.1:4321', { + kilo_root_1: { type: 'busy' }, + kilo_child_1: { type: 'retry', attempt: 1, message: 'retrying', next: 123 }, + }), + fetchImpl, + WebSocketImpl: FakeWebSocket as unknown as GlobalFeedWebSocketImpl, + retryDelayMs: 60_000, + }); + + await new Promise(resolve => setTimeout(resolve, 0)); + connection.close(); + await connection.done; + + const sent = FakeWebSocket.instances[0].sent.map(frame => JSON.parse(frame)); + expect(sent).toEqual([ + { + directory: process.cwd(), + payload: { + type: 'session.status', + properties: { sessionID: 'kilo_root_1', status: { type: 'busy' } }, + }, + }, + ]); + }); + + it('retries the feed when the authoritative root status bootstrap fails', async () => { + const state = new WrapperState(); + bindGlobalFeedSession(state); + let statusAttempt = 0; + let secondAttemptStarted: (() => void) | undefined; + const secondAttempt = new Promise(resolve => { + secondAttemptStarted = resolve; + }); + const kiloClient = { + ...makeKiloClient(), + getSessionStatuses: async () => { + statusAttempt += 1; + if (statusAttempt === 1) throw new Error('status unavailable'); + secondAttemptStarted?.(); + return {}; + }, + } as WrapperKiloClient; + + const connection = openKiloGlobalFeed({ + state, + kiloClient, + fetchImpl: asFetch(async () => new Response(streamFromChunks([]), { status: 200 })), + WebSocketImpl: FakeWebSocket as unknown as GlobalFeedWebSocketImpl, + }); + + await secondAttempt; + await new Promise(resolve => setTimeout(resolve, 0)); + connection.close(); + await connection.done; + + expect(statusAttempt).toBeGreaterThanOrEqual(2); + expect(FakeWebSocket.instances.at(-1)?.sent).toContain(rootStatusFrame({ type: 'idle' })); + }); + it('forwards substantive events from a CRLF feed split between delimiter bytes', async () => { const state = new WrapperState(); bindGlobalFeedSession(state); @@ -225,6 +305,7 @@ describe('openKiloGlobalFeed', () => { await connection.done; expect(FakeWebSocket.instances[0].sent).toEqual([ + rootStatusFrame({ type: 'idle' }), JSON.stringify({ directory: '/workspace/root', payload: { type: 'message.updated', properties: { id: 'msg_crlf' } }, diff --git a/services/cloud-agent-next/wrapper/src/global-feed.ts b/services/cloud-agent-next/wrapper/src/global-feed.ts index d7b6e98cc9..ca29c8c3cd 100644 --- a/services/cloud-agent-next/wrapper/src/global-feed.ts +++ b/services/cloud-agent-next/wrapper/src/global-feed.ts @@ -185,6 +185,28 @@ export function openKiloGlobalFeed(options: OpenKiloGlobalFeedOptions): KiloGlob } } + function sendToWorker(ws: WebSocket, event: unknown): void { + if (ws.readyState !== OPEN_READY_STATE) return; + const serialized = JSON.stringify(event); + const pendingBytes = ws.bufferedAmount + encoder.encode(serialized).byteLength; + if (pendingBytes > MAX_GLOBAL_FEED_WEBSOCKET_BUFFERED_BYTES) { + throw new Error('Kilo global feed WebSocket buffer limit exceeded'); + } + ws.send(serialized); + } + + async function sendCurrentRootStatus(ws: WebSocket): Promise { + const statuses = await options.kiloClient.getSessionStatuses(); + const status = statuses[session.kiloSessionId] ?? { type: 'idle' as const }; + sendToWorker(ws, { + directory: process.cwd(), + payload: { + type: 'session.status', + properties: { sessionID: session.kiloSessionId, status }, + }, + }); + } + async function runAttempt(): Promise { const abortController = new AbortController(); attemptAbortController = abortController; @@ -234,6 +256,7 @@ export function openKiloGlobalFeed(options: OpenKiloGlobalFeedOptions): KiloGlob if (!response.ok || !response.body) { throw new Error(`Kilo global event stream failed: ${response.status}`); } + await sendCurrentRootStatus(ws); for await (const data of parseSseDataStream(response.body)) { if (closed || abortController.signal.aborted) break; @@ -249,14 +272,7 @@ export function openKiloGlobalFeed(options: OpenKiloGlobalFeedOptions): KiloGlob continue; } - if (ws.readyState === OPEN_READY_STATE) { - const serialized = JSON.stringify(parsed); - const pendingBytes = ws.bufferedAmount + encoder.encode(serialized).byteLength; - if (pendingBytes > MAX_GLOBAL_FEED_WEBSOCKET_BUFFERED_BYTES) { - throw new Error('Kilo global feed WebSocket buffer limit exceeded'); - } - ws.send(serialized); - } + sendToWorker(ws, parsed); } } diff --git a/services/cloud-agent-next/wrapper/src/kilo-api.ts b/services/cloud-agent-next/wrapper/src/kilo-api.ts index 0c002b1ff7..5e7d7e9ae6 100644 --- a/services/cloud-agent-next/wrapper/src/kilo-api.ts +++ b/services/cloud-agent-next/wrapper/src/kilo-api.ts @@ -15,6 +15,7 @@ import type { KiloClient as SDKClient } from '@kilocode/sdk'; import { createKiloClient as createV2Client } from '@kilocode/sdk/v2'; import { logToFile } from './utils.js'; import { toSlashCommandInfo, type SlashCommandInfo } from '../../src/shared/slash-commands.js'; +import type { SessionStatus } from '../../src/shared/protocol.js'; function isRecord(value: unknown): value is Record { return typeof value === 'object' && value !== null; @@ -46,6 +47,40 @@ function requireSdkData(result: { data?: T; error?: unknown }, operation: str return result.data; } +function isJsonNumber(value: unknown): value is number { + return typeof value === 'number' && Number.isFinite(value); +} + +function isSessionStatus(value: unknown): value is SessionStatus { + if (!isRecord(value) || typeof value.type !== 'string') return false; + if (value.type === 'idle' || value.type === 'busy') return true; + if (value.type === 'offline') { + return typeof value.requestID === 'string' && typeof value.message === 'string'; + } + if (value.type !== 'retry') return false; + if ( + !isJsonNumber(value.attempt) || + typeof value.message !== 'string' || + !isJsonNumber(value.next) + ) { + return false; + } + if (value.action === undefined) return true; + return ( + isRecord(value.action) && + typeof value.action.reason === 'string' && + typeof value.action.provider === 'string' && + typeof value.action.title === 'string' && + typeof value.action.message === 'string' && + typeof value.action.label === 'string' && + (value.action.link === undefined || typeof value.action.link === 'string') + ); +} + +function isSessionStatusMap(value: unknown): value is Record { + return isRecord(value) && Object.values(value).every(isSessionStatus); +} + // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- @@ -137,7 +172,7 @@ export type WrapperKiloClient = { ) => Promise; answerQuestion: (questionId: string, answers: string[][]) => Promise; rejectQuestion: (questionId: string) => Promise; - getSessionStatuses: () => Promise>; + getSessionStatuses: () => Promise>; getQuestions: () => Promise< Array<{ id: string; sessionID: string; tool?: { messageID: string; callID: string } }> >; @@ -327,7 +362,11 @@ export function createWrapperKiloClient( getSessionStatuses: async () => { const result = await v2Client.session.status(); - return (result.data ?? {}) as Record; + const statuses: unknown = requireSdkData(result, 'Session status'); + if (!isSessionStatusMap(statuses)) { + throw new Error('Session status returned invalid data'); + } + return statuses; }, getQuestions: async () => { diff --git a/services/cloud-agent-next/wrapper/src/server.ts b/services/cloud-agent-next/wrapper/src/server.ts index cd8fb5ef28..bf375ed86b 100644 --- a/services/cloud-agent-next/wrapper/src/server.ts +++ b/services/cloud-agent-next/wrapper/src/server.ts @@ -94,6 +94,7 @@ type CommandBody = { command: string; args?: string; messageId?: string; + snapshotInitialization?: unknown; agent?: WrapperPromptAgent; autoCommit?: boolean; condenseOnComplete?: boolean; @@ -519,6 +520,13 @@ export function createCommandHandler(config: ServerConfig, deps: ServerDependenc } catch { return errorResponse('INVALID_REQUEST', 'Invalid JSON body', 400); } + if (body.snapshotInitialization !== undefined && body.snapshotInitialization !== 'wait') { + return errorResponse( + 'INVALID_REQUEST', + 'snapshotInitialization must be wait when provided', + 400 + ); + } const bindError = await bindSessionContext(body.session ?? body.execution, config, deps); if (bindError) return bindError; @@ -599,7 +607,10 @@ export function createCommandHandler(config: ServerConfig, deps: ServerDependenc }); } } else { - const snapshotInitialization = snapshotInitializationForPlatform(session.platform); + const snapshotInitialization = + body.snapshotInitialization === 'wait' + ? 'wait' + : snapshotInitializationForPlatform(session.platform); result = await kiloClient.sendCommand({ sessionId: session.kiloSessionId, command: body.command, diff --git a/services/session-ingest/src/app.ts b/services/session-ingest/src/app.ts index ff4d6f1977..7aea4fe14e 100644 --- a/services/session-ingest/src/app.ts +++ b/services/session-ingest/src/app.ts @@ -8,11 +8,10 @@ import { cli_sessions_v2 } from '@kilocode/db/schema'; import { kiloJwtAuthMiddleware } from './middleware/kilo-jwt-auth'; import { api } from './routes/api'; import { getSessionIngestDO } from './dos/SessionIngestDO'; -import { getSessionExport } from './services/session-export'; +import { internalRoutes } from './internal-routes'; +import { internalUserEventRoutes } from './internal-user-events'; import { withDORetry } from '@kilocode/worker-utils'; -const sessionIdSchema = z.string().startsWith('ses_').length(30); - export const app = new Hono<{ Bindings: Env; Variables: { @@ -66,31 +65,5 @@ app.get('/session/:sessionId', async c => { }); }); -// Internal route for service-binding HTTP fetch (secret-protected) -app.get('/internal/session/:sessionId/export', async c => { - const secret = c.req.header('X-Internal-Secret'); - const expected = await c.env.INTERNAL_API_SECRET_PROD.get(); - - if (!secret || !expected) { - return c.json({ success: false, error: 'Unauthorized' }, 401); - } - - const encoder = new TextEncoder(); - const a = encoder.encode(secret); - const b = encoder.encode(expected); - - if (a.byteLength !== b.byteLength || !crypto.subtle.timingSafeEqual(a, b)) { - return c.json({ success: false, error: 'Unauthorized' }, 401); - } - - const kiloUserId = c.req.header('X-Kilo-User-Id'); - if (!kiloUserId) return c.json({ success: false, error: 'Missing X-Kilo-User-Id' }, 400); - - const parsed = sessionIdSchema.safeParse(c.req.param('sessionId')); - if (!parsed.success) return c.json({ success: false, error: 'Invalid sessionId' }, 400); - - const stream = await getSessionExport(c.env, parsed.data, kiloUserId); - if (stream === null) return c.json({ success: false, error: 'Session not found' }, 404); - - return c.body(stream, 200, { 'content-type': 'application/json; charset=utf-8' }); -}); +app.route('/internal', internalUserEventRoutes); +app.route('/internal', internalRoutes); diff --git a/services/session-ingest/src/dos/SessionIngestDO.ts b/services/session-ingest/src/dos/SessionIngestDO.ts index 841cbf34e2..36773801d9 100644 --- a/services/session-ingest/src/dos/SessionIngestDO.ts +++ b/services/session-ingest/src/dos/SessionIngestDO.ts @@ -25,6 +25,7 @@ import { } from './session-metrics'; import migrations from '../../drizzle/migrations'; import { + readKiloSdkMessage, readKiloSdkMessages, readKiloSdkSessionSnapshot, type KiloSdkSessionSnapshotRead, @@ -312,6 +313,10 @@ export class SessionIngestDO extends DurableObject { return readKiloSdkMessages(this.db, this.env.SESSION_INGEST_R2, params); } + async readKiloSdkMessage(messageId: string) { + return readKiloSdkMessage(this.db, this.env.SESSION_INGEST_R2, messageId); + } + async getAllStream(): Promise> { const db = this.db; const r2 = this.env.SESSION_INGEST_R2; diff --git a/services/session-ingest/src/dos/kilo-sdk-materialization.ts b/services/session-ingest/src/dos/kilo-sdk-materialization.ts index ca78164154..7175893566 100644 --- a/services/session-ingest/src/dos/kilo-sdk-materialization.ts +++ b/services/session-ingest/src/dos/kilo-sdk-materialization.ts @@ -19,6 +19,7 @@ export const MAX_KILO_SDK_HISTORY_MATERIALIZATION_BYTES = 8 * 1024 * 1024; export const MAX_KILO_SDK_SESSION_SNAPSHOT_BYTES = 8 * 1024 * 1024; export const KILO_SDK_HISTORY_CANDIDATE_OVERHEAD_BYTES = 256; const KILO_SDK_HISTORY_ENUMERATION_BATCH_SIZE = 64; +export const KILO_SDK_EXACT_MESSAGE_PART_ROW_WORK_CAP = 2 * KILO_SDK_HISTORY_ENUMERATION_BATCH_SIZE; // Bound cold positive-limit scans to two SQLite batches before failing unsafe continuation. export const KILO_SDK_HISTORY_BOUNDED_MESSAGE_SCAN_ROW_WORK_CAP = 2 * KILO_SDK_HISTORY_ENUMERATION_BATCH_SIZE; @@ -50,9 +51,19 @@ export type KiloSdkSessionSnapshotRead = | { kind: 'retryable_failure' } | KiloSdkInvalidData; +type KiloSdkStoredMessageValue = { + info: Record; + parts: Record[]; +}; + +export type KiloSdkExactMessageRead = + | { kind: 'value'; message: KiloSdkStoredMessageValue } + | { kind: 'not_found' } + | KiloSdkHistoryReadFailure; + type KiloSdkMessagesRead = | { - messages: Array<{ info: Record; parts: Record[] }>; + messages: KiloSdkStoredMessageValue[]; nextCursor: string | null; omittedItemCount: number; } @@ -71,6 +82,21 @@ export function createKiloSdkHistoryReadBudget( } type ItemDataRef = Pick; +type ExactMessageSnapshotRow = ItemDataRef & + Pick; +type ExactMessagePartEnumerationRef = Pick< + typeof ingestItems.$inferSelect, + 'id' | 'item_id' | 'item_data_r2_key' +> & { inlineByteLength: number | null }; +type ExactMessageSnapshot = { + messageRow: ExactMessageSnapshotRow; + partRows: ExactMessageSnapshotRow[]; +}; +type ExactMessageSnapshotRead = + | ExactMessageSnapshot + | KiloSdkHistoryTooLarge + | KiloSdkHistoryRetryableFailure + | null; type MaterializedKiloSdkMessage = { info: Record; @@ -129,6 +155,38 @@ export async function readKiloSdkSessionSnapshot( ); } +export async function readKiloSdkMessage( + db: DrizzleSqliteDODatabase, + r2: R2Bucket, + messageId: string +): Promise { + if (!messageIdSchema.safeParse(messageId).success) return { kind: 'invalid_data' }; + const snapshot = readExactMessageSnapshot(db, messageId); + if (snapshot === null) return { kind: 'not_found' }; + if (isKiloSdkHistoryReadFailure(snapshot)) return snapshot; + const { messageRow, partRows } = snapshot; + + const storageIdentity = parsePersistedKiloSdkMessageStorageIdentity(messageRow.item_id); + if (!storageIdentity || storageIdentity.messageId !== messageId) return { kind: 'invalid_data' }; + const budget = createKiloSdkHistoryReadBudget(); + const materialized = await readKiloSdkHistoryCandidate( + messageRow.id, + rowId => readItemReference(db, rowId), + r2, + budget, + 'message_scan' + ); + const outcome = resolveKiloSdkHistoryCandidateOutcome(materialized, 'fail', 'message_scan'); + if (outcome.kind === 'skip') return { kind: 'retryable_failure', phase: 'message_scan' }; + if (outcome.kind !== 'value') return outcome; + const identity = readMessageIdentity(outcome.value); + if (!identity || identity.id !== messageId) return { kind: 'invalid_data' }; + + const hydratedParts = await hydrateExactKiloSdkMessageParts(r2, budget, identity, partRows); + if (isKiloSdkHistoryReadFailure(hydratedParts)) return hydratedParts; + return { kind: 'value', message: { info: outcome.value, parts: hydratedParts } }; +} + export async function readKiloSdkMessages( db: DrizzleSqliteDODatabase, r2: R2Bucket, @@ -220,6 +278,110 @@ function messageItemId(messageId: string): string { return `message/${messageId}`; } +function readExactMessageSnapshot( + db: DrizzleSqliteDODatabase, + messageId: string +): ExactMessageSnapshotRead { + const messageRow = db + .select({ + id: ingestItems.id, + item_id: ingestItems.item_id, + item_data: ingestItems.item_data, + item_data_r2_key: ingestItems.item_data_r2_key, + }) + .from(ingestItems) + .where( + and(eq(ingestItems.item_type, 'message'), eq(ingestItems.item_id, messageItemId(messageId))) + ) + .limit(1) + .get(); + if (!messageRow) return null; + + const partRange = getPartItemIdentityRange(messageId); + const partRefs: ExactMessagePartEnumerationRef[] = db + .select({ + id: ingestItems.id, + item_id: ingestItems.item_id, + item_data_r2_key: ingestItems.item_data_r2_key, + inlineByteLength: sql< + number | null + >`CASE WHEN ${ingestItems.item_data_r2_key} IS NULL THEN length(CAST(${ingestItems.item_data} AS BLOB)) ELSE NULL END`, + }) + .from(ingestItems) + .where( + and( + eq(ingestItems.item_type, 'part'), + gte(ingestItems.item_id, partRange.start), + lt(ingestItems.item_id, partRange.end) + ) + ) + .orderBy(ingestItems.id) + .limit(KILO_SDK_EXACT_MESSAGE_PART_ROW_WORK_CAP + 1) + .all(); + if (partRefs.length > KILO_SDK_EXACT_MESSAGE_PART_ROW_WORK_CAP) { + return { + kind: 'too_large', + maximumBytes: MAX_KILO_SDK_HISTORY_MATERIALIZATION_BYTES, + phase: 'page_parts', + }; + } + let estimatedBytes = 0; + for (const partRef of partRefs) { + estimatedBytes += KILO_SDK_HISTORY_CANDIDATE_OVERHEAD_BYTES + (partRef.inlineByteLength ?? 0); + if (estimatedBytes > MAX_KILO_SDK_HISTORY_MATERIALIZATION_BYTES) { + return { + kind: 'too_large', + maximumBytes: MAX_KILO_SDK_HISTORY_MATERIALIZATION_BYTES, + phase: 'page_parts', + }; + } + } + const partRows: ExactMessageSnapshotRow[] = []; + let lastPartRowId = 0; + for (;;) { + const rows = db + .select({ + id: ingestItems.id, + item_id: ingestItems.item_id, + item_data: ingestItems.item_data, + item_data_r2_key: ingestItems.item_data_r2_key, + }) + .from(ingestItems) + .where( + and( + eq(ingestItems.item_type, 'part'), + gte(ingestItems.item_id, partRange.start), + lt(ingestItems.item_id, partRange.end), + gt(ingestItems.id, lastPartRowId) + ) + ) + .orderBy(ingestItems.id) + .limit(KILO_SDK_HISTORY_ENUMERATION_BATCH_SIZE) + .all(); + if (rows.length === 0) break; + partRows.push(...rows); + lastPartRowId = rows[rows.length - 1]?.id ?? lastPartRowId; + if (rows.length < KILO_SDK_HISTORY_ENUMERATION_BATCH_SIZE) break; + } + if ( + partRows.length !== partRefs.length || + partRows.some((row, index) => { + const ref = partRefs[index]; + return ( + ref === undefined || + row.id !== ref.id || + row.item_id !== ref.item_id || + row.item_data_r2_key !== ref.item_data_r2_key || + (row.item_data_r2_key === null && + new TextEncoder().encode(row.item_data).byteLength !== ref.inlineByteLength) + ); + }) + ) { + return { kind: 'retryable_failure', phase: 'page_parts' }; + } + return { messageRow, partRows }; +} + function parsePersistedKiloSdkMessageStorageIdentity( itemId: string ): PersistedKiloSdkMessageStorageIdentity | null { @@ -543,6 +705,43 @@ function countOmittedKiloSdkMessageRows( return parts.kind === 'invalid_data' ? parts : { kind: 'count', count: 1 + parts.count }; } +async function hydrateExactKiloSdkMessageParts( + r2: R2Bucket, + budget: KiloSdkHistoryReadBudget, + identity: KiloSdkMessagesLegacyCursor, + partRows: ExactMessageSnapshotRow[] +): Promise[] | KiloSdkHistoryReadFailure> { + const parts: Record[] = []; + for (const partRow of partRows) { + const storageIdentity = parsePersistedKiloSdkPartStorageIdentity(partRow.item_id, identity.id); + if (!storageIdentity) return { kind: 'invalid_data' }; + if (budget.consumedBytes + KILO_SDK_HISTORY_CANDIDATE_OVERHEAD_BYTES > budget.maximumBytes) { + return { kind: 'too_large', maximumBytes: budget.maximumBytes, phase: 'page_parts' }; + } + budget.consumedBytes += KILO_SDK_HISTORY_CANDIDATE_OVERHEAD_BYTES; + const materialized = await readKiloSdkHistoryItem(partRow, r2, budget, 'page_parts'); + if (materialized.kind === 'intrinsically_too_large') { + return { + kind: 'too_large', + maximumBytes: MAX_KILO_SDK_HISTORY_MATERIALIZATION_BYTES, + phase: 'page_parts', + }; + } + if (materialized.kind !== 'value') return materialized; + const bodyIdentity = readPartIdentity(materialized.value); + if ( + !bodyIdentity || + bodyIdentity.messageId !== identity.id || + bodyIdentity.messageId !== storageIdentity.messageId || + bodyIdentity.partId !== storageIdentity.partId + ) { + return { kind: 'invalid_data' }; + } + parts.push(materialized.value); + } + return parts.sort(compareKiloSdkPart); +} + async function hydrateKiloSdkMessageParts( db: DrizzleSqliteDODatabase, r2: R2Bucket, diff --git a/services/session-ingest/src/index.test.ts b/services/session-ingest/src/index.test.ts index 83a6cbe011..4521073246 100644 --- a/services/session-ingest/src/index.test.ts +++ b/services/session-ingest/src/index.test.ts @@ -25,9 +25,19 @@ vi.mock('./dos/SessionIngestDO', () => ({ getSessionIngestDO: vi.fn(), })); +vi.mock('./dos/UserConnectionDO', () => ({ + getUserConnectionDO: vi.fn(), +})); + +vi.mock('./services/session-export', () => ({ + getSessionExport: vi.fn(), +})); + import { app } from './index'; import { getWorkerDb } from '@kilocode/db/client'; import { getSessionIngestDO } from './dos/SessionIngestDO'; +import { getUserConnectionDO } from './dos/UserConnectionDO'; +import { getSessionExport } from './services/session-export'; type TestBindings = { HYPERDRIVE: { connectionString: string }; @@ -35,6 +45,7 @@ type TestBindings = { SESSION_ACCESS_CACHE_DO: unknown; NEXTAUTH_SECRET: unknown; NEXTAUTH_SECRET_RAW?: string; + INTERNAL_API_SECRET_PROD: { get: () => Promise }; }; function makeDbFakes() { @@ -59,8 +70,152 @@ const defaultEnv: TestBindings = { SESSION_ACCESS_CACHE_DO: {}, NEXTAUTH_SECRET: {}, NEXTAUTH_SECRET_RAW: 'secret', + INTERNAL_API_SECRET_PROD: { get: async () => 'internal-secret' }, }; +describe('internal user events route', () => { + beforeEach(() => { + vi.resetAllMocks(); + Object.defineProperty(crypto.subtle, 'timingSafeEqual', { + configurable: true, + value: (left: ArrayBuffer, right: ArrayBuffer) => { + const leftBytes = new Uint8Array(left); + const rightBytes = new Uint8Array(right); + return ( + leftBytes.length === rightBytes.length && + leftBytes.every((byte, i) => byte === rightBytes[i]) + ); + }, + }); + }); + + function request( + path = '/internal/user/events?connectionId=facade-connection', + headers: Record = {} + ) { + return app.request( + path, + { + headers: { + 'X-Internal-Secret': 'internal-secret', + 'X-Kilo-User-Id': 'usr_test', + Upgrade: 'websocket', + ...headers, + }, + }, + defaultEnv + ); + } + + it('returns 401 for an invalid internal secret', async () => { + const response = await request(undefined, { 'X-Internal-Secret': 'wrong-secret' }); + + expect(response.status).toBe(401); + expect(getUserConnectionDO).not.toHaveBeenCalled(); + }); + + it('returns 426 unless Upgrade is websocket case-insensitively', async () => { + const response = await request(undefined, { Upgrade: 'h2c' }); + + expect(response.status).toBe(426); + expect(getUserConnectionDO).not.toHaveBeenCalled(); + }); + + it.each([ + [{ 'X-Kilo-User-Id': '' }, '/internal/user/events?connectionId=facade-connection'], + [{ 'X-Kilo-User-Id': ' '.repeat(3) }, '/internal/user/events?connectionId=facade-connection'], + [{ 'X-Kilo-User-Id': 'u'.repeat(257) }, '/internal/user/events?connectionId=facade-connection'], + [{}, '/internal/user/events'], + [{}, '/internal/user/events?connectionId='], + [{}, '/internal/user/events?connectionId=one&connectionId=two'], + [{}, `/internal/user/events?connectionId=${'c'.repeat(257)}`], + ])('returns 400 for invalid identity or connection', async (headers, path) => { + const response = await request(path, headers); + + expect(response.status).toBe(400); + expect(getUserConnectionDO).not.toHaveBeenCalled(); + }); + + it('forwards an authenticated upgrade to the user DO web path', async () => { + const forwardedResponse = new Response('forwarded', { status: 200 }); + const fetch = vi.fn(async (_request: Request) => forwardedResponse); + vi.mocked(getUserConnectionDO).mockReturnValue({ fetch } as never); + + const response = await request('/internal/user/events?connectionId=facade%2Fconnection', { + Upgrade: 'WebSocket', + }); + + expect(response).toBe(forwardedResponse); + expect(getUserConnectionDO).toHaveBeenCalledWith(defaultEnv, { kiloUserId: 'usr_test' }); + expect(fetch).toHaveBeenCalledOnce(); + const forwardedRequest = fetch.mock.calls[0]?.[0]; + expect(forwardedRequest).toBeInstanceOf(Request); + if (!(forwardedRequest instanceof Request)) throw new Error('Expected forwarded Request'); + const forwardedUrl = new URL(forwardedRequest.url); + expect(forwardedUrl.pathname).toBe('/web'); + expect(forwardedUrl.searchParams.getAll('connectionId')).toEqual(['facade/connection']); + expect(forwardedRequest.headers.get('Upgrade')).toBe('websocket'); + }); +}); + +describe('internal session export route', () => { + beforeEach(() => { + vi.resetAllMocks(); + Object.defineProperty(crypto.subtle, 'timingSafeEqual', { + configurable: true, + value: (left: ArrayBuffer, right: ArrayBuffer) => { + const leftBytes = new Uint8Array(left); + const rightBytes = new Uint8Array(right); + return ( + leftBytes.length === rightBytes.length && + leftBytes.every((byte, i) => byte === rightBytes[i]) + ); + }, + }); + }); + + it('reuses internal secret authentication before exporting', async () => { + const response = await app.request( + '/internal/session/ses_12345678901234567890123456/export', + { + headers: { + 'X-Internal-Secret': 'wrong-secret', + 'X-Kilo-User-Id': 'usr_test', + }, + }, + defaultEnv + ); + + expect(response.status).toBe(401); + expect(getSessionExport).not.toHaveBeenCalled(); + }); + + it('returns an authenticated session export', async () => { + vi.mocked(getSessionExport).mockResolvedValue( + new Response('{"ok":true}').body as ReadableStream + ); + + const response = await app.request( + '/internal/session/ses_12345678901234567890123456/export', + { + headers: { + 'X-Internal-Secret': 'internal-secret', + 'X-Kilo-User-Id': 'usr_test', + }, + }, + defaultEnv + ); + + expect(response.status).toBe(200); + expect(await response.text()).toBe('{"ok":true}'); + expect(getSessionExport).toHaveBeenCalledWith( + defaultEnv, + 'ses_12345678901234567890123456', + 'usr_test' + ); + }); +}); + describe('public session route', () => { beforeEach(() => { vi.resetAllMocks(); diff --git a/services/session-ingest/src/internal-auth.ts b/services/session-ingest/src/internal-auth.ts new file mode 100644 index 0000000000..8f1dde1b18 --- /dev/null +++ b/services/session-ingest/src/internal-auth.ts @@ -0,0 +1,17 @@ +import type { Env } from './env'; + +async function digestSecret(secret: string): Promise { + return crypto.subtle.digest('SHA-256', new TextEncoder().encode(secret)); +} + +export async function hasValidInternalSecret(request: Request, env: Env): Promise { + const provided = request.headers.get('X-Internal-Secret'); + const expected = await env.INTERNAL_API_SECRET_PROD.get(); + if (!provided || !expected) return false; + + const [providedDigest, expectedDigest] = await Promise.all([ + digestSecret(provided), + digestSecret(expected), + ]); + return crypto.subtle.timingSafeEqual(providedDigest, expectedDigest); +} diff --git a/services/session-ingest/src/internal-routes.ts b/services/session-ingest/src/internal-routes.ts new file mode 100644 index 0000000000..6274ffbc28 --- /dev/null +++ b/services/session-ingest/src/internal-routes.ts @@ -0,0 +1,24 @@ +import { sessionIdSchema } from '@kilocode/session-ingest-contracts'; +import { Hono } from 'hono'; +import type { Env } from './env'; +import { hasValidInternalSecret } from './internal-auth'; +import { getSessionExport } from './services/session-export'; + +export const internalRoutes = new Hono<{ Bindings: Env }>(); + +internalRoutes.get('/session/:sessionId/export', async c => { + if (!(await hasValidInternalSecret(c.req.raw, c.env))) { + return c.json({ success: false, error: 'Unauthorized' }, 401); + } + + const kiloUserId = c.req.header('X-Kilo-User-Id'); + if (!kiloUserId) return c.json({ success: false, error: 'Missing X-Kilo-User-Id' }, 400); + + const parsed = sessionIdSchema.safeParse(c.req.param('sessionId')); + if (!parsed.success) return c.json({ success: false, error: 'Invalid sessionId' }, 400); + + const stream = await getSessionExport(c.env, parsed.data, kiloUserId); + if (stream === null) return c.json({ success: false, error: 'Session not found' }, 404); + + return c.body(stream, 200, { 'content-type': 'application/json; charset=utf-8' }); +}); diff --git a/services/session-ingest/src/internal-user-events-entrypoint.ts b/services/session-ingest/src/internal-user-events-entrypoint.ts new file mode 100644 index 0000000000..0bbdd5f293 --- /dev/null +++ b/services/session-ingest/src/internal-user-events-entrypoint.ts @@ -0,0 +1,13 @@ +import { WorkerEntrypoint } from 'cloudflare:workers'; +import { Hono } from 'hono'; +import type { Env } from './env'; +import { internalUserEventRoutes } from './internal-user-events'; + +const app = new Hono<{ Bindings: Env }>(); +app.route('/internal', internalUserEventRoutes); + +export class InternalUserEventsEntrypoint extends WorkerEntrypoint { + fetch(request: Request): Response | Promise { + return app.fetch(request, this.env, this.ctx); + } +} diff --git a/services/session-ingest/src/internal-user-events.ts b/services/session-ingest/src/internal-user-events.ts new file mode 100644 index 0000000000..172945f5e5 --- /dev/null +++ b/services/session-ingest/src/internal-user-events.ts @@ -0,0 +1,32 @@ +import { Hono } from 'hono'; +import { z } from 'zod'; +import { getUserConnectionDO } from './dos/UserConnectionDO'; +import type { Env } from './env'; +import { hasValidInternalSecret } from './internal-auth'; + +const internalIdentitySchema = z.string().trim().min(1).max(256); + +export const internalUserEventRoutes = new Hono<{ Bindings: Env }>(); + +internalUserEventRoutes.get('/user/events', async c => { + if (!(await hasValidInternalSecret(c.req.raw, c.env))) { + return c.json({ success: false, error: 'Unauthorized' }, 401); + } + if (c.req.header('Upgrade')?.toLowerCase() !== 'websocket') { + return c.json({ success: false, error: 'WebSocket upgrade required' }, 426); + } + + const kiloUserId = internalIdentitySchema.safeParse(c.req.header('X-Kilo-User-Id')); + const url = new URL(c.req.url); + const connectionIds = url.searchParams.getAll('connectionId'); + const connectionId = internalIdentitySchema.safeParse(connectionIds[0]); + if (!kiloUserId.success || connectionIds.length !== 1 || !connectionId.success) { + return c.json({ success: false, error: 'Invalid identity or connectionId' }, 400); + } + + const target = new URL('/web', url.origin); + target.searchParams.set('connectionId', connectionId.data); + const forwardedRequest = new Request(target, c.req.raw); + forwardedRequest.headers.set('Upgrade', 'websocket'); + return getUserConnectionDO(c.env, { kiloUserId: kiloUserId.data }).fetch(forwardedRequest); +}); diff --git a/services/session-ingest/src/queue-consumer.test.ts b/services/session-ingest/src/queue-consumer.test.ts index 7fc80c8fcb..4575eeaaeb 100644 --- a/services/session-ingest/src/queue-consumer.test.ts +++ b/services/session-ingest/src/queue-consumer.test.ts @@ -48,6 +48,7 @@ import { QUEUE_RETRY_DELAY_SECONDS, computeSessionMetadataUpdates, createItemExtractor, + preserveCloudAgentRootGitUrl, queue, } from './queue-consumer'; @@ -740,6 +741,242 @@ describe('computeSessionMetadataUpdates', () => { }); }); +describe('preserveCloudAgentRootGitUrl', () => { + it('fills missing identity and accepts the same normalized identity', () => { + expect(preserveCloudAgentRootGitUrl(null, 'https://github.com/acme/widgets')).toBe( + 'https://github.com/acme/widgets' + ); + expect( + preserveCloudAgentRootGitUrl( + 'https://github.com/acme/widgets', + 'https://github.com/acme/widgets' + ) + ).toBe('https://github.com/acme/widgets'); + }); + + it('preserves a mapped root identity when metadata clears or replaces it', () => { + expect(preserveCloudAgentRootGitUrl('https://github.com/acme/widgets', null)).toBe( + 'https://github.com/acme/widgets' + ); + expect( + preserveCloudAgentRootGitUrl( + 'https://github.com/acme/widgets', + 'https://github.com/other/repo' + ) + ).toBe('https://github.com/acme/widgets'); + }); +}); + +describe('queue Cloud Agent root repository identity', () => { + it.each([ + { parentSessionId: null, incoming: 'https://github.com/other/repo' }, + { + parentSessionId: 'ses_parent_123456789012345678', + incoming: 'https://github.com/other/repo', + }, + { parentSessionId: null, incoming: null }, + ])( + 'acknowledges rejected later gitUrl $incoming without updating or notifying for mapped parent $parentSessionId', + async ({ parentSessionId, incoming }) => { + vi.mocked(notifyUserSessionEvent).mockClear(); + const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined); + const sessionId = 'ses_12345678901234567890123456'; + const existing = 'https://github.com/acme/widgets'; + const selectResults: unknown[][] = [ + [{ session_id: sessionId }], + [ + { + status: null, + gitUrl: existing, + cloudAgentSessionId: 'agent_root', + parentSessionId, + }, + ], + ]; + const selectResult = vi.fn(async () => selectResults.shift() ?? []); + const select = { + from: vi.fn(() => select), + where: vi.fn(() => select), + limit: vi.fn(() => select), + for: vi.fn(() => select), + then: vi.fn((resolve: (value: unknown) => unknown) => resolve(selectResult())), + }; + const update = { + set: vi.fn(() => update), + where: vi.fn(() => update), + then: vi.fn((resolve: (value: undefined) => unknown) => resolve(undefined)), + }; + const dbRef: Record = {}; + const db = { + select: vi.fn(() => select), + update: vi.fn(() => update), + transaction: vi.fn(async (fn: (tx: unknown) => Promise) => fn(dbRef)), + } as unknown as ReturnType; + Object.assign(dbRef, db); + vi.mocked(getWorkerDb).mockReturnValue(db); + vi.mocked(getSessionIngestDO).mockReturnValue({ + ingest: vi.fn(async () => ({ changes: [{ name: 'gitUrl', value: incoming }] })), + } as never); + const body = new ReadableStream({ + start(controller) { + controller.enqueue( + encoder.encode( + JSON.stringify({ + data: [ + { + type: 'kilo_meta', + data: { platform: 'cloud-agent', gitUrl: incoming ?? existing }, + }, + ], + }) + ) + ); + controller.close(); + }, + }); + const remove = vi.fn(async () => undefined); + const env = { + HYPERDRIVE: { connectionString: 'postgres://test' }, + SESSION_INGEST_R2: { + get: vi.fn(async () => ({ body })), + delete: remove, + put: vi.fn(async () => undefined), + }, + } as never; + const ack = vi.fn(); + const retry = vi.fn(); + const batch = { + messages: [ + { + body: { + r2Key: 'ingest/rejected-git-url', + kiloUserId: 'usr_test', + sessionId, + ingestVersion: 2, + ingestedAt: 2, + }, + ack, + retry, + }, + ], + } as never; + const ctx = { waitUntil: vi.fn() } as unknown as ExecutionContext; + + await queue(batch, env, ctx); + + expect(update.set).not.toHaveBeenCalled(); + expect(notifyUserSessionEvent).not.toHaveBeenCalled(); + expect(remove).toHaveBeenCalledWith('ingest/rejected-git-url'); + expect(ack).toHaveBeenCalledTimes(1); + expect(retry).not.toHaveBeenCalled(); + expect(warn).toHaveBeenCalledWith( + 'Ignored Cloud Agent root repository identity replacement', + { + sessionId, + } + ); + expect(JSON.stringify(warn.mock.calls)).not.toContain(existing); + if (incoming !== null) expect(JSON.stringify(warn.mock.calls)).not.toContain(incoming); + } + ); + + it('fills a null mapped-session gitUrl from later kilo_meta', async () => { + vi.mocked(notifyUserSessionEvent).mockClear(); + const sessionId = 'ses_12345678901234567890123456'; + const incoming = 'https://github.com/acme/widgets'; + const persistedSession = { + session_id: sessionId, + created_at: '2026-05-05T00:00:00.000Z', + updated_at: '2026-05-05T00:00:01.000Z', + title: null, + created_on_platform: 'cloud-agent', + organization_id: null, + git_url: incoming, + git_branch: null, + parent_session_id: null, + status: null, + status_updated_at: null, + }; + const selectResults: unknown[][] = [ + [{ session_id: sessionId }], + [{ status: null, gitUrl: null, cloudAgentSessionId: 'agent_root', parentSessionId: null }], + [persistedSession], + ]; + const selectResult = vi.fn(async () => selectResults.shift() ?? []); + const select = { + from: vi.fn(() => select), + where: vi.fn(() => select), + limit: vi.fn(() => select), + for: vi.fn(() => select), + then: vi.fn((resolve: (value: unknown) => unknown) => resolve(selectResult())), + }; + const update = { + set: vi.fn(() => update), + where: vi.fn(() => update), + then: vi.fn((resolve: (value: undefined) => unknown) => resolve(undefined)), + }; + const dbRef: Record = {}; + const db = { + select: vi.fn(() => select), + update: vi.fn(() => update), + transaction: vi.fn(async (fn: (tx: unknown) => Promise) => fn(dbRef)), + } as unknown as ReturnType; + Object.assign(dbRef, db); + vi.mocked(getWorkerDb).mockReturnValue(db); + vi.mocked(getSessionIngestDO).mockReturnValue({ + ingest: vi.fn(async () => ({ changes: [{ name: 'gitUrl', value: incoming }] })), + } as never); + const body = new ReadableStream({ + start(controller) { + controller.enqueue( + encoder.encode( + JSON.stringify({ + data: [{ type: 'kilo_meta', data: { platform: 'cloud-agent', gitUrl: incoming } }], + }) + ) + ); + controller.close(); + }, + }); + const env = { + HYPERDRIVE: { connectionString: 'postgres://test' }, + SESSION_INGEST_R2: { + get: vi.fn(async () => ({ body })), + delete: vi.fn(async () => undefined), + put: vi.fn(async () => undefined), + }, + } as never; + const ack = vi.fn(); + const batch = { + messages: [ + { + body: { + r2Key: 'ingest/fill-git-url', + kiloUserId: 'usr_test', + sessionId, + ingestVersion: 2, + ingestedAt: 2, + }, + ack, + retry: vi.fn(), + }, + ], + } as never; + const ctx = { waitUntil: vi.fn() } as unknown as ExecutionContext; + + await queue(batch, env, ctx); + + expect(update.set).toHaveBeenCalledWith({ git_url: incoming }); + expect(notifyUserSessionEvent).toHaveBeenCalledWith( + env, + 'usr_test', + expect.objectContaining({ type: 'session.updated' }), + ctx + ); + expect(ack).toHaveBeenCalledTimes(1); + }); +}); + describe('queue status notifications', () => { it('emits a status update using the locked pre-update status instead of the intake snapshot', async () => { vi.mocked(notifyUserSessionEvent).mockClear(); diff --git a/services/session-ingest/src/queue-consumer.ts b/services/session-ingest/src/queue-consumer.ts index a56e8d0273..5c9c79f54a 100644 --- a/services/session-ingest/src/queue-consumer.ts +++ b/services/session-ingest/src/queue-consumer.ts @@ -416,6 +416,13 @@ type SessionMetadataUpdates = Partial< * match new sessions without per-read normalization. Status bumps carry * `status_updated_at = now()`. */ +export function preserveCloudAgentRootGitUrl( + current: string | null, + incoming: string | null +): string | null { + return current ?? incoming; +} + export function computeSessionMetadataUpdates( mergedChanges: Map, now: () => string = () => new Date().toISOString() @@ -462,7 +469,7 @@ async function applyMetadataChanges( const parentSessionId = mergedChanges.has('parentId') ? (mergedChanges.get('parentId') ?? null) : undefined; - const changedNonStatus = + const changedNonStatus = () => mergedChanges.has('title') || mergedChanges.has('platform') || mergedChanges.has('orgId') || @@ -471,12 +478,15 @@ async function applyMetadataChanges( parentSessionId !== undefined; const notification = await db.transaction(async tx => { - const statusChange = - status === undefined - ? { changed: false, previousStatus: null } - : await (async () => { - const [statusRow] = await tx - .select({ status: cli_sessions_v2.status }) + const locked = + status !== undefined || updates.git_url !== undefined + ? await (async () => { + const [row] = await tx + .select({ + status: cli_sessions_v2.status, + gitUrl: cli_sessions_v2.git_url, + cloudAgentSessionId: cli_sessions_v2.cloud_agent_session_id, + }) .from(cli_sessions_v2) .where( and( @@ -486,12 +496,25 @@ async function applyMetadataChanges( ) .limit(1) .for('update'); - if (!statusRow) return null; - const previousStatus = SessionStatusSchema.nullable().parse(statusRow.status); - return { changed: status !== previousStatus, previousStatus }; - })(); - - if (!statusChange) return null; + return row ?? null; + })() + : undefined; + if (locked === null) return null; + + const previousStatus = SessionStatusSchema.nullable().parse(locked?.status ?? null); + const statusChange = { + changed: status !== undefined && status !== previousStatus, + previousStatus, + }; + if (updates.git_url !== undefined && locked?.cloudAgentSessionId != null) { + const incoming = updates.git_url; + updates.git_url = preserveCloudAgentRootGitUrl(locked.gitUrl, incoming); + if (locked.gitUrl !== null && incoming !== locked.gitUrl) { + console.warn('Ignored Cloud Agent root repository identity replacement', { sessionId }); + delete updates.git_url; + mergedChanges.delete('gitUrl'); + } + } if (Object.keys(updates).length > 0) { await tx @@ -544,7 +567,7 @@ async function applyMetadataChanges( } } - if (!changedNonStatus && !statusChange.changed) return null; + if (!changedNonStatus() && !statusChange.changed) return null; const [persistedRow] = await tx .select({ @@ -569,7 +592,7 @@ async function applyMetadataChanges( if (!persistedRow) return null; return { - changedNonStatus, + changedNonStatus: changedNonStatus(), changedStatus: statusChange.changed, previousStatus: statusChange.previousStatus, session: mapSessionEventRow(persistedRow), diff --git a/services/session-ingest/src/session-ingest-rpc.test.ts b/services/session-ingest/src/session-ingest-rpc.test.ts index 0a2c549544..a2cb718349 100644 --- a/services/session-ingest/src/session-ingest-rpc.test.ts +++ b/services/session-ingest/src/session-ingest-rpc.test.ts @@ -25,6 +25,7 @@ vi.mock('drizzle-orm', async importOriginal => { return { ...actual, desc: vi.fn(actual.desc), + eq: vi.fn(actual.eq), gte: vi.fn(actual.gte), isNotNull: vi.fn(actual.isNotNull), or: vi.fn(actual.or), @@ -44,12 +45,13 @@ import { cli_sessions_v2, organization_memberships } from '@kilocode/db/schema'; import { decodeKiloSdkMessagesCursor, encodeKiloSdkMessagesCursor, + createSessionForCloudAgentSchema, MAX_KILO_SDK_MESSAGE_HISTORY_PAGE_SIZE, messageIdSchema, partIdSchema, validateKiloSdkMessagesCursor, } from '@kilocode/session-ingest-contracts'; -import { desc, gte, isNotNull, or } from 'drizzle-orm'; +import { desc, eq, gte, isNotNull, or } from 'drizzle-orm'; import { getSessionIngestDO } from './dos/SessionIngestDO'; import { SessionIngestRPC } from './session-ingest-rpc'; @@ -106,6 +108,24 @@ function makeDbFakes(rows: MappingRow[]) { return { db, select, selectResult }; } +function makeCreateDbFake(existing: unknown[] = [], persisted: unknown[] = []) { + const select = { + from: vi.fn(() => select), + where: vi.fn(() => select), + limit: vi.fn(async () => existing), + }; + const insert = { + values: vi.fn(() => insert), + onConflictDoUpdate: vi.fn(() => insert), + returning: vi.fn(async () => persisted), + }; + const db = { + select: vi.fn(() => select), + insert: vi.fn(() => insert), + }; + return { db, insert }; +} + function makeRpc(db: ReturnType['db']) { vi.mocked(getWorkerDb).mockReturnValue(db as never); const ctx = { @@ -171,6 +191,200 @@ describe('Kilo SDK message cursor codec', () => { }); }); +describe('createSessionForCloudAgentSchema', () => { + it('accepts omitted and safe HTTPS repository identity', () => { + const base = { + sessionId: sdkSessionInfoFixture.id, + kiloUserId: 'usr_owner', + cloudAgentSessionId: 'agent_root', + createdOnPlatform: 'cloud-agent', + }; + + expect(createSessionForCloudAgentSchema.safeParse(base).success).toBe(true); + expect( + createSessionForCloudAgentSchema.safeParse({ + ...base, + gitUrl: 'https://github.com/acme/widgets', + }).success + ).toBe(true); + expect( + createSessionForCloudAgentSchema.safeParse({ + ...base, + gitUrl: 'https://git.example.com/widgets.git', + }).success + ).toBe(true); + }); + + it.each([ + 'not-a-url', + 'git@github.com:acme/widgets.git', + 'https://token@github.com/acme/widgets', + 'https://github.com/acme/widgets?token=secret', + 'https://github.com/acme/widgets#readme', + ])('rejects unsafe repository identity %s', gitUrl => { + expect( + createSessionForCloudAgentSchema.safeParse({ + sessionId: sdkSessionInfoFixture.id, + kiloUserId: 'usr_owner', + cloudAgentSessionId: 'agent_root', + createdOnPlatform: 'cloud-agent', + gitUrl, + }).success + ).toBe(false); + }); +}); + +describe('SessionIngestRPC.createSessionForCloudAgent', () => { + beforeEach(() => { + vi.resetAllMocks(); + }); + + it('accepts the legacy create contract when repository identity is omitted', async () => { + const { db, insert } = makeCreateDbFake(); + const rpc = makeRpc(db as never); + + await expect( + rpc.createSessionForCloudAgent({ + sessionId: sdkSessionInfoFixture.id, + kiloUserId: 'usr_owner', + cloudAgentSessionId: 'agent_root', + createdOnPlatform: 'cloud-agent', + }) + ).resolves.toBeUndefined(); + + expect(insert.values).not.toHaveBeenCalledWith( + expect.objectContaining({ git_url: expect.anything() }) + ); + }); + + it.each([ + 'not-a-url', + 'git@github.com:acme/widgets.git', + 'https://token@github.com/acme/widgets', + 'https://github.com/acme/widgets?token=secret', + 'https://github.com/acme/widgets#readme', + ])('rejects unsafe repository identity %s before querying or persisting', async gitUrl => { + const { db, insert } = makeCreateDbFake(); + const rpc = makeRpc(db as never); + + await expect( + rpc.createSessionForCloudAgent({ + sessionId: sdkSessionInfoFixture.id, + kiloUserId: 'usr_owner', + cloudAgentSessionId: 'agent_root', + createdOnPlatform: 'cloud-agent', + gitUrl, + }) + ).rejects.toThrow(); + + expect(db.select).not.toHaveBeenCalled(); + expect(insert.values).not.toHaveBeenCalled(); + }); + + it('normalizes repository identity on insert and only fills null identity on conflict', async () => { + const { db, insert } = makeCreateDbFake( + [], + [ + { + session_id: sdkSessionInfoFixture.id, + created_at: new Date(0), + updated_at: new Date(0), + title: null, + created_on_platform: 'cloud-agent', + organization_id: null, + git_url: 'https://github.com/acme/widgets', + git_branch: null, + parent_session_id: null, + status: null, + status_updated_at: null, + }, + ] + ); + const rpc = makeRpc(db as never); + + await rpc.createSessionForCloudAgent({ + sessionId: sdkSessionInfoFixture.id, + kiloUserId: 'usr_owner', + cloudAgentSessionId: 'agent_root', + createdOnPlatform: 'cloud-agent', + gitUrl: 'https://GitHub.com/ACME/Widgets.git', + }); + + expect(insert.values).toHaveBeenCalledWith( + expect.objectContaining({ git_url: 'https://github.com/acme/widgets' }) + ); + expect(insert.onConflictDoUpdate).toHaveBeenCalledWith( + expect.objectContaining({ set: expect.objectContaining({ git_url: expect.anything() }) }) + ); + }); + + it('atomically rejects a concurrent conflicting repository identity before ownership updates', async () => { + const { db, insert } = makeCreateDbFake([], []); + const rpc = makeRpc(db as never); + + await expect( + rpc.createSessionForCloudAgent({ + sessionId: sdkSessionInfoFixture.id, + kiloUserId: 'usr_owner', + cloudAgentSessionId: 'agent_competing_root', + organizationId: 'org_competing', + createdOnPlatform: 'cloud-agent', + gitUrl: 'https://github.com/other/repo', + }) + ).rejects.toThrow('repository identity conflict'); + + expect(insert.onConflictDoUpdate).toHaveBeenCalledWith( + expect.objectContaining({ setWhere: expect.anything() }) + ); + }); + + it('rejects a different non-null repository identity on creation conflict', async () => { + const { db } = makeCreateDbFake([ + { + cloud_agent_session_id: 'agent_root', + organization_id: null, + git_url: 'https://github.com/acme/widgets', + }, + ]); + const rpc = makeRpc(db as never); + + await expect( + rpc.createSessionForCloudAgent({ + sessionId: sdkSessionInfoFixture.id, + kiloUserId: 'usr_owner', + cloudAgentSessionId: 'agent_root', + createdOnPlatform: 'cloud-agent', + gitUrl: 'https://github.com/other/repo', + }) + ).rejects.toThrow('repository identity conflict'); + }); + + it('preserves existing repository identity when creation omits it', async () => { + const { db, insert } = makeCreateDbFake([ + { + cloud_agent_session_id: 'agent_root', + organization_id: null, + git_url: 'https://github.com/acme/widgets', + }, + ]); + const rpc = makeRpc(db as never); + + await rpc.createSessionForCloudAgent({ + sessionId: sdkSessionInfoFixture.id, + kiloUserId: 'usr_owner', + cloudAgentSessionId: 'agent_root', + createdOnPlatform: 'cloud-agent', + }); + + expect(insert.values).not.toHaveBeenCalledWith( + expect.objectContaining({ git_url: expect.anything() }) + ); + expect(insert.onConflictDoUpdate).toHaveBeenCalledWith( + expect.objectContaining({ set: expect.not.objectContaining({ git_url: expect.anything() }) }) + ); + }); +}); + describe('SessionIngestRPC.resolveCloudAgentRootSessionForKiloSession', () => { beforeEach(() => { vi.resetAllMocks(); @@ -336,6 +550,183 @@ describe('SessionIngestRPC.getCloudAgentRootSessionSnapshot', () => { }); }); +describe('SessionIngestRPC.getCloudAgentRootSessionMessage', () => { + beforeEach(() => { + vi.resetAllMocks(); + }); + + it('distinguishes an unavailable owned root from an exact missing message', async () => { + const { db: missingDb } = makeDbFakes([]); + const missingRpc = makeRpc(missingDb); + await expect( + missingRpc.getCloudAgentRootSessionMessage({ + kiloUserId: 'usr_owner', + kiloSessionId: sdkSessionInfoFixture.id, + messageId: sdkUserMessageFixture.id, + }) + ).resolves.toBeNull(); + + const { db } = makeDbFakes([{ cloudAgentSessionId: 'agent_owned_root' }]); + const readKiloSdkMessage = vi.fn(async () => ({ kind: 'not_found' as const })); + vi.mocked(getSessionIngestDO).mockReturnValue({ readKiloSdkMessage } as never); + const rpc = makeRpc(db); + + await expect( + rpc.getCloudAgentRootSessionMessage({ + kiloUserId: 'usr_owner', + kiloSessionId: sdkSessionInfoFixture.id, + messageId: sdkUserMessageFixture.id, + }) + ).resolves.toEqual({ + kiloSessionId: sdkSessionInfoFixture.id, + cloudAgentSessionId: 'agent_owned_root', + message: { kind: 'not_found' }, + }); + expect(readKiloSdkMessage).toHaveBeenCalledWith(sdkUserMessageFixture.id); + }); + + it('returns an exact stored message only for the selected owned root', async () => { + const { db } = makeDbFakes([{ cloudAgentSessionId: 'agent_owned_root' }]); + vi.mocked(getSessionIngestDO).mockReturnValue({ + readKiloSdkMessage: vi.fn(async () => ({ kind: 'value', message: sdkStoredMessageFixture })), + } as never); + const rpc = makeRpc(db); + + await expect( + rpc.getCloudAgentRootSessionMessage({ + kiloUserId: 'usr_owner', + kiloSessionId: sdkSessionInfoFixture.id, + messageId: sdkUserMessageFixture.id, + }) + ).resolves.toEqual({ + kiloSessionId: sdkSessionInfoFixture.id, + cloudAgentSessionId: 'agent_owned_root', + message: { kind: 'value', message: sdkStoredMessageFixture }, + }); + }); + + it('normalizes historical diffs and omits well-identified unknown parts for exact reads', async () => { + const { db } = makeDbFakes([{ cloudAgentSessionId: 'agent_owned_root' }]); + const currentDiff = { + file: '/workspace/private/current.ts', + patch: '@@ -1 +1 @@', + additions: 1, + deletions: 1, + }; + vi.mocked(getSessionIngestDO).mockReturnValue({ + readKiloSdkMessage: vi.fn(async () => ({ + kind: 'value', + message: { + info: { + ...sdkUserMessageFixture, + summary: { + title: 'Persisted summary', + diffs: [ + { + file: '/workspace/private/historical.ts', + before: 'const value = 1;', + after: 'const value = 2;', + additions: 1, + deletions: 1, + }, + currentDiff, + ], + }, + }, + parts: [ + sdkTextPartFixture, + { + id: 'prt_future_01', + sessionID: sdkSessionInfoFixture.id, + messageID: sdkUserMessageFixture.id, + type: 'future-safe-part', + payload: { value: 'new CLI field' }, + }, + ], + }, + })), + } as never); + const rpc = makeRpc(db); + + await expect( + rpc.getCloudAgentRootSessionMessage({ + kiloUserId: 'usr_owner', + kiloSessionId: sdkSessionInfoFixture.id, + messageId: sdkUserMessageFixture.id, + }) + ).resolves.toEqual({ + kiloSessionId: sdkSessionInfoFixture.id, + cloudAgentSessionId: 'agent_owned_root', + message: { + kind: 'value', + message: { + info: { + ...sdkUserMessageFixture, + summary: { title: 'Persisted summary', diffs: [currentDiff] }, + }, + parts: [sdkTextPartFixture], + }, + }, + }); + }); + + it('returns invalid_data for unknown exact-read parts with malformed identities', async () => { + const { db } = makeDbFakes([{ cloudAgentSessionId: 'agent_owned_root' }]); + vi.mocked(getSessionIngestDO).mockReturnValue({ + readKiloSdkMessage: vi.fn(async () => ({ + kind: 'value', + message: { + info: sdkUserMessageFixture, + parts: [ + { + id: 'other_future_01', + sessionID: sdkSessionInfoFixture.id, + messageID: sdkUserMessageFixture.id, + type: 'future-safe-part', + }, + ], + }, + })), + } as never); + const rpc = makeRpc(db); + + await expect( + rpc.getCloudAgentRootSessionMessage({ + kiloUserId: 'usr_owner', + kiloSessionId: sdkSessionInfoFixture.id, + messageId: sdkUserMessageFixture.id, + }) + ).resolves.toMatchObject({ message: { kind: 'invalid_data' } }); + }); + + it('maps malformed exact-message DO output to invalid_data and validates input first', async () => { + const { db } = makeDbFakes([{ cloudAgentSessionId: 'agent_owned_root' }]); + vi.mocked(getSessionIngestDO).mockReturnValue({ + readKiloSdkMessage: vi.fn(async () => ({ kind: 'value', message: { info: {}, parts: [] } })), + } as never); + const rpc = makeRpc(db); + + await expect( + rpc.getCloudAgentRootSessionMessage({ + kiloUserId: 'usr_owner', + kiloSessionId: sdkSessionInfoFixture.id, + messageId: sdkUserMessageFixture.id, + }) + ).resolves.toMatchObject({ message: { kind: 'invalid_data' } }); + + vi.resetAllMocks(); + const invalidRpc = makeRpc(makeDbFakes([]).db); + await expect( + invalidRpc.getCloudAgentRootSessionMessage({ + kiloUserId: 'usr_owner', + kiloSessionId: sdkSessionInfoFixture.id, + messageId: 'not-a-message', + }) + ).rejects.toThrow(); + expect(getWorkerDb).not.toHaveBeenCalled(); + }); +}); + describe('SessionIngestRPC.getCloudAgentRootSessionMessages', () => { beforeEach(() => { vi.resetAllMocks(); @@ -883,6 +1274,45 @@ describe('SessionIngestRPC.listCloudAgentRootSessions', () => { expect(select.limit).toHaveBeenCalledWith(2); }); + it('normalizes and filters repository roots before ordering and limiting', async () => { + const timestamp = '2026-05-28 09:13:37.651263+00'; + const { db, select } = makeDbFakes([ + { + kiloSessionId: sdkSessionInfoFixture.id, + cloudAgentSessionId: 'agent_repo_root', + title: 'Repository root', + createdAt: timestamp, + updatedAt: timestamp, + }, + ]); + const rpc = makeRpc(db); + + await expect( + rpc.listCloudAgentRootSessionsByGitUrl({ + kiloUserId: 'usr_owner', + gitUrl: 'https://GitHub.com/ACME/Widgets.git', + limit: 1, + }) + ).resolves.toHaveLength(1); + + expect(eq).toHaveBeenCalledWith(cli_sessions_v2.git_url, 'https://github.com/acme/widgets'); + expect(select.where).toHaveBeenCalledBefore(select.orderBy); + expect(select.orderBy).toHaveBeenCalledBefore(select.limit); + expect(select.limit).toHaveBeenCalledWith(1); + expect(or).toHaveBeenCalled(); + expect(isNotNull).toHaveBeenCalledWith(cli_sessions_v2.cloud_agent_session_id); + }); + + it('requires a repository identity before querying repository roots', async () => { + const { db } = makeDbFakes([]); + const rpc = makeRpc(db); + + await expect( + rpc.listCloudAgentRootSessionsByGitUrl({ kiloUserId: 'usr_owner', gitUrl: '' }) + ).rejects.toThrow(); + expect(db.select).not.toHaveBeenCalled(); + }); + it('returns mapped roots without requiring a materialized SDK snapshot and bounds titles', async () => { const timestamp = '2026-05-28 09:13:37.651263+00'; const longTitle = 'x'.repeat(600); diff --git a/services/session-ingest/src/session-ingest-rpc.ts b/services/session-ingest/src/session-ingest-rpc.ts index 34252535a7..870ff15cc8 100644 --- a/services/session-ingest/src/session-ingest-rpc.ts +++ b/services/session-ingest/src/session-ingest-rpc.ts @@ -1,33 +1,38 @@ -import { WorkerEntrypoint } from 'cloudflare:workers'; import { eq, and, desc, gte, isNotNull, isNull, or, sql } from 'drizzle-orm'; import { getWorkerDb } from '@kilocode/db/client'; import { cli_sessions_v2, organization_memberships } from '@kilocode/db/schema'; import { createSessionForCloudAgentSchema, deleteSessionForCloudAgentSchema, + getCloudAgentRootSessionMessageSchema, getCloudAgentRootSessionMessagesSchema, kiloSdkSessionSnapshotOutcomeSchema, + listCloudAgentRootSessionsByGitUrlSchema, listCloudAgentRootSessionsSchema, + persistedKiloSdkExactMessageReadSchema, persistedKiloSdkMessageHistorySchema, resolveCloudAgentRootSessionSchema, type CloudAgentRootSessionSnapshot, type CloudAgentRootSessionSummary, type CreateSessionForCloudAgentParams, type DeleteSessionForCloudAgentParams, + type GetCloudAgentRootSessionMessageParams, + type GetCloudAgentRootSessionMessageResult, type GetCloudAgentRootSessionMessagesParams, type GetCloudAgentRootSessionMessagesResult, type GetCloudAgentRootSessionSnapshotParams, type GetCloudAgentRootSessionSnapshotResult, + type ListCloudAgentRootSessionsByGitUrlParams, type ListCloudAgentRootSessionsParams, type ResolveCloudAgentRootSessionForKiloSessionParams, type ResolveCloudAgentRootSessionForKiloSessionResult, type SessionIngestRpcMethods, } from '@kilocode/session-ingest-contracts'; -import type { Env } from './env'; import { getSessionIngestDO } from './dos/SessionIngestDO'; +import { InternalUserEventsEntrypoint } from './internal-user-events-entrypoint'; import { getSessionAccessCacheDO } from './dos/SessionAccessCacheDO'; -import { withDORetry } from '@kilocode/worker-utils'; +import { normalizeGitUrl, withDORetry } from '@kilocode/worker-utils'; import { app } from './app'; import { mapSessionEventRow, notifyUserSessionEvent } from './session-events'; @@ -52,10 +57,14 @@ function personalOrAccessibleOrganizationCondition() { return or(isNull(cli_sessions_v2.organization_id), isNotNull(organization_memberships.id)); } -export class SessionIngestRPC extends WorkerEntrypoint implements SessionIngestRpcMethods { +export class SessionIngestRPC + extends InternalUserEventsEntrypoint + implements SessionIngestRpcMethods +{ // Delegate HTTP requests to the Hono app so callers using the service // binding can `.fetch()` against this entrypoint (not just call RPC methods). fetch(request: Request): Response | Promise { + if (new URL(request.url).pathname === '/internal/user/events') return super.fetch(request); return app.fetch(request, this.env, this.ctx); } @@ -70,6 +79,7 @@ export class SessionIngestRPC extends WorkerEntrypoint implements SessionIn const parsed = createSessionForCloudAgentSchema.parse(params); const db = getWorkerDb(this.env.HYPERDRIVE.connectionString); + const gitUrl = parsed.gitUrl === undefined ? undefined : normalizeGitUrl(parsed.gitUrl); const existingRows = await db .select() @@ -82,11 +92,15 @@ export class SessionIngestRPC extends WorkerEntrypoint implements SessionIn ) .limit(1); const existingRow = existingRows[0]; + if (existingRow?.git_url != null && gitUrl !== undefined && existingRow.git_url !== gitUrl) { + throw new Error('Cloud Agent root repository identity conflict'); + } const hasMeaningfulChange = existingRow ? existingRow.cloud_agent_session_id !== parsed.cloudAgentSessionId || (parsed.organizationId !== undefined && - existingRow.organization_id !== parsed.organizationId) + existingRow.organization_id !== parsed.organizationId) || + (gitUrl !== undefined && existingRow.git_url === null) : true; const [persistedRow] = await db @@ -98,6 +112,7 @@ export class SessionIngestRPC extends WorkerEntrypoint implements SessionIn organization_id: parsed.organizationId ?? null, created_on_platform: parsed.createdOnPlatform, ...(parsed.title !== undefined ? { title: parsed.title } : {}), + ...(gitUrl !== undefined ? { git_url: gitUrl } : {}), version: 0, }) .onConflictDoUpdate({ @@ -107,10 +122,22 @@ export class SessionIngestRPC extends WorkerEntrypoint implements SessionIn ...(parsed.organizationId !== undefined ? { organization_id: parsed.organizationId } : {}), + ...(gitUrl !== undefined + ? { git_url: sql`coalesce(${cli_sessions_v2.git_url}, ${gitUrl})` } + : {}), }, + ...(gitUrl !== undefined + ? { + setWhere: or(isNull(cli_sessions_v2.git_url), eq(cli_sessions_v2.git_url, gitUrl)), + } + : {}), }) .returning(); + if (gitUrl !== undefined && (!persistedRow || persistedRow.git_url !== gitUrl)) { + throw new Error('Cloud Agent root repository identity conflict'); + } + if (hasMeaningfulChange && persistedRow) { const session = mapSessionEventRow(persistedRow); notifyUserSessionEvent( @@ -165,6 +192,33 @@ export class SessionIngestRPC extends WorkerEntrypoint implements SessionIn }); } + async getCloudAgentRootSessionMessage( + params: GetCloudAgentRootSessionMessageParams + ): Promise { + const parsed = getCloudAgentRootSessionMessageSchema.parse(params); + const mapping = await this.findOwnedRootCloudAgentMapping(parsed); + if (!mapping) { + return null; + } + + const rawMessage = await withDORetry, unknown>( + () => + getSessionIngestDO(this.env, { + kiloUserId: parsed.kiloUserId, + sessionId: parsed.kiloSessionId, + }), + stub => stub.readKiloSdkMessage(parsed.messageId), + 'SessionIngestDO.readKiloSdkMessage' + ); + const message = persistedKiloSdkExactMessageReadSchema.safeParse(rawMessage); + + return { + kiloSessionId: parsed.kiloSessionId, + cloudAgentSessionId: mapping.cloudAgentSessionId, + message: message.success ? message.data : { kind: 'invalid_data' }, + }; + } + async getCloudAgentRootSessionMessages( params: GetCloudAgentRootSessionMessagesParams ): Promise { @@ -196,15 +250,31 @@ export class SessionIngestRPC extends WorkerEntrypoint implements SessionIn params: ListCloudAgentRootSessionsParams ): Promise { const parsed = listCloudAgentRootSessionsSchema.parse(params); + return this.listCloudAgentRoots(parsed); + } + + async listCloudAgentRootSessionsByGitUrl( + params: ListCloudAgentRootSessionsByGitUrlParams + ): Promise { + const parsed = listCloudAgentRootSessionsByGitUrlSchema.parse(params); + return this.listCloudAgentRoots({ ...parsed, gitUrl: normalizeGitUrl(parsed.gitUrl) }); + } + + private async listCloudAgentRoots( + params: ListCloudAgentRootSessionsParams & { gitUrl?: string } + ): Promise { const db = getWorkerDb(this.env.HYPERDRIVE.connectionString); const conditions = [ - eq(cli_sessions_v2.kilo_user_id, parsed.kiloUserId), + eq(cli_sessions_v2.kilo_user_id, params.kiloUserId), isNull(cli_sessions_v2.parent_session_id), isNotNull(cli_sessions_v2.cloud_agent_session_id), personalOrAccessibleOrganizationCondition(), ]; - if (parsed.start !== undefined) { - conditions.push(gte(cli_sessions_v2.updated_at, new Date(parsed.start).toISOString())); + if (params.gitUrl !== undefined) { + conditions.push(eq(cli_sessions_v2.git_url, params.gitUrl)); + } + if (params.start !== undefined) { + conditions.push(gte(cli_sessions_v2.updated_at, new Date(params.start).toISOString())); } const rows = await db @@ -218,10 +288,10 @@ export class SessionIngestRPC extends WorkerEntrypoint implements SessionIn updatedAt: cli_sessions_v2.updated_at, }) .from(cli_sessions_v2) - .leftJoin(organization_memberships, organizationMembershipJoinCondition(parsed.kiloUserId)) + .leftJoin(organization_memberships, organizationMembershipJoinCondition(params.kiloUserId)) .where(and(...conditions)) .orderBy(desc(cli_sessions_v2.updated_at), desc(cli_sessions_v2.session_id)) - .limit(parsed.limit); + .limit(params.limit ?? 100); const sessions: CloudAgentRootSessionSummary[] = []; for (const row of rows) { diff --git a/services/session-ingest/src/types/user-connection-protocol.test.ts b/services/session-ingest/src/types/user-connection-protocol.test.ts index b5145be65f..6f25a17655 100644 --- a/services/session-ingest/src/types/user-connection-protocol.test.ts +++ b/services/session-ingest/src/types/user-connection-protocol.test.ts @@ -1,3 +1,4 @@ +import { sessionUpdatedMetadataMessageSchema } from '@kilocode/session-ingest-contracts'; import { describe, it, expect } from 'vitest'; import { CLIOutboundMessageSchema, @@ -303,6 +304,68 @@ describe('WebInboundMessageSchema', () => { }); }); +describe('sessionUpdatedMetadataMessageSchema', () => { + const session = { + source: 'v2', + sessionId: validSessionId, + createdAt: '2026-01-01T00:00:00.000Z', + updatedAt: '2026-01-01T00:00:01.000Z', + title: 'Test', + createdOnPlatform: 'web', + organizationId: null, + gitUrl: null, + gitBranch: null, + parentSessionId: null, + status: 'idle', + statusUpdatedAt: null, + }; + + it('accepts only the shared v2 session.updated metadata message', () => { + expect( + sessionUpdatedMetadataMessageSchema.safeParse({ + type: 'system', + event: 'session.updated', + data: { source: 'v2', session, changedAt: session.updatedAt }, + }).success + ).toBe(true); + }); + + it.each([ + { type: 'system', event: 'sessions.list', data: { sessions: [] } }, + { type: 'event', event: 'session.updated', data: { source: 'v2', session } }, + { + type: 'system', + event: 'session.status.updated', + data: { source: 'v2', session, changedAt: session.updatedAt }, + }, + { + type: 'system', + event: 'session.updated', + data: { source: 'v1', session, changedAt: session.updatedAt }, + }, + { + type: 'system', + event: 'session.updated', + data: { + source: 'v2', + session: { ...session, sessionId: undefined }, + changedAt: session.updatedAt, + }, + }, + { + type: 'system', + event: 'session.updated', + data: { + source: 'v2', + session: { ...session, sessionId: 'not-a-kilo-session' }, + changedAt: session.updatedAt, + }, + }, + ])('rejects messages outside the narrow facade metadata contract', message => { + expect(sessionUpdatedMetadataMessageSchema.safeParse(message).success).toBe(false); + }); +}); + describe('SessionEventPayloadSchema', () => { const session = { source: 'v2', diff --git a/services/session-ingest/src/types/user-connection-protocol.ts b/services/session-ingest/src/types/user-connection-protocol.ts index 702066f961..170c237747 100644 --- a/services/session-ingest/src/types/user-connection-protocol.ts +++ b/services/session-ingest/src/types/user-connection-protocol.ts @@ -1,3 +1,10 @@ +import { + sessionEventV2RowSchema, + sessionRowEventPayloadSchema, + sessionStatusSchema, + type SessionEventV2Row, + type SessionRowEventPayload, +} from '@kilocode/session-ingest-contracts'; import { z } from 'zod'; // Use z.string() for session IDs (not the strict sessionIdSchema from ws-protocol) @@ -90,28 +97,9 @@ export const WebOutboundMessageSchema = z.discriminatedUnion('type', [ // -- V2 session system events ------------------------------------------------- -export const SessionStatusSchema = z.enum(['idle', 'busy', 'question', 'permission', 'retry']); - -export const SessionEventV2RowSchema = z.object({ - source: z.literal('v2'), - sessionId: z.string(), - createdAt: z.string(), - updatedAt: z.string(), - title: z.string().nullable(), - createdOnPlatform: z.string().nullable(), - organizationId: z.string().nullable(), - gitUrl: z.string().nullable(), - gitBranch: z.string().nullable(), - parentSessionId: z.string().nullable(), - status: SessionStatusSchema.nullable(), - statusUpdatedAt: z.string().nullable(), -}); - -export const SessionRowEventPayloadSchema = z.object({ - source: z.literal('v2'), - session: SessionEventV2RowSchema, - changedAt: z.string(), -}); +export const SessionStatusSchema = sessionStatusSchema; +export const SessionEventV2RowSchema = sessionEventV2RowSchema; +export const SessionRowEventPayloadSchema = sessionRowEventPayloadSchema; // Temporary rollout compatibility: remove the lightweight branch after all web clients consume full session rows. export const SessionStatusUpdatedPayloadSchema = z.union([ @@ -185,8 +173,7 @@ export type CLIOutboundMessage = z.infer; export type CLIInboundMessage = z.infer; export type WebOutboundMessage = z.infer; export type WebInboundMessage = z.infer; -export type SessionEventV2Row = z.infer; -export type SessionRowEventPayload = z.infer; +export type { SessionEventV2Row, SessionRowEventPayload }; export type SessionStatusUpdatedPayload = z.infer; export type SessionDeletedPayload = z.infer; export type SessionEventPayload = z.infer; diff --git a/services/session-ingest/test/integration/session-ingest-do.test.ts b/services/session-ingest/test/integration/session-ingest-do.test.ts index d770813243..5aaab87c3d 100644 --- a/services/session-ingest/test/integration/session-ingest-do.test.ts +++ b/services/session-ingest/test/integration/session-ingest-do.test.ts @@ -1,16 +1,18 @@ import { env, runInDurableObject } from 'cloudflare:test'; import { describe, it, expect } from 'vitest'; +import { drizzle } from 'drizzle-orm/durable-sqlite'; import { decodeKiloSdkMessagesCursor, MAX_KILO_SDK_MESSAGE_HISTORY_PAGE_SIZE, } from '@kilocode/session-ingest-contracts'; import { + KILO_SDK_EXACT_MESSAGE_PART_ROW_WORK_CAP, KILO_SDK_HISTORY_BOUNDED_MESSAGE_SCAN_ROW_WORK_CAP, MAX_KILO_SDK_HISTORY_MATERIALIZATION_BYTES, MAX_KILO_SDK_SESSION_SNAPSHOT_BYTES, + readKiloSdkMessage, } from '../../src/dos/kilo-sdk-materialization'; - function getStub(kiloUserId: string, sessionId: string) { const doKey = `${kiloUserId}/${sessionId}`; const id = env.SESSION_INGEST_DO.idFromName(doKey); @@ -245,6 +247,389 @@ describe('SessionIngestDO integration', () => { }); }); + describe('SDK exact message reads', () => { + it('directly materializes the selected message and its parts', async () => { + const sessionId = 'ses_sdk_exact_message_000000001'; + const stub = getStub(kiloUserId, sessionId); + const info = { + id: 'msg_exact_01', + sessionID: sessionId, + role: 'user' as const, + time: { created: 100 }, + agent: 'build', + model: { providerID: 'provider', modelID: 'model' }, + }; + const part = { + id: 'prt_exact_01', + sessionID: sessionId, + messageID: info.id, + type: 'text' as const, + text: 'selected', + }; + await stub.ingest( + [ + { type: 'message', data: info }, + { type: 'part', data: part }, + { + type: 'message', + data: { + ...info, + id: 'msg_other_02', + time: { created: 200 }, + }, + }, + ], + kiloUserId, + sessionId, + 1 + ); + + await expect(stub.readKiloSdkMessage(info.id)).resolves.toEqual({ + kind: 'value', + message: { info, parts: [part] }, + }); + await expect(stub.readKiloSdkMessage('msg_missing_03')).resolves.toEqual({ + kind: 'not_found', + }); + }); + + it('returns retryable_failure when selected parts change during initial snapshot enumeration', async () => { + const sessionId = 'ses_sdk_exact_initial_race_000001'; + const stub = getStub(kiloUserId, sessionId); + const messageId = 'msg_exact_initial_race'; + const partId = 'prt_exact_initial_race'; + await stub.ingest( + [ + { + type: 'message', + data: { + id: messageId, + sessionID: sessionId, + role: 'user' as const, + time: { created: 100 }, + agent: 'build', + model: { providerID: 'provider', modelID: 'model' }, + }, + }, + { + type: 'part', + data: { + id: partId, + sessionID: sessionId, + messageID: messageId, + type: 'text' as const, + text: 'before', + }, + }, + ], + kiloUserId, + sessionId, + 1 + ); + + await runInDurableObject(stub, async (_instance, state) => { + const db = drizzle(state.storage, { logger: false }); + let allCallCount = 0; + const wrapQuery = (query: T): T => + new Proxy(query, { + get(target, property, receiver) { + const value: unknown = Reflect.get(target, property, receiver); + if (typeof value !== 'function') return value; + if (property === 'all') { + return (...args: unknown[]) => { + allCallCount += 1; + if (allCallCount === 2) { + state.storage.sql.exec( + 'DELETE FROM ingest_items WHERE item_id = ?', + `${messageId}/${partId}` + ); + } + return Reflect.apply(value, target, args); + }; + } + return (...args: unknown[]) => { + const result: unknown = Reflect.apply(value, target, args); + return typeof result === 'object' && result !== null ? wrapQuery(result) : result; + }; + }, + }); + const racingDb = new Proxy(db, { + get(target, property, receiver) { + if (property !== 'select') return Reflect.get(target, property, receiver); + return (...args: Parameters) => wrapQuery(db.select(...args)); + }, + }); + + await expect( + readKiloSdkMessage(racingDb, env.SESSION_INGEST_R2, messageId) + ).resolves.toEqual({ + kind: 'retryable_failure', + phase: 'page_parts', + }); + }); + }); + + it('validates selected storage, body, and part identities', async () => { + const sessionId = 'ses_sdk_exact_identity_0000001'; + const stub = getStub(kiloUserId, sessionId); + await runInDurableObject(stub, async (_instance, state) => { + state.storage.sql.exec( + 'INSERT INTO ingest_items (item_id, item_type, item_data) VALUES (?, ?, ?)', + 'message/msg_selected', + 'message', + JSON.stringify({ + id: 'msg_other', + sessionID: sessionId, + role: 'user', + time: { created: 100 }, + agent: 'build', + model: { providerID: 'provider', modelID: 'model' }, + }) + ); + }); + + await expect(stub.readKiloSdkMessage('msg_selected')).resolves.toEqual({ + kind: 'invalid_data', + }); + }); + + it('returns invalid_data when an exact selected part storage and body identity disagree', async () => { + const sessionId = 'ses_sdk_exact_part_mismatch_0001'; + const stub = getStub(kiloUserId, sessionId); + const messageId = 'msg_exact_part_mismatch'; + await stub.ingest( + [ + { + type: 'message', + data: { + id: messageId, + sessionID: sessionId, + role: 'user' as const, + time: { created: 100 }, + agent: 'build', + model: { providerID: 'provider', modelID: 'model' }, + }, + }, + { + type: 'part', + data: { + id: 'prt_storage_identity', + sessionID: sessionId, + messageID: messageId, + type: 'text' as const, + text: 'stored', + }, + }, + ], + kiloUserId, + sessionId, + 1 + ); + await runInDurableObject(stub, async (_instance, state) => { + state.storage.sql.exec( + 'UPDATE ingest_items SET item_data = ? WHERE item_id = ?', + JSON.stringify({ + id: 'prt_body_identity', + sessionID: sessionId, + messageID: messageId, + type: 'text', + text: 'body', + }), + `${messageId}/prt_storage_identity` + ); + }); + + await expect(stub.readKiloSdkMessage(messageId)).resolves.toEqual({ kind: 'invalid_data' }); + }); + + it('returns page_parts too_large for an oversized R2-backed exact selected part', async () => { + const sessionId = 'ses_sdk_exact_part_oversized_001'; + const stub = getStub(kiloUserId, sessionId); + const messageId = 'msg_exact_part_oversized'; + const partId = 'prt_exact_part_oversized'; + const oversizedKey = `items/${sessionId}/part/oversized-exact`; + await env.SESSION_INGEST_R2.put( + oversizedKey, + 'x'.repeat(MAX_KILO_SDK_HISTORY_MATERIALIZATION_BYTES + 1) + ); + await stub.ingest( + [ + { + type: 'message', + data: { + id: messageId, + sessionID: sessionId, + role: 'user' as const, + time: { created: 100 }, + agent: 'build', + model: { providerID: 'provider', modelID: 'model' }, + }, + }, + { + type: 'part', + data: { + id: partId, + sessionID: sessionId, + messageID: messageId, + type: 'text' as const, + text: 'not-used', + }, + }, + ], + kiloUserId, + sessionId, + 1, + 1000, + { [`${messageId}/${partId}`]: oversizedKey } + ); + + await expect(stub.readKiloSdkMessage(messageId)).resolves.toEqual({ + kind: 'too_large', + maximumBytes: MAX_KILO_SDK_HISTORY_MATERIALIZATION_BYTES, + phase: 'page_parts', + }); + }); + + it('returns page_parts too_large before enumerating an excessive exact part set', async () => { + const sessionId = 'ses_sdk_exact_part_rows_oversize01'; + const stub = getStub(kiloUserId, sessionId); + const messageId = 'msg_exact_part_rows_oversize'; + await stub.ingest( + [ + { + type: 'message', + data: { + id: messageId, + sessionID: sessionId, + role: 'user' as const, + time: { created: 100 }, + agent: 'build', + model: { providerID: 'provider', modelID: 'model' }, + }, + }, + ], + kiloUserId, + sessionId, + 1 + ); + await runInDurableObject(stub, async (_instance, state) => { + for (let index = 0; index <= KILO_SDK_EXACT_MESSAGE_PART_ROW_WORK_CAP; index += 1) { + state.storage.sql.exec( + 'INSERT INTO ingest_items (item_id, item_type, item_data) VALUES (?, ?, ?)', + `${messageId}/prt_${String(index).padStart(4, '0')}`, + 'part', + JSON.stringify({ + id: `prt_${String(index).padStart(4, '0')}`, + sessionID: sessionId, + messageID: messageId, + type: 'text', + text: 'x', + }) + ); + } + }); + + await expect(stub.readKiloSdkMessage(messageId)).resolves.toEqual({ + kind: 'too_large', + maximumBytes: MAX_KILO_SDK_HISTORY_MATERIALIZATION_BYTES, + phase: 'page_parts', + }); + }); + + it('returns page_parts too_large before loading excessive inline exact part bodies', async () => { + const sessionId = 'ses_sdk_exact_inline_oversize_001'; + const stub = getStub(kiloUserId, sessionId); + const messageId = 'msg_exact_inline_oversize'; + await stub.ingest( + [ + { + type: 'message', + data: { + id: messageId, + sessionID: sessionId, + role: 'user' as const, + time: { created: 100 }, + agent: 'build', + model: { providerID: 'provider', modelID: 'model' }, + }, + }, + ], + kiloUserId, + sessionId, + 1 + ); + const perPartBytes = Math.ceil( + MAX_KILO_SDK_HISTORY_MATERIALIZATION_BYTES / KILO_SDK_EXACT_MESSAGE_PART_ROW_WORK_CAP + ); + await runInDurableObject(stub, async (_instance, state) => { + for (let index = 0; index < KILO_SDK_EXACT_MESSAGE_PART_ROW_WORK_CAP; index += 1) { + const id = `prt_inline_${String(index).padStart(4, '0')}`; + state.storage.sql.exec( + 'INSERT INTO ingest_items (item_id, item_type, item_data) VALUES (?, ?, ?)', + `${messageId}/${id}`, + 'part', + JSON.stringify({ + id, + sessionID: sessionId, + messageID: messageId, + type: 'text', + text: 'x'.repeat(perPartBytes), + }) + ); + } + }); + + await expect(stub.readKiloSdkMessage(messageId)).resolves.toEqual({ + kind: 'too_large', + maximumBytes: MAX_KILO_SDK_HISTORY_MATERIALIZATION_BYTES, + phase: 'page_parts', + }); + }); + + it('preserves exact-message R2 race and size outcomes', async () => { + const sessionId = 'ses_sdk_exact_r2_000000000001'; + const stub = getStub(kiloUserId, sessionId); + const missing = { + id: 'msg_missing_r2', + sessionID: sessionId, + role: 'user' as const, + time: { created: 100 }, + agent: 'build', + model: { providerID: 'provider', modelID: 'model' }, + }; + const oversized = { ...missing, id: 'msg_oversized_r2', time: { created: 200 } }; + const oversizedKey = `items/${sessionId}/message/oversized-exact`; + await env.SESSION_INGEST_R2.put( + oversizedKey, + 'x'.repeat(MAX_KILO_SDK_HISTORY_MATERIALIZATION_BYTES + 1) + ); + await stub.ingest( + [ + { type: 'message', data: missing }, + { type: 'message', data: oversized }, + ], + kiloUserId, + sessionId, + 1, + 1000, + { + [`message/${missing.id}`]: `items/${sessionId}/message/missing-exact`, + [`message/${oversized.id}`]: oversizedKey, + } + ); + + await expect(stub.readKiloSdkMessage(missing.id)).resolves.toEqual({ + kind: 'retryable_failure', + phase: 'message_scan', + }); + await expect(stub.readKiloSdkMessage(oversized.id)).resolves.toEqual({ + kind: 'too_large', + maximumBytes: MAX_KILO_SDK_HISTORY_MATERIALIZATION_BYTES, + phase: 'message_scan', + }); + }); + }); + describe('SDK message transcript reads', () => { it('returns the complete stored SDK message transcript when no page limit is set', async () => { const sessionId = 'ses_sdk_messages_00000000001'; diff --git a/services/session-ingest/test/integration/session-ingest-rpc-websocket.test.ts b/services/session-ingest/test/integration/session-ingest-rpc-websocket.test.ts new file mode 100644 index 0000000000..49e5d9800d --- /dev/null +++ b/services/session-ingest/test/integration/session-ingest-rpc-websocket.test.ts @@ -0,0 +1,74 @@ +import { adminSecretsStore, env } from 'cloudflare:test'; +import { expect, it } from 'vitest'; + +const INTERNAL_SECRET = 'workers-runtime-internal-secret'; + +function nextMessage(webSocket: WebSocket): Promise { + return new Promise((resolve, reject) => { + webSocket.addEventListener('message', resolve, { once: true }); + webSocket.addEventListener( + 'error', + () => reject(new Error('WebSocket failed before receiving a message')), + { once: true } + ); + }); +} + +it('upgrades through the SessionIngestRPC binding and receives a committed session update', async () => { + await adminSecretsStore(env.INTERNAL_API_SECRET_PROD).create(INTERNAL_SECRET); + + const kiloUserId = 'usr_workers_runtime_integration'; + const response = await env.SESSION_INGEST.fetch( + new Request( + 'https://session-ingest.test/internal/user/events?connectionId=facade-integration', + { + headers: { + Upgrade: 'websocket', + 'X-Internal-Secret': INTERNAL_SECRET, + 'X-Kilo-User-Id': kiloUserId, + }, + } + ) + ); + + expect(response.status).toBe(101); + const webSocket = response.webSocket; + expect(webSocket).not.toBeNull(); + if (!webSocket) throw new Error('Expected SessionIngestRPC to return a WebSocket'); + webSocket.accept(); + + const initialMessage = await nextMessage(webSocket); + expect(JSON.parse(String(initialMessage.data))).toMatchObject({ + type: 'system', + event: 'sessions.list', + }); + + const session = { + source: 'v2' as const, + sessionId: 'ses_12345678901234567890123456', + createdAt: '2026-06-08T20:00:00.000Z', + updatedAt: '2026-06-08T20:01:00.000Z', + title: 'Committed Workers runtime title', + createdOnPlatform: 'cloud-agent', + organizationId: null, + gitUrl: null, + gitBranch: null, + parentSessionId: null, + status: 'idle' as const, + statusUpdatedAt: null, + }; + const semanticMessage = nextMessage(webSocket); + const notification = await env.USER_CONNECTION_DO.getByName(kiloUserId).notifySessionEvent({ + type: 'session.updated', + data: { source: 'v2', session, changedAt: session.updatedAt }, + }); + + expect(notification).toEqual({ delivered: 1 }); + expect(JSON.parse(String((await semanticMessage).data))).toEqual({ + type: 'system', + event: 'session.updated', + data: { source: 'v2', session, changedAt: session.updatedAt }, + }); + + webSocket.close(1000, 'test complete'); +}); diff --git a/services/session-ingest/test/test-worker.ts b/services/session-ingest/test/test-worker.ts index eb6abffccb..738c06493b 100644 --- a/services/session-ingest/test/test-worker.ts +++ b/services/session-ingest/test/test-worker.ts @@ -1,7 +1,12 @@ +import { InternalUserEventsEntrypoint } from '../src/internal-user-events-entrypoint'; + export { SessionIngestDO } from '../src/dos/SessionIngestDO'; +export { UserConnectionDO } from '../src/dos/UserConnectionDO'; + +export class SessionIngestRPC extends InternalUserEventsEntrypoint {} export default { fetch(): Response { - return new Response('SessionIngestDO test worker'); + return new Response('Session Ingest test worker'); }, }; diff --git a/services/session-ingest/wrangler.test.jsonc b/services/session-ingest/wrangler.test.jsonc index 02a2ebec9d..3d384ec6e7 100644 --- a/services/session-ingest/wrangler.test.jsonc +++ b/services/session-ingest/wrangler.test.jsonc @@ -20,6 +20,10 @@ "name": "SESSION_INGEST_DO", "class_name": "SessionIngestDO", }, + { + "name": "USER_CONNECTION_DO", + "class_name": "UserConnectionDO", + }, ], }, @@ -28,6 +32,26 @@ "tag": "v1", "new_sqlite_classes": ["SessionIngestDO"], }, + { + "tag": "v2", + "new_sqlite_classes": ["UserConnectionDO"], + }, + ], + + "services": [ + { + "binding": "SESSION_INGEST", + "service": "session-ingest-test", + "entrypoint": "SessionIngestRPC", + }, + ], + + "secrets_store_secrets": [ + { + "binding": "INTERNAL_API_SECRET_PROD", + "store_id": "00000000000000000000000000000000", + "secret_name": "INTERNAL_API_SECRET_PROD", + }, ], "r2_buckets": [