From 163a73c9a372d5e00aaf644e3a0e8e5fe0bc4fc8 Mon Sep 17 00:00:00 2001 From: Jean du Plessis Date: Tue, 3 Mar 2026 10:13:21 +0200 Subject: [PATCH 1/6] Remove unused FlatCompat import from eslint.config.mjs --- eslint.config.mjs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 465d7de1e..438a9361f 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,6 +1,5 @@ import { dirname } from 'path'; import { fileURLToPath } from 'url'; -import { FlatCompat } from '@eslint/eslintrc'; import tailwindcssPlugin from 'eslint-plugin-tailwindcss'; import drizzlePlugin from 'eslint-plugin-drizzle'; import nodePlugin from 'eslint-plugin-n'; @@ -13,10 +12,6 @@ import tanstackQueryPlugin from '@tanstack/eslint-plugin-query'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); -const compat = new FlatCompat({ - baseDirectory: __dirname, -}); - export default defineConfig([ eslintPluginNext.configs.recommended, eslintPluginNext.configs['core-web-vitals'], From 616f9dd97943539f8b616d5754f609a9cd0cbdcb Mon Sep 17 00:00:00 2001 From: Jean du Plessis Date: Tue, 3 Mar 2026 10:13:36 +0200 Subject: [PATCH 2/6] Add security agent auto-analysis queue system Cloudflare Worker that automatically triages and analyzes security findings via a queue-based pipeline. Dispatches due owners on a cron schedule, claims queued findings per-owner with pessimistic locking, runs LLM triage to filter noise, then launches full analysis sessions via cloud-agent-next. Uses @kilocode/db with Drizzle ORM for all database access through Hyperdrive, matching the cloudflare-security-sync reference pattern. Includes DB migration for security_analysis_queue and security_analysis_owner_state tables, plus indexes on security_findings for in-flight analysis tracking. --- .github/workflows/ci.yml | 33 + .github/workflows/deploy-production.yml | 26 + .../deploy-security-auto-analysis.yml | 50 + DEVELOPMENT.md | 2 +- .../src/ai-attribution.worker.ts | 1 - cloudflare-security-auto-analysis/.gitignore | 2 + cloudflare-security-auto-analysis/README.md | 224 + .../eslint.config.mjs | 8 + .../package.json | 27 + .../src/consumer.ts | 735 + .../src/db/queries.ts | 566 + .../src/dispatcher.ts | 44 + .../src/index.ts | 59 + .../src/launch.ts | 273 + .../src/logger.ts | 13 + .../src/token.ts | 101 + .../src/triage.ts | 223 + .../src/types.test.ts | 34 + .../src/types.ts | 84 + .../tsconfig.json | 15 + .../vitest.config.ts | 9 + .../worker-configuration.d.ts | 80 + .../wrangler.jsonc | 130 + cloudflare-webhook-agent-ingest/src/index.ts | 1 - jest.config.ts | 1 + .../src/migrations/0039_naive_yellow_claw.sql | 84 + .../db/src/migrations/meta/0039_snapshot.json | 13666 ++++++++++++++++ packages/db/src/migrations/meta/_journal.json | 7 + packages/db/src/schema.ts | 165 + pnpm-lock.yaml | 1629 +- pnpm-workspace.yaml | 36 +- .../[findingId]/route.test.ts | 39 +- .../[findingId]/route.ts | 159 +- src/lib/security-agent/core/constants.ts | 37 +- src/lib/security-agent/core/schemas.ts | 148 +- src/lib/security-agent/core/types.ts | 74 +- .../db/security-analysis.test.ts | 40 + .../security-agent/db/security-analysis.ts | 393 +- src/lib/security-agent/db/security-config.ts | 58 +- .../security-agent/db/security-findings.ts | 303 +- .../services/analysis-service.test.ts | 53 +- .../services/analysis-service.ts | 87 +- .../services/sync-service.test.ts | 173 + .../security-agent/services/sync-service.ts | 106 +- src/lib/user.test.ts | 121 + src/lib/user.ts | 8 +- .../organization-security-agent-router.ts | 21 +- src/routers/security-agent-router.ts | 21 +- 48 files changed, 18510 insertions(+), 1659 deletions(-) create mode 100644 .github/workflows/deploy-security-auto-analysis.yml create mode 100644 cloudflare-security-auto-analysis/.gitignore create mode 100644 cloudflare-security-auto-analysis/README.md create mode 100644 cloudflare-security-auto-analysis/eslint.config.mjs create mode 100644 cloudflare-security-auto-analysis/package.json create mode 100644 cloudflare-security-auto-analysis/src/consumer.ts create mode 100644 cloudflare-security-auto-analysis/src/db/queries.ts create mode 100644 cloudflare-security-auto-analysis/src/dispatcher.ts create mode 100644 cloudflare-security-auto-analysis/src/index.ts create mode 100644 cloudflare-security-auto-analysis/src/launch.ts create mode 100644 cloudflare-security-auto-analysis/src/logger.ts create mode 100644 cloudflare-security-auto-analysis/src/token.ts create mode 100644 cloudflare-security-auto-analysis/src/triage.ts create mode 100644 cloudflare-security-auto-analysis/src/types.test.ts create mode 100644 cloudflare-security-auto-analysis/src/types.ts create mode 100644 cloudflare-security-auto-analysis/tsconfig.json create mode 100644 cloudflare-security-auto-analysis/vitest.config.ts create mode 100644 cloudflare-security-auto-analysis/worker-configuration.d.ts create mode 100644 cloudflare-security-auto-analysis/wrangler.jsonc create mode 100644 packages/db/src/migrations/0039_naive_yellow_claw.sql create mode 100644 packages/db/src/migrations/meta/0039_snapshot.json create mode 100644 src/lib/security-agent/db/security-analysis.test.ts create mode 100644 src/lib/security-agent/services/sync-service.test.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eff6c99e2..17a66b703 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,7 @@ jobs: cloud_agent: ${{ steps.filter.outputs.cloud_agent }} cloud_agent_next: ${{ steps.filter.outputs.cloud_agent_next }} webhook_agent: ${{ steps.filter.outputs.webhook_agent }} + security_auto_analysis: ${{ steps.filter.outputs.security_auto_analysis }} kiloclaw: ${{ steps.filter.outputs.kiloclaw }} app_builder: ${{ steps.filter.outputs.app_builder }} steps: @@ -35,6 +36,8 @@ jobs: - 'cloud-agent-next/**' webhook_agent: - 'cloudflare-webhook-agent-ingest/**' + security_auto_analysis: + - 'cloudflare-security-auto-analysis/**' kiloclaw: - 'kiloclaw/**' app_builder: @@ -265,6 +268,36 @@ jobs: - name: Run webhook-agent-ingest tests run: pnpm --filter cloudflare-webhook-agent-ingest test + security-auto-analysis: + needs: changes + if: needs.changes.outputs.security_auto_analysis == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + lfs: true + + - name: Setup pnpm + uses: pnpm/action-setup@v2 + with: + version: latest + run_install: false + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Typecheck (security-auto-analysis) + run: pnpm --filter cloudflare-security-auto-analysis typecheck + + - name: Run security-auto-analysis tests + run: pnpm --filter cloudflare-security-auto-analysis test + kiloclaw: needs: changes if: needs.changes.outputs.kiloclaw == 'true' diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml index d68ac09e9..db467da6a 100644 --- a/.github/workflows/deploy-production.yml +++ b/.github/workflows/deploy-production.yml @@ -358,6 +358,32 @@ jobs: with: environment: prod + check-security-auto-analysis-changes: + runs-on: ubuntu-latest + outputs: + changed: ${{ steps.changes.outputs['security-auto-analysis'] }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Check for security-auto-analysis changes + uses: dorny/paths-filter@v3 + id: changes + with: + filters: | + security-auto-analysis: + - 'cloudflare-security-auto-analysis/**' + + deploy-security-auto-analysis: + needs: [check-security-auto-analysis-changes] + if: needs.check-security-auto-analysis-changes.outputs.changed == 'true' + uses: ./.github/workflows/deploy-security-auto-analysis.yml + secrets: inherit + with: + environment: prod + check-deployment-dispatcher-changes: runs-on: ubuntu-latest outputs: diff --git a/.github/workflows/deploy-security-auto-analysis.yml b/.github/workflows/deploy-security-auto-analysis.yml new file mode 100644 index 000000000..fd9f70fd5 --- /dev/null +++ b/.github/workflows/deploy-security-auto-analysis.yml @@ -0,0 +1,50 @@ +name: Deploy Security Auto Analysis + +on: + workflow_dispatch: + inputs: + environment: + description: 'Choose an environment to deploy to: dev or prod' + required: true + default: 'prod' + type: choice + options: + - dev + - prod + workflow_call: + inputs: + environment: + description: 'Choose an environment to deploy to: dev or prod' + required: false + default: 'prod' + type: string + +jobs: + deploy: + runs-on: ubuntu-latest + name: Deploy Security Auto Analysis + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v2 + with: + version: latest + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Install dependencies + working-directory: cloudflare-security-auto-analysis + run: pnpm install --frozen-lockfile + + - name: Deploy to Cloudflare Workers + uses: cloudflare/wrangler-action@v3 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + workingDirectory: cloudflare-security-auto-analysis + command: ${{ inputs.environment == 'dev' && 'deploy --env dev' || 'deploy' }} diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 317553a42..3c3422983 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -219,7 +219,7 @@ http://localhost:3000/users/sign_in?fakeUser=someone@example.com&callbackPath=/p ### Admin access -Some features (e.g., Security Agent, admin panels) are only visible to users with `is_admin = true`. The admin flag is set at user-creation time based on the email address: +Some features (e.g., admin panels) are only visible to users with `is_admin = true`. The admin flag is set at user-creation time based on the email address: - **Real OAuth:** emails ending in `@kilocode.ai` with the `kilocode.ai` hosted domain are admins. - **Fake login:** emails must end in `@admin.example.com` to get admin access. diff --git a/cloudflare-ai-attribution/src/ai-attribution.worker.ts b/cloudflare-ai-attribution/src/ai-attribution.worker.ts index 1a529de01..3b149d402 100644 --- a/cloudflare-ai-attribution/src/ai-attribution.worker.ts +++ b/cloudflare-ai-attribution/src/ai-attribution.worker.ts @@ -30,7 +30,6 @@ export type HonoContext = { const app = new Hono(); -// @ts-expect-error workers-tagged-logger returns Handler typed against an older hono; incompatible with hono 4.12+ app.use('*', useWorkersLogger('ai-attribution')); // Health check endpoint (no auth required) diff --git a/cloudflare-security-auto-analysis/.gitignore b/cloudflare-security-auto-analysis/.gitignore new file mode 100644 index 000000000..10486094e --- /dev/null +++ b/cloudflare-security-auto-analysis/.gitignore @@ -0,0 +1,2 @@ +node_modules +.wrangler diff --git a/cloudflare-security-auto-analysis/README.md b/cloudflare-security-auto-analysis/README.md new file mode 100644 index 000000000..6bc72cc56 --- /dev/null +++ b/cloudflare-security-auto-analysis/README.md @@ -0,0 +1,224 @@ +# cloudflare-security-auto-analysis + +Cloudflare Worker that automatically triages and analyzes security findings via a queue-based pipeline. Dispatches due owners on a per-minute cron, claims queued findings with pessimistic locking, runs LLM triage to filter noise, then launches full analysis sessions via `cloud-agent-next`. + +## Endpoints + +- `GET /health` — health check +- `POST /internal/dispatch` — manual dispatch trigger (bearer-token auth) +- Cron trigger (`* * * * *`) — discovers owners with queued work and enqueues them + +## Queue + +- Producer binding: `OWNER_QUEUE` +- Consumer queue: `security-auto-analysis-owner-queue` (`-dev` in dev) +- DLQ: `security-auto-analysis-owner-dlq` + +The consumer claims rows per-owner with `FOR UPDATE SKIP LOCKED`, resolves an actor, runs triage, and launches analysis via `cloud-agent-next`. + +## Service bindings + +- `CLOUD_AGENT_NEXT` — launches analysis sessions +- `GIT_TOKEN_SERVICE` — resolves GitHub tokens for repo access (RPC via `GitTokenRPCEntrypoint`) + +## Operational commands + +```bash +# Trigger the dispatcher cron immediately (dev) +pnpm --filter cloudflare-security-auto-analysis exec wrangler triggers trigger --name security-auto-analysis-dev + +# Enqueue a single owner message manually (dev) +pnpm --filter cloudflare-security-auto-analysis exec wrangler queues produce security-auto-analysis-owner-queue-dev \ + '{"ownerType":"org","ownerId":"","dispatchId":"manual","enqueuedAt":"2026-01-01T00:00:00.000Z"}' + +# Stream worker logs +pnpm --filter cloudflare-security-auto-analysis exec wrangler tail security-auto-analysis-dev + +# Inspect queues +pnpm --filter cloudflare-security-auto-analysis exec wrangler queues list +``` + +--- + +## Runbook + +### Queue states + +`security_analysis_queue.queue_status`: + +- `queued` — eligible and waiting for claim +- `pending` — claimed by worker consumer, launch in progress +- `running` — launch succeeded, waiting for callback +- `completed` — terminal success (including eligibility skips) +- `failed` — terminal error + +### Key invariants + +- Exactly one queue row per finding (`UQ_security_analysis_queue_finding_id`) +- Owner XOR: exactly one of `owned_by_organization_id` / `owned_by_user_id` +- `claim_token` must be non-null when state is `pending` or `running` + +### Staleness thresholds + +- `pending` is stale after 15 minutes +- `running` is stale after 2 hours + +> **Note:** There is no automated reconciliation cron for stale rows yet. Stuck rows must be identified and resolved manually using the diagnostic queries below. + +### Failure codes + +`security_analysis_queue.failure_code`: + +- `NETWORK_TIMEOUT` — retryable launch/callback timeout +- `UPSTREAM_5XX` — retryable upstream 5xx +- `TEMP_TOKEN_FAILURE` — token lookup threw (temporary; requeue) +- `START_CALL_AMBIGUOUS` — ambiguous launch result; retry or fail on max attempts +- `REQUEUE_TEMPORARY_PRECONDITION` — generic retryable precondition gate +- `ACTOR_RESOLUTION_FAILED` — no eligible actor; owner blocked +- `GITHUB_TOKEN_UNAVAILABLE` — actor resolved but no GitHub token +- `INVALID_CONFIG` — non-retryable config validation issue +- `MISSING_OWNERSHIP` — invalid owner linkage +- `PERMISSION_DENIED_PERMANENT` — non-retryable 403/forbidden +- `UNSUPPORTED_SEVERITY` — unsupported finding severity +- `INSUFFICIENT_CREDITS` — owner credit block, requeue with cooldown +- `STATE_GUARD_REJECTED` — state guard failure (missing finding/session/user/context) +- `SKIPPED_ALREADY_IN_PROGRESS` — analysis already running elsewhere +- `SKIPPED_NO_LONGER_ELIGIBLE` — finding/config no longer eligible at launch time +- `REOPEN_LOOP_GUARD` — reopen requeue cap reached +- `RUN_LOST` — stale `running` with no terminal progress + +`security_analysis_owner_state.block_reason`: + +- `INSUFFICIENT_CREDITS` +- `ACTOR_RESOLUTION_FAILED` +- `OPERATOR_PAUSE` + +### Log fields for correlation + +- `job_id`, `queue_id`, `finding_id`, `claim_token` +- `owned_by_organization_id` or `owned_by_user_id` +- `from_state`, `to_state`, `attempt_count`, `failure_code` +- `actor_user_id`, `actor_resolution_mode` + +### Diagnostic: queue lag + +```sql +SELECT + queue_status, + COUNT(*) AS rows, + ROUND(EXTRACT(EPOCH FROM (now() - MIN(queued_at))) / 60.0, 1) AS oldest_queued_age_min, + ROUND(EXTRACT(EPOCH FROM (now() - MAX(queued_at))) / 60.0, 1) AS newest_queued_age_min +FROM security_analysis_queue +GROUP BY queue_status +ORDER BY queue_status; +``` + +### Diagnostic: stuck pending/running + +```sql +SELECT + q.id, q.finding_id, q.queue_status, q.claim_token, + q.claimed_by_job_id, q.claimed_at, q.updated_at, + q.attempt_count, q.failure_code, + f.analysis_status, f.analysis_started_at, f.analysis_completed_at +FROM security_analysis_queue q +JOIN security_findings f ON f.id = q.finding_id +WHERE (q.queue_status = 'pending' AND q.claimed_at <= now() - interval '15 minutes') + OR (q.queue_status = 'running' AND q.updated_at <= now() - interval '2 hours') +ORDER BY q.queue_status, q.claimed_at NULLS LAST; +``` + +For stuck rows, manually transition them: + +```sql +-- Requeue a stuck pending row +UPDATE security_analysis_queue +SET queue_status = 'queued', claim_token = NULL, claimed_at = NULL, + claimed_by_job_id = NULL, failure_code = NULL, updated_at = now() +WHERE id = '' AND queue_status = 'pending'; + +-- Fail a stuck running row +UPDATE security_analysis_queue +SET queue_status = 'failed', failure_code = 'RUN_LOST', + last_error_redacted = 'Manual reconciliation', updated_at = now() +WHERE id = '' AND queue_status = 'running'; +``` + +Then trigger the dispatcher manually to resume processing. + +### Diagnostic: duplicate launch suspicion + +```sql +SELECT finding_id, COUNT(*) AS queue_rows +FROM security_analysis_queue +GROUP BY finding_id +HAVING COUNT(*) > 1; +``` + +If this returns rows, treat as an invariant violation and escalate. + +### Diagnostic: actor resolution failures + +```sql +SELECT + owned_by_organization_id, owned_by_user_id, + blocked_until, block_reason, + consecutive_actor_resolution_failures, + last_actor_resolution_failure_at +FROM security_analysis_owner_state +WHERE block_reason = 'ACTOR_RESOLUTION_FAILED' + OR consecutive_actor_resolution_failures > 0 +ORDER BY updated_at DESC; +``` + +Fix: ensure the owner has at least one eligible member with a GitHub token, then clear the block and trigger the dispatcher. + +### Diagnostic: credit blocking + +```sql +SELECT + s.owned_by_organization_id, s.owned_by_user_id, + s.block_reason, s.blocked_until, + COUNT(q.id) FILTER (WHERE q.queue_status = 'queued') AS queued_rows +FROM security_analysis_owner_state s +LEFT JOIN security_analysis_queue q + ON (s.owned_by_organization_id = q.owned_by_organization_id + OR s.owned_by_user_id = q.owned_by_user_id) +WHERE s.block_reason = 'INSUFFICIENT_CREDITS' + AND s.blocked_until > now() +GROUP BY s.owned_by_organization_id, s.owned_by_user_id, s.block_reason, s.blocked_until; +``` + +Do not clear the block until credits are restored. After top-up, clear the block and trigger the dispatcher. + +### Rollback and kill-switch + +**Global stop** (fastest): + +1. Disable the Cloudflare scheduled trigger for `security-auto-analysis` +2. Pause the `security-auto-analysis-owner-queue` consumer +3. Verify queued backlog is no longer draining + +**Owner-scoped stop** (surgical): + +```sql +UPDATE security_analysis_owner_state +SET blocked_until = now() + interval '7 days', + block_reason = 'OPERATOR_PAUSE', updated_at = now() +WHERE owned_by_organization_id = '' + OR owned_by_user_id = ''; +``` + +**Config-level disable:** + +- Set `auto_analysis_enabled = false` in the owner's `agent_configs.config` +- Or disable the security agent entirely (`agent_configs.is_enabled = false`) + +**Unpause owner:** + +```sql +UPDATE security_analysis_owner_state +SET blocked_until = NULL, block_reason = NULL, updated_at = now() +WHERE owned_by_organization_id = '' + OR owned_by_user_id = ''; +``` diff --git a/cloudflare-security-auto-analysis/eslint.config.mjs b/cloudflare-security-auto-analysis/eslint.config.mjs new file mode 100644 index 000000000..826249d26 --- /dev/null +++ b/cloudflare-security-auto-analysis/eslint.config.mjs @@ -0,0 +1,8 @@ +import { dirname } from 'path'; +import { fileURLToPath } from 'url'; +import { defineConfig } from 'eslint/config'; +import baseConfig from '@kilocode/eslint-config'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export default defineConfig([...baseConfig(__dirname)]); diff --git a/cloudflare-security-auto-analysis/package.json b/cloudflare-security-auto-analysis/package.json new file mode 100644 index 000000000..d2d016cda --- /dev/null +++ b/cloudflare-security-auto-analysis/package.json @@ -0,0 +1,27 @@ +{ + "name": "cloudflare-security-auto-analysis", + "private": true, + "type": "module", + "scripts": { + "deploy:prod": "wrangler deploy", + "deploy:dev": "wrangler deploy --env dev", + "dev": "wrangler dev --env dev", + "lint": "eslint --config eslint.config.mjs --cache 'src/**/*.ts'", + "typecheck": "tsc --noEmit", + "types": "wrangler types --env-interface CloudflareEnv worker-configuration.d.ts", + "test": "vitest run" + }, + "dependencies": { + "@kilocode/db": "workspace:*", + "drizzle-orm": "catalog:", + "workers-tagged-logger": "catalog:", + "zod": "catalog:" + }, + "devDependencies": { + "@kilocode/eslint-config": "workspace:*", + "@types/node": "^22", + "typescript": "catalog:", + "vitest": "^3.2.4", + "wrangler": "catalog:" + } +} diff --git a/cloudflare-security-auto-analysis/src/consumer.ts b/cloudflare-security-auto-analysis/src/consumer.ts new file mode 100644 index 000000000..566d87741 --- /dev/null +++ b/cloudflare-security-auto-analysis/src/consumer.ts @@ -0,0 +1,735 @@ +import { randomUUID } from 'crypto'; +import { getWorkerDb, type WorkerDb } from '@kilocode/db/client'; +import { + claimRowsForOwner, + clearOwnerActorResolutionFailure, + getSecurityFindingById, + markOwnerActorResolutionFailure, + markOwnerCreditFailure, + resolveAutoAnalysisActor, + updateQueueFromPending, + type ClaimedQueueRow, +} from './db/queries.js'; +import { logger } from './logger.js'; +import { InsufficientCreditsError, startSecurityAnalysis } from './launch.js'; +import { + AutoAnalysisOwnerMessageSchema, + AUTO_ANALYSIS_MAX_ATTEMPTS, + AUTO_ANALYSIS_OWNER_CAP, + type ActorResolutionMode, + type AutoAnalysisFailureCode, + type ProcessCounters, + type QueueOwner, + type SecurityAgentConfig, +} from './types.js'; + +type QueueStatus = 'queued' | 'pending' | 'running' | 'completed' | 'failed'; + +type QueueTransitionContext = { + jobId?: string; + owner?: QueueOwner; + findingId?: string; + queueId: string; + claimToken?: string | null; + fromState: QueueStatus; + toState: QueueStatus; + attemptCount?: number; + failureCode?: AutoAnalysisFailureCode | null; + actorUserId?: string; + actorResolutionMode?: ActorResolutionMode; +}; + +type LaunchFailureClass = 'retryable' | 'non_retryable' | 'credit_gated'; + +type LaunchFailureClassification = { + code: AutoAnalysisFailureCode; + class: LaunchFailureClass; +}; + +type TransitionParams = { + db: WorkerDb; + rowId: string; + claimToken: string; + status: 'queued' | 'running' | 'completed' | 'failed'; + failureCode?: AutoAnalysisFailureCode; + errorMessage?: string; + incrementAttempt?: boolean; + nextRetryAt?: Date | null; + logContext?: Omit & { + fromState?: QueueStatus; + attemptCount?: number; + }; +}; + +function logQueueTransition(context: QueueTransitionContext): void { + logger.info('Auto-analysis queue transition', { + job_id: context.jobId, + owned_by_organization_id: context.owner?.type === 'org' ? context.owner.id : null, + owned_by_user_id: context.owner?.type === 'user' ? context.owner.id : null, + owner_type: context.owner?.type, + owner_id: context.owner?.id, + finding_id: context.findingId, + queue_id: context.queueId, + claim_token: context.claimToken, + from_state: context.fromState, + to_state: context.toState, + attempt_count: context.attemptCount, + failure_code: context.failureCode ?? null, + actor_user_id: context.actorUserId, + actor_resolution_mode: context.actorResolutionMode, + }); +} + +function classifyLaunchFailureMessage( + errorMessage: string | undefined +): LaunchFailureClassification { + const message = errorMessage?.toLowerCase() ?? ''; + if (message.includes('timeout') || message.includes('timed out')) { + return { code: 'NETWORK_TIMEOUT', class: 'retryable' }; + } + if ( + message.includes('502') || + message.includes('503') || + message.includes('504') || + message.includes('5xx') || + message.includes('upstream') + ) { + return { code: 'UPSTREAM_5XX', class: 'retryable' }; + } + if ( + message.includes('permission denied') || + message.includes('forbidden') || + message.includes('403') + ) { + return { code: 'PERMISSION_DENIED_PERMANENT', class: 'non_retryable' }; + } + if (message.includes('invalid config') || message.includes('invalid configuration')) { + return { code: 'INVALID_CONFIG', class: 'non_retryable' }; + } + if (message.includes('unsupported severity')) { + return { code: 'UNSUPPORTED_SEVERITY', class: 'non_retryable' }; + } + return { code: 'START_CALL_AMBIGUOUS', class: 'retryable' }; +} + +function classifyLaunchStartResult(startResult: { + started: boolean; + error?: string; +}): LaunchFailureClassification { + if (startResult.error === 'Analysis already in progress') { + return { code: 'SKIPPED_ALREADY_IN_PROGRESS', class: 'non_retryable' }; + } + if (startResult.error?.includes("analysis requires 'open' status")) { + return { code: 'SKIPPED_NO_LONGER_ELIGIBLE', class: 'non_retryable' }; + } + + return classifyLaunchFailureMessage(startResult.error); +} + +function classifyLaunchException(error: unknown): LaunchFailureClassification { + if (error instanceof InsufficientCreditsError) { + return { code: 'INSUFFICIENT_CREDITS', class: 'credit_gated' }; + } + + const message = error instanceof Error ? error.message : String(error); + return classifyLaunchFailureMessage(message); +} + +function ownerFromQueueRow(row: ClaimedQueueRow): QueueOwner | null { + if (row.owned_by_organization_id) { + return { type: 'org', id: row.owned_by_organization_id }; + } + if (row.owned_by_user_id) { + return { type: 'user', id: row.owned_by_user_id }; + } + + return null; +} + +function getSeverityRankForAutoAnalysis(severity: string | null): number | null { + if (severity === 'critical') return 0; + if (severity === 'high') return 1; + if (severity === 'medium') return 2; + if (severity === 'low') return 3; + return null; +} + +function maxSeverityRankForThreshold( + minSeverity: SecurityAgentConfig['auto_analysis_min_severity'] +): number { + if (minSeverity === 'critical') return 0; + if (minSeverity === 'high') return 1; + if (minSeverity === 'medium') return 2; + return 3; +} + +function isEligibleForAutoLaunch(params: { + findingCreatedAt: string; + findingStatus: string; + findingSeverity: string | null; + autoAnalysisEnabledAt: string | null; + config: SecurityAgentConfig; + isAgentEnabled: boolean; +}): boolean { + if (!params.isAgentEnabled || !params.config.auto_analysis_enabled) { + return false; + } + if (params.findingStatus !== 'open') { + return false; + } + if (!params.autoAnalysisEnabledAt) { + return false; + } + if (Date.parse(params.findingCreatedAt) < Date.parse(params.autoAnalysisEnabledAt)) { + return false; + } + + const severityRank = getSeverityRankForAutoAnalysis(params.findingSeverity); + if (severityRank == null) { + return false; + } + + return severityRank <= maxSeverityRankForThreshold(params.config.auto_analysis_min_severity); +} + +function nextRetryAt(attemptCount: number): Date { + const baseDelayMs = 15_000 * 2 ** Math.max(0, attemptCount); + const cappedDelayMs = Math.min(15 * 60 * 1000, baseDelayMs); + const jitterMs = Math.round(cappedDelayMs * Math.random() * 0.2); + return new Date(Date.now() + cappedDelayMs + jitterMs); +} + +async function markQueuePendingState(params: TransitionParams): Promise { + const result = await updateQueueFromPending(params.db, { + rowId: params.rowId, + claimToken: params.claimToken, + status: params.status, + failureCode: params.failureCode ?? null, + errorMessage: params.errorMessage ?? null, + incrementAttempt: params.incrementAttempt ?? false, + nextRetryAt: params.nextRetryAt ? params.nextRetryAt.toISOString() : null, + }); + + const attemptCount = + params.logContext?.attemptCount !== undefined + ? params.logContext.attemptCount + : (result.attemptCount ?? undefined); + + logQueueTransition({ + queueId: params.rowId, + fromState: params.logContext?.fromState ?? 'pending', + toState: params.status, + failureCode: params.failureCode ?? null, + jobId: params.logContext?.jobId, + owner: params.logContext?.owner, + findingId: params.logContext?.findingId, + claimToken: params.claimToken, + attemptCount, + actorUserId: params.logContext?.actorUserId, + actorResolutionMode: params.logContext?.actorResolutionMode, + }); + + if (!result.updated) { + logger.warn('Queue transition was not applied due to guard mismatch', { + queue_id: params.rowId, + claim_token: params.claimToken, + desired_status: params.status, + }); + } +} + +async function resolveGitHubToken(params: { + env: CloudflareEnv; + owner: QueueOwner; + actorUserId: string; + githubRepo: string; +}): Promise { + const lookup = await params.env.GIT_TOKEN_SERVICE.getTokenForRepo({ + githubRepo: params.githubRepo, + userId: params.actorUserId, + orgId: params.owner.type === 'org' ? params.owner.id : undefined, + }); + + if (!lookup.success) { + if (lookup.reason === 'database_not_configured') { + throw new Error('database_not_configured'); + } + + return undefined; + } + + return lookup.token; +} + +async function processOwnerMessage(params: { + env: CloudflareEnv; + owner: QueueOwner; + dispatchId: string; +}): Promise { + const db = getWorkerDb(params.env.HYPERDRIVE.connectionString, { statement_timeout: 30_000 }); + const jobId = randomUUID(); + const counters: ProcessCounters = { + processed: 0, + launched: 0, + completed: 0, + failed: 0, + requeued: 0, + skipped: 0, + }; + + const claim = await claimRowsForOwner(db, { + owner: params.owner, + jobId, + maxPerOwner: AUTO_ANALYSIS_OWNER_CAP, + }); + + for (const claimedRow of claim.rows) { + logQueueTransition({ + jobId, + owner: params.owner, + findingId: claimedRow.finding_id, + queueId: claimedRow.id, + claimToken: claimedRow.claim_token, + fromState: 'queued', + toState: 'pending', + attemptCount: claimedRow.attempt_count, + }); + } + + if (claim.rows.length === 0) { + return counters; + } + + const [nextAuthSecret, internalApiSecret] = await Promise.all([ + params.env.NEXTAUTH_SECRET.get(), + params.env.INTERNAL_API_SECRET.get(), + ]); + + ownerLoop: for (const [rowIndex, row] of claim.rows.entries()) { + counters.processed += 1; + + try { + const finding = await getSecurityFindingById(db, row.finding_id); + if (!finding) { + await markQueuePendingState({ + db, + rowId: row.id, + claimToken: row.claim_token, + status: 'failed', + failureCode: 'STATE_GUARD_REJECTED', + errorMessage: 'Finding missing while processing auto-analysis queue', + logContext: { + jobId, + owner: params.owner, + findingId: row.finding_id, + attemptCount: row.attempt_count, + }, + }); + counters.failed += 1; + continue; + } + + const stillEligible = isEligibleForAutoLaunch({ + findingCreatedAt: finding.created_at, + findingStatus: finding.status, + findingSeverity: finding.severity, + autoAnalysisEnabledAt: claim.autoAnalysisEnabledAt, + config: claim.config, + isAgentEnabled: claim.isAgentEnabled, + }); + + if (!stillEligible) { + await markQueuePendingState({ + db, + rowId: row.id, + claimToken: row.claim_token, + status: 'completed', + failureCode: 'SKIPPED_NO_LONGER_ELIGIBLE', + logContext: { + jobId, + owner: params.owner, + findingId: row.finding_id, + attemptCount: row.attempt_count, + }, + }); + counters.completed += 1; + continue; + } + + const launchOwner = ownerFromQueueRow(row); + if (!launchOwner) { + await markQueuePendingState({ + db, + rowId: row.id, + claimToken: row.claim_token, + status: 'failed', + failureCode: 'MISSING_OWNERSHIP', + logContext: { + jobId, + owner: params.owner, + findingId: row.finding_id, + attemptCount: row.attempt_count, + }, + }); + counters.failed += 1; + continue; + } + + const actorResolution = await resolveAutoAnalysisActor(db, launchOwner); + if (!actorResolution) { + await markOwnerActorResolutionFailure(db, launchOwner); + + await markQueuePendingState({ + db, + rowId: row.id, + claimToken: row.claim_token, + status: 'queued', + failureCode: 'ACTOR_RESOLUTION_FAILED', + nextRetryAt: new Date(Date.now() + 30 * 60 * 1000), + logContext: { + jobId, + owner: launchOwner, + findingId: row.finding_id, + attemptCount: row.attempt_count, + }, + }); + counters.requeued += 1; + + for (const remainingRow of claim.rows.slice(rowIndex + 1)) { + await markQueuePendingState({ + db, + rowId: remainingRow.id, + claimToken: remainingRow.claim_token, + status: 'queued', + failureCode: 'ACTOR_RESOLUTION_FAILED', + nextRetryAt: new Date(Date.now() + 30 * 60 * 1000), + logContext: { + jobId, + owner: launchOwner, + findingId: remainingRow.finding_id, + attemptCount: remainingRow.attempt_count, + }, + }); + counters.requeued += 1; + } + + break ownerLoop; + } + + await clearOwnerActorResolutionFailure(db, launchOwner); + + let githubToken: string | undefined; + try { + githubToken = await resolveGitHubToken({ + env: params.env, + owner: launchOwner, + actorUserId: actorResolution.user.id, + githubRepo: finding.repo_full_name, + }); + } catch (error) { + await markQueuePendingState({ + db, + rowId: row.id, + claimToken: row.claim_token, + status: 'queued', + failureCode: 'TEMP_TOKEN_FAILURE', + errorMessage: error instanceof Error ? error.message : String(error), + nextRetryAt: nextRetryAt(row.attempt_count + 1), + logContext: { + jobId, + owner: launchOwner, + findingId: row.finding_id, + attemptCount: row.attempt_count, + actorUserId: actorResolution.user.id, + actorResolutionMode: actorResolution.mode, + }, + }); + counters.requeued += 1; + continue; + } + + if (!githubToken) { + const nextAttemptCount = row.attempt_count + 1; + const terminal = nextAttemptCount >= AUTO_ANALYSIS_MAX_ATTEMPTS; + await markQueuePendingState({ + db, + rowId: row.id, + claimToken: row.claim_token, + status: terminal ? 'failed' : 'queued', + failureCode: 'GITHUB_TOKEN_UNAVAILABLE', + incrementAttempt: true, + nextRetryAt: terminal ? undefined : nextRetryAt(nextAttemptCount), + logContext: { + jobId, + owner: launchOwner, + findingId: row.finding_id, + attemptCount: nextAttemptCount, + actorUserId: actorResolution.user.id, + actorResolutionMode: actorResolution.mode, + }, + }); + if (terminal) { + counters.failed += 1; + } else { + counters.requeued += 1; + } + continue; + } + + const startResult = await startSecurityAnalysis({ + db, + env: params.env, + findingId: finding.id, + actorUser: actorResolution.user, + githubToken, + model: claim.config.model_slug ?? 'anthropic/claude-opus-4.6', + analysisMode: claim.config.analysis_mode, + organizationId: launchOwner.type === 'org' ? launchOwner.id : undefined, + nextAuthSecret, + internalApiSecret, + }); + + if (startResult.started) { + if (startResult.triageOnly) { + await markQueuePendingState({ + db, + rowId: row.id, + claimToken: row.claim_token, + status: 'completed', + incrementAttempt: true, + logContext: { + jobId, + owner: launchOwner, + findingId: row.finding_id, + attemptCount: row.attempt_count + 1, + actorUserId: actorResolution.user.id, + actorResolutionMode: actorResolution.mode, + }, + }); + counters.completed += 1; + } else { + await markQueuePendingState({ + db, + rowId: row.id, + claimToken: row.claim_token, + status: 'running', + incrementAttempt: true, + logContext: { + jobId, + owner: launchOwner, + findingId: row.finding_id, + attemptCount: row.attempt_count + 1, + actorUserId: actorResolution.user.id, + actorResolutionMode: actorResolution.mode, + }, + }); + counters.launched += 1; + } + continue; + } + + const classification = classifyLaunchStartResult(startResult); + if (classification.code === 'SKIPPED_ALREADY_IN_PROGRESS') { + await markQueuePendingState({ + db, + rowId: row.id, + claimToken: row.claim_token, + status: 'completed', + failureCode: classification.code, + logContext: { + jobId, + owner: launchOwner, + findingId: row.finding_id, + attemptCount: row.attempt_count, + actorUserId: actorResolution.user.id, + actorResolutionMode: actorResolution.mode, + }, + }); + counters.skipped += 1; + continue; + } + + const nextAttemptCount = row.attempt_count + 1; + const isRetryable = classification.class === 'retryable'; + const terminal = !isRetryable || nextAttemptCount >= AUTO_ANALYSIS_MAX_ATTEMPTS; + + if (terminal) { + await markQueuePendingState({ + db, + rowId: row.id, + claimToken: row.claim_token, + status: 'failed', + failureCode: classification.code, + errorMessage: startResult.error, + incrementAttempt: true, + logContext: { + jobId, + owner: launchOwner, + findingId: row.finding_id, + attemptCount: nextAttemptCount, + actorUserId: actorResolution.user.id, + actorResolutionMode: actorResolution.mode, + }, + }); + counters.failed += 1; + } else { + await markQueuePendingState({ + db, + rowId: row.id, + claimToken: row.claim_token, + status: 'queued', + failureCode: classification.code, + errorMessage: startResult.error, + incrementAttempt: true, + nextRetryAt: nextRetryAt(nextAttemptCount), + logContext: { + jobId, + owner: launchOwner, + findingId: row.finding_id, + attemptCount: nextAttemptCount, + actorUserId: actorResolution.user.id, + actorResolutionMode: actorResolution.mode, + }, + }); + counters.requeued += 1; + } + } catch (error) { + const classification = classifyLaunchException(error); + const catchOwner = ownerFromQueueRow(row) ?? params.owner; + + if (classification.class === 'credit_gated') { + await markOwnerCreditFailure(db, catchOwner); + + await markQueuePendingState({ + db, + rowId: row.id, + claimToken: row.claim_token, + status: 'queued', + failureCode: classification.code, + errorMessage: error instanceof Error ? error.message : String(error), + nextRetryAt: new Date(Date.now() + 30 * 60 * 1000), + logContext: { + jobId, + owner: catchOwner, + findingId: row.finding_id, + attemptCount: row.attempt_count, + }, + }); + + counters.requeued += 1; + + for (const remainingRow of claim.rows.slice(rowIndex + 1)) { + await markQueuePendingState({ + db, + rowId: remainingRow.id, + claimToken: remainingRow.claim_token, + status: 'queued', + failureCode: classification.code, + nextRetryAt: new Date(Date.now() + 30 * 60 * 1000), + logContext: { + jobId, + owner: catchOwner, + findingId: remainingRow.finding_id, + attemptCount: remainingRow.attempt_count, + }, + }); + counters.requeued += 1; + } + break; + } + + const nextAttemptCount = row.attempt_count + 1; + const terminal = + classification.class !== 'retryable' || nextAttemptCount >= AUTO_ANALYSIS_MAX_ATTEMPTS; + + if (terminal) { + await markQueuePendingState({ + db, + rowId: row.id, + claimToken: row.claim_token, + status: 'failed', + failureCode: classification.code, + errorMessage: error instanceof Error ? error.message : String(error), + incrementAttempt: true, + logContext: { + jobId, + owner: catchOwner, + findingId: row.finding_id, + attemptCount: nextAttemptCount, + }, + }); + counters.failed += 1; + } else { + await markQueuePendingState({ + db, + rowId: row.id, + claimToken: row.claim_token, + status: 'queued', + failureCode: classification.code, + errorMessage: error instanceof Error ? error.message : String(error), + incrementAttempt: true, + nextRetryAt: nextRetryAt(nextAttemptCount), + logContext: { + jobId, + owner: catchOwner, + findingId: row.finding_id, + attemptCount: nextAttemptCount, + }, + }); + counters.requeued += 1; + } + } + } + + logger.info('Processed owner auto-analysis message', { + dispatch_id: params.dispatchId, + job_id: jobId, + owner_type: params.owner.type, + owner_id: params.owner.id, + ...counters, + }); + + return counters; +} + +export async function consumeOwnerBatch( + batch: MessageBatch, + env: CloudflareEnv +): Promise { + for (const message of batch.messages) { + const parsedMessage = AutoAnalysisOwnerMessageSchema.safeParse(message.body); + if (!parsedMessage.success) { + logger.error('Invalid owner queue message shape', { + queue: batch.queue, + error: parsedMessage.error.message, + }); + message.ack(); + continue; + } + + const owner: QueueOwner = + parsedMessage.data.ownerType === 'org' + ? { type: 'org', id: parsedMessage.data.ownerId } + : { type: 'user', id: parsedMessage.data.ownerId }; + + try { + await processOwnerMessage({ + env, + owner, + dispatchId: parsedMessage.data.dispatchId, + }); + + message.ack(); + } catch (error) { + logger.error('Failed processing owner queue message', { + queue: batch.queue, + owner_type: owner.type, + owner_id: owner.id, + attempts: message.attempts, + error: error instanceof Error ? error.message : String(error), + }); + + message.retry(); + } + } +} diff --git a/cloudflare-security-auto-analysis/src/db/queries.ts b/cloudflare-security-auto-analysis/src/db/queries.ts new file mode 100644 index 000000000..cd6b26304 --- /dev/null +++ b/cloudflare-security-auto-analysis/src/db/queries.ts @@ -0,0 +1,566 @@ +import type { WorkerDb } from '@kilocode/db/client'; +import { + security_analysis_queue, + security_analysis_owner_state, + security_findings, + agent_configs, + kilocode_users, + organization_memberships, +} from '@kilocode/db/schema'; +import { and, asc, count, eq, inArray, isNull, lte, or, sql } from 'drizzle-orm'; +import type { + AnalysisMode, + QueueOwner, + SecurityAgentConfig, + SecurityFindingAnalysis, +} from '../types.js'; +import { + AUTO_ANALYSIS_OWNER_CAP, + DEFAULT_SECURITY_AGENT_CONFIG, + SecurityAgentConfigSchema, + SECURITY_ANALYSIS_OWNER_CAP, +} from '../types.js'; + +export type ClaimedQueueRow = { + id: string; + finding_id: string; + claim_token: string; + attempt_count: number; + owned_by_organization_id: string | null; + owned_by_user_id: string | null; +}; + +export type ActorUser = { + id: string; + api_token_pepper: string | null; +}; + +type ClaimRowsForOwnerResult = { + rows: ClaimedQueueRow[]; + config: SecurityAgentConfig; + isAgentEnabled: boolean; + autoAnalysisEnabledAt: string | null; + blocked: boolean; +}; + +function parseSecurityConfig(config: unknown): SecurityAgentConfig { + let configValue: unknown = config; + + if (typeof configValue === 'string') { + try { + configValue = JSON.parse(configValue); + } catch { + configValue = {}; + } + } + + const parsed = SecurityAgentConfigSchema.partial().safeParse(configValue ?? {}); + if (!parsed.success) { + return DEFAULT_SECURITY_AGENT_CONFIG; + } + + return { + ...DEFAULT_SECURITY_AGENT_CONFIG, + ...parsed.data, + }; +} + +function ownerWhereQueue(owner: QueueOwner) { + return owner.type === 'org' + ? eq(security_analysis_queue.owned_by_organization_id, owner.id) + : eq(security_analysis_queue.owned_by_user_id, owner.id); +} + +function ownerWhereFindings(owner: QueueOwner) { + return owner.type === 'org' + ? eq(security_findings.owned_by_organization_id, owner.id) + : eq(security_findings.owned_by_user_id, owner.id); +} + +function ownerWhereOwnerState(owner: QueueOwner) { + return owner.type === 'org' + ? eq(security_analysis_owner_state.owned_by_organization_id, owner.id) + : eq(security_analysis_owner_state.owned_by_user_id, owner.id); +} + +export async function discoverDueOwners(db: WorkerDb, limit: number): Promise { + const rows = await db + .selectDistinct({ + owned_by_organization_id: security_analysis_queue.owned_by_organization_id, + owned_by_user_id: security_analysis_queue.owned_by_user_id, + }) + .from(security_analysis_queue) + .leftJoin( + security_analysis_owner_state, + or( + and( + sql`${security_analysis_queue.owned_by_organization_id} IS NOT NULL`, + eq( + security_analysis_queue.owned_by_organization_id, + security_analysis_owner_state.owned_by_organization_id + ) + ), + and( + sql`${security_analysis_queue.owned_by_user_id} IS NOT NULL`, + eq( + security_analysis_queue.owned_by_user_id, + security_analysis_owner_state.owned_by_user_id + ) + ) + ) + ) + .where( + and( + eq(security_analysis_queue.queue_status, 'queued'), + lte( + sql`coalesce(${security_analysis_queue.next_retry_at}, '-infinity'::timestamptz)`, + sql`now()` + ), + or( + isNull(security_analysis_owner_state.blocked_until), + lte(security_analysis_owner_state.blocked_until, sql`now()`) + ) + ) + ) + .orderBy( + asc(security_analysis_queue.owned_by_organization_id), + asc(security_analysis_queue.owned_by_user_id) + ) + .limit(limit); + + const owners: QueueOwner[] = []; + for (const row of rows) { + if (row.owned_by_organization_id) { + owners.push({ type: 'org', id: row.owned_by_organization_id }); + } else if (row.owned_by_user_id) { + owners.push({ type: 'user', id: row.owned_by_user_id }); + } + } + return owners; +} + +export async function claimRowsForOwner( + db: WorkerDb, + params: { owner: QueueOwner; jobId: string; maxPerOwner: number } +): Promise { + const orgId = params.owner.type === 'org' ? params.owner.id : null; + const userId = params.owner.type === 'user' ? params.owner.id : null; + + return db.transaction(async tx => { + await tx + .insert(security_analysis_owner_state) + .values({ + owned_by_organization_id: orgId, + owned_by_user_id: userId, + }) + .onConflictDoNothing(); + + const stateRows = await tx + .select({ + blocked_until: security_analysis_owner_state.blocked_until, + auto_analysis_enabled_at: security_analysis_owner_state.auto_analysis_enabled_at, + }) + .from(security_analysis_owner_state) + .where(ownerWhereOwnerState(params.owner)) + .for('update') + .limit(1); + + const state = stateRows[0] ?? { blocked_until: null, auto_analysis_enabled_at: null }; + + const blocked = + state.blocked_until !== null && Number.isFinite(Date.parse(state.blocked_until)) + ? Date.parse(state.blocked_until) > Date.now() + : false; + + const configRows = await tx + .select({ + config: agent_configs.config, + is_enabled: agent_configs.is_enabled, + }) + .from(agent_configs) + .where( + and( + eq(agent_configs.agent_type, 'security_scan'), + eq(agent_configs.platform, 'github'), + params.owner.type === 'org' + ? eq(agent_configs.owned_by_organization_id, params.owner.id) + : eq(agent_configs.owned_by_user_id, params.owner.id) + ) + ) + .limit(1); + + const parsedConfig = parseSecurityConfig(configRows[0]?.config); + const isAgentEnabled = configRows[0]?.is_enabled ?? false; + + const emptyResult: ClaimRowsForOwnerResult = { + rows: [], + config: parsedConfig, + isAgentEnabled, + autoAnalysisEnabledAt: state.auto_analysis_enabled_at, + blocked, + }; + + if (!isAgentEnabled || !parsedConfig.auto_analysis_enabled || blocked) { + return emptyResult; + } + + const [totalInflightResult] = await tx + .select({ total: count() }) + .from(security_findings) + .where( + and( + ownerWhereFindings(params.owner), + inArray(security_findings.analysis_status, ['pending', 'running']) + ) + ); + + const [autoInflightResult] = await tx + .select({ total: count() }) + .from(security_analysis_queue) + .where( + and( + ownerWhereQueue(params.owner), + inArray(security_analysis_queue.queue_status, ['pending', 'running']) + ) + ); + + const totalInflight = totalInflightResult?.total ?? 0; + const autoInflight = autoInflightResult?.total ?? 0; + + const availableByTotal = Math.max(0, SECURITY_ANALYSIS_OWNER_CAP - totalInflight); + const availableByAuto = Math.max(0, AUTO_ANALYSIS_OWNER_CAP - autoInflight); + const claimLimit = Math.min(params.maxPerOwner, availableByTotal, availableByAuto); + + if (claimLimit <= 0) { + return emptyResult; + } + + const claimedRows = await tx.execute(sql` + UPDATE security_analysis_queue + SET + queue_status = 'pending', + claimed_at = now(), + claimed_by_job_id = ${params.jobId}, + claim_token = gen_random_uuid()::text, + updated_at = now() + WHERE id IN ( + SELECT id + FROM security_analysis_queue + WHERE queue_status = 'queued' + AND ${ + params.owner.type === 'org' + ? sql`owned_by_organization_id = ${orgId}::uuid` + : sql`owned_by_user_id = ${userId}` + } + AND coalesce(next_retry_at, '-infinity'::timestamptz) <= now() + ORDER BY severity_rank ASC, queued_at ASC, id ASC + LIMIT ${claimLimit} + FOR UPDATE SKIP LOCKED + ) + RETURNING + id, + finding_id, + claim_token, + attempt_count, + owned_by_organization_id::text, + owned_by_user_id + `); + + return { + rows: claimedRows.rows, + config: parsedConfig, + isAgentEnabled, + autoAnalysisEnabledAt: state.auto_analysis_enabled_at, + blocked, + }; + }); +} + +export async function updateQueueFromPending( + db: WorkerDb, + params: { + rowId: string; + claimToken: string; + status: 'queued' | 'running' | 'completed' | 'failed'; + failureCode: string | null; + errorMessage: string | null; + incrementAttempt: boolean; + nextRetryAt: string | null; + } +): Promise<{ updated: boolean; attemptCount: number | null }> { + const rows = await db.execute<{ attempt_count: number }>(sql` + UPDATE security_analysis_queue + SET + queue_status = ${params.status}, + attempt_count = CASE + WHEN ${params.incrementAttempt} THEN attempt_count + 1 + ELSE attempt_count + END, + failure_code = ${params.failureCode}, + last_error_redacted = ${params.errorMessage}, + next_retry_at = ${params.nextRetryAt}, + claimed_at = CASE + WHEN ${params.status} = 'queued' THEN NULL + ELSE claimed_at + END, + claimed_by_job_id = CASE + WHEN ${params.status} = 'queued' THEN NULL + ELSE claimed_by_job_id + END, + claim_token = CASE + WHEN ${params.status} = 'queued' THEN NULL + ELSE claim_token + END, + updated_at = now() + WHERE id = ${params.rowId}::uuid + AND queue_status = 'pending' + AND claim_token = ${params.claimToken} + RETURNING attempt_count + `); + + const updatedRow = rows.rows[0]; + return { + updated: updatedRow !== undefined, + attemptCount: updatedRow?.attempt_count ?? null, + }; +} + +export async function resolveAutoAnalysisActor( + db: WorkerDb, + owner: QueueOwner +): Promise<{ user: ActorUser; mode: 'owner' | 'member_fallback' } | null> { + if (owner.type === 'user') { + const rows = await db + .select({ + id: kilocode_users.id, + api_token_pepper: kilocode_users.api_token_pepper, + }) + .from(kilocode_users) + .where(and(eq(kilocode_users.id, owner.id), isNull(kilocode_users.blocked_reason))) + .limit(1); + + const user = rows[0]; + return user ? { user, mode: 'owner' } : null; + } + + // Org: try owner first + const ownerRows = await db + .select({ + id: kilocode_users.id, + api_token_pepper: kilocode_users.api_token_pepper, + }) + .from(organization_memberships) + .innerJoin(kilocode_users, eq(kilocode_users.id, organization_memberships.kilo_user_id)) + .where( + and( + eq(organization_memberships.organization_id, owner.id), + eq(organization_memberships.role, 'owner'), + isNull(kilocode_users.blocked_reason) + ) + ) + .orderBy(asc(kilocode_users.created_at), asc(kilocode_users.id)) + .limit(1); + + const ownerUser = ownerRows[0]; + if (ownerUser) { + return { user: ownerUser, mode: 'owner' }; + } + + // Org: fall back to first member + const memberRows = await db + .select({ + id: kilocode_users.id, + api_token_pepper: kilocode_users.api_token_pepper, + }) + .from(organization_memberships) + .innerJoin(kilocode_users, eq(kilocode_users.id, organization_memberships.kilo_user_id)) + .where( + and( + eq(organization_memberships.organization_id, owner.id), + eq(organization_memberships.role, 'member'), + isNull(kilocode_users.blocked_reason) + ) + ) + .orderBy(asc(kilocode_users.created_at), asc(kilocode_users.id)) + .limit(1); + + const memberUser = memberRows[0]; + return memberUser ? { user: memberUser, mode: 'member_fallback' } : null; +} + +export async function markOwnerActorResolutionFailure( + db: WorkerDb, + owner: QueueOwner +): Promise { + await db + .update(security_analysis_owner_state) + .set({ + block_reason: 'ACTOR_RESOLUTION_FAILED', + blocked_until: sql`now() + interval '30 minutes'`.mapWith(String), + consecutive_actor_resolution_failures: sql`${security_analysis_owner_state.consecutive_actor_resolution_failures} + 1`, + last_actor_resolution_failure_at: sql`now()`.mapWith(String), + }) + .where(ownerWhereOwnerState(owner)); +} + +export async function clearOwnerActorResolutionFailure( + db: WorkerDb, + owner: QueueOwner +): Promise { + await db.execute(sql` + UPDATE security_analysis_owner_state + SET + consecutive_actor_resolution_failures = 0, + last_actor_resolution_failure_at = NULL, + blocked_until = CASE + WHEN block_reason = 'ACTOR_RESOLUTION_FAILED' THEN NULL + ELSE blocked_until + END, + block_reason = CASE + WHEN block_reason = 'ACTOR_RESOLUTION_FAILED' THEN NULL + ELSE block_reason + END, + updated_at = now() + WHERE ${ownerWhereOwnerState(owner)} + `); +} + +export async function markOwnerCreditFailure(db: WorkerDb, owner: QueueOwner): Promise { + await db + .update(security_analysis_owner_state) + .set({ + blocked_until: sql`now() + interval '30 minutes'`.mapWith(String), + block_reason: 'INSUFFICIENT_CREDITS', + }) + .where(ownerWhereOwnerState(owner)); +} + +export async function getSecurityFindingById(db: WorkerDb, findingId: string) { + const rows = await db + .select({ + id: security_findings.id, + repo_full_name: security_findings.repo_full_name, + created_at: security_findings.created_at, + status: security_findings.status, + severity: security_findings.severity, + package_name: security_findings.package_name, + package_ecosystem: security_findings.package_ecosystem, + dependency_scope: security_findings.dependency_scope, + cve_id: security_findings.cve_id, + ghsa_id: security_findings.ghsa_id, + title: security_findings.title, + description: security_findings.description, + vulnerable_version_range: security_findings.vulnerable_version_range, + patched_version: security_findings.patched_version, + manifest_path: security_findings.manifest_path, + raw_data: security_findings.raw_data, + analysis_status: security_findings.analysis_status, + owned_by_organization_id: security_findings.owned_by_organization_id, + owned_by_user_id: security_findings.owned_by_user_id, + }) + .from(security_findings) + .where(eq(security_findings.id, findingId)) + .limit(1); + + return rows[0] ?? null; +} + +export type SecurityFindingRecord = NonNullable>>; + +export async function tryAcquireAnalysisStartLease( + db: WorkerDb, + findingId: string +): Promise { + const rows = await db + .update(security_findings) + .set({ + analysis_status: 'pending', + updated_at: sql`now()`.mapWith(String), + }) + .where( + and( + eq(security_findings.id, findingId), + eq(security_findings.status, 'open'), + or( + isNull(security_findings.analysis_status), + eq(security_findings.analysis_status, 'completed'), + eq(security_findings.analysis_status, 'failed') + ) + ) + ) + .returning({ id: security_findings.id }); + + return rows.length > 0; +} + +export async function setFindingPending( + db: WorkerDb, + findingId: string, + analysis: SecurityFindingAnalysis | null +): Promise { + await db + .update(security_findings) + .set({ + analysis_status: 'pending', + analysis_error: null, + analysis: analysis ? sql`${JSON.stringify(analysis)}::jsonb` : null, + analysis_completed_at: null, + session_id: null, + cli_session_id: null, + }) + .where(eq(security_findings.id, findingId)); +} + +export async function setFindingRunning( + db: WorkerDb, + findingId: string, + cloudAgentSessionId: string, + kiloSessionId: string +): Promise { + await db + .update(security_findings) + .set({ + analysis_status: 'running', + session_id: cloudAgentSessionId, + cli_session_id: kiloSessionId, + analysis_started_at: sql`coalesce(${security_findings.analysis_started_at}, now())`.mapWith( + String + ), + }) + .where(eq(security_findings.id, findingId)); +} + +export async function setFindingCompleted( + db: WorkerDb, + findingId: string, + analysis: SecurityFindingAnalysis +): Promise { + await db + .update(security_findings) + .set({ + analysis_status: 'completed', + analysis: sql`${JSON.stringify(analysis)}::jsonb`, + analysis_error: null, + analysis_completed_at: sql`now()`.mapWith(String), + }) + .where(eq(security_findings.id, findingId)); +} + +export async function setFindingFailed( + db: WorkerDb, + findingId: string, + errorMessage: string +): Promise { + await db + .update(security_findings) + .set({ + analysis_status: 'failed', + analysis_error: errorMessage, + analysis_completed_at: sql`now()`.mapWith(String), + }) + .where(eq(security_findings.id, findingId)); +} + +export function sanitizeAnalysisMode(mode: AnalysisMode | undefined): AnalysisMode { + return mode ?? DEFAULT_SECURITY_AGENT_CONFIG.analysis_mode; +} diff --git a/cloudflare-security-auto-analysis/src/dispatcher.ts b/cloudflare-security-auto-analysis/src/dispatcher.ts new file mode 100644 index 000000000..1b112458a --- /dev/null +++ b/cloudflare-security-auto-analysis/src/dispatcher.ts @@ -0,0 +1,44 @@ +import { randomUUID } from 'crypto'; +import { getWorkerDb } from '@kilocode/db/client'; +import { discoverDueOwners } from './db/queries.js'; +import { logger } from './logger.js'; + +const DISPATCH_OWNER_LIMIT = 100; + +export async function dispatchDueOwners(env: CloudflareEnv): Promise<{ + dispatchId: string; + discoveredOwners: number; + enqueuedMessages: number; +}> { + const dispatchId = randomUUID(); + const db = getWorkerDb(env.HYPERDRIVE.connectionString, { statement_timeout: 30_000 }); + + const owners = await discoverDueOwners(db, DISPATCH_OWNER_LIMIT); + + const messages = owners.map(owner => ({ + body: { + ownerType: owner.type, + ownerId: owner.id, + dispatchId, + enqueuedAt: new Date().toISOString(), + }, + contentType: 'json' as const, + })); + + const QUEUE_SEND_BATCH_LIMIT = 100; + for (let i = 0; i < messages.length; i += QUEUE_SEND_BATCH_LIMIT) { + await env.OWNER_QUEUE.sendBatch(messages.slice(i, i + QUEUE_SEND_BATCH_LIMIT)); + } + + logger.info('Dispatched due owners to queue', { + dispatch_id: dispatchId, + discovered_owners: owners.length, + enqueued_messages: messages.length, + }); + + return { + dispatchId, + discoveredOwners: owners.length, + enqueuedMessages: messages.length, + }; +} diff --git a/cloudflare-security-auto-analysis/src/index.ts b/cloudflare-security-auto-analysis/src/index.ts new file mode 100644 index 000000000..ee4686a0e --- /dev/null +++ b/cloudflare-security-auto-analysis/src/index.ts @@ -0,0 +1,59 @@ +import { timingSafeEqual as nodeTSE } from 'crypto'; +import { consumeOwnerBatch } from './consumer.js'; +import { dispatchDueOwners } from './dispatcher.js'; + +/** + * Constant-time string equality that does not leak either string's length. + * Both inputs are hashed first so the comparison is always on equal-length digests. + */ +async function timingSafeEqual(a: string, b: string): Promise { + const enc = new TextEncoder(); + const [digestA, digestB] = await Promise.all([ + crypto.subtle.digest('SHA-256', enc.encode(a)), + crypto.subtle.digest('SHA-256', enc.encode(b)), + ]); + return nodeTSE(new Uint8Array(digestA), new Uint8Array(digestB)); +} + +async function handleFetch(request: Request, env: CloudflareEnv): Promise { + const url = new URL(request.url); + + if (request.method === 'GET' && url.pathname === '/health') { + return Response.json({ + status: 'ok', + service: 'security-auto-analysis', + timestamp: new Date().toISOString(), + }); + } + + if (request.method === 'POST' && url.pathname === '/internal/dispatch') { + const internalSecret = await env.INTERNAL_API_SECRET.get(); + const authHeader = request.headers.get('x-internal-api-key'); + + if (!authHeader || !internalSecret || !(await timingSafeEqual(authHeader, internalSecret))) { + return Response.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const result = await dispatchDueOwners(env); + return Response.json({ + success: true, + ...result, + }); + } + + return Response.json({ error: 'Not found' }, { status: 404 }); +} + +export default { + async fetch(request: Request, env: CloudflareEnv): Promise { + return handleFetch(request, env); + }, + + async scheduled(_controller: ScheduledController, env: CloudflareEnv): Promise { + await dispatchDueOwners(env); + }, + + async queue(batch: MessageBatch, env: CloudflareEnv): Promise { + await consumeOwnerBatch(batch, env); + }, +}; diff --git a/cloudflare-security-auto-analysis/src/launch.ts b/cloudflare-security-auto-analysis/src/launch.ts new file mode 100644 index 000000000..b685d7292 --- /dev/null +++ b/cloudflare-security-auto-analysis/src/launch.ts @@ -0,0 +1,273 @@ +import { randomUUID } from 'crypto'; +import { z } from 'zod'; +import type { WorkerDb } from '@kilocode/db/client'; +import { + getSecurityFindingById, + setFindingCompleted, + setFindingFailed, + setFindingPending, + setFindingRunning, + tryAcquireAnalysisStartLease, + type SecurityFindingRecord, +} from './db/queries.js'; +import { logger } from './logger.js'; +import { generateApiToken } from './token.js'; +import { triageSecurityFinding } from './triage.js'; +import type { AnalysisMode, SecurityFindingAnalysis } from './types.js'; + +export class InsufficientCreditsError extends Error { + readonly httpStatus = 402; + + constructor(message = 'Insufficient credits: $1 minimum required') { + super(message); + this.name = 'InsufficientCreditsError'; + } +} + +const PrepareSessionResponseSchema = z.object({ + result: z.object({ + data: z.object({ + cloudAgentSessionId: z.string(), + kiloSessionId: z.string(), + }), + }), +}); + +const InitiateResponseSchema = z.object({ + result: z.object({ + data: z.object({ + executionId: z.string(), + status: z.string(), + }), + }), +}); + +function buildAnalysisPrompt(finding: SecurityFindingRecord): string { + const replacements = { + packageName: finding.package_name, + packageEcosystem: finding.package_ecosystem, + severity: finding.severity ?? 'unknown', + dependencyScope: finding.dependency_scope ?? 'runtime', + cveId: finding.cve_id ?? 'N/A', + ghsaId: finding.ghsa_id ?? 'N/A', + title: finding.title, + description: finding.description ?? 'No description available', + vulnerableVersionRange: finding.vulnerable_version_range ?? 'Unknown', + patchedVersion: finding.patched_version ?? 'No patch available', + manifestPath: finding.manifest_path ?? 'Unknown', + }; + + const template = `You are a security analyst reviewing a dependency vulnerability alert for a codebase. + +## Vulnerability Details +- **Package**: {{packageName}} ({{packageEcosystem}}) +- **Severity**: {{severity}} +- **Dependency Scope**: {{dependencyScope}} +- **CVE**: {{cveId}} +- **GHSA**: {{ghsaId}} +- **Title**: {{title}} +- **Description**: {{description}} +- **Vulnerable Versions**: {{vulnerableVersionRange}} +- **Patched Version**: {{patchedVersion}} +- **Manifest Path**: {{manifestPath}} + +## Your Task + +1. Search the codebase for usages of the package. +2. Analyze relevance and whether vulnerable paths are used. +3. Determine exploitability and required attacker conditions. +4. Provide concrete remediation guidance. + +## Output Format + +Provide a markdown analysis with: +- Usage locations with file paths and line numbers +- Exploitability assessment +- Reasoning +- Suggested fix +- Brief summary`; + + return template.replace( + /\{\{(\w+)\}\}/g, + (_, key: string) => + (key in replacements ? replacements[key as keyof typeof replacements] : '') ?? '' + ); +} + +type StartSecurityAnalysisParams = { + db: WorkerDb; + env: CloudflareEnv; + findingId: string; + actorUser: { + id: string; + api_token_pepper: string | null; + }; + githubToken?: string; + model: string; + analysisMode: AnalysisMode; + organizationId?: string; + nextAuthSecret: string; + internalApiSecret: string; +}; + +export async function startSecurityAnalysis( + params: StartSecurityAnalysisParams +): Promise<{ started: boolean; error?: string; triageOnly?: boolean }> { + const correlationId = randomUUID(); + + const finding = await getSecurityFindingById(params.db, params.findingId); + if (!finding) { + return { started: false, error: `Finding not found: ${params.findingId}` }; + } + + const leaseAcquired = await tryAcquireAnalysisStartLease(params.db, params.findingId); + if (!leaseAcquired) { + if (finding.status !== 'open') { + return { + started: false, + error: `Finding status is '${finding.status}', analysis requires 'open' status`, + }; + } + return { started: false, error: 'Analysis already in progress' }; + } + + await setFindingPending(params.db, params.findingId, null); + + try { + const environment = params.env.ENVIRONMENT === 'production' ? 'production' : 'development'; + const authToken = await generateApiToken(params.actorUser, params.nextAuthSecret, environment); + const triage = await triageSecurityFinding({ + finding, + authToken, + model: params.model, + backendBaseUrl: params.env.KILOCODE_BACKEND_BASE_URL, + organizationId: params.organizationId, + }); + + const runSandbox = + params.analysisMode === 'deep' || + (params.analysisMode === 'auto' && triage.needsSandboxAnalysis); + + if (!runSandbox) { + const triageOnlyAnalysis: SecurityFindingAnalysis = { + triage, + analyzedAt: new Date().toISOString(), + modelUsed: params.model, + triggeredByUserId: params.actorUser.id, + correlationId, + }; + await setFindingCompleted(params.db, params.findingId, triageOnlyAnalysis); + return { started: true, triageOnly: true }; + } + + const partialAnalysis: SecurityFindingAnalysis = { + triage, + analyzedAt: new Date().toISOString(), + modelUsed: params.model, + triggeredByUserId: params.actorUser.id, + correlationId, + }; + + await setFindingPending(params.db, params.findingId, partialAnalysis); + + const callbackUrl = `${params.env.KILOCODE_BACKEND_BASE_URL}/api/internal/security-analysis-callback/${params.findingId}`; + + const prepareInput = { + prompt: buildAnalysisPrompt(finding), + mode: 'code', + model: params.model, + githubRepo: finding.repo_full_name, + githubToken: params.githubToken, + kilocodeOrganizationId: params.organizationId, + createdOnPlatform: 'security-agent', + callbackTarget: { + url: callbackUrl, + headers: { + 'X-Internal-Secret': params.internalApiSecret, + }, + }, + }; + + const prepareResponse = await params.env.CLOUD_AGENT_NEXT.fetch( + new Request('https://cloud-agent-next/trpc/prepareSession', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${authToken}`, + 'x-internal-api-key': params.internalApiSecret, + }, + body: JSON.stringify(prepareInput), + }) + ); + + if (!prepareResponse.ok) { + const errorText = await prepareResponse.text(); + await setFindingFailed(params.db, params.findingId, errorText); + return { started: false, error: errorText }; + } + + const parsedPrepare = PrepareSessionResponseSchema.safeParse(await prepareResponse.json()); + if (!parsedPrepare.success) { + await setFindingFailed(params.db, params.findingId, 'Invalid prepareSession response shape'); + return { started: false, error: 'Invalid prepareSession response shape' }; + } + + const { cloudAgentSessionId, kiloSessionId } = parsedPrepare.data.result.data; + await setFindingRunning(params.db, params.findingId, cloudAgentSessionId, kiloSessionId); + + const initiateResponse = await params.env.CLOUD_AGENT_NEXT.fetch( + new Request('https://cloud-agent-next/trpc/initiateFromKilocodeSessionV2', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${authToken}`, + }, + body: JSON.stringify({ cloudAgentSessionId }), + }) + ); + + if (!initiateResponse.ok) { + const errorText = await initiateResponse.text(); + await setFindingFailed(params.db, params.findingId, errorText); + + if (initiateResponse.status === 402) { + throw new InsufficientCreditsError(errorText || 'Insufficient credits'); + } + + return { + started: false, + error: errorText, + }; + } + + const parsedInitiate = InitiateResponseSchema.safeParse(await initiateResponse.json()); + if (!parsedInitiate.success) { + await setFindingFailed( + params.db, + params.findingId, + 'Invalid initiateFromKilocodeSessionV2 response shape' + ); + return { + started: false, + error: 'Invalid initiateFromKilocodeSessionV2 response shape', + }; + } + + return { started: true, triageOnly: false }; + } catch (error) { + if (error instanceof InsufficientCreditsError) { + await setFindingFailed(params.db, params.findingId, error.message); + throw error; + } + + const errorMessage = error instanceof Error ? error.message : String(error); + logger.error('startSecurityAnalysis failed', { + finding_id: params.findingId, + correlation_id: correlationId, + error: errorMessage, + }); + + await setFindingFailed(params.db, params.findingId, errorMessage); + return { started: false, error: errorMessage }; + } +} diff --git a/cloudflare-security-auto-analysis/src/logger.ts b/cloudflare-security-auto-analysis/src/logger.ts new file mode 100644 index 000000000..1ab13a988 --- /dev/null +++ b/cloudflare-security-auto-analysis/src/logger.ts @@ -0,0 +1,13 @@ +import { WorkersLogger } from 'workers-tagged-logger'; + +function getLogLevel(): 'debug' | 'info' | 'warn' | 'error' { + if (typeof process !== 'undefined' && process.env?.VITEST) { + return 'error'; + } + + return 'info'; +} + +export const logger = new WorkersLogger({ + minimumLogLevel: getLogLevel(), +}); diff --git a/cloudflare-security-auto-analysis/src/token.ts b/cloudflare-security-auto-analysis/src/token.ts new file mode 100644 index 000000000..45859e8e5 --- /dev/null +++ b/cloudflare-security-auto-analysis/src/token.ts @@ -0,0 +1,101 @@ +type JwtPayload = Record; +type JwtOptions = { + expiresIn: string; +}; + +type TokenUser = { + id: string; + api_token_pepper: string | null; +}; + +const JWT_TOKEN_VERSION = 3; + +export async function generateApiToken( + user: TokenUser, + secret: string, + environment: string +): Promise { + return signJwt( + { + env: environment, + kiloUserId: user.id, + apiTokenPepper: user.api_token_pepper, + version: JWT_TOKEN_VERSION, + internalApiUse: true, + createdOnPlatform: 'security-agent', + }, + secret, + { expiresIn: '1h' } + ); +} + +async function signJwt(payload: JwtPayload, secret: string, options: JwtOptions): Promise { + const header = { + alg: 'HS256', + typ: 'JWT', + }; + + const now = Math.floor(Date.now() / 1000); + const exp = now + parseExpiresIn(options.expiresIn); + + const encodedHeader = base64UrlEncode(JSON.stringify(header)); + const encodedPayload = base64UrlEncode( + JSON.stringify({ + ...payload, + iat: now, + exp, + }) + ); + + const signature = await hmacSha256(`${encodedHeader}.${encodedPayload}`, secret); + const encodedSignature = base64UrlEncodeBytes(new Uint8Array(signature)); + + return `${encodedHeader}.${encodedPayload}.${encodedSignature}`; +} + +function parseExpiresIn(expiresIn: string): number { + const match = expiresIn.match(/^(\d+)([smhd])$/); + if (!match) { + throw new Error(`Invalid expiresIn format: ${expiresIn}`); + } + + const value = Number.parseInt(match[1], 10); + const unit = match[2]; + + if (unit === 's') { + return value; + } + if (unit === 'm') { + return value * 60; + } + if (unit === 'h') { + return value * 60 * 60; + } + + return value * 60 * 60 * 24; +} + +function base64UrlEncodeBytes(bytes: Uint8Array): string { + const base64 = btoa(bytes.reduce((s, b) => s + String.fromCharCode(b), '')); + return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); +} + +function base64UrlEncode(value: string): string { + return base64UrlEncodeBytes(new TextEncoder().encode(value)); +} + +async function hmacSha256(message: string, secret: string): Promise { + const encoder = new TextEncoder(); + const keyData = encoder.encode(secret); + const messageData = encoder.encode(message); + + const key = await crypto.subtle.importKey( + 'raw', + keyData, + { name: 'HMAC', hash: 'SHA-256' }, + false, + ['sign'] + ); + + return crypto.subtle.sign('HMAC', key, messageData); +} diff --git a/cloudflare-security-auto-analysis/src/triage.ts b/cloudflare-security-auto-analysis/src/triage.ts new file mode 100644 index 000000000..642e07cd6 --- /dev/null +++ b/cloudflare-security-auto-analysis/src/triage.ts @@ -0,0 +1,223 @@ +import { z } from 'zod'; +import { logger } from './logger.js'; +import type { SecurityFindingRecord } from './db/queries.js'; +import type { SecurityFindingTriage } from './types.js'; + +const TRIAGE_SERVICE_VERSION = '5.0.0'; +const TRIAGE_SERVICE_USER_AGENT = `Kilo-Security-Triage/${TRIAGE_SERVICE_VERSION}`; + +const TRIAGE_SYSTEM_PROMPT = `You are a security analyst performing quick triage of dependency vulnerability alerts. + +Your task is to analyze the vulnerability metadata and determine if deeper codebase analysis is needed. + +## Triage Guidelines + +### Dismiss candidates (needsSandboxAnalysis: false, suggestedAction: 'dismiss'): +- Development dependencies with low/medium severity (test frameworks, linters, build tools) +- Vulnerabilities in packages that are clearly dev-only (jest, mocha, eslint, webpack, etc.) +- DoS vulnerabilities in CLI-only tools that don't affect production +- Low severity vulnerabilities with no known exploits + +### Needs codebase analysis (needsSandboxAnalysis: true, suggestedAction: 'analyze_codebase'): +- Runtime dependencies with high/critical severity +- RCE (Remote Code Execution) vulnerabilities +- SQL injection, XSS, or authentication bypass vulnerabilities +- Vulnerabilities in core frameworks (express, react, etc.) +- Any vulnerability where exploitability depends on how the package is used + +### Manual review (needsSandboxAnalysis: false, suggestedAction: 'manual_review'): +- Edge cases where you're uncertain +- Critical severity in dev dependencies +- Complex vulnerabilities that need human judgment + +## Confidence Levels +- high: Clear-cut case based on metadata alone +- medium: Reasonable confidence but some uncertainty +- low: Uncertain, recommend manual review + +Always err on the side of caution - if unsure, recommend codebase analysis or manual review.`; + +const TriagedResultSchema = z.object({ + needsSandboxAnalysis: z.boolean(), + needsSandboxReasoning: z.string(), + suggestedAction: z.enum(['dismiss', 'analyze_codebase', 'manual_review']), + confidence: z.enum(['high', 'medium', 'low']), +}); + +const TriageResponseSchema = z.object({ + choices: z.array( + z.object({ + message: z.object({ + tool_calls: z + .array( + z.object({ + type: z.literal('function'), + function: z.object({ + name: z.string(), + arguments: z.string(), + }), + }) + ) + .optional(), + }), + }) + ), +}); + +function buildTriagePrompt(finding: SecurityFindingRecord): string { + let cweContext = ''; + if (finding.raw_data && typeof finding.raw_data === 'object') { + const serialized = JSON.stringify(finding.raw_data); + if (serialized.length > 0) { + cweContext = `\n\n**Additional Context**: ${serialized.slice(0, 1000)}`; + } + } + + return `## Vulnerability Alert to Triage + +**Package**: ${finding.package_name} (${finding.package_ecosystem}) +**Severity**: ${finding.severity ?? 'unknown'} +**Dependency Scope**: ${finding.dependency_scope ?? 'unknown'} +**CVE**: ${finding.cve_id ?? 'N/A'} +**GHSA**: ${finding.ghsa_id ?? 'N/A'} + +**Title**: ${finding.title} +**Description**: ${finding.description ?? 'No description available'} + +**Vulnerable Versions**: ${finding.vulnerable_version_range ?? 'Unknown'} +**Patched Version**: ${finding.patched_version ?? 'No patch available'} +**Manifest Path**: ${finding.manifest_path ?? 'Unknown'}${cweContext} + +Please analyze this vulnerability and call the submit_triage_result tool with your assessment.`; +} + +function createFallbackTriage(reason: string): SecurityFindingTriage { + return { + needsSandboxAnalysis: true, + needsSandboxReasoning: `Triage failed: ${reason}. Defaulting to sandbox analysis.`, + suggestedAction: 'analyze_codebase', + confidence: 'low', + triageAt: new Date().toISOString(), + }; +} + +export async function triageSecurityFinding(params: { + finding: SecurityFindingRecord; + authToken: string; + model: string; + backendBaseUrl: string; + organizationId?: string; +}): Promise { + const requestBody = { + model: params.model, + messages: [ + { role: 'system', content: TRIAGE_SYSTEM_PROMPT }, + { role: 'user', content: buildTriagePrompt(params.finding) }, + ], + tools: [ + { + type: 'function', + function: { + name: 'submit_triage_result', + description: 'Submit triage result for this vulnerability finding', + parameters: { + type: 'object', + properties: { + needsSandboxAnalysis: { + type: 'boolean', + }, + needsSandboxReasoning: { + type: 'string', + }, + suggestedAction: { + type: 'string', + enum: ['dismiss', 'analyze_codebase', 'manual_review'], + }, + confidence: { + type: 'string', + enum: ['high', 'medium', 'low'], + }, + }, + required: [ + 'needsSandboxAnalysis', + 'needsSandboxReasoning', + 'suggestedAction', + 'confidence', + ], + }, + }, + }, + ], + tool_choice: { + type: 'function', + function: { name: 'submit_triage_result' }, + }, + stream: false, + }; + + const headers = new Headers({ + 'Content-Type': 'application/json', + Authorization: `Bearer ${params.authToken}`, + 'X-KiloCode-Version': TRIAGE_SERVICE_VERSION, + 'User-Agent': TRIAGE_SERVICE_USER_AGENT, + }); + + if (params.organizationId) { + headers.set('X-KiloCode-OrganizationId', params.organizationId); + } + + try { + const response = await fetch(`${params.backendBaseUrl}/api/openrouter/chat/completions`, { + method: 'POST', + headers, + body: JSON.stringify(requestBody), + signal: AbortSignal.timeout(45_000), + }); + + if (!response.ok) { + const errorText = await response.text(); + logger.error('Triage request failed', { + finding_id: params.finding.id, + status: response.status, + error: errorText, + }); + return createFallbackTriage(`API error: ${response.status}`); + } + + const parsedResponse = TriageResponseSchema.safeParse(await response.json()); + if (!parsedResponse.success) { + logger.warn('Triage response did not match expected shape', { + finding_id: params.finding.id, + }); + return createFallbackTriage('Invalid response shape'); + } + + const firstChoice = parsedResponse.data.choices[0]; + const firstToolCall = firstChoice?.message.tool_calls?.[0]; + if (!firstToolCall || firstToolCall.function.name !== 'submit_triage_result') { + return createFallbackTriage('Tool call missing'); + } + + let args: unknown; + try { + args = JSON.parse(firstToolCall.function.arguments); + } catch { + return createFallbackTriage('Tool call arguments not valid JSON'); + } + const parsedArgs = TriagedResultSchema.safeParse(args); + if (!parsedArgs.success) { + return createFallbackTriage('Tool call arguments invalid'); + } + + return { + ...parsedArgs.data, + triageAt: new Date().toISOString(), + }; + } catch (error) { + logger.error('Triage call threw', { + finding_id: params.finding.id, + error: error instanceof Error ? error.message : String(error), + }); + return createFallbackTriage(error instanceof Error ? error.message : 'Unknown triage error'); + } +} diff --git a/cloudflare-security-auto-analysis/src/types.test.ts b/cloudflare-security-auto-analysis/src/types.test.ts new file mode 100644 index 000000000..ae457684d --- /dev/null +++ b/cloudflare-security-auto-analysis/src/types.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from 'vitest'; +import { AutoAnalysisOwnerMessageSchema, DEFAULT_SECURITY_AGENT_CONFIG } from './types.js'; + +describe('AutoAnalysisOwnerMessageSchema', () => { + it('accepts valid owner messages', () => { + const parsed = AutoAnalysisOwnerMessageSchema.parse({ + ownerType: 'org', + ownerId: 'org_123', + dispatchId: 'dispatch_123', + enqueuedAt: '2026-02-26T00:00:00.000Z', + }); + + expect(parsed.ownerType).toBe('org'); + expect(parsed.ownerId).toBe('org_123'); + }); + + it('rejects messages with missing ownerId', () => { + const result = AutoAnalysisOwnerMessageSchema.safeParse({ + ownerType: 'user', + ownerId: '', + dispatchId: 'dispatch_123', + enqueuedAt: '2026-02-26T00:00:00.000Z', + }); + + expect(result.success).toBe(false); + }); +}); + +describe('DEFAULT_SECURITY_AGENT_CONFIG', () => { + it('defaults to auto analysis mode and high threshold', () => { + expect(DEFAULT_SECURITY_AGENT_CONFIG.analysis_mode).toBe('auto'); + expect(DEFAULT_SECURITY_AGENT_CONFIG.auto_analysis_min_severity).toBe('high'); + }); +}); diff --git a/cloudflare-security-auto-analysis/src/types.ts b/cloudflare-security-auto-analysis/src/types.ts new file mode 100644 index 000000000..a4d3bb642 --- /dev/null +++ b/cloudflare-security-auto-analysis/src/types.ts @@ -0,0 +1,84 @@ +import { z } from 'zod'; + +export const AUTO_ANALYSIS_OWNER_CAP = 2; +export const SECURITY_ANALYSIS_OWNER_CAP = 3; +export const AUTO_ANALYSIS_MAX_ATTEMPTS = 5; + +export const SecurityAgentConfigSchema = z + .object({ + model_slug: z.string().optional(), + analysis_mode: z.enum(['auto', 'shallow', 'deep']).default('auto'), + auto_analysis_enabled: z.boolean().default(false), + auto_analysis_min_severity: z.enum(['critical', 'high', 'medium', 'all']).default('high'), + }) + .passthrough(); + +export type SecurityAgentConfig = z.infer; +export type AnalysisMode = SecurityAgentConfig['analysis_mode']; +export type AutoAnalysisMinSeverity = SecurityAgentConfig['auto_analysis_min_severity']; + +export const DEFAULT_SECURITY_AGENT_CONFIG: SecurityAgentConfig = { + model_slug: 'anthropic/claude-opus-4.6', + analysis_mode: 'auto', + auto_analysis_enabled: false, + auto_analysis_min_severity: 'high', +}; + +export const AutoAnalysisFailureCodeSchema = z.enum([ + 'NETWORK_TIMEOUT', + 'UPSTREAM_5XX', + 'TEMP_TOKEN_FAILURE', + 'START_CALL_AMBIGUOUS', + 'REQUEUE_TEMPORARY_PRECONDITION', + 'ACTOR_RESOLUTION_FAILED', + 'GITHUB_TOKEN_UNAVAILABLE', + 'INVALID_CONFIG', + 'MISSING_OWNERSHIP', + 'PERMISSION_DENIED_PERMANENT', + 'UNSUPPORTED_SEVERITY', + 'INSUFFICIENT_CREDITS', + 'STATE_GUARD_REJECTED', + 'SKIPPED_ALREADY_IN_PROGRESS', + 'SKIPPED_NO_LONGER_ELIGIBLE', + 'REOPEN_LOOP_GUARD', + 'RUN_LOST', +]); + +export type AutoAnalysisFailureCode = z.infer; + +export type QueueOwner = { type: 'org'; id: string } | { type: 'user'; id: string }; +export type ActorResolutionMode = 'owner' | 'member_fallback'; + +export const AutoAnalysisOwnerMessageSchema = z.object({ + ownerType: z.enum(['org', 'user']), + ownerId: z.string().min(1), + dispatchId: z.string().min(1), + enqueuedAt: z.string().min(1), +}); + +export type AutoAnalysisOwnerMessage = z.infer; + +export type SecurityFindingTriage = { + needsSandboxAnalysis: boolean; + needsSandboxReasoning: string; + suggestedAction: 'dismiss' | 'analyze_codebase' | 'manual_review'; + confidence: 'high' | 'medium' | 'low'; + triageAt: string; +}; + +export type SecurityFindingAnalysis = { + triage?: SecurityFindingTriage; + analyzedAt: string; + modelUsed?: string; + triggeredByUserId?: string; + correlationId?: string; +}; + +export type ProcessCounters = { + processed: number; + launched: number; + completed: number; + failed: number; + requeued: number; + skipped: number; +}; diff --git a/cloudflare-security-auto-analysis/tsconfig.json b/cloudflare-security-auto-analysis/tsconfig.json new file mode 100644 index 000000000..fc9830188 --- /dev/null +++ b/cloudflare-security-auto-analysis/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "esnext", + "lib": ["esnext"], + "module": "esnext", + "moduleResolution": "bundler", + "types": ["@types/node", "./worker-configuration.d.ts"], + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "noEmit": true + }, + "include": ["src/**/*.ts", "vitest.config.ts", "worker-configuration.d.ts"] +} diff --git a/cloudflare-security-auto-analysis/vitest.config.ts b/cloudflare-security-auto-analysis/vitest.config.ts new file mode 100644 index 000000000..7dd13254e --- /dev/null +++ b/cloudflare-security-auto-analysis/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + include: ['src/**/*.test.ts'], + }, +}); diff --git a/cloudflare-security-auto-analysis/worker-configuration.d.ts b/cloudflare-security-auto-analysis/worker-configuration.d.ts new file mode 100644 index 000000000..edeb2970e --- /dev/null +++ b/cloudflare-security-auto-analysis/worker-configuration.d.ts @@ -0,0 +1,80 @@ +declare type Hyperdrive = { + connectionString: string; +}; + +declare type Message = { + body: T; + attempts: number; + ack(): void; + retry(): void; +}; + +declare type MessageBatch = { + queue: string; + messages: Array>; +}; + +declare type MessageSendRequest = { + body: T; + contentType: 'json' | 'text' | 'bytes' | 'v8'; +}; + +declare type Queue = { + sendBatch(messages: Array>): Promise; +}; + +declare type GitTokenForRepoResult = + | { + success: true; + token: string; + installationId: string; + accountLogin: string; + appType: 'standard' | 'lite'; + } + | { + success: false; + reason: + | 'database_not_configured' + | 'invalid_repo_format' + | 'no_installation_found' + | 'invalid_org_id'; + }; + +declare type GitTokenService = { + getTokenForRepo(params: { + githubRepo: string; + userId: string; + orgId?: string; + }): Promise; +}; + +declare type SecretBinding = { + get(): Promise; +}; + +declare type ScheduledController = { + scheduledTime: number; + cron: string; + noRetry(): void; +}; + +declare type ExecutionContext = { + waitUntil(promise: Promise): void; + passThroughOnException(): void; +}; + +declare type CloudflareEnv = { + HYPERDRIVE: Hyperdrive; + OWNER_QUEUE: Queue; + CLOUD_AGENT_NEXT: { fetch(input: RequestInfo | URL, init?: RequestInit): Promise }; + GIT_TOKEN_SERVICE: GitTokenService; + NEXTAUTH_SECRET: SecretBinding; + INTERNAL_API_SECRET: SecretBinding; + KILOCODE_BACKEND_BASE_URL: string; + ENVIRONMENT: string; +}; + +declare type GitTokenLookupFailureReason = Extract< + GitTokenForRepoResult, + { success: false } +>['reason']; diff --git a/cloudflare-security-auto-analysis/wrangler.jsonc b/cloudflare-security-auto-analysis/wrangler.jsonc new file mode 100644 index 000000000..e4898914a --- /dev/null +++ b/cloudflare-security-auto-analysis/wrangler.jsonc @@ -0,0 +1,130 @@ +{ + "$schema": "node_modules/wrangler/config-schema.json", + "name": "security-auto-analysis", + "account_id": "e115e769bcdd4c3d66af59d3332cb394", + "main": "src/index.ts", + "compatibility_date": "2026-02-26", + "compatibility_flags": ["nodejs_compat"], + "workers_dev": false, + "dev": { + "port": 8797, + "local_protocol": "http", + "ip": "0.0.0.0", + }, + "observability": { + "enabled": true, + }, + "vars": { + "ENVIRONMENT": "production", + "KILOCODE_BACKEND_BASE_URL": "https://api.kilo.ai", + }, + "triggers": { + "crons": ["* * * * *"], + }, + "hyperdrive": [ + { + "binding": "HYPERDRIVE", + "id": "624ec80650dd414199349f4e217ddb10", + "localConnectionString": "postgres://postgres:postgres@localhost:5432/postgres", + }, + ], + "queues": { + "producers": [ + { + "binding": "OWNER_QUEUE", + "queue": "security-auto-analysis-owner-queue", + }, + ], + "consumers": [ + { + "queue": "security-auto-analysis-owner-queue", + "max_batch_size": 1, + "max_retries": 3, + "dead_letter_queue": "security-auto-analysis-owner-dlq", + "max_concurrency": 50, + "type": "unbound", + }, + ], + }, + "services": [ + { + "binding": "CLOUD_AGENT_NEXT", + "service": "cloud-agent-next", + }, + { + "binding": "GIT_TOKEN_SERVICE", + "service": "git-token-service", + "entrypoint": "GitTokenRPCEntrypoint", + }, + ], + "secrets_store_secrets": [ + { + "binding": "INTERNAL_API_SECRET", + "store_id": "342a86d9e3a94da698e82d0c6e2a36f0", + "secret_name": "INTERNAL_API_SECRET_PROD", + }, + { + "binding": "NEXTAUTH_SECRET", + "store_id": "342a86d9e3a94da698e82d0c6e2a36f0", + "secret_name": "NEXTAUTH_SECRET_PROD", + }, + ], + "env": { + "dev": { + "name": "security-auto-analysis-dev", + "workers_dev": false, + "vars": { + "ENVIRONMENT": "development", + "KILOCODE_BACKEND_BASE_URL": "http://localhost:3000", + }, + "hyperdrive": [ + { + "binding": "HYPERDRIVE", + "id": "624ec80650dd414199349f4e217ddb10", + "localConnectionString": "postgres://postgres:postgres@localhost:5432/postgres", + }, + ], + "queues": { + "producers": [ + { + "binding": "OWNER_QUEUE", + "queue": "security-auto-analysis-owner-queue-dev", + }, + ], + "consumers": [ + { + "queue": "security-auto-analysis-owner-queue-dev", + "max_batch_size": 1, + "max_retries": 3, + "dead_letter_queue": "security-auto-analysis-owner-dlq-dev", + "max_concurrency": 50, + "type": "unbound", + }, + ], + }, + "services": [ + { + "binding": "CLOUD_AGENT_NEXT", + "service": "cloud-agent-next-dev", + }, + { + "binding": "GIT_TOKEN_SERVICE", + "service": "git-token-service-dev", + "entrypoint": "GitTokenRPCEntrypoint", + }, + ], + "secrets_store_secrets": [ + { + "binding": "INTERNAL_API_SECRET", + "store_id": "342a86d9e3a94da698e82d0c6e2a36f0", + "secret_name": "INTERNAL_API_SECRET_DEV", + }, + { + "binding": "NEXTAUTH_SECRET", + "store_id": "342a86d9e3a94da698e82d0c6e2a36f0", + "secret_name": "NEXTAUTH_SECRET_DEV", + }, + ], + }, + }, +} diff --git a/cloudflare-webhook-agent-ingest/src/index.ts b/cloudflare-webhook-agent-ingest/src/index.ts index bc2331f0b..f8ce1d6d6 100644 --- a/cloudflare-webhook-agent-ingest/src/index.ts +++ b/cloudflare-webhook-agent-ingest/src/index.ts @@ -18,7 +18,6 @@ export type HonoContext = { const app = new Hono(); -// @ts-expect-error workers-tagged-logger returns Handler typed against an older hono; incompatible with hono 4.12+ app.use('*', useWorkersLogger('webhook-agent')); app.get('/health', c => { diff --git a/jest.config.ts b/jest.config.ts index 007981c89..9dcce49fe 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -41,6 +41,7 @@ const config: Config = { '/cloudflare-webhook-agent-ingest/', '/cloudflare-session-ingest/', '/cloudflare-gastown/', + '/cloudflare-security-auto-analysis/', '/kiloclaw/', '/packages/encryption/', '/packages/worker-utils/', diff --git a/packages/db/src/migrations/0039_naive_yellow_claw.sql b/packages/db/src/migrations/0039_naive_yellow_claw.sql new file mode 100644 index 000000000..02b3195ed --- /dev/null +++ b/packages/db/src/migrations/0039_naive_yellow_claw.sql @@ -0,0 +1,84 @@ +CREATE TABLE "security_analysis_owner_state" ( + "id" uuid PRIMARY KEY DEFAULT pg_catalog.gen_random_uuid() NOT NULL, + "owned_by_organization_id" uuid, + "owned_by_user_id" text, + "auto_analysis_enabled_at" timestamp with time zone, + "blocked_until" timestamp with time zone, + "block_reason" text, + "consecutive_actor_resolution_failures" integer DEFAULT 0 NOT NULL, + "last_actor_resolution_failure_at" timestamp with time zone, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "security_analysis_owner_state_owner_check" CHECK (( + ("security_analysis_owner_state"."owned_by_user_id" IS NOT NULL AND "security_analysis_owner_state"."owned_by_organization_id" IS NULL) OR + ("security_analysis_owner_state"."owned_by_user_id" IS NULL AND "security_analysis_owner_state"."owned_by_organization_id" IS NOT NULL) + )), + CONSTRAINT "security_analysis_owner_state_block_reason_check" CHECK ("security_analysis_owner_state"."block_reason" IS NULL OR "security_analysis_owner_state"."block_reason" IN ('INSUFFICIENT_CREDITS', 'ACTOR_RESOLUTION_FAILED', 'OPERATOR_PAUSE')) +); +--> statement-breakpoint +CREATE TABLE "security_analysis_queue" ( + "id" uuid PRIMARY KEY DEFAULT pg_catalog.gen_random_uuid() NOT NULL, + "finding_id" uuid NOT NULL, + "owned_by_organization_id" uuid, + "owned_by_user_id" text, + "queue_status" text NOT NULL, + "severity_rank" smallint NOT NULL, + "queued_at" timestamp with time zone NOT NULL, + "claimed_at" timestamp with time zone, + "claimed_by_job_id" text, + "claim_token" text, + "attempt_count" integer DEFAULT 0 NOT NULL, + "reopen_requeue_count" integer DEFAULT 0 NOT NULL, + "next_retry_at" timestamp with time zone, + "failure_code" text, + "last_error_redacted" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "security_analysis_queue_owner_check" CHECK (( + ("security_analysis_queue"."owned_by_user_id" IS NOT NULL AND "security_analysis_queue"."owned_by_organization_id" IS NULL) OR + ("security_analysis_queue"."owned_by_user_id" IS NULL AND "security_analysis_queue"."owned_by_organization_id" IS NOT NULL) + )), + CONSTRAINT "security_analysis_queue_status_check" CHECK ("security_analysis_queue"."queue_status" IN ('queued', 'pending', 'running', 'failed', 'completed')), + CONSTRAINT "security_analysis_queue_claim_token_required_check" CHECK ("security_analysis_queue"."queue_status" NOT IN ('pending', 'running') OR "security_analysis_queue"."claim_token" IS NOT NULL), + CONSTRAINT "security_analysis_queue_attempt_count_non_negative_check" CHECK ("security_analysis_queue"."attempt_count" >= 0), + CONSTRAINT "security_analysis_queue_reopen_requeue_count_non_negative_check" CHECK ("security_analysis_queue"."reopen_requeue_count" >= 0), + CONSTRAINT "security_analysis_queue_severity_rank_check" CHECK ("security_analysis_queue"."severity_rank" IN (0, 1, 2, 3)), + CONSTRAINT "security_analysis_queue_failure_code_check" CHECK ("security_analysis_queue"."failure_code" IS NULL OR "security_analysis_queue"."failure_code" IN ( + 'NETWORK_TIMEOUT', + 'UPSTREAM_5XX', + 'TEMP_TOKEN_FAILURE', + 'START_CALL_AMBIGUOUS', + 'REQUEUE_TEMPORARY_PRECONDITION', + 'ACTOR_RESOLUTION_FAILED', + 'GITHUB_TOKEN_UNAVAILABLE', + 'INVALID_CONFIG', + 'MISSING_OWNERSHIP', + 'PERMISSION_DENIED_PERMANENT', + 'UNSUPPORTED_SEVERITY', + 'INSUFFICIENT_CREDITS', + 'STATE_GUARD_REJECTED', + 'SKIPPED_ALREADY_IN_PROGRESS', + 'SKIPPED_NO_LONGER_ELIGIBLE', + 'REOPEN_LOOP_GUARD', + 'RUN_LOST' + )) +); +--> statement-breakpoint +ALTER TABLE "security_analysis_owner_state" ADD CONSTRAINT "security_analysis_owner_state_owned_by_organization_id_organizations_id_fk" FOREIGN KEY ("owned_by_organization_id") REFERENCES "public"."organizations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "security_analysis_owner_state" ADD CONSTRAINT "security_analysis_owner_state_owned_by_user_id_kilocode_users_id_fk" FOREIGN KEY ("owned_by_user_id") REFERENCES "public"."kilocode_users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "security_analysis_queue" ADD CONSTRAINT "security_analysis_queue_finding_id_security_findings_id_fk" FOREIGN KEY ("finding_id") REFERENCES "public"."security_findings"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "security_analysis_queue" ADD CONSTRAINT "security_analysis_queue_owned_by_organization_id_organizations_id_fk" FOREIGN KEY ("owned_by_organization_id") REFERENCES "public"."organizations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "security_analysis_queue" ADD CONSTRAINT "security_analysis_queue_owned_by_user_id_kilocode_users_id_fk" FOREIGN KEY ("owned_by_user_id") REFERENCES "public"."kilocode_users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +CREATE UNIQUE INDEX "UQ_security_analysis_owner_state_org_owner" ON "security_analysis_owner_state" USING btree ("owned_by_organization_id") WHERE "security_analysis_owner_state"."owned_by_organization_id" is not null;--> statement-breakpoint +CREATE UNIQUE INDEX "UQ_security_analysis_owner_state_user_owner" ON "security_analysis_owner_state" USING btree ("owned_by_user_id") WHERE "security_analysis_owner_state"."owned_by_user_id" is not null;--> statement-breakpoint +CREATE UNIQUE INDEX "UQ_security_analysis_queue_finding_id" ON "security_analysis_queue" USING btree ("finding_id");--> statement-breakpoint +CREATE INDEX "idx_security_analysis_queue_claim_path_org" ON "security_analysis_queue" USING btree ("owned_by_organization_id",coalesce("next_retry_at", '-infinity'::timestamptz),"severity_rank","queued_at","id") WHERE "security_analysis_queue"."queue_status" = 'queued';--> statement-breakpoint +CREATE INDEX "idx_security_analysis_queue_claim_path_user" ON "security_analysis_queue" USING btree ("owned_by_user_id",coalesce("next_retry_at", '-infinity'::timestamptz),"severity_rank","queued_at","id") WHERE "security_analysis_queue"."queue_status" = 'queued';--> statement-breakpoint +CREATE INDEX "idx_security_analysis_queue_in_flight_org" ON "security_analysis_queue" USING btree ("owned_by_organization_id","queue_status","claimed_at","id") WHERE "security_analysis_queue"."queue_status" IN ('pending', 'running');--> statement-breakpoint +CREATE INDEX "idx_security_analysis_queue_in_flight_user" ON "security_analysis_queue" USING btree ("owned_by_user_id","queue_status","claimed_at","id") WHERE "security_analysis_queue"."queue_status" IN ('pending', 'running');--> statement-breakpoint +CREATE INDEX "idx_security_analysis_queue_lag_dashboards" ON "security_analysis_queue" USING btree ("queued_at") WHERE "security_analysis_queue"."queue_status" = 'queued';--> statement-breakpoint +CREATE INDEX "idx_security_analysis_queue_pending_reconciliation" ON "security_analysis_queue" USING btree ("claimed_at","id") WHERE "security_analysis_queue"."queue_status" = 'pending';--> statement-breakpoint +CREATE INDEX "idx_security_analysis_queue_running_reconciliation" ON "security_analysis_queue" USING btree ("updated_at","id") WHERE "security_analysis_queue"."queue_status" = 'running';--> statement-breakpoint +CREATE INDEX "idx_security_analysis_queue_failure_trend" ON "security_analysis_queue" USING btree ("failure_code","updated_at") WHERE "security_analysis_queue"."failure_code" IS NOT NULL;--> statement-breakpoint +CREATE INDEX "idx_security_findings_org_analysis_in_flight" ON "security_findings" USING btree ("owned_by_organization_id","analysis_status") WHERE "security_findings"."analysis_status" IN ('pending', 'running');--> statement-breakpoint +CREATE INDEX "idx_security_findings_user_analysis_in_flight" ON "security_findings" USING btree ("owned_by_user_id","analysis_status") WHERE "security_findings"."analysis_status" IN ('pending', 'running'); \ No newline at end of file diff --git a/packages/db/src/migrations/meta/0039_snapshot.json b/packages/db/src/migrations/meta/0039_snapshot.json new file mode 100644 index 000000000..72505e4fc --- /dev/null +++ b/packages/db/src/migrations/meta/0039_snapshot.json @@ -0,0 +1,13666 @@ +{ + "id": "ece4ecc6-d4d7-4519-afbe-b2cb136489dc", + "prevId": "f223a1da-6fdb-4664-be8f-861924909fa5", + "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 + }, + "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_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_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 + }, + "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": {} + } + }, + "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_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 + }, + "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 + } + }, + "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": "'v1'" + } + }, + "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": {} + } + }, + "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_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": "'{}'" + }, + "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, + "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" + } + }, + "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.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 + }, + "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_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 + }, + "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": {} + } + }, + "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_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": "uuid", + "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 + }, + "agent_version": { + "name": "agent_version", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'v1'" + }, + "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" + }, + "cloud_agent_code_reviews_cli_session_id_cli_sessions_session_id_fk": { + "name": "cloud_agent_code_reviews_cli_session_id_cli_sessions_session_id_fk", + "tableFrom": "cloud_agent_code_reviews", + "tableTo": "cli_sessions", + "columnsFrom": [ + "cli_session_id" + ], + "columnsTo": [ + "session_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_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 + }, + "github_repo": { + "name": "github_repo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "profile_id": { + "name": "profile_id", + "type": "uuid", + "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_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_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 )" + } + }, + "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_file_hash": { + "name": "IDX_code_indexing_manifest_file_hash", + "columns": [ + { + "expression": "file_hash", + "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.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_llm": { + "name": "custom_llm", + "schema": "", + "columns": { + "public_id": { + "name": "public_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "context_length": { + "name": "context_length", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "max_completion_tokens": { + "name": "max_completion_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "internal_id": { + "name": "internal_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "base_url": { + "name": "base_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "api_key": { + "name": "api_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_ids": { + "name": "organization_ids", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "included_tools": { + "name": "included_tools", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "excluded_tools": { + "name": "excluded_tools", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "supports_image_input": { + "name": "supports_image_input", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "force_reasoning": { + "name": "force_reasoning", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "opencode_settings": { + "name": "opencode_settings", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "verbosity": { + "name": "verbosity", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reasoning_effort": { + "name": "reasoning_effort", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "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.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.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.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.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', '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', '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')" + } + }, + "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 + }, + "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', 'cron')" + } + }, + "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_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 + }, + "stripe_subscription_id": { + "name": "stripe_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "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_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": {} + } + }, + "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_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.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_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()" + } + }, + "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": {} + } + }, + "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_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 + }, + "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 + } + }, + "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": {} + } + }, + "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": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "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()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "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_user_id_kilocode_users_id_fk": { + "name": "kiloclaw_version_pins_user_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_version_pins", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "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_user_id_unique": { + "name": "kiloclaw_version_pins_user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "user_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 + }, + "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 + }, + "api_token_pepper": { + "name": "api_token_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 + }, + "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 + }, + "openrouter_upstream_safety_identifier": { + "name": "openrouter_upstream_safety_identifier", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "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": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "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.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_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 + }, + "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 + } + }, + "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": {} + } + }, + "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.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_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": false + }, + "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.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 + }, + "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": {} + }, + "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_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.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.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.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_fingerprint_data": { + "name": "idx_fingerprint_data", + "columns": [ + { + "expression": "fingerprint_data", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "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_reasons": { + "name": "idx_reasons", + "columns": [ + { + "expression": "reasons", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "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.temp_phase": { + "name": "temp_phase", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "IDX_temp_phase_created_at": { + "name": "IDX_temp_phase_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.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_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 + }, + "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_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.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 + }, + "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 + } + }, + "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 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 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 \"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 dc27db083..31fe35fe6 100644 --- a/packages/db/src/migrations/meta/_journal.json +++ b/packages/db/src/migrations/meta/_journal.json @@ -274,6 +274,13 @@ "when": 1772540011976, "tag": "0038_late_shaman", "breakpoints": true + }, + { + "idx": 39, + "version": "7", + "when": 1772547493233, + "tag": "0039_naive_yellow_claw", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/db/src/schema.ts b/packages/db/src/schema.ts index 9844a09a9..72b2bca59 100644 --- a/packages/db/src/schema.ts +++ b/packages/db/src/schema.ts @@ -2477,6 +2477,12 @@ export const security_findings = pgTable( index('idx_security_findings_session_id').on(table.session_id), index('idx_security_findings_cli_session_id').on(table.cli_session_id), index('idx_security_findings_analysis_status').on(table.analysis_status), + index('idx_security_findings_org_analysis_in_flight') + .on(table.owned_by_organization_id, table.analysis_status) + .where(sql`${table.analysis_status} IN ('pending', 'running')`), + index('idx_security_findings_user_analysis_in_flight') + .on(table.owned_by_user_id, table.analysis_status) + .where(sql`${table.analysis_status} IN ('pending', 'running')`), // Owner check constraint check( 'security_findings_owner_check', @@ -2491,6 +2497,165 @@ export const security_findings = pgTable( export type SecurityFinding = typeof security_findings.$inferSelect; export type NewSecurityFinding = typeof security_findings.$inferInsert; +export const security_analysis_queue = pgTable( + 'security_analysis_queue', + { + id: idPrimaryKeyColumn, + finding_id: uuid() + .notNull() + .references(() => security_findings.id, { onDelete: 'cascade' }), + owned_by_organization_id: uuid().references(() => organizations.id, { onDelete: 'cascade' }), + owned_by_user_id: text().references(() => kilocode_users.id, { onDelete: 'cascade' }), + queue_status: text().notNull(), + severity_rank: smallint().notNull(), + queued_at: timestamp({ withTimezone: true, mode: 'string' }).notNull(), + claimed_at: timestamp({ withTimezone: true, mode: 'string' }), + claimed_by_job_id: text(), + claim_token: text(), + attempt_count: integer().notNull().default(0), + reopen_requeue_count: integer().notNull().default(0), + next_retry_at: timestamp({ withTimezone: true, mode: 'string' }), + failure_code: text(), + last_error_redacted: text(), + created_at: timestamp({ withTimezone: true, mode: 'string' }).defaultNow().notNull(), + updated_at: timestamp({ withTimezone: true, mode: 'string' }) + .defaultNow() + .notNull() + .$onUpdateFn(() => sql`now()`), + }, + table => [ + uniqueIndex('UQ_security_analysis_queue_finding_id').on(table.finding_id), + check( + 'security_analysis_queue_owner_check', + sql`( + (${table.owned_by_user_id} IS NOT NULL AND ${table.owned_by_organization_id} IS NULL) OR + (${table.owned_by_user_id} IS NULL AND ${table.owned_by_organization_id} IS NOT NULL) + )` + ), + check( + 'security_analysis_queue_status_check', + sql`${table.queue_status} IN ('queued', 'pending', 'running', 'failed', 'completed')` + ), + check( + 'security_analysis_queue_claim_token_required_check', + sql`${table.queue_status} NOT IN ('pending', 'running') OR ${table.claim_token} IS NOT NULL` + ), + check( + 'security_analysis_queue_attempt_count_non_negative_check', + sql`${table.attempt_count} >= 0` + ), + check( + 'security_analysis_queue_reopen_requeue_count_non_negative_check', + sql`${table.reopen_requeue_count} >= 0` + ), + check( + 'security_analysis_queue_severity_rank_check', + sql`${table.severity_rank} IN (0, 1, 2, 3)` + ), + check( + 'security_analysis_queue_failure_code_check', + sql`${table.failure_code} IS NULL OR ${table.failure_code} IN ( + 'NETWORK_TIMEOUT', + 'UPSTREAM_5XX', + 'TEMP_TOKEN_FAILURE', + 'START_CALL_AMBIGUOUS', + 'REQUEUE_TEMPORARY_PRECONDITION', + 'ACTOR_RESOLUTION_FAILED', + 'GITHUB_TOKEN_UNAVAILABLE', + 'INVALID_CONFIG', + 'MISSING_OWNERSHIP', + 'PERMISSION_DENIED_PERMANENT', + 'UNSUPPORTED_SEVERITY', + 'INSUFFICIENT_CREDITS', + 'STATE_GUARD_REJECTED', + 'SKIPPED_ALREADY_IN_PROGRESS', + 'SKIPPED_NO_LONGER_ELIGIBLE', + 'REOPEN_LOOP_GUARD', + 'RUN_LOST' + )` + ), + index('idx_security_analysis_queue_claim_path_org') + .on( + table.owned_by_organization_id, + sql`coalesce(${table.next_retry_at}, '-infinity'::timestamptz)`, + table.severity_rank, + table.queued_at, + table.id + ) + .where(sql`${table.queue_status} = 'queued'`), + index('idx_security_analysis_queue_claim_path_user') + .on( + table.owned_by_user_id, + sql`coalesce(${table.next_retry_at}, '-infinity'::timestamptz)`, + table.severity_rank, + table.queued_at, + table.id + ) + .where(sql`${table.queue_status} = 'queued'`), + index('idx_security_analysis_queue_in_flight_org') + .on(table.owned_by_organization_id, table.queue_status, table.claimed_at, table.id) + .where(sql`${table.queue_status} IN ('pending', 'running')`), + index('idx_security_analysis_queue_in_flight_user') + .on(table.owned_by_user_id, table.queue_status, table.claimed_at, table.id) + .where(sql`${table.queue_status} IN ('pending', 'running')`), + index('idx_security_analysis_queue_lag_dashboards') + .on(table.queued_at) + .where(sql`${table.queue_status} = 'queued'`), + index('idx_security_analysis_queue_pending_reconciliation') + .on(table.claimed_at, table.id) + .where(sql`${table.queue_status} = 'pending'`), + index('idx_security_analysis_queue_running_reconciliation') + .on(table.updated_at, table.id) + .where(sql`${table.queue_status} = 'running'`), + index('idx_security_analysis_queue_failure_trend') + .on(table.failure_code, table.updated_at) + .where(sql`${table.failure_code} IS NOT NULL`), + ] +); + +export type SecurityAnalysisQueue = typeof security_analysis_queue.$inferSelect; +export type NewSecurityAnalysisQueue = typeof security_analysis_queue.$inferInsert; + +export const security_analysis_owner_state = pgTable( + 'security_analysis_owner_state', + { + id: idPrimaryKeyColumn, + owned_by_organization_id: uuid().references(() => organizations.id, { onDelete: 'cascade' }), + owned_by_user_id: text().references(() => kilocode_users.id, { onDelete: 'cascade' }), + auto_analysis_enabled_at: timestamp({ withTimezone: true, mode: 'string' }), + blocked_until: timestamp({ withTimezone: true, mode: 'string' }), + block_reason: text(), + consecutive_actor_resolution_failures: integer().notNull().default(0), + last_actor_resolution_failure_at: timestamp({ withTimezone: true, mode: 'string' }), + created_at: timestamp({ withTimezone: true, mode: 'string' }).defaultNow().notNull(), + updated_at: timestamp({ withTimezone: true, mode: 'string' }) + .defaultNow() + .notNull() + .$onUpdateFn(() => sql`now()`), + }, + table => [ + check( + 'security_analysis_owner_state_owner_check', + sql`( + (${table.owned_by_user_id} IS NOT NULL AND ${table.owned_by_organization_id} IS NULL) OR + (${table.owned_by_user_id} IS NULL AND ${table.owned_by_organization_id} IS NOT NULL) + )` + ), + check( + 'security_analysis_owner_state_block_reason_check', + sql`${table.block_reason} IS NULL OR ${table.block_reason} IN ('INSUFFICIENT_CREDITS', 'ACTOR_RESOLUTION_FAILED', 'OPERATOR_PAUSE')` + ), + uniqueIndex('UQ_security_analysis_owner_state_org_owner') + .on(table.owned_by_organization_id) + .where(isNotNull(table.owned_by_organization_id)), + uniqueIndex('UQ_security_analysis_owner_state_user_owner') + .on(table.owned_by_user_id) + .where(isNotNull(table.owned_by_user_id)), + ] +); + +export type SecurityAnalysisOwnerState = typeof security_analysis_owner_state.$inferSelect; + // Security Audit Log — SOC2-compliant audit trail for security agent actions export const security_audit_log = pgTable( 'security_audit_log', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 79eb482d6..d7bbd4748 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -107,7 +107,7 @@ importers: version: 0.17.7(react@19.2.0) '@mdx-js/loader': specifier: ^3.1.1 - version: 3.1.1(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) + version: 3.1.1(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) '@mdx-js/react': specifier: ^3.1.1 version: 3.1.1(@types/react@19.2.2)(react@19.2.0) @@ -122,7 +122,7 @@ importers: version: 16.1.6 '@next/mdx': specifier: ^16.1.6 - version: 16.1.6(@mdx-js/loader@3.1.1(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)))(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@19.2.0)) + version: 16.1.6(@mdx-js/loader@3.1.1(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)))(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@19.2.0)) '@next/third-parties': specifier: ^16.1.6 version: 16.1.6(next@16.1.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0) @@ -149,7 +149,7 @@ importers: version: 2.5.1(@opentelemetry/api@1.9.0) '@qdrant/js-client-rest': specifier: ^1.16.2 - version: 1.16.2(typescript@5.9.3) + version: 1.17.0(typescript@5.9.3) '@radix-ui/react-accordion': specifier: ^1.2.12 version: 1.2.12(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -191,7 +191,7 @@ importers: version: 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@sentry/nextjs': specifier: ^10.29.0 - version: 10.29.0(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@16.1.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) + version: 10.29.0(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@16.1.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) '@sentry/opentelemetry': specifier: ^10.29.0 version: 10.29.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0) @@ -203,7 +203,7 @@ importers: version: 2.19.0 '@slack/web-api': specifier: ^7.13.0 - version: 7.13.0 + version: 7.14.1 '@stripe/stripe-js': specifier: ^5.7.0 version: 5.10.0 @@ -1108,7 +1108,7 @@ importers: version: 1.0.20 fast-xml-parser: specifier: '>=5.3.4' - version: 5.3.4 + version: 5.4.1 fetch-to-node: specifier: ^2.1.0 version: 2.1.0 @@ -1184,6 +1184,37 @@ importers: specifier: 'catalog:' version: 4.68.1(@cloudflare/workers-types@4.20260130.0) + cloudflare-security-auto-analysis: + dependencies: + '@kilocode/db': + specifier: workspace:* + version: link:../packages/db + drizzle-orm: + specifier: 'catalog:' + version: 0.45.1(@cloudflare/workers-types@4.20260130.0)(@opentelemetry/api@1.9.0)(@types/pg@8.16.0)(bun-types@1.3.9)(pg@8.18.0) + workers-tagged-logger: + specifier: 'catalog:' + version: 1.0.0 + zod: + specifier: 'catalog:' + version: 4.3.6 + devDependencies: + '@kilocode/eslint-config': + specifier: workspace:* + version: link:../packages/eslint-config + '@types/node': + specifier: ^22 + version: 22.19.1 + typescript: + specifier: 'catalog:' + version: 5.9.3 + vitest: + specifier: ^3.2.4 + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.19.1)(@vitest/ui@3.2.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + wrangler: + specifier: 'catalog:' + version: 4.68.1(@cloudflare/workers-types@4.20260130.0) + cloudflare-security-sync: dependencies: '@kilocode/db': @@ -1207,7 +1238,7 @@ importers: version: 5.9.3 wrangler: specifier: ^4.61.0 - version: 4.61.1(@cloudflare/workers-types@4.20260130.0) + version: 4.68.1(@cloudflare/workers-types@4.20260130.0) cloudflare-session-ingest: dependencies: @@ -1483,22 +1514,22 @@ importers: version: 3.14.0(zod@4.3.6) '@chromatic-com/storybook': specifier: ^4.1.3 - version: 4.1.3(@chromatic-com/playwright@0.12.8(@playwright/test@1.57.0)(@swc/core@1.12.5)(@types/react@19.2.2)(esbuild@0.27.0)(prettier@3.8.1)(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + version: 4.1.3(@chromatic-com/playwright@0.12.8(@playwright/test@1.57.0)(@swc/core@1.12.5)(@types/react@19.2.2)(esbuild@0.27.0)(prettier@3.8.1)(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) '@storybook/addon-docs': specifier: ^9.1.17 - version: 9.1.17(@types/react@19.2.2)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + version: 9.1.17(@types/react@19.2.2)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) '@storybook/addon-links': specifier: ^9.1.17 - version: 9.1.17(react@19.2.0)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + version: 9.1.17(react@19.2.0)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) '@storybook/addon-themes': specifier: ^9.1.17 - version: 9.1.17(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + version: 9.1.17(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) '@storybook/nextjs': specifier: ^9.1.17 - version: 9.1.17(@swc/core@1.12.5)(esbuild@0.27.0)(next@16.1.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(type-fest@4.41.0)(typescript@5.9.3)(webpack-hot-middleware@2.26.1)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) + version: 9.1.17(@swc/core@1.12.5)(esbuild@0.27.0)(next@16.1.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(type-fest@4.41.0)(typescript@5.9.3)(webpack-hot-middleware@2.26.1)(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) '@storybook/test-runner': specifier: ^0.23.0 - version: 0.23.0(@types/node@25.2.0)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + version: 0.23.0(@types/node@25.2.0)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) '@tailwindcss/typography': specifier: ^0.5.19 version: 0.5.19(tailwindcss@4.1.17) @@ -1528,7 +1559,7 @@ importers: version: 3.0.5 storybook: specifier: ^9.1.17 - version: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + version: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) tailwindcss: specifier: ^4.1.16 version: 4.1.17 @@ -1540,7 +1571,7 @@ importers: version: 5.9.3 webpack: specifier: ^5.102.1 - version: 5.102.1(@swc/core@1.12.5)(esbuild@0.27.0) + version: 5.105.2(@swc/core@1.12.5)(esbuild@0.27.0) packages: @@ -2671,12 +2702,6 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.25.12': - resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.27.0': resolution: {integrity: sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==} engines: {node: '>=18'} @@ -2689,12 +2714,6 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.25.12': - resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - '@esbuild/android-arm64@0.27.0': resolution: {integrity: sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==} engines: {node: '>=18'} @@ -2707,12 +2726,6 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.25.12': - resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - '@esbuild/android-arm@0.27.0': resolution: {integrity: sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==} engines: {node: '>=18'} @@ -2725,12 +2738,6 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.25.12': - resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - '@esbuild/android-x64@0.27.0': resolution: {integrity: sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==} engines: {node: '>=18'} @@ -2743,12 +2750,6 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.25.12': - resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - '@esbuild/darwin-arm64@0.27.0': resolution: {integrity: sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==} engines: {node: '>=18'} @@ -2761,12 +2762,6 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.25.12': - resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - '@esbuild/darwin-x64@0.27.0': resolution: {integrity: sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==} engines: {node: '>=18'} @@ -2779,12 +2774,6 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.25.12': - resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - '@esbuild/freebsd-arm64@0.27.0': resolution: {integrity: sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==} engines: {node: '>=18'} @@ -2797,12 +2786,6 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.12': - resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - '@esbuild/freebsd-x64@0.27.0': resolution: {integrity: sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==} engines: {node: '>=18'} @@ -2815,12 +2798,6 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.25.12': - resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - '@esbuild/linux-arm64@0.27.0': resolution: {integrity: sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==} engines: {node: '>=18'} @@ -2833,12 +2810,6 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.25.12': - resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - '@esbuild/linux-arm@0.27.0': resolution: {integrity: sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==} engines: {node: '>=18'} @@ -2851,12 +2822,6 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.25.12': - resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - '@esbuild/linux-ia32@0.27.0': resolution: {integrity: sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==} engines: {node: '>=18'} @@ -2869,12 +2834,6 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.25.12': - resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-loong64@0.27.0': resolution: {integrity: sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==} engines: {node: '>=18'} @@ -2887,12 +2846,6 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.25.12': - resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - '@esbuild/linux-mips64el@0.27.0': resolution: {integrity: sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==} engines: {node: '>=18'} @@ -2905,12 +2858,6 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.25.12': - resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - '@esbuild/linux-ppc64@0.27.0': resolution: {integrity: sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==} engines: {node: '>=18'} @@ -2923,12 +2870,6 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.25.12': - resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - '@esbuild/linux-riscv64@0.27.0': resolution: {integrity: sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==} engines: {node: '>=18'} @@ -2941,12 +2882,6 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.25.12': - resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-s390x@0.27.0': resolution: {integrity: sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==} engines: {node: '>=18'} @@ -2959,12 +2894,6 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.25.12': - resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - '@esbuild/linux-x64@0.27.0': resolution: {integrity: sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==} engines: {node: '>=18'} @@ -2977,12 +2906,6 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-arm64@0.25.12': - resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - '@esbuild/netbsd-arm64@0.27.0': resolution: {integrity: sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==} engines: {node: '>=18'} @@ -2995,12 +2918,6 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.12': - resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - '@esbuild/netbsd-x64@0.27.0': resolution: {integrity: sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==} engines: {node: '>=18'} @@ -3013,12 +2930,6 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.25.12': - resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - '@esbuild/openbsd-arm64@0.27.0': resolution: {integrity: sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==} engines: {node: '>=18'} @@ -3031,12 +2942,6 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.12': - resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - '@esbuild/openbsd-x64@0.27.0': resolution: {integrity: sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==} engines: {node: '>=18'} @@ -3049,12 +2954,6 @@ packages: cpu: [arm64] os: [openharmony] - '@esbuild/openharmony-arm64@0.25.12': - resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - '@esbuild/openharmony-arm64@0.27.0': resolution: {integrity: sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==} engines: {node: '>=18'} @@ -3067,12 +2966,6 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.25.12': - resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - '@esbuild/sunos-x64@0.27.0': resolution: {integrity: sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==} engines: {node: '>=18'} @@ -3085,12 +2978,6 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.25.12': - resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - '@esbuild/win32-arm64@0.27.0': resolution: {integrity: sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==} engines: {node: '>=18'} @@ -3103,12 +2990,6 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.25.12': - resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - '@esbuild/win32-ia32@0.27.0': resolution: {integrity: sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==} engines: {node: '>=18'} @@ -3121,12 +3002,6 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.25.12': - resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - '@esbuild/win32-x64@0.27.0': resolution: {integrity: sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==} engines: {node: '>=18'} @@ -3374,14 +3249,6 @@ packages: cpu: [x64] os: [win32] - '@isaacs/balanced-match@4.0.1': - resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} - engines: {node: 20 || >=22} - - '@isaacs/brace-expansion@5.0.1': - resolution: {integrity: sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==} - engines: {node: 20 || >=22} - '@istanbuljs/load-nyc-config@1.1.0': resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} engines: {node: '>=8'} @@ -4185,8 +4052,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.8 - '@qdrant/js-client-rest@1.16.2': - resolution: {integrity: sha512-Zm4wEZURrZ24a+Hmm4l1QQYjiz975Ep3vF0yzWR7ICGcxittNz47YK2iBOk8kb8qseCu8pg7WmO1HOIsO8alvw==} + '@qdrant/js-client-rest@1.17.0': + resolution: {integrity: sha512-aZFQeirWVqWAa1a8vJ957LMzcXkFHGbsoRhzc8AkGfg6V0jtK8PlG8/eyyc2xhYsR961FDDx1Tx6nyE0K7lS+A==} engines: {node: '>=18.17.0', pnpm: '>=8'} peerDependencies: typescript: '>=4.7' @@ -4715,262 +4582,141 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.53.3': - resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm-eabi@4.55.1': - resolution: {integrity: sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==} + '@rollup/rollup-android-arm-eabi@4.59.0': + resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.53.3': - resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==} - cpu: [arm64] - os: [android] - - '@rollup/rollup-android-arm64@4.55.1': - resolution: {integrity: sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==} + '@rollup/rollup-android-arm64@4.59.0': + resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.53.3': - resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==} - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-arm64@4.55.1': - resolution: {integrity: sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==} + '@rollup/rollup-darwin-arm64@4.59.0': + resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.53.3': - resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.55.1': - resolution: {integrity: sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==} + '@rollup/rollup-darwin-x64@4.59.0': + resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.53.3': - resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-arm64@4.55.1': - resolution: {integrity: sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==} + '@rollup/rollup-freebsd-arm64@4.59.0': + resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.53.3': - resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==} + '@rollup/rollup-freebsd-x64@4.59.0': + resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} cpu: [x64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.55.1': - resolution: {integrity: sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==} - cpu: [x64] - os: [freebsd] - - '@rollup/rollup-linux-arm-gnueabihf@4.53.3': - resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} + '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-gnueabihf@4.55.1': - resolution: {integrity: sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm-musleabihf@4.53.3': - resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} - cpu: [arm] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-arm-musleabihf@4.55.1': - resolution: {integrity: sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==} + '@rollup/rollup-linux-arm-musleabihf@4.59.0': + resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.53.3': - resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm64-gnu@4.55.1': - resolution: {integrity: sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==} + '@rollup/rollup-linux-arm64-gnu@4.59.0': + resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.53.3': - resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} + '@rollup/rollup-linux-arm64-musl@4.59.0': + resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-musl@4.55.1': - resolution: {integrity: sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-loong64-gnu@4.53.3': - resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} - cpu: [loong64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-loong64-gnu@4.55.1': - resolution: {integrity: sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==} + '@rollup/rollup-linux-loong64-gnu@4.59.0': + resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.55.1': - resolution: {integrity: sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==} + '@rollup/rollup-linux-loong64-musl@4.59.0': + resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.53.3': - resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-ppc64-gnu@4.55.1': - resolution: {integrity: sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==} + '@rollup/rollup-linux-ppc64-gnu@4.59.0': + resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.55.1': - resolution: {integrity: sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==} + '@rollup/rollup-linux-ppc64-musl@4.59.0': + resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.53.3': - resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-riscv64-gnu@4.55.1': - resolution: {integrity: sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==} + '@rollup/rollup-linux-riscv64-gnu@4.59.0': + resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.53.3': - resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} - cpu: [riscv64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-riscv64-musl@4.55.1': - resolution: {integrity: sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==} + '@rollup/rollup-linux-riscv64-musl@4.59.0': + resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.53.3': - resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-s390x-gnu@4.55.1': - resolution: {integrity: sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==} + '@rollup/rollup-linux-s390x-gnu@4.59.0': + resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.53.3': - resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-x64-gnu@4.55.1': - resolution: {integrity: sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==} + '@rollup/rollup-linux-x64-gnu@4.59.0': + resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.53.3': - resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} + '@rollup/rollup-linux-x64-musl@4.59.0': + resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-linux-x64-musl@4.55.1': - resolution: {integrity: sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==} - cpu: [x64] - os: [linux] - libc: [musl] - - '@rollup/rollup-openbsd-x64@4.55.1': - resolution: {integrity: sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==} + '@rollup/rollup-openbsd-x64@4.59.0': + resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.53.3': - resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} + '@rollup/rollup-openharmony-arm64@4.59.0': + resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-openharmony-arm64@4.55.1': - resolution: {integrity: sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==} - cpu: [arm64] - os: [openharmony] - - '@rollup/rollup-win32-arm64-msvc@4.53.3': - resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==} - cpu: [arm64] - os: [win32] - - '@rollup/rollup-win32-arm64-msvc@4.55.1': - resolution: {integrity: sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==} + '@rollup/rollup-win32-arm64-msvc@4.59.0': + resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.53.3': - resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==} + '@rollup/rollup-win32-ia32-msvc@4.59.0': + resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.55.1': - resolution: {integrity: sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-x64-gnu@4.53.3': - resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==} - cpu: [x64] - os: [win32] - - '@rollup/rollup-win32-x64-gnu@4.55.1': - resolution: {integrity: sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==} - cpu: [x64] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.53.3': - resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==} + '@rollup/rollup-win32-x64-gnu@4.59.0': + resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.55.1': - resolution: {integrity: sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==} + '@rollup/rollup-win32-x64-msvc@4.59.0': + resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} cpu: [x64] os: [win32] @@ -5181,8 +4927,12 @@ packages: resolution: {integrity: sha512-7+QZ38HGcNh/b/7MpvPG6jnw7mliV6UmrquJLqgdxkzJgQEYUcEztvFWRU49z0x4vthF0ixL5lTK601AXrS8IA==} engines: {node: '>= 12.13.0', npm: '>= 6.12.0'} - '@slack/web-api@7.13.0': - resolution: {integrity: sha512-ERcExbWrnkDN8ovoWWe6Wgt/usanj1dWUd18dJLpctUI4mlPS0nKt81Joh8VI+OPbNnY1lIilVt9gdMBD9U2ig==} + '@slack/types@2.20.0': + resolution: {integrity: sha512-PVF6P6nxzDMrzPC8fSCsnwaI+kF8YfEpxf3MqXmdyjyWTYsZQURpkK7WWUWvP5QpH55pB7zyYL9Qem/xSgc5VA==} + engines: {node: '>= 12.13.0', npm: '>= 6.12.0'} + + '@slack/web-api@7.14.1': + resolution: {integrity: sha512-RoygyteJeFswxDPJjUMESn9dldWVMD2xUcHHd9DenVavSfVC6FeVnSdDerOO7m8LLvw4Q132nQM4hX8JiF7dng==} engines: {node: '>= 18', npm: '>= 8.6.0'} '@smithy/abort-controller@4.2.8': @@ -6606,12 +6356,15 @@ packages: peerDependencies: ajv: ^8.8.2 - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@6.14.0: + resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + ajv@8.18.0: + resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} @@ -6716,8 +6469,8 @@ packages: aws4fetch@1.0.20: resolution: {integrity: sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g==} - axios@1.12.2: - resolution: {integrity: sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==} + axios@1.13.5: + resolution: {integrity: sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==} b4a@1.7.3: resolution: {integrity: sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==} @@ -6803,6 +6556,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + bare-events@2.8.2: resolution: {integrity: sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==} peerDependencies: @@ -6814,6 +6571,11 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + baseline-browser-mapping@2.10.0: + resolution: {integrity: sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==} + engines: {node: '>=6.0.0'} + hasBin: true + baseline-browser-mapping@2.8.16: resolution: {integrity: sha512-OMu3BGQ4E7P1ErFsIPpbJh0qvDudM/UuJeHgkAvfWe+0HFJCXh+t/l8L6fVLR55RI/UbKrVLnAXZSVwd9ysWYw==} hasBin: true @@ -6838,11 +6600,11 @@ packages: blake3-wasm@2.1.5: resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} - bn.js@4.12.2: - resolution: {integrity: sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==} + bn.js@4.12.3: + resolution: {integrity: sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==} - bn.js@5.2.2: - resolution: {integrity: sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==} + bn.js@5.2.3: + resolution: {integrity: sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==} body-parser@2.2.2: resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} @@ -6857,8 +6619,9 @@ packages: brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + brace-expansion@5.0.3: + resolution: {integrity: sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==} + engines: {node: 18 || 20 || >=22} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} @@ -6890,8 +6653,8 @@ packages: browserify-zlib@0.2.0: resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} - browserslist@4.26.3: - resolution: {integrity: sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==} + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -7703,8 +7466,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.234: - resolution: {integrity: sha512-RXfEp2x+VRYn8jbKfQlRImzoJU01kyDvVPBmG39eU2iuRVhuS6vQNocB8J0/8GrIMLnPzgz4eW6WiRnJkTuNWg==} + electron-to-chromium@1.5.302: + resolution: {integrity: sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==} elliptic@6.6.1: resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} @@ -7731,6 +7494,10 @@ packages: resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} engines: {node: '>=10.13.0'} + enhanced-resolve@5.19.0: + resolution: {integrity: sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==} + engines: {node: '>=10.13.0'} + entities@1.1.2: resolution: {integrity: sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==} @@ -7765,6 +7532,9 @@ packages: es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-module-lexer@2.0.0: + resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -7795,11 +7565,6 @@ packages: engines: {node: '>=18'} hasBin: true - esbuild@0.25.12: - resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} - engines: {node: '>=18'} - hasBin: true - esbuild@0.27.0: resolution: {integrity: sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==} engines: {node: '>=18'} @@ -8069,8 +7834,11 @@ packages: fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - fast-xml-parser@5.3.4: - resolution: {integrity: sha512-EFd6afGmXlCx8H8WTZHhAoDaWaGyuIBoZJ2mknrNxug+aZKjkp0a0dlars9Izl+jF+7Gu1/5f/2h68cQpe0IiA==} + fast-xml-builder@1.0.0: + resolution: {integrity: sha512-fpZuDogrAgnyt9oDDz+5DBz0zgPdPZz6D4IR7iESxRXElrlGTRkHJ9eEt+SACRJwT0FNFrt71DFQIUFBJfX/uQ==} + + fast-xml-parser@5.4.1: + resolution: {integrity: sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A==} hasBin: true fastq@1.19.1: @@ -8196,8 +7964,8 @@ packages: typescript: '>3.6.0' webpack: ^5.11.0 - form-data@4.0.4: - resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} formatly@0.3.0: @@ -8440,10 +8208,6 @@ packages: unstorage: optional: true - hono@4.11.7: - resolution: {integrity: sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw==} - engines: {node: '>=16.9.0'} - hono@4.12.2: resolution: {integrity: sha512-gJnaDHXKDayjt8ue0n8Gs0A007yKXj4Xzb8+cNjZeYsSzzwKc0Lr+OZgYwVfB0pHfUs17EPoLvrOsEaJ9mj+Tg==} engines: {node: '>=16.9.0'} @@ -9684,15 +9448,15 @@ packages: minimalistic-crypto-utils@1.0.1: resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} - minimatch@10.1.2: - resolution: {integrity: sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==} - engines: {node: 20 || >=22} + minimatch@10.2.4: + resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} + engines: {node: 18 || 20 || >=22} - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} - minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + minimatch@9.0.8: + resolution: {integrity: sha512-reYkDYtj/b19TeqbNZCV4q9t+Yxylf/rYBsLb42SXJatTv4/ylq5lEiAmhA/IToxO7NI2UzNMghHoHuaqDkAjw==} engines: {node: '>=16 || 14 >=14.17'} minimist@1.2.8: @@ -9839,8 +9603,8 @@ packages: resolution: {integrity: sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==} engines: {node: '>=8'} - node-releases@2.0.23: - resolution: {integrity: sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==} + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} node-source-walk@7.0.1: resolution: {integrity: sha512-3VW/8JpPqPvnJvseXowjZcirPisssnBuDikk6JIZ8jQzF7KJQX52iPFX4RYYxLycYH7IbMRSPUOga/esVjy5Yg==} @@ -10104,6 +9868,9 @@ packages: peerDependencies: pg: '>=8.0' + pg-protocol@1.10.3: + resolution: {integrity: sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==} + pg-protocol@1.11.0: resolution: {integrity: sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g==} @@ -10425,8 +10192,8 @@ packages: pure-rand@7.0.1: resolution: {integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==} - qs@6.14.1: - resolution: {integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==} + qs@6.15.0: + resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} engines: {node: '>=0.6'} quansync@0.2.11: @@ -10763,13 +10530,8 @@ packages: resolution: {integrity: sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==} engines: {node: '>= 0.8'} - rollup@4.53.3: - resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - - rollup@4.55.1: - resolution: {integrity: sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==} + rollup@4.59.0: + resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -11020,8 +10782,8 @@ packages: std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} - storybook@9.1.17: - resolution: {integrity: sha512-kfr6kxQAjA96ADlH6FMALJwJ+eM80UqXy106yVHNgdsAP/CdzkkicglRAhZAvUycXK9AeadF6KZ00CWLtVMN4w==} + storybook@9.1.19: + resolution: {integrity: sha512-P7K/b+Pn1sXJzwYCF6hH5Zjdrg4ZlA5Bz9rdOJEdvm6ev27XESDGI+Ql+dfUfUcGOym3Aud4MssJIDEF2ocsyQ==} hasBin: true peerDependencies: prettier: ^2 || ^3 @@ -11118,8 +10880,8 @@ packages: '@types/node': optional: true - strnum@2.1.1: - resolution: {integrity: sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==} + strnum@2.1.2: + resolution: {integrity: sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==} style-loader@3.3.4: resolution: {integrity: sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==} @@ -11202,8 +10964,8 @@ packages: tar-stream@3.1.7: resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} - terser-webpack-plugin@5.3.14: - resolution: {integrity: sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==} + terser-webpack-plugin@5.3.16: + resolution: {integrity: sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==} engines: {node: '>= 10.13.0'} peerDependencies: '@swc/core': '*' @@ -11481,8 +11243,8 @@ packages: resolution: {integrity: sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==} engines: {node: '>=18.17'} - undici@6.22.0: - resolution: {integrity: sha512-hU/10obOIu62MGYjdskASR3CUAiYaFTtC9Pa6vHyf//mAipSvSQg6od2CnJswq7fvzNS3zJhxoRkgNVaHurWKw==} + undici@6.23.0: + resolution: {integrity: sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==} engines: {node: '>=18.17'} undici@7.18.2: @@ -11557,8 +11319,8 @@ packages: unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} - update-browserslist-db@1.1.3: - resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -11770,8 +11532,8 @@ packages: walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - watchpack@2.4.4: - resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==} + watchpack@2.5.1: + resolution: {integrity: sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==} engines: {node: '>=10.13.0'} watskeburt@5.0.0: @@ -11815,8 +11577,8 @@ packages: webpack-virtual-modules@0.6.2: resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} - webpack@5.102.1: - resolution: {integrity: sha512-7h/weGm9d/ywQ6qzJ+Xy+r9n/3qgp/thalBbpOi5i223dPXKi04IBtqPN9nTd+jBc7QKfvDbaBnFipYp4sJAUQ==} + webpack@5.105.2: + resolution: {integrity: sha512-dRXm0a2qcHPUBEzVk8uph0xWSjV/xZxenQQbLwnwP7caQCYpqG1qddwlyEkIDkYn0K8tvmcrZ+bOrzoQ3HxCDw==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -12625,7 +12387,7 @@ snapshots: '@aws-sdk/xml-builder@3.972.2': dependencies: '@smithy/types': 4.12.0 - fast-xml-parser: 5.3.4 + fast-xml-parser: 5.4.1 tslib: 2.8.1 '@aws/lambda-invoke-store@0.2.3': {} @@ -12694,7 +12456,7 @@ snapshots: dependencies: '@babel/compat-data': 7.28.5 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.26.3 + browserslist: 4.28.1 lru-cache: 5.1.1 semver: 6.3.1 @@ -13495,11 +13257,11 @@ snapshots: '@chromaui/rrweb-snapshot': 2.0.0-alpha.18-noAbsolute '@playwright/test': 1.57.0 '@segment/analytics-node': 2.1.3 - '@storybook/addon-essentials': 8.5.8(@types/react@19.2.2)(storybook@9.1.17(prettier@3.8.1)) + '@storybook/addon-essentials': 8.5.8(@types/react@19.2.2)(storybook@9.1.19(prettier@3.8.1)) '@storybook/csf': 0.1.13 - '@storybook/manager-api': 8.5.8(storybook@9.1.17(prettier@3.8.1)) - '@storybook/server-webpack5': 8.5.8(@swc/core@1.12.5)(esbuild@0.27.0)(storybook@9.1.17(prettier@3.8.1))(typescript@5.9.3) - storybook: 9.1.17(prettier@3.8.1) + '@storybook/manager-api': 8.5.8(storybook@9.1.19(prettier@3.8.1)) + '@storybook/server-webpack5': 8.5.8(@swc/core@1.12.5)(esbuild@0.27.0)(storybook@9.1.19(prettier@3.8.1))(typescript@5.9.3) + storybook: 9.1.19(prettier@3.8.1) ts-dedent: 2.2.0 transitivePeerDependencies: - '@rspack/core' @@ -13523,11 +13285,11 @@ snapshots: '@chromaui/rrweb-snapshot': 2.0.0-alpha.18-noAbsolute '@playwright/test': 1.57.0 '@segment/analytics-node': 2.1.3 - '@storybook/addon-essentials': 8.5.8(@types/react@19.2.2)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/addon-essentials': 8.5.8(@types/react@19.2.2)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) '@storybook/csf': 0.1.13 - '@storybook/manager-api': 8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) - '@storybook/server-webpack5': 8.5.8(@swc/core@1.12.5)(esbuild@0.27.0)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(typescript@5.9.3) - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + '@storybook/manager-api': 8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/server-webpack5': 8.5.8(@swc/core@1.12.5)(esbuild@0.27.0)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(typescript@5.9.3) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) ts-dedent: 2.2.0 transitivePeerDependencies: - '@rspack/core' @@ -13547,13 +13309,13 @@ snapshots: - webpack-cli optional: true - '@chromatic-com/storybook@4.1.3(@chromatic-com/playwright@0.12.8(@playwright/test@1.57.0)(@swc/core@1.12.5)(@types/react@19.2.2)(esbuild@0.27.0)(prettier@3.8.1)(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@chromatic-com/storybook@4.1.3(@chromatic-com/playwright@0.12.8(@playwright/test@1.57.0)(@swc/core@1.12.5)(@types/react@19.2.2)(esbuild@0.27.0)(prettier@3.8.1)(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: '@neoconfetti/react': 1.0.0 chromatic: 13.3.3(@chromatic-com/playwright@0.12.8(@playwright/test@1.57.0)(@swc/core@1.12.5)(@types/react@19.2.2)(esbuild@0.27.0)(prettier@3.8.1)(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) filesize: 10.1.6 jsonfile: 6.2.0 - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) strip-ansi: 7.1.2 transitivePeerDependencies: - '@chromatic-com/cypress' @@ -13755,234 +13517,156 @@ snapshots: '@esbuild/aix-ppc64@0.25.10': optional: true - '@esbuild/aix-ppc64@0.25.12': - optional: true - '@esbuild/aix-ppc64@0.27.0': optional: true '@esbuild/android-arm64@0.25.10': optional: true - '@esbuild/android-arm64@0.25.12': - optional: true - '@esbuild/android-arm64@0.27.0': optional: true '@esbuild/android-arm@0.25.10': optional: true - '@esbuild/android-arm@0.25.12': - optional: true - '@esbuild/android-arm@0.27.0': optional: true '@esbuild/android-x64@0.25.10': optional: true - '@esbuild/android-x64@0.25.12': - optional: true - '@esbuild/android-x64@0.27.0': optional: true '@esbuild/darwin-arm64@0.25.10': optional: true - '@esbuild/darwin-arm64@0.25.12': - optional: true - '@esbuild/darwin-arm64@0.27.0': optional: true '@esbuild/darwin-x64@0.25.10': optional: true - '@esbuild/darwin-x64@0.25.12': - optional: true - '@esbuild/darwin-x64@0.27.0': optional: true '@esbuild/freebsd-arm64@0.25.10': optional: true - '@esbuild/freebsd-arm64@0.25.12': - optional: true - '@esbuild/freebsd-arm64@0.27.0': optional: true '@esbuild/freebsd-x64@0.25.10': optional: true - '@esbuild/freebsd-x64@0.25.12': - optional: true - '@esbuild/freebsd-x64@0.27.0': optional: true '@esbuild/linux-arm64@0.25.10': optional: true - '@esbuild/linux-arm64@0.25.12': - optional: true - '@esbuild/linux-arm64@0.27.0': optional: true '@esbuild/linux-arm@0.25.10': optional: true - '@esbuild/linux-arm@0.25.12': - optional: true - '@esbuild/linux-arm@0.27.0': optional: true '@esbuild/linux-ia32@0.25.10': optional: true - '@esbuild/linux-ia32@0.25.12': - optional: true - '@esbuild/linux-ia32@0.27.0': optional: true '@esbuild/linux-loong64@0.25.10': optional: true - '@esbuild/linux-loong64@0.25.12': - optional: true - '@esbuild/linux-loong64@0.27.0': optional: true '@esbuild/linux-mips64el@0.25.10': optional: true - '@esbuild/linux-mips64el@0.25.12': - optional: true - '@esbuild/linux-mips64el@0.27.0': optional: true '@esbuild/linux-ppc64@0.25.10': optional: true - '@esbuild/linux-ppc64@0.25.12': - optional: true - '@esbuild/linux-ppc64@0.27.0': optional: true '@esbuild/linux-riscv64@0.25.10': optional: true - '@esbuild/linux-riscv64@0.25.12': - optional: true - '@esbuild/linux-riscv64@0.27.0': optional: true '@esbuild/linux-s390x@0.25.10': optional: true - '@esbuild/linux-s390x@0.25.12': - optional: true - '@esbuild/linux-s390x@0.27.0': optional: true '@esbuild/linux-x64@0.25.10': optional: true - '@esbuild/linux-x64@0.25.12': - optional: true - '@esbuild/linux-x64@0.27.0': optional: true '@esbuild/netbsd-arm64@0.25.10': optional: true - '@esbuild/netbsd-arm64@0.25.12': - optional: true - '@esbuild/netbsd-arm64@0.27.0': optional: true '@esbuild/netbsd-x64@0.25.10': optional: true - '@esbuild/netbsd-x64@0.25.12': - optional: true - '@esbuild/netbsd-x64@0.27.0': optional: true '@esbuild/openbsd-arm64@0.25.10': optional: true - '@esbuild/openbsd-arm64@0.25.12': - optional: true - '@esbuild/openbsd-arm64@0.27.0': optional: true '@esbuild/openbsd-x64@0.25.10': optional: true - '@esbuild/openbsd-x64@0.25.12': - optional: true - '@esbuild/openbsd-x64@0.27.0': optional: true '@esbuild/openharmony-arm64@0.25.10': optional: true - '@esbuild/openharmony-arm64@0.25.12': - optional: true - '@esbuild/openharmony-arm64@0.27.0': optional: true '@esbuild/sunos-x64@0.25.10': optional: true - '@esbuild/sunos-x64@0.25.12': - optional: true - '@esbuild/sunos-x64@0.27.0': optional: true '@esbuild/win32-arm64@0.25.10': optional: true - '@esbuild/win32-arm64@0.25.12': - optional: true - '@esbuild/win32-arm64@0.27.0': optional: true '@esbuild/win32-ia32@0.25.10': optional: true - '@esbuild/win32-ia32@0.25.12': - optional: true - '@esbuild/win32-ia32@0.27.0': optional: true '@esbuild/win32-x64@0.25.10': optional: true - '@esbuild/win32-x64@0.25.12': - optional: true - '@esbuild/win32-x64@0.27.0': optional: true @@ -13997,7 +13681,7 @@ snapshots: dependencies: '@eslint/object-schema': 2.1.7 debug: 4.4.3 - minimatch: 3.1.2 + minimatch: 3.1.5 transitivePeerDependencies: - supports-color @@ -14011,14 +13695,14 @@ snapshots: '@eslint/eslintrc@3.3.3': dependencies: - ajv: 6.12.6 + ajv: 6.14.0 debug: 4.4.3 espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 js-yaml: 4.1.1 - minimatch: 3.1.2 + minimatch: 3.1.5 strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color @@ -14055,9 +13739,9 @@ snapshots: dependencies: '@hapi/hoek': 9.3.0 - '@hono/node-server@1.19.9(hono@4.11.7)': + '@hono/node-server@1.19.9(hono@4.12.2)': dependencies: - hono: 4.11.7 + hono: 4.12.2 '@hono/trpc-server@0.4.2(@trpc/server@11.9.0(typescript@5.9.3))(hono@4.12.2)': dependencies: @@ -14171,12 +13855,6 @@ snapshots: '@img/sharp-win32-x64@0.34.5': optional: true - '@isaacs/balanced-match@4.0.1': {} - - '@isaacs/brace-expansion@5.0.1': - dependencies: - '@isaacs/balanced-match': 4.0.1 - '@istanbuljs/load-nyc-config@1.1.0': dependencies: camelcase: 5.3.1 @@ -14595,12 +14273,12 @@ snapshots: dependencies: '@lukeed/csprng': 1.1.0 - '@mdx-js/loader@3.1.1(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0))': + '@mdx-js/loader@3.1.1(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0))': dependencies: '@mdx-js/mdx': 3.1.1 source-map: 0.7.6 optionalDependencies: - webpack: 5.102.1(@swc/core@1.12.5)(esbuild@0.27.0) + webpack: 5.105.2(@swc/core@1.12.5)(esbuild@0.27.0) transitivePeerDependencies: - supports-color @@ -14653,8 +14331,8 @@ snapshots: '@modelcontextprotocol/sdk@1.27.0(zod@4.3.6)': dependencies: - '@hono/node-server': 1.19.9(hono@4.11.7) - ajv: 8.17.1 + '@hono/node-server': 1.19.9(hono@4.12.2) + ajv: 8.18.0 ajv-formats: 3.0.1 content-type: 1.0.5 cors: 2.8.6 @@ -14663,7 +14341,7 @@ snapshots: eventsource-parser: 3.0.6 express: 5.2.1 express-rate-limit: 8.2.1(express@5.2.1) - hono: 4.11.7 + hono: 4.12.2 jose: 6.1.3 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 @@ -14713,11 +14391,11 @@ snapshots: dependencies: fast-glob: 3.3.1 - '@next/mdx@16.1.6(@mdx-js/loader@3.1.1(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)))(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@19.2.0))': + '@next/mdx@16.1.6(@mdx-js/loader@3.1.1(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)))(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@19.2.0))': dependencies: source-map: 0.7.6 optionalDependencies: - '@mdx-js/loader': 3.1.1(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) + '@mdx-js/loader': 3.1.1(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) '@mdx-js/react': 3.1.1(@types/react@19.2.2)(react@19.2.0) '@next/swc-darwin-arm64@16.1.6': @@ -15193,7 +14871,7 @@ snapshots: dependencies: playwright: 1.57.0 - '@pmmmwh/react-refresh-webpack-plugin@0.5.17(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-hot-middleware@2.26.1)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0))': + '@pmmmwh/react-refresh-webpack-plugin@0.5.17(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-hot-middleware@2.26.1)(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0))': dependencies: ansi-html: 0.0.9 core-js-pure: 3.47.0 @@ -15203,7 +14881,7 @@ snapshots: react-refresh: 0.14.2 schema-utils: 4.3.3 source-map: 0.7.6 - webpack: 5.102.1(@swc/core@1.12.5)(esbuild@0.27.0) + webpack: 5.105.2(@swc/core@1.12.5)(esbuild@0.27.0) optionalDependencies: type-fest: 4.41.0 webpack-hot-middleware: 2.26.1 @@ -15233,11 +14911,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@qdrant/js-client-rest@1.16.2(typescript@5.9.3)': + '@qdrant/js-client-rest@1.17.0(typescript@5.9.3)': dependencies: '@qdrant/openapi-typescript-fetch': 1.2.6 typescript: 5.9.3 - undici: 6.22.0 + undici: 6.23.0 '@qdrant/openapi-typescript-fetch@1.2.6': {} @@ -15740,9 +15418,9 @@ snapshots: '@rocicorp/resolver@1.0.2': {} - '@rollup/plugin-commonjs@28.0.1(rollup@4.53.3)': + '@rollup/plugin-commonjs@28.0.1(rollup@4.59.0)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.53.3) + '@rollup/pluginutils': 5.3.0(rollup@4.59.0) commondir: 1.0.1 estree-walker: 2.0.2 fdir: 6.5.0(picomatch@4.0.3) @@ -15750,155 +15428,89 @@ snapshots: magic-string: 0.30.21 picomatch: 4.0.3 optionalDependencies: - rollup: 4.53.3 + rollup: 4.59.0 - '@rollup/pluginutils@5.3.0(rollup@4.53.3)': + '@rollup/pluginutils@5.3.0(rollup@4.59.0)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: - rollup: 4.53.3 - - '@rollup/rollup-android-arm-eabi@4.53.3': - optional: true - - '@rollup/rollup-android-arm-eabi@4.55.1': - optional: true - - '@rollup/rollup-android-arm64@4.53.3': - optional: true - - '@rollup/rollup-android-arm64@4.55.1': - optional: true - - '@rollup/rollup-darwin-arm64@4.53.3': - optional: true - - '@rollup/rollup-darwin-arm64@4.55.1': - optional: true - - '@rollup/rollup-darwin-x64@4.53.3': - optional: true - - '@rollup/rollup-darwin-x64@4.55.1': - optional: true - - '@rollup/rollup-freebsd-arm64@4.53.3': - optional: true - - '@rollup/rollup-freebsd-arm64@4.55.1': - optional: true - - '@rollup/rollup-freebsd-x64@4.53.3': - optional: true - - '@rollup/rollup-freebsd-x64@4.55.1': - optional: true + rollup: 4.59.0 - '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + '@rollup/rollup-android-arm-eabi@4.59.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.55.1': + '@rollup/rollup-android-arm64@4.59.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.53.3': + '@rollup/rollup-darwin-arm64@4.59.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.55.1': + '@rollup/rollup-darwin-x64@4.59.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.53.3': + '@rollup/rollup-freebsd-arm64@4.59.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.55.1': + '@rollup/rollup-freebsd-x64@4.59.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.53.3': + '@rollup/rollup-linux-arm-gnueabihf@4.59.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.55.1': + '@rollup/rollup-linux-arm-musleabihf@4.59.0': optional: true - '@rollup/rollup-linux-loong64-gnu@4.53.3': + '@rollup/rollup-linux-arm64-gnu@4.59.0': optional: true - '@rollup/rollup-linux-loong64-gnu@4.55.1': + '@rollup/rollup-linux-arm64-musl@4.59.0': optional: true - '@rollup/rollup-linux-loong64-musl@4.55.1': + '@rollup/rollup-linux-loong64-gnu@4.59.0': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.53.3': + '@rollup/rollup-linux-loong64-musl@4.59.0': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.55.1': + '@rollup/rollup-linux-ppc64-gnu@4.59.0': optional: true - '@rollup/rollup-linux-ppc64-musl@4.55.1': + '@rollup/rollup-linux-ppc64-musl@4.59.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.53.3': + '@rollup/rollup-linux-riscv64-gnu@4.59.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.55.1': + '@rollup/rollup-linux-riscv64-musl@4.59.0': optional: true - '@rollup/rollup-linux-riscv64-musl@4.53.3': + '@rollup/rollup-linux-s390x-gnu@4.59.0': optional: true - '@rollup/rollup-linux-riscv64-musl@4.55.1': + '@rollup/rollup-linux-x64-gnu@4.59.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.53.3': + '@rollup/rollup-linux-x64-musl@4.59.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.55.1': + '@rollup/rollup-openbsd-x64@4.59.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.53.3': + '@rollup/rollup-openharmony-arm64@4.59.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.55.1': + '@rollup/rollup-win32-arm64-msvc@4.59.0': optional: true - '@rollup/rollup-linux-x64-musl@4.53.3': + '@rollup/rollup-win32-ia32-msvc@4.59.0': optional: true - '@rollup/rollup-linux-x64-musl@4.55.1': + '@rollup/rollup-win32-x64-gnu@4.59.0': optional: true - '@rollup/rollup-openbsd-x64@4.55.1': - optional: true - - '@rollup/rollup-openharmony-arm64@4.53.3': - optional: true - - '@rollup/rollup-openharmony-arm64@4.55.1': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.53.3': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.55.1': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.53.3': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.55.1': - optional: true - - '@rollup/rollup-win32-x64-gnu@4.53.3': - optional: true - - '@rollup/rollup-win32-x64-gnu@4.55.1': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.53.3': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.55.1': + '@rollup/rollup-win32-x64-msvc@4.59.0': optional: true '@sapphire/async-queue@1.5.5': {} @@ -16030,11 +15642,11 @@ snapshots: '@sentry/core@10.29.0': {} - '@sentry/nextjs@10.29.0(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@16.1.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0))': + '@sentry/nextjs@10.29.0(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@16.1.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0))': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.38.0 - '@rollup/plugin-commonjs': 28.0.1(rollup@4.53.3) + '@rollup/plugin-commonjs': 28.0.1(rollup@4.59.0) '@sentry-internal/browser-utils': 10.29.0 '@sentry/bundler-plugin-core': 4.6.1 '@sentry/core': 10.29.0 @@ -16042,10 +15654,10 @@ snapshots: '@sentry/opentelemetry': 10.29.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0) '@sentry/react': 10.29.0(react@19.2.0) '@sentry/vercel-edge': 10.29.0 - '@sentry/webpack-plugin': 4.6.1(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) + '@sentry/webpack-plugin': 4.6.1(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) next: 16.1.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) resolve: 1.22.8 - rollup: 4.53.3 + rollup: 4.59.0 stacktrace-parser: 0.1.11 transitivePeerDependencies: - '@opentelemetry/context-async-hooks' @@ -16108,7 +15720,7 @@ snapshots: '@sentry/node-core': 10.29.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.208.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0) '@sentry/opentelemetry': 10.29.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0) import-in-the-middle: 2.0.0 - minimatch: 9.0.5 + minimatch: 9.0.8 transitivePeerDependencies: - supports-color @@ -16143,12 +15755,12 @@ snapshots: '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) '@sentry/core': 10.29.0 - '@sentry/webpack-plugin@4.6.1(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0))': + '@sentry/webpack-plugin@4.6.1(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0))': dependencies: '@sentry/bundler-plugin-core': 4.6.1 unplugin: 1.0.1 uuid: 9.0.1 - webpack: 5.102.1(@swc/core@1.12.5)(esbuild@0.27.0) + webpack: 5.105.2(@swc/core@1.12.5)(esbuild@0.27.0) transitivePeerDependencies: - encoding - supports-color @@ -16186,7 +15798,7 @@ snapshots: '@slack/oauth@3.0.4': dependencies: '@slack/logger': 4.0.0 - '@slack/web-api': 7.13.0 + '@slack/web-api': 7.14.1 '@types/jsonwebtoken': 9.0.10 '@types/node': 22.19.1 jsonwebtoken: 9.0.3 @@ -16195,15 +15807,17 @@ snapshots: '@slack/types@2.19.0': {} - '@slack/web-api@7.13.0': + '@slack/types@2.20.0': {} + + '@slack/web-api@7.14.1': dependencies: '@slack/logger': 4.0.0 - '@slack/types': 2.19.0 + '@slack/types': 2.20.0 '@types/node': 22.19.1 '@types/retry': 0.12.0 - axios: 1.12.2 + axios: 1.13.5 eventemitter3: 5.0.1 - form-data: 4.0.4 + form-data: 4.0.5 is-electron: 2.2.2 is-stream: 2.0.1 p-queue: 6.6.2 @@ -16556,243 +16170,243 @@ snapshots: '@standard-schema/utils@0.3.0': {} - '@storybook/addon-actions@8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/addon-actions@8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: '@storybook/global': 5.0.0 '@types/uuid': 9.0.8 dequal: 2.0.3 polished: 4.3.1 - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) uuid: 9.0.1 optional: true - '@storybook/addon-actions@8.5.8(storybook@9.1.17(prettier@3.8.1))': + '@storybook/addon-actions@8.5.8(storybook@9.1.19(prettier@3.8.1))': dependencies: '@storybook/global': 5.0.0 '@types/uuid': 9.0.8 dequal: 2.0.3 polished: 4.3.1 - storybook: 9.1.17(prettier@3.8.1) + storybook: 9.1.19(prettier@3.8.1) uuid: 9.0.1 - '@storybook/addon-backgrounds@8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/addon-backgrounds@8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: '@storybook/global': 5.0.0 memoizerific: 1.11.3 - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) ts-dedent: 2.2.0 optional: true - '@storybook/addon-backgrounds@8.5.8(storybook@9.1.17(prettier@3.8.1))': + '@storybook/addon-backgrounds@8.5.8(storybook@9.1.19(prettier@3.8.1))': dependencies: '@storybook/global': 5.0.0 memoizerific: 1.11.3 - storybook: 9.1.17(prettier@3.8.1) + storybook: 9.1.19(prettier@3.8.1) ts-dedent: 2.2.0 - '@storybook/addon-controls@8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/addon-controls@8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: '@storybook/global': 5.0.0 dequal: 2.0.3 - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) ts-dedent: 2.2.0 optional: true - '@storybook/addon-controls@8.5.8(storybook@9.1.17(prettier@3.8.1))': + '@storybook/addon-controls@8.5.8(storybook@9.1.19(prettier@3.8.1))': dependencies: '@storybook/global': 5.0.0 dequal: 2.0.3 - storybook: 9.1.17(prettier@3.8.1) + storybook: 9.1.19(prettier@3.8.1) ts-dedent: 2.2.0 - '@storybook/addon-docs@8.5.8(@types/react@19.2.2)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/addon-docs@8.5.8(@types/react@19.2.2)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: '@mdx-js/react': 3.1.1(@types/react@19.2.2)(react@19.2.3) - '@storybook/blocks': 8.5.8(react-dom@19.2.0(react@19.2.0))(react@19.2.3)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) - '@storybook/csf-plugin': 8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) - '@storybook/react-dom-shim': 8.5.8(react-dom@19.2.0(react@19.2.0))(react@19.2.3)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/blocks': 8.5.8(react-dom@19.2.0(react@19.2.0))(react@19.2.3)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/csf-plugin': 8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/react-dom-shim': 8.5.8(react-dom@19.2.0(react@19.2.0))(react@19.2.3)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) react: 19.2.3 react-dom: 19.2.0(react@19.2.3) - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' optional: true - '@storybook/addon-docs@8.5.8(@types/react@19.2.2)(storybook@9.1.17(prettier@3.8.1))': + '@storybook/addon-docs@8.5.8(@types/react@19.2.2)(storybook@9.1.19(prettier@3.8.1))': dependencies: '@mdx-js/react': 3.1.1(@types/react@19.2.2)(react@19.2.3) - '@storybook/blocks': 8.5.8(react-dom@19.2.0(react@19.2.0))(react@19.2.3)(storybook@9.1.17(prettier@3.8.1)) - '@storybook/csf-plugin': 8.5.8(storybook@9.1.17(prettier@3.8.1)) - '@storybook/react-dom-shim': 8.5.8(react-dom@19.2.0(react@19.2.0))(react@19.2.3)(storybook@9.1.17(prettier@3.8.1)) + '@storybook/blocks': 8.5.8(react-dom@19.2.0(react@19.2.0))(react@19.2.3)(storybook@9.1.19(prettier@3.8.1)) + '@storybook/csf-plugin': 8.5.8(storybook@9.1.19(prettier@3.8.1)) + '@storybook/react-dom-shim': 8.5.8(react-dom@19.2.0(react@19.2.0))(react@19.2.3)(storybook@9.1.19(prettier@3.8.1)) react: 19.2.3 react-dom: 19.2.0(react@19.2.3) - storybook: 9.1.17(prettier@3.8.1) + storybook: 9.1.19(prettier@3.8.1) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' - '@storybook/addon-docs@9.1.17(@types/react@19.2.2)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/addon-docs@9.1.17(@types/react@19.2.2)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: '@mdx-js/react': 3.1.1(@types/react@19.2.2)(react@19.2.3) - '@storybook/csf-plugin': 9.1.17(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/csf-plugin': 9.1.17(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) '@storybook/icons': 1.6.0(react-dom@19.2.0(react@19.2.0))(react@19.2.3) - '@storybook/react-dom-shim': 9.1.17(react-dom@19.2.0(react@19.2.0))(react@19.2.3)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/react-dom-shim': 9.1.17(react-dom@19.2.0(react@19.2.0))(react@19.2.3)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) react: 19.2.3 react-dom: 19.2.0(react@19.2.3) - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' - '@storybook/addon-essentials@8.5.8(@types/react@19.2.2)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': - dependencies: - '@storybook/addon-actions': 8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) - '@storybook/addon-backgrounds': 8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) - '@storybook/addon-controls': 8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) - '@storybook/addon-docs': 8.5.8(@types/react@19.2.2)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) - '@storybook/addon-highlight': 8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) - '@storybook/addon-measure': 8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) - '@storybook/addon-outline': 8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) - '@storybook/addon-toolbars': 8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) - '@storybook/addon-viewport': 8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + '@storybook/addon-essentials@8.5.8(@types/react@19.2.2)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + dependencies: + '@storybook/addon-actions': 8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/addon-backgrounds': 8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/addon-controls': 8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/addon-docs': 8.5.8(@types/react@19.2.2)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/addon-highlight': 8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/addon-measure': 8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/addon-outline': 8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/addon-toolbars': 8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/addon-viewport': 8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' optional: true - '@storybook/addon-essentials@8.5.8(@types/react@19.2.2)(storybook@9.1.17(prettier@3.8.1))': - dependencies: - '@storybook/addon-actions': 8.5.8(storybook@9.1.17(prettier@3.8.1)) - '@storybook/addon-backgrounds': 8.5.8(storybook@9.1.17(prettier@3.8.1)) - '@storybook/addon-controls': 8.5.8(storybook@9.1.17(prettier@3.8.1)) - '@storybook/addon-docs': 8.5.8(@types/react@19.2.2)(storybook@9.1.17(prettier@3.8.1)) - '@storybook/addon-highlight': 8.5.8(storybook@9.1.17(prettier@3.8.1)) - '@storybook/addon-measure': 8.5.8(storybook@9.1.17(prettier@3.8.1)) - '@storybook/addon-outline': 8.5.8(storybook@9.1.17(prettier@3.8.1)) - '@storybook/addon-toolbars': 8.5.8(storybook@9.1.17(prettier@3.8.1)) - '@storybook/addon-viewport': 8.5.8(storybook@9.1.17(prettier@3.8.1)) - storybook: 9.1.17(prettier@3.8.1) + '@storybook/addon-essentials@8.5.8(@types/react@19.2.2)(storybook@9.1.19(prettier@3.8.1))': + dependencies: + '@storybook/addon-actions': 8.5.8(storybook@9.1.19(prettier@3.8.1)) + '@storybook/addon-backgrounds': 8.5.8(storybook@9.1.19(prettier@3.8.1)) + '@storybook/addon-controls': 8.5.8(storybook@9.1.19(prettier@3.8.1)) + '@storybook/addon-docs': 8.5.8(@types/react@19.2.2)(storybook@9.1.19(prettier@3.8.1)) + '@storybook/addon-highlight': 8.5.8(storybook@9.1.19(prettier@3.8.1)) + '@storybook/addon-measure': 8.5.8(storybook@9.1.19(prettier@3.8.1)) + '@storybook/addon-outline': 8.5.8(storybook@9.1.19(prettier@3.8.1)) + '@storybook/addon-toolbars': 8.5.8(storybook@9.1.19(prettier@3.8.1)) + '@storybook/addon-viewport': 8.5.8(storybook@9.1.19(prettier@3.8.1)) + storybook: 9.1.19(prettier@3.8.1) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' - '@storybook/addon-highlight@8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/addon-highlight@8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: '@storybook/global': 5.0.0 - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) optional: true - '@storybook/addon-highlight@8.5.8(storybook@9.1.17(prettier@3.8.1))': + '@storybook/addon-highlight@8.5.8(storybook@9.1.19(prettier@3.8.1))': dependencies: '@storybook/global': 5.0.0 - storybook: 9.1.17(prettier@3.8.1) + storybook: 9.1.19(prettier@3.8.1) - '@storybook/addon-links@9.1.17(react@19.2.0)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/addon-links@9.1.17(react@19.2.0)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: '@storybook/global': 5.0.0 - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) optionalDependencies: react: 19.2.0 - '@storybook/addon-measure@8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/addon-measure@8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: '@storybook/global': 5.0.0 - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) tiny-invariant: 1.3.3 optional: true - '@storybook/addon-measure@8.5.8(storybook@9.1.17(prettier@3.8.1))': + '@storybook/addon-measure@8.5.8(storybook@9.1.19(prettier@3.8.1))': dependencies: '@storybook/global': 5.0.0 - storybook: 9.1.17(prettier@3.8.1) + storybook: 9.1.19(prettier@3.8.1) tiny-invariant: 1.3.3 - '@storybook/addon-outline@8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/addon-outline@8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: '@storybook/global': 5.0.0 - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) ts-dedent: 2.2.0 optional: true - '@storybook/addon-outline@8.5.8(storybook@9.1.17(prettier@3.8.1))': + '@storybook/addon-outline@8.5.8(storybook@9.1.19(prettier@3.8.1))': dependencies: '@storybook/global': 5.0.0 - storybook: 9.1.17(prettier@3.8.1) + storybook: 9.1.19(prettier@3.8.1) ts-dedent: 2.2.0 - '@storybook/addon-themes@9.1.17(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/addon-themes@9.1.17(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) ts-dedent: 2.2.0 - '@storybook/addon-toolbars@8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/addon-toolbars@8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) optional: true - '@storybook/addon-toolbars@8.5.8(storybook@9.1.17(prettier@3.8.1))': + '@storybook/addon-toolbars@8.5.8(storybook@9.1.19(prettier@3.8.1))': dependencies: - storybook: 9.1.17(prettier@3.8.1) + storybook: 9.1.19(prettier@3.8.1) - '@storybook/addon-viewport@8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/addon-viewport@8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: memoizerific: 1.11.3 - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) optional: true - '@storybook/addon-viewport@8.5.8(storybook@9.1.17(prettier@3.8.1))': + '@storybook/addon-viewport@8.5.8(storybook@9.1.19(prettier@3.8.1))': dependencies: memoizerific: 1.11.3 - storybook: 9.1.17(prettier@3.8.1) + storybook: 9.1.19(prettier@3.8.1) - '@storybook/blocks@8.5.8(react-dom@19.2.0(react@19.2.0))(react@19.2.3)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/blocks@8.5.8(react-dom@19.2.0(react@19.2.0))(react@19.2.3)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: '@storybook/csf': 0.1.12 '@storybook/icons': 1.6.0(react-dom@19.2.0(react@19.2.0))(react@19.2.3) - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) ts-dedent: 2.2.0 optionalDependencies: react: 19.2.3 react-dom: 19.2.0(react@19.2.0) optional: true - '@storybook/blocks@8.5.8(react-dom@19.2.0(react@19.2.0))(react@19.2.3)(storybook@9.1.17(prettier@3.8.1))': + '@storybook/blocks@8.5.8(react-dom@19.2.0(react@19.2.0))(react@19.2.3)(storybook@9.1.19(prettier@3.8.1))': dependencies: '@storybook/csf': 0.1.12 '@storybook/icons': 1.6.0(react-dom@19.2.0(react@19.2.0))(react@19.2.3) - storybook: 9.1.17(prettier@3.8.1) + storybook: 9.1.19(prettier@3.8.1) ts-dedent: 2.2.0 optionalDependencies: react: 19.2.3 react-dom: 19.2.0(react@19.2.0) - '@storybook/builder-webpack5@8.5.8(@swc/core@1.12.5)(esbuild@0.27.0)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(typescript@5.9.3)': + '@storybook/builder-webpack5@8.5.8(@swc/core@1.12.5)(esbuild@0.27.0)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(typescript@5.9.3)': dependencies: - '@storybook/core-webpack': 8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/core-webpack': 8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) '@types/semver': 7.7.1 browser-assert: 1.2.1 case-sensitive-paths-webpack-plugin: 2.4.0 cjs-module-lexer: 1.4.3 constants-browserify: 1.0.0 - css-loader: 6.11.0(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) + css-loader: 6.11.0(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) es-module-lexer: 1.7.0 - fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.9.3)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) - html-webpack-plugin: 5.6.4(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) + fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.9.3)(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) + html-webpack-plugin: 5.6.4(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) magic-string: 0.30.21 path-browserify: 1.0.1 process: 0.11.10 semver: 7.7.3 - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) - style-loader: 3.3.4(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) - terser-webpack-plugin: 5.3.14(@swc/core@1.12.5)(esbuild@0.27.0)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + style-loader: 3.3.4(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) + terser-webpack-plugin: 5.3.16(@swc/core@1.12.5)(esbuild@0.27.0)(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) ts-dedent: 2.2.0 url: 0.11.4 util: 0.12.5 util-deprecate: 1.0.2 - webpack: 5.102.1(@swc/core@1.12.5)(esbuild@0.27.0) - webpack-dev-middleware: 6.1.3(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) + webpack: 5.105.2(@swc/core@1.12.5)(esbuild@0.27.0) + webpack-dev-middleware: 6.1.3(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) webpack-hot-middleware: 2.26.1 webpack-virtual-modules: 0.6.2 optionalDependencies: @@ -16805,31 +16419,31 @@ snapshots: - webpack-cli optional: true - '@storybook/builder-webpack5@8.5.8(@swc/core@1.12.5)(esbuild@0.27.0)(storybook@9.1.17(prettier@3.8.1))(typescript@5.9.3)': + '@storybook/builder-webpack5@8.5.8(@swc/core@1.12.5)(esbuild@0.27.0)(storybook@9.1.19(prettier@3.8.1))(typescript@5.9.3)': dependencies: - '@storybook/core-webpack': 8.5.8(storybook@9.1.17(prettier@3.8.1)) + '@storybook/core-webpack': 8.5.8(storybook@9.1.19(prettier@3.8.1)) '@types/semver': 7.7.1 browser-assert: 1.2.1 case-sensitive-paths-webpack-plugin: 2.4.0 cjs-module-lexer: 1.4.3 constants-browserify: 1.0.0 - css-loader: 6.11.0(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) + css-loader: 6.11.0(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) es-module-lexer: 1.7.0 - fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.9.3)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) - html-webpack-plugin: 5.6.4(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) + fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.9.3)(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) + html-webpack-plugin: 5.6.4(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) magic-string: 0.30.21 path-browserify: 1.0.1 process: 0.11.10 semver: 7.7.3 - storybook: 9.1.17(prettier@3.8.1) - style-loader: 3.3.4(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) - terser-webpack-plugin: 5.3.14(@swc/core@1.12.5)(esbuild@0.27.0)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) + storybook: 9.1.19(prettier@3.8.1) + style-loader: 3.3.4(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) + terser-webpack-plugin: 5.3.16(@swc/core@1.12.5)(esbuild@0.27.0)(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) ts-dedent: 2.2.0 url: 0.11.4 util: 0.12.5 util-deprecate: 1.0.2 - webpack: 5.102.1(@swc/core@1.12.5)(esbuild@0.27.0) - webpack-dev-middleware: 6.1.3(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) + webpack: 5.105.2(@swc/core@1.12.5)(esbuild@0.27.0) + webpack-dev-middleware: 6.1.3(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) webpack-hot-middleware: 2.26.1 webpack-virtual-modules: 0.6.2 optionalDependencies: @@ -16841,22 +16455,22 @@ snapshots: - uglify-js - webpack-cli - '@storybook/builder-webpack5@9.1.17(@swc/core@1.12.5)(esbuild@0.27.0)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(typescript@5.9.3)': + '@storybook/builder-webpack5@9.1.17(@swc/core@1.12.5)(esbuild@0.27.0)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(typescript@5.9.3)': dependencies: - '@storybook/core-webpack': 9.1.17(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/core-webpack': 9.1.17(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) case-sensitive-paths-webpack-plugin: 2.4.0 cjs-module-lexer: 1.4.3 - css-loader: 6.11.0(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) + css-loader: 6.11.0(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) es-module-lexer: 1.7.0 - fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.9.3)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) - html-webpack-plugin: 5.6.4(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) + fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.9.3)(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) + html-webpack-plugin: 5.6.4(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) magic-string: 0.30.21 - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) - style-loader: 3.3.4(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) - terser-webpack-plugin: 5.3.14(@swc/core@1.12.5)(esbuild@0.27.0)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + style-loader: 3.3.4(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) + terser-webpack-plugin: 5.3.16(@swc/core@1.12.5)(esbuild@0.27.0)(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) ts-dedent: 2.2.0 - webpack: 5.102.1(@swc/core@1.12.5)(esbuild@0.27.0) - webpack-dev-middleware: 6.1.3(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) + webpack: 5.105.2(@swc/core@1.12.5)(esbuild@0.27.0) + webpack-dev-middleware: 6.1.3(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) webpack-hot-middleware: 2.26.1 webpack-virtual-modules: 0.6.2 optionalDependencies: @@ -16868,45 +16482,45 @@ snapshots: - uglify-js - webpack-cli - '@storybook/components@8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/components@8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) optional: true - '@storybook/components@8.5.8(storybook@9.1.17(prettier@3.8.1))': + '@storybook/components@8.5.8(storybook@9.1.19(prettier@3.8.1))': dependencies: - storybook: 9.1.17(prettier@3.8.1) + storybook: 9.1.19(prettier@3.8.1) - '@storybook/core-webpack@8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/core-webpack@8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) ts-dedent: 2.2.0 optional: true - '@storybook/core-webpack@8.5.8(storybook@9.1.17(prettier@3.8.1))': + '@storybook/core-webpack@8.5.8(storybook@9.1.19(prettier@3.8.1))': dependencies: - storybook: 9.1.17(prettier@3.8.1) + storybook: 9.1.19(prettier@3.8.1) ts-dedent: 2.2.0 - '@storybook/core-webpack@9.1.17(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/core-webpack@9.1.17(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) ts-dedent: 2.2.0 - '@storybook/csf-plugin@8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/csf-plugin@8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) unplugin: 1.16.1 optional: true - '@storybook/csf-plugin@8.5.8(storybook@9.1.17(prettier@3.8.1))': + '@storybook/csf-plugin@8.5.8(storybook@9.1.19(prettier@3.8.1))': dependencies: - storybook: 9.1.17(prettier@3.8.1) + storybook: 9.1.19(prettier@3.8.1) unplugin: 1.16.1 - '@storybook/csf-plugin@9.1.17(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/csf-plugin@9.1.17(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) unplugin: 1.16.1 '@storybook/csf@0.1.12': @@ -16924,16 +16538,16 @@ snapshots: react: 19.2.3 react-dom: 19.2.0(react@19.2.0) - '@storybook/manager-api@8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/manager-api@8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) optional: true - '@storybook/manager-api@8.5.8(storybook@9.1.17(prettier@3.8.1))': + '@storybook/manager-api@8.5.8(storybook@9.1.19(prettier@3.8.1))': dependencies: - storybook: 9.1.17(prettier@3.8.1) + storybook: 9.1.19(prettier@3.8.1) - '@storybook/nextjs@9.1.17(@swc/core@1.12.5)(esbuild@0.27.0)(next@16.1.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(type-fest@4.41.0)(typescript@5.9.3)(webpack-hot-middleware@2.26.1)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0))': + '@storybook/nextjs@9.1.17(@swc/core@1.12.5)(esbuild@0.27.0)(next@16.1.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(type-fest@4.41.0)(typescript@5.9.3)(webpack-hot-middleware@2.26.1)(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0))': dependencies: '@babel/core': 7.28.5 '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.5) @@ -16948,33 +16562,33 @@ snapshots: '@babel/preset-react': 7.28.5(@babel/core@7.28.5) '@babel/preset-typescript': 7.28.5(@babel/core@7.28.5) '@babel/runtime': 7.28.4 - '@pmmmwh/react-refresh-webpack-plugin': 0.5.17(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-hot-middleware@2.26.1)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) - '@storybook/builder-webpack5': 9.1.17(@swc/core@1.12.5)(esbuild@0.27.0)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(typescript@5.9.3) - '@storybook/preset-react-webpack': 9.1.17(@swc/core@1.12.5)(esbuild@0.27.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(typescript@5.9.3) - '@storybook/react': 9.1.17(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(typescript@5.9.3) + '@pmmmwh/react-refresh-webpack-plugin': 0.5.17(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-hot-middleware@2.26.1)(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) + '@storybook/builder-webpack5': 9.1.17(@swc/core@1.12.5)(esbuild@0.27.0)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(typescript@5.9.3) + '@storybook/preset-react-webpack': 9.1.17(@swc/core@1.12.5)(esbuild@0.27.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(typescript@5.9.3) + '@storybook/react': 9.1.17(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(typescript@5.9.3) '@types/semver': 7.7.1 - babel-loader: 9.2.1(@babel/core@7.28.5)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) - css-loader: 6.11.0(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) + babel-loader: 9.2.1(@babel/core@7.28.5)(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) + css-loader: 6.11.0(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) image-size: 2.0.2 loader-utils: 3.3.1 next: 16.1.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - node-polyfill-webpack-plugin: 2.0.1(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) + node-polyfill-webpack-plugin: 2.0.1(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) postcss: 8.5.6 - postcss-loader: 8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) + postcss-loader: 8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) react-refresh: 0.14.2 resolve-url-loader: 5.0.0 - sass-loader: 16.0.6(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) + sass-loader: 16.0.6(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) semver: 7.7.3 - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) - style-loader: 3.3.4(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + style-loader: 3.3.4(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) styled-jsx: 5.1.6(@babel/core@7.28.5)(react@19.2.0) tsconfig-paths: 4.2.0 tsconfig-paths-webpack-plugin: 4.2.0 optionalDependencies: typescript: 5.9.3 - webpack: 5.102.1(@swc/core@1.12.5)(esbuild@0.27.0) + webpack: 5.105.2(@swc/core@1.12.5)(esbuild@0.27.0) transitivePeerDependencies: - '@rspack/core' - '@swc/core' @@ -16993,10 +16607,10 @@ snapshots: - webpack-hot-middleware - webpack-plugin-serve - '@storybook/preset-react-webpack@9.1.17(@swc/core@1.12.5)(esbuild@0.27.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(typescript@5.9.3)': + '@storybook/preset-react-webpack@9.1.17(@swc/core@1.12.5)(esbuild@0.27.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(typescript@5.9.3)': dependencies: - '@storybook/core-webpack': 9.1.17(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) - '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.9.3)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) + '@storybook/core-webpack': 9.1.17(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.9.3)(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) '@types/semver': 7.7.1 find-up: 7.0.0 magic-string: 0.30.21 @@ -17005,9 +16619,9 @@ snapshots: react-dom: 19.2.0(react@19.2.0) resolve: 1.22.11 semver: 7.7.3 - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) tsconfig-paths: 4.2.0 - webpack: 5.102.1(@swc/core@1.12.5)(esbuild@0.27.0) + webpack: 5.105.2(@swc/core@1.12.5)(esbuild@0.27.0) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -17017,37 +16631,37 @@ snapshots: - uglify-js - webpack-cli - '@storybook/preset-server-webpack@8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/preset-server-webpack@8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: - '@storybook/core-webpack': 8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/core-webpack': 8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) '@storybook/global': 5.0.0 - '@storybook/server': 8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/server': 8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) safe-identifier: 0.4.2 - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) ts-dedent: 2.2.0 yaml-loader: 0.8.1 optional: true - '@storybook/preset-server-webpack@8.5.8(storybook@9.1.17(prettier@3.8.1))': + '@storybook/preset-server-webpack@8.5.8(storybook@9.1.19(prettier@3.8.1))': dependencies: - '@storybook/core-webpack': 8.5.8(storybook@9.1.17(prettier@3.8.1)) + '@storybook/core-webpack': 8.5.8(storybook@9.1.19(prettier@3.8.1)) '@storybook/global': 5.0.0 - '@storybook/server': 8.5.8(storybook@9.1.17(prettier@3.8.1)) + '@storybook/server': 8.5.8(storybook@9.1.19(prettier@3.8.1)) safe-identifier: 0.4.2 - storybook: 9.1.17(prettier@3.8.1) + storybook: 9.1.19(prettier@3.8.1) ts-dedent: 2.2.0 yaml-loader: 0.8.1 - '@storybook/preview-api@8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/preview-api@8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) optional: true - '@storybook/preview-api@8.5.8(storybook@9.1.17(prettier@3.8.1))': + '@storybook/preview-api@8.5.8(storybook@9.1.19(prettier@3.8.1))': dependencies: - storybook: 9.1.17(prettier@3.8.1) + storybook: 9.1.19(prettier@3.8.1) - '@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.9.3)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0))': + '@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.9.3)(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0))': dependencies: debug: 4.4.3 endent: 2.1.0 @@ -17057,51 +16671,51 @@ snapshots: react-docgen-typescript: 2.4.0(typescript@5.9.3) tslib: 2.8.1 typescript: 5.9.3 - webpack: 5.102.1(@swc/core@1.12.5)(esbuild@0.27.0) + webpack: 5.105.2(@swc/core@1.12.5)(esbuild@0.27.0) transitivePeerDependencies: - supports-color - '@storybook/react-dom-shim@8.5.8(react-dom@19.2.0(react@19.2.0))(react@19.2.3)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/react-dom-shim@8.5.8(react-dom@19.2.0(react@19.2.0))(react@19.2.3)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: react: 19.2.3 react-dom: 19.2.0(react@19.2.0) - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) optional: true - '@storybook/react-dom-shim@8.5.8(react-dom@19.2.0(react@19.2.0))(react@19.2.3)(storybook@9.1.17(prettier@3.8.1))': + '@storybook/react-dom-shim@8.5.8(react-dom@19.2.0(react@19.2.0))(react@19.2.3)(storybook@9.1.19(prettier@3.8.1))': dependencies: react: 19.2.3 react-dom: 19.2.0(react@19.2.0) - storybook: 9.1.17(prettier@3.8.1) + storybook: 9.1.19(prettier@3.8.1) - '@storybook/react-dom-shim@9.1.17(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/react-dom-shim@9.1.17(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) - '@storybook/react-dom-shim@9.1.17(react-dom@19.2.0(react@19.2.0))(react@19.2.3)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/react-dom-shim@9.1.17(react-dom@19.2.0(react@19.2.0))(react@19.2.3)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: react: 19.2.3 react-dom: 19.2.0(react@19.2.0) - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) - '@storybook/react@9.1.17(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(typescript@5.9.3)': + '@storybook/react@9.1.17(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(typescript@5.9.3)': dependencies: '@storybook/global': 5.0.0 - '@storybook/react-dom-shim': 9.1.17(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/react-dom-shim': 9.1.17(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) optionalDependencies: typescript: 5.9.3 - '@storybook/server-webpack5@8.5.8(@swc/core@1.12.5)(esbuild@0.27.0)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(typescript@5.9.3)': + '@storybook/server-webpack5@8.5.8(@swc/core@1.12.5)(esbuild@0.27.0)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(typescript@5.9.3)': dependencies: - '@storybook/builder-webpack5': 8.5.8(@swc/core@1.12.5)(esbuild@0.27.0)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(typescript@5.9.3) - '@storybook/preset-server-webpack': 8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) - '@storybook/server': 8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + '@storybook/builder-webpack5': 8.5.8(@swc/core@1.12.5)(esbuild@0.27.0)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(typescript@5.9.3) + '@storybook/preset-server-webpack': 8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/server': 8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) transitivePeerDependencies: - '@rspack/core' - '@swc/core' @@ -17111,12 +16725,12 @@ snapshots: - webpack-cli optional: true - '@storybook/server-webpack5@8.5.8(@swc/core@1.12.5)(esbuild@0.27.0)(storybook@9.1.17(prettier@3.8.1))(typescript@5.9.3)': + '@storybook/server-webpack5@8.5.8(@swc/core@1.12.5)(esbuild@0.27.0)(storybook@9.1.19(prettier@3.8.1))(typescript@5.9.3)': dependencies: - '@storybook/builder-webpack5': 8.5.8(@swc/core@1.12.5)(esbuild@0.27.0)(storybook@9.1.17(prettier@3.8.1))(typescript@5.9.3) - '@storybook/preset-server-webpack': 8.5.8(storybook@9.1.17(prettier@3.8.1)) - '@storybook/server': 8.5.8(storybook@9.1.17(prettier@3.8.1)) - storybook: 9.1.17(prettier@3.8.1) + '@storybook/builder-webpack5': 8.5.8(@swc/core@1.12.5)(esbuild@0.27.0)(storybook@9.1.19(prettier@3.8.1))(typescript@5.9.3) + '@storybook/preset-server-webpack': 8.5.8(storybook@9.1.19(prettier@3.8.1)) + '@storybook/server': 8.5.8(storybook@9.1.19(prettier@3.8.1)) + storybook: 9.1.19(prettier@3.8.1) transitivePeerDependencies: - '@rspack/core' - '@swc/core' @@ -17125,32 +16739,32 @@ snapshots: - uglify-js - webpack-cli - '@storybook/server@8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/server@8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: - '@storybook/components': 8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/components': 8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) '@storybook/csf': 0.1.12 '@storybook/global': 5.0.0 - '@storybook/manager-api': 8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) - '@storybook/preview-api': 8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) - '@storybook/theming': 8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + '@storybook/manager-api': 8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/preview-api': 8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + '@storybook/theming': 8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) ts-dedent: 2.2.0 yaml: 2.8.1 optional: true - '@storybook/server@8.5.8(storybook@9.1.17(prettier@3.8.1))': + '@storybook/server@8.5.8(storybook@9.1.19(prettier@3.8.1))': dependencies: - '@storybook/components': 8.5.8(storybook@9.1.17(prettier@3.8.1)) + '@storybook/components': 8.5.8(storybook@9.1.19(prettier@3.8.1)) '@storybook/csf': 0.1.12 '@storybook/global': 5.0.0 - '@storybook/manager-api': 8.5.8(storybook@9.1.17(prettier@3.8.1)) - '@storybook/preview-api': 8.5.8(storybook@9.1.17(prettier@3.8.1)) - '@storybook/theming': 8.5.8(storybook@9.1.17(prettier@3.8.1)) - storybook: 9.1.17(prettier@3.8.1) + '@storybook/manager-api': 8.5.8(storybook@9.1.19(prettier@3.8.1)) + '@storybook/preview-api': 8.5.8(storybook@9.1.19(prettier@3.8.1)) + '@storybook/theming': 8.5.8(storybook@9.1.19(prettier@3.8.1)) + storybook: 9.1.19(prettier@3.8.1) ts-dedent: 2.2.0 yaml: 2.8.1 - '@storybook/test-runner@0.23.0(@types/node@25.2.0)(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/test-runner@0.23.0(@types/node@25.2.0)(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: '@babel/core': 7.28.5 '@babel/generator': 7.28.5 @@ -17170,7 +16784,7 @@ snapshots: jest-watch-typeahead: 2.2.2(jest@29.7.0(@types/node@25.2.0)) nyc: 15.1.0 playwright: 1.57.0 - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) transitivePeerDependencies: - '@swc/helpers' - '@types/node' @@ -17180,14 +16794,14 @@ snapshots: - supports-color - ts-node - '@storybook/theming@8.5.8(storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': + '@storybook/theming@8.5.8(storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))': dependencies: - storybook: 9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + storybook: 9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) optional: true - '@storybook/theming@8.5.8(storybook@9.1.17(prettier@3.8.1))': + '@storybook/theming@8.5.8(storybook@9.1.19(prettier@3.8.1))': dependencies: - storybook: 9.1.17(prettier@3.8.1) + storybook: 9.1.19(prettier@3.8.1) '@stripe/stripe-js@5.10.0': {} @@ -17257,7 +16871,7 @@ snapshots: '@tailwindcss/node@4.1.17': dependencies: '@jridgewell/remapping': 2.3.5 - enhanced-resolve: 5.18.3 + enhanced-resolve: 5.19.0 jiti: 2.6.1 lightningcss: 1.30.2 magic-string: 0.30.21 @@ -17551,7 +17165,7 @@ snapshots: '@types/pg@8.15.6': dependencies: '@types/node': 22.19.1 - pg-protocol: 1.11.0 + pg-protocol: 1.10.3 pg-types: 2.2.0 '@types/pg@8.16.0': @@ -17699,7 +17313,7 @@ snapshots: debug: 4.4.3 fast-glob: 3.3.3 is-glob: 4.0.3 - minimatch: 9.0.5 + minimatch: 9.0.8 semver: 7.7.3 ts-api-utils: 2.1.0(typescript@5.9.3) typescript: 5.9.3 @@ -17715,7 +17329,7 @@ snapshots: debug: 4.4.3 fast-glob: 3.3.3 is-glob: 4.0.3 - minimatch: 9.0.5 + minimatch: 9.0.8 semver: 7.7.3 ts-api-utils: 2.1.0(typescript@5.9.3) typescript: 5.9.3 @@ -18124,9 +17738,9 @@ snapshots: dependencies: acorn: 8.16.0 - acorn-import-phases@1.0.4(acorn@8.15.0): + acorn-import-phases@1.0.4(acorn@8.16.0): dependencies: - acorn: 8.15.0 + acorn: 8.16.0 acorn-jsx-walk@2.0.0: {} @@ -18180,22 +17794,22 @@ snapshots: ajv-formats@2.1.1: dependencies: - ajv: 8.17.1 + ajv: 8.18.0 ajv-formats@3.0.1: dependencies: - ajv: 8.17.1 + ajv: 8.18.0 - ajv-keywords@3.5.2(ajv@6.12.6): + ajv-keywords@3.5.2(ajv@6.14.0): dependencies: - ajv: 6.12.6 + ajv: 6.14.0 - ajv-keywords@5.1.0(ajv@8.17.1): + ajv-keywords@5.1.0(ajv@8.18.0): dependencies: - ajv: 8.17.1 + ajv: 8.18.0 fast-deep-equal: 3.1.3 - ajv@6.12.6: + ajv@6.14.0: dependencies: fast-deep-equal: 3.1.3 fast-json-stable-stringify: 2.1.0 @@ -18209,6 +17823,13 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 + ajv@8.18.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + ansi-escapes@4.3.2: dependencies: type-fest: 0.21.3 @@ -18262,7 +17883,7 @@ snapshots: asn1.js@4.10.1: dependencies: - bn.js: 4.12.2 + bn.js: 4.12.3 inherits: 2.0.4 minimalistic-assert: 1.0.1 @@ -18298,10 +17919,10 @@ snapshots: aws4fetch@1.0.20: {} - axios@1.12.2: + axios@1.13.5: dependencies: follow-redirects: 1.15.11 - form-data: 4.0.4 + form-data: 4.0.5 proxy-from-env: 1.1.0 transitivePeerDependencies: - debug @@ -18334,12 +17955,12 @@ snapshots: transitivePeerDependencies: - supports-color - babel-loader@9.2.1(@babel/core@7.28.5)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)): + babel-loader@9.2.1(@babel/core@7.28.5)(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)): dependencies: '@babel/core': 7.28.5 find-cache-dir: 4.0.0 schema-utils: 4.3.3 - webpack: 5.102.1(@swc/core@1.12.5)(esbuild@0.27.0) + webpack: 5.105.2(@swc/core@1.12.5)(esbuild@0.27.0) babel-plugin-istanbul@6.1.1: dependencies: @@ -18435,10 +18056,14 @@ snapshots: balanced-match@1.0.2: {} + balanced-match@4.0.4: {} + bare-events@2.8.2: {} base64-js@1.5.1: {} + baseline-browser-mapping@2.10.0: {} + baseline-browser-mapping@2.8.16: {} before-after-hook@4.0.0: {} @@ -18459,9 +18084,9 @@ snapshots: blake3-wasm@2.1.5: {} - bn.js@4.12.2: {} + bn.js@4.12.3: {} - bn.js@5.2.2: {} + bn.js@5.2.3: {} body-parser@2.2.2: dependencies: @@ -18471,7 +18096,7 @@ snapshots: http-errors: 2.0.1 iconv-lite: 0.7.2 on-finished: 2.4.1 - qs: 6.14.1 + qs: 6.15.0 raw-body: 3.0.2 type-is: 2.0.1 transitivePeerDependencies: @@ -18486,9 +18111,9 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.2: + brace-expansion@5.0.3: dependencies: - balanced-match: 1.0.2 + balanced-match: 4.0.4 braces@3.0.3: dependencies: @@ -18522,13 +18147,13 @@ snapshots: browserify-rsa@4.1.1: dependencies: - bn.js: 5.2.2 + bn.js: 5.2.3 randombytes: 2.1.0 safe-buffer: 5.2.1 browserify-sign@4.2.5: dependencies: - bn.js: 5.2.2 + bn.js: 5.2.3 browserify-rsa: 4.1.1 create-hash: 1.2.0 create-hmac: 1.1.7 @@ -18542,13 +18167,13 @@ snapshots: dependencies: pako: 1.0.11 - browserslist@4.26.3: + browserslist@4.28.1: dependencies: - baseline-browser-mapping: 2.8.16 + baseline-browser-mapping: 2.10.0 caniuse-lite: 1.0.30001760 - electron-to-chromium: 1.5.234 - node-releases: 2.0.23 - update-browserslist-db: 1.1.3(browserslist@4.26.3) + electron-to-chromium: 1.5.302 + node-releases: 2.0.27 + update-browserslist-db: 1.2.3(browserslist@4.28.1) bs-logger@0.2.6: dependencies: @@ -18810,7 +18435,7 @@ snapshots: core-js-compat@3.46.0: dependencies: - browserslist: 4.26.3 + browserslist: 4.28.1 core-js-pure@3.47.0: {} @@ -18846,7 +18471,7 @@ snapshots: create-ecdh@4.0.4: dependencies: - bn.js: 4.12.2 + bn.js: 4.12.3 elliptic: 6.6.1 create-hash@1.2.0: @@ -18917,7 +18542,7 @@ snapshots: randombytes: 2.1.0 randomfill: 1.0.4 - css-loader@6.11.0(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)): + css-loader@6.11.0(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)): dependencies: icss-utils: 5.1.0(postcss@8.5.6) postcss: 8.5.6 @@ -18928,7 +18553,7 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.7.3 optionalDependencies: - webpack: 5.102.1(@swc/core@1.12.5)(esbuild@0.27.0) + webpack: 5.105.2(@swc/core@1.12.5)(esbuild@0.27.0) css-select@4.3.0: dependencies: @@ -19168,7 +18793,7 @@ snapshots: diffie-hellman@5.0.3: dependencies: - bn.js: 4.12.2 + bn.js: 4.12.3 miller-rabin: 4.0.1 randombytes: 2.1.0 @@ -19301,11 +18926,11 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.234: {} + electron-to-chromium@1.5.302: {} elliptic@6.6.1: dependencies: - bn.js: 4.12.2 + bn.js: 4.12.3 brorand: 1.1.0 hash.js: 1.1.7 hmac-drbg: 1.0.1 @@ -19332,6 +18957,11 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.0 + enhanced-resolve@5.19.0: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.0 + entities@1.1.2: {} entities@2.2.0: {} @@ -19356,6 +18986,8 @@ snapshots: es-module-lexer@1.7.0: {} + es-module-lexer@2.0.0: {} + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -19385,13 +19017,6 @@ snapshots: esast-util-from-estree: 2.0.0 vfile-message: 4.0.3 - esbuild-register@3.6.0(esbuild@0.25.12): - dependencies: - debug: 4.4.3 - esbuild: 0.25.12 - transitivePeerDependencies: - - supports-color - esbuild-register@3.6.0(esbuild@0.27.0): dependencies: debug: 4.4.3 @@ -19428,35 +19053,6 @@ snapshots: '@esbuild/win32-ia32': 0.25.10 '@esbuild/win32-x64': 0.25.10 - esbuild@0.25.12: - optionalDependencies: - '@esbuild/aix-ppc64': 0.25.12 - '@esbuild/android-arm': 0.25.12 - '@esbuild/android-arm64': 0.25.12 - '@esbuild/android-x64': 0.25.12 - '@esbuild/darwin-arm64': 0.25.12 - '@esbuild/darwin-x64': 0.25.12 - '@esbuild/freebsd-arm64': 0.25.12 - '@esbuild/freebsd-x64': 0.25.12 - '@esbuild/linux-arm': 0.25.12 - '@esbuild/linux-arm64': 0.25.12 - '@esbuild/linux-ia32': 0.25.12 - '@esbuild/linux-loong64': 0.25.12 - '@esbuild/linux-mips64el': 0.25.12 - '@esbuild/linux-ppc64': 0.25.12 - '@esbuild/linux-riscv64': 0.25.12 - '@esbuild/linux-s390x': 0.25.12 - '@esbuild/linux-x64': 0.25.12 - '@esbuild/netbsd-arm64': 0.25.12 - '@esbuild/netbsd-x64': 0.25.12 - '@esbuild/openbsd-arm64': 0.25.12 - '@esbuild/openbsd-x64': 0.25.12 - '@esbuild/openharmony-arm64': 0.25.12 - '@esbuild/sunos-x64': 0.25.12 - '@esbuild/win32-arm64': 0.25.12 - '@esbuild/win32-ia32': 0.25.12 - '@esbuild/win32-x64': 0.25.12 - esbuild@0.27.0: optionalDependencies: '@esbuild/aix-ppc64': 0.27.0 @@ -19588,7 +19184,7 @@ snapshots: '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 '@types/estree': 1.0.8 - ajv: 6.12.6 + ajv: 6.14.0 chalk: 4.1.2 cross-spawn: 7.0.6 debug: 4.4.3 @@ -19607,7 +19203,7 @@ snapshots: is-glob: 4.0.3 json-stable-stringify-without-jsonify: 1.0.1 lodash.merge: 4.6.2 - minimatch: 3.1.2 + minimatch: 3.1.5 natural-compare: 1.4.0 optionator: 0.9.4 optionalDependencies: @@ -19778,7 +19374,7 @@ snapshots: once: 1.4.0 parseurl: 1.3.3 proxy-addr: 2.0.7 - qs: 6.14.1 + qs: 6.15.0 range-parser: 1.2.1 router: 2.2.0 send: 1.2.1 @@ -19823,9 +19419,12 @@ snapshots: fast-uri@3.1.0: {} - fast-xml-parser@5.3.4: + fast-xml-builder@1.0.0: {} + + fast-xml-parser@5.4.1: dependencies: - strnum: 2.1.1 + fast-xml-builder: 1.0.0 + strnum: 2.1.2 fastq@1.19.1: dependencies: @@ -19859,7 +19458,7 @@ snapshots: dependencies: app-module-path: 2.2.0 commander: 12.1.0 - enhanced-resolve: 5.18.3 + enhanced-resolve: 5.19.0 module-definition: 6.0.1 module-lookup-amd: 9.0.5 resolve: 1.22.11 @@ -19957,7 +19556,7 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 3.0.7 - fork-ts-checker-webpack-plugin@8.0.0(typescript@5.9.3)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)): + fork-ts-checker-webpack-plugin@8.0.0(typescript@5.9.3)(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)): dependencies: '@babel/code-frame': 7.27.1 chalk: 4.1.2 @@ -19966,15 +19565,15 @@ snapshots: deepmerge: 4.3.1 fs-extra: 10.1.0 memfs: 3.5.3 - minimatch: 3.1.2 + minimatch: 3.1.5 node-abort-controller: 3.1.1 schema-utils: 3.3.0 semver: 7.7.3 tapable: 2.3.0 typescript: 5.9.3 - webpack: 5.102.1(@swc/core@1.12.5)(esbuild@0.27.0) + webpack: 5.105.2(@swc/core@1.12.5)(esbuild@0.27.0) - form-data@4.0.4: + form-data@4.0.5: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 @@ -20079,7 +19678,7 @@ snapshots: glob@13.0.1: dependencies: - minimatch: 10.1.2 + minimatch: 10.2.4 minipass: 7.1.2 path-scurry: 2.0.0 @@ -20239,8 +19838,6 @@ snapshots: dependencies: hono: 4.12.2 - hono@4.11.7: {} - hono@4.12.2: {} html-entities@2.6.0: {} @@ -20259,7 +19856,7 @@ snapshots: html-url-attributes@3.0.1: {} - html-webpack-plugin@5.6.4(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)): + html-webpack-plugin@5.6.4(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)): dependencies: '@types/html-minifier-terser': 6.1.0 html-minifier-terser: 6.1.0 @@ -20267,7 +19864,7 @@ snapshots: pretty-error: 4.0.0 tapable: 2.3.0 optionalDependencies: - webpack: 5.102.1(@swc/core@1.12.5)(esbuild@0.27.0) + webpack: 5.105.2(@swc/core@1.12.5)(esbuild@0.27.0) htmlparser2@3.10.1: dependencies: @@ -22122,7 +21719,7 @@ snapshots: miller-rabin@4.0.1: dependencies: - bn.js: 4.12.2 + bn.js: 4.12.3 brorand: 1.1.0 mime-db@1.52.0: {} @@ -22173,17 +21770,17 @@ snapshots: minimalistic-crypto-utils@1.0.1: {} - minimatch@10.1.2: + minimatch@10.2.4: dependencies: - '@isaacs/brace-expansion': 5.0.1 + brace-expansion: 5.0.3 - minimatch@3.1.2: + minimatch@3.1.5: dependencies: brace-expansion: 1.1.12 - minimatch@9.0.5: + minimatch@9.0.8: dependencies: - brace-expansion: 2.0.2 + brace-expansion: 5.0.3 minimist@1.2.8: {} @@ -22305,7 +21902,7 @@ snapshots: node-int64@0.4.0: {} - node-polyfill-webpack-plugin@2.0.1(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)): + node-polyfill-webpack-plugin@2.0.1(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)): dependencies: assert: 2.1.0 browserify-zlib: 0.2.0 @@ -22332,13 +21929,13 @@ snapshots: url: 0.11.4 util: 0.12.5 vm-browserify: 1.1.2 - webpack: 5.102.1(@swc/core@1.12.5)(esbuild@0.27.0) + webpack: 5.105.2(@swc/core@1.12.5)(esbuild@0.27.0) node-preload@0.2.1: dependencies: process-on-spawn: 1.1.0 - node-releases@2.0.23: {} + node-releases@2.0.27: {} node-source-walk@7.0.1: dependencies: @@ -22646,6 +22243,8 @@ snapshots: dependencies: pg: 8.18.0 + pg-protocol@1.10.3: {} + pg-protocol@1.11.0: {} pg-types@2.2.0: @@ -22718,14 +22317,14 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss-loader@8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)): + postcss-loader@8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)): dependencies: cosmiconfig: 9.0.0(typescript@5.9.3) jiti: 2.6.1 postcss: 8.5.6 semver: 7.7.3 optionalDependencies: - webpack: 5.102.1(@swc/core@1.12.5)(esbuild@0.27.0) + webpack: 5.105.2(@swc/core@1.12.5)(esbuild@0.27.0) transitivePeerDependencies: - typescript @@ -22897,7 +22496,7 @@ snapshots: public-encrypt@4.0.3: dependencies: - bn.js: 4.12.2 + bn.js: 4.12.3 browserify-rsa: 4.1.1 create-hash: 1.2.0 parse-asn1: 5.1.9 @@ -22912,7 +22511,7 @@ snapshots: pure-rand@7.0.1: {} - qs@6.14.1: + qs@6.15.0: dependencies: side-channel: 1.1.0 @@ -23331,63 +22930,35 @@ snapshots: hash-base: 3.1.2 inherits: 2.0.4 - rollup@4.53.3: + rollup@4.59.0: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.53.3 - '@rollup/rollup-android-arm64': 4.53.3 - '@rollup/rollup-darwin-arm64': 4.53.3 - '@rollup/rollup-darwin-x64': 4.53.3 - '@rollup/rollup-freebsd-arm64': 4.53.3 - '@rollup/rollup-freebsd-x64': 4.53.3 - '@rollup/rollup-linux-arm-gnueabihf': 4.53.3 - '@rollup/rollup-linux-arm-musleabihf': 4.53.3 - '@rollup/rollup-linux-arm64-gnu': 4.53.3 - '@rollup/rollup-linux-arm64-musl': 4.53.3 - '@rollup/rollup-linux-loong64-gnu': 4.53.3 - '@rollup/rollup-linux-ppc64-gnu': 4.53.3 - '@rollup/rollup-linux-riscv64-gnu': 4.53.3 - '@rollup/rollup-linux-riscv64-musl': 4.53.3 - '@rollup/rollup-linux-s390x-gnu': 4.53.3 - '@rollup/rollup-linux-x64-gnu': 4.53.3 - '@rollup/rollup-linux-x64-musl': 4.53.3 - '@rollup/rollup-openharmony-arm64': 4.53.3 - '@rollup/rollup-win32-arm64-msvc': 4.53.3 - '@rollup/rollup-win32-ia32-msvc': 4.53.3 - '@rollup/rollup-win32-x64-gnu': 4.53.3 - '@rollup/rollup-win32-x64-msvc': 4.53.3 - fsevents: 2.3.3 - - rollup@4.55.1: - dependencies: - '@types/estree': 1.0.8 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.55.1 - '@rollup/rollup-android-arm64': 4.55.1 - '@rollup/rollup-darwin-arm64': 4.55.1 - '@rollup/rollup-darwin-x64': 4.55.1 - '@rollup/rollup-freebsd-arm64': 4.55.1 - '@rollup/rollup-freebsd-x64': 4.55.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.55.1 - '@rollup/rollup-linux-arm-musleabihf': 4.55.1 - '@rollup/rollup-linux-arm64-gnu': 4.55.1 - '@rollup/rollup-linux-arm64-musl': 4.55.1 - '@rollup/rollup-linux-loong64-gnu': 4.55.1 - '@rollup/rollup-linux-loong64-musl': 4.55.1 - '@rollup/rollup-linux-ppc64-gnu': 4.55.1 - '@rollup/rollup-linux-ppc64-musl': 4.55.1 - '@rollup/rollup-linux-riscv64-gnu': 4.55.1 - '@rollup/rollup-linux-riscv64-musl': 4.55.1 - '@rollup/rollup-linux-s390x-gnu': 4.55.1 - '@rollup/rollup-linux-x64-gnu': 4.55.1 - '@rollup/rollup-linux-x64-musl': 4.55.1 - '@rollup/rollup-openbsd-x64': 4.55.1 - '@rollup/rollup-openharmony-arm64': 4.55.1 - '@rollup/rollup-win32-arm64-msvc': 4.55.1 - '@rollup/rollup-win32-ia32-msvc': 4.55.1 - '@rollup/rollup-win32-x64-gnu': 4.55.1 - '@rollup/rollup-win32-x64-msvc': 4.55.1 + '@rollup/rollup-android-arm-eabi': 4.59.0 + '@rollup/rollup-android-arm64': 4.59.0 + '@rollup/rollup-darwin-arm64': 4.59.0 + '@rollup/rollup-darwin-x64': 4.59.0 + '@rollup/rollup-freebsd-arm64': 4.59.0 + '@rollup/rollup-freebsd-x64': 4.59.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.59.0 + '@rollup/rollup-linux-arm-musleabihf': 4.59.0 + '@rollup/rollup-linux-arm64-gnu': 4.59.0 + '@rollup/rollup-linux-arm64-musl': 4.59.0 + '@rollup/rollup-linux-loong64-gnu': 4.59.0 + '@rollup/rollup-linux-loong64-musl': 4.59.0 + '@rollup/rollup-linux-ppc64-gnu': 4.59.0 + '@rollup/rollup-linux-ppc64-musl': 4.59.0 + '@rollup/rollup-linux-riscv64-gnu': 4.59.0 + '@rollup/rollup-linux-riscv64-musl': 4.59.0 + '@rollup/rollup-linux-s390x-gnu': 4.59.0 + '@rollup/rollup-linux-x64-gnu': 4.59.0 + '@rollup/rollup-linux-x64-musl': 4.59.0 + '@rollup/rollup-openbsd-x64': 4.59.0 + '@rollup/rollup-openharmony-arm64': 4.59.0 + '@rollup/rollup-win32-arm64-msvc': 4.59.0 + '@rollup/rollup-win32-ia32-msvc': 4.59.0 + '@rollup/rollup-win32-x64-gnu': 4.59.0 + '@rollup/rollup-win32-x64-msvc': 4.59.0 fsevents: 2.3.3 router@2.2.0: @@ -23426,31 +22997,31 @@ snapshots: safer-buffer@2.1.2: {} - sass-loader@16.0.6(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)): + sass-loader@16.0.6(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)): dependencies: neo-async: 2.6.2 optionalDependencies: - webpack: 5.102.1(@swc/core@1.12.5)(esbuild@0.27.0) + webpack: 5.105.2(@swc/core@1.12.5)(esbuild@0.27.0) sass-lookup@6.1.0: dependencies: commander: 12.1.0 - enhanced-resolve: 5.18.3 + enhanced-resolve: 5.19.0 scheduler@0.27.0: {} schema-utils@3.3.0: dependencies: '@types/json-schema': 7.0.15 - ajv: 6.12.6 - ajv-keywords: 3.5.2(ajv@6.12.6) + ajv: 6.14.0 + ajv-keywords: 3.5.2(ajv@6.14.0) schema-utils@4.3.3: dependencies: '@types/json-schema': 7.0.15 - ajv: 8.17.1 + ajv: 8.18.0 ajv-formats: 2.1.1 - ajv-keywords: 5.1.0(ajv@8.17.1) + ajv-keywords: 5.1.0(ajv@8.18.0) seedrandom@3.0.5: {} @@ -23674,7 +23245,7 @@ snapshots: std-env@3.10.0: {} - storybook@9.1.17(prettier@3.8.1): + storybook@9.1.19(prettier@3.8.1): dependencies: '@storybook/global': 5.0.0 '@testing-library/jest-dom': 6.9.1 @@ -23683,8 +23254,8 @@ snapshots: '@vitest/mocker': 3.2.4 '@vitest/spy': 3.2.4 better-opn: 3.0.2 - esbuild: 0.25.12 - esbuild-register: 3.6.0(esbuild@0.25.12) + esbuild: 0.27.0 + esbuild-register: 3.6.0(esbuild@0.27.0) recast: 0.23.11 semver: 7.7.3 ws: 8.18.2 @@ -23698,7 +23269,7 @@ snapshots: - utf-8-validate - vite - storybook@9.1.17(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)): + storybook@9.1.19(prettier@3.8.1)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)): dependencies: '@storybook/global': 5.0.0 '@testing-library/jest-dom': 6.9.1 @@ -23707,8 +23278,8 @@ snapshots: '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) '@vitest/spy': 3.2.4 better-opn: 3.0.2 - esbuild: 0.25.12 - esbuild-register: 3.6.0(esbuild@0.25.12) + esbuild: 0.27.0 + esbuild-register: 3.6.0(esbuild@0.27.0) recast: 0.23.11 semver: 7.7.3 ws: 8.18.2 @@ -23814,15 +23385,15 @@ snapshots: stripe@19.1.0(@types/node@22.19.1): dependencies: - qs: 6.14.1 + qs: 6.15.0 optionalDependencies: '@types/node': 22.19.1 - strnum@2.1.1: {} + strnum@2.1.2: {} - style-loader@3.3.4(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)): + style-loader@3.3.4(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)): dependencies: - webpack: 5.102.1(@swc/core@1.12.5)(esbuild@0.27.0) + webpack: 5.105.2(@swc/core@1.12.5)(esbuild@0.27.0) style-to-js@1.1.17: dependencies: @@ -23846,7 +23417,7 @@ snapshots: stytch@12.43.0: dependencies: jose: 5.10.0 - undici: 6.22.0 + undici: 6.23.0 supports-color@10.2.2: {} @@ -23870,7 +23441,7 @@ snapshots: tailwind-api-utils@1.0.3(tailwindcss@4.1.17): dependencies: - enhanced-resolve: 5.18.3 + enhanced-resolve: 5.19.0 jiti: 2.5.1 local-pkg: 1.1.2 tailwindcss: 4.1.17 @@ -23894,14 +23465,14 @@ snapshots: - bare-abort-controller - react-native-b4a - terser-webpack-plugin@5.3.14(@swc/core@1.12.5)(esbuild@0.27.0)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)): + terser-webpack-plugin@5.3.16(@swc/core@1.12.5)(esbuild@0.27.0)(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 serialize-javascript: 6.0.2 terser: 5.44.0 - webpack: 5.102.1(@swc/core@1.12.5)(esbuild@0.27.0) + webpack: 5.105.2(@swc/core@1.12.5)(esbuild@0.27.0) optionalDependencies: '@swc/core': 1.12.5 esbuild: 0.27.0 @@ -23917,7 +23488,7 @@ snapshots: dependencies: '@istanbuljs/schema': 0.1.3 glob: 13.0.1 - minimatch: 3.1.2 + minimatch: 3.1.5 text-decoder@1.2.3: dependencies: @@ -24064,7 +23635,7 @@ snapshots: tsconfig-paths-webpack-plugin@4.2.0: dependencies: chalk: 4.1.2 - enhanced-resolve: 5.18.3 + enhanced-resolve: 5.19.0 tapable: 2.3.0 tsconfig-paths: 4.2.0 @@ -24147,7 +23718,7 @@ snapshots: undici@6.21.3: {} - undici@6.22.0: {} + undici@6.23.0: {} undici@7.18.2: {} @@ -24249,9 +23820,9 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 - update-browserslist-db@1.1.3(browserslist@4.26.3): + update-browserslist-db@1.2.3(browserslist@4.28.1): dependencies: - browserslist: 4.26.3 + browserslist: 4.28.1 escalade: 3.2.0 picocolors: 1.1.1 @@ -24262,7 +23833,7 @@ snapshots: url@0.11.4: dependencies: punycode: 1.4.1 - qs: 6.14.1 + qs: 6.15.0 use-callback-ref@1.3.3(@types/react@19.2.2)(react@19.2.0): dependencies: @@ -24393,7 +23964,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.55.1 + rollup: 4.59.0 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 22.19.1 @@ -24410,7 +23981,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.55.1 + rollup: 4.59.0 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 25.2.0 @@ -24549,7 +24120,7 @@ snapshots: wait-on@7.2.0: dependencies: - axios: 1.12.2 + axios: 1.13.5 joi: 17.13.3 lodash: 4.17.23 minimist: 1.2.8 @@ -24573,7 +24144,7 @@ snapshots: dependencies: makeerror: 1.0.12 - watchpack@2.4.4: + watchpack@2.5.1: dependencies: glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 @@ -24607,7 +24178,7 @@ snapshots: - bufferutil - utf-8-validate - webpack-dev-middleware@6.1.3(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)): + webpack-dev-middleware@6.1.3(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)): dependencies: colorette: 2.0.20 memfs: 3.5.3 @@ -24615,7 +24186,7 @@ snapshots: range-parser: 1.2.1 schema-utils: 4.3.3 optionalDependencies: - webpack: 5.102.1(@swc/core@1.12.5)(esbuild@0.27.0) + webpack: 5.105.2(@swc/core@1.12.5)(esbuild@0.27.0) webpack-hot-middleware@2.26.1: dependencies: @@ -24629,7 +24200,7 @@ snapshots: webpack-virtual-modules@0.6.2: {} - webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0): + webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -24637,12 +24208,12 @@ snapshots: '@webassemblyjs/ast': 1.14.1 '@webassemblyjs/wasm-edit': 1.14.1 '@webassemblyjs/wasm-parser': 1.14.1 - acorn: 8.15.0 - acorn-import-phases: 1.0.4(acorn@8.15.0) - browserslist: 4.26.3 + acorn: 8.16.0 + acorn-import-phases: 1.0.4(acorn@8.16.0) + browserslist: 4.28.1 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.18.3 - es-module-lexer: 1.7.0 + enhanced-resolve: 5.19.0 + es-module-lexer: 2.0.0 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 @@ -24653,8 +24224,8 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.14(@swc/core@1.12.5)(esbuild@0.27.0)(webpack@5.102.1(@swc/core@1.12.5)(esbuild@0.27.0)) - watchpack: 2.4.4 + terser-webpack-plugin: 5.3.16(@swc/core@1.12.5)(esbuild@0.27.0)(webpack@5.105.2(@swc/core@1.12.5)(esbuild@0.27.0)) + watchpack: 2.5.1 webpack-sources: 3.3.3 transitivePeerDependencies: - '@swc/core' @@ -24715,7 +24286,7 @@ snapshots: dependencies: zod: 4.3.6 optionalDependencies: - hono: 4.11.7 + hono: 4.12.2 wrangler@4.61.1(@cloudflare/workers-types@4.20260130.0): dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index e31c311f1..30ef93c5f 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -41,6 +41,7 @@ packages: - 'cloudflare-o11y' - 'cloudflare-git-token-service' - 'cloudflare-security-sync' + - 'cloudflare-security-auto-analysis' - 'kiloclaw' - 'cloudflare-gastown' - 'cloudflare-gastown/container' @@ -54,26 +55,25 @@ ignoredBuiltDependencies: minimumReleaseAge: 5760 -# Storybook 9.1.17 excluded for security update - https://storybook.js.org/blog/security-advisory/ +# Storybook 9.1.19 excluded for security update (GHSA-mjf5-7g4m-gx5w) minimumReleaseAgeExclude: - - '@storybook/addon-docs@9.1.17' - - '@storybook/addon-links@9.1.17' - - '@storybook/addon-themes@9.1.17' - - '@storybook/builder-webpack5@9.1.17' - - '@storybook/core-webpack@9.1.17' - - '@storybook/csf-plugin@9.1.17' - - '@storybook/global@9.1.17' - - '@storybook/icons@9.1.17' - - '@storybook/nextjs@9.1.17' - - '@storybook/preset-react-webpack@9.1.17' - - '@storybook/react@9.1.17' - - '@storybook/react-docgen-typescript-plugin@9.1.17' - - '@storybook/react-dom-shim@9.1.17' - - storybook@9.1.17 - # glob@13.0.1 + minimatch@10.1.2 + @isaacs/brace-expansion@5.0.1 fix GHSA-7h2j-956f-4vf2 + - '@storybook/addon-docs@9.1.19' + - '@storybook/addon-links@9.1.19' + - '@storybook/addon-themes@9.1.19' + - '@storybook/builder-webpack5@9.1.19' + - '@storybook/core-webpack@9.1.19' + - '@storybook/csf-plugin@9.1.19' + - '@storybook/nextjs@9.1.19' + - '@storybook/preset-react-webpack@9.1.19' + - '@storybook/react@9.1.19' + - '@storybook/react-dom-shim@9.1.19' + - storybook@9.1.19 + # glob@13.0.1 + minimatch@10.2.3 + brace-expansion@5.0.2 fix minimatch ReDoS vulns - glob@13.0.1 - - minimatch@10.1.2 - - '@isaacs/brace-expansion@5.0.1' + - minimatch@10.2.3 + - brace-expansion@5.0.2 + # Security overrides for transitive dependencies + - serialize-javascript@7.0.3 onlyBuiltDependencies: - '@swc/core' diff --git a/src/app/api/internal/security-analysis-callback/[findingId]/route.test.ts b/src/app/api/internal/security-analysis-callback/[findingId]/route.test.ts index 4a14caf1a..c4b289447 100644 --- a/src/app/api/internal/security-analysis-callback/[findingId]/route.test.ts +++ b/src/app/api/internal/security-analysis-callback/[findingId]/route.test.ts @@ -17,6 +17,9 @@ const mockGetSecurityFindingById = jest.fn() as jest.MockedFunction< const mockUpdateAnalysisStatus = jest.fn() as jest.MockedFunction< typeof securityAnalysisModule.updateAnalysisStatus >; +const mockTransitionAutoAnalysisQueueFromCallback = jest.fn() as jest.MockedFunction< + typeof securityAnalysisModule.transitionAutoAnalysisQueueFromCallback +>; const mockFinalizeAnalysis = jest.fn() as jest.MockedFunction< typeof analysisServiceModule.finalizeAnalysis >; @@ -32,6 +35,7 @@ const mockTrackAnalysisCompleted = jest.fn() as jest.MockedFunction< const mockGenerateApiToken = jest.fn() as jest.MockedFunction; // eslint-disable-next-line @typescript-eslint/no-explicit-any const mockDbSelect = jest.fn(); +const mockCaptureMessage = jest.fn(); // --- Module mocks --- @@ -64,6 +68,7 @@ jest.mock('@/lib/security-agent/db/security-findings', () => ({ jest.mock('@/lib/security-agent/db/security-analysis', () => ({ updateAnalysisStatus: mockUpdateAnalysisStatus, + transitionAutoAnalysisQueueFromCallback: mockTransitionAutoAnalysisQueueFromCallback, })); jest.mock('@/lib/security-agent/services/analysis-service', () => ({ @@ -85,6 +90,7 @@ jest.mock('@/lib/tokens', () => ({ jest.mock('@sentry/nextjs', () => ({ captureException: jest.fn(), + captureMessage: mockCaptureMessage, })); jest.mock('@/lib/utils.server', () => ({ @@ -180,7 +186,7 @@ function createMockFinding(overrides: Partial = {}): SecurityFi cwe_ids: null, cvss_score: null, dependency_scope: 'runtime', - session_id: 'ses-agent-1', + session_id: 'agent-session-1', cli_session_id: null, analysis_status: 'running', analysis_started_at: '2025-01-01T00:00:00.000Z', @@ -231,6 +237,7 @@ beforeEach(async () => { jest.useFakeTimers(); afterPromises = []; mockUpdateAnalysisStatus.mockResolvedValue(undefined); + mockTransitionAutoAnalysisQueueFromCallback.mockResolvedValue(undefined); mockFinalizeAnalysis.mockResolvedValue(undefined); ({ POST } = await import('./route')); }); @@ -283,6 +290,27 @@ describe('POST /api/internal/security-analysis-callback/[findingId]', () => { }); describe('idempotency', () => { + it('no-ops callback when session IDs do not match the current finding session', async () => { + mockGetSecurityFindingById.mockResolvedValue( + createMockFinding({ session_id: 'agent-current', cli_session_id: 'kilo-current' }) + ); + + const req = makeRequest(FINDING_ID, { + ...completedPayload, + cloudAgentSessionId: 'agent-stale', + kiloSessionId: 'kilo-stale', + }); + const response = await POST(req, makeParams(FINDING_ID)); + + expect(response.status).toBe(200); + expect(mockUpdateAnalysisStatus).not.toHaveBeenCalled(); + expect(mockTransitionAutoAnalysisQueueFromCallback).not.toHaveBeenCalled(); + expect(mockCaptureMessage).toHaveBeenCalledWith( + 'Auto-analysis callback session mismatch', + expect.objectContaining({ level: 'warning' }) + ); + }); + it('skips processing when finding is already completed', async () => { mockGetSecurityFindingById.mockResolvedValue( createMockFinding({ analysis_status: 'completed' }) @@ -722,6 +750,9 @@ describe('POST /api/internal/security-analysis-callback/[findingId]', () => { expect(mockUpdateAnalysisStatus).toHaveBeenCalledWith(FINDING_ID, 'failed', { error: 'Sandbox timed out', }); + expect(mockTransitionAutoAnalysisQueueFromCallback).toHaveBeenCalledWith( + expect.objectContaining({ failureCode: 'NETWORK_TIMEOUT' }) + ); }); it('marks finding as failed with prefixed message for interrupted status', async () => { @@ -734,6 +765,9 @@ describe('POST /api/internal/security-analysis-callback/[findingId]', () => { expect(mockUpdateAnalysisStatus).toHaveBeenCalledWith(FINDING_ID, 'failed', { error: 'Analysis interrupted: User cancelled', }); + expect(mockTransitionAutoAnalysisQueueFromCallback).toHaveBeenCalledWith( + expect.objectContaining({ failureCode: 'STATE_GUARD_REJECTED' }) + ); }); it('uses default message when errorMessage is absent for failed status', async () => { @@ -767,6 +801,7 @@ describe('POST /api/internal/security-analysis-callback/[findingId]', () => { const req = makeRequest(FINDING_ID, failedPayload); await POST(req, makeParams(FINDING_ID)); + await flushAfterCallbacks(); expect(mockTrackAnalysisCompleted).toHaveBeenCalledWith({ distinctId: 'user-trigger-1', @@ -806,6 +841,7 @@ describe('POST /api/internal/security-analysis-callback/[findingId]', () => { const req = makeRequest(FINDING_ID, failedPayload); await POST(req, makeParams(FINDING_ID)); + await flushAfterCallbacks(); expect(mockTrackAnalysisCompleted).toHaveBeenCalledWith( expect.objectContaining({ organizationId: orgId }) @@ -818,6 +854,7 @@ describe('POST /api/internal/security-analysis-callback/[findingId]', () => { const req = makeRequest(FINDING_ID, failedPayload); await POST(req, makeParams(FINDING_ID)); + await flushAfterCallbacks(); expect(mockTrackAnalysisCompleted).toHaveBeenCalledWith( expect.objectContaining({ durationMs: 0 }) diff --git a/src/app/api/internal/security-analysis-callback/[findingId]/route.ts b/src/app/api/internal/security-analysis-callback/[findingId]/route.ts index 965d418ae..41c02caac 100644 --- a/src/app/api/internal/security-analysis-callback/[findingId]/route.ts +++ b/src/app/api/internal/security-analysis-callback/[findingId]/route.ts @@ -1,21 +1,13 @@ -/** - * Internal API Endpoint: Security Analysis Callback - * - * Called by: - * - cloud-agent-next (when sandbox analysis completes, fails, or is interrupted) - * - * The findingId is passed in the URL path. - * - * URL: POST /api/internal/security-analysis-callback/{findingId} - * Protected by internal API secret - */ - import type { NextRequest } from 'next/server'; import { after, NextResponse } from 'next/server'; import { INTERNAL_API_SECRET } from '@/lib/config.server'; -import { captureException } from '@sentry/nextjs'; +import { captureException, captureMessage } from '@sentry/nextjs'; import { getSecurityFindingById } from '@/lib/security-agent/db/security-findings'; -import { updateAnalysisStatus } from '@/lib/security-agent/db/security-analysis'; +import { + updateAnalysisStatus, + transitionAutoAnalysisQueueFromCallback, + type AutoAnalysisFailureCode, +} from '@/lib/security-agent/db/security-analysis'; import { finalizeAnalysis, extractLastAssistantMessage, @@ -51,6 +43,34 @@ type ExecutionCallbackPayload = { lastSeenBranch?: string; }; +function mapCallbackFailure(params: { status: 'failed' | 'interrupted'; errorMessage?: string }): { + errorMessage: string; + failureCode: AutoAnalysisFailureCode; +} { + if (params.status === 'interrupted') { + return { + errorMessage: `Analysis interrupted: ${params.errorMessage ?? 'unknown reason'}`, + failureCode: 'STATE_GUARD_REJECTED', + }; + } + + const errorMessage = params.errorMessage ?? 'Analysis failed'; + const normalized = errorMessage.toLowerCase(); + if (normalized.includes('timeout') || normalized.includes('timed out')) { + return { errorMessage, failureCode: 'NETWORK_TIMEOUT' }; + } + if ( + normalized.includes('502') || + normalized.includes('503') || + normalized.includes('504') || + normalized.includes('upstream') || + normalized.includes('5xx') + ) { + return { errorMessage, failureCode: 'UPSTREAM_5XX' }; + } + return { errorMessage, failureCode: 'START_CALL_AMBIGUOUS' }; +} + export async function POST( req: NextRequest, { params }: { params: Promise<{ findingId: string }> } @@ -83,6 +103,36 @@ export async function POST( return NextResponse.json({ error: 'Finding not found' }, { status: 404 }); } + const sessionMismatch = + (payload.cloudAgentSessionId && + finding.session_id && + payload.cloudAgentSessionId !== finding.session_id) || + (payload.kiloSessionId && + finding.cli_session_id && + payload.kiloSessionId !== finding.cli_session_id); + + if (sessionMismatch) { + warn('Ignoring stale auto-analysis callback due to session mismatch', { + findingId, + findingSessionId: finding.session_id, + findingCliSessionId: finding.cli_session_id, + callbackCloudAgentSessionId: payload.cloudAgentSessionId, + callbackKiloSessionId: payload.kiloSessionId, + }); + captureMessage('Auto-analysis callback session mismatch', { + level: 'warning', + tags: { source: 'security-analysis-callback-api' }, + extra: { + findingId, + findingSessionId: finding.session_id, + findingCliSessionId: finding.cli_session_id, + callbackCloudAgentSessionId: payload.cloudAgentSessionId, + callbackKiloSessionId: payload.kiloSessionId, + }, + }); + return NextResponse.json({ success: true, message: 'Stale callback ignored' }); + } + // Skip if already in a terminal state if (finding.analysis_status === 'completed' || finding.analysis_status === 'failed') { log('Finding already in terminal state, skipping callback', { @@ -101,8 +151,23 @@ export async function POST( try { if (payload.status === 'completed') { await handleAnalysisCompleted(findingId, payload, finding); - } else { + } else if (payload.status === 'failed' || payload.status === 'interrupted') { await handleAnalysisFailed(findingId, payload, finding); + } else { + const unknownStatus = payload.status as string; + logError('Unknown callback status received, marking as failed', { + findingId, + status: unknownStatus, + }); + await updateAnalysisStatus(findingId, 'failed', { + error: `Unknown callback status: ${unknownStatus}`, + }); + await transitionAutoAnalysisQueueFromCallback({ + findingId, + toStatus: 'failed', + failureCode: 'STATE_GUARD_REJECTED', + errorMessage: `Unknown callback status: ${unknownStatus}`, + }); } } catch (error) { logError('Error processing security analysis callback', { error }); @@ -129,7 +194,7 @@ export async function POST( } } -/** Safely read the stored analysis metadata from the JSONB column */ +/** Read stored analysis metadata, filling in defaults for missing fields. */ function readAnalysisContext(analysis: SecurityFindingAnalysis | null | undefined): { correlationId: string; modelUsed: string; @@ -172,6 +237,12 @@ async function handleAnalysisCompleted( await updateAnalysisStatus(findingId, 'failed', { error: 'Cannot process callback — triggeredByUserId missing from analysis context', }); + await transitionAutoAnalysisQueueFromCallback({ + findingId, + toStatus: 'failed', + failureCode: 'STATE_GUARD_REJECTED', + errorMessage: 'Cannot process callback — triggeredByUserId missing from analysis context', + }); return; } @@ -181,6 +252,12 @@ async function handleAnalysisCompleted( await updateAnalysisStatus(findingId, 'failed', { error: 'Callback missing kiloSessionId — cannot retrieve analysis result', }); + await transitionAutoAnalysisQueueFromCallback({ + findingId, + toStatus: 'failed', + failureCode: 'STATE_GUARD_REJECTED', + errorMessage: 'Callback missing kiloSessionId — cannot retrieve analysis result', + }); return; } @@ -236,6 +313,12 @@ async function handleAnalysisCompleted( await updateAnalysisStatus(findingId, 'failed', { error: 'Analysis completed but result could not be retrieved from ingest service', }); + await transitionAutoAnalysisQueueFromCallback({ + findingId, + toStatus: 'failed', + failureCode: 'START_CALL_AMBIGUOUS', + errorMessage: 'Analysis completed but result could not be retrieved from ingest service', + }); return; } @@ -265,6 +348,12 @@ async function handleAnalysisCompleted( await updateAnalysisStatus(findingId, 'failed', { error: `User ${triggeredByUserId} not found — cannot run Tier 3 extraction`, }); + await transitionAutoAnalysisQueueFromCallback({ + findingId, + toStatus: 'failed', + failureCode: 'STATE_GUARD_REJECTED', + errorMessage: `User ${triggeredByUserId} not found — cannot run Tier 3 extraction`, + }); return; } @@ -298,6 +387,25 @@ async function handleAnalysisCompleted( correlationId, organizationId ); + + const updatedFinding = await getSecurityFindingById(findingId); + if (updatedFinding?.analysis_status === 'completed') { + await transitionAutoAnalysisQueueFromCallback({ findingId, toStatus: 'completed' }); + } else if (updatedFinding?.analysis_status === 'failed') { + await transitionAutoAnalysisQueueFromCallback({ + findingId, + toStatus: 'failed', + failureCode: 'START_CALL_AMBIGUOUS', + errorMessage: updatedFinding.analysis_error ?? undefined, + }); + } else { + await transitionAutoAnalysisQueueFromCallback({ + findingId, + toStatus: 'failed', + failureCode: 'STATE_GUARD_REJECTED', + errorMessage: `Unexpected post-finalize state: ${updatedFinding?.analysis_status ?? 'finding_not_found'}`, + }); + } } async function handleAnalysisFailed( @@ -314,10 +422,15 @@ async function handleAnalysisFailed( } = readAnalysisContext(finding.analysis); const organizationId = finding.owned_by_organization_id ?? undefined; - const errorMessage = - payload.status === 'interrupted' - ? `Analysis interrupted: ${payload.errorMessage ?? 'unknown reason'}` - : (payload.errorMessage ?? 'Analysis failed'); + if (payload.status !== 'failed' && payload.status !== 'interrupted') { + return; + } + + const callbackFailure = mapCallbackFailure({ + status: payload.status, + errorMessage: payload.errorMessage, + }); + const errorMessage = callbackFailure.errorMessage; if (payload.status === 'interrupted') { logExceptInTest('Analysis interrupted by user', { @@ -336,6 +449,12 @@ async function handleAnalysisFailed( } await updateAnalysisStatus(findingId, 'failed', { error: errorMessage }); + await transitionAutoAnalysisQueueFromCallback({ + findingId, + toStatus: 'failed', + failureCode: callbackFailure.failureCode, + errorMessage, + }); if (!triggeredByUserId) { logError('Missing triggeredByUserId in analysis context, skipping PostHog tracking', { diff --git a/src/lib/security-agent/core/constants.ts b/src/lib/security-agent/core/constants.ts index af208f9ad..ef5499a7d 100644 --- a/src/lib/security-agent/core/constants.ts +++ b/src/lib/security-agent/core/constants.ts @@ -1,15 +1,6 @@ -/** - * Security Agent - Constants - * - * Default configuration values and constants for the security agent. - */ +import { SecurityAgentConfigSchema, type SecurityAgentConfig } from './types'; -import type { SecurityAgentConfig } from './types'; - -/** - * Available models for security agent analysis - * Order matters - first one is the default - */ +/** Order matters — first entry is the default. */ export const SECURITY_AGENT_MODELS = [ { id: 'anthropic/claude-opus-4.6', name: 'Claude Opus 4.6', free: false }, { id: 'anthropic/claude-opus-4.5', name: 'Claude Opus 4.5', free: false }, @@ -17,17 +8,10 @@ export const SECURITY_AGENT_MODELS = [ { id: 'x-ai/grok-code-fast-1', name: 'Grok Code Fast 1 (free)', free: true }, ] as const; -/** - * Default model for security agent analysis - */ export const DEFAULT_SECURITY_AGENT_MODEL = SECURITY_AGENT_MODELS[0].id; - export const DEFAULT_SECURITY_AGENT_TRIAGE_MODEL = SECURITY_AGENT_MODELS[0].id; export const DEFAULT_SECURITY_AGENT_ANALYSIS_MODEL = SECURITY_AGENT_MODELS[0].id; -/** - * Default configuration for the security agent - */ export const DEFAULT_SECURITY_AGENT_CONFIG: SecurityAgentConfig = { sla_critical_days: 15, sla_high_days: 30, @@ -38,9 +22,22 @@ export const DEFAULT_SECURITY_AGENT_CONFIG: SecurityAgentConfig = { model_slug: DEFAULT_SECURITY_AGENT_ANALYSIS_MODEL, triage_model_slug: DEFAULT_SECURITY_AGENT_TRIAGE_MODEL, analysis_model_slug: DEFAULT_SECURITY_AGENT_ANALYSIS_MODEL, - // Analysis mode: auto (triage → conditional sandbox), shallow (triage only), deep (always sandbox) analysis_mode: 'auto', - // Auto-dismiss is off by default - users manually review and dismiss findings auto_dismiss_enabled: false, auto_dismiss_confidence_threshold: 'high', + auto_analysis_enabled: false, + auto_analysis_min_severity: 'high', }; + +export const SECURITY_ANALYSIS_OWNER_CAP = 3; + +const SecurityAgentConfigPartialSchema = SecurityAgentConfigSchema.partial().passthrough(); + +/** Parse a raw (possibly partial) config into a full SecurityAgentConfig. */ +export function parseSecurityAgentConfig(rawConfig: unknown): SecurityAgentConfig { + const partial = SecurityAgentConfigPartialSchema.parse(rawConfig ?? {}); + return SecurityAgentConfigSchema.parse({ + ...DEFAULT_SECURITY_AGENT_CONFIG, + ...partial, + }); +} diff --git a/src/lib/security-agent/core/schemas.ts b/src/lib/security-agent/core/schemas.ts index ed9054fe1..f0f19e69d 100644 --- a/src/lib/security-agent/core/schemas.ts +++ b/src/lib/security-agent/core/schemas.ts @@ -1,31 +1,11 @@ -/** - * Security Reviews - Zod Validation Schemas - * - * Runtime validation schemas for security review inputs. - * Follows validation patterns used throughout the codebase. - */ - import * as z from 'zod'; -// ============================================================================ -// Status and Severity Schemas -// ============================================================================ - -/** - * Security finding status enum - */ -// NOTE: 'closed' is a UI-only filter value that maps to status IN ('fixed', 'ignored'). -// It is not persisted in the database. +// 'closed' is a UI-only filter that maps to status IN ('fixed', 'ignored'). export const SecurityFindingStatusSchema = z.enum(['open', 'fixed', 'ignored', 'closed']); -/** - * Security finding severity enum - */ export const SecuritySeveritySchema = z.enum(['critical', 'high', 'medium', 'low']); -/** - * Dependabot dismiss reason enum (matches GitHub API) - */ +/** Matches GitHub API dismiss reasons. */ export const DismissReasonSchema = z.enum([ 'fix_started', 'no_bandwidth', @@ -34,28 +14,11 @@ export const DismissReasonSchema = z.enum([ 'not_used', ]); -// ============================================================================ -// tRPC Input Schemas -// ============================================================================ - -/** - * Repository selection mode enum - */ export const RepositorySelectionModeSchema = z.enum(['all', 'selected']); - -/** - * Auto-dismiss confidence threshold enum - */ export const AutoDismissConfidenceThresholdSchema = z.enum(['high', 'medium', 'low']); - -/** - * Analysis mode enum - */ export const AnalysisModeSchema = z.enum(['auto', 'shallow', 'deep']); +export const AutoAnalysisMinSeveritySchema = z.enum(['critical', 'high', 'medium', 'all']); -/** - * Save security config input schema - */ export const SaveSecurityConfigInputSchema = z.object({ slaCriticalDays: z.number().min(1).max(365).optional(), slaHighDays: z.number().min(1).max(365).optional(), @@ -67,33 +30,15 @@ export const SaveSecurityConfigInputSchema = z.object({ modelSlug: z.string().optional(), triageModelSlug: z.string().optional(), analysisModelSlug: z.string().optional(), - // Analysis mode configuration analysisMode: AnalysisModeSchema.optional(), - // Auto-dismiss configuration autoDismissEnabled: z.boolean().optional(), autoDismissConfidenceThreshold: AutoDismissConfidenceThresholdSchema.optional(), + autoAnalysisEnabled: z.boolean().optional(), + autoAnalysisMinSeverity: AutoAnalysisMinSeveritySchema.optional(), }); -/** - * Exploitability filter enum for listing findings - */ export const ExploitabilityFilterSchema = z.enum(['all', 'exploitable', 'not_exploitable']); - -/** - * Suggested action filter enum for listing findings - * - dismissable: findings where triage or sandbox suggests 'dismiss' - */ export const SuggestedActionFilterSchema = z.enum(['all', 'dismissable']); - -/** - * Analysis status filter enum for listing findings - * - all: no filter - * - not_analyzed: findings without any analysis (analysis_status is null) - * - pending: analysis is pending - * - running: analysis is currently running - * - completed: analysis has completed - * - failed: analysis failed - */ export const AnalysisStatusFilterSchema = z.enum([ 'all', 'not_analyzed', @@ -103,9 +48,6 @@ export const AnalysisStatusFilterSchema = z.enum([ 'failed', ]); -/** - * List security findings input schema - */ export const ListFindingsInputSchema = z.object({ repoFullName: z.string().optional(), status: SecurityFindingStatusSchema.optional(), @@ -117,63 +59,30 @@ export const ListFindingsInputSchema = z.object({ offset: z.number().min(0).default(0), }); -/** - * Trigger sync input schema - * When repoFullName is not provided, syncs all enabled repositories - */ export const TriggerSyncInputSchema = z.object({ repoFullName: z.string().optional(), }); -/** - * Dismiss finding input schema - */ export const DismissFindingInputSchema = z.object({ findingId: z.string().uuid(), reason: DismissReasonSchema, comment: z.string().optional(), }); -/** - * Get finding input schema - */ export const GetFindingInputSchema = z.object({ id: z.string().uuid(), }); -/** - * Set enabled input schema - * When enabling, optionally include repository selection to save with the config - */ export const SetEnabledInputSchema = z.object({ isEnabled: z.boolean(), - // Optional repository selection - when provided, saves the config before enabling repositorySelectionMode: RepositorySelectionModeSchema.optional(), selectedRepositoryIds: z.array(z.number()).optional(), }); -// ============================================================================ -// Analysis Schemas (Three-Tier: Triage + Sandbox + Extraction) -// ============================================================================ - -/** - * Analysis status enum - */ export const AnalysisStatusSchema = z.enum(['pending', 'running', 'completed', 'failed']); - -/** - * Triage suggested action enum - */ export const TriageSuggestedActionSchema = z.enum(['dismiss', 'analyze_codebase', 'manual_review']); - -/** - * Triage confidence level enum - */ export const TriageConfidenceSchema = z.enum(['high', 'medium', 'low']); -/** - * Schema for Tier 1 triage result (from metadata analysis) - */ export const SecurityFindingTriageSchema = z.object({ needsSandboxAnalysis: z.boolean(), needsSandboxReasoning: z.string(), @@ -182,19 +91,13 @@ export const SecurityFindingTriageSchema = z.object({ triageAt: z.string(), }); -/** - * Sandbox suggested action enum (Tier 3 extraction) - */ export const SandboxSuggestedActionSchema = z.enum([ - 'dismiss', // Not exploitable in this codebase - 'open_pr', // Exploitable with clear fix - open a PR - 'manual_review', // Complex situation, needs human review - 'monitor', // Exploitable but low risk - keep open, low priority + 'dismiss', + 'open_pr', + 'manual_review', + 'monitor', ]); -/** - * Schema for Tier 2 sandbox analysis result (from cloud agent + Tier 3 extraction) - */ export const SecurityFindingSandboxAnalysisSchema = z.object({ isExploitable: z.union([z.boolean(), z.literal('unknown')]), exploitabilityReasoning: z.string(), @@ -207,68 +110,45 @@ export const SecurityFindingSandboxAnalysisSchema = z.object({ modelUsed: z.string().optional(), }); -/** - * Schema for full analysis result (triage + optional sandbox) - * Note: triage is optional for backwards compatibility with legacy data - */ export const AnalysisResponseSchema = z.object({ - triage: SecurityFindingTriageSchema.optional(), // Optional for backwards compatibility + triage: SecurityFindingTriageSchema.optional(), sandboxAnalysis: SecurityFindingSandboxAnalysisSchema.optional(), - rawMarkdown: z.string().optional(), // Present in legacy format or as fallback + rawMarkdown: z.string().optional(), analyzedAt: z.string(), modelUsed: z.string().optional(), triageModel: z.string().optional(), analysisModel: z.string().optional(), - triggeredByUserId: z.string().optional(), // User ID who triggered the analysis (for audit tracking) + triggeredByUserId: z.string().optional(), }); -/** - * Legacy schema for backwards compatibility with existing data - * @deprecated Use AnalysisResponseSchema with triage field instead - */ +/** @deprecated Use AnalysisResponseSchema with triage field instead. */ export const AnalysisResponseLegacySchema = z.object({ rawMarkdown: z.string().min(1), analyzedAt: z.string(), modelUsed: z.string().optional(), }); -/** - * Start analysis input schema - */ export const StartAnalysisInputSchema = z.object({ findingId: z.string().uuid(), model: z.string().optional(), triageModel: z.string().optional(), analysisModel: z.string().optional(), - retrySandboxOnly: z.boolean().optional(), // Skip triage, reuse existing triage data, retry only sandbox + retrySandboxOnly: z.boolean().optional(), }); -/** - * Get analysis input schema - */ export const GetAnalysisInputSchema = z.object({ findingId: z.string().uuid(), }); -/** - * List analysis jobs input schema - */ export const ListAnalysisJobsInputSchema = z.object({ limit: z.number().min(1).max(100).default(10), offset: z.number().min(0).default(0), }); -/** - * Delete findings by repository input schema - */ export const DeleteFindingsByRepoInputSchema = z.object({ repoFullName: z.string().min(1), }); -// ============================================================================ -// Inferred TypeScript Types from Zod Schemas -// ============================================================================ - export type SaveSecurityConfigInput = z.infer; export type ListFindingsInput = z.infer; export type TriggerSyncInput = z.infer; diff --git a/src/lib/security-agent/core/types.ts b/src/lib/security-agent/core/types.ts index be39c821b..d281c8636 100644 --- a/src/lib/security-agent/core/types.ts +++ b/src/lib/security-agent/core/types.ts @@ -16,9 +16,6 @@ import type { DependabotAlertState as DependabotAlertStateType, } from '@kilocode/db/schema-types'; -/** - * Security finding source types - */ export const SecurityFindingSource = { DEPENDABOT: 'dependabot', PNPM_AUDIT: 'pnpm_audit', @@ -28,9 +25,6 @@ export const SecurityFindingSource = { export type SecurityFindingSource = (typeof SecurityFindingSource)[keyof typeof SecurityFindingSource]; -/** - * Security finding status - */ export const SecurityFindingStatus = { OPEN: 'open', FIXED: 'fixed', @@ -40,9 +34,6 @@ export const SecurityFindingStatus = { export type SecurityFindingStatus = (typeof SecurityFindingStatus)[keyof typeof SecurityFindingStatus]; -/** - * Security finding analysis status (for agent workflow) - */ export const SecurityFindingAnalysisStatus = { PENDING: 'pending', RUNNING: 'running', @@ -53,40 +44,32 @@ export const SecurityFindingAnalysisStatus = { export type SecurityFindingAnalysisStatus = (typeof SecurityFindingAnalysisStatus)[keyof typeof SecurityFindingAnalysisStatus]; -/** - * Analysis mode for the security agent pipeline: - * - auto: triage first, sandbox only if triage recommends it - * - shallow: triage only, never runs sandbox - * - deep: always force sandbox analysis - */ export type AnalysisMode = 'auto' | 'shallow' | 'deep'; -/** - * Zod schema for SecurityAgentConfig - */ -export const SecurityAgentConfigSchema = z.object({ - sla_critical_days: z.number().int().positive().default(15), - sla_high_days: z.number().int().positive().default(30), - sla_medium_days: z.number().int().positive().default(45), - sla_low_days: z.number().int().positive().default(90), - auto_sync_enabled: z.boolean().default(true), - repository_selection_mode: z.enum(['all', 'selected']).default('all'), - selected_repository_ids: z.array(z.number()).optional(), - model_slug: z.string().optional(), - triage_model_slug: z.string().optional(), - analysis_model_slug: z.string().optional(), - // Analysis mode: auto (default), shallow (triage only), deep (always sandbox) - analysis_mode: z.enum(['auto', 'shallow', 'deep']).default('auto'), - // Auto-dismiss configuration (off by default) - auto_dismiss_enabled: z.boolean().default(false), - auto_dismiss_confidence_threshold: z.enum(['high', 'medium', 'low']).default('high'), -}); +export type AutoAnalysisMinSeverity = 'critical' | 'high' | 'medium' | 'all'; + +export const SecurityAgentConfigSchema = z + .object({ + sla_critical_days: z.number().int().positive().default(15), + sla_high_days: z.number().int().positive().default(30), + sla_medium_days: z.number().int().positive().default(45), + sla_low_days: z.number().int().positive().default(90), + auto_sync_enabled: z.boolean().default(true), + repository_selection_mode: z.enum(['all', 'selected']).default('all'), + selected_repository_ids: z.array(z.number()).optional(), + model_slug: z.string().optional(), + triage_model_slug: z.string().optional(), + analysis_model_slug: z.string().optional(), + analysis_mode: z.enum(['auto', 'shallow', 'deep']).default('auto'), + auto_dismiss_enabled: z.boolean().default(false), + auto_dismiss_confidence_threshold: z.enum(['high', 'medium', 'low']).default('high'), + auto_analysis_enabled: z.boolean().default(false), + auto_analysis_min_severity: z.enum(['critical', 'high', 'medium', 'all']).default('high'), + }) + .passthrough(); export type SecurityAgentConfig = z.infer; -/** - * Map Dependabot state to our internal status - */ export function mapDependabotStateToStatus(state: DependabotAlertStateType): SecurityFindingStatus { switch (state) { case DependabotAlertState.OPEN: @@ -101,9 +84,6 @@ export function mapDependabotStateToStatus(state: DependabotAlertStateType): Sec } } -/** - * Get SLA days for a given severity - */ export function getSlaForSeverity( config: SecurityAgentConfig, severity: (typeof SecuritySeverity)[keyof typeof SecuritySeverity] @@ -122,9 +102,6 @@ export function getSlaForSeverity( } } -/** - * Calculate SLA due date from first detected date and SLA days - */ export function calculateSlaDueAt(firstDetectedAt: Date | string, slaDays: number): Date { const date = typeof firstDetectedAt === 'string' ? new Date(firstDetectedAt) : firstDetectedAt; const dueAt = new Date(date); @@ -132,9 +109,6 @@ export function calculateSlaDueAt(firstDetectedAt: Date | string, slaDays: numbe return dueAt; } -/** - * Parsed security finding ready for database insertion - */ export type ParsedSecurityFinding = { source: SecurityFindingSource; source_id: string; @@ -160,16 +134,10 @@ export type ParsedSecurityFinding = { dependency_scope: 'development' | 'runtime' | null; }; -/** - * Owner type for security reviews (org or user) - */ export type SecurityReviewOwner = | { organizationId: string; userId?: never } | { userId: string; organizationId?: never }; -/** - * Sync result type - */ export type SyncResult = { synced: number; created: number; diff --git a/src/lib/security-agent/db/security-analysis.test.ts b/src/lib/security-agent/db/security-analysis.test.ts new file mode 100644 index 000000000..d5c72fde9 --- /dev/null +++ b/src/lib/security-agent/db/security-analysis.test.ts @@ -0,0 +1,40 @@ +import { beforeAll, beforeEach, describe, expect, it, jest } from '@jest/globals'; +import { inspect } from 'util'; +import type * as analysisDbModule from './security-analysis'; + +const mockReturning: jest.Mock = jest.fn(); +const mockWhere: jest.Mock = jest.fn(() => ({ returning: mockReturning })); +const mockSet: jest.Mock = jest.fn(() => ({ where: mockWhere })); +const mockUpdate: jest.Mock = jest.fn(() => ({ set: mockSet })); + +jest.mock('@/lib/drizzle', () => ({ + db: { + update: mockUpdate, + }, +})); + +jest.mock('@sentry/nextjs', () => ({ captureException: jest.fn() })); + +let cleanupStaleAnalyses: typeof analysisDbModule.cleanupStaleAnalyses; + +beforeAll(async () => { + ({ cleanupStaleAnalyses } = await import('./security-analysis')); +}); + +beforeEach(() => { + jest.clearAllMocks(); + mockReturning.mockImplementation(async () => []); +}); + +describe('cleanupStaleAnalyses', () => { + it('adds anti-join exclusion for queue-owned pending/running rows', async () => { + await cleanupStaleAnalyses(30); + + expect(mockWhere).toHaveBeenCalledTimes(1); + const whereArg = mockWhere.mock.calls[0][0]; + const serialized = inspect(whereArg, { depth: 10 }); + expect(serialized).toContain('security_analysis_queue'); + expect(serialized).toContain('pending'); + expect(serialized).toContain('running'); + }); +}); diff --git a/src/lib/security-agent/db/security-analysis.ts b/src/lib/security-agent/db/security-analysis.ts index d9315340d..565382274 100644 --- a/src/lib/security-agent/db/security-analysis.ts +++ b/src/lib/security-agent/db/security-analysis.ts @@ -1,29 +1,24 @@ -/** - * Security Analysis - Database Operations - * - * Database operations for security finding analysis workflow. - * Handles analysis status updates, concurrency control, and cleanup. - */ - import { db } from '@/lib/drizzle'; -import { security_findings } from '@kilocode/db/schema'; -import { eq, and, sql, count, isNotNull, desc, or } from 'drizzle-orm'; +import { + security_findings, + security_analysis_queue, + security_analysis_owner_state, +} from '@kilocode/db/schema'; +import { eq, and, sql, count, isNotNull, desc, or, isNull } from 'drizzle-orm'; import { captureException } from '@sentry/nextjs'; import type { SecurityFinding } from '@kilocode/db/schema'; import type { + AutoAnalysisMinSeverity, + SecurityFindingStatus, + SecuritySeverity, SecurityReviewOwner, SecurityFindingAnalysis, SecurityFindingAnalysisStatus, } from '../core/types'; +import { SECURITY_ANALYSIS_OWNER_CAP } from '../core/constants'; -/** - * Owner type for database queries - */ type Owner = { type: 'org'; id: string } | { type: 'user'; id: string }; -/** - * Convert SecurityReviewOwner to Owner format used in queries - */ function toOwner(owner: SecurityReviewOwner): Owner { if ('organizationId' in owner && owner.organizationId) { return { type: 'org', id: owner.organizationId }; @@ -34,9 +29,97 @@ function toOwner(owner: SecurityReviewOwner): Owner { throw new Error('Invalid owner: must have either organizationId or userId'); } -/** - * Update analysis status for a finding - */ +export type AutoAnalysisQueueStatus = 'queued' | 'pending' | 'running' | 'failed' | 'completed'; + +export type AutoAnalysisFailureCode = + | 'NETWORK_TIMEOUT' + | 'UPSTREAM_5XX' + | 'TEMP_TOKEN_FAILURE' + | 'SKIPPED_NO_LONGER_ELIGIBLE' + | 'REOPEN_LOOP_GUARD' + | 'SKIPPED_ALREADY_IN_PROGRESS' + | 'ACTOR_RESOLUTION_FAILED' + | 'GITHUB_TOKEN_UNAVAILABLE' + | 'INVALID_CONFIG' + | 'MISSING_OWNERSHIP' + | 'PERMISSION_DENIED_PERMANENT' + | 'UNSUPPORTED_SEVERITY' + | 'STATE_GUARD_REJECTED' + | 'REQUEUE_TEMPORARY_PRECONDITION' + | 'INSUFFICIENT_CREDITS' + | 'START_CALL_AMBIGUOUS' + | 'RUN_LOST'; + +export type AutoAnalysisQueueSyncResult = { + enqueueCount: number; + eligibleCount: number; + boundarySkipCount: number; + unknownSeverityCount: number; +}; + +export const AUTO_ANALYSIS_REOPEN_REQUEUE_CAP = 2; +export const AUTO_ANALYSIS_OWNER_CAP = 2; +export const AUTO_ANALYSIS_MAX_ATTEMPTS = 5; + +const severityRankBySeverity = { + critical: 0, + high: 1, + medium: 2, + low: 3, +} satisfies Record; + +function minSeverityToMaxRank(minSeverity: AutoAnalysisMinSeverity): number { + switch (minSeverity) { + case 'critical': + return severityRankBySeverity.critical; + case 'high': + return severityRankBySeverity.high; + case 'medium': + return severityRankBySeverity.medium; + case 'all': + return severityRankBySeverity.low; + } +} + +export function getSeverityRank(severity: string | null | undefined): number | null { + if (severity === 'critical') return severityRankBySeverity.critical; + if (severity === 'high') return severityRankBySeverity.high; + if (severity === 'medium') return severityRankBySeverity.medium; + if (severity === 'low') return severityRankBySeverity.low; + return null; +} + +export function isFindingEligibleForAutoAnalysis(params: { + findingCreatedAt: string; + findingStatus: string; + severity: string | null; + ownerAutoAnalysisEnabledAt: string | null; + isAgentEnabled: boolean; + autoAnalysisEnabled: boolean; + autoAnalysisMinSeverity: AutoAnalysisMinSeverity; +}): { eligible: boolean; severityRank: number | null } { + const severityRank = getSeverityRank(params.severity); + + if (!params.isAgentEnabled || !params.autoAnalysisEnabled) { + return { eligible: false, severityRank }; + } + if (params.findingStatus !== 'open') { + return { eligible: false, severityRank }; + } + if (!params.ownerAutoAnalysisEnabledAt) { + return { eligible: false, severityRank }; + } + if (Date.parse(params.findingCreatedAt) < Date.parse(params.ownerAutoAnalysisEnabledAt)) { + return { eligible: false, severityRank }; + } + if (severityRank == null) { + return { eligible: false, severityRank: null }; + } + + const maxRank = minSeverityToMaxRank(params.autoAnalysisMinSeverity); + return { eligible: severityRank <= maxRank, severityRank }; +} + export async function updateAnalysisStatus( findingId: string, status: SecurityFindingAnalysisStatus, @@ -66,11 +149,9 @@ export async function updateAnalysisStatus( updateData.analysis = updates.analysis; } - // Auto-set timestamps and clear previous state based on status if (status === 'pending') { - // Clear previous error, analysis, and session IDs when starting a new analysis - // BUT preserve analysis if explicitly provided (e.g., triage data before sandbox runs) updateData.analysis_error = null; + // Preserve analysis if explicitly provided (e.g. triage data before sandbox runs) if (updates.analysis === undefined) { updateData.analysis = null; } @@ -79,8 +160,7 @@ export async function updateAnalysisStatus( updateData.cli_session_id = null; } if (status === 'running') { - // IMPORTANT: don't reset started_at on repeated "running" updates (status events arrive frequently). - // Only set if it is currently null. + // Only set started_at once (coalesce keeps the original value) updateData.analysis_started_at = sql`coalesce(${security_findings.analysis_started_at}, now())`; } if (status === 'completed' || status === 'failed') { @@ -97,9 +177,6 @@ export async function updateAnalysisStatus( } } -/** - * Count currently running analyses for an owner - */ export async function countRunningAnalyses(owner: SecurityReviewOwner): Promise { try { const ownerConverted = toOwner(owner); @@ -111,7 +188,6 @@ export async function countRunningAnalyses(owner: SecurityReviewOwner): Promise< conditions.push(eq(security_findings.owned_by_user_id, ownerConverted.id)); } - // Count pending and running analyses conditions.push( or( eq(security_findings.analysis_status, 'pending'), @@ -134,12 +210,9 @@ export async function countRunningAnalyses(owner: SecurityReviewOwner): Promise< } } -/** - * Check if owner can start a new analysis (within concurrency limit) - */ export async function canStartAnalysis( owner: SecurityReviewOwner, - maxConcurrent = 3 + maxConcurrent = SECURITY_ANALYSIS_OWNER_CAP ): Promise<{ allowed: boolean; currentCount: number; limit: number }> { const currentCount = await countRunningAnalyses(owner); return { @@ -149,9 +222,6 @@ export async function canStartAnalysis( }; } -/** - * Get findings pending analysis for an owner - */ export async function getFindingsPendingAnalysis( owner: SecurityReviewOwner, limit = 10 @@ -166,7 +236,6 @@ export async function getFindingsPendingAnalysis( conditions.push(eq(security_findings.owned_by_user_id, ownerConverted.id)); } - // Only open findings without analysis conditions.push(eq(security_findings.status, 'open')); conditions.push(sql`${security_findings.analysis_status} IS NULL`); @@ -186,10 +255,7 @@ export async function getFindingsPendingAnalysis( } } -/** - * Clean up stale "running" analyses (e.g., from crashed sessions) - * Call this periodically via cron job - */ +/** Clean up stale "running" analyses from crashed sessions. */ export async function cleanupStaleAnalyses(maxAgeMinutes = 30): Promise { try { const result = await db @@ -203,7 +269,13 @@ export async function cleanupStaleAnalyses(maxAgeMinutes = 30): Promise .where( and( eq(security_findings.analysis_status, 'running'), - sql`${security_findings.analysis_started_at} < now() - make_interval(mins => ${maxAgeMinutes})` + sql`${security_findings.analysis_started_at} < now() - make_interval(mins => ${maxAgeMinutes})`, + sql`NOT EXISTS ( + SELECT 1 + FROM ${security_analysis_queue} + WHERE ${security_analysis_queue.finding_id} = ${security_findings.id} + AND ${security_analysis_queue.queue_status} IN ('pending', 'running') + )` ) ) .returning({ id: security_findings.id }); @@ -218,10 +290,6 @@ export async function cleanupStaleAnalyses(maxAgeMinutes = 30): Promise } } -/** - * List findings that have analysis status (for jobs view) - * Returns findings ordered by analysis_started_at desc (most recent first) - */ export async function listSecurityFindingsWithAnalysis(params: { owner: SecurityReviewOwner; limit?: number; @@ -238,7 +306,6 @@ export async function listSecurityFindingsWithAnalysis(params: { conditions.push(eq(security_findings.owned_by_user_id, ownerConverted.id)); } - // Only findings with analysis status set conditions.push(isNotNull(security_findings.analysis_status)); const findings = await db @@ -259,9 +326,6 @@ export async function listSecurityFindingsWithAnalysis(params: { } } -/** - * Count findings with analysis status (for pagination) - */ export async function countSecurityFindingsWithAnalysis( owner: SecurityReviewOwner ): Promise { @@ -275,7 +339,6 @@ export async function countSecurityFindingsWithAnalysis( conditions.push(eq(security_findings.owned_by_user_id, ownerConverted.id)); } - // Only findings with analysis status set conditions.push(isNotNull(security_findings.analysis_status)); const result = await db @@ -292,3 +355,235 @@ export async function countSecurityFindingsWithAnalysis( throw error; } } + +export async function getOwnerAutoAnalysisEnabledAt( + owner: SecurityReviewOwner +): Promise { + const ownerConverted = toOwner(owner); + const ownerCondition = + ownerConverted.type === 'org' + ? eq(security_analysis_owner_state.owned_by_organization_id, ownerConverted.id) + : eq(security_analysis_owner_state.owned_by_user_id, ownerConverted.id); + + const [state] = await db + .select({ autoAnalysisEnabledAt: security_analysis_owner_state.auto_analysis_enabled_at }) + .from(security_analysis_owner_state) + .where(ownerCondition) + .limit(1); + + return state?.autoAnalysisEnabledAt ?? null; +} + +export async function setOwnerAutoAnalysisEnabledAtNow(owner: SecurityReviewOwner): Promise { + const ownerConverted = toOwner(owner); + const ownerCondition = + ownerConverted.type === 'org' + ? eq(security_analysis_owner_state.owned_by_organization_id, ownerConverted.id) + : eq(security_analysis_owner_state.owned_by_user_id, ownerConverted.id); + + await db + .insert(security_analysis_owner_state) + .values({ + owned_by_organization_id: ownerConverted.type === 'org' ? ownerConverted.id : null, + owned_by_user_id: ownerConverted.type === 'user' ? ownerConverted.id : null, + auto_analysis_enabled_at: sql`now()`, + updated_at: sql`now()`, + }) + .onConflictDoNothing(); + + await db + .update(security_analysis_owner_state) + .set({ auto_analysis_enabled_at: sql`now()`, updated_at: sql`now()` }) + .where(and(ownerCondition, isNull(security_analysis_owner_state.auto_analysis_enabled_at))); +} + +export async function syncAutoAnalysisQueueForFinding(params: { + owner: SecurityReviewOwner; + findingId: string; + findingCreatedAt: string; + previousStatus: SecurityFindingStatus | null; + currentStatus: SecurityFindingStatus; + severity: string | null; + isAgentEnabled: boolean; + autoAnalysisEnabled: boolean; + autoAnalysisMinSeverity: AutoAnalysisMinSeverity; + ownerAutoAnalysisEnabledAt: string | null; +}): Promise { + const ownerConverted = toOwner(params.owner); + const { eligible, severityRank } = isFindingEligibleForAutoAnalysis({ + findingCreatedAt: params.findingCreatedAt, + findingStatus: params.currentStatus, + severity: params.severity, + ownerAutoAnalysisEnabledAt: params.ownerAutoAnalysisEnabledAt, + isAgentEnabled: params.isAgentEnabled, + autoAnalysisEnabled: params.autoAnalysisEnabled, + autoAnalysisMinSeverity: params.autoAnalysisMinSeverity, + }); + const isBoundarySkip = + params.ownerAutoAnalysisEnabledAt != null && + Date.parse(params.findingCreatedAt) < Date.parse(params.ownerAutoAnalysisEnabledAt); + const unknownSeverityCount = severityRank == null ? 1 : 0; + let enqueueCount = 0; + + await db.transaction(async tx => { + if (severityRank != null) { + await tx + .update(security_analysis_queue) + .set({ + severity_rank: severityRank, + updated_at: sql`now()`, + }) + .where( + and( + eq(security_analysis_queue.finding_id, params.findingId), + eq(security_analysis_queue.queue_status, 'queued') + ) + ); + } + + if (!eligible) { + await tx + .update(security_analysis_queue) + .set({ + queue_status: 'completed', + failure_code: 'SKIPPED_NO_LONGER_ELIGIBLE', + claim_token: null, + claimed_at: null, + claimed_by_job_id: null, + updated_at: sql`now()`, + }) + .where( + and( + eq(security_analysis_queue.finding_id, params.findingId), + eq(security_analysis_queue.queue_status, 'queued') + ) + ); + } + + const isReopened = + (params.previousStatus === 'fixed' || params.previousStatus === 'ignored') && + params.currentStatus === 'open'; + + if (isReopened && eligible) { + await tx + .update(security_analysis_queue) + .set({ + queue_status: 'queued', + queued_at: sql`now()`, + attempt_count: 0, + next_retry_at: null, + failure_code: null, + last_error_redacted: null, + claimed_at: null, + claimed_by_job_id: null, + claim_token: null, + reopen_requeue_count: sql`${security_analysis_queue.reopen_requeue_count} + 1`, + updated_at: sql`now()`, + }) + .where( + and( + eq(security_analysis_queue.finding_id, params.findingId), + or( + eq(security_analysis_queue.queue_status, 'completed'), + eq(security_analysis_queue.queue_status, 'failed') + ), + sql`${security_analysis_queue.reopen_requeue_count} < ${AUTO_ANALYSIS_REOPEN_REQUEUE_CAP}` + ) + ); + + await tx + .update(security_analysis_queue) + .set({ + queue_status: 'failed', + failure_code: 'REOPEN_LOOP_GUARD', + updated_at: sql`now()`, + }) + .where( + and( + eq(security_analysis_queue.finding_id, params.findingId), + or( + eq(security_analysis_queue.queue_status, 'completed'), + eq(security_analysis_queue.queue_status, 'failed') + ), + sql`${security_analysis_queue.reopen_requeue_count} >= ${AUTO_ANALYSIS_REOPEN_REQUEUE_CAP}` + ) + ); + } + + if (eligible) { + const inserted = await tx + .insert(security_analysis_queue) + .values({ + finding_id: params.findingId, + owned_by_organization_id: ownerConverted.type === 'org' ? ownerConverted.id : null, + owned_by_user_id: ownerConverted.type === 'user' ? ownerConverted.id : null, + queue_status: 'queued', + severity_rank: severityRank ?? severityRankBySeverity.low, + queued_at: sql`now()`, + updated_at: sql`now()`, + }) + .onConflictDoNothing() + .returning({ id: security_analysis_queue.id }); + enqueueCount = inserted.length; + } + }); + + return { + enqueueCount, + eligibleCount: eligible ? 1 : 0, + boundarySkipCount: isBoundarySkip ? 1 : 0, + unknownSeverityCount, + }; +} + +export async function tryAcquireAnalysisStartLease(findingId: string): Promise { + const [lease] = await db + .update(security_findings) + .set({ + analysis_status: 'pending', + updated_at: sql`now()`, + }) + .where( + and( + eq(security_findings.id, findingId), + eq(security_findings.status, 'open'), + or( + isNull(security_findings.analysis_status), + eq(security_findings.analysis_status, 'completed'), + eq(security_findings.analysis_status, 'failed') + ) + ) + ) + .returning({ id: security_findings.id }); + + return Boolean(lease); +} + +export async function transitionAutoAnalysisQueueFromCallback(params: { + findingId: string; + toStatus: 'completed' | 'failed'; + failureCode?: AutoAnalysisFailureCode; + errorMessage?: string; +}): Promise { + const values: { + queue_status: AutoAnalysisQueueStatus; + failure_code: string | null; + last_error_redacted: string | null; + updated_at: ReturnType; + } = { + queue_status: params.toStatus, + failure_code: params.failureCode ?? null, + last_error_redacted: params.errorMessage ?? null, + updated_at: sql`now()`, + }; + + await db + .update(security_analysis_queue) + .set(values) + .where( + and( + eq(security_analysis_queue.finding_id, params.findingId), + eq(security_analysis_queue.queue_status, 'running') + ) + ); +} diff --git a/src/lib/security-agent/db/security-config.ts b/src/lib/security-agent/db/security-config.ts index 56322d89e..488f74bab 100644 --- a/src/lib/security-agent/db/security-config.ts +++ b/src/lib/security-agent/db/security-config.ts @@ -1,26 +1,16 @@ -/** - * Security Config - Database Operations - * - * Wrapper around agent_configs for security agent configuration. - * Uses the existing agent_configs table with agent_type: 'security_scan'. - */ - import { getAgentConfigForOwner, upsertAgentConfigForOwner, setAgentEnabledForOwner, } from '@/lib/agent-config/db/agent-configs'; import type { Owner } from '@/lib/code-reviews/core'; -import { DEFAULT_SECURITY_AGENT_CONFIG } from '../core/constants'; -import type { SecurityAgentConfig } from '../core/types'; +import { DEFAULT_SECURITY_AGENT_CONFIG, parseSecurityAgentConfig } from '../core/constants'; +import { SecurityAgentConfigSchema, type SecurityAgentConfig } from '../core/types'; +import { setOwnerAutoAnalysisEnabledAtNow } from './security-analysis'; const AGENT_TYPE = 'security_scan'; const DEFAULT_PLATFORM = 'github'; -/** - * Gets security agent configuration for an owner - * Returns default config if none exists - */ export async function getSecurityAgentConfig( owner: Owner, platform: string = DEFAULT_PLATFORM @@ -28,20 +18,12 @@ export async function getSecurityAgentConfig( const config = await getAgentConfigForOwner(owner, AGENT_TYPE, platform); if (!config) { - return DEFAULT_SECURITY_AGENT_CONFIG; + return SecurityAgentConfigSchema.parse(DEFAULT_SECURITY_AGENT_CONFIG); } - // Merge with defaults to ensure all fields are present - return { - ...DEFAULT_SECURITY_AGENT_CONFIG, - ...(config.config as Partial), - }; + return parseSecurityAgentConfig(config.config); } -/** - * Gets security agent configuration with enabled status - * Returns null if no config exists - */ export async function getSecurityAgentConfigWithStatus( owner: Owner, platform: string = DEFAULT_PLATFORM @@ -58,29 +40,19 @@ export async function getSecurityAgentConfigWithStatus( return { storedConfig: agentConfig.config as Partial, - config: { - ...DEFAULT_SECURITY_AGENT_CONFIG, - ...(agentConfig.config as Partial), - }, + config: parseSecurityAgentConfig(agentConfig.config), isEnabled: agentConfig.is_enabled, }; } -/** - * Creates or updates security agent configuration for an owner - */ export async function upsertSecurityAgentConfig( owner: Owner, config: Partial, createdBy: string, platform: string = DEFAULT_PLATFORM ): Promise { - const existingConfig = await getAgentConfigForOwner(owner, AGENT_TYPE, platform); - const fullConfig = { - ...DEFAULT_SECURITY_AGENT_CONFIG, - ...(existingConfig?.config as Partial | undefined), - ...config, - }; + const existingConfig = await getSecurityAgentConfigWithStatus(owner, platform); + const fullConfig = parseSecurityAgentConfig({ ...existingConfig?.storedConfig, ...config }); await upsertAgentConfigForOwner({ owner, @@ -90,11 +62,16 @@ export async function upsertSecurityAgentConfig( isEnabled: true, createdBy, }); + + // Idempotent: sets auto_analysis_enabled_at only when null, so safe to call on every save. + // This guards against a prior save where the config committed but the timestamp write failed. + if (fullConfig.auto_analysis_enabled) { + await setOwnerAutoAnalysisEnabledAtNow( + owner.type === 'org' ? { organizationId: owner.id } : { userId: owner.id } + ); + } } -/** - * Enables or disables security agent for an owner - */ export async function setSecurityAgentEnabled( owner: Owner, isEnabled: boolean, @@ -103,9 +80,6 @@ export async function setSecurityAgentEnabled( await setAgentEnabledForOwner(owner, AGENT_TYPE, platform, isEnabled); } -/** - * Checks if security agent is enabled for an owner - */ export async function isSecurityAgentEnabled( owner: Owner, platform: string = DEFAULT_PLATFORM diff --git a/src/lib/security-agent/db/security-findings.ts b/src/lib/security-agent/db/security-findings.ts index d6492e8d3..85ff6463f 100644 --- a/src/lib/security-agent/db/security-findings.ts +++ b/src/lib/security-agent/db/security-findings.ts @@ -1,10 +1,3 @@ -/** - * Security Findings - Database Operations - * - * Database operations for security findings. - * Follows Drizzle ORM patterns used throughout the codebase. - */ - import { db } from '@/lib/drizzle'; import { security_findings } from '@kilocode/db/schema'; import { eq, and, desc, count, sql, max, or } from 'drizzle-orm'; @@ -19,14 +12,8 @@ import type { type SecurityFindingStatusFilter = SecurityFindingStatus | 'closed'; -/** - * Owner type for database queries - */ type Owner = { type: 'org'; id: string } | { type: 'user'; id: string }; -/** - * Convert SecurityReviewOwner to Owner format used in queries - */ function toOwner(owner: SecurityReviewOwner): Owner { if ('organizationId' in owner && owner.organizationId) { return { type: 'org', id: owner.organizationId }; @@ -37,9 +24,6 @@ function toOwner(owner: SecurityReviewOwner): Owner { throw new Error('Invalid owner: must have either organizationId or userId'); } -/** - * Parameters for creating a security finding - */ type CreateFindingParams = ParsedSecurityFinding & { owner: SecurityReviewOwner; platformIntegrationId?: string; @@ -47,9 +31,6 @@ type CreateFindingParams = ParsedSecurityFinding & { slaDueAt?: Date; }; -/** - * Creates a new security finding - */ export async function createSecurityFinding(params: CreateFindingParams): Promise { try { const owner = toOwner(params.owner); @@ -98,78 +79,164 @@ export async function createSecurityFinding(params: CreateFindingParams): Promis } } -/** - * Upserts a security finding (create or update based on unique constraint) - * Uses repo_full_name + source + source_id as the unique key - */ -export async function upsertSecurityFinding(params: CreateFindingParams): Promise { +export type UpsertSecurityFindingResult = { + findingId: string; + wasInserted: boolean; + previousStatus: SecurityFindingStatus | null; + findingCreatedAt: string; +}; + +/** Upsert using repo_full_name + source + source_id as the unique key. */ +export async function upsertSecurityFinding( + params: CreateFindingParams +): Promise { try { const owner = toOwner(params.owner); - const [finding] = await db - .insert(security_findings) - .values({ - owned_by_organization_id: owner.type === 'org' ? owner.id : null, - owned_by_user_id: owner.type === 'user' ? owner.id : null, - platform_integration_id: params.platformIntegrationId || null, - repo_full_name: params.repoFullName, - source: params.source, - source_id: params.source_id, - severity: params.severity, - ghsa_id: params.ghsa_id, - cve_id: params.cve_id, - package_name: params.package_name, - package_ecosystem: params.package_ecosystem, - vulnerable_version_range: params.vulnerable_version_range, - patched_version: params.patched_version, - manifest_path: params.manifest_path, - title: params.title, - description: params.description, - status: params.status, - ignored_reason: params.ignored_reason, - ignored_by: params.ignored_by, - fixed_at: params.fixed_at, - sla_due_at: params.slaDueAt?.toISOString() || null, - dependabot_html_url: params.dependabot_html_url, - raw_data: params.raw_data, - first_detected_at: params.first_detected_at, - // Additional metadata - cwe_ids: params.cwe_ids, - cvss_score: params.cvss_score?.toString() || null, - dependency_scope: params.dependency_scope, - }) - .onConflictDoUpdate({ - target: [ - security_findings.repo_full_name, - security_findings.source, - security_findings.source_id, - ], - set: { - severity: params.severity, - ghsa_id: params.ghsa_id, - cve_id: params.cve_id, - vulnerable_version_range: params.vulnerable_version_range, - patched_version: params.patched_version, - title: params.title, - description: params.description, - status: params.status, - ignored_reason: params.ignored_reason, - ignored_by: params.ignored_by, - fixed_at: params.fixed_at, - sla_due_at: params.slaDueAt?.toISOString() || null, - dependabot_html_url: params.dependabot_html_url, - raw_data: params.raw_data, - // Additional metadata - cwe_ids: params.cwe_ids, - cvss_score: params.cvss_score?.toString() || null, - dependency_scope: params.dependency_scope, - last_synced_at: sql`now()`, - updated_at: sql`now()`, - }, - }) - .returning({ id: security_findings.id }); + const { rows } = await db.execute<{ + findingId: string; + wasInserted: boolean; + previousStatus: SecurityFindingStatus | null; + findingCreatedAt: string; + }>(sql` + WITH existing_match AS ( + SELECT ${security_findings.id} AS id, + ${security_findings.status} AS previous_status, + ${security_findings.created_at} AS created_at + FROM ${security_findings} + WHERE ${security_findings.repo_full_name} = ${params.repoFullName} + AND ${security_findings.source} = ${params.source} + AND ${security_findings.source_id} = ${params.source_id} + FOR UPDATE + ), + updated AS ( + UPDATE ${security_findings} + SET + ${security_findings.severity} = ${params.severity}, + ${security_findings.ghsa_id} = ${params.ghsa_id}, + ${security_findings.cve_id} = ${params.cve_id}, + ${security_findings.vulnerable_version_range} = ${params.vulnerable_version_range}, + ${security_findings.patched_version} = ${params.patched_version}, + ${security_findings.title} = ${params.title}, + ${security_findings.description} = ${params.description}, + ${security_findings.status} = ${params.status}, + ${security_findings.ignored_reason} = ${params.ignored_reason}, + ${security_findings.ignored_by} = ${params.ignored_by}, + ${security_findings.fixed_at} = ${params.fixed_at}, + ${security_findings.sla_due_at} = ${params.slaDueAt?.toISOString() || null}, + ${security_findings.dependabot_html_url} = ${params.dependabot_html_url}, + ${security_findings.raw_data} = ${params.raw_data}, + ${security_findings.cwe_ids} = ${params.cwe_ids}, + ${security_findings.cvss_score} = ${params.cvss_score?.toString() || null}, + ${security_findings.dependency_scope} = ${params.dependency_scope}, + ${security_findings.last_synced_at} = now(), + ${security_findings.updated_at} = now() + FROM existing_match + WHERE ${security_findings.id} = existing_match.id + RETURNING + ${security_findings.id} AS id, + existing_match.previous_status AS previous_status, + ${security_findings.created_at} AS created_at + ), + inserted AS ( + INSERT INTO ${security_findings} ( + ${security_findings.owned_by_organization_id}, + ${security_findings.owned_by_user_id}, + ${security_findings.platform_integration_id}, + ${security_findings.repo_full_name}, + ${security_findings.source}, + ${security_findings.source_id}, + ${security_findings.severity}, + ${security_findings.ghsa_id}, + ${security_findings.cve_id}, + ${security_findings.package_name}, + ${security_findings.package_ecosystem}, + ${security_findings.vulnerable_version_range}, + ${security_findings.patched_version}, + ${security_findings.manifest_path}, + ${security_findings.title}, + ${security_findings.description}, + ${security_findings.status}, + ${security_findings.ignored_reason}, + ${security_findings.ignored_by}, + ${security_findings.fixed_at}, + ${security_findings.sla_due_at}, + ${security_findings.dependabot_html_url}, + ${security_findings.raw_data}, + ${security_findings.first_detected_at}, + ${security_findings.cwe_ids}, + ${security_findings.cvss_score}, + ${security_findings.dependency_scope} + ) + SELECT + ${owner.type === 'org' ? owner.id : null}, + ${owner.type === 'user' ? owner.id : null}, + ${params.platformIntegrationId || null}, + ${params.repoFullName}, + ${params.source}, + ${params.source_id}, + ${params.severity}, + ${params.ghsa_id}, + ${params.cve_id}, + ${params.package_name}, + ${params.package_ecosystem}, + ${params.vulnerable_version_range}, + ${params.patched_version}, + ${params.manifest_path}, + ${params.title}, + ${params.description}, + ${params.status}, + ${params.ignored_reason}, + ${params.ignored_by}, + ${params.fixed_at}, + ${params.slaDueAt?.toISOString() || null}, + ${params.dependabot_html_url}, + ${params.raw_data}, + ${params.first_detected_at}, + ${params.cwe_ids}, + ${params.cvss_score?.toString() || null}, + ${params.dependency_scope} + WHERE NOT EXISTS (SELECT 1 FROM updated) + ON CONFLICT (${security_findings.repo_full_name}, ${security_findings.source}, ${security_findings.source_id}) DO NOTHING + RETURNING ${security_findings.id} AS id, + NULL::text AS previous_status, + ${security_findings.created_at} AS created_at + ), + fallback AS ( + SELECT + ${security_findings.id} AS id, + ${security_findings.status} AS previous_status, + ${security_findings.created_at} AS created_at + FROM ${security_findings} + WHERE ${security_findings.repo_full_name} = ${params.repoFullName} + AND ${security_findings.source} = ${params.source} + AND ${security_findings.source_id} = ${params.source_id} + AND NOT EXISTS (SELECT 1 FROM updated) + AND NOT EXISTS (SELECT 1 FROM inserted) + LIMIT 1 + ), + chosen AS ( + SELECT id, false AS was_inserted, previous_status, created_at FROM updated + UNION ALL + SELECT id, true AS was_inserted, previous_status, created_at FROM inserted + UNION ALL + SELECT id, false AS was_inserted, previous_status, created_at FROM fallback + ) + SELECT + chosen.id AS "findingId", + chosen.was_inserted AS "wasInserted", + chosen.previous_status AS "previousStatus", + chosen.created_at AS "findingCreatedAt" + FROM chosen + LIMIT 1 + `); + + const finding = rows[0]; + if (!finding) { + throw new Error('Failed to upsert security finding'); + } - return finding.id; + return finding; } catch (error) { captureException(error, { tags: { operation: 'upsertSecurityFinding' }, @@ -179,9 +246,6 @@ export async function upsertSecurityFinding(params: CreateFindingParams): Promis } } -/** - * Gets a security finding by ID - */ export async function getSecurityFindingById(findingId: string): Promise { try { const [finding] = await db @@ -200,24 +264,10 @@ export async function getSecurityFindingById(findingId: string): Promise { try { const { @@ -252,14 +299,12 @@ export async function listSecurityFindings(params: ListFindingsParams): Promise< const conditions = []; - // Owner condition if (ownerConverted.type === 'org') { conditions.push(eq(security_findings.owned_by_organization_id, ownerConverted.id)); } else { conditions.push(eq(security_findings.owned_by_user_id, ownerConverted.id)); } - // Optional filters if (status) { if (status === 'closed') { conditions.push( @@ -278,7 +323,6 @@ export async function listSecurityFindings(params: ListFindingsParams): Promise< if (packageName) { conditions.push(eq(security_findings.package_name, packageName)); } - // Exploitability filter - filters based on analysis.sandboxAnalysis.isExploitable if (exploitability && exploitability !== 'all') { if (exploitability === 'exploitable') { // isExploitable === true @@ -292,10 +336,8 @@ export async function listSecurityFindings(params: ListFindingsParams): Promise< ); } } - // Suggested action filter - filters based on triage or sandbox suggestedAction = 'dismiss' if (suggestedAction && suggestedAction !== 'all') { if (suggestedAction === 'dismissable') { - // Either triage.suggestedAction = 'dismiss' OR sandboxAnalysis.suggestedAction = 'dismiss' conditions.push( or( sql`(${security_findings.analysis}->'triage'->>'suggestedAction') = 'dismiss'`, @@ -304,18 +346,14 @@ export async function listSecurityFindings(params: ListFindingsParams): Promise< ); } } - // Analysis status filter - filters based on analysis_status column if (analysisStatus && analysisStatus !== 'all') { if (analysisStatus === 'not_analyzed') { - // analysis_status is null (never analyzed) conditions.push(sql`${security_findings.analysis_status} IS NULL`); } else { - // Match specific analysis status conditions.push(eq(security_findings.analysis_status, analysisStatus)); } } - // Sort by severity (critical > high > medium > low) then by created_at descending const severityOrder = sql`CASE ${security_findings.severity} WHEN 'critical' THEN 1 WHEN 'high' THEN 2 @@ -342,9 +380,6 @@ export async function listSecurityFindings(params: ListFindingsParams): Promise< } } -/** - * Counts security findings for an owner - */ export async function countSecurityFindings(params: { owner: SecurityReviewOwner; status?: SecurityFindingStatusFilter; @@ -396,9 +431,6 @@ export async function countSecurityFindings(params: { } } -/** - * Gets summary counts by severity for an owner - */ export async function getSecurityFindingsSummary(params: { owner: SecurityReviewOwner; repoFullName?: string; @@ -419,7 +451,6 @@ export async function getSecurityFindingsSummary(params: { const baseConditions = []; - // Owner condition if (ownerConverted.type === 'org') { baseConditions.push(eq(security_findings.owned_by_organization_id, ownerConverted.id)); } else { @@ -440,7 +471,6 @@ export async function getSecurityFindingsSummary(params: { } } - // Get counts by severity const severityCounts = await db .select({ severity: security_findings.severity, @@ -450,7 +480,6 @@ export async function getSecurityFindingsSummary(params: { .where(and(...baseConditions)) .groupBy(security_findings.severity); - // Get counts by status const statusCounts = await db .select({ status: security_findings.status, @@ -488,9 +517,6 @@ export async function getSecurityFindingsSummary(params: { } } -/** - * Updates the status of a security finding - */ export async function updateSecurityFindingStatus( findingId: string, status: SecurityFindingStatus, @@ -526,9 +552,6 @@ export async function updateSecurityFindingStatus( } } -/** - * Gets distinct repositories with security findings for an owner - */ export async function getRepositoriesWithFindings(owner: SecurityReviewOwner): Promise { try { const ownerConverted = toOwner(owner); @@ -553,9 +576,6 @@ export async function getRepositoriesWithFindings(owner: SecurityReviewOwner): P } } -/** - * Finds an existing security finding by source - */ export async function findSecurityFindingBySource( repoFullName: string, source: string, @@ -584,10 +604,6 @@ export async function findSecurityFindingBySource( } } -/** - * Gets the most recent last_synced_at timestamp for an owner's findings - * Optionally filtered by repository - */ export async function getLastSyncTime(params: { owner: SecurityReviewOwner; repoFullName?: string; @@ -625,11 +641,6 @@ export async function getLastSyncTime(params: { } } -/** - * Gets repositories with findings that are not in the list of accessible repositories. - * These are "orphaned" repositories - they have findings but the GitHub integration - * no longer has access to them. - */ export async function getOrphanedRepositoriesWithFindingCounts(params: { owner: SecurityReviewOwner; accessibleRepoFullNames: string[]; @@ -647,7 +658,6 @@ export async function getOrphanedRepositoriesWithFindingCounts(params: { conditions.push(eq(security_findings.owned_by_user_id, ownerConverted.id)); } - // Get all repositories with findings for this owner const reposWithFindings = await db .select({ repoFullName: security_findings.repo_full_name, @@ -657,7 +667,6 @@ export async function getOrphanedRepositoriesWithFindingCounts(params: { .where(and(...conditions)) .groupBy(security_findings.repo_full_name); - // Filter to only include repos that are NOT in the accessible list const orphanedRepos = reposWithFindings.filter( repo => !accessibleRepoFullNames.includes(repo.repoFullName) ); @@ -672,10 +681,6 @@ export async function getOrphanedRepositoriesWithFindingCounts(params: { } } -/** - * Deletes all security findings for a specific repository owned by the given owner. - * Returns the count of deleted findings. - */ export async function deleteFindingsByRepository(params: { owner: SecurityReviewOwner; repoFullName: string; diff --git a/src/lib/security-agent/services/analysis-service.test.ts b/src/lib/security-agent/services/analysis-service.test.ts index 0a13a5b63..f1673d9f8 100644 --- a/src/lib/security-agent/services/analysis-service.test.ts +++ b/src/lib/security-agent/services/analysis-service.test.ts @@ -14,6 +14,9 @@ const mockGetSecurityFindingById = jest.fn() as jest.MockedFunction< const mockUpdateAnalysisStatus = jest.fn() as jest.MockedFunction< typeof securityAnalysisModule.updateAnalysisStatus >; +const mockTryAcquireAnalysisStartLease = jest.fn() as jest.MockedFunction< + typeof securityAnalysisModule.tryAcquireAnalysisStartLease +>; const mockTriageSecurityFinding = jest.fn() as jest.MockedFunction< typeof triageModule.triageSecurityFinding >; @@ -24,13 +27,28 @@ const mockPrepareSession = jest.fn(); const mockInitiateFromPreparedSession = jest.fn(); // eslint-disable-next-line @typescript-eslint/no-explicit-any const mockDeleteSession = jest.fn(); +const mockInfoLogger = jest.fn(); +const mockErrorLogger = jest.fn(); jest.mock('@/lib/security-agent/db/security-findings', () => ({ getSecurityFindingById: mockGetSecurityFindingById, })); -jest.mock('@/lib/security-agent/db/security-analysis', () => ({ - updateAnalysisStatus: mockUpdateAnalysisStatus, +jest.mock('@/lib/security-agent/db/security-analysis', () => { + const actual: { isFindingEligibleForAutoAnalysis: unknown } = jest.requireActual( + '@/lib/security-agent/db/security-analysis' + ); + return { + updateAnalysisStatus: mockUpdateAnalysisStatus, + tryAcquireAnalysisStartLease: mockTryAcquireAnalysisStartLease, + isFindingEligibleForAutoAnalysis: actual.isFindingEligibleForAutoAnalysis, + AUTO_ANALYSIS_MAX_ATTEMPTS: 5, + AUTO_ANALYSIS_OWNER_CAP: 2, + }; +}); + +jest.mock('@/lib/config.server', () => ({ + INTERNAL_API_SECRET: 'test-internal-secret', })); jest.mock('./triage-service', () => ({ @@ -65,6 +83,11 @@ jest.mock('./extraction-service', () => ({ extractSandboxAnalysis: jest.fn(() => Promise.resolve()), })); +jest.mock('@/lib/utils.server', () => ({ + sentryLogger: (_scope: string, level: string) => + level === 'error' ? mockErrorLogger : mockInfoLogger, +})); + let startSecurityAnalysis: typeof startSecurityAnalysisType; let extractLastAssistantMessage: typeof extractLastAssistantMessageType; @@ -75,6 +98,32 @@ beforeAll(async () => { describe('analysis-service', () => { beforeEach(() => { jest.clearAllMocks(); + mockTryAcquireAnalysisStartLease.mockResolvedValue(true); + }); + + it('does not start when start lease cannot be acquired', async () => { + const findingId = 'finding-lease-miss'; + const user = { id: 'user-1', google_user_email: 'test@example.com' } as User; + + mockGetSecurityFindingById.mockResolvedValue({ + id: findingId, + status: 'fixed', + analysis_status: 'running', + } as Awaited>); + mockTryAcquireAnalysisStartLease.mockResolvedValue(false); + + const result = await startSecurityAnalysis({ + findingId, + user, + githubRepo: 'acme/repo', + githubToken: 'gh-token', + }); + + expect(result).toEqual({ + started: false, + error: "Finding status is 'fixed', analysis requires 'open' status", + }); + expect(mockUpdateAnalysisStatus).not.toHaveBeenCalledWith(findingId, 'pending'); }); it('passes organization id to cloud-agent-next prepareSession', async () => { diff --git a/src/lib/security-agent/services/analysis-service.ts b/src/lib/security-agent/services/analysis-service.ts index 0078d8d26..647cfb173 100644 --- a/src/lib/security-agent/services/analysis-service.ts +++ b/src/lib/security-agent/services/analysis-service.ts @@ -1,15 +1,3 @@ -/** - * Security Analysis Service - * - * Orchestrates LLM-powered analysis of security findings using a three-tier approach: - * - Tier 1: Quick triage via direct LLM call (always runs first) - * - Tier 2: Sandbox analysis via cloud-agent-next (only if needed or forced) - * - Tier 3: Structured extraction via direct LLM call (extracts fields from raw markdown) - * - * Tier 2 uses a prepare+initiate+callback pattern via cloud-agent-next. - * The callback endpoint handles result retrieval and Tier 3 extraction. - */ - import 'server-only'; import { randomUUID } from 'crypto'; import { @@ -18,7 +6,7 @@ import { } from '@/lib/cloud-agent-next/cloud-agent-client'; import { generateApiToken } from '@/lib/tokens'; import { getSecurityFindingById } from '../db/security-findings'; -import { updateAnalysisStatus } from '../db/security-analysis'; +import { updateAnalysisStatus, tryAcquireAnalysisStartLease } from '../db/security-analysis'; import type { AnalysisMode, SecurityFindingAnalysis, @@ -40,6 +28,7 @@ import { sentryLogger } from '@/lib/utils.server'; import { APP_URL } from '@/lib/constants'; import { INTERNAL_API_SECRET } from '@/lib/config.server'; import type { SessionSnapshot } from '@/lib/session-ingest-client'; + import { DEFAULT_SECURITY_AGENT_ANALYSIS_MODEL, DEFAULT_SECURITY_AGENT_TRIAGE_MODEL, @@ -95,9 +84,6 @@ Provide a detailed markdown analysis covering: - **Summary**: Brief 1-2 sentence summary of findings `; -/** - * Build the analysis prompt for a finding - */ function buildAnalysisPrompt(finding: SecurityFinding): string { const replacements: Record = { packageName: finding.package_name, @@ -115,12 +101,7 @@ function buildAnalysisPrompt(finding: SecurityFinding): string { return ANALYSIS_PROMPT_TEMPLATE.replace(/\{\{(\w+)\}\}/g, (_, key) => replacements[key] ?? ''); } -/** - * Extract the last assistant message text from a session snapshot. - * - * Iterates messages in reverse order to find the last assistant message, - * then concatenates all text-type parts into a single string. - */ +/** Extract the last assistant message text from a session snapshot. */ export function extractLastAssistantMessage(snapshot: SessionSnapshot): string | null { for (let i = snapshot.messages.length - 1; i >= 0; i--) { const msg = snapshot.messages[i]; @@ -139,14 +120,8 @@ export function extractLastAssistantMessage(snapshot: SessionSnapshot): string | } /** - * Finalize sandbox analysis by extracting structured fields from raw markdown. - * - * Tier 3: Uses direct LLM call to extract structured fields from the raw analysis. - * Preserves existing triage data. - * - * After storing the analysis, attempts auto-dismiss if: - * - Auto-dismiss is enabled in config - * - sandboxAnalysis.isExploitable === false + * Tier 3: Extract structured fields from raw markdown, preserve triage data, + * and optionally auto-dismiss if sandboxAnalysis.isExploitable === false. */ export async function finalizeAnalysis( findingId: string, @@ -165,7 +140,6 @@ export async function finalizeAnalysis( return; } - // Get existing analysis to preserve triage data const finding = await getSecurityFindingById(findingId); if (!finding) { await updateAnalysisStatus(findingId, 'failed', { @@ -176,9 +150,6 @@ export async function finalizeAnalysis( const existingAnalysis = finding.analysis; - // ========================================================================= - // Tier 3: Extract structured fields from raw markdown - // ========================================================================= log('Starting Tier 3 extraction', { correlationId, findingId }); const sandboxAnalysis = await extractSandboxAnalysis({ @@ -231,7 +202,6 @@ export async function finalizeAnalysis( : 0, }); - // Attempt auto-dismiss after sandbox analysis if isExploitable === false if (sandboxAnalysis.isExploitable === false) { void maybeAutoDismissAnalysis({ findingId, analysis, owner, userId, correlationId }).catch( (error: unknown) => { @@ -245,13 +215,6 @@ export async function finalizeAnalysis( } } -/** - * Start analysis for a security finding using three-tier approach. - * - * Tier 1 (Quick Triage): Always runs first. Direct LLM call to analyze metadata. - * Tier 2 (Sandbox Analysis): Controlled by analysisMode — always in 'deep', never in 'shallow', triage-driven in 'auto'. - * Tier 3 (Structured Extraction): Extracts structured fields from raw markdown output. - */ export async function startSecurityAnalysis(params: { findingId: string; user: User; @@ -284,18 +247,22 @@ export async function startSecurityAnalysis(params: { const correlationId = randomUUID(); - // Get the finding const finding = await getSecurityFindingById(findingId); if (!finding) { return { started: false, error: `Finding not found: ${findingId}` }; } - // Check if already running - if (finding.analysis_status === 'running') { + const leaseAcquired = await tryAcquireAnalysisStartLease(findingId); + if (!leaseAcquired) { + if (finding.status !== 'open') { + return { + started: false, + error: `Finding status is '${finding.status}', analysis requires 'open' status`, + }; + } return { started: false, error: 'Analysis already in progress' }; } - // When retrying sandbox only, preserve existing triage data const existingTriage = retrySandboxOnly ? finding.analysis?.triage : undefined; if (retrySandboxOnly && !existingTriage) { log('retrySandboxOnly requested but no existing triage found, falling back to full analysis', { @@ -305,10 +272,7 @@ export async function startSecurityAnalysis(params: { } const skipTriage = retrySandboxOnly && !!existingTriage; - // Mark as pending, preserving existing analysis (with triage) when retrying sandbox only. - // Coerce null → undefined so updateAnalysisStatus treats it as "not provided" and - // does not overwrite the analysis column with null (its `status === 'pending'` branch - // only clears `analysis` when `updates.analysis === undefined`). + // Coerce null → undefined so updateAnalysisStatus preserves the existing analysis if (skipTriage) { await updateAnalysisStatus(findingId, 'pending', { analysis: finding.analysis ?? undefined }); } else { @@ -318,15 +282,11 @@ export async function startSecurityAnalysis(params: { const analysisStartTime = Date.now(); try { - // Generate auth token for LLM calls const authToken = generateApiToken(user); let triage: SecurityFindingTriage; if (skipTriage) { - // ========================================================================= - // Reuse existing triage (sandbox-only retry) - // ========================================================================= triage = existingTriage; log('Skipping Tier 1 triage, reusing existing triage for sandbox retry', { correlationId, @@ -346,9 +306,6 @@ export async function startSecurityAnalysis(params: { analysisMode, }); } else { - // ========================================================================= - // Tier 1: Quick Triage (always runs) - // ========================================================================= log('Starting Tier 1 triage', { correlationId, findingId, triageModel }); trackSecurityAgentAnalysisStarted({ @@ -397,7 +354,6 @@ export async function startSecurityAnalysis(params: { }); } - // Decide whether to run sandbox analysis based on analysis mode and per-request overrides const runSandbox = forceSandbox || skipTriage || @@ -405,9 +361,6 @@ export async function startSecurityAnalysis(params: { (analysisMode === 'auto' && triage.needsSandboxAnalysis); if (!runSandbox) { - // ========================================================================= - // Triage-only: Save result and potentially auto-dismiss - // ========================================================================= log('Triage-only completion', { correlationId, findingId }); const analysis: SecurityFindingAnalysis = { @@ -437,10 +390,8 @@ export async function startSecurityAnalysis(params: { durationMs: Date.now() - analysisStartTime, }); - // Attempt auto-dismiss if configured (off by default) const owner: SecurityReviewOwner = organizationId ? { organizationId } : { userId: user.id }; - // Run auto-dismiss in background (don't block response) void maybeAutoDismissAnalysis({ findingId, analysis, @@ -459,12 +410,8 @@ export async function startSecurityAnalysis(params: { return { started: true, triageOnly: true }; } - // ========================================================================= - // Tier 2: Sandbox Analysis (cloud-agent-next) - // ========================================================================= log('Starting Tier 2 sandbox analysis', { correlationId, findingId }); - // Store triage + context the callback handler will need to run Tier 3 const partialAnalysis: SecurityFindingAnalysis = { triage, analyzedAt: new Date().toISOString(), @@ -495,7 +442,6 @@ export async function startSecurityAnalysis(params: { }, }); - // Store session IDs immediately (before initiation) await updateAnalysisStatus(findingId, 'running', { sessionId: cloudAgentSessionId, cliSessionId: kiloSessionId, @@ -512,23 +458,19 @@ export async function startSecurityAnalysis(params: { try { await client.initiateFromPreparedSession({ cloudAgentSessionId }); } catch (initiateError) { - // Re-throw InsufficientCreditsError so it propagates to the caller if (initiateError instanceof InsufficientCreditsError) { warn('Sandbox initiation blocked by insufficient credits', { correlationId, findingId, cloudAgentSessionId, }); - // Clean up the prepared session void client.deleteSession(cloudAgentSessionId).catch(() => {}); throw initiateError; } - // Clean up the prepared session void client.deleteSession(cloudAgentSessionId).catch(() => {}); const classified = classifyAnalysisError(initiateError); - // Default to SANDBOX_FAILED for initiation errors unless a more specific code applies const isUnknown = classified.code === 'UNKNOWN'; const errorCode: AnalysisErrorCode = isUnknown ? 'SANDBOX_FAILED' : classified.code; const userMessage = isUnknown @@ -558,7 +500,6 @@ export async function startSecurityAnalysis(params: { return { started: true, triageOnly: false }; } catch (error) { - // Propagate InsufficientCreditsError so the caller can show a payment-required error if (error instanceof InsufficientCreditsError) { await updateAnalysisStatus(findingId, 'failed', { error: error.message }); throw error; diff --git a/src/lib/security-agent/services/sync-service.test.ts b/src/lib/security-agent/services/sync-service.test.ts new file mode 100644 index 000000000..44252b083 --- /dev/null +++ b/src/lib/security-agent/services/sync-service.test.ts @@ -0,0 +1,173 @@ +import { beforeAll, beforeEach, describe, expect, it, jest } from '@jest/globals'; +import type * as dependabotApiModule from '../github/dependabot-api'; +import type * as parserModule from '../parsers/dependabot-parser'; +import type * as findingsDbModule from '../db/security-findings'; +import type * as configDbModule from '../db/security-config'; +import type * as analysisDbModule from '../db/security-analysis'; +import type { syncDependabotAlertsForRepo as syncDependabotAlertsForRepoType } from './sync-service'; + +const mockFetchAllDependabotAlerts = jest.fn() as jest.MockedFunction< + typeof dependabotApiModule.fetchAllDependabotAlerts +>; +const mockParseDependabotAlerts = jest.fn() as jest.MockedFunction< + typeof parserModule.parseDependabotAlerts +>; +const mockUpsertSecurityFinding = jest.fn() as jest.MockedFunction< + typeof findingsDbModule.upsertSecurityFinding +>; +const mockGetSecurityAgentConfigWithStatus = jest.fn() as jest.MockedFunction< + typeof configDbModule.getSecurityAgentConfigWithStatus +>; +const mockGetSecurityAgentConfig = jest.fn() as jest.MockedFunction< + typeof configDbModule.getSecurityAgentConfig +>; +const mockGetOwnerAutoAnalysisEnabledAt = jest.fn() as jest.MockedFunction< + typeof analysisDbModule.getOwnerAutoAnalysisEnabledAt +>; +const mockSyncAutoAnalysisQueueForFinding = jest.fn() as jest.MockedFunction< + typeof analysisDbModule.syncAutoAnalysisQueueForFinding +>; +const mockSyncLogger = jest.fn(); + +jest.mock('../github/dependabot-api', () => ({ + fetchAllDependabotAlerts: mockFetchAllDependabotAlerts, +})); + +jest.mock('../parsers/dependabot-parser', () => ({ + parseDependabotAlerts: mockParseDependabotAlerts, +})); + +jest.mock('../db/security-findings', () => ({ + upsertSecurityFinding: mockUpsertSecurityFinding, +})); + +jest.mock('../db/security-config', () => ({ + getSecurityAgentConfigWithStatus: mockGetSecurityAgentConfigWithStatus, + getSecurityAgentConfig: mockGetSecurityAgentConfig, +})); + +jest.mock('../db/security-analysis', () => ({ + getOwnerAutoAnalysisEnabledAt: mockGetOwnerAutoAnalysisEnabledAt, + syncAutoAnalysisQueueForFinding: mockSyncAutoAnalysisQueueForFinding, +})); + +jest.mock('@/lib/drizzle', () => ({ db: {} })); +jest.mock('@kilocode/db/schema', () => ({ platform_integrations: {}, agent_configs: {} })); +jest.mock('../github/permissions', () => ({ hasSecurityReviewPermissions: () => true })); +jest.mock('@/lib/utils.server', () => ({ sentryLogger: () => mockSyncLogger })); +jest.mock('./audit-log-service', () => ({ + logSecurityAudit: jest.fn(), + SecurityAuditLogAction: { SyncCompleted: 'sync_completed' }, +})); +jest.mock('../posthog-tracking', () => ({ trackSecurityAgentFullSync: jest.fn() })); + +let syncDependabotAlertsForRepo: typeof syncDependabotAlertsForRepoType; + +beforeAll(async () => { + ({ syncDependabotAlertsForRepo } = await import('./sync-service')); +}); + +describe('sync-service queue enqueue wiring', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockFetchAllDependabotAlerts.mockResolvedValue({ status: 'success', alerts: [] }); + mockParseDependabotAlerts.mockReturnValue([ + { + source: 'dependabot', + source_id: '101', + severity: 'high', + ghsa_id: 'GHSA-1', + cve_id: null, + package_name: 'lodash', + package_ecosystem: 'npm', + vulnerable_version_range: '<4.17.21', + patched_version: '4.17.21', + manifest_path: 'package.json', + title: 'test finding', + description: 'desc', + status: 'open', + ignored_reason: null, + ignored_by: null, + fixed_at: null, + dependabot_html_url: null, + first_detected_at: '2026-01-01T00:00:00.000Z', + raw_data: {} as never, + cwe_ids: null, + cvss_score: null, + dependency_scope: 'runtime', + }, + ]); + const config: Awaited> = { + sla_critical_days: 15, + sla_high_days: 30, + sla_medium_days: 45, + sla_low_days: 90, + auto_sync_enabled: true, + repository_selection_mode: 'all', + model_slug: 'anthropic/claude-opus-4.6', + analysis_mode: 'auto', + auto_dismiss_enabled: false, + auto_dismiss_confidence_threshold: 'high', + auto_analysis_enabled: true, + auto_analysis_min_severity: 'high', + }; + const configWithStatus: Awaited> = { + isEnabled: true, + config, + storedConfig: config, + }; + mockGetSecurityAgentConfigWithStatus.mockResolvedValue(configWithStatus); + mockGetSecurityAgentConfig.mockResolvedValue(config); + mockGetOwnerAutoAnalysisEnabledAt.mockResolvedValue('2026-01-01T00:00:00.000Z'); + mockUpsertSecurityFinding.mockResolvedValue({ + findingId: 'finding-1', + wasInserted: true, + previousStatus: null, + findingCreatedAt: '2026-01-01T00:00:00.000Z', + }); + mockSyncAutoAnalysisQueueForFinding.mockResolvedValue({ + enqueueCount: 1, + eligibleCount: 1, + boundarySkipCount: 0, + unknownSeverityCount: 0, + }); + }); + + it('passes upsert metadata into auto-analysis queue sync', async () => { + await syncDependabotAlertsForRepo({ + owner: { userId: 'user-1' }, + platformIntegrationId: 'integration-1', + installationId: 'inst-1', + repoFullName: 'acme/repo', + }); + + expect(mockSyncAutoAnalysisQueueForFinding).toHaveBeenCalledWith( + expect.objectContaining({ + findingId: 'finding-1', + previousStatus: null, + findingCreatedAt: '2026-01-01T00:00:00.000Z', + autoAnalysisEnabled: true, + isAgentEnabled: true, + }) + ); + }); + + it('logs queue enqueue observability fields for each sync', async () => { + await syncDependabotAlertsForRepo({ + owner: { userId: 'user-1' }, + platformIntegrationId: 'integration-1', + installationId: 'inst-1', + repoFullName: 'acme/repo', + }); + + expect(mockSyncLogger).toHaveBeenCalledWith( + 'Repo sync complete', + expect.objectContaining({ + enqueue_count_per_sync: 1, + eligible_count_per_sync: 1, + boundary_skip_count: 0, + unknown_severity_count: 0, + }) + ); + }); +}); diff --git a/src/lib/security-agent/services/sync-service.ts b/src/lib/security-agent/services/sync-service.ts index c4abc9242..938ca9fed 100644 --- a/src/lib/security-agent/services/sync-service.ts +++ b/src/lib/security-agent/services/sync-service.ts @@ -1,9 +1,3 @@ -/** - * Security Reviews - Sync Service - * - * Orchestrates syncing Dependabot alerts from GitHub to our database. - */ - import { captureException } from '@sentry/nextjs'; import { trackSecurityAgentFullSync } from '../posthog-tracking'; import { db } from '@/lib/drizzle'; @@ -14,6 +8,11 @@ import { hasSecurityReviewPermissions } from '../github/permissions'; import { parseDependabotAlerts } from '../parsers/dependabot-parser'; import { upsertSecurityFinding } from '../db/security-findings'; import { getSecurityAgentConfig, getSecurityAgentConfigWithStatus } from '../db/security-config'; +import { + getOwnerAutoAnalysisEnabledAt, + syncAutoAnalysisQueueForFinding, + type AutoAnalysisQueueSyncResult, +} from '../db/security-analysis'; import { upsertAgentConfigForOwner } from '@/lib/agent-config/db/agent-configs'; import { getSlaForSeverity, @@ -29,10 +28,6 @@ const log = sentryLogger('security-agent:sync', 'info'); const warn = sentryLogger('security-agent:sync', 'warning'); const logError = sentryLogger('security-agent:sync', 'error'); -/** - * Convert SecurityReviewOwner to Owner type used by agent_configs - * The userId field is used for audit purposes; for system operations we use 'system' - */ function toAgentConfigOwner(owner: SecurityReviewOwner): Owner { if (owner.organizationId) { return { type: 'org', id: owner.organizationId, userId: 'system' }; @@ -43,9 +38,6 @@ function toAgentConfigOwner(owner: SecurityReviewOwner): Owner { throw new Error('Invalid owner: must have either organizationId or userId'); } -/** - * Sync Dependabot alerts for a single repository - */ export async function syncDependabotAlertsForRepo(params: { owner: SecurityReviewOwner; platformIntegrationId: string; @@ -64,15 +56,19 @@ export async function syncDependabotAlertsForRepo(params: { errors: 0, staleRepos: [], }; + const queueSyncTotals: AutoAnalysisQueueSyncResult = { + enqueueCount: 0, + eligibleCount: 0, + boundarySkipCount: 0, + unknownSeverityCount: 0, + }; try { - // Parse repo owner and name const [repoOwner, repoName] = repoFullName.split('/'); if (!repoOwner || !repoName) { throw new Error(`Invalid repo full name: ${repoFullName}`); } - // Fetch all alerts from Dependabot const fetchResult = await fetchAllDependabotAlerts(installationId, repoOwner, repoName); if (fetchResult.status === 'repo_not_found') { @@ -89,20 +85,21 @@ export async function syncDependabotAlertsForRepo(params: { const alerts = fetchResult.alerts; log(`Fetched ${alerts.length} alerts from GitHub for ${repoFullName}`); - // Parse alerts to our internal format const findings = parseDependabotAlerts(alerts, repoFullName); log(`Parsed ${findings.length} findings for ${repoFullName}`); - // Get SLA config for this owner - const config = await getSecurityAgentConfig(toAgentConfigOwner(owner)); + const configOwner = toAgentConfigOwner(owner); + const configWithStatus = await getSecurityAgentConfigWithStatus(configOwner); + const config = configWithStatus?.config ?? (await getSecurityAgentConfig(configOwner)); + const isAgentEnabled = configWithStatus?.isEnabled ?? false; + const ownerAutoAnalysisEnabledAt = await getOwnerAutoAnalysisEnabledAt(owner); - // Upsert each finding for (const finding of findings) { try { const slaDays = getSlaForSeverity(config, finding.severity); const slaDueAt = calculateSlaDueAt(finding.first_detected_at, slaDays); - await upsertSecurityFinding({ + const upsertResult = await upsertSecurityFinding({ ...finding, owner, platformIntegrationId, @@ -111,6 +108,39 @@ export async function syncDependabotAlertsForRepo(params: { }); result.synced++; + + try { + const queueSyncResult = await syncAutoAnalysisQueueForFinding({ + owner, + findingId: upsertResult.findingId, + findingCreatedAt: upsertResult.findingCreatedAt, + previousStatus: upsertResult.previousStatus, + currentStatus: finding.status, + severity: finding.severity, + isAgentEnabled, + autoAnalysisEnabled: config.auto_analysis_enabled, + autoAnalysisMinSeverity: config.auto_analysis_min_severity, + ownerAutoAnalysisEnabledAt, + }); + queueSyncTotals.enqueueCount += queueSyncResult.enqueueCount; + queueSyncTotals.eligibleCount += queueSyncResult.eligibleCount; + queueSyncTotals.boundarySkipCount += queueSyncResult.boundarySkipCount; + queueSyncTotals.unknownSeverityCount += queueSyncResult.unknownSeverityCount; + } catch (error) { + logError(`Error syncing auto-analysis queue for ${repoFullName}`, { + error, + alertNumber: finding.source_id, + findingId: upsertResult.findingId, + }); + captureException(error, { + tags: { operation: 'syncDependabotAlertsForRepo', step: 'syncAutoAnalysisQueue' }, + extra: { + repoFullName, + alertNumber: finding.source_id, + findingId: upsertResult.findingId, + }, + }); + } } catch (error) { result.errors++; logError(`Error upserting finding for ${repoFullName}`, { @@ -130,6 +160,10 @@ export async function syncDependabotAlertsForRepo(params: { durationMs: repoDurationMs, alertsSynced: result.synced, errors: result.errors, + enqueue_count_per_sync: queueSyncTotals.enqueueCount, + eligible_count_per_sync: queueSyncTotals.eligibleCount, + boundary_skip_count: queueSyncTotals.boundarySkipCount, + unknown_severity_count: queueSyncTotals.unknownSeverityCount, }); return result; @@ -145,9 +179,8 @@ export async function syncDependabotAlertsForRepo(params: { } /** - * Sync Dependabot alerts for all repositories of an owner. - * If all repositories fail to sync, throws the first error encountered. - * Stale repos (404 from GitHub) are collected and returned for pruning. + * Sync all repos for an owner. Throws the first error if every repo fails. + * Stale repos (GitHub 404) are returned for pruning. */ export async function syncAllReposForOwner(params: { owner: SecurityReviewOwner; @@ -165,7 +198,6 @@ export async function syncAllReposForOwner(params: { staleRepos: [], }; - // Track the first error encountered to throw if all repos fail let firstError: Error | null = null; let successfulRepos = 0; @@ -193,8 +225,6 @@ export async function syncAllReposForOwner(params: { } } - // If all repositories failed to sync, throw the first error - // This ensures the frontend gets an error response instead of success if (successfulRepos === 0 && firstError) { throw firstError; } @@ -211,11 +241,7 @@ type EnabledSecurityReviewConfig = { repoNameToId: Map; }; -/** - * Get all enabled security review configurations with their integrations - */ export async function getEnabledSecurityReviewConfigs(): Promise { - // Get all enabled security_review configs const configs = await db .select() .from(agent_configs) @@ -224,7 +250,6 @@ export async function getEnabledSecurityReviewConfigs(): Promise typeof r.id === 'number' && typeof r.full_name === 'string' && r.full_name.length > 0 @@ -272,27 +294,22 @@ export async function getEnabledSecurityReviewConfigs(): Promise [r.full_name, r.id])); - // Parse the security agent config to get repository selection settings const securityConfig = config.config as { repository_selection_mode?: 'all' | 'selected'; selected_repository_ids?: number[]; }; - // Filter repositories based on selection mode let selectedRepos: string[]; if ( securityConfig.repository_selection_mode === 'selected' && securityConfig.selected_repository_ids && securityConfig.selected_repository_ids.length > 0 ) { - // Only sync selected repositories const selectedIds = new Set(securityConfig.selected_repository_ids); selectedRepos = allRepositories.filter(r => selectedIds.has(r.id)).map(r => r.full_name); } else { - // Sync all repositories selectedRepos = allRepositories.map(r => r.full_name); } @@ -320,10 +337,7 @@ export async function getEnabledSecurityReviewConfigs(): Promise 0) { try { await pruneStaleReposFromConfig(config.owner, result.staleRepos, config.repoNameToId); diff --git a/src/lib/user.test.ts b/src/lib/user.test.ts index 5f2f58002..b7905c352 100644 --- a/src/lib/user.test.ts +++ b/src/lib/user.test.ts @@ -26,6 +26,9 @@ import { stytch_fingerprints, kiloclaw_version_pins, kiloclaw_image_catalog, + security_findings, + security_analysis_queue, + security_analysis_owner_state, } from '@kilocode/db/schema'; import { eq, count } from 'drizzle-orm'; import { softDeleteUser, SoftDeletePreconditionError, findUserById, findUsersByIds } from './user'; @@ -55,6 +58,9 @@ describe('User', () => { await db.delete(referral_codes); await db.delete(organization_audit_logs); await db.delete(security_audit_log); + await db.delete(security_analysis_queue); + await db.delete(security_findings); + await db.delete(security_analysis_owner_state); await db.delete(organization_invitations); await db.delete(organization_user_usage); await db.delete(organization_user_limits); @@ -393,6 +399,121 @@ describe('User', () => { expect(logs[0].action).toBe(SecurityAuditLogAction.FindingDismissed); // action preserved }); + it('should delete security_analysis_owner_state rows for the user', async () => { + const user1 = await insertTestUser(); + const user2 = await insertTestUser(); + + await db.insert(security_analysis_owner_state).values([ + { + owned_by_user_id: user1.id, + auto_analysis_enabled_at: new Date().toISOString(), + }, + { + owned_by_user_id: user2.id, + auto_analysis_enabled_at: new Date().toISOString(), + }, + ]); + + await softDeleteUser(user1.id); + + expect( + await db + .select({ count: count() }) + .from(security_analysis_owner_state) + .where(eq(security_analysis_owner_state.owned_by_user_id, user1.id)) + .then(r => r[0].count) + ).toBe(0); + expect( + await db + .select({ count: count() }) + .from(security_analysis_owner_state) + .where(eq(security_analysis_owner_state.owned_by_user_id, user2.id)) + .then(r => r[0].count) + ).toBe(1); + }); + + it('should remove security_analysis_queue rows via security_findings cascade', async () => { + const user1 = await insertTestUser(); + const user2 = await insertTestUser(); + + const [finding1] = await db + .insert(security_findings) + .values({ + owned_by_user_id: user1.id, + repo_full_name: 'kilo-org/cloud-user-1', + source: 'dependabot', + source_id: `source-${randomUUID()}`, + severity: 'high', + package_name: 'zod', + package_ecosystem: 'npm', + title: 'User1 finding', + }) + .returning(); + + const [finding2] = await db + .insert(security_findings) + .values({ + owned_by_user_id: user2.id, + repo_full_name: 'kilo-org/cloud-user-2', + source: 'dependabot', + source_id: `source-${randomUUID()}`, + severity: 'medium', + package_name: 'drizzle-orm', + package_ecosystem: 'npm', + title: 'User2 finding', + }) + .returning(); + + await db.insert(security_analysis_queue).values([ + { + finding_id: finding1.id, + owned_by_user_id: user1.id, + queue_status: 'queued', + severity_rank: 1, + queued_at: new Date().toISOString(), + }, + { + finding_id: finding2.id, + owned_by_user_id: user2.id, + queue_status: 'queued', + severity_rank: 2, + queued_at: new Date().toISOString(), + }, + ]); + + await softDeleteUser(user1.id); + + expect( + await db + .select({ count: count() }) + .from(security_findings) + .where(eq(security_findings.owned_by_user_id, user1.id)) + .then(r => r[0].count) + ).toBe(0); + expect( + await db + .select({ count: count() }) + .from(security_analysis_queue) + .where(eq(security_analysis_queue.finding_id, finding1.id)) + .then(r => r[0].count) + ).toBe(0); + + expect( + await db + .select({ count: count() }) + .from(security_findings) + .where(eq(security_findings.owned_by_user_id, user2.id)) + .then(r => r[0].count) + ).toBe(1); + expect( + await db + .select({ count: count() }) + .from(security_analysis_queue) + .where(eq(security_analysis_queue.finding_id, finding2.id)) + .then(r => r[0].count) + ).toBe(1); + }); + it('should soft-delete and anonymize payment methods', async () => { const user = await insertTestUser(); const pm = createTestPaymentMethod(user.id); diff --git a/src/lib/user.ts b/src/lib/user.ts index 1b8e41b35..7a476b7ec 100644 --- a/src/lib/user.ts +++ b/src/lib/user.ts @@ -47,6 +47,7 @@ import { cloud_agent_feedback, free_model_usage, kilo_pass_scheduled_changes, + security_analysis_owner_state, } from '@kilocode/db/schema'; import { eq, and, inArray, sql } from 'drizzle-orm'; import { allow_fake_login } from './constants'; @@ -406,7 +407,9 @@ export class SoftDeletePreconditionError extends Error { * - Various user-owned resources (platform_integrations, byok_api_keys, * agent_configs, webhook_events, code_indexing_*, source_embeddings, * cloud_agent_webhook_triggers, agent_environment_profiles, - * security_findings, auto_triage/fix_tickets, slack_bot_requests, + * security_findings, security_analysis_owner_state, + * security_analysis_queue (via cascade when security_findings are deleted), + * auto_triage/fix_tickets, slack_bot_requests, * cloud_agent_code_reviews, device_auth_requests, auto_top_up_configs, * kiloclaw_instances/access_codes, user_period_cache, * kilo_pass_scheduled_changes) @@ -502,6 +505,9 @@ export async function softDeleteUser(userId: string) { await tx.delete(byok_api_keys).where(eq(byok_api_keys.kilo_user_id, userId)); await tx.delete(agent_configs).where(eq(agent_configs.owned_by_user_id, userId)); await tx.delete(webhook_events).where(eq(webhook_events.owned_by_user_id, userId)); + await tx + .delete(security_analysis_owner_state) + .where(eq(security_analysis_owner_state.owned_by_user_id, userId)); await tx.delete(security_findings).where(eq(security_findings.owned_by_user_id, userId)); await tx.delete(auto_fix_tickets).where(eq(auto_fix_tickets.owned_by_user_id, userId)); await tx.delete(auto_triage_tickets).where(eq(auto_triage_tickets.owned_by_user_id, userId)); diff --git a/src/routers/organizations/organization-security-agent-router.ts b/src/routers/organizations/organization-security-agent-router.ts index d54fc90a6..5af3c8330 100644 --- a/src/routers/organizations/organization-security-agent-router.ts +++ b/src/routers/organizations/organization-security-agent-router.ts @@ -62,6 +62,7 @@ import { import { DEFAULT_SECURITY_AGENT_TRIAGE_MODEL, DEFAULT_SECURITY_AGENT_ANALYSIS_MODEL, + SECURITY_ANALYSIS_OWNER_CAP, } from '@/lib/security-agent/core/constants'; import { trackSecurityAgentEnabled, @@ -141,11 +142,11 @@ export const organizationSecurityAgentRouter = createTRPCRouter({ modelSlug: DEFAULT_SECURITY_AGENT_ANALYSIS_MODEL, triageModelSlug: DEFAULT_SECURITY_AGENT_TRIAGE_MODEL, analysisModelSlug: DEFAULT_SECURITY_AGENT_ANALYSIS_MODEL, - // Analysis mode default analysisMode: 'auto' as const, - // Auto-dismiss defaults (off by default) autoDismissEnabled: false, autoDismissConfidenceThreshold: 'high' as const, + autoAnalysisEnabled: false, + autoAnalysisMinSeverity: 'high' as const, }; } @@ -172,11 +173,11 @@ export const organizationSecurityAgentRouter = createTRPCRouter({ modelSlug: result.config.model_slug || analysisModelSlug, triageModelSlug, analysisModelSlug, - // Analysis mode configuration analysisMode: result.config.analysis_mode ?? 'auto', - // Auto-dismiss configuration autoDismissEnabled: result.config.auto_dismiss_enabled ?? false, autoDismissConfidenceThreshold: result.config.auto_dismiss_confidence_threshold ?? 'high', + autoAnalysisEnabled: result.config.auto_analysis_enabled ?? false, + autoAnalysisMinSeverity: result.config.auto_analysis_min_severity ?? 'high', }; }), @@ -205,6 +206,8 @@ export const organizationSecurityAgentRouter = createTRPCRouter({ analysisMode: existingConfig.config.analysis_mode, autoDismissEnabled: existingConfig.config.auto_dismiss_enabled, autoDismissConfidenceThreshold: existingConfig.config.auto_dismiss_confidence_threshold, + autoAnalysisEnabled: existingConfig.config.auto_analysis_enabled, + autoAnalysisMinSeverity: existingConfig.config.auto_analysis_min_severity, modelSlug: existingConfig.config.model_slug, triageModelSlug: existingTriageModelSlug, analysisModelSlug: existingAnalysisModelSlug, @@ -244,11 +247,11 @@ export const organizationSecurityAgentRouter = createTRPCRouter({ model_slug: modelSlug, triage_model_slug: triageModelSlug, analysis_model_slug: analysisModelSlug, - // Analysis mode configuration analysis_mode: input.analysisMode, - // Auto-dismiss configuration auto_dismiss_enabled: input.autoDismissEnabled, auto_dismiss_confidence_threshold: input.autoDismissConfidenceThreshold, + auto_analysis_enabled: input.autoAnalysisEnabled, + auto_analysis_min_severity: input.autoAnalysisMinSeverity, }, ctx.user.id ); @@ -282,6 +285,8 @@ export const organizationSecurityAgentRouter = createTRPCRouter({ analysisMode: input.analysisMode, autoDismissEnabled: input.autoDismissEnabled, autoDismissConfidenceThreshold: input.autoDismissConfidenceThreshold, + autoAnalysisEnabled: input.autoAnalysisEnabled, + autoAnalysisMinSeverity: input.autoAnalysisMinSeverity, modelSlug, triageModelSlug, analysisModelSlug, @@ -841,7 +846,7 @@ export const organizationSecurityAgentRouter = createTRPCRouter({ // Check concurrency limit const securityOwner: SecurityReviewOwner = { organizationId: input.organizationId }; - const concurrencyCheck = await canStartAnalysis(securityOwner); + const concurrencyCheck = await canStartAnalysis(securityOwner, SECURITY_ANALYSIS_OWNER_CAP); if (!concurrencyCheck.allowed) { throw new TRPCError({ @@ -974,7 +979,7 @@ export const organizationSecurityAgentRouter = createTRPCRouter({ const total = await countSecurityFindingsWithAnalysis(securityOwner); // Get concurrency info - const concurrencyCheck = await canStartAnalysis(securityOwner); + const concurrencyCheck = await canStartAnalysis(securityOwner, SECURITY_ANALYSIS_OWNER_CAP); return { jobs, diff --git a/src/routers/security-agent-router.ts b/src/routers/security-agent-router.ts index 3fdac7d21..086f8a312 100644 --- a/src/routers/security-agent-router.ts +++ b/src/routers/security-agent-router.ts @@ -57,6 +57,7 @@ import { import { DEFAULT_SECURITY_AGENT_TRIAGE_MODEL, DEFAULT_SECURITY_AGENT_ANALYSIS_MODEL, + SECURITY_ANALYSIS_OWNER_CAP, } from '@/lib/security-agent/core/constants'; import { trackSecurityAgentEnabled, @@ -122,11 +123,11 @@ export const securityAgentRouter = createTRPCRouter({ modelSlug: DEFAULT_SECURITY_AGENT_ANALYSIS_MODEL, triageModelSlug: DEFAULT_SECURITY_AGENT_TRIAGE_MODEL, analysisModelSlug: DEFAULT_SECURITY_AGENT_ANALYSIS_MODEL, - // Analysis mode default analysisMode: 'auto' as const, - // Auto-dismiss defaults (off by default) autoDismissEnabled: false, autoDismissConfidenceThreshold: 'high' as const, + autoAnalysisEnabled: false, + autoAnalysisMinSeverity: 'high' as const, }; } @@ -153,11 +154,11 @@ export const securityAgentRouter = createTRPCRouter({ modelSlug: result.config.model_slug || analysisModelSlug, triageModelSlug, analysisModelSlug, - // Analysis mode configuration analysisMode: result.config.analysis_mode ?? 'auto', - // Auto-dismiss configuration autoDismissEnabled: result.config.auto_dismiss_enabled ?? false, autoDismissConfidenceThreshold: result.config.auto_dismiss_confidence_threshold ?? 'high', + autoAnalysisEnabled: result.config.auto_analysis_enabled ?? false, + autoAnalysisMinSeverity: result.config.auto_analysis_min_severity ?? 'high', }; }), @@ -186,6 +187,8 @@ export const securityAgentRouter = createTRPCRouter({ analysisMode: existingConfig.config.analysis_mode, autoDismissEnabled: existingConfig.config.auto_dismiss_enabled, autoDismissConfidenceThreshold: existingConfig.config.auto_dismiss_confidence_threshold, + autoAnalysisEnabled: existingConfig.config.auto_analysis_enabled, + autoAnalysisMinSeverity: existingConfig.config.auto_analysis_min_severity, modelSlug: existingConfig.config.model_slug, triageModelSlug: existingTriageModelSlug, analysisModelSlug: existingAnalysisModelSlug, @@ -225,11 +228,11 @@ export const securityAgentRouter = createTRPCRouter({ model_slug: modelSlug, triage_model_slug: triageModelSlug, analysis_model_slug: analysisModelSlug, - // Analysis mode configuration analysis_mode: input.analysisMode, - // Auto-dismiss configuration auto_dismiss_enabled: input.autoDismissEnabled, auto_dismiss_confidence_threshold: input.autoDismissConfidenceThreshold, + auto_analysis_enabled: input.autoAnalysisEnabled, + auto_analysis_min_severity: input.autoAnalysisMinSeverity, }, ctx.user.id ); @@ -262,6 +265,8 @@ export const securityAgentRouter = createTRPCRouter({ analysisMode: input.analysisMode, autoDismissEnabled: input.autoDismissEnabled, autoDismissConfidenceThreshold: input.autoDismissConfidenceThreshold, + autoAnalysisEnabled: input.autoAnalysisEnabled, + autoAnalysisMinSeverity: input.autoAnalysisMinSeverity, modelSlug, triageModelSlug, analysisModelSlug, @@ -815,7 +820,7 @@ export const securityAgentRouter = createTRPCRouter({ // Note: Triage may trigger sandbox analysis, so we always check concurrency // to prevent overload when triage.needsSandboxAnalysis returns true const securityOwner: SecurityReviewOwner = { userId: ctx.user.id }; - const concurrencyCheck = await canStartAnalysis(securityOwner); + const concurrencyCheck = await canStartAnalysis(securityOwner, SECURITY_ANALYSIS_OWNER_CAP); if (!concurrencyCheck.allowed) { throw new TRPCError({ @@ -946,7 +951,7 @@ export const securityAgentRouter = createTRPCRouter({ const total = await countSecurityFindingsWithAnalysis(securityOwner); // Get concurrency info - const concurrencyCheck = await canStartAnalysis(securityOwner); + const concurrencyCheck = await canStartAnalysis(securityOwner, SECURITY_ANALYSIS_OWNER_CAP); return { jobs, From 073bbaee51f4a9def9e2f2da59634068c05cf665 Mon Sep 17 00:00:00 2001 From: Jean du Plessis Date: Tue, 3 Mar 2026 17:15:16 +0200 Subject: [PATCH 3/6] Remove redundant setFindingFailed call on InsufficientCreditsError path --- cloudflare-security-auto-analysis/src/launch.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudflare-security-auto-analysis/src/launch.ts b/cloudflare-security-auto-analysis/src/launch.ts index b685d7292..6d6d72475 100644 --- a/cloudflare-security-auto-analysis/src/launch.ts +++ b/cloudflare-security-auto-analysis/src/launch.ts @@ -256,7 +256,7 @@ export async function startSecurityAnalysis( return { started: true, triageOnly: false }; } catch (error) { if (error instanceof InsufficientCreditsError) { - await setFindingFailed(params.db, params.findingId, error.message); + // setFindingFailed already called at the throw site (line 231) throw error; } From 69d180b878e52f03c60e5b1ac8be245165214659 Mon Sep 17 00:00:00 2001 From: Jean du Plessis Date: Tue, 3 Mar 2026 17:57:05 +0200 Subject: [PATCH 4/6] Add updated_at to finding state-transition helpers; remove dead sanitizeAnalysisMode --- .../src/db/queries.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/cloudflare-security-auto-analysis/src/db/queries.ts b/cloudflare-security-auto-analysis/src/db/queries.ts index cd6b26304..69e152e58 100644 --- a/cloudflare-security-auto-analysis/src/db/queries.ts +++ b/cloudflare-security-auto-analysis/src/db/queries.ts @@ -8,12 +8,7 @@ import { organization_memberships, } from '@kilocode/db/schema'; import { and, asc, count, eq, inArray, isNull, lte, or, sql } from 'drizzle-orm'; -import type { - AnalysisMode, - QueueOwner, - SecurityAgentConfig, - SecurityFindingAnalysis, -} from '../types.js'; +import type { QueueOwner, SecurityAgentConfig, SecurityFindingAnalysis } from '../types.js'; import { AUTO_ANALYSIS_OWNER_CAP, DEFAULT_SECURITY_AGENT_CONFIG, @@ -507,6 +502,7 @@ export async function setFindingPending( analysis_completed_at: null, session_id: null, cli_session_id: null, + updated_at: sql`now()`.mapWith(String), }) .where(eq(security_findings.id, findingId)); } @@ -526,6 +522,7 @@ export async function setFindingRunning( analysis_started_at: sql`coalesce(${security_findings.analysis_started_at}, now())`.mapWith( String ), + updated_at: sql`now()`.mapWith(String), }) .where(eq(security_findings.id, findingId)); } @@ -542,6 +539,7 @@ export async function setFindingCompleted( analysis: sql`${JSON.stringify(analysis)}::jsonb`, analysis_error: null, analysis_completed_at: sql`now()`.mapWith(String), + updated_at: sql`now()`.mapWith(String), }) .where(eq(security_findings.id, findingId)); } @@ -557,10 +555,7 @@ export async function setFindingFailed( analysis_status: 'failed', analysis_error: errorMessage, analysis_completed_at: sql`now()`.mapWith(String), + updated_at: sql`now()`.mapWith(String), }) .where(eq(security_findings.id, findingId)); } - -export function sanitizeAnalysisMode(mode: AnalysisMode | undefined): AnalysisMode { - return mode ?? DEFAULT_SECURITY_AGENT_CONFIG.analysis_mode; -} From a9251b7c3939a268e501dded20633e423fd29201 Mon Sep 17 00:00:00 2001 From: Jean du Plessis Date: Tue, 3 Mar 2026 18:16:52 +0200 Subject: [PATCH 5/6] Fix queue-stuck scenarios in auto-analysis callback pipeline - Wrap finalizeAnalysis in try/catch so queue transitions to 'failed' on throw - Widen transitionAutoAnalysisQueueFromCallback to match 'pending' OR 'running' - Document fallback CTE previousStatus limitation during concurrent insert races --- .../[findingId]/route.ts | 34 +++++++++++++------ .../security-agent/db/security-analysis.ts | 5 ++- .../security-agent/db/security-findings.ts | 4 +++ 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/app/api/internal/security-analysis-callback/[findingId]/route.ts b/src/app/api/internal/security-analysis-callback/[findingId]/route.ts index 41c02caac..a819d6714 100644 --- a/src/app/api/internal/security-analysis-callback/[findingId]/route.ts +++ b/src/app/api/internal/security-analysis-callback/[findingId]/route.ts @@ -377,16 +377,30 @@ async function handleAnalysisCompleted( }, }); - await finalizeAnalysis( - findingId, - rawMarkdown, - analysisModel, - owner, - triggeredByUserId, - authToken, - correlationId, - organizationId - ); + try { + await finalizeAnalysis( + findingId, + rawMarkdown, + analysisModel, + owner, + triggeredByUserId, + authToken, + correlationId, + organizationId + ); + } catch (error) { + captureException(error, { + tags: { source: 'security-analysis-callback-api', operation: 'finalizeAnalysis' }, + extra: { findingId, correlationId }, + }); + await transitionAutoAnalysisQueueFromCallback({ + findingId, + toStatus: 'failed', + failureCode: 'START_CALL_AMBIGUOUS', + errorMessage: error instanceof Error ? error.message : String(error), + }); + return; + } const updatedFinding = await getSecurityFindingById(findingId); if (updatedFinding?.analysis_status === 'completed') { diff --git a/src/lib/security-agent/db/security-analysis.ts b/src/lib/security-agent/db/security-analysis.ts index 565382274..f1a286da0 100644 --- a/src/lib/security-agent/db/security-analysis.ts +++ b/src/lib/security-agent/db/security-analysis.ts @@ -583,7 +583,10 @@ export async function transitionAutoAnalysisQueueFromCallback(params: { .where( and( eq(security_analysis_queue.finding_id, params.findingId), - eq(security_analysis_queue.queue_status, 'running') + or( + eq(security_analysis_queue.queue_status, 'running'), + eq(security_analysis_queue.queue_status, 'pending') + ) ) ); } diff --git a/src/lib/security-agent/db/security-findings.ts b/src/lib/security-agent/db/security-findings.ts index 85ff6463f..21de6e895 100644 --- a/src/lib/security-agent/db/security-findings.ts +++ b/src/lib/security-agent/db/security-findings.ts @@ -202,6 +202,10 @@ export async function upsertSecurityFinding( NULL::text AS previous_status, ${security_findings.created_at} AS created_at ), + -- fallback: concurrent insert race — previous_status reflects the current row state + -- (written by the concurrent winner), not the true pre-update value. This means + -- syncAutoAnalysisQueueForFinding may misidentify a status transition during races. + -- Acceptable because the concurrent winner's sync call will have the correct value. fallback AS ( SELECT ${security_findings.id} AS id, From aef4e3d3ced0700947b079175f1fc815ba352a72 Mon Sep 17 00:00:00 2001 From: Jean du Plessis Date: Tue, 3 Mar 2026 22:05:56 +0200 Subject: [PATCH 6/6] Add auto-analysis config UI with unsaved changes guard - Add auto-analysis toggle and min severity selector to security agent config - Convert analysis mode, auto-analysis severity, auto-dismiss confidence, SLA, and AI model sections to responsive card-tile grid layouts - Add confirmation dialog when navigating away from config with unsaved changes --- .../SecurityAgentPageClient.tsx | 65 +++- .../security-agent/SecurityConfigForm.tsx | 350 ++++++++++++++---- 2 files changed, 340 insertions(+), 75 deletions(-) diff --git a/src/components/security-agent/SecurityAgentPageClient.tsx b/src/components/security-agent/SecurityAgentPageClient.tsx index 22de71ea2..0d86aed43 100644 --- a/src/components/security-agent/SecurityAgentPageClient.tsx +++ b/src/components/security-agent/SecurityAgentPageClient.tsx @@ -1,10 +1,18 @@ 'use client'; -import { useState, useCallback } from 'react'; +import { useState, useCallback, useRef } from 'react'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog'; import { SecurityFindingsCard } from './SecurityFindingsCard'; import { FindingDetailDialog } from './FindingDetailDialog'; import { DismissFindingDialog, type DismissReason } from './DismissFindingDialog'; @@ -62,6 +70,8 @@ export function SecurityAgentPageClient({ organizationId }: SecurityAgentPageCli const [dismissDialogOpen, setDismissDialogOpen] = useState(false); const [startingAnalysisId, setStartingAnalysisId] = useState(null); const [gitHubError, setGitHubError] = useState(null); + const configHasChanges = useRef(false); + const [pendingTabChange, setPendingTabChange] = useState(null); // Determine which router to use based on organizationId const isOrg = !!organizationId; @@ -461,6 +471,8 @@ export function SecurityAgentPageClient({ organizationId }: SecurityAgentPageCli analysisMode: 'auto' | 'shallow' | 'deep'; autoDismissEnabled: boolean; autoDismissConfidenceThreshold: 'high' | 'medium' | 'low'; + autoAnalysisEnabled: boolean; + autoAnalysisMinSeverity: 'critical' | 'high' | 'medium' | 'all'; } ) => { const modelConfigPayload = { @@ -481,6 +493,8 @@ export function SecurityAgentPageClient({ organizationId }: SecurityAgentPageCli analysisMode: config.analysisMode, autoDismissEnabled: config.autoDismissEnabled, autoDismissConfidenceThreshold: config.autoDismissConfidenceThreshold, + autoAnalysisEnabled: config.autoAnalysisEnabled, + autoAnalysisMinSeverity: config.autoAnalysisMinSeverity, ...modelConfigPayload, }); } else if (!isOrg) { @@ -494,6 +508,8 @@ export function SecurityAgentPageClient({ organizationId }: SecurityAgentPageCli analysisMode: config.analysisMode, autoDismissEnabled: config.autoDismissEnabled, autoDismissConfidenceThreshold: config.autoDismissConfidenceThreshold, + autoAnalysisEnabled: config.autoAnalysisEnabled, + autoAnalysisMinSeverity: config.autoAnalysisMinSeverity, ...modelConfigPayload, }); } @@ -752,8 +768,48 @@ export function SecurityAgentPageClient({ organizationId }: SecurityAgentPageCli )} + {/* Unsaved Changes Confirmation Dialog */} + !open && setPendingTabChange(null)} + > + + + Unsaved Changes + + You have unsaved configuration changes. Do you want to discard them? + + + + + + + + + {/* Tabs - only show config and jobs tabs when GitHub integration is available */} - + { + if (effectiveTab === 'config' && tab !== 'config' && configHasChanges.current) { + setPendingTabChange(tab); + } else { + setActiveTab(tab); + } + }} + className="w-full" + > @@ -822,9 +878,14 @@ export function SecurityAgentPageClient({ organizationId }: SecurityAgentPageCli analysisMode={configData?.analysisMode ?? 'auto'} autoDismissEnabled={configData?.autoDismissEnabled ?? false} autoDismissConfidenceThreshold={configData?.autoDismissConfidenceThreshold ?? 'high'} + autoAnalysisEnabled={configData?.autoAnalysisEnabled ?? false} + autoAnalysisMinSeverity={configData?.autoAnalysisMinSeverity ?? 'high'} repositories={allRepositories} onSave={handleSaveConfig} onToggleEnabled={handleToggleEnabled} + onHasChangesChange={v => { + configHasChanges.current = v; + }} isSaving={isSavingConfig} isToggling={isTogglingEnabled} /> diff --git a/src/components/security-agent/SecurityConfigForm.tsx b/src/components/security-agent/SecurityConfigForm.tsx index f06826b45..bb3ae5b59 100644 --- a/src/components/security-agent/SecurityConfigForm.tsx +++ b/src/components/security-agent/SecurityConfigForm.tsx @@ -43,6 +43,8 @@ type AnalysisMode = 'auto' | 'shallow' | 'deep'; type AutoDismissConfidenceThreshold = 'high' | 'medium' | 'low'; +type AutoAnalysisMinSeverity = 'critical' | 'high' | 'medium' | 'all'; + type RepositoryData = { id: number; fullName: string; @@ -62,6 +64,8 @@ type SecurityConfigFormProps = { analysisMode: AnalysisMode; autoDismissEnabled: boolean; autoDismissConfidenceThreshold: AutoDismissConfidenceThreshold; + autoAnalysisEnabled: boolean; + autoAnalysisMinSeverity: AutoAnalysisMinSeverity; repositories: RepositoryData[]; repositoriesSyncedAt?: string | null; isLoadingRepositories?: boolean; @@ -75,6 +79,8 @@ type SecurityConfigFormProps = { analysisMode: AnalysisMode; autoDismissEnabled: boolean; autoDismissConfidenceThreshold: AutoDismissConfidenceThreshold; + autoAnalysisEnabled: boolean; + autoAnalysisMinSeverity: AutoAnalysisMinSeverity; } ) => void; onToggleEnabled: ( @@ -85,6 +91,7 @@ type SecurityConfigFormProps = { } ) => void; onRefreshRepositories?: () => void; + onHasChangesChange?: (hasChanges: boolean) => void; isSaving: boolean; isToggling: boolean; isRefreshingRepositories?: boolean; @@ -136,6 +143,29 @@ const CONFIDENCE_THRESHOLD_OPTIONS = [ }, ]; +const AUTO_ANALYSIS_MIN_SEVERITY_OPTIONS = [ + { + value: 'critical' as const, + label: 'Critical only', + description: 'Only auto-analyse findings with critical severity', + }, + { + value: 'high' as const, + label: 'High and above', + description: 'Auto-analyse findings with high or critical severity', + }, + { + value: 'medium' as const, + label: 'Medium and above', + description: 'Auto-analyse findings with medium, high, or critical severity', + }, + { + value: 'all' as const, + label: 'All severities', + description: 'Auto-analyse all findings regardless of severity', + }, +]; + const SEVERITY_INFO = [ { key: 'critical' as const, @@ -179,12 +209,15 @@ export function SecurityConfigForm({ analysisMode: initialAnalysisMode, autoDismissEnabled: initialAutoDismissEnabled, autoDismissConfidenceThreshold: initialAutoDismissThreshold, + autoAnalysisEnabled: initialAutoAnalysisEnabled, + autoAnalysisMinSeverity: initialAutoAnalysisMinSeverity, repositories, repositoriesSyncedAt, isLoadingRepositories, onSave, onToggleEnabled, onRefreshRepositories, + onHasChangesChange, isSaving, isToggling, isRefreshingRepositories, @@ -207,8 +240,16 @@ export function SecurityConfigForm({ const [autoDismissEnabled, setAutoDismissEnabled] = useState(initialAutoDismissEnabled); const [autoDismissConfidenceThreshold, setAutoDismissConfidenceThreshold] = useState(initialAutoDismissThreshold); + const [autoAnalysisEnabled, setAutoAnalysisEnabled] = useState(initialAutoAnalysisEnabled); + const [autoAnalysisMinSeverity, setAutoAnalysisMinSeverity] = useState( + initialAutoAnalysisMinSeverity + ); const [hasChanges, setHasChanges] = useState(false); + useEffect(() => { + onHasChangesChange?.(hasChanges); + }, [hasChanges, onHasChangesChange]); + useEffect(() => { setLocalConfig(slaConfig); setRepositorySelectionMode(initialSelectionMode); @@ -218,6 +259,8 @@ export function SecurityConfigForm({ setAnalysisMode(initialAnalysisMode); setAutoDismissEnabled(initialAutoDismissEnabled); setAutoDismissConfidenceThreshold(initialAutoDismissThreshold); + setAutoAnalysisEnabled(initialAutoAnalysisEnabled); + setAutoAnalysisMinSeverity(initialAutoAnalysisMinSeverity); setHasChanges(false); }, [ slaConfig, @@ -228,6 +271,8 @@ export function SecurityConfigForm({ initialAnalysisMode, initialAutoDismissEnabled, initialAutoDismissThreshold, + initialAutoAnalysisEnabled, + initialAutoAnalysisMinSeverity, ]); const checkForChanges = useCallback( @@ -239,7 +284,9 @@ export function SecurityConfigForm({ newAnalysisModel: string, newAnalysisMode: AnalysisMode, newAutoDismissEnabled: boolean, - newAutoDismissThreshold: AutoDismissConfidenceThreshold + newAutoDismissThreshold: AutoDismissConfidenceThreshold, + newAutoAnalysisEnabled: boolean, + newAutoAnalysisMinSeverity: AutoAnalysisMinSeverity ) => { const configChanged = newConfig.critical !== slaConfig.critical || @@ -256,6 +303,9 @@ export function SecurityConfigForm({ const analysisModeChanged = newAnalysisMode !== initialAnalysisMode; const autoDismissEnabledChanged = newAutoDismissEnabled !== initialAutoDismissEnabled; const autoDismissThresholdChanged = newAutoDismissThreshold !== initialAutoDismissThreshold; + const autoAnalysisEnabledChanged = newAutoAnalysisEnabled !== initialAutoAnalysisEnabled; + const autoAnalysisMinSeverityChanged = + newAutoAnalysisMinSeverity !== initialAutoAnalysisMinSeverity; setHasChanges( configChanged || @@ -265,7 +315,9 @@ export function SecurityConfigForm({ analysisModelChanged || analysisModeChanged || autoDismissEnabledChanged || - autoDismissThresholdChanged + autoDismissThresholdChanged || + autoAnalysisEnabledChanged || + autoAnalysisMinSeverityChanged ); }, [ @@ -277,6 +329,8 @@ export function SecurityConfigForm({ initialAnalysisMode, initialAutoDismissEnabled, initialAutoDismissThreshold, + initialAutoAnalysisEnabled, + initialAutoAnalysisMinSeverity, ] ); @@ -294,7 +348,9 @@ export function SecurityConfigForm({ selectedAnalysisModel, analysisMode, autoDismissEnabled, - autoDismissConfidenceThreshold + autoDismissConfidenceThreshold, + autoAnalysisEnabled, + autoAnalysisMinSeverity ); }; @@ -308,7 +364,9 @@ export function SecurityConfigForm({ selectedAnalysisModel, analysisMode, autoDismissEnabled, - autoDismissConfidenceThreshold + autoDismissConfidenceThreshold, + autoAnalysisEnabled, + autoAnalysisMinSeverity ); }; @@ -322,7 +380,9 @@ export function SecurityConfigForm({ selectedAnalysisModel, analysisMode, autoDismissEnabled, - autoDismissConfidenceThreshold + autoDismissConfidenceThreshold, + autoAnalysisEnabled, + autoAnalysisMinSeverity ); }; @@ -336,7 +396,9 @@ export function SecurityConfigForm({ selectedAnalysisModel, analysisMode, autoDismissEnabled, - autoDismissConfidenceThreshold + autoDismissConfidenceThreshold, + autoAnalysisEnabled, + autoAnalysisMinSeverity ); }; @@ -350,7 +412,9 @@ export function SecurityConfigForm({ model, analysisMode, autoDismissEnabled, - autoDismissConfidenceThreshold + autoDismissConfidenceThreshold, + autoAnalysisEnabled, + autoAnalysisMinSeverity ); }; @@ -364,7 +428,9 @@ export function SecurityConfigForm({ selectedAnalysisModel, mode, autoDismissEnabled, - autoDismissConfidenceThreshold + autoDismissConfidenceThreshold, + autoAnalysisEnabled, + autoAnalysisMinSeverity ); }; @@ -378,7 +444,9 @@ export function SecurityConfigForm({ selectedAnalysisModel, analysisMode, newEnabled, - autoDismissConfidenceThreshold + autoDismissConfidenceThreshold, + autoAnalysisEnabled, + autoAnalysisMinSeverity ); }; @@ -392,7 +460,41 @@ export function SecurityConfigForm({ selectedAnalysisModel, analysisMode, autoDismissEnabled, - threshold + threshold, + autoAnalysisEnabled, + autoAnalysisMinSeverity + ); + }; + + const handleAutoAnalysisEnabledChange = (newEnabled: boolean) => { + setAutoAnalysisEnabled(newEnabled); + checkForChanges( + localConfig, + repositorySelectionMode, + selectedRepositoryIds, + selectedTriageModel, + selectedAnalysisModel, + analysisMode, + autoDismissEnabled, + autoDismissConfidenceThreshold, + newEnabled, + autoAnalysisMinSeverity + ); + }; + + const handleAutoAnalysisMinSeverityChange = (severity: AutoAnalysisMinSeverity) => { + setAutoAnalysisMinSeverity(severity); + checkForChanges( + localConfig, + repositorySelectionMode, + selectedRepositoryIds, + selectedTriageModel, + selectedAnalysisModel, + analysisMode, + autoDismissEnabled, + autoDismissConfidenceThreshold, + autoAnalysisEnabled, + severity ); }; @@ -407,6 +509,8 @@ export function SecurityConfigForm({ analysisMode, autoDismissEnabled, autoDismissConfidenceThreshold, + autoAnalysisEnabled, + autoAnalysisMinSeverity, }); }; @@ -420,7 +524,9 @@ export function SecurityConfigForm({ selectedAnalysisModel, analysisMode, autoDismissEnabled, - autoDismissConfidenceThreshold + autoDismissConfidenceThreshold, + autoAnalysisEnabled, + autoAnalysisMinSeverity ); }; @@ -582,24 +688,28 @@ export function SecurityConfigForm({ -
- +
+
+ +
- +
+ +
@@ -621,31 +731,112 @@ export function SecurityConfigForm({
- + handleAnalysisModeChange(value as AnalysisMode)} - className="space-y-3" + className="grid grid-cols-1 gap-3 md:grid-cols-3" > {ANALYSIS_MODE_OPTIONS.map(option => ( -
- +
+ ))}
)} + {/* Auto-Analysis Configuration Card - only show when enabled */} + {enabled && ( + + +
+
+ +
+
+ Auto-Analysis +

+ Automatically analyse new findings as they are synced +

+
+
+
+ +
+
+ +

+ When enabled, new findings will be automatically triaged and analysed based on the + analysis mode configured above +

+
+ +
+ + {autoAnalysisEnabled && ( +
+ + + handleAutoAnalysisMinSeverityChange(value as AutoAnalysisMinSeverity) + } + className="grid grid-cols-1 gap-3 md:grid-cols-4" + > + {AUTO_ANALYSIS_MIN_SEVERITY_OPTIONS.map(option => ( + + ))} + +
+ )} +
+
+ )} + {/* Auto-Dismiss Configuration Card - only show when enabled */} {enabled && ( @@ -688,21 +879,29 @@ export function SecurityConfigForm({ onValueChange={value => handleAutoDismissThresholdChange(value as AutoDismissConfidenceThreshold) } - className="space-y-3" + className="grid grid-cols-1 gap-3 md:grid-cols-3" > {CONFIDENCE_THRESHOLD_OPTIONS.map(option => ( -
- +
+ ))} @@ -728,32 +927,37 @@ export function SecurityConfigForm({ - {SEVERITY_INFO.map(({ key, label, description, icon: Icon, color }) => ( -
-
- -
- -

{description}

+
+ {SEVERITY_INFO.map(({ key, label, description, icon: Icon, color }) => ( +
+
+ +
+ +

{description}

+
+
+
+ handleChange(key, e.target.value)} + className="w-20 text-center" + disabled={!enabled} + /> + days
-
- handleChange(key, e.target.value)} - className="w-20 text-center" - disabled={!enabled} - /> - days -
-
- ))} + ))} +