From 4bf19755638078cdac9573422b6c0e085e31b02a Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Fri, 22 May 2026 21:07:48 +0300 Subject: [PATCH 01/23] feat(review-memory): add feedback and proposal schema --- .../lib/code-reviews/review-memory/db.test.ts | 178 + .../src/lib/code-reviews/review-memory/db.ts | 986 + apps/web/src/lib/user/index.test.ts | 150 + apps/web/src/lib/user/index.ts | 33 + .../0141_chunky_captain_america.sql | 234 + .../db/src/migrations/meta/0141_snapshot.json | 25434 ++++++++++++++++ packages/db/src/migrations/meta/_journal.json | 7 + packages/db/src/schema-types.ts | 108 + packages/db/src/schema.ts | 339 + 9 files changed, 27469 insertions(+) create mode 100644 apps/web/src/lib/code-reviews/review-memory/db.test.ts create mode 100644 apps/web/src/lib/code-reviews/review-memory/db.ts create mode 100644 packages/db/src/migrations/0141_chunky_captain_america.sql create mode 100644 packages/db/src/migrations/meta/0141_snapshot.json diff --git a/apps/web/src/lib/code-reviews/review-memory/db.test.ts b/apps/web/src/lib/code-reviews/review-memory/db.test.ts new file mode 100644 index 0000000000..8a0fb4f7d7 --- /dev/null +++ b/apps/web/src/lib/code-reviews/review-memory/db.test.ts @@ -0,0 +1,178 @@ +/* eslint-disable drizzle/enforce-delete-with-where */ +import { db } from '@/lib/drizzle'; +import { insertTestUser } from '@/tests/helpers/user.helper'; +import { + code_review_feedback_events, + code_review_feedback_subjects, + code_review_memory_aggregation_state, + code_review_memory_proposal_evidence, + code_review_memory_proposals, + kilocode_users, +} from '@kilocode/db/schema'; +import { count, eq } from 'drizzle-orm'; +import { + listProposalEvidence, + recordFeedbackEvent, + upsertFeedbackSubject, + upsertReviewMemoryProposal, +} from './db'; + +describe('review memory db helpers', () => { + afterEach(async () => { + await db.delete(code_review_memory_proposal_evidence); + await db.delete(code_review_memory_proposals); + await db.delete(code_review_feedback_events); + await db.delete(code_review_feedback_subjects); + await db.delete(code_review_memory_aggregation_state); + await db.delete(kilocode_users); + }); + + it('upserts feedback subjects by platform, repo, type, and external ID', async () => { + const user = await insertTestUser(); + const owner = { type: 'user' as const, id: user.id }; + + const first = await upsertFeedbackSubject({ + owner, + platform: 'github', + repoFullName: 'owner/repo', + subjectType: 'inline_comment', + externalId: 'comment-123', + externalThreadId: 'thread-123', + prNumber: 10, + bodyExcerpt: 'Initial finding body', + state: 'active', + }); + + const second = await upsertFeedbackSubject({ + owner, + platform: 'github', + repoFullName: 'owner/repo', + subjectType: 'inline_comment', + externalId: 'comment-123', + externalThreadId: 'thread-123', + prNumber: 10, + bodyExcerpt: 'Updated finding body', + state: 'resolved', + }); + + expect(second.id).toBe(first.id); + expect(second.state).toBe('resolved'); + expect(second.body_excerpt).toBe('Updated finding body'); + + const rows = await db + .select({ count: count() }) + .from(code_review_feedback_subjects) + .where(eq(code_review_feedback_subjects.external_id, 'comment-123')); + expect(rows[0].count).toBe(1); + }); + + it('dedupes feedback events and refreshes aggregation state', async () => { + const user = await insertTestUser(); + const owner = { type: 'user' as const, id: user.id }; + const subject = await upsertFeedbackSubject({ + owner, + platform: 'github', + repoFullName: 'owner/repo', + subjectType: 'summary_comment', + externalId: 'summary-123', + prNumber: 12, + state: 'active', + }); + + const first = await recordFeedbackEvent({ + owner, + platform: 'github', + subjectId: subject.id, + repoFullName: 'owner/repo', + prNumber: 12, + eventSource: 'github_webhook', + signalKind: 'negative_reaction', + sentiment: 'negative', + strength: 3, + dedupeHash: 'dedupe-review-memory-event', + }); + const second = await recordFeedbackEvent({ + owner, + platform: 'github', + subjectId: subject.id, + repoFullName: 'owner/repo', + prNumber: 12, + eventSource: 'github_webhook', + signalKind: 'negative_reaction', + sentiment: 'negative', + strength: 3, + dedupeHash: 'dedupe-review-memory-event', + }); + + expect(first.created).toBe(true); + expect(second.created).toBe(false); + expect(second.event.id).toBe(first.event.id); + + const [state] = await db.select().from(code_review_memory_aggregation_state); + expect(state.fresh_event_count).toBe(1); + expect(state.fresh_weight).toBe(3); + expect(state.fresh_distinct_subject_count).toBe(1); + expect(state.fresh_distinct_pr_count).toBe(1); + expect(state.status).toBe('eligible'); + }); + + it('updates active proposals by dedupe key and links evidence once', async () => { + const user = await insertTestUser(); + const owner = { type: 'user' as const, id: user.id }; + const event = await recordFeedbackEvent({ + owner, + platform: 'github', + repoFullName: 'owner/repo', + prNumber: 14, + eventSource: 'github_webhook', + signalKind: 'corrective_reply', + sentiment: 'negative', + strength: 4, + dedupeHash: 'proposal-evidence-event', + }); + + const first = await upsertReviewMemoryProposal({ + owner, + platform: 'github', + repoFullName: 'owner/repo', + proposalType: 'clarify', + scopeKind: 'repository', + title: 'Clarify noisy guidance', + rationale: 'Maintainers corrected the same guidance.', + proposedMarkdown: '### Review guidance: Clarify noisy guidance', + dedupeKey: 'clarify-noisy-guidance', + negativeCount: 1, + distinctPrCount: 1, + distinctSubjectCount: 1, + evidence: [{ feedbackEventId: event.event.id, role: 'primary' }], + }); + + const second = await upsertReviewMemoryProposal({ + owner, + platform: 'github', + repoFullName: 'owner/repo', + proposalType: 'clarify', + scopeKind: 'repository', + title: 'Clarify review guidance', + rationale: 'Maintainers corrected the same guidance again.', + proposedMarkdown: '### Review guidance: Clarify review guidance', + dedupeKey: 'clarify-noisy-guidance', + negativeCount: 2, + distinctPrCount: 1, + distinctSubjectCount: 1, + evidence: [{ feedbackEventId: event.event.id, role: 'primary' }], + }); + + expect(second.id).toBe(first.id); + expect(second.title).toBe('Clarify review guidance'); + expect(second.negative_count).toBe(2); + + const proposals = await db.select().from(code_review_memory_proposals); + expect(proposals).toHaveLength(1); + + const evidence = await listProposalEvidence({ proposalId: second.id }); + expect(evidence).toHaveLength(1); + expect(evidence[0].feedbackEvent.id).toBe(event.event.id); + expect(evidence[0].role).toBe('primary'); + }); +}); diff --git a/apps/web/src/lib/code-reviews/review-memory/db.ts b/apps/web/src/lib/code-reviews/review-memory/db.ts new file mode 100644 index 0000000000..2e4f1ebaa8 --- /dev/null +++ b/apps/web/src/lib/code-reviews/review-memory/db.ts @@ -0,0 +1,986 @@ +import { createHash } from 'node:crypto'; +import { db, type DrizzleTransaction } from '@/lib/drizzle'; +import { + code_review_feedback_events, + code_review_feedback_subjects, + code_review_memory_aggregation_runs, + code_review_memory_aggregation_state, + code_review_memory_proposal_evidence, + code_review_memory_proposals, +} from '@kilocode/db/schema'; +import type { + CodeReviewFeedbackEvent, + CodeReviewFeedbackSubject, + CodeReviewMemoryAggregationRun, + CodeReviewMemoryAggregationState, + CodeReviewMemoryProposal, +} from '@kilocode/db/schema'; +import type { + ReviewMemoryAggregationRunStatus, + ReviewMemoryAggregationRunTrigger, + ReviewMemoryChangeRequestType, + ReviewMemoryEvidenceRole, + ReviewMemoryFeedbackEventSource, + ReviewMemoryPlatform, + ReviewMemoryProposalScopeKind, + ReviewMemoryProposalStatus, + ReviewMemoryProposalType, + ReviewMemorySentiment, + ReviewMemorySignalKind, + ReviewMemorySubjectState, + ReviewMemorySubjectType, +} from '@kilocode/db/schema-types'; +import { and, desc, eq, inArray, sql, type SQL } from 'drizzle-orm'; + +export type ReviewMemoryDatabase = typeof db | DrizzleTransaction; + +export type ReviewMemoryOwner = { type: 'org'; id: string } | { type: 'user'; id: string }; + +export const ACTIONABLE_REVIEW_MEMORY_PROPOSAL_STATUSES = [ + 'open', + 'edited', + 'change_request_failed', +] as const satisfies readonly ReviewMemoryProposalStatus[]; + +const DEFAULT_EXCERPT_LIMIT = 500; + +function databaseOrDefault(database?: ReviewMemoryDatabase): ReviewMemoryDatabase { + return database ?? db; +} + +function ownerColumns(owner: ReviewMemoryOwner) { + return owner.type === 'org' + ? { owned_by_organization_id: owner.id, owned_by_user_id: null } + : { owned_by_organization_id: null, owned_by_user_id: owner.id }; +} + +function ownerScope(owner: ReviewMemoryOwner): string { + return `${owner.type}:${owner.id}`; +} + +function subjectOwnerWhere(owner: ReviewMemoryOwner): SQL { + return owner.type === 'org' + ? eq(code_review_feedback_subjects.owned_by_organization_id, owner.id) + : eq(code_review_feedback_subjects.owned_by_user_id, owner.id); +} + +function eventOwnerWhere(owner: ReviewMemoryOwner): SQL { + return owner.type === 'org' + ? eq(code_review_feedback_events.owned_by_organization_id, owner.id) + : eq(code_review_feedback_events.owned_by_user_id, owner.id); +} + +function aggregationStateOwnerWhere(owner: ReviewMemoryOwner): SQL { + return owner.type === 'org' + ? eq(code_review_memory_aggregation_state.owned_by_organization_id, owner.id) + : eq(code_review_memory_aggregation_state.owned_by_user_id, owner.id); +} + +function aggregationRunOwnerWhere(owner: ReviewMemoryOwner): SQL { + return owner.type === 'org' + ? eq(code_review_memory_aggregation_runs.owned_by_organization_id, owner.id) + : eq(code_review_memory_aggregation_runs.owned_by_user_id, owner.id); +} + +function proposalOwnerWhere(owner: ReviewMemoryOwner): SQL { + return owner.type === 'org' + ? eq(code_review_memory_proposals.owned_by_organization_id, owner.id) + : eq(code_review_memory_proposals.owned_by_user_id, owner.id); +} + +function compactText( + value: string | null | undefined, + limit = DEFAULT_EXCERPT_LIMIT +): string | null { + if (!value) return null; + const normalized = value.replace(/\s+/g, ' ').trim(); + if (normalized.length <= limit) return normalized; + return `${normalized.slice(0, Math.max(0, limit - 3)).trimEnd()}...`; +} + +export function createReviewMemoryDedupeHash(parts: readonly unknown[]): string { + return createHash('sha256') + .update( + JSON.stringify( + parts.map(part => { + if (part === undefined) return null; + return part; + }) + ) + ) + .digest('hex'); +} + +export type UpsertFeedbackSubjectInput = { + owner: ReviewMemoryOwner; + platform: ReviewMemoryPlatform; + platformIntegrationId?: string | null; + codeReviewId?: string | null; + subjectType: ReviewMemorySubjectType; + externalId: string; + externalThreadId?: string | null; + externalUrl?: string | null; + repoFullName: string; + platformProjectId?: number | null; + prNumber?: number | null; + prUrl?: string | null; + headSha?: string | null; + filePath?: string | null; + lineNumber?: number | null; + diffHunk?: string | null; + bodyExcerpt?: string | null; + severity?: string | null; + findingTitle?: string | null; + findingFingerprint?: string | null; + state?: ReviewMemorySubjectState; + database?: ReviewMemoryDatabase; +}; + +export async function upsertFeedbackSubject( + input: UpsertFeedbackSubjectInput +): Promise { + const database = databaseOrDefault(input.database); + const now = new Date().toISOString(); + const [subject] = await database + .insert(code_review_feedback_subjects) + .values({ + ...ownerColumns(input.owner), + platform: input.platform, + platform_integration_id: input.platformIntegrationId ?? null, + code_review_id: input.codeReviewId ?? null, + subject_type: input.subjectType, + external_id: input.externalId, + external_thread_id: input.externalThreadId ?? null, + external_url: input.externalUrl ?? null, + repo_full_name: input.repoFullName, + platform_project_id: input.platformProjectId ?? null, + pr_number: input.prNumber ?? null, + pr_url: input.prUrl ?? null, + head_sha: input.headSha ?? null, + file_path: input.filePath ?? null, + line_number: input.lineNumber ?? null, + diff_hunk: compactText(input.diffHunk, 1_000), + body_excerpt: compactText(input.bodyExcerpt), + severity: input.severity ?? null, + finding_title: input.findingTitle ?? null, + finding_fingerprint: input.findingFingerprint ?? null, + state: input.state ?? 'unknown', + first_seen_at: now, + last_seen_at: now, + updated_at: now, + }) + .onConflictDoUpdate({ + target: [ + code_review_feedback_subjects.platform, + code_review_feedback_subjects.repo_full_name, + code_review_feedback_subjects.subject_type, + code_review_feedback_subjects.external_id, + ], + set: { + owned_by_organization_id: ownerColumns(input.owner).owned_by_organization_id, + owned_by_user_id: ownerColumns(input.owner).owned_by_user_id, + platform_integration_id: input.platformIntegrationId ?? null, + code_review_id: input.codeReviewId ?? null, + external_thread_id: input.externalThreadId ?? null, + external_url: input.externalUrl ?? null, + platform_project_id: input.platformProjectId ?? null, + pr_number: input.prNumber ?? null, + pr_url: input.prUrl ?? null, + head_sha: input.headSha ?? null, + file_path: input.filePath ?? null, + line_number: input.lineNumber ?? null, + diff_hunk: compactText(input.diffHunk, 1_000), + body_excerpt: compactText(input.bodyExcerpt), + severity: input.severity ?? null, + finding_title: input.findingTitle ?? null, + finding_fingerprint: input.findingFingerprint ?? null, + state: input.state ?? 'unknown', + last_seen_at: now, + updated_at: now, + }, + }) + .returning(); + + if (!subject) { + throw new Error('Failed to upsert review memory subject'); + } + + return subject; +} + +export type FindFeedbackSubjectInput = { + owner: ReviewMemoryOwner; + platform: ReviewMemoryPlatform; + repoFullName: string; + subjectType: ReviewMemorySubjectType; + externalId: string; + database?: ReviewMemoryDatabase; +}; + +export async function findFeedbackSubject( + input: FindFeedbackSubjectInput +): Promise { + const database = databaseOrDefault(input.database); + const [subject] = await database + .select() + .from(code_review_feedback_subjects) + .where( + and( + subjectOwnerWhere(input.owner), + eq(code_review_feedback_subjects.platform, input.platform), + eq(code_review_feedback_subjects.repo_full_name, input.repoFullName), + eq(code_review_feedback_subjects.subject_type, input.subjectType), + eq(code_review_feedback_subjects.external_id, input.externalId) + ) + ) + .limit(1); + + return subject ?? null; +} + +export type RecordFeedbackEventInput = { + owner: ReviewMemoryOwner; + platform: ReviewMemoryPlatform; + platformIntegrationId?: string | null; + subjectId?: string | null; + codeReviewId?: string | null; + repoFullName: string; + platformProjectId?: number | null; + prNumber?: number | null; + prUrl?: string | null; + eventSource: ReviewMemoryFeedbackEventSource; + signalKind: ReviewMemorySignalKind; + sentiment: ReviewMemorySentiment; + strength: number; + externalEventId?: string | null; + dedupeHash?: string | null; + externalUrl?: string | null; + evidenceExcerpt?: string | null; + metadata?: Record; + occurredAt?: string | Date | null; + database?: ReviewMemoryDatabase; +}; + +export type RecordFeedbackEventResult = { + event: CodeReviewFeedbackEvent; + created: boolean; +}; + +export async function recordFeedbackEvent( + input: RecordFeedbackEventInput +): Promise { + const database = databaseOrDefault(input.database); + const occurredAt = input.occurredAt + ? new Date(input.occurredAt).toISOString() + : new Date().toISOString(); + const evidenceExcerpt = compactText(input.evidenceExcerpt); + const dedupeHash = + input.dedupeHash ?? + createReviewMemoryDedupeHash([ + ownerScope(input.owner), + input.platform, + input.repoFullName, + input.externalEventId, + input.subjectId, + input.signalKind, + evidenceExcerpt, + occurredAt, + ]); + + const [inserted] = await database + .insert(code_review_feedback_events) + .values({ + ...ownerColumns(input.owner), + platform: input.platform, + platform_integration_id: input.platformIntegrationId ?? null, + subject_id: input.subjectId ?? null, + code_review_id: input.codeReviewId ?? null, + repo_full_name: input.repoFullName, + platform_project_id: input.platformProjectId ?? null, + pr_number: input.prNumber ?? null, + pr_url: input.prUrl ?? null, + event_source: input.eventSource, + signal_kind: input.signalKind, + sentiment: input.sentiment, + strength: input.strength, + external_event_id: input.externalEventId ?? null, + dedupe_hash: dedupeHash, + external_url: input.externalUrl ?? null, + evidence_excerpt: evidenceExcerpt, + metadata: input.metadata ?? {}, + aggregation_state: 'fresh', + occurred_at: occurredAt, + }) + .onConflictDoNothing({ target: code_review_feedback_events.dedupe_hash }) + .returning(); + + if (inserted) { + await refreshAggregationStateForScope({ + owner: input.owner, + platform: input.platform, + repoFullName: input.repoFullName, + platformProjectId: input.platformProjectId, + database, + }); + return { event: inserted, created: true }; + } + + const [existing] = await database + .select() + .from(code_review_feedback_events) + .where(eq(code_review_feedback_events.dedupe_hash, dedupeHash)) + .limit(1); + + if (!existing) { + throw new Error('Failed to read deduped review memory feedback event'); + } + + await refreshAggregationStateForScope({ + owner: input.owner, + platform: input.platform, + repoFullName: input.repoFullName, + platformProjectId: input.platformProjectId, + database, + }); + + return { event: existing, created: false }; +} + +export type RefreshAggregationStateInput = { + owner: ReviewMemoryOwner; + platform: ReviewMemoryPlatform; + repoFullName: string; + platformProjectId?: number | null; + database?: ReviewMemoryDatabase; +}; + +export async function refreshAggregationStateForScope( + input: RefreshAggregationStateInput +): Promise { + const database = databaseOrDefault(input.database); + const conditions = [ + eventOwnerWhere(input.owner), + eq(code_review_feedback_events.platform, input.platform), + eq(code_review_feedback_events.repo_full_name, input.repoFullName), + eq(code_review_feedback_events.aggregation_state, 'fresh'), + ] satisfies SQL[]; + + const [rollup] = await database + .select({ + eventCount: sql`COUNT(*)::int`, + freshWeight: sql`COALESCE(SUM(${code_review_feedback_events.strength}), 0)::int`, + distinctSubjectCount: sql`COUNT(DISTINCT ${code_review_feedback_events.subject_id}) FILTER (WHERE ${code_review_feedback_events.subject_id} IS NOT NULL)::int`, + distinctPrCount: sql`COUNT(DISTINCT ${code_review_feedback_events.pr_number}) FILTER (WHERE ${code_review_feedback_events.pr_number} IS NOT NULL)::int`, + lastFreshEventCreatedAt: sql`MAX(${code_review_feedback_events.created_at})`, + }) + .from(code_review_feedback_events) + .where(and(...conditions)); + + const [existing] = await database + .select() + .from(code_review_memory_aggregation_state) + .where( + and( + aggregationStateOwnerWhere(input.owner), + eq(code_review_memory_aggregation_state.platform, input.platform), + eq(code_review_memory_aggregation_state.repo_full_name, input.repoFullName) + ) + ) + .limit(1); + + const eventCount = rollup?.eventCount ?? 0; + const status = existing?.status === 'running' ? 'running' : eventCount > 0 ? 'eligible' : 'idle'; + const now = new Date().toISOString(); + const stateValues = { + fresh_event_count: eventCount, + fresh_weight: rollup?.freshWeight ?? 0, + fresh_distinct_subject_count: rollup?.distinctSubjectCount ?? 0, + fresh_distinct_pr_count: rollup?.distinctPrCount ?? 0, + last_included_event_created_at: rollup?.lastFreshEventCreatedAt ?? null, + platform_project_id: input.platformProjectId ?? existing?.platform_project_id ?? null, + status, + updated_at: now, + }; + + if (existing) { + const [updated] = await database + .update(code_review_memory_aggregation_state) + .set(stateValues) + .where(eq(code_review_memory_aggregation_state.id, existing.id)) + .returning(); + + if (!updated) throw new Error('Failed to update review memory aggregation state'); + return updated; + } + + const [inserted] = await database + .insert(code_review_memory_aggregation_state) + .values({ + ...ownerColumns(input.owner), + platform: input.platform, + repo_full_name: input.repoFullName, + platform_project_id: input.platformProjectId ?? null, + ...stateValues, + next_eligible_at: now, + }) + .returning(); + + if (!inserted) throw new Error('Failed to insert review memory aggregation state'); + return inserted; +} + +export type CreateAggregationRunInput = { + owner: ReviewMemoryOwner; + platform: ReviewMemoryPlatform; + repoFullName: string; + platformProjectId?: number | null; + modelSlug: string; + trigger: ReviewMemoryAggregationRunTrigger; + inputEventCount?: number; + inputSubjectCount?: number; + inputClusterCount?: number; + freshEventCutoffAt?: string | null; + database?: ReviewMemoryDatabase; +}; + +export async function createAggregationRun( + input: CreateAggregationRunInput +): Promise { + const database = databaseOrDefault(input.database); + const [run] = await database + .insert(code_review_memory_aggregation_runs) + .values({ + ...ownerColumns(input.owner), + platform: input.platform, + repo_full_name: input.repoFullName, + platform_project_id: input.platformProjectId ?? null, + model_slug: input.modelSlug, + trigger: input.trigger, + input_event_count: input.inputEventCount ?? 0, + input_subject_count: input.inputSubjectCount ?? 0, + input_cluster_count: input.inputClusterCount ?? 0, + fresh_event_cutoff_at: input.freshEventCutoffAt ?? null, + status: 'running', + }) + .returning(); + + if (!run) throw new Error('Failed to create review memory aggregation run'); + return run; +} + +export async function updateAggregationRunStatus(input: { + runId: string; + status: ReviewMemoryAggregationRunStatus; + skipReason?: string | null; + tokensIn?: number | null; + tokensOut?: number | null; + totalCostMusd?: number | null; + errorMessage?: string | null; + database?: ReviewMemoryDatabase; +}): Promise { + const database = databaseOrDefault(input.database); + const [run] = await database + .update(code_review_memory_aggregation_runs) + .set({ + status: input.status, + skip_reason: input.skipReason ?? null, + tokens_in: input.tokensIn ?? null, + tokens_out: input.tokensOut ?? null, + total_cost_musd: input.totalCostMusd ?? null, + error_message: input.errorMessage ?? null, + completed_at: input.status === 'running' ? null : new Date().toISOString(), + }) + .where(eq(code_review_memory_aggregation_runs.id, input.runId)) + .returning(); + + if (!run) throw new Error('Failed to update review memory aggregation run'); + return run; +} + +export type UpsertProposalInput = { + owner: ReviewMemoryOwner; + platform: ReviewMemoryPlatform; + platformIntegrationId?: string | null; + repoFullName: string; + platformProjectId?: number | null; + aggregationRunId?: string | null; + targetFilePath?: string; + scopeKind: ReviewMemoryProposalScopeKind; + scopeValue?: string | null; + proposalType: ReviewMemoryProposalType; + title: string; + rationale: string; + proposedMarkdown: string; + dedupeKey: string; + llmConfidence?: number | null; + positiveCount?: number; + negativeCount?: number; + neutralCount?: number; + distinctPrCount?: number; + distinctSubjectCount?: number; + contradictoryCount?: number; + evidence?: { feedbackEventId: string; role: ReviewMemoryEvidenceRole }[]; + database?: ReviewMemoryDatabase; +}; + +export async function upsertReviewMemoryProposal( + input: UpsertProposalInput +): Promise { + const database = databaseOrDefault(input.database); + const activeStatuses = [ + 'open', + 'edited', + 'approved', + 'opening_change_request', + 'change_request_opened', + ] satisfies ReviewMemoryProposalStatus[]; + + const [existing] = await database + .select() + .from(code_review_memory_proposals) + .where( + and( + proposalOwnerWhere(input.owner), + eq(code_review_memory_proposals.platform, input.platform), + eq(code_review_memory_proposals.repo_full_name, input.repoFullName), + eq(code_review_memory_proposals.dedupe_key, input.dedupeKey), + inArray(code_review_memory_proposals.status, activeStatuses) + ) + ) + .limit(1); + + const values = { + platform_integration_id: input.platformIntegrationId ?? null, + platform_project_id: input.platformProjectId ?? null, + aggregation_run_id: input.aggregationRunId ?? null, + target_file_path: input.targetFilePath ?? 'REVIEW.md', + scope_kind: input.scopeKind, + scope_value: input.scopeValue ?? null, + proposal_type: input.proposalType, + title: input.title, + rationale: input.rationale, + proposed_markdown: input.proposedMarkdown, + llm_confidence: input.llmConfidence ?? null, + positive_count: input.positiveCount ?? 0, + negative_count: input.negativeCount ?? 0, + neutral_count: input.neutralCount ?? 0, + distinct_pr_count: input.distinctPrCount ?? 0, + distinct_subject_count: input.distinctSubjectCount ?? 0, + contradictory_count: input.contradictoryCount ?? 0, + updated_at: new Date().toISOString(), + }; + + const proposal = existing + ? await updateProposalById(database, existing.id, values) + : await insertProposal(database, input, values); + + if (input.evidence?.length) { + await linkProposalEvidence({ + proposalId: proposal.id, + evidence: input.evidence, + database, + }); + } + + return proposal; +} + +async function insertProposal( + database: ReviewMemoryDatabase, + input: UpsertProposalInput, + values: Partial +): Promise { + const [proposal] = await database + .insert(code_review_memory_proposals) + .values({ + ...ownerColumns(input.owner), + platform: input.platform, + repo_full_name: input.repoFullName, + proposal_type: input.proposalType, + title: input.title, + rationale: input.rationale, + proposed_markdown: input.proposedMarkdown, + dedupe_key: input.dedupeKey, + ...values, + }) + .returning(); + + if (!proposal) throw new Error('Failed to insert review memory proposal'); + return proposal; +} + +async function updateProposalById( + database: ReviewMemoryDatabase, + proposalId: string, + values: Partial +): Promise { + const [proposal] = await database + .update(code_review_memory_proposals) + .set(values) + .where(eq(code_review_memory_proposals.id, proposalId)) + .returning(); + + if (!proposal) throw new Error('Failed to update review memory proposal'); + return proposal; +} + +export async function linkProposalEvidence(input: { + proposalId: string; + evidence: { feedbackEventId: string; role: ReviewMemoryEvidenceRole }[]; + database?: ReviewMemoryDatabase; +}): Promise { + if (input.evidence.length === 0) return; + const database = databaseOrDefault(input.database); + await database + .insert(code_review_memory_proposal_evidence) + .values( + input.evidence.map(item => ({ + proposal_id: input.proposalId, + feedback_event_id: item.feedbackEventId, + evidence_role: item.role, + })) + ) + .onConflictDoNothing(); +} + +export async function listFeedbackEventsForAggregation(input: { + owner: ReviewMemoryOwner; + platform: ReviewMemoryPlatform; + repoFullName: string; + limit?: number; + database?: ReviewMemoryDatabase; +}): Promise<(CodeReviewFeedbackEvent & { subject: CodeReviewFeedbackSubject | null })[]> { + const database = databaseOrDefault(input.database); + const rows = await database + .select({ + event: code_review_feedback_events, + subject: code_review_feedback_subjects, + }) + .from(code_review_feedback_events) + .leftJoin( + code_review_feedback_subjects, + eq(code_review_feedback_events.subject_id, code_review_feedback_subjects.id) + ) + .where( + and( + eventOwnerWhere(input.owner), + eq(code_review_feedback_events.platform, input.platform), + eq(code_review_feedback_events.repo_full_name, input.repoFullName), + eq(code_review_feedback_events.aggregation_state, 'fresh') + ) + ) + .orderBy(desc(code_review_feedback_events.created_at)) + .limit(Math.min(input.limit ?? 200, 500)); + + return rows.map(row => ({ ...row.event, subject: row.subject })); +} + +export async function markFeedbackEventsIncluded(input: { + eventIds: string[]; + database?: ReviewMemoryDatabase; +}): Promise { + if (input.eventIds.length === 0) return; + const database = databaseOrDefault(input.database); + await database + .update(code_review_feedback_events) + .set({ aggregation_state: 'included' }) + .where(inArray(code_review_feedback_events.id, input.eventIds)); +} + +export async function listAggregationStates(input: { + owner: ReviewMemoryOwner; + platform: ReviewMemoryPlatform; + repoFullName?: string; + database?: ReviewMemoryDatabase; +}): Promise { + const database = databaseOrDefault(input.database); + const conditions: SQL[] = [ + aggregationStateOwnerWhere(input.owner), + eq(code_review_memory_aggregation_state.platform, input.platform), + ]; + if (input.repoFullName) { + conditions.push(eq(code_review_memory_aggregation_state.repo_full_name, input.repoFullName)); + } + + return await database + .select() + .from(code_review_memory_aggregation_state) + .where(and(...conditions)) + .orderBy(desc(code_review_memory_aggregation_state.updated_at)); +} + +export async function listReviewMemoryRepositories(input: { + owner: ReviewMemoryOwner; + platform: ReviewMemoryPlatform; + database?: ReviewMemoryDatabase; +}): Promise<{ repoFullName: string; platformProjectId: number | null; freshEventCount: number }[]> { + const database = databaseOrDefault(input.database); + const rows = await database + .select({ + repoFullName: code_review_memory_aggregation_state.repo_full_name, + platformProjectId: code_review_memory_aggregation_state.platform_project_id, + freshEventCount: code_review_memory_aggregation_state.fresh_event_count, + }) + .from(code_review_memory_aggregation_state) + .where( + and( + aggregationStateOwnerWhere(input.owner), + eq(code_review_memory_aggregation_state.platform, input.platform) + ) + ) + .orderBy(desc(code_review_memory_aggregation_state.updated_at)); + + return rows; +} + +export async function countActionableProposals(input: { + owner: ReviewMemoryOwner; + platform: ReviewMemoryPlatform; + repoFullName?: string; + database?: ReviewMemoryDatabase; +}): Promise { + const database = databaseOrDefault(input.database); + const conditions: SQL[] = [ + proposalOwnerWhere(input.owner), + eq(code_review_memory_proposals.platform, input.platform), + inArray(code_review_memory_proposals.status, ACTIONABLE_REVIEW_MEMORY_PROPOSAL_STATUSES), + ]; + if (input.repoFullName) { + conditions.push(eq(code_review_memory_proposals.repo_full_name, input.repoFullName)); + } + + const [row] = await database + .select({ count: sql`COUNT(*)::int` }) + .from(code_review_memory_proposals) + .where(and(...conditions)); + + return row?.count ?? 0; +} + +export async function listReviewMemoryProposals(input: { + owner: ReviewMemoryOwner; + platform: ReviewMemoryPlatform; + repoFullName?: string; + statuses?: ReviewMemoryProposalStatus[]; + proposalType?: ReviewMemoryProposalType; + limit?: number; + offset?: number; + database?: ReviewMemoryDatabase; +}): Promise { + const database = databaseOrDefault(input.database); + const conditions: SQL[] = [ + proposalOwnerWhere(input.owner), + eq(code_review_memory_proposals.platform, input.platform), + ]; + if (input.repoFullName) + conditions.push(eq(code_review_memory_proposals.repo_full_name, input.repoFullName)); + if (input.statuses?.length) + conditions.push(inArray(code_review_memory_proposals.status, input.statuses)); + if (input.proposalType) + conditions.push(eq(code_review_memory_proposals.proposal_type, input.proposalType)); + + return await database + .select() + .from(code_review_memory_proposals) + .where(and(...conditions)) + .orderBy(desc(code_review_memory_proposals.updated_at)) + .limit(Math.min(input.limit ?? 50, 100)) + .offset(input.offset ?? 0); +} + +export async function getReviewMemoryProposal(input: { + owner: ReviewMemoryOwner; + proposalId: string; + database?: ReviewMemoryDatabase; +}): Promise { + const database = databaseOrDefault(input.database); + const [proposal] = await database + .select() + .from(code_review_memory_proposals) + .where( + and(proposalOwnerWhere(input.owner), eq(code_review_memory_proposals.id, input.proposalId)) + ) + .limit(1); + + return proposal ?? null; +} + +export async function listProposalEvidence(input: { + proposalId: string; + database?: ReviewMemoryDatabase; +}): Promise< + { + feedbackEvent: CodeReviewFeedbackEvent; + subject: CodeReviewFeedbackSubject | null; + role: ReviewMemoryEvidenceRole; + }[] +> { + const database = databaseOrDefault(input.database); + const rows = await database + .select({ + evidenceRole: code_review_memory_proposal_evidence.evidence_role, + feedbackEvent: code_review_feedback_events, + subject: code_review_feedback_subjects, + }) + .from(code_review_memory_proposal_evidence) + .innerJoin( + code_review_feedback_events, + eq(code_review_memory_proposal_evidence.feedback_event_id, code_review_feedback_events.id) + ) + .leftJoin( + code_review_feedback_subjects, + eq(code_review_feedback_events.subject_id, code_review_feedback_subjects.id) + ) + .where(eq(code_review_memory_proposal_evidence.proposal_id, input.proposalId)) + .orderBy(desc(code_review_feedback_events.created_at)); + + return rows.map(row => ({ + feedbackEvent: row.feedbackEvent, + subject: row.subject, + role: row.evidenceRole, + })); +} + +export async function updateReviewMemoryProposal(input: { + owner: ReviewMemoryOwner; + proposalId: string; + editedByUserId: string; + title: string; + rationale: string; + proposedMarkdown: string; + scopeKind: ReviewMemoryProposalScopeKind; + scopeValue?: string | null; + database?: ReviewMemoryDatabase; +}): Promise { + const database = databaseOrDefault(input.database); + const [proposal] = await database + .update(code_review_memory_proposals) + .set({ + title: input.title, + rationale: input.rationale, + proposed_markdown: input.proposedMarkdown, + scope_kind: input.scopeKind, + scope_value: input.scopeValue ?? null, + status: 'edited', + edited_by_user_id: input.editedByUserId, + updated_at: new Date().toISOString(), + }) + .where( + and(proposalOwnerWhere(input.owner), eq(code_review_memory_proposals.id, input.proposalId)) + ) + .returning(); + + return proposal ?? null; +} + +export async function rejectReviewMemoryProposal(input: { + owner: ReviewMemoryOwner; + proposalId: string; + rejectedByUserId: string; + database?: ReviewMemoryDatabase; +}): Promise { + const database = databaseOrDefault(input.database); + const [proposal] = await database + .update(code_review_memory_proposals) + .set({ + status: 'rejected', + rejected_by_user_id: input.rejectedByUserId, + rejected_at: new Date().toISOString(), + updated_at: new Date().toISOString(), + }) + .where( + and(proposalOwnerWhere(input.owner), eq(code_review_memory_proposals.id, input.proposalId)) + ) + .returning(); + + return proposal ?? null; +} + +export async function markProposalOpeningChangeRequest(input: { + owner: ReviewMemoryOwner; + proposalId: string; + approvedByUserId: string; + changeRequestType: ReviewMemoryChangeRequestType; + branchName: string; + database?: ReviewMemoryDatabase; +}): Promise { + const database = databaseOrDefault(input.database); + const [proposal] = await database + .update(code_review_memory_proposals) + .set({ + status: 'opening_change_request', + approved_by_user_id: input.approvedByUserId, + approved_at: new Date().toISOString(), + change_request_type: input.changeRequestType, + branch_name: input.branchName, + change_request_error_message: null, + updated_at: new Date().toISOString(), + }) + .where( + and(proposalOwnerWhere(input.owner), eq(code_review_memory_proposals.id, input.proposalId)) + ) + .returning(); + + return proposal ?? null; +} + +export async function markProposalChangeRequestOpened(input: { + proposalId: string; + changeRequestNumber: number; + changeRequestUrl: string; + database?: ReviewMemoryDatabase; +}): Promise { + const database = databaseOrDefault(input.database); + const [proposal] = await database + .update(code_review_memory_proposals) + .set({ + status: 'change_request_opened', + change_request_number: input.changeRequestNumber, + change_request_url: input.changeRequestUrl, + change_request_error_message: null, + updated_at: new Date().toISOString(), + }) + .where(eq(code_review_memory_proposals.id, input.proposalId)) + .returning(); + + if (!proposal) throw new Error('Failed to mark review memory change request opened'); + return proposal; +} + +export async function markProposalChangeRequestFailed(input: { + proposalId: string; + errorMessage: string; + database?: ReviewMemoryDatabase; +}): Promise { + const database = databaseOrDefault(input.database); + const [proposal] = await database + .update(code_review_memory_proposals) + .set({ + status: 'change_request_failed', + change_request_error_message: compactText(input.errorMessage, 1_000), + updated_at: new Date().toISOString(), + }) + .where(eq(code_review_memory_proposals.id, input.proposalId)) + .returning(); + + if (!proposal) throw new Error('Failed to mark review memory change request failed'); + return proposal; +} + +export async function markProposalSuperseded(input: { + proposalId: string; + database?: ReviewMemoryDatabase; +}): Promise { + const database = databaseOrDefault(input.database); + const [proposal] = await database + .update(code_review_memory_proposals) + .set({ + status: 'superseded', + updated_at: new Date().toISOString(), + }) + .where(eq(code_review_memory_proposals.id, input.proposalId)) + .returning(); + + if (!proposal) throw new Error('Failed to mark review memory proposal superseded'); + return proposal; +} diff --git a/apps/web/src/lib/user/index.test.ts b/apps/web/src/lib/user/index.test.ts index 507942ebde..9babaf6e3e 100644 --- a/apps/web/src/lib/user/index.test.ts +++ b/apps/web/src/lib/user/index.test.ts @@ -67,6 +67,12 @@ import { impact_conversion_reports, github_branch_pull_requests, model_eval_ingestions, + code_review_feedback_subjects, + code_review_feedback_events, + code_review_memory_aggregation_state, + code_review_memory_aggregation_runs, + code_review_memory_proposals, + code_review_memory_proposal_evidence, } from '@kilocode/db/schema'; import { eq, count, sql } from 'drizzle-orm'; import { @@ -137,6 +143,12 @@ describe('User', () => { await db.delete(security_audit_log); await db.delete(kiloclaw_admin_audit_logs); await db.delete(model_eval_ingestions); + await db.delete(code_review_memory_proposal_evidence); + await db.delete(code_review_memory_proposals); + await db.delete(code_review_feedback_events); + await db.delete(code_review_feedback_subjects); + await db.delete(code_review_memory_aggregation_runs); + await db.delete(code_review_memory_aggregation_state); await db.delete(kiloclaw_scheduled_action_targets); await db.delete(kiloclaw_scheduled_action_stages); await db.delete(kiloclaw_scheduled_actions); @@ -449,6 +461,144 @@ describe('User', () => { expect(softDeleted!.stripe_customer_id).toBe(user.stripe_customer_id); }); + it('should delete user-owned review memory and anonymize retained proposal actors', async () => { + const user = await insertTestUser({ google_user_email: 'review-memory-delete@example.com' }); + const orgId = randomUUID(); + + await db.insert(organizations).values({ + id: orgId, + name: 'Review Memory Soft Delete Org', + stripe_customer_id: `stripe-org-${orgId}`, + plan: 'teams', + }); + + const [subject] = await db + .insert(code_review_feedback_subjects) + .values({ + owned_by_user_id: user.id, + platform: 'github', + subject_type: 'summary_comment', + external_id: 'summary-1', + repo_full_name: 'owner/repo', + state: 'active', + }) + .returning(); + const [event] = await db + .insert(code_review_feedback_events) + .values({ + owned_by_user_id: user.id, + platform: 'github', + subject_id: subject.id, + repo_full_name: 'owner/repo', + event_source: 'sync', + signal_kind: 'negative_reaction', + sentiment: 'negative', + strength: 2, + dedupe_hash: `soft-delete-${randomUUID()}`, + }) + .returning(); + const [run] = await db + .insert(code_review_memory_aggregation_runs) + .values({ + owned_by_user_id: user.id, + platform: 'github', + repo_full_name: 'owner/repo', + model_slug: 'test/model', + trigger: 'manual', + }) + .returning(); + const [proposal] = await db + .insert(code_review_memory_proposals) + .values({ + owned_by_user_id: user.id, + platform: 'github', + repo_full_name: 'owner/repo', + aggregation_run_id: run.id, + proposal_type: 'suppress', + title: 'Reduce noisy comments', + rationale: 'Maintainers rejected repeated findings.', + proposed_markdown: '### Review guidance: Reduce noisy comments', + dedupe_key: `user-owned-${randomUUID()}`, + }) + .returning(); + await db.insert(code_review_memory_proposal_evidence).values({ + proposal_id: proposal.id, + feedback_event_id: event.id, + evidence_role: 'primary', + }); + await db.insert(code_review_memory_aggregation_state).values({ + owned_by_user_id: user.id, + platform: 'github', + repo_full_name: 'owner/repo', + fresh_event_count: 1, + fresh_weight: 2, + }); + + const [orgProposal] = await db + .insert(code_review_memory_proposals) + .values({ + owned_by_organization_id: orgId, + platform: 'github', + repo_full_name: 'owner/repo', + proposal_type: 'clarify', + title: 'Clarify guidance', + rationale: 'Org-owned proposal keeps aggregate data.', + proposed_markdown: '### Review guidance: Clarify guidance', + dedupe_key: `org-owned-${randomUUID()}`, + edited_by_user_id: user.id, + approved_by_user_id: user.id, + rejected_by_user_id: user.id, + }) + .returning(); + + await softDeleteUser(user.id); + + await expect( + db + .select({ count: count() }) + .from(code_review_feedback_subjects) + .where(eq(code_review_feedback_subjects.owned_by_user_id, user.id)) + .then(r => r[0].count) + ).resolves.toBe(0); + await expect( + db + .select({ count: count() }) + .from(code_review_feedback_events) + .where(eq(code_review_feedback_events.owned_by_user_id, user.id)) + .then(r => r[0].count) + ).resolves.toBe(0); + await expect( + db + .select({ count: count() }) + .from(code_review_memory_proposals) + .where(eq(code_review_memory_proposals.owned_by_user_id, user.id)) + .then(r => r[0].count) + ).resolves.toBe(0); + await expect( + db + .select({ count: count() }) + .from(code_review_memory_aggregation_runs) + .where(eq(code_review_memory_aggregation_runs.owned_by_user_id, user.id)) + .then(r => r[0].count) + ).resolves.toBe(0); + await expect( + db + .select({ count: count() }) + .from(code_review_memory_aggregation_state) + .where(eq(code_review_memory_aggregation_state.owned_by_user_id, user.id)) + .then(r => r[0].count) + ).resolves.toBe(0); + + const [retainedOrgProposal] = await db + .select() + .from(code_review_memory_proposals) + .where(eq(code_review_memory_proposals.id, orgProposal.id)); + expect(retainedOrgProposal).toBeDefined(); + expect(retainedOrgProposal.edited_by_user_id).toBeNull(); + expect(retainedOrgProposal.approved_by_user_id).toBeNull(); + expect(retainedOrgProposal.rejected_by_user_id).toBeNull(); + }); + it('should rotate and scrub App Store account-linked Kilo Pass data', async () => { const user = await insertTestUser({ google_user_email: 'app-store-delete@example.com', diff --git a/apps/web/src/lib/user/index.ts b/apps/web/src/lib/user/index.ts index 6b260cabbb..53c762ff57 100644 --- a/apps/web/src/lib/user/index.ts +++ b/apps/web/src/lib/user/index.ts @@ -46,6 +46,11 @@ import { slack_bot_requests, bot_requests, cloud_agent_code_reviews, + code_review_feedback_subjects, + code_review_feedback_events, + code_review_memory_aggregation_state, + code_review_memory_aggregation_runs, + code_review_memory_proposals, kiloclaw_instances, kiloclaw_composio_identities, kiloclaw_google_oauth_connections, @@ -1030,6 +1035,21 @@ export async function softDeleteUser(userId: string) { await tx .delete(cloud_agent_code_reviews) .where(eq(cloud_agent_code_reviews.owned_by_user_id, userId)); + await tx + .delete(code_review_memory_proposals) + .where(eq(code_review_memory_proposals.owned_by_user_id, userId)); + await tx + .delete(code_review_feedback_events) + .where(eq(code_review_feedback_events.owned_by_user_id, userId)); + await tx + .delete(code_review_feedback_subjects) + .where(eq(code_review_feedback_subjects.owned_by_user_id, userId)); + await tx + .delete(code_review_memory_aggregation_runs) + .where(eq(code_review_memory_aggregation_runs.owned_by_user_id, userId)); + await tx + .delete(code_review_memory_aggregation_state) + .where(eq(code_review_memory_aggregation_state.owned_by_user_id, userId)); await tx.delete(device_auth_requests).where(eq(device_auth_requests.kilo_user_id, userId)); await tx.delete(auto_top_up_configs).where(eq(auto_top_up_configs.owned_by_user_id, userId)); await tx.delete(kiloclaw_access_codes).where(eq(kiloclaw_access_codes.kilo_user_id, userId)); @@ -1073,6 +1093,19 @@ export async function softDeleteUser(userId: string) { .delete(github_branch_pull_requests) .where(eq(github_branch_pull_requests.owned_by_user_id, userId)); + await tx + .update(code_review_memory_proposals) + .set({ edited_by_user_id: null }) + .where(eq(code_review_memory_proposals.edited_by_user_id, userId)); + await tx + .update(code_review_memory_proposals) + .set({ approved_by_user_id: null }) + .where(eq(code_review_memory_proposals.approved_by_user_id, userId)); + await tx + .update(code_review_memory_proposals) + .set({ rejected_by_user_id: null }) + .where(eq(code_review_memory_proposals.rejected_by_user_id, userId)); + // Code indexing data await tx.delete(source_embeddings).where(eq(source_embeddings.kilo_user_id, userId)); await tx.delete(code_indexing_search).where(eq(code_indexing_search.kilo_user_id, userId)); diff --git a/packages/db/src/migrations/0141_chunky_captain_america.sql b/packages/db/src/migrations/0141_chunky_captain_america.sql new file mode 100644 index 0000000000..afa0ffe1ec --- /dev/null +++ b/packages/db/src/migrations/0141_chunky_captain_america.sql @@ -0,0 +1,234 @@ +CREATE TABLE "code_review_feedback_events" ( + "id" uuid PRIMARY KEY DEFAULT pg_catalog.gen_random_uuid() NOT NULL, + "owned_by_organization_id" uuid, + "owned_by_user_id" text, + "platform" text NOT NULL, + "platform_integration_id" uuid, + "subject_id" uuid, + "code_review_id" uuid, + "repo_full_name" text NOT NULL, + "platform_project_id" integer, + "pr_number" integer, + "pr_url" text, + "event_source" text NOT NULL, + "signal_kind" text NOT NULL, + "sentiment" text NOT NULL, + "strength" smallint DEFAULT 1 NOT NULL, + "external_event_id" text, + "dedupe_hash" text NOT NULL, + "external_url" text, + "evidence_excerpt" text, + "metadata" jsonb DEFAULT '{}'::jsonb NOT NULL, + "aggregation_state" text DEFAULT 'fresh' NOT NULL, + "occurred_at" timestamp with time zone DEFAULT now() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "code_review_feedback_events_owner_check" CHECK (( + ("code_review_feedback_events"."owned_by_user_id" IS NOT NULL AND "code_review_feedback_events"."owned_by_organization_id" IS NULL) OR + ("code_review_feedback_events"."owned_by_user_id" IS NULL AND "code_review_feedback_events"."owned_by_organization_id" IS NOT NULL) + )), + CONSTRAINT "code_review_feedback_events_strength_check" CHECK ("code_review_feedback_events"."strength" > 0) +); +--> statement-breakpoint +CREATE TABLE "code_review_feedback_subjects" ( + "id" uuid PRIMARY KEY DEFAULT pg_catalog.gen_random_uuid() NOT NULL, + "owned_by_organization_id" uuid, + "owned_by_user_id" text, + "platform" text NOT NULL, + "platform_integration_id" uuid, + "code_review_id" uuid, + "subject_type" text NOT NULL, + "external_id" text NOT NULL, + "external_thread_id" text, + "external_url" text, + "repo_full_name" text NOT NULL, + "platform_project_id" integer, + "pr_number" integer, + "pr_url" text, + "head_sha" text, + "file_path" text, + "line_number" integer, + "diff_hunk" text, + "body_excerpt" text, + "severity" text, + "finding_title" text, + "finding_fingerprint" text, + "state" text DEFAULT 'unknown' NOT NULL, + "first_seen_at" timestamp with time zone DEFAULT now() NOT NULL, + "last_seen_at" timestamp with time zone DEFAULT now() NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "code_review_feedback_subjects_owner_check" CHECK (( + ("code_review_feedback_subjects"."owned_by_user_id" IS NOT NULL AND "code_review_feedback_subjects"."owned_by_organization_id" IS NULL) OR + ("code_review_feedback_subjects"."owned_by_user_id" IS NULL AND "code_review_feedback_subjects"."owned_by_organization_id" IS NOT NULL) + )) +); +--> statement-breakpoint +CREATE TABLE "code_review_memory_aggregation_runs" ( + "id" uuid PRIMARY KEY DEFAULT pg_catalog.gen_random_uuid() NOT NULL, + "owned_by_organization_id" uuid, + "owned_by_user_id" text, + "platform" text NOT NULL, + "repo_full_name" text NOT NULL, + "platform_project_id" integer, + "model_slug" text NOT NULL, + "trigger" text NOT NULL, + "input_event_count" integer DEFAULT 0 NOT NULL, + "input_subject_count" integer DEFAULT 0 NOT NULL, + "input_cluster_count" integer DEFAULT 0 NOT NULL, + "fresh_event_cutoff_at" timestamp with time zone, + "status" text DEFAULT 'running' NOT NULL, + "skip_reason" text, + "tokens_in" integer, + "tokens_out" integer, + "total_cost_musd" integer, + "error_message" text, + "started_at" timestamp with time zone DEFAULT now() NOT NULL, + "completed_at" timestamp with time zone, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "code_review_memory_aggregation_runs_owner_check" CHECK (( + ("code_review_memory_aggregation_runs"."owned_by_user_id" IS NOT NULL AND "code_review_memory_aggregation_runs"."owned_by_organization_id" IS NULL) OR + ("code_review_memory_aggregation_runs"."owned_by_user_id" IS NULL AND "code_review_memory_aggregation_runs"."owned_by_organization_id" IS NOT NULL) + )) +); +--> statement-breakpoint +CREATE TABLE "code_review_memory_aggregation_state" ( + "id" uuid PRIMARY KEY DEFAULT pg_catalog.gen_random_uuid() NOT NULL, + "owned_by_organization_id" uuid, + "owned_by_user_id" text, + "platform" text NOT NULL, + "repo_full_name" text NOT NULL, + "platform_project_id" integer, + "last_successful_run_at" timestamp with time zone, + "last_attempted_run_at" timestamp with time zone, + "last_included_event_created_at" timestamp with time zone, + "fresh_event_count" integer DEFAULT 0 NOT NULL, + "fresh_weight" integer DEFAULT 0 NOT NULL, + "fresh_distinct_subject_count" integer DEFAULT 0 NOT NULL, + "fresh_distinct_pr_count" integer DEFAULT 0 NOT NULL, + "next_eligible_at" timestamp with time zone DEFAULT now() NOT NULL, + "status" text DEFAULT 'idle' NOT NULL, + "claimed_at" timestamp with time zone, + "claim_token" text, + "last_model_slug" text, + "last_error_message" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "code_review_memory_aggregation_state_owner_check" CHECK (( + ("code_review_memory_aggregation_state"."owned_by_user_id" IS NOT NULL AND "code_review_memory_aggregation_state"."owned_by_organization_id" IS NULL) OR + ("code_review_memory_aggregation_state"."owned_by_user_id" IS NULL AND "code_review_memory_aggregation_state"."owned_by_organization_id" IS NOT NULL) + )) +); +--> statement-breakpoint +CREATE TABLE "code_review_memory_proposal_evidence" ( + "proposal_id" uuid NOT NULL, + "feedback_event_id" uuid NOT NULL, + "evidence_role" text DEFAULT 'supporting' NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "code_review_memory_proposal_evidence_proposal_id_feedback_event_id_pk" PRIMARY KEY("proposal_id","feedback_event_id") +); +--> statement-breakpoint +CREATE TABLE "code_review_memory_proposals" ( + "id" uuid PRIMARY KEY DEFAULT pg_catalog.gen_random_uuid() NOT NULL, + "owned_by_organization_id" uuid, + "owned_by_user_id" text, + "platform" text NOT NULL, + "platform_integration_id" uuid, + "repo_full_name" text NOT NULL, + "platform_project_id" integer, + "aggregation_run_id" uuid, + "target_file_path" text DEFAULT 'REVIEW.md' NOT NULL, + "scope_kind" text DEFAULT 'repository' NOT NULL, + "scope_value" text, + "proposal_type" text NOT NULL, + "status" text DEFAULT 'open' NOT NULL, + "title" text NOT NULL, + "rationale" text NOT NULL, + "proposed_markdown" text NOT NULL, + "dedupe_key" text NOT NULL, + "llm_confidence" real, + "positive_count" integer DEFAULT 0 NOT NULL, + "negative_count" integer DEFAULT 0 NOT NULL, + "neutral_count" integer DEFAULT 0 NOT NULL, + "distinct_pr_count" integer DEFAULT 0 NOT NULL, + "distinct_subject_count" integer DEFAULT 0 NOT NULL, + "contradictory_count" integer DEFAULT 0 NOT NULL, + "edited_by_user_id" text, + "approved_by_user_id" text, + "rejected_by_user_id" text, + "approved_at" timestamp with time zone, + "rejected_at" timestamp with time zone, + "change_request_type" text, + "branch_name" text, + "change_request_number" integer, + "change_request_url" text, + "change_request_error_message" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "code_review_memory_proposals_owner_check" CHECK (( + ("code_review_memory_proposals"."owned_by_user_id" IS NOT NULL AND "code_review_memory_proposals"."owned_by_organization_id" IS NULL) OR + ("code_review_memory_proposals"."owned_by_user_id" IS NULL AND "code_review_memory_proposals"."owned_by_organization_id" IS NOT NULL) + )) +); +--> statement-breakpoint +ALTER TABLE "code_review_feedback_events" ADD CONSTRAINT "code_review_feedback_events_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 "code_review_feedback_events" ADD CONSTRAINT "code_review_feedback_events_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 "code_review_feedback_events" ADD CONSTRAINT "code_review_feedback_events_platform_integration_id_platform_integrations_id_fk" FOREIGN KEY ("platform_integration_id") REFERENCES "public"."platform_integrations"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "code_review_feedback_events" ADD CONSTRAINT "code_review_feedback_events_subject_id_code_review_feedback_subjects_id_fk" FOREIGN KEY ("subject_id") REFERENCES "public"."code_review_feedback_subjects"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "code_review_feedback_events" ADD CONSTRAINT "code_review_feedback_events_code_review_id_cloud_agent_code_reviews_id_fk" FOREIGN KEY ("code_review_id") REFERENCES "public"."cloud_agent_code_reviews"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "code_review_feedback_subjects" ADD CONSTRAINT "code_review_feedback_subjects_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 "code_review_feedback_subjects" ADD CONSTRAINT "code_review_feedback_subjects_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 "code_review_feedback_subjects" ADD CONSTRAINT "code_review_feedback_subjects_platform_integration_id_platform_integrations_id_fk" FOREIGN KEY ("platform_integration_id") REFERENCES "public"."platform_integrations"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "code_review_feedback_subjects" ADD CONSTRAINT "code_review_feedback_subjects_code_review_id_cloud_agent_code_reviews_id_fk" FOREIGN KEY ("code_review_id") REFERENCES "public"."cloud_agent_code_reviews"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "code_review_memory_aggregation_runs" ADD CONSTRAINT "code_review_memory_aggregation_runs_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 "code_review_memory_aggregation_runs" ADD CONSTRAINT "code_review_memory_aggregation_runs_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 "code_review_memory_aggregation_state" ADD CONSTRAINT "code_review_memory_aggregation_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 "code_review_memory_aggregation_state" ADD CONSTRAINT "code_review_memory_aggregation_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 "code_review_memory_proposal_evidence" ADD CONSTRAINT "code_review_memory_proposal_evidence_proposal_id_code_review_memory_proposals_id_fk" FOREIGN KEY ("proposal_id") REFERENCES "public"."code_review_memory_proposals"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "code_review_memory_proposal_evidence" ADD CONSTRAINT "code_review_memory_proposal_evidence_feedback_event_id_code_review_feedback_events_id_fk" FOREIGN KEY ("feedback_event_id") REFERENCES "public"."code_review_feedback_events"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "code_review_memory_proposals" ADD CONSTRAINT "code_review_memory_proposals_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 "code_review_memory_proposals" ADD CONSTRAINT "code_review_memory_proposals_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 "code_review_memory_proposals" ADD CONSTRAINT "code_review_memory_proposals_platform_integration_id_platform_integrations_id_fk" FOREIGN KEY ("platform_integration_id") REFERENCES "public"."platform_integrations"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "code_review_memory_proposals" ADD CONSTRAINT "code_review_memory_proposals_aggregation_run_id_code_review_memory_aggregation_runs_id_fk" FOREIGN KEY ("aggregation_run_id") REFERENCES "public"."code_review_memory_aggregation_runs"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "code_review_memory_proposals" ADD CONSTRAINT "code_review_memory_proposals_edited_by_user_id_kilocode_users_id_fk" FOREIGN KEY ("edited_by_user_id") REFERENCES "public"."kilocode_users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "code_review_memory_proposals" ADD CONSTRAINT "code_review_memory_proposals_approved_by_user_id_kilocode_users_id_fk" FOREIGN KEY ("approved_by_user_id") REFERENCES "public"."kilocode_users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "code_review_memory_proposals" ADD CONSTRAINT "code_review_memory_proposals_rejected_by_user_id_kilocode_users_id_fk" FOREIGN KEY ("rejected_by_user_id") REFERENCES "public"."kilocode_users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +CREATE UNIQUE INDEX "UQ_code_review_feedback_events_external_event_id" ON "code_review_feedback_events" USING btree ("external_event_id") WHERE "code_review_feedback_events"."external_event_id" is not null;--> statement-breakpoint +CREATE UNIQUE INDEX "UQ_code_review_feedback_events_dedupe_hash" ON "code_review_feedback_events" USING btree ("dedupe_hash");--> statement-breakpoint +CREATE INDEX "idx_code_review_feedback_events_owned_by_org_id" ON "code_review_feedback_events" USING btree ("owned_by_organization_id");--> statement-breakpoint +CREATE INDEX "idx_code_review_feedback_events_owned_by_user_id" ON "code_review_feedback_events" USING btree ("owned_by_user_id");--> statement-breakpoint +CREATE INDEX "idx_code_review_feedback_events_platform_repo" ON "code_review_feedback_events" USING btree ("platform","repo_full_name");--> statement-breakpoint +CREATE INDEX "idx_code_review_feedback_events_subject_id" ON "code_review_feedback_events" USING btree ("subject_id");--> statement-breakpoint +CREATE INDEX "idx_code_review_feedback_events_code_review_id" ON "code_review_feedback_events" USING btree ("code_review_id");--> statement-breakpoint +CREATE INDEX "idx_code_review_feedback_events_sentiment" ON "code_review_feedback_events" USING btree ("sentiment");--> statement-breakpoint +CREATE INDEX "idx_code_review_feedback_events_signal_kind" ON "code_review_feedback_events" USING btree ("signal_kind");--> statement-breakpoint +CREATE INDEX "idx_code_review_feedback_events_aggregation_state" ON "code_review_feedback_events" USING btree ("aggregation_state");--> statement-breakpoint +CREATE INDEX "idx_code_review_feedback_events_created_at" ON "code_review_feedback_events" USING btree ("created_at");--> statement-breakpoint +CREATE UNIQUE INDEX "UQ_code_review_feedback_subjects_platform_external" ON "code_review_feedback_subjects" USING btree ("platform","repo_full_name","subject_type","external_id");--> statement-breakpoint +CREATE INDEX "idx_code_review_feedback_subjects_owned_by_org_id" ON "code_review_feedback_subjects" USING btree ("owned_by_organization_id");--> statement-breakpoint +CREATE INDEX "idx_code_review_feedback_subjects_owned_by_user_id" ON "code_review_feedback_subjects" USING btree ("owned_by_user_id");--> statement-breakpoint +CREATE INDEX "idx_code_review_feedback_subjects_platform_repo" ON "code_review_feedback_subjects" USING btree ("platform","repo_full_name");--> statement-breakpoint +CREATE INDEX "idx_code_review_feedback_subjects_pr" ON "code_review_feedback_subjects" USING btree ("repo_full_name","pr_number");--> statement-breakpoint +CREATE INDEX "idx_code_review_feedback_subjects_code_review_id" ON "code_review_feedback_subjects" USING btree ("code_review_id");--> statement-breakpoint +CREATE INDEX "idx_code_review_feedback_subjects_fingerprint" ON "code_review_feedback_subjects" USING btree ("finding_fingerprint");--> statement-breakpoint +CREATE INDEX "idx_code_review_feedback_subjects_state" ON "code_review_feedback_subjects" USING btree ("state");--> statement-breakpoint +CREATE INDEX "idx_code_review_feedback_subjects_thread" ON "code_review_feedback_subjects" USING btree ("external_thread_id");--> statement-breakpoint +CREATE INDEX "idx_code_review_feedback_subjects_last_seen_at" ON "code_review_feedback_subjects" USING btree ("last_seen_at");--> statement-breakpoint +CREATE INDEX "idx_code_review_memory_aggregation_runs_owned_by_org_id" ON "code_review_memory_aggregation_runs" USING btree ("owned_by_organization_id");--> statement-breakpoint +CREATE INDEX "idx_code_review_memory_aggregation_runs_owned_by_user_id" ON "code_review_memory_aggregation_runs" USING btree ("owned_by_user_id");--> statement-breakpoint +CREATE INDEX "idx_code_review_memory_aggregation_runs_scope" ON "code_review_memory_aggregation_runs" USING btree ("platform","repo_full_name");--> statement-breakpoint +CREATE INDEX "idx_code_review_memory_aggregation_runs_status" ON "code_review_memory_aggregation_runs" USING btree ("status");--> statement-breakpoint +CREATE INDEX "idx_code_review_memory_aggregation_runs_created_at" ON "code_review_memory_aggregation_runs" USING btree ("created_at");--> statement-breakpoint +CREATE UNIQUE INDEX "UQ_code_review_memory_aggregation_state_org_scope" ON "code_review_memory_aggregation_state" USING btree ("owned_by_organization_id","platform","repo_full_name") WHERE "code_review_memory_aggregation_state"."owned_by_organization_id" is not null;--> statement-breakpoint +CREATE UNIQUE INDEX "UQ_code_review_memory_aggregation_state_user_scope" ON "code_review_memory_aggregation_state" USING btree ("owned_by_user_id","platform","repo_full_name") WHERE "code_review_memory_aggregation_state"."owned_by_user_id" is not null;--> statement-breakpoint +CREATE INDEX "idx_code_review_memory_aggregation_state_status" ON "code_review_memory_aggregation_state" USING btree ("status");--> statement-breakpoint +CREATE INDEX "idx_code_review_memory_aggregation_state_next_eligible_at" ON "code_review_memory_aggregation_state" USING btree ("next_eligible_at");--> statement-breakpoint +CREATE INDEX "idx_code_review_memory_aggregation_state_fresh_counts" ON "code_review_memory_aggregation_state" USING btree ("fresh_event_count","fresh_weight");--> statement-breakpoint +CREATE INDEX "idx_code_review_memory_proposal_evidence_feedback_event_id" ON "code_review_memory_proposal_evidence" USING btree ("feedback_event_id");--> statement-breakpoint +CREATE INDEX "idx_code_review_memory_proposal_evidence_role" ON "code_review_memory_proposal_evidence" USING btree ("evidence_role");--> statement-breakpoint +CREATE INDEX "idx_code_review_memory_proposals_owned_by_org_id" ON "code_review_memory_proposals" USING btree ("owned_by_organization_id");--> statement-breakpoint +CREATE INDEX "idx_code_review_memory_proposals_owned_by_user_id" ON "code_review_memory_proposals" USING btree ("owned_by_user_id");--> statement-breakpoint +CREATE INDEX "idx_code_review_memory_proposals_platform_repo_status" ON "code_review_memory_proposals" USING btree ("platform","repo_full_name","status");--> statement-breakpoint +CREATE INDEX "idx_code_review_memory_proposals_proposal_type" ON "code_review_memory_proposals" USING btree ("proposal_type");--> statement-breakpoint +CREATE INDEX "idx_code_review_memory_proposals_created_at" ON "code_review_memory_proposals" USING btree ("created_at");--> statement-breakpoint +CREATE UNIQUE INDEX "UQ_code_review_memory_proposals_org_active_dedupe" ON "code_review_memory_proposals" USING btree ("owned_by_organization_id","platform","repo_full_name","dedupe_key") WHERE "code_review_memory_proposals"."owned_by_organization_id" IS NOT NULL AND "code_review_memory_proposals"."status" IN ('open', 'edited', 'approved', 'opening_change_request', 'change_request_opened');--> statement-breakpoint +CREATE UNIQUE INDEX "UQ_code_review_memory_proposals_user_active_dedupe" ON "code_review_memory_proposals" USING btree ("owned_by_user_id","platform","repo_full_name","dedupe_key") WHERE "code_review_memory_proposals"."owned_by_user_id" IS NOT NULL AND "code_review_memory_proposals"."status" IN ('open', 'edited', 'approved', 'opening_change_request', 'change_request_opened'); \ No newline at end of file diff --git a/packages/db/src/migrations/meta/0141_snapshot.json b/packages/db/src/migrations/meta/0141_snapshot.json new file mode 100644 index 0000000000..672d0e6e56 --- /dev/null +++ b/packages/db/src/migrations/meta/0141_snapshot.json @@ -0,0 +1,25434 @@ +{ + "id": "f3cccce4-9fe2-49c1-919e-606e70f66549", + "prevId": "8164d94c-7070-484e-a062-84779df93922", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.agent_configs": { + "name": "agent_configs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "agent_type": { + "name": "agent_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "runtime_state": { + "name": "runtime_state", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_agent_configs_org_id": { + "name": "IDX_agent_configs_org_id", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_agent_configs_owned_by_user_id": { + "name": "IDX_agent_configs_owned_by_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_agent_configs_agent_type": { + "name": "IDX_agent_configs_agent_type", + "columns": [ + { + "expression": "agent_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_agent_configs_platform": { + "name": "IDX_agent_configs_platform", + "columns": [ + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_configs_owned_by_organization_id_organizations_id_fk": { + "name": "agent_configs_owned_by_organization_id_organizations_id_fk", + "tableFrom": "agent_configs", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "agent_configs_owned_by_user_id_kilocode_users_id_fk": { + "name": "agent_configs_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "agent_configs", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_agent_configs_org_agent_platform": { + "name": "UQ_agent_configs_org_agent_platform", + "nullsNotDistinct": false, + "columns": [ + "owned_by_organization_id", + "agent_type", + "platform" + ] + }, + "UQ_agent_configs_user_agent_platform": { + "name": "UQ_agent_configs_user_agent_platform", + "nullsNotDistinct": false, + "columns": [ + "owned_by_user_id", + "agent_type", + "platform" + ] + } + }, + "policies": {}, + "checkConstraints": { + "agent_configs_owner_check": { + "name": "agent_configs_owner_check", + "value": "(\n (\"agent_configs\".\"owned_by_user_id\" IS NOT NULL AND \"agent_configs\".\"owned_by_organization_id\" IS NULL) OR\n (\"agent_configs\".\"owned_by_user_id\" IS NULL AND \"agent_configs\".\"owned_by_organization_id\" IS NOT NULL)\n )" + }, + "agent_configs_agent_type_check": { + "name": "agent_configs_agent_type_check", + "value": "\"agent_configs\".\"agent_type\" IN ('code_review', 'auto_triage', 'auto_fix', 'security_scan')" + } + }, + "isRLSEnabled": false + }, + "public.agent_environment_profile_agents": { + "name": "agent_environment_profile_agents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "profile_id": { + "name": "profile_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_agent_env_profile_agents_profile_id": { + "name": "IDX_agent_env_profile_agents_profile_id", + "columns": [ + { + "expression": "profile_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_environment_profile_agents_profile_id_agent_environment_profiles_id_fk": { + "name": "agent_environment_profile_agents_profile_id_agent_environment_profiles_id_fk", + "tableFrom": "agent_environment_profile_agents", + "tableTo": "agent_environment_profiles", + "columnsFrom": [ + "profile_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_agent_env_profile_agents_profile_slug": { + "name": "UQ_agent_env_profile_agents_profile_slug", + "nullsNotDistinct": false, + "columns": [ + "profile_id", + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_environment_profile_commands": { + "name": "agent_environment_profile_commands", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "profile_id": { + "name": "profile_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sequence": { + "name": "sequence", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_agent_env_profile_commands_profile_id": { + "name": "IDX_agent_env_profile_commands_profile_id", + "columns": [ + { + "expression": "profile_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_environment_profile_commands_profile_id_agent_environment_profiles_id_fk": { + "name": "agent_environment_profile_commands_profile_id_agent_environment_profiles_id_fk", + "tableFrom": "agent_environment_profile_commands", + "tableTo": "agent_environment_profiles", + "columnsFrom": [ + "profile_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_agent_env_profile_commands_profile_sequence": { + "name": "UQ_agent_env_profile_commands_profile_sequence", + "nullsNotDistinct": false, + "columns": [ + "profile_id", + "sequence" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_environment_profile_kilo_commands": { + "name": "agent_environment_profile_kilo_commands", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "profile_id": { + "name": "profile_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "template": { + "name": "template", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "agent": { + "name": "agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "subtask": { + "name": "subtask", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_agent_env_profile_kilo_cmds_profile_id": { + "name": "IDX_agent_env_profile_kilo_cmds_profile_id", + "columns": [ + { + "expression": "profile_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_environment_profile_kilo_commands_profile_id_agent_environment_profiles_id_fk": { + "name": "agent_environment_profile_kilo_commands_profile_id_agent_environment_profiles_id_fk", + "tableFrom": "agent_environment_profile_kilo_commands", + "tableTo": "agent_environment_profiles", + "columnsFrom": [ + "profile_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_agent_env_profile_kilo_cmds_profile_name": { + "name": "UQ_agent_env_profile_kilo_cmds_profile_name", + "nullsNotDistinct": false, + "columns": [ + "profile_id", + "name" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_environment_profile_mcp_servers": { + "name": "agent_environment_profile_mcp_servers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "profile_id": { + "name": "profile_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "timeout": { + "name": "timeout", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_agent_env_profile_mcp_servers_profile_id": { + "name": "IDX_agent_env_profile_mcp_servers_profile_id", + "columns": [ + { + "expression": "profile_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_environment_profile_mcp_servers_profile_id_agent_environment_profiles_id_fk": { + "name": "agent_environment_profile_mcp_servers_profile_id_agent_environment_profiles_id_fk", + "tableFrom": "agent_environment_profile_mcp_servers", + "tableTo": "agent_environment_profiles", + "columnsFrom": [ + "profile_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_agent_env_profile_mcp_servers_profile_name": { + "name": "UQ_agent_env_profile_mcp_servers_profile_name", + "nullsNotDistinct": false, + "columns": [ + "profile_id", + "name" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_environment_profile_repo_bindings": { + "name": "agent_environment_profile_repo_bindings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "repo_full_name": { + "name": "repo_full_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "profile_id": { + "name": "profile_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_agent_env_profile_repo_bindings_user": { + "name": "UQ_agent_env_profile_repo_bindings_user", + "columns": [ + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"agent_environment_profile_repo_bindings\".\"owned_by_user_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_agent_env_profile_repo_bindings_org": { + "name": "UQ_agent_env_profile_repo_bindings_org", + "columns": [ + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"agent_environment_profile_repo_bindings\".\"owned_by_organization_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_environment_profile_repo_bindings_profile_id_agent_environment_profiles_id_fk": { + "name": "agent_environment_profile_repo_bindings_profile_id_agent_environment_profiles_id_fk", + "tableFrom": "agent_environment_profile_repo_bindings", + "tableTo": "agent_environment_profiles", + "columnsFrom": [ + "profile_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "agent_environment_profile_repo_bindings_owned_by_organization_id_organizations_id_fk": { + "name": "agent_environment_profile_repo_bindings_owned_by_organization_id_organizations_id_fk", + "tableFrom": "agent_environment_profile_repo_bindings", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "agent_environment_profile_repo_bindings_owned_by_user_id_kilocode_users_id_fk": { + "name": "agent_environment_profile_repo_bindings_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "agent_environment_profile_repo_bindings", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "agent_env_profile_repo_bindings_owner_check": { + "name": "agent_env_profile_repo_bindings_owner_check", + "value": "(\n (\"agent_environment_profile_repo_bindings\".\"owned_by_user_id\" IS NOT NULL AND \"agent_environment_profile_repo_bindings\".\"owned_by_organization_id\" IS NULL) OR\n (\"agent_environment_profile_repo_bindings\".\"owned_by_user_id\" IS NULL AND \"agent_environment_profile_repo_bindings\".\"owned_by_organization_id\" IS NOT NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.agent_environment_profile_skills": { + "name": "agent_environment_profile_skills", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "profile_id": { + "name": "profile_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_type": { + "name": "source_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_url": { + "name": "source_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "raw_markdown": { + "name": "raw_markdown", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "files": { + "name": "files", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_agent_env_profile_skills_profile_id": { + "name": "IDX_agent_env_profile_skills_profile_id", + "columns": [ + { + "expression": "profile_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_environment_profile_skills_profile_id_agent_environment_profiles_id_fk": { + "name": "agent_environment_profile_skills_profile_id_agent_environment_profiles_id_fk", + "tableFrom": "agent_environment_profile_skills", + "tableTo": "agent_environment_profiles", + "columnsFrom": [ + "profile_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_agent_env_profile_skills_profile_name": { + "name": "UQ_agent_env_profile_skills_profile_name", + "nullsNotDistinct": false, + "columns": [ + "profile_id", + "name" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_environment_profile_vars": { + "name": "agent_environment_profile_vars", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "profile_id": { + "name": "profile_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_secret": { + "name": "is_secret", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_agent_env_profile_vars_profile_id": { + "name": "IDX_agent_env_profile_vars_profile_id", + "columns": [ + { + "expression": "profile_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_environment_profile_vars_profile_id_agent_environment_profiles_id_fk": { + "name": "agent_environment_profile_vars_profile_id_agent_environment_profiles_id_fk", + "tableFrom": "agent_environment_profile_vars", + "tableTo": "agent_environment_profiles", + "columnsFrom": [ + "profile_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_agent_env_profile_vars_profile_key": { + "name": "UQ_agent_env_profile_vars_profile_key", + "nullsNotDistinct": false, + "columns": [ + "profile_id", + "key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.agent_environment_profiles": { + "name": "agent_environment_profiles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_agent_env_profiles_org_name": { + "name": "UQ_agent_env_profiles_org_name", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"agent_environment_profiles\".\"owned_by_organization_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_agent_env_profiles_user_name": { + "name": "UQ_agent_env_profiles_user_name", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"agent_environment_profiles\".\"owned_by_user_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_agent_env_profiles_org_default": { + "name": "UQ_agent_env_profiles_org_default", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"agent_environment_profiles\".\"is_default\" = true AND \"agent_environment_profiles\".\"owned_by_organization_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_agent_env_profiles_user_default": { + "name": "UQ_agent_env_profiles_user_default", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"agent_environment_profiles\".\"is_default\" = true AND \"agent_environment_profiles\".\"owned_by_user_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_agent_env_profiles_org_id": { + "name": "IDX_agent_env_profiles_org_id", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_agent_env_profiles_user_id": { + "name": "IDX_agent_env_profiles_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_agent_env_profiles_created_by_user_id": { + "name": "IDX_agent_env_profiles_created_by_user_id", + "columns": [ + { + "expression": "created_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "agent_environment_profiles_owned_by_organization_id_organizations_id_fk": { + "name": "agent_environment_profiles_owned_by_organization_id_organizations_id_fk", + "tableFrom": "agent_environment_profiles", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "agent_environment_profiles_owned_by_user_id_kilocode_users_id_fk": { + "name": "agent_environment_profiles_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "agent_environment_profiles", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "agent_env_profiles_owner_check": { + "name": "agent_env_profiles_owner_check", + "value": "(\n (\"agent_environment_profiles\".\"owned_by_user_id\" IS NOT NULL AND \"agent_environment_profiles\".\"owned_by_organization_id\" IS NULL) OR\n (\"agent_environment_profiles\".\"owned_by_user_id\" IS NULL AND \"agent_environment_profiles\".\"owned_by_organization_id\" IS NOT NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.api_kind": { + "name": "api_kind", + "schema": "", + "columns": { + "api_kind_id": { + "name": "api_kind_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "api_kind": { + "name": "api_kind", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UQ_api_kind": { + "name": "UQ_api_kind", + "columns": [ + { + "expression": "api_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.api_request_log": { + "name": "api_request_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status_code": { + "name": "status_code", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "request": { + "name": "request", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "response": { + "name": "response", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_api_request_log_created_at": { + "name": "idx_api_request_log_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.app_builder_feedback": { + "name": "app_builder_feedback", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "preview_status": { + "name": "preview_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_streaming": { + "name": "is_streaming", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "message_count": { + "name": "message_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "feedback_text": { + "name": "feedback_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "recent_messages": { + "name": "recent_messages", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_app_builder_feedback_created_at": { + "name": "IDX_app_builder_feedback_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_app_builder_feedback_kilo_user_id": { + "name": "IDX_app_builder_feedback_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_app_builder_feedback_project_id": { + "name": "IDX_app_builder_feedback_project_id", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "app_builder_feedback_kilo_user_id_kilocode_users_id_fk": { + "name": "app_builder_feedback_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "app_builder_feedback", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "app_builder_feedback_project_id_app_builder_projects_id_fk": { + "name": "app_builder_feedback_project_id_app_builder_projects_id_fk", + "tableFrom": "app_builder_feedback", + "tableTo": "app_builder_projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.app_builder_project_sessions": { + "name": "app_builder_project_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "cloud_agent_session_id": { + "name": "cloud_agent_session_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "worker_version": { + "name": "worker_version", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'v2'" + } + }, + "indexes": { + "IDX_app_builder_project_sessions_project_id": { + "name": "IDX_app_builder_project_sessions_project_id", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "app_builder_project_sessions_project_id_app_builder_projects_id_fk": { + "name": "app_builder_project_sessions_project_id_app_builder_projects_id_fk", + "tableFrom": "app_builder_project_sessions", + "tableTo": "app_builder_projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_app_builder_project_sessions_cloud_agent_session_id": { + "name": "UQ_app_builder_project_sessions_cloud_agent_session_id", + "nullsNotDistinct": false, + "columns": [ + "cloud_agent_session_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.app_builder_projects": { + "name": "app_builder_projects", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model_id": { + "name": "model_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "template": { + "name": "template", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deployment_id": { + "name": "deployment_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "last_message_at": { + "name": "last_message_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "git_repo_full_name": { + "name": "git_repo_full_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "git_platform_integration_id": { + "name": "git_platform_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "migrated_at": { + "name": "migrated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_app_builder_projects_created_by_user_id": { + "name": "IDX_app_builder_projects_created_by_user_id", + "columns": [ + { + "expression": "created_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_app_builder_projects_owned_by_user_id": { + "name": "IDX_app_builder_projects_owned_by_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_app_builder_projects_owned_by_organization_id": { + "name": "IDX_app_builder_projects_owned_by_organization_id", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_app_builder_projects_created_at": { + "name": "IDX_app_builder_projects_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_app_builder_projects_last_message_at": { + "name": "IDX_app_builder_projects_last_message_at", + "columns": [ + { + "expression": "last_message_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_app_builder_projects_git_repo_integration": { + "name": "IDX_app_builder_projects_git_repo_integration", + "columns": [ + { + "expression": "git_repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "git_platform_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"app_builder_projects\".\"git_repo_full_name\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "app_builder_projects_owned_by_user_id_kilocode_users_id_fk": { + "name": "app_builder_projects_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "app_builder_projects", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "app_builder_projects_owned_by_organization_id_organizations_id_fk": { + "name": "app_builder_projects_owned_by_organization_id_organizations_id_fk", + "tableFrom": "app_builder_projects", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "app_builder_projects_deployment_id_deployments_id_fk": { + "name": "app_builder_projects_deployment_id_deployments_id_fk", + "tableFrom": "app_builder_projects", + "tableTo": "deployments", + "columnsFrom": [ + "deployment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "app_builder_projects_git_platform_integration_id_platform_integrations_id_fk": { + "name": "app_builder_projects_git_platform_integration_id_platform_integrations_id_fk", + "tableFrom": "app_builder_projects", + "tableTo": "platform_integrations", + "columnsFrom": [ + "git_platform_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "app_builder_projects_owner_check": { + "name": "app_builder_projects_owner_check", + "value": "(\n (\"app_builder_projects\".\"owned_by_user_id\" IS NOT NULL AND \"app_builder_projects\".\"owned_by_organization_id\" IS NULL) OR\n (\"app_builder_projects\".\"owned_by_user_id\" IS NULL AND \"app_builder_projects\".\"owned_by_organization_id\" IS NOT NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.app_min_versions": { + "name": "app_min_versions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "ios_min_version": { + "name": "ios_min_version", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'1.0.0'" + }, + "android_min_version": { + "name": "android_min_version", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'1.0.0'" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.app_reported_messages": { + "name": "app_reported_messages", + "schema": "", + "columns": { + "report_id": { + "name": "report_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "report_type": { + "name": "report_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "signature": { + "name": "signature", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "cli_session_id": { + "name": "cli_session_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "app_reported_messages_cli_session_id_cli_sessions_session_id_fk": { + "name": "app_reported_messages_cli_session_id_cli_sessions_session_id_fk", + "tableFrom": "app_reported_messages", + "tableTo": "cli_sessions", + "columnsFrom": [ + "cli_session_id" + ], + "columnsTo": [ + "session_id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auto_fix_tickets": { + "name": "auto_fix_tickets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "platform_integration_id": { + "name": "platform_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "triage_ticket_id": { + "name": "triage_ticket_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "repo_full_name": { + "name": "repo_full_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issue_number": { + "name": "issue_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "issue_url": { + "name": "issue_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issue_title": { + "name": "issue_title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issue_body": { + "name": "issue_body", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "issue_author": { + "name": "issue_author", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issue_labels": { + "name": "issue_labels", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "trigger_source": { + "name": "trigger_source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'label'" + }, + "review_comment_id": { + "name": "review_comment_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "review_comment_body": { + "name": "review_comment_body", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "line_number": { + "name": "line_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "diff_hunk": { + "name": "diff_hunk", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pr_head_ref": { + "name": "pr_head_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "classification": { + "name": "classification", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confidence": { + "name": "confidence", + "type": "numeric(3, 2)", + "primaryKey": false, + "notNull": false + }, + "intent_summary": { + "name": "intent_summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "related_files": { + "name": "related_files", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cli_session_id": { + "name": "cli_session_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "pr_number": { + "name": "pr_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "pr_url": { + "name": "pr_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pr_branch": { + "name": "pr_branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_auto_fix_tickets_repo_issue": { + "name": "UQ_auto_fix_tickets_repo_issue", + "columns": [ + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"auto_fix_tickets\".\"trigger_source\" = 'label'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_auto_fix_tickets_repo_review_comment": { + "name": "UQ_auto_fix_tickets_repo_review_comment", + "columns": [ + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "review_comment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"auto_fix_tickets\".\"review_comment_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_fix_tickets_owned_by_org": { + "name": "IDX_auto_fix_tickets_owned_by_org", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_fix_tickets_owned_by_user": { + "name": "IDX_auto_fix_tickets_owned_by_user", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_fix_tickets_status": { + "name": "IDX_auto_fix_tickets_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_fix_tickets_created_at": { + "name": "IDX_auto_fix_tickets_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_fix_tickets_triage_ticket_id": { + "name": "IDX_auto_fix_tickets_triage_ticket_id", + "columns": [ + { + "expression": "triage_ticket_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_fix_tickets_session_id": { + "name": "IDX_auto_fix_tickets_session_id", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auto_fix_tickets_owned_by_organization_id_organizations_id_fk": { + "name": "auto_fix_tickets_owned_by_organization_id_organizations_id_fk", + "tableFrom": "auto_fix_tickets", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "auto_fix_tickets_owned_by_user_id_kilocode_users_id_fk": { + "name": "auto_fix_tickets_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "auto_fix_tickets", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "auto_fix_tickets_platform_integration_id_platform_integrations_id_fk": { + "name": "auto_fix_tickets_platform_integration_id_platform_integrations_id_fk", + "tableFrom": "auto_fix_tickets", + "tableTo": "platform_integrations", + "columnsFrom": [ + "platform_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "auto_fix_tickets_triage_ticket_id_auto_triage_tickets_id_fk": { + "name": "auto_fix_tickets_triage_ticket_id_auto_triage_tickets_id_fk", + "tableFrom": "auto_fix_tickets", + "tableTo": "auto_triage_tickets", + "columnsFrom": [ + "triage_ticket_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "auto_fix_tickets_cli_session_id_cli_sessions_session_id_fk": { + "name": "auto_fix_tickets_cli_session_id_cli_sessions_session_id_fk", + "tableFrom": "auto_fix_tickets", + "tableTo": "cli_sessions", + "columnsFrom": [ + "cli_session_id" + ], + "columnsTo": [ + "session_id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "auto_fix_tickets_owner_check": { + "name": "auto_fix_tickets_owner_check", + "value": "(\n (\"auto_fix_tickets\".\"owned_by_user_id\" IS NOT NULL AND \"auto_fix_tickets\".\"owned_by_organization_id\" IS NULL) OR\n (\"auto_fix_tickets\".\"owned_by_user_id\" IS NULL AND \"auto_fix_tickets\".\"owned_by_organization_id\" IS NOT NULL)\n )" + }, + "auto_fix_tickets_status_check": { + "name": "auto_fix_tickets_status_check", + "value": "\"auto_fix_tickets\".\"status\" IN ('pending', 'running', 'completed', 'failed', 'cancelled')" + }, + "auto_fix_tickets_classification_check": { + "name": "auto_fix_tickets_classification_check", + "value": "\"auto_fix_tickets\".\"classification\" IN ('bug', 'feature', 'question', 'unclear')" + }, + "auto_fix_tickets_confidence_check": { + "name": "auto_fix_tickets_confidence_check", + "value": "\"auto_fix_tickets\".\"confidence\" >= 0 AND \"auto_fix_tickets\".\"confidence\" <= 1" + }, + "auto_fix_tickets_trigger_source_check": { + "name": "auto_fix_tickets_trigger_source_check", + "value": "\"auto_fix_tickets\".\"trigger_source\" IN ('label', 'review_comment')" + } + }, + "isRLSEnabled": false + }, + "public.auto_model": { + "name": "auto_model", + "schema": "", + "columns": { + "auto_model_id": { + "name": "auto_model_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "auto_model": { + "name": "auto_model", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UQ_auto_model": { + "name": "UQ_auto_model", + "columns": [ + { + "expression": "auto_model", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auto_top_up_configs": { + "name": "auto_top_up_configs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_payment_method_id": { + "name": "stripe_payment_method_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount_cents": { + "name": "amount_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5000 + }, + "last_auto_top_up_at": { + "name": "last_auto_top_up_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "attempt_started_at": { + "name": "attempt_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "disabled_reason": { + "name": "disabled_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_auto_top_up_configs_owned_by_user_id": { + "name": "UQ_auto_top_up_configs_owned_by_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"auto_top_up_configs\".\"owned_by_user_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_auto_top_up_configs_owned_by_organization_id": { + "name": "UQ_auto_top_up_configs_owned_by_organization_id", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"auto_top_up_configs\".\"owned_by_organization_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auto_top_up_configs_owned_by_user_id_kilocode_users_id_fk": { + "name": "auto_top_up_configs_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "auto_top_up_configs", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "auto_top_up_configs_owned_by_organization_id_organizations_id_fk": { + "name": "auto_top_up_configs_owned_by_organization_id_organizations_id_fk", + "tableFrom": "auto_top_up_configs", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "auto_top_up_configs_exactly_one_owner": { + "name": "auto_top_up_configs_exactly_one_owner", + "value": "(\"auto_top_up_configs\".\"owned_by_user_id\" IS NOT NULL AND \"auto_top_up_configs\".\"owned_by_organization_id\" IS NULL) OR (\"auto_top_up_configs\".\"owned_by_user_id\" IS NULL AND \"auto_top_up_configs\".\"owned_by_organization_id\" IS NOT NULL)" + } + }, + "isRLSEnabled": false + }, + "public.auto_triage_tickets": { + "name": "auto_triage_tickets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "platform_integration_id": { + "name": "platform_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "repo_full_name": { + "name": "repo_full_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issue_number": { + "name": "issue_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "issue_url": { + "name": "issue_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issue_title": { + "name": "issue_title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issue_body": { + "name": "issue_body", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "issue_author": { + "name": "issue_author", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issue_type": { + "name": "issue_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issue_labels": { + "name": "issue_labels", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "classification": { + "name": "classification", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confidence": { + "name": "confidence", + "type": "numeric(3, 2)", + "primaryKey": false, + "notNull": false + }, + "intent_summary": { + "name": "intent_summary", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "related_files": { + "name": "related_files", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "is_duplicate": { + "name": "is_duplicate", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "duplicate_of_ticket_id": { + "name": "duplicate_of_ticket_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "similarity_score": { + "name": "similarity_score", + "type": "numeric(3, 2)", + "primaryKey": false, + "notNull": false + }, + "qdrant_point_id": { + "name": "qdrant_point_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "should_auto_fix": { + "name": "should_auto_fix", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "action_taken": { + "name": "action_taken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action_metadata": { + "name": "action_metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_auto_triage_tickets_repo_issue": { + "name": "UQ_auto_triage_tickets_repo_issue", + "columns": [ + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "issue_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_triage_tickets_owned_by_org": { + "name": "IDX_auto_triage_tickets_owned_by_org", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_triage_tickets_owned_by_user": { + "name": "IDX_auto_triage_tickets_owned_by_user", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_triage_tickets_status": { + "name": "IDX_auto_triage_tickets_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_triage_tickets_created_at": { + "name": "IDX_auto_triage_tickets_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_triage_tickets_qdrant_point_id": { + "name": "IDX_auto_triage_tickets_qdrant_point_id", + "columns": [ + { + "expression": "qdrant_point_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_triage_tickets_owner_status_created": { + "name": "IDX_auto_triage_tickets_owner_status_created", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_triage_tickets_user_status_created": { + "name": "IDX_auto_triage_tickets_user_status_created", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_auto_triage_tickets_repo_classification": { + "name": "IDX_auto_triage_tickets_repo_classification", + "columns": [ + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "classification", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "auto_triage_tickets_owned_by_organization_id_organizations_id_fk": { + "name": "auto_triage_tickets_owned_by_organization_id_organizations_id_fk", + "tableFrom": "auto_triage_tickets", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "auto_triage_tickets_owned_by_user_id_kilocode_users_id_fk": { + "name": "auto_triage_tickets_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "auto_triage_tickets", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "auto_triage_tickets_platform_integration_id_platform_integrations_id_fk": { + "name": "auto_triage_tickets_platform_integration_id_platform_integrations_id_fk", + "tableFrom": "auto_triage_tickets", + "tableTo": "platform_integrations", + "columnsFrom": [ + "platform_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "auto_triage_tickets_duplicate_of_ticket_id_auto_triage_tickets_id_fk": { + "name": "auto_triage_tickets_duplicate_of_ticket_id_auto_triage_tickets_id_fk", + "tableFrom": "auto_triage_tickets", + "tableTo": "auto_triage_tickets", + "columnsFrom": [ + "duplicate_of_ticket_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "auto_triage_tickets_owner_check": { + "name": "auto_triage_tickets_owner_check", + "value": "(\n (\"auto_triage_tickets\".\"owned_by_user_id\" IS NOT NULL AND \"auto_triage_tickets\".\"owned_by_organization_id\" IS NULL) OR\n (\"auto_triage_tickets\".\"owned_by_user_id\" IS NULL AND \"auto_triage_tickets\".\"owned_by_organization_id\" IS NOT NULL)\n )" + }, + "auto_triage_tickets_issue_type_check": { + "name": "auto_triage_tickets_issue_type_check", + "value": "\"auto_triage_tickets\".\"issue_type\" IN ('issue', 'pull_request')" + }, + "auto_triage_tickets_classification_check": { + "name": "auto_triage_tickets_classification_check", + "value": "\"auto_triage_tickets\".\"classification\" IN ('bug', 'feature', 'question', 'duplicate', 'unclear')" + }, + "auto_triage_tickets_confidence_check": { + "name": "auto_triage_tickets_confidence_check", + "value": "\"auto_triage_tickets\".\"confidence\" >= 0 AND \"auto_triage_tickets\".\"confidence\" <= 1" + }, + "auto_triage_tickets_similarity_score_check": { + "name": "auto_triage_tickets_similarity_score_check", + "value": "\"auto_triage_tickets\".\"similarity_score\" >= 0 AND \"auto_triage_tickets\".\"similarity_score\" <= 1" + }, + "auto_triage_tickets_status_check": { + "name": "auto_triage_tickets_status_check", + "value": "\"auto_triage_tickets\".\"status\" IN ('pending', 'analyzing', 'actioned', 'failed', 'skipped')" + }, + "auto_triage_tickets_action_taken_check": { + "name": "auto_triage_tickets_action_taken_check", + "value": "\"auto_triage_tickets\".\"action_taken\" IN ('pr_created', 'comment_posted', 'closed_duplicate', 'needs_clarification')" + } + }, + "isRLSEnabled": false + }, + "public.bot_request_cloud_agent_sessions": { + "name": "bot_request_cloud_agent_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "bot_request_id": { + "name": "bot_request_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "spawn_group_id": { + "name": "spawn_group_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "cloud_agent_session_id": { + "name": "cloud_agent_session_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kilo_session_id": { + "name": "kilo_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'running'" + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "github_repo": { + "name": "github_repo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlab_project": { + "name": "gitlab_project", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "callback_step": { + "name": "callback_step", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "final_message": { + "name": "final_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "final_message_fetched_at": { + "name": "final_message_fetched_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "final_message_error": { + "name": "final_message_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "terminal_at": { + "name": "terminal_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "continuation_started_at": { + "name": "continuation_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_bot_request_cas_cloud_agent_session_id": { + "name": "UQ_bot_request_cas_cloud_agent_session_id", + "columns": [ + { + "expression": "cloud_agent_session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_bot_request_cas_bot_request_id": { + "name": "IDX_bot_request_cas_bot_request_id", + "columns": [ + { + "expression": "bot_request_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_bot_request_cas_bot_request_id_spawn_group_id": { + "name": "IDX_bot_request_cas_bot_request_id_spawn_group_id", + "columns": [ + { + "expression": "bot_request_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "spawn_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_bot_request_cas_bot_request_id_spawn_group_id_status": { + "name": "IDX_bot_request_cas_bot_request_id_spawn_group_id_status", + "columns": [ + { + "expression": "bot_request_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "spawn_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "bot_request_cloud_agent_sessions_bot_request_id_bot_requests_id_fk": { + "name": "bot_request_cloud_agent_sessions_bot_request_id_bot_requests_id_fk", + "tableFrom": "bot_request_cloud_agent_sessions", + "tableTo": "bot_requests", + "columnsFrom": [ + "bot_request_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bot_requests": { + "name": "bot_requests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "platform_integration_id": { + "name": "platform_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform_thread_id": { + "name": "platform_thread_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform_message_id": { + "name": "platform_message_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_message": { + "name": "user_message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model_used": { + "name": "model_used", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "steps": { + "name": "steps", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "cloud_agent_session_id": { + "name": "cloud_agent_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "response_time_ms": { + "name": "response_time_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_bot_requests_created_at": { + "name": "IDX_bot_requests_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_bot_requests_created_by": { + "name": "IDX_bot_requests_created_by", + "columns": [ + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_bot_requests_organization_id": { + "name": "IDX_bot_requests_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_bot_requests_platform_integration_id": { + "name": "IDX_bot_requests_platform_integration_id", + "columns": [ + { + "expression": "platform_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_bot_requests_status": { + "name": "IDX_bot_requests_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "bot_requests_created_by_kilocode_users_id_fk": { + "name": "bot_requests_created_by_kilocode_users_id_fk", + "tableFrom": "bot_requests", + "tableTo": "kilocode_users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "bot_requests_organization_id_organizations_id_fk": { + "name": "bot_requests_organization_id_organizations_id_fk", + "tableFrom": "bot_requests", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "bot_requests_platform_integration_id_platform_integrations_id_fk": { + "name": "bot_requests_platform_integration_id_platform_integrations_id_fk", + "tableFrom": "bot_requests", + "tableTo": "platform_integrations", + "columnsFrom": [ + "platform_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.byok_api_keys": { + "name": "byok_api_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "encrypted_api_key": { + "name": "encrypted_api_key", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "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 + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status_updated_at": { + "name": "status_updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_cli_sessions_v2_parent_session_id_kilo_user_id": { + "name": "IDX_cli_sessions_v2_parent_session_id_kilo_user_id", + "columns": [ + { + "expression": "parent_session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_cli_sessions_v2_public_id": { + "name": "UQ_cli_sessions_v2_public_id", + "columns": [ + { + "expression": "public_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"cli_sessions_v2\".\"public_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_cli_sessions_v2_cloud_agent_session_id": { + "name": "UQ_cli_sessions_v2_cloud_agent_session_id", + "columns": [ + { + "expression": "cloud_agent_session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"cli_sessions_v2\".\"cloud_agent_session_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cli_sessions_v2_organization_id": { + "name": "IDX_cli_sessions_v2_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cli_sessions_v2_kilo_user_id": { + "name": "IDX_cli_sessions_v2_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cli_sessions_v2_created_at": { + "name": "IDX_cli_sessions_v2_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cli_sessions_v2_user_updated": { + "name": "IDX_cli_sessions_v2_user_updated", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "cli_sessions_v2_git_url_branch_idx": { + "name": "cli_sessions_v2_git_url_branch_idx", + "columns": [ + { + "expression": "git_url", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "git_branch", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "cli_sessions_v2_kilo_user_id_kilocode_users_id_fk": { + "name": "cli_sessions_v2_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "cli_sessions_v2", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "cli_sessions_v2_organization_id_organizations_id_fk": { + "name": "cli_sessions_v2_organization_id_organizations_id_fk", + "tableFrom": "cli_sessions_v2", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "cli_sessions_v2_parent_session_id_kilo_user_id_fk": { + "name": "cli_sessions_v2_parent_session_id_kilo_user_id_fk", + "tableFrom": "cli_sessions_v2", + "tableTo": "cli_sessions_v2", + "columnsFrom": [ + "parent_session_id", + "kilo_user_id" + ], + "columnsTo": [ + "session_id", + "kilo_user_id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "cli_sessions_v2_session_id_kilo_user_id_pk": { + "name": "cli_sessions_v2_session_id_kilo_user_id_pk", + "columns": [ + "session_id", + "kilo_user_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cloud_agent_code_review_attempts": { + "name": "cloud_agent_code_review_attempts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "code_review_id": { + "name": "code_review_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "attempt_number": { + "name": "attempt_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "retry_of_attempt_id": { + "name": "retry_of_attempt_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "retry_reason": { + "name": "retry_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cli_session_id": { + "name": "cli_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "terminal_reason": { + "name": "terminal_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_cloud_agent_code_review_attempts_review_attempt_number": { + "name": "UQ_cloud_agent_code_review_attempts_review_attempt_number", + "columns": [ + { + "expression": "code_review_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "attempt_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_review_attempts_code_review_id": { + "name": "idx_cloud_agent_code_review_attempts_code_review_id", + "columns": [ + { + "expression": "code_review_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_review_attempts_session_id": { + "name": "idx_cloud_agent_code_review_attempts_session_id", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_review_attempts_cli_session_id": { + "name": "idx_cloud_agent_code_review_attempts_cli_session_id", + "columns": [ + { + "expression": "cli_session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_review_attempts_status": { + "name": "idx_cloud_agent_code_review_attempts_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_review_attempts_retry_reason": { + "name": "idx_cloud_agent_code_review_attempts_retry_reason", + "columns": [ + { + "expression": "retry_reason", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "cloud_agent_code_review_attempts_code_review_id_cloud_agent_code_reviews_id_fk": { + "name": "cloud_agent_code_review_attempts_code_review_id_cloud_agent_code_reviews_id_fk", + "tableFrom": "cloud_agent_code_review_attempts", + "tableTo": "cloud_agent_code_reviews", + "columnsFrom": [ + "code_review_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "cloud_agent_code_review_attempts_retry_of_attempt_id_cloud_agent_code_review_attempts_id_fk": { + "name": "cloud_agent_code_review_attempts_retry_of_attempt_id_cloud_agent_code_review_attempts_id_fk", + "tableFrom": "cloud_agent_code_review_attempts", + "tableTo": "cloud_agent_code_review_attempts", + "columnsFrom": [ + "retry_of_attempt_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "cloud_agent_code_review_attempts_attempt_number_check": { + "name": "cloud_agent_code_review_attempts_attempt_number_check", + "value": "\"cloud_agent_code_review_attempts\".\"attempt_number\" >= 1" + } + }, + "isRLSEnabled": false + }, + "public.cloud_agent_code_reviews": { + "name": "cloud_agent_code_reviews", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "platform_integration_id": { + "name": "platform_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "repo_full_name": { + "name": "repo_full_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pr_number": { + "name": "pr_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "pr_url": { + "name": "pr_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pr_title": { + "name": "pr_title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pr_author": { + "name": "pr_author", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pr_author_github_id": { + "name": "pr_author_github_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "base_ref": { + "name": "base_ref", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "head_ref": { + "name": "head_ref", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "head_sha": { + "name": "head_sha", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "platform_project_id": { + "name": "platform_project_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cli_session_id": { + "name": "cli_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "dispatch_reservation_id": { + "name": "dispatch_reservation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "terminal_reason": { + "name": "terminal_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "agent_version": { + "name": "agent_version", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'v1'" + }, + "check_run_id": { + "name": "check_run_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "repository_review_instructions_used": { + "name": "repository_review_instructions_used", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "repository_review_instructions_ref": { + "name": "repository_review_instructions_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "repository_review_instructions_truncated": { + "name": "repository_review_instructions_truncated", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "total_tokens_in": { + "name": "total_tokens_in", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_tokens_out": { + "name": "total_tokens_out", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_cost_musd": { + "name": "total_cost_musd", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_cloud_agent_code_reviews_repo_pr_sha": { + "name": "UQ_cloud_agent_code_reviews_repo_pr_sha", + "columns": [ + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "pr_number", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "head_sha", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_reviews_owned_by_org_id": { + "name": "idx_cloud_agent_code_reviews_owned_by_org_id", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_reviews_owned_by_user_id": { + "name": "idx_cloud_agent_code_reviews_owned_by_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_reviews_session_id": { + "name": "idx_cloud_agent_code_reviews_session_id", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_reviews_cli_session_id": { + "name": "idx_cloud_agent_code_reviews_cli_session_id", + "columns": [ + { + "expression": "cli_session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_reviews_status": { + "name": "idx_cloud_agent_code_reviews_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_reviews_repo": { + "name": "idx_cloud_agent_code_reviews_repo", + "columns": [ + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_reviews_pr_number": { + "name": "idx_cloud_agent_code_reviews_pr_number", + "columns": [ + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "pr_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_reviews_created_at": { + "name": "idx_cloud_agent_code_reviews_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_cloud_agent_code_reviews_pr_author_github_id": { + "name": "idx_cloud_agent_code_reviews_pr_author_github_id", + "columns": [ + { + "expression": "pr_author_github_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "cloud_agent_code_reviews_owned_by_organization_id_organizations_id_fk": { + "name": "cloud_agent_code_reviews_owned_by_organization_id_organizations_id_fk", + "tableFrom": "cloud_agent_code_reviews", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "cloud_agent_code_reviews_owned_by_user_id_kilocode_users_id_fk": { + "name": "cloud_agent_code_reviews_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "cloud_agent_code_reviews", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "cloud_agent_code_reviews_platform_integration_id_platform_integrations_id_fk": { + "name": "cloud_agent_code_reviews_platform_integration_id_platform_integrations_id_fk", + "tableFrom": "cloud_agent_code_reviews", + "tableTo": "platform_integrations", + "columnsFrom": [ + "platform_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "cloud_agent_code_reviews_owner_check": { + "name": "cloud_agent_code_reviews_owner_check", + "value": "(\n (\"cloud_agent_code_reviews\".\"owned_by_user_id\" IS NOT NULL AND \"cloud_agent_code_reviews\".\"owned_by_organization_id\" IS NULL) OR\n (\"cloud_agent_code_reviews\".\"owned_by_user_id\" IS NULL AND \"cloud_agent_code_reviews\".\"owned_by_organization_id\" IS NOT NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.cloud_agent_feedback": { + "name": "cloud_agent_feedback", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cloud_agent_session_id": { + "name": "cloud_agent_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_streaming": { + "name": "is_streaming", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "message_count": { + "name": "message_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "feedback_text": { + "name": "feedback_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "recent_messages": { + "name": "recent_messages", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_cloud_agent_feedback_created_at": { + "name": "IDX_cloud_agent_feedback_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cloud_agent_feedback_kilo_user_id": { + "name": "IDX_cloud_agent_feedback_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cloud_agent_feedback_cloud_agent_session_id": { + "name": "IDX_cloud_agent_feedback_cloud_agent_session_id", + "columns": [ + { + "expression": "cloud_agent_session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "cloud_agent_feedback_kilo_user_id_kilocode_users_id_fk": { + "name": "cloud_agent_feedback_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "cloud_agent_feedback", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "cloud_agent_feedback_organization_id_organizations_id_fk": { + "name": "cloud_agent_feedback_organization_id_organizations_id_fk", + "tableFrom": "cloud_agent_feedback", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cloud_agent_webhook_triggers": { + "name": "cloud_agent_webhook_triggers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "trigger_id": { + "name": "trigger_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "target_type": { + "name": "target_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'cloud_agent'" + }, + "kiloclaw_instance_id": { + "name": "kiloclaw_instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "activation_mode": { + "name": "activation_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'webhook'" + }, + "cron_expression": { + "name": "cron_expression", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cron_timezone": { + "name": "cron_timezone", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'UTC'" + }, + "github_repo": { + "name": "github_repo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "profile_id": { + "name": "profile_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_cloud_agent_webhook_triggers_user_trigger": { + "name": "UQ_cloud_agent_webhook_triggers_user_trigger", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "trigger_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"cloud_agent_webhook_triggers\".\"user_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_cloud_agent_webhook_triggers_org_trigger": { + "name": "UQ_cloud_agent_webhook_triggers_org_trigger", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "trigger_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"cloud_agent_webhook_triggers\".\"organization_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cloud_agent_webhook_triggers_user": { + "name": "IDX_cloud_agent_webhook_triggers_user", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cloud_agent_webhook_triggers_org": { + "name": "IDX_cloud_agent_webhook_triggers_org", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cloud_agent_webhook_triggers_active": { + "name": "IDX_cloud_agent_webhook_triggers_active", + "columns": [ + { + "expression": "is_active", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_cloud_agent_webhook_triggers_profile": { + "name": "IDX_cloud_agent_webhook_triggers_profile", + "columns": [ + { + "expression": "profile_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "cloud_agent_webhook_triggers_user_id_kilocode_users_id_fk": { + "name": "cloud_agent_webhook_triggers_user_id_kilocode_users_id_fk", + "tableFrom": "cloud_agent_webhook_triggers", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "cloud_agent_webhook_triggers_organization_id_organizations_id_fk": { + "name": "cloud_agent_webhook_triggers_organization_id_organizations_id_fk", + "tableFrom": "cloud_agent_webhook_triggers", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "cloud_agent_webhook_triggers_kiloclaw_instance_id_kiloclaw_instances_id_fk": { + "name": "cloud_agent_webhook_triggers_kiloclaw_instance_id_kiloclaw_instances_id_fk", + "tableFrom": "cloud_agent_webhook_triggers", + "tableTo": "kiloclaw_instances", + "columnsFrom": [ + "kiloclaw_instance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "cloud_agent_webhook_triggers_profile_id_agent_environment_profiles_id_fk": { + "name": "cloud_agent_webhook_triggers_profile_id_agent_environment_profiles_id_fk", + "tableFrom": "cloud_agent_webhook_triggers", + "tableTo": "agent_environment_profiles", + "columnsFrom": [ + "profile_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "CHK_cloud_agent_webhook_triggers_owner": { + "name": "CHK_cloud_agent_webhook_triggers_owner", + "value": "(\n (\"cloud_agent_webhook_triggers\".\"user_id\" IS NOT NULL AND \"cloud_agent_webhook_triggers\".\"organization_id\" IS NULL) OR\n (\"cloud_agent_webhook_triggers\".\"user_id\" IS NULL AND \"cloud_agent_webhook_triggers\".\"organization_id\" IS NOT NULL)\n )" + }, + "CHK_cloud_agent_webhook_triggers_cloud_agent_fields": { + "name": "CHK_cloud_agent_webhook_triggers_cloud_agent_fields", + "value": "(\n \"cloud_agent_webhook_triggers\".\"target_type\" != 'cloud_agent' OR\n (\"cloud_agent_webhook_triggers\".\"github_repo\" IS NOT NULL AND \"cloud_agent_webhook_triggers\".\"profile_id\" IS NOT NULL)\n )" + }, + "CHK_cloud_agent_webhook_triggers_kiloclaw_fields": { + "name": "CHK_cloud_agent_webhook_triggers_kiloclaw_fields", + "value": "(\n \"cloud_agent_webhook_triggers\".\"target_type\" != 'kiloclaw_chat' OR\n \"cloud_agent_webhook_triggers\".\"kiloclaw_instance_id\" IS NOT NULL\n )" + }, + "CHK_cloud_agent_webhook_triggers_scheduled_fields": { + "name": "CHK_cloud_agent_webhook_triggers_scheduled_fields", + "value": "(\n \"cloud_agent_webhook_triggers\".\"activation_mode\" != 'scheduled' OR\n \"cloud_agent_webhook_triggers\".\"cron_expression\" IS NOT NULL\n )" + } + }, + "isRLSEnabled": false + }, + "public.code_indexing_manifest": { + "name": "code_indexing_manifest", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "git_branch": { + "name": "git_branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_hash": { + "name": "file_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chunk_count": { + "name": "chunk_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "total_lines": { + "name": "total_lines", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_ai_lines": { + "name": "total_ai_lines", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_code_indexing_manifest_organization_id": { + "name": "IDX_code_indexing_manifest_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_code_indexing_manifest_kilo_user_id": { + "name": "IDX_code_indexing_manifest_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_code_indexing_manifest_project_id": { + "name": "IDX_code_indexing_manifest_project_id", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_code_indexing_manifest_git_branch": { + "name": "IDX_code_indexing_manifest_git_branch", + "columns": [ + { + "expression": "git_branch", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_code_indexing_manifest_created_at": { + "name": "IDX_code_indexing_manifest_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "code_indexing_manifest_kilo_user_id_kilocode_users_id_fk": { + "name": "code_indexing_manifest_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "code_indexing_manifest", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_code_indexing_manifest_org_user_project_hash_branch": { + "name": "UQ_code_indexing_manifest_org_user_project_hash_branch", + "nullsNotDistinct": true, + "columns": [ + "organization_id", + "kilo_user_id", + "project_id", + "file_path", + "git_branch" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.code_indexing_search": { + "name": "code_indexing_search", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "query": { + "name": "query", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_code_indexing_search_organization_id": { + "name": "IDX_code_indexing_search_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_code_indexing_search_kilo_user_id": { + "name": "IDX_code_indexing_search_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_code_indexing_search_project_id": { + "name": "IDX_code_indexing_search_project_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_code_indexing_search_created_at": { + "name": "IDX_code_indexing_search_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "code_indexing_search_kilo_user_id_kilocode_users_id_fk": { + "name": "code_indexing_search_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "code_indexing_search", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.code_review_feedback_events": { + "name": "code_review_feedback_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform_integration_id": { + "name": "platform_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "subject_id": { + "name": "subject_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "code_review_id": { + "name": "code_review_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "repo_full_name": { + "name": "repo_full_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform_project_id": { + "name": "platform_project_id", + "type": "integer", + "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 + }, + "event_source": { + "name": "event_source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "signal_kind": { + "name": "signal_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sentiment": { + "name": "sentiment", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "strength": { + "name": "strength", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "external_event_id": { + "name": "external_event_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dedupe_hash": { + "name": "dedupe_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_url": { + "name": "external_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "evidence_excerpt": { + "name": "evidence_excerpt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "aggregation_state": { + "name": "aggregation_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'fresh'" + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_code_review_feedback_events_external_event_id": { + "name": "UQ_code_review_feedback_events_external_event_id", + "columns": [ + { + "expression": "external_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"code_review_feedback_events\".\"external_event_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_code_review_feedback_events_dedupe_hash": { + "name": "UQ_code_review_feedback_events_dedupe_hash", + "columns": [ + { + "expression": "dedupe_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_feedback_events_owned_by_org_id": { + "name": "idx_code_review_feedback_events_owned_by_org_id", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_feedback_events_owned_by_user_id": { + "name": "idx_code_review_feedback_events_owned_by_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_feedback_events_platform_repo": { + "name": "idx_code_review_feedback_events_platform_repo", + "columns": [ + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_feedback_events_subject_id": { + "name": "idx_code_review_feedback_events_subject_id", + "columns": [ + { + "expression": "subject_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_feedback_events_code_review_id": { + "name": "idx_code_review_feedback_events_code_review_id", + "columns": [ + { + "expression": "code_review_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_feedback_events_sentiment": { + "name": "idx_code_review_feedback_events_sentiment", + "columns": [ + { + "expression": "sentiment", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_feedback_events_signal_kind": { + "name": "idx_code_review_feedback_events_signal_kind", + "columns": [ + { + "expression": "signal_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_feedback_events_aggregation_state": { + "name": "idx_code_review_feedback_events_aggregation_state", + "columns": [ + { + "expression": "aggregation_state", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_feedback_events_created_at": { + "name": "idx_code_review_feedback_events_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "code_review_feedback_events_owned_by_organization_id_organizations_id_fk": { + "name": "code_review_feedback_events_owned_by_organization_id_organizations_id_fk", + "tableFrom": "code_review_feedback_events", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "code_review_feedback_events_owned_by_user_id_kilocode_users_id_fk": { + "name": "code_review_feedback_events_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "code_review_feedback_events", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "code_review_feedback_events_platform_integration_id_platform_integrations_id_fk": { + "name": "code_review_feedback_events_platform_integration_id_platform_integrations_id_fk", + "tableFrom": "code_review_feedback_events", + "tableTo": "platform_integrations", + "columnsFrom": [ + "platform_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "code_review_feedback_events_subject_id_code_review_feedback_subjects_id_fk": { + "name": "code_review_feedback_events_subject_id_code_review_feedback_subjects_id_fk", + "tableFrom": "code_review_feedback_events", + "tableTo": "code_review_feedback_subjects", + "columnsFrom": [ + "subject_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "code_review_feedback_events_code_review_id_cloud_agent_code_reviews_id_fk": { + "name": "code_review_feedback_events_code_review_id_cloud_agent_code_reviews_id_fk", + "tableFrom": "code_review_feedback_events", + "tableTo": "cloud_agent_code_reviews", + "columnsFrom": [ + "code_review_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "code_review_feedback_events_owner_check": { + "name": "code_review_feedback_events_owner_check", + "value": "(\n (\"code_review_feedback_events\".\"owned_by_user_id\" IS NOT NULL AND \"code_review_feedback_events\".\"owned_by_organization_id\" IS NULL) OR\n (\"code_review_feedback_events\".\"owned_by_user_id\" IS NULL AND \"code_review_feedback_events\".\"owned_by_organization_id\" IS NOT NULL)\n )" + }, + "code_review_feedback_events_strength_check": { + "name": "code_review_feedback_events_strength_check", + "value": "\"code_review_feedback_events\".\"strength\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.code_review_feedback_subjects": { + "name": "code_review_feedback_subjects", + "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 + }, + "platform_integration_id": { + "name": "platform_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "code_review_id": { + "name": "code_review_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "subject_type": { + "name": "subject_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "external_thread_id": { + "name": "external_thread_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "external_url": { + "name": "external_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "repo_full_name": { + "name": "repo_full_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform_project_id": { + "name": "platform_project_id", + "type": "integer", + "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 + }, + "head_sha": { + "name": "head_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "line_number": { + "name": "line_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "diff_hunk": { + "name": "diff_hunk", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "body_excerpt": { + "name": "body_excerpt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "severity": { + "name": "severity", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "finding_title": { + "name": "finding_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "finding_fingerprint": { + "name": "finding_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "first_seen_at": { + "name": "first_seen_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_seen_at": { + "name": "last_seen_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_code_review_feedback_subjects_platform_external": { + "name": "UQ_code_review_feedback_subjects_platform_external", + "columns": [ + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "subject_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_feedback_subjects_owned_by_org_id": { + "name": "idx_code_review_feedback_subjects_owned_by_org_id", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_feedback_subjects_owned_by_user_id": { + "name": "idx_code_review_feedback_subjects_owned_by_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_feedback_subjects_platform_repo": { + "name": "idx_code_review_feedback_subjects_platform_repo", + "columns": [ + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_feedback_subjects_pr": { + "name": "idx_code_review_feedback_subjects_pr", + "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_code_review_feedback_subjects_code_review_id": { + "name": "idx_code_review_feedback_subjects_code_review_id", + "columns": [ + { + "expression": "code_review_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_feedback_subjects_fingerprint": { + "name": "idx_code_review_feedback_subjects_fingerprint", + "columns": [ + { + "expression": "finding_fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_feedback_subjects_state": { + "name": "idx_code_review_feedback_subjects_state", + "columns": [ + { + "expression": "state", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_feedback_subjects_thread": { + "name": "idx_code_review_feedback_subjects_thread", + "columns": [ + { + "expression": "external_thread_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_feedback_subjects_last_seen_at": { + "name": "idx_code_review_feedback_subjects_last_seen_at", + "columns": [ + { + "expression": "last_seen_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "code_review_feedback_subjects_owned_by_organization_id_organizations_id_fk": { + "name": "code_review_feedback_subjects_owned_by_organization_id_organizations_id_fk", + "tableFrom": "code_review_feedback_subjects", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "code_review_feedback_subjects_owned_by_user_id_kilocode_users_id_fk": { + "name": "code_review_feedback_subjects_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "code_review_feedback_subjects", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "code_review_feedback_subjects_platform_integration_id_platform_integrations_id_fk": { + "name": "code_review_feedback_subjects_platform_integration_id_platform_integrations_id_fk", + "tableFrom": "code_review_feedback_subjects", + "tableTo": "platform_integrations", + "columnsFrom": [ + "platform_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "code_review_feedback_subjects_code_review_id_cloud_agent_code_reviews_id_fk": { + "name": "code_review_feedback_subjects_code_review_id_cloud_agent_code_reviews_id_fk", + "tableFrom": "code_review_feedback_subjects", + "tableTo": "cloud_agent_code_reviews", + "columnsFrom": [ + "code_review_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "code_review_feedback_subjects_owner_check": { + "name": "code_review_feedback_subjects_owner_check", + "value": "(\n (\"code_review_feedback_subjects\".\"owned_by_user_id\" IS NOT NULL AND \"code_review_feedback_subjects\".\"owned_by_organization_id\" IS NULL) OR\n (\"code_review_feedback_subjects\".\"owned_by_user_id\" IS NULL AND \"code_review_feedback_subjects\".\"owned_by_organization_id\" IS NOT NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.code_review_memory_aggregation_runs": { + "name": "code_review_memory_aggregation_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "repo_full_name": { + "name": "repo_full_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform_project_id": { + "name": "platform_project_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "model_slug": { + "name": "model_slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "trigger": { + "name": "trigger", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "input_event_count": { + "name": "input_event_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "input_subject_count": { + "name": "input_subject_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "input_cluster_count": { + "name": "input_cluster_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "fresh_event_cutoff_at": { + "name": "fresh_event_cutoff_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'running'" + }, + "skip_reason": { + "name": "skip_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tokens_in": { + "name": "tokens_in", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "tokens_out": { + "name": "tokens_out", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_cost_musd": { + "name": "total_cost_musd", + "type": "integer", + "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": true, + "default": "now()" + }, + "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_code_review_memory_aggregation_runs_owned_by_org_id": { + "name": "idx_code_review_memory_aggregation_runs_owned_by_org_id", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_memory_aggregation_runs_owned_by_user_id": { + "name": "idx_code_review_memory_aggregation_runs_owned_by_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_memory_aggregation_runs_scope": { + "name": "idx_code_review_memory_aggregation_runs_scope", + "columns": [ + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_memory_aggregation_runs_status": { + "name": "idx_code_review_memory_aggregation_runs_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_memory_aggregation_runs_created_at": { + "name": "idx_code_review_memory_aggregation_runs_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "code_review_memory_aggregation_runs_owned_by_organization_id_organizations_id_fk": { + "name": "code_review_memory_aggregation_runs_owned_by_organization_id_organizations_id_fk", + "tableFrom": "code_review_memory_aggregation_runs", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "code_review_memory_aggregation_runs_owned_by_user_id_kilocode_users_id_fk": { + "name": "code_review_memory_aggregation_runs_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "code_review_memory_aggregation_runs", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "code_review_memory_aggregation_runs_owner_check": { + "name": "code_review_memory_aggregation_runs_owner_check", + "value": "(\n (\"code_review_memory_aggregation_runs\".\"owned_by_user_id\" IS NOT NULL AND \"code_review_memory_aggregation_runs\".\"owned_by_organization_id\" IS NULL) OR\n (\"code_review_memory_aggregation_runs\".\"owned_by_user_id\" IS NULL AND \"code_review_memory_aggregation_runs\".\"owned_by_organization_id\" IS NOT NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.code_review_memory_aggregation_state": { + "name": "code_review_memory_aggregation_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 + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "repo_full_name": { + "name": "repo_full_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform_project_id": { + "name": "platform_project_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_successful_run_at": { + "name": "last_successful_run_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_attempted_run_at": { + "name": "last_attempted_run_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_included_event_created_at": { + "name": "last_included_event_created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "fresh_event_count": { + "name": "fresh_event_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "fresh_weight": { + "name": "fresh_weight", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "fresh_distinct_subject_count": { + "name": "fresh_distinct_subject_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "fresh_distinct_pr_count": { + "name": "fresh_distinct_pr_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_eligible_at": { + "name": "next_eligible_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "claim_token": { + "name": "claim_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_model_slug": { + "name": "last_model_slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_error_message": { + "name": "last_error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_code_review_memory_aggregation_state_org_scope": { + "name": "UQ_code_review_memory_aggregation_state_org_scope", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"code_review_memory_aggregation_state\".\"owned_by_organization_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_code_review_memory_aggregation_state_user_scope": { + "name": "UQ_code_review_memory_aggregation_state_user_scope", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"code_review_memory_aggregation_state\".\"owned_by_user_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_memory_aggregation_state_status": { + "name": "idx_code_review_memory_aggregation_state_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_memory_aggregation_state_next_eligible_at": { + "name": "idx_code_review_memory_aggregation_state_next_eligible_at", + "columns": [ + { + "expression": "next_eligible_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_memory_aggregation_state_fresh_counts": { + "name": "idx_code_review_memory_aggregation_state_fresh_counts", + "columns": [ + { + "expression": "fresh_event_count", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "fresh_weight", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "code_review_memory_aggregation_state_owned_by_organization_id_organizations_id_fk": { + "name": "code_review_memory_aggregation_state_owned_by_organization_id_organizations_id_fk", + "tableFrom": "code_review_memory_aggregation_state", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "code_review_memory_aggregation_state_owned_by_user_id_kilocode_users_id_fk": { + "name": "code_review_memory_aggregation_state_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "code_review_memory_aggregation_state", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "code_review_memory_aggregation_state_owner_check": { + "name": "code_review_memory_aggregation_state_owner_check", + "value": "(\n (\"code_review_memory_aggregation_state\".\"owned_by_user_id\" IS NOT NULL AND \"code_review_memory_aggregation_state\".\"owned_by_organization_id\" IS NULL) OR\n (\"code_review_memory_aggregation_state\".\"owned_by_user_id\" IS NULL AND \"code_review_memory_aggregation_state\".\"owned_by_organization_id\" IS NOT NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.code_review_memory_proposal_evidence": { + "name": "code_review_memory_proposal_evidence", + "schema": "", + "columns": { + "proposal_id": { + "name": "proposal_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "feedback_event_id": { + "name": "feedback_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "evidence_role": { + "name": "evidence_role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'supporting'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_code_review_memory_proposal_evidence_feedback_event_id": { + "name": "idx_code_review_memory_proposal_evidence_feedback_event_id", + "columns": [ + { + "expression": "feedback_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_memory_proposal_evidence_role": { + "name": "idx_code_review_memory_proposal_evidence_role", + "columns": [ + { + "expression": "evidence_role", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "code_review_memory_proposal_evidence_proposal_id_code_review_memory_proposals_id_fk": { + "name": "code_review_memory_proposal_evidence_proposal_id_code_review_memory_proposals_id_fk", + "tableFrom": "code_review_memory_proposal_evidence", + "tableTo": "code_review_memory_proposals", + "columnsFrom": [ + "proposal_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "code_review_memory_proposal_evidence_feedback_event_id_code_review_feedback_events_id_fk": { + "name": "code_review_memory_proposal_evidence_feedback_event_id_code_review_feedback_events_id_fk", + "tableFrom": "code_review_memory_proposal_evidence", + "tableTo": "code_review_feedback_events", + "columnsFrom": [ + "feedback_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "code_review_memory_proposal_evidence_proposal_id_feedback_event_id_pk": { + "name": "code_review_memory_proposal_evidence_proposal_id_feedback_event_id_pk", + "columns": [ + "proposal_id", + "feedback_event_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.code_review_memory_proposals": { + "name": "code_review_memory_proposals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "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 + }, + "platform_project_id": { + "name": "platform_project_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "aggregation_run_id": { + "name": "aggregation_run_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "target_file_path": { + "name": "target_file_path", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'REVIEW.md'" + }, + "scope_kind": { + "name": "scope_kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'repository'" + }, + "scope_value": { + "name": "scope_value", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "proposal_type": { + "name": "proposal_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'open'" + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rationale": { + "name": "rationale", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "proposed_markdown": { + "name": "proposed_markdown", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dedupe_key": { + "name": "dedupe_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "llm_confidence": { + "name": "llm_confidence", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "positive_count": { + "name": "positive_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "negative_count": { + "name": "negative_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "neutral_count": { + "name": "neutral_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "distinct_pr_count": { + "name": "distinct_pr_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "distinct_subject_count": { + "name": "distinct_subject_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "contradictory_count": { + "name": "contradictory_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "edited_by_user_id": { + "name": "edited_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "approved_by_user_id": { + "name": "approved_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rejected_by_user_id": { + "name": "rejected_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "approved_at": { + "name": "approved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "rejected_at": { + "name": "rejected_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "change_request_type": { + "name": "change_request_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch_name": { + "name": "branch_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "change_request_number": { + "name": "change_request_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "change_request_url": { + "name": "change_request_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "change_request_error_message": { + "name": "change_request_error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_code_review_memory_proposals_owned_by_org_id": { + "name": "idx_code_review_memory_proposals_owned_by_org_id", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_memory_proposals_owned_by_user_id": { + "name": "idx_code_review_memory_proposals_owned_by_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_memory_proposals_platform_repo_status": { + "name": "idx_code_review_memory_proposals_platform_repo_status", + "columns": [ + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_memory_proposals_proposal_type": { + "name": "idx_code_review_memory_proposals_proposal_type", + "columns": [ + { + "expression": "proposal_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_code_review_memory_proposals_created_at": { + "name": "idx_code_review_memory_proposals_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_code_review_memory_proposals_org_active_dedupe": { + "name": "UQ_code_review_memory_proposals_org_active_dedupe", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "dedupe_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"code_review_memory_proposals\".\"owned_by_organization_id\" IS NOT NULL AND \"code_review_memory_proposals\".\"status\" IN ('open', 'edited', 'approved', 'opening_change_request', 'change_request_opened')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_code_review_memory_proposals_user_active_dedupe": { + "name": "UQ_code_review_memory_proposals_user_active_dedupe", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "repo_full_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "dedupe_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"code_review_memory_proposals\".\"owned_by_user_id\" IS NOT NULL AND \"code_review_memory_proposals\".\"status\" IN ('open', 'edited', 'approved', 'opening_change_request', 'change_request_opened')", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "code_review_memory_proposals_owned_by_organization_id_organizations_id_fk": { + "name": "code_review_memory_proposals_owned_by_organization_id_organizations_id_fk", + "tableFrom": "code_review_memory_proposals", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "code_review_memory_proposals_owned_by_user_id_kilocode_users_id_fk": { + "name": "code_review_memory_proposals_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "code_review_memory_proposals", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "code_review_memory_proposals_platform_integration_id_platform_integrations_id_fk": { + "name": "code_review_memory_proposals_platform_integration_id_platform_integrations_id_fk", + "tableFrom": "code_review_memory_proposals", + "tableTo": "platform_integrations", + "columnsFrom": [ + "platform_integration_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "code_review_memory_proposals_aggregation_run_id_code_review_memory_aggregation_runs_id_fk": { + "name": "code_review_memory_proposals_aggregation_run_id_code_review_memory_aggregation_runs_id_fk", + "tableFrom": "code_review_memory_proposals", + "tableTo": "code_review_memory_aggregation_runs", + "columnsFrom": [ + "aggregation_run_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "code_review_memory_proposals_edited_by_user_id_kilocode_users_id_fk": { + "name": "code_review_memory_proposals_edited_by_user_id_kilocode_users_id_fk", + "tableFrom": "code_review_memory_proposals", + "tableTo": "kilocode_users", + "columnsFrom": [ + "edited_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "code_review_memory_proposals_approved_by_user_id_kilocode_users_id_fk": { + "name": "code_review_memory_proposals_approved_by_user_id_kilocode_users_id_fk", + "tableFrom": "code_review_memory_proposals", + "tableTo": "kilocode_users", + "columnsFrom": [ + "approved_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "code_review_memory_proposals_rejected_by_user_id_kilocode_users_id_fk": { + "name": "code_review_memory_proposals_rejected_by_user_id_kilocode_users_id_fk", + "tableFrom": "code_review_memory_proposals", + "tableTo": "kilocode_users", + "columnsFrom": [ + "rejected_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "code_review_memory_proposals_owner_check": { + "name": "code_review_memory_proposals_owner_check", + "value": "(\n (\"code_review_memory_proposals\".\"owned_by_user_id\" IS NOT NULL AND \"code_review_memory_proposals\".\"owned_by_organization_id\" IS NULL) OR\n (\"code_review_memory_proposals\".\"owned_by_user_id\" IS NULL AND \"code_review_memory_proposals\".\"owned_by_organization_id\" IS NOT NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.contributor_champion_contributors": { + "name": "contributor_champion_contributors", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "github_login": { + "name": "github_login", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "github_profile_url": { + "name": "github_profile_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "github_user_id": { + "name": "github_user_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "first_contribution_at": { + "name": "first_contribution_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_contribution_at": { + "name": "last_contribution_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "all_time_contributions": { + "name": "all_time_contributions", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "manual_email": { + "name": "manual_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_contributor_champion_contributors_last_contribution_at": { + "name": "IDX_contributor_champion_contributors_last_contribution_at", + "columns": [ + { + "expression": "last_contribution_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_contributor_champion_contributors_manual_email": { + "name": "IDX_contributor_champion_contributors_manual_email", + "columns": [ + { + "expression": "manual_email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_contributor_champion_contributors_github_login": { + "name": "UQ_contributor_champion_contributors_github_login", + "nullsNotDistinct": false, + "columns": [ + "github_login" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.contributor_champion_events": { + "name": "contributor_champion_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "contributor_id": { + "name": "contributor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "repo_full_name": { + "name": "repo_full_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "github_pr_number": { + "name": "github_pr_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "github_pr_url": { + "name": "github_pr_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "github_pr_title": { + "name": "github_pr_title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "github_author_login": { + "name": "github_author_login", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "github_author_email": { + "name": "github_author_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "merged_at": { + "name": "merged_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_contributor_champion_events_contributor_id": { + "name": "IDX_contributor_champion_events_contributor_id", + "columns": [ + { + "expression": "contributor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_contributor_champion_events_merged_at": { + "name": "IDX_contributor_champion_events_merged_at", + "columns": [ + { + "expression": "merged_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_contributor_champion_events_author_email": { + "name": "IDX_contributor_champion_events_author_email", + "columns": [ + { + "expression": "github_author_email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "contributor_champion_events_contributor_id_contributor_champion_contributors_id_fk": { + "name": "contributor_champion_events_contributor_id_contributor_champion_contributors_id_fk", + "tableFrom": "contributor_champion_events", + "tableTo": "contributor_champion_contributors", + "columnsFrom": [ + "contributor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_contributor_champion_events_repo_pr": { + "name": "UQ_contributor_champion_events_repo_pr", + "nullsNotDistinct": false, + "columns": [ + "repo_full_name", + "github_pr_number" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.contributor_champion_memberships": { + "name": "contributor_champion_memberships", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "contributor_id": { + "name": "contributor_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "selected_tier": { + "name": "selected_tier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enrolled_tier": { + "name": "enrolled_tier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enrolled_at": { + "name": "enrolled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "credit_amount_microdollars": { + "name": "credit_amount_microdollars", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "credits_last_granted_at": { + "name": "credits_last_granted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "linked_kilo_user_id": { + "name": "linked_kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_contributor_champion_memberships_credits_due": { + "name": "IDX_contributor_champion_memberships_credits_due", + "columns": [ + { + "expression": "credits_last_granted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"contributor_champion_memberships\".\"enrolled_tier\" IS NOT NULL AND \"contributor_champion_memberships\".\"credit_amount_microdollars\" > 0", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_contributor_champion_memberships_linked_kilo_user_id": { + "name": "IDX_contributor_champion_memberships_linked_kilo_user_id", + "columns": [ + { + "expression": "linked_kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "contributor_champion_memberships_contributor_id_contributor_champion_contributors_id_fk": { + "name": "contributor_champion_memberships_contributor_id_contributor_champion_contributors_id_fk", + "tableFrom": "contributor_champion_memberships", + "tableTo": "contributor_champion_contributors", + "columnsFrom": [ + "contributor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "contributor_champion_memberships_linked_kilo_user_id_kilocode_users_id_fk": { + "name": "contributor_champion_memberships_linked_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "contributor_champion_memberships", + "tableTo": "kilocode_users", + "columnsFrom": [ + "linked_kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_contributor_champion_memberships_contributor_id": { + "name": "UQ_contributor_champion_memberships_contributor_id", + "nullsNotDistinct": false, + "columns": [ + "contributor_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "contributor_champion_memberships_selected_tier_check": { + "name": "contributor_champion_memberships_selected_tier_check", + "value": "\"contributor_champion_memberships\".\"selected_tier\" IS NULL OR \"contributor_champion_memberships\".\"selected_tier\" IN ('contributor', 'ambassador', 'champion')" + }, + "contributor_champion_memberships_enrolled_tier_check": { + "name": "contributor_champion_memberships_enrolled_tier_check", + "value": "\"contributor_champion_memberships\".\"enrolled_tier\" IS NULL OR \"contributor_champion_memberships\".\"enrolled_tier\" IN ('contributor', 'ambassador', 'champion')" + } + }, + "isRLSEnabled": false + }, + "public.contributor_champion_sync_state": { + "name": "contributor_champion_sync_state", + "schema": "", + "columns": { + "repo_full_name": { + "name": "repo_full_name", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "last_merged_at": { + "name": "last_merged_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.credit_campaigns": { + "name": "credit_campaigns", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credit_category": { + "name": "credit_category", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount_microdollars": { + "name": "amount_microdollars", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "credit_expiry_hours": { + "name": "credit_expiry_hours", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "campaign_ends_at": { + "name": "campaign_ends_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "total_redemptions_allowed": { + "name": "total_redemptions_allowed", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by_kilo_user_id": { + "name": "created_by_kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_credit_campaigns_slug": { + "name": "UQ_credit_campaigns_slug", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_credit_campaigns_credit_category": { + "name": "UQ_credit_campaigns_credit_category", + "columns": [ + { + "expression": "credit_category", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "credit_campaigns_slug_format_check": { + "name": "credit_campaigns_slug_format_check", + "value": "\"credit_campaigns\".\"slug\" ~ '^[a-z0-9-]{5,40}$'" + }, + "credit_campaigns_amount_positive_check": { + "name": "credit_campaigns_amount_positive_check", + "value": "\"credit_campaigns\".\"amount_microdollars\" > 0" + }, + "credit_campaigns_credit_expiry_hours_positive_check": { + "name": "credit_campaigns_credit_expiry_hours_positive_check", + "value": "\"credit_campaigns\".\"credit_expiry_hours\" IS NULL OR \"credit_campaigns\".\"credit_expiry_hours\" > 0" + }, + "credit_campaigns_total_redemptions_allowed_positive_check": { + "name": "credit_campaigns_total_redemptions_allowed_positive_check", + "value": "\"credit_campaigns\".\"total_redemptions_allowed\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.credit_transactions": { + "name": "credit_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount_microdollars": { + "name": "amount_microdollars", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "expiration_baseline_microdollars_used": { + "name": "expiration_baseline_microdollars_used", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "original_baseline_microdollars_used": { + "name": "original_baseline_microdollars_used", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "is_free": { + "name": "is_free", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "original_transaction_id": { + "name": "original_transaction_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "stripe_payment_id": { + "name": "stripe_payment_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "coinbase_credit_block_id": { + "name": "coinbase_credit_block_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "credit_category": { + "name": "credit_category", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expiry_date": { + "name": "expiry_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "check_category_uniqueness": { + "name": "check_category_uniqueness", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "IDX_credit_transactions_created_at": { + "name": "IDX_credit_transactions_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_credit_transactions_is_free": { + "name": "IDX_credit_transactions_is_free", + "columns": [ + { + "expression": "is_free", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_credit_transactions_kilo_user_id": { + "name": "IDX_credit_transactions_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_credit_transactions_credit_category": { + "name": "IDX_credit_transactions_credit_category", + "columns": [ + { + "expression": "credit_category", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_credit_transactions_stripe_payment_id": { + "name": "IDX_credit_transactions_stripe_payment_id", + "columns": [ + { + "expression": "stripe_payment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_credit_transactions_original_transaction_id": { + "name": "IDX_credit_transactions_original_transaction_id", + "columns": [ + { + "expression": "original_transaction_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_credit_transactions_coinbase_credit_block_id": { + "name": "IDX_credit_transactions_coinbase_credit_block_id", + "columns": [ + { + "expression": "coinbase_credit_block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_credit_transactions_organization_id": { + "name": "IDX_credit_transactions_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_credit_transactions_unique_category": { + "name": "IDX_credit_transactions_unique_category", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "credit_category", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"credit_transactions\".\"check_category_uniqueness\" = TRUE", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.custom_llm2": { + "name": "custom_llm2", + "schema": "", + "columns": { + "public_id": { + "name": "public_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "definition": { + "name": "definition", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deleted_user_email_tombstones": { + "name": "deleted_user_email_tombstones", + "schema": "", + "columns": { + "normalized_email_hash": { + "name": "normalized_email_hash", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment_builds": { + "name": "deployment_builds", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "deployment_id": { + "name": "deployment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_deployment_builds_deployment_id": { + "name": "idx_deployment_builds_deployment_id", + "columns": [ + { + "expression": "deployment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_deployment_builds_status": { + "name": "idx_deployment_builds_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "deployment_builds_deployment_id_deployments_id_fk": { + "name": "deployment_builds_deployment_id_deployments_id_fk", + "tableFrom": "deployment_builds", + "tableTo": "deployments", + "columnsFrom": [ + "deployment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment_env_vars": { + "name": "deployment_env_vars", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "deployment_id": { + "name": "deployment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_secret": { + "name": "is_secret", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_deployment_env_vars_deployment_id": { + "name": "idx_deployment_env_vars_deployment_id", + "columns": [ + { + "expression": "deployment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "deployment_env_vars_deployment_id_deployments_id_fk": { + "name": "deployment_env_vars_deployment_id_deployments_id_fk", + "tableFrom": "deployment_env_vars", + "tableTo": "deployments", + "columnsFrom": [ + "deployment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_deployment_env_vars_deployment_key": { + "name": "UQ_deployment_env_vars_deployment_key", + "nullsNotDistinct": false, + "columns": [ + "deployment_id", + "key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment_events": { + "name": "deployment_events", + "schema": "", + "columns": { + "build_id": { + "name": "build_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "event_id": { + "name": "event_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'log'" + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "idx_deployment_events_build_id": { + "name": "idx_deployment_events_build_id", + "columns": [ + { + "expression": "build_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_deployment_events_timestamp": { + "name": "idx_deployment_events_timestamp", + "columns": [ + { + "expression": "timestamp", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_deployment_events_type": { + "name": "idx_deployment_events_type", + "columns": [ + { + "expression": "event_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "deployment_events_build_id_deployment_builds_id_fk": { + "name": "deployment_events_build_id_deployment_builds_id_fk", + "tableFrom": "deployment_events", + "tableTo": "deployment_builds", + "columnsFrom": [ + "build_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "deployment_events_build_id_event_id_pk": { + "name": "deployment_events_build_id_event_id_pk", + "columns": [ + "build_id", + "event_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment_threat_detections": { + "name": "deployment_threat_detections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "deployment_id": { + "name": "deployment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "build_id": { + "name": "build_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "threat_type": { + "name": "threat_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_deployment_threat_detections_deployment_id": { + "name": "idx_deployment_threat_detections_deployment_id", + "columns": [ + { + "expression": "deployment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_deployment_threat_detections_created_at": { + "name": "idx_deployment_threat_detections_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "deployment_threat_detections_deployment_id_deployments_id_fk": { + "name": "deployment_threat_detections_deployment_id_deployments_id_fk", + "tableFrom": "deployment_threat_detections", + "tableTo": "deployments", + "columnsFrom": [ + "deployment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_threat_detections_build_id_deployment_builds_id_fk": { + "name": "deployment_threat_detections_build_id_deployment_builds_id_fk", + "tableFrom": "deployment_threat_detections", + "tableTo": "deployment_builds", + "columnsFrom": [ + "build_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployments": { + "name": "deployments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "deployment_slug": { + "name": "deployment_slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_worker_name": { + "name": "internal_worker_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "repository_source": { + "name": "repository_source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deployment_url": { + "name": "deployment_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform_integration_id": { + "name": "platform_integration_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "source_type": { + "name": "source_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "git_auth_token": { + "name": "git_auth_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_deployed_at": { + "name": "last_deployed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_build_id": { + "name": "last_build_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "threat_status": { + "name": "threat_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_from": { + "name": "created_from", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_deployments_owned_by_user_id": { + "name": "idx_deployments_owned_by_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_deployments_owned_by_organization_id": { + "name": "idx_deployments_owned_by_organization_id", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_deployments_platform_integration_id": { + "name": "idx_deployments_platform_integration_id", + "columns": [ + { + "expression": "platform_integration_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_deployments_repository_source_branch": { + "name": "idx_deployments_repository_source_branch", + "columns": [ + { + "expression": "repository_source", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "branch", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_deployments_threat_status_pending": { + "name": "idx_deployments_threat_status_pending", + "columns": [ + { + "expression": "threat_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"deployments\".\"threat_status\" = 'pending_scan'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "deployments_owned_by_user_id_kilocode_users_id_fk": { + "name": "deployments_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "deployments", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "deployments_owned_by_organization_id_organizations_id_fk": { + "name": "deployments_owned_by_organization_id_organizations_id_fk", + "tableFrom": "deployments", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_deployments_deployment_slug": { + "name": "UQ_deployments_deployment_slug", + "nullsNotDistinct": false, + "columns": [ + "deployment_slug" + ] + } + }, + "policies": {}, + "checkConstraints": { + "deployments_owner_check": { + "name": "deployments_owner_check", + "value": "(\n (\"deployments\".\"owned_by_user_id\" IS NOT NULL AND \"deployments\".\"owned_by_organization_id\" IS NULL) OR\n (\"deployments\".\"owned_by_user_id\" IS NULL AND \"deployments\".\"owned_by_organization_id\" IS NOT NULL)\n )" + }, + "deployments_source_type_check": { + "name": "deployments_source_type_check", + "value": "\"deployments\".\"source_type\" IN ('github', 'git', 'app-builder')" + } + }, + "isRLSEnabled": false + }, + "public.device_auth_requests": { + "name": "device_auth_requests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "approved_at": { + "name": "approved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_device_auth_requests_code": { + "name": "UQ_device_auth_requests_code", + "columns": [ + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_device_auth_requests_status": { + "name": "IDX_device_auth_requests_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_device_auth_requests_expires_at": { + "name": "IDX_device_auth_requests_expires_at", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_device_auth_requests_kilo_user_id": { + "name": "IDX_device_auth_requests_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "device_auth_requests_kilo_user_id_kilocode_users_id_fk": { + "name": "device_auth_requests_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "device_auth_requests", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.discord_gateway_listener": { + "name": "discord_gateway_listener", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "default": 1 + }, + "listener_id": { + "name": "listener_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.editor_name": { + "name": "editor_name", + "schema": "", + "columns": { + "editor_name_id": { + "name": "editor_name_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "editor_name": { + "name": "editor_name", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UQ_editor_name": { + "name": "UQ_editor_name", + "columns": [ + { + "expression": "editor_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.enrichment_data": { + "name": "enrichment_data", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "github_enrichment_data": { + "name": "github_enrichment_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "linkedin_enrichment_data": { + "name": "linkedin_enrichment_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "clay_enrichment_data": { + "name": "clay_enrichment_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_enrichment_data_user_id": { + "name": "IDX_enrichment_data_user_id", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "enrichment_data_user_id_kilocode_users_id_fk": { + "name": "enrichment_data_user_id_kilocode_users_id_fk", + "tableFrom": "enrichment_data", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_enrichment_data_user_id": { + "name": "UQ_enrichment_data_user_id", + "nullsNotDistinct": false, + "columns": [ + "user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.exa_monthly_usage": { + "name": "exa_monthly_usage", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "month": { + "name": "month", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "total_cost_microdollars": { + "name": "total_cost_microdollars", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_charged_microdollars": { + "name": "total_charged_microdollars", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "request_count": { + "name": "request_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "free_allowance_microdollars": { + "name": "free_allowance_microdollars", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 10000000 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_exa_monthly_usage_personal": { + "name": "idx_exa_monthly_usage_personal", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "month", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"exa_monthly_usage\".\"organization_id\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_exa_monthly_usage_org": { + "name": "idx_exa_monthly_usage_org", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "month", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"exa_monthly_usage\".\"organization_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.exa_usage_log": { + "name": "exa_usage_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cost_microdollars": { + "name": "cost_microdollars", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "charged_to_balance": { + "name": "charged_to_balance", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "feature_id": { + "name": "feature_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_exa_usage_log_user_created": { + "name": "idx_exa_usage_log_user_created", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "exa_usage_log_id_created_at_pk": { + "name": "exa_usage_log_id_created_at_pk", + "columns": [ + "id", + "created_at" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.feature": { + "name": "feature", + "schema": "", + "columns": { + "feature_id": { + "name": "feature_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "feature": { + "name": "feature", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UQ_feature": { + "name": "UQ_feature", + "columns": [ + { + "expression": "feature", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.finish_reason": { + "name": "finish_reason", + "schema": "", + "columns": { + "finish_reason_id": { + "name": "finish_reason_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "finish_reason": { + "name": "finish_reason", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UQ_finish_reason": { + "name": "UQ_finish_reason", + "columns": [ + { + "expression": "finish_reason", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.free_model_usage": { + "name": "free_model_usage", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_free_model_usage_ip_created_at": { + "name": "idx_free_model_usage_ip_created_at", + "columns": [ + { + "expression": "ip_address", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_free_model_usage_created_at": { + "name": "idx_free_model_usage_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github_branch_pull_requests": { + "name": "github_branch_pull_requests", + "schema": "", + "columns": { + "git_url": { + "name": "git_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "git_branch": { + "name": "git_branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pr_url": { + "name": "pr_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pr_number": { + "name": "pr_number", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "pr_state": { + "name": "pr_state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pr_title": { + "name": "pr_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pr_head_sha": { + "name": "pr_head_sha", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pr_review_decision": { + "name": "pr_review_decision", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "review_decision_pending": { + "name": "review_decision_pending", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "review_decision_fetching_at": { + "name": "review_decision_fetching_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "pr_last_synced_at": { + "name": "pr_last_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_github_branch_prs_org": { + "name": "UQ_github_branch_prs_org", + "columns": [ + { + "expression": "git_url", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "git_branch", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"github_branch_pull_requests\".\"owned_by_organization_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_github_branch_prs_user": { + "name": "UQ_github_branch_prs_user", + "columns": [ + { + "expression": "git_url", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "git_branch", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"github_branch_pull_requests\".\"owned_by_user_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "github_branch_pull_requests_owned_by_organization_id_organizations_id_fk": { + "name": "github_branch_pull_requests_owned_by_organization_id_organizations_id_fk", + "tableFrom": "github_branch_pull_requests", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "github_branch_pull_requests_owned_by_user_id_kilocode_users_id_fk": { + "name": "github_branch_pull_requests_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "github_branch_pull_requests", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "github_branch_pull_requests_owner_check": { + "name": "github_branch_pull_requests_owner_check", + "value": "(\n (\"github_branch_pull_requests\".\"owned_by_organization_id\" IS NOT NULL AND \"github_branch_pull_requests\".\"owned_by_user_id\" IS NULL) OR\n (\"github_branch_pull_requests\".\"owned_by_organization_id\" IS NULL AND \"github_branch_pull_requests\".\"owned_by_user_id\" IS NOT NULL)\n )" + }, + "github_branch_pull_requests_review_decision_check": { + "name": "github_branch_pull_requests_review_decision_check", + "value": "\"github_branch_pull_requests\".\"pr_review_decision\" IS NULL OR \"github_branch_pull_requests\".\"pr_review_decision\" IN ('approved', 'changes_requested', 'review_required')" + } + }, + "isRLSEnabled": false + }, + "public.http_ip": { + "name": "http_ip", + "schema": "", + "columns": { + "http_ip_id": { + "name": "http_ip_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "http_ip": { + "name": "http_ip", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UQ_http_ip": { + "name": "UQ_http_ip", + "columns": [ + { + "expression": "http_ip", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.http_user_agent": { + "name": "http_user_agent", + "schema": "", + "columns": { + "http_user_agent_id": { + "name": "http_user_agent_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "http_user_agent": { + "name": "http_user_agent", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UQ_http_user_agent": { + "name": "UQ_http_user_agent", + "columns": [ + { + "expression": "http_user_agent", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.impact_advocate_participants": { + "name": "impact_advocate_participants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "advocate_id": { + "name": "advocate_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "advocate_account_id": { + "name": "advocate_account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "opaque_referral_identifier": { + "name": "opaque_referral_identifier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "contact_email": { + "name": "contact_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "locale": { + "name": "locale", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "country_code": { + "name": "country_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registration_state": { + "name": "registration_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "registered_at": { + "name": "registered_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_registration_attempt_at": { + "name": "last_registration_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_error_code": { + "name": "last_error_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_error_message": { + "name": "last_error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_impact_advocate_participants_registration_state": { + "name": "IDX_impact_advocate_participants_registration_state", + "columns": [ + { + "expression": "registration_state", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "impact_advocate_participants_user_id_kilocode_users_id_fk": { + "name": "impact_advocate_participants_user_id_kilocode_users_id_fk", + "tableFrom": "impact_advocate_participants", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_impact_advocate_participants_user_id": { + "name": "UQ_impact_advocate_participants_user_id", + "nullsNotDistinct": false, + "columns": [ + "user_id" + ] + }, + "UQ_impact_advocate_participants_opaque_referral_identifier": { + "name": "UQ_impact_advocate_participants_opaque_referral_identifier", + "nullsNotDistinct": false, + "columns": [ + "opaque_referral_identifier" + ] + } + }, + "policies": {}, + "checkConstraints": { + "impact_advocate_participants_registration_state_check": { + "name": "impact_advocate_participants_registration_state_check", + "value": "\"impact_advocate_participants\".\"registration_state\" IN ('pending', 'retrying', 'registered', 'failed')" + } + }, + "isRLSEnabled": false + }, + "public.impact_advocate_registration_attempts": { + "name": "impact_advocate_registration_attempts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "dedupe_key": { + "name": "dedupe_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "opaque_cookie_value": { + "name": "opaque_cookie_value", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cookie_value_length": { + "name": "cookie_value_length", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "delivery_state": { + "name": "delivery_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "request_payload": { + "name": "request_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "response_payload": { + "name": "response_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "response_status_code": { + "name": "response_status_code", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_retry_at": { + "name": "next_retry_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_impact_advocate_registration_attempts_participant_id": { + "name": "IDX_impact_advocate_registration_attempts_participant_id", + "columns": [ + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_impact_advocate_registration_attempts_delivery_state": { + "name": "IDX_impact_advocate_registration_attempts_delivery_state", + "columns": [ + { + "expression": "delivery_state", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "impact_advocate_registration_attempts_participant_id_impact_advocate_participants_id_fk": { + "name": "impact_advocate_registration_attempts_participant_id_impact_advocate_participants_id_fk", + "tableFrom": "impact_advocate_registration_attempts", + "tableTo": "impact_advocate_participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_impact_advocate_registration_attempts_dedupe_key": { + "name": "UQ_impact_advocate_registration_attempts_dedupe_key", + "nullsNotDistinct": false, + "columns": [ + "dedupe_key" + ] + } + }, + "policies": {}, + "checkConstraints": { + "impact_advocate_registration_attempts_delivery_state_check": { + "name": "impact_advocate_registration_attempts_delivery_state_check", + "value": "\"impact_advocate_registration_attempts\".\"delivery_state\" IN ('queued', 'sending', 'succeeded', 'failed')" + }, + "impact_advocate_registration_attempts_cookie_value_length_non_negative_check": { + "name": "impact_advocate_registration_attempts_cookie_value_length_non_negative_check", + "value": "\"impact_advocate_registration_attempts\".\"cookie_value_length\" >= 0" + }, + "impact_advocate_registration_attempts_attempt_count_non_negative_check": { + "name": "impact_advocate_registration_attempts_attempt_count_non_negative_check", + "value": "\"impact_advocate_registration_attempts\".\"attempt_count\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.impact_advocate_reward_redemptions": { + "name": "impact_advocate_reward_redemptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "reward_id": { + "name": "reward_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "dedupe_key": { + "name": "dedupe_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "beneficiary_user_id": { + "name": "beneficiary_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "impact_reward_id": { + "name": "impact_reward_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "request_payload": { + "name": "request_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "lookup_response_payload": { + "name": "lookup_response_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "redeem_response_payload": { + "name": "redeem_response_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "response_status_code": { + "name": "response_status_code", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_retry_at": { + "name": "next_retry_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "redeemed_at": { + "name": "redeemed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_impact_advocate_reward_redemptions_beneficiary_user_id": { + "name": "IDX_impact_advocate_reward_redemptions_beneficiary_user_id", + "columns": [ + { + "expression": "beneficiary_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_impact_advocate_reward_redemptions_state": { + "name": "IDX_impact_advocate_reward_redemptions_state", + "columns": [ + { + "expression": "state", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "impact_advocate_reward_redemptions_reward_id_kiloclaw_referral_rewards_id_fk": { + "name": "impact_advocate_reward_redemptions_reward_id_kiloclaw_referral_rewards_id_fk", + "tableFrom": "impact_advocate_reward_redemptions", + "tableTo": "kiloclaw_referral_rewards", + "columnsFrom": [ + "reward_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "impact_advocate_reward_redemptions_beneficiary_user_id_kilocode_users_id_fk": { + "name": "impact_advocate_reward_redemptions_beneficiary_user_id_kilocode_users_id_fk", + "tableFrom": "impact_advocate_reward_redemptions", + "tableTo": "kilocode_users", + "columnsFrom": [ + "beneficiary_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_impact_advocate_reward_redemptions_reward_id": { + "name": "UQ_impact_advocate_reward_redemptions_reward_id", + "nullsNotDistinct": false, + "columns": [ + "reward_id" + ] + }, + "UQ_impact_advocate_reward_redemptions_dedupe_key": { + "name": "UQ_impact_advocate_reward_redemptions_dedupe_key", + "nullsNotDistinct": false, + "columns": [ + "dedupe_key" + ] + } + }, + "policies": {}, + "checkConstraints": { + "impact_advocate_reward_redemptions_state_check": { + "name": "impact_advocate_reward_redemptions_state_check", + "value": "\"impact_advocate_reward_redemptions\".\"state\" IN ('queued', 'retrying', 'redeemed', 'failed')" + }, + "impact_advocate_reward_redemptions_attempt_count_non_negative_check": { + "name": "impact_advocate_reward_redemptions_attempt_count_non_negative_check", + "value": "\"impact_advocate_reward_redemptions\".\"attempt_count\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.impact_conversion_reports": { + "name": "impact_conversion_reports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "conversion_id": { + "name": "conversion_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "dedupe_key": { + "name": "dedupe_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "action_tracker_id": { + "name": "action_tracker_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "order_id": { + "name": "order_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "request_payload": { + "name": "request_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "response_payload": { + "name": "response_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "response_status_code": { + "name": "response_status_code", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_retry_at": { + "name": "next_retry_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "delivered_at": { + "name": "delivered_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_impact_conversion_reports_conversion_id": { + "name": "IDX_impact_conversion_reports_conversion_id", + "columns": [ + { + "expression": "conversion_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_impact_conversion_reports_state": { + "name": "IDX_impact_conversion_reports_state", + "columns": [ + { + "expression": "state", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "impact_conversion_reports_conversion_id_kiloclaw_referral_conversions_id_fk": { + "name": "impact_conversion_reports_conversion_id_kiloclaw_referral_conversions_id_fk", + "tableFrom": "impact_conversion_reports", + "tableTo": "kiloclaw_referral_conversions", + "columnsFrom": [ + "conversion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_impact_conversion_reports_dedupe_key": { + "name": "UQ_impact_conversion_reports_dedupe_key", + "nullsNotDistinct": false, + "columns": [ + "dedupe_key" + ] + } + }, + "policies": {}, + "checkConstraints": { + "impact_conversion_reports_state_check": { + "name": "impact_conversion_reports_state_check", + "value": "\"impact_conversion_reports\".\"state\" IN ('queued', 'retrying', 'delivered', 'failed')" + }, + "impact_conversion_reports_attempt_count_non_negative_check": { + "name": "impact_conversion_reports_attempt_count_non_negative_check", + "value": "\"impact_conversion_reports\".\"attempt_count\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.ja4_digest": { + "name": "ja4_digest", + "schema": "", + "columns": { + "ja4_digest_id": { + "name": "ja4_digest_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "ja4_digest": { + "name": "ja4_digest", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UQ_ja4_digest": { + "name": "UQ_ja4_digest", + "columns": [ + { + "expression": "ja4_digest", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kilo_pass_audit_log": { + "name": "kilo_pass_audit_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "kilo_pass_subscription_id": { + "name": "kilo_pass_subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "result": { + "name": "result", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_event_id": { + "name": "stripe_event_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_invoice_id": { + "name": "stripe_invoice_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_subscription_id": { + "name": "stripe_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "related_credit_transaction_id": { + "name": "related_credit_transaction_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "related_monthly_issuance_id": { + "name": "related_monthly_issuance_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "payload_json": { + "name": "payload_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + } + }, + "indexes": { + "IDX_kilo_pass_audit_log_created_at": { + "name": "IDX_kilo_pass_audit_log_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_audit_log_kilo_user_id": { + "name": "IDX_kilo_pass_audit_log_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_audit_log_kilo_pass_subscription_id": { + "name": "IDX_kilo_pass_audit_log_kilo_pass_subscription_id", + "columns": [ + { + "expression": "kilo_pass_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_audit_log_action": { + "name": "IDX_kilo_pass_audit_log_action", + "columns": [ + { + "expression": "action", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_audit_log_result": { + "name": "IDX_kilo_pass_audit_log_result", + "columns": [ + { + "expression": "result", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_audit_log_idempotency_key": { + "name": "IDX_kilo_pass_audit_log_idempotency_key", + "columns": [ + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_audit_log_stripe_event_id": { + "name": "IDX_kilo_pass_audit_log_stripe_event_id", + "columns": [ + { + "expression": "stripe_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_audit_log_stripe_invoice_id": { + "name": "IDX_kilo_pass_audit_log_stripe_invoice_id", + "columns": [ + { + "expression": "stripe_invoice_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_audit_log_stripe_subscription_id": { + "name": "IDX_kilo_pass_audit_log_stripe_subscription_id", + "columns": [ + { + "expression": "stripe_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_audit_log_related_credit_transaction_id": { + "name": "IDX_kilo_pass_audit_log_related_credit_transaction_id", + "columns": [ + { + "expression": "related_credit_transaction_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_audit_log_related_monthly_issuance_id": { + "name": "IDX_kilo_pass_audit_log_related_monthly_issuance_id", + "columns": [ + { + "expression": "related_monthly_issuance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kilo_pass_audit_log_kilo_user_id_kilocode_users_id_fk": { + "name": "kilo_pass_audit_log_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "kilo_pass_audit_log", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "kilo_pass_audit_log_kilo_pass_subscription_id_kilo_pass_subscriptions_id_fk": { + "name": "kilo_pass_audit_log_kilo_pass_subscription_id_kilo_pass_subscriptions_id_fk", + "tableFrom": "kilo_pass_audit_log", + "tableTo": "kilo_pass_subscriptions", + "columnsFrom": [ + "kilo_pass_subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "kilo_pass_audit_log_related_credit_transaction_id_credit_transactions_id_fk": { + "name": "kilo_pass_audit_log_related_credit_transaction_id_credit_transactions_id_fk", + "tableFrom": "kilo_pass_audit_log", + "tableTo": "credit_transactions", + "columnsFrom": [ + "related_credit_transaction_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "kilo_pass_audit_log_related_monthly_issuance_id_kilo_pass_issuances_id_fk": { + "name": "kilo_pass_audit_log_related_monthly_issuance_id_kilo_pass_issuances_id_fk", + "tableFrom": "kilo_pass_audit_log", + "tableTo": "kilo_pass_issuances", + "columnsFrom": [ + "related_monthly_issuance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kilo_pass_audit_log_action_check": { + "name": "kilo_pass_audit_log_action_check", + "value": "\"kilo_pass_audit_log\".\"action\" IN ('stripe_webhook_received', 'kilo_pass_invoice_paid_handled', 'store_purchase_completed', 'store_notification_received', 'store_subscription_renewed', 'store_subscription_canceled', 'store_subscription_expired', 'store_subscription_refunded', 'base_credits_issued', 'bonus_credits_issued', 'bonus_credits_skipped_idempotent', 'first_month_50pct_promo_issued', 'yearly_monthly_base_cron_started', 'yearly_monthly_base_cron_completed', 'issue_yearly_remaining_credits', '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', 'app_store_transaction', 'google_play_transaction', 'cron')" + } + }, + "isRLSEnabled": false + }, + "public.kilo_pass_pause_events": { + "name": "kilo_pass_pause_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_pass_subscription_id": { + "name": "kilo_pass_subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "paused_at": { + "name": "paused_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "resumes_at": { + "name": "resumes_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "resumed_at": { + "name": "resumed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_kilo_pass_pause_events_subscription_id": { + "name": "IDX_kilo_pass_pause_events_subscription_id", + "columns": [ + { + "expression": "kilo_pass_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kilo_pass_pause_events_one_open_per_sub": { + "name": "UQ_kilo_pass_pause_events_one_open_per_sub", + "columns": [ + { + "expression": "kilo_pass_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kilo_pass_pause_events\".\"resumed_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kilo_pass_pause_events_kilo_pass_subscription_id_kilo_pass_subscriptions_id_fk": { + "name": "kilo_pass_pause_events_kilo_pass_subscription_id_kilo_pass_subscriptions_id_fk", + "tableFrom": "kilo_pass_pause_events", + "tableTo": "kilo_pass_subscriptions", + "columnsFrom": [ + "kilo_pass_subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kilo_pass_pause_events_resumed_at_after_paused_at_check": { + "name": "kilo_pass_pause_events_resumed_at_after_paused_at_check", + "value": "\"kilo_pass_pause_events\".\"resumed_at\" IS NULL OR \"kilo_pass_pause_events\".\"resumed_at\" >= \"kilo_pass_pause_events\".\"paused_at\"" + } + }, + "isRLSEnabled": false + }, + "public.kilo_pass_scheduled_changes": { + "name": "kilo_pass_scheduled_changes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_subscription_id": { + "name": "stripe_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "from_tier": { + "name": "from_tier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "from_cadence": { + "name": "from_cadence", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "to_tier": { + "name": "to_tier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "to_cadence": { + "name": "to_cadence", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_schedule_id": { + "name": "stripe_schedule_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "effective_at": { + "name": "effective_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_kilo_pass_scheduled_changes_kilo_user_id": { + "name": "IDX_kilo_pass_scheduled_changes_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_scheduled_changes_status": { + "name": "IDX_kilo_pass_scheduled_changes_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_scheduled_changes_stripe_subscription_id": { + "name": "IDX_kilo_pass_scheduled_changes_stripe_subscription_id", + "columns": [ + { + "expression": "stripe_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kilo_pass_scheduled_changes_active_stripe_subscription_id": { + "name": "UQ_kilo_pass_scheduled_changes_active_stripe_subscription_id", + "columns": [ + { + "expression": "stripe_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kilo_pass_scheduled_changes\".\"deleted_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_scheduled_changes_effective_at": { + "name": "IDX_kilo_pass_scheduled_changes_effective_at", + "columns": [ + { + "expression": "effective_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_scheduled_changes_deleted_at": { + "name": "IDX_kilo_pass_scheduled_changes_deleted_at", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kilo_pass_scheduled_changes_kilo_user_id_kilocode_users_id_fk": { + "name": "kilo_pass_scheduled_changes_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "kilo_pass_scheduled_changes", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "kilo_pass_scheduled_changes_stripe_subscription_id_kilo_pass_subscriptions_stripe_subscription_id_fk": { + "name": "kilo_pass_scheduled_changes_stripe_subscription_id_kilo_pass_subscriptions_stripe_subscription_id_fk", + "tableFrom": "kilo_pass_scheduled_changes", + "tableTo": "kilo_pass_subscriptions", + "columnsFrom": [ + "stripe_subscription_id" + ], + "columnsTo": [ + "stripe_subscription_id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kilo_pass_scheduled_changes_from_tier_check": { + "name": "kilo_pass_scheduled_changes_from_tier_check", + "value": "\"kilo_pass_scheduled_changes\".\"from_tier\" IN ('tier_19', 'tier_49', 'tier_199')" + }, + "kilo_pass_scheduled_changes_from_cadence_check": { + "name": "kilo_pass_scheduled_changes_from_cadence_check", + "value": "\"kilo_pass_scheduled_changes\".\"from_cadence\" IN ('monthly', 'yearly')" + }, + "kilo_pass_scheduled_changes_to_tier_check": { + "name": "kilo_pass_scheduled_changes_to_tier_check", + "value": "\"kilo_pass_scheduled_changes\".\"to_tier\" IN ('tier_19', 'tier_49', 'tier_199')" + }, + "kilo_pass_scheduled_changes_to_cadence_check": { + "name": "kilo_pass_scheduled_changes_to_cadence_check", + "value": "\"kilo_pass_scheduled_changes\".\"to_cadence\" IN ('monthly', 'yearly')" + }, + "kilo_pass_scheduled_changes_status_check": { + "name": "kilo_pass_scheduled_changes_status_check", + "value": "\"kilo_pass_scheduled_changes\".\"status\" IN ('not_started', 'active', 'completed', 'released', 'canceled')" + } + }, + "isRLSEnabled": false + }, + "public.kilo_pass_store_events": { + "name": "kilo_pass_store_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "payment_provider": { + "name": "payment_provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_id": { + "name": "event_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_subscription_id": { + "name": "provider_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_transaction_id": { + "name": "provider_transaction_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "app_account_token": { + "name": "app_account_token", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "product_id": { + "name": "product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "environment": { + "name": "environment", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload_json": { + "name": "payload_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "processing_started_at": { + "name": "processing_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_kilo_pass_store_events_provider_event": { + "name": "UQ_kilo_pass_store_events_provider_event", + "columns": [ + { + "expression": "payment_provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_store_events_provider_subscription": { + "name": "IDX_kilo_pass_store_events_provider_subscription", + "columns": [ + { + "expression": "payment_provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_store_events_app_account_token": { + "name": "IDX_kilo_pass_store_events_app_account_token", + "columns": [ + { + "expression": "app_account_token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kilo_pass_store_events_payment_provider_check": { + "name": "kilo_pass_store_events_payment_provider_check", + "value": "\"kilo_pass_store_events\".\"payment_provider\" IN ('stripe', 'app_store', 'google_play')" + } + }, + "isRLSEnabled": false + }, + "public.kilo_pass_store_purchases": { + "name": "kilo_pass_store_purchases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_pass_subscription_id": { + "name": "kilo_pass_subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payment_provider": { + "name": "payment_provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "product_id": { + "name": "product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_subscription_id": { + "name": "provider_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_transaction_id": { + "name": "provider_transaction_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_original_transaction_id": { + "name": "provider_original_transaction_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "app_account_token": { + "name": "app_account_token", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "purchase_token": { + "name": "purchase_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "environment": { + "name": "environment", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "purchased_at": { + "name": "purchased_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "raw_payload_json": { + "name": "raw_payload_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_kilo_pass_store_purchases_provider_transaction": { + "name": "UQ_kilo_pass_store_purchases_provider_transaction", + "columns": [ + { + "expression": "payment_provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_transaction_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_store_purchases_subscription_id": { + "name": "IDX_kilo_pass_store_purchases_subscription_id", + "columns": [ + { + "expression": "kilo_pass_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_store_purchases_user_id": { + "name": "IDX_kilo_pass_store_purchases_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_store_purchases_app_account_token": { + "name": "IDX_kilo_pass_store_purchases_app_account_token", + "columns": [ + { + "expression": "app_account_token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_store_purchases_latest_subscription_purchase": { + "name": "IDX_kilo_pass_store_purchases_latest_subscription_purchase", + "columns": [ + { + "expression": "payment_provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "purchased_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kilo_pass_store_purchases_kilo_pass_subscription_id_kilo_pass_subscriptions_id_fk": { + "name": "kilo_pass_store_purchases_kilo_pass_subscription_id_kilo_pass_subscriptions_id_fk", + "tableFrom": "kilo_pass_store_purchases", + "tableTo": "kilo_pass_subscriptions", + "columnsFrom": [ + "kilo_pass_subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "kilo_pass_store_purchases_kilo_user_id_kilocode_users_id_fk": { + "name": "kilo_pass_store_purchases_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "kilo_pass_store_purchases", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "FK_kilo_pass_store_purchases_subscription_owner_provider": { + "name": "FK_kilo_pass_store_purchases_subscription_owner_provider", + "tableFrom": "kilo_pass_store_purchases", + "tableTo": "kilo_pass_subscriptions", + "columnsFrom": [ + "kilo_pass_subscription_id", + "kilo_user_id", + "payment_provider", + "provider_subscription_id" + ], + "columnsTo": [ + "id", + "kilo_user_id", + "payment_provider", + "provider_subscription_id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kilo_pass_store_purchases_store_provider_check": { + "name": "kilo_pass_store_purchases_store_provider_check", + "value": "\"kilo_pass_store_purchases\".\"payment_provider\" IN ('app_store', 'google_play')" + }, + "kilo_pass_store_purchases_payment_provider_check": { + "name": "kilo_pass_store_purchases_payment_provider_check", + "value": "\"kilo_pass_store_purchases\".\"payment_provider\" IN ('stripe', 'app_store', 'google_play')" + } + }, + "isRLSEnabled": false + }, + "public.kilo_pass_subscriptions": { + "name": "kilo_pass_subscriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payment_provider": { + "name": "payment_provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'stripe'" + }, + "provider_subscription_id": { + "name": "provider_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_subscription_id": { + "name": "stripe_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tier": { + "name": "tier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cadence": { + "name": "cadence", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cancel_at_period_end": { + "name": "cancel_at_period_end", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "current_streak_months": { + "name": "current_streak_months", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_yearly_issue_at": { + "name": "next_yearly_issue_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_kilo_pass_subscriptions_kilo_user_id": { + "name": "IDX_kilo_pass_subscriptions_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_subscriptions_payment_provider": { + "name": "IDX_kilo_pass_subscriptions_payment_provider", + "columns": [ + { + "expression": "payment_provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_subscriptions_status": { + "name": "IDX_kilo_pass_subscriptions_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilo_pass_subscriptions_cadence": { + "name": "IDX_kilo_pass_subscriptions_cadence", + "columns": [ + { + "expression": "cadence", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kilo_pass_subscriptions_provider_subscription": { + "name": "UQ_kilo_pass_subscriptions_provider_subscription", + "columns": [ + { + "expression": "payment_provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kilo_pass_subscriptions\".\"provider_subscription_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kilo_pass_subscriptions_store_purchase_reference": { + "name": "UQ_kilo_pass_subscriptions_store_purchase_reference", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "payment_provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kilo_pass_subscriptions_kilo_user_id_kilocode_users_id_fk": { + "name": "kilo_pass_subscriptions_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "kilo_pass_subscriptions", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "kilo_pass_subscriptions_stripe_subscription_id_unique": { + "name": "kilo_pass_subscriptions_stripe_subscription_id_unique", + "nullsNotDistinct": false, + "columns": [ + "stripe_subscription_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "kilo_pass_subscriptions_current_streak_months_non_negative_check": { + "name": "kilo_pass_subscriptions_current_streak_months_non_negative_check", + "value": "\"kilo_pass_subscriptions\".\"current_streak_months\" >= 0" + }, + "kilo_pass_subscriptions_provider_ids_check": { + "name": "kilo_pass_subscriptions_provider_ids_check", + "value": "(\n \"kilo_pass_subscriptions\".\"payment_provider\" = 'stripe'\n AND \"kilo_pass_subscriptions\".\"provider_subscription_id\" IS NOT NULL\n AND \"kilo_pass_subscriptions\".\"stripe_subscription_id\" IS NOT NULL\n AND \"kilo_pass_subscriptions\".\"provider_subscription_id\" = \"kilo_pass_subscriptions\".\"stripe_subscription_id\"\n ) OR (\n \"kilo_pass_subscriptions\".\"payment_provider\" IN ('app_store', 'google_play')\n AND \"kilo_pass_subscriptions\".\"provider_subscription_id\" IS NOT NULL\n AND \"kilo_pass_subscriptions\".\"stripe_subscription_id\" IS NULL\n )" + }, + "kilo_pass_subscriptions_payment_provider_check": { + "name": "kilo_pass_subscriptions_payment_provider_check", + "value": "\"kilo_pass_subscriptions\".\"payment_provider\" IN ('stripe', 'app_store', 'google_play')" + }, + "kilo_pass_subscriptions_tier_check": { + "name": "kilo_pass_subscriptions_tier_check", + "value": "\"kilo_pass_subscriptions\".\"tier\" IN ('tier_19', 'tier_49', 'tier_199')" + }, + "kilo_pass_subscriptions_cadence_check": { + "name": "kilo_pass_subscriptions_cadence_check", + "value": "\"kilo_pass_subscriptions\".\"cadence\" IN ('monthly', 'yearly')" + } + }, + "isRLSEnabled": false + }, + "public.kiloclaw_access_codes": { + "name": "kiloclaw_access_codes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "redeemed_at": { + "name": "redeemed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_kiloclaw_access_codes_code": { + "name": "UQ_kiloclaw_access_codes_code", + "columns": [ + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_access_codes_user_status": { + "name": "IDX_kiloclaw_access_codes_user_status", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kiloclaw_access_codes_one_active_per_user": { + "name": "UQ_kiloclaw_access_codes_one_active_per_user", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "status = 'active'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_access_codes_kilo_user_id_kilocode_users_id_fk": { + "name": "kiloclaw_access_codes_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_access_codes", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_admin_audit_logs": { + "name": "kiloclaw_admin_audit_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_email": { + "name": "actor_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_name": { + "name": "actor_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_user_id": { + "name": "target_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_kiloclaw_admin_audit_logs_target_user_id": { + "name": "IDX_kiloclaw_admin_audit_logs_target_user_id", + "columns": [ + { + "expression": "target_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_admin_audit_logs_action": { + "name": "IDX_kiloclaw_admin_audit_logs_action", + "columns": [ + { + "expression": "action", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_admin_audit_logs_created_at": { + "name": "IDX_kiloclaw_admin_audit_logs_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_attribution_touches": { + "name": "kiloclaw_attribution_touches", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "dedupe_key": { + "name": "dedupe_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "anonymous_id": { + "name": "anonymous_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "touch_type": { + "name": "touch_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "opaque_tracking_value": { + "name": "opaque_tracking_value", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tracking_value_length": { + "name": "tracking_value_length", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "is_tracking_value_accepted": { + "name": "is_tracking_value_accepted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "rs_code": { + "name": "rs_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rs_share_medium": { + "name": "rs_share_medium", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rs_engagement_medium": { + "name": "rs_engagement_medium", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "im_ref": { + "name": "im_ref", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "landing_path": { + "name": "landing_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "utm_source": { + "name": "utm_source", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "utm_medium": { + "name": "utm_medium", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "utm_campaign": { + "name": "utm_campaign", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "utm_term": { + "name": "utm_term", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "utm_content": { + "name": "utm_content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "touched_at": { + "name": "touched_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "sale_attributed_at": { + "name": "sale_attributed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_kiloclaw_attribution_touches_user_id": { + "name": "IDX_kiloclaw_attribution_touches_user_id", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_attribution_touches_anonymous_id": { + "name": "IDX_kiloclaw_attribution_touches_anonymous_id", + "columns": [ + { + "expression": "anonymous_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_attribution_touches_expires_at": { + "name": "IDX_kiloclaw_attribution_touches_expires_at", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_attribution_touches_sale_attributed_at": { + "name": "IDX_kiloclaw_attribution_touches_sale_attributed_at", + "columns": [ + { + "expression": "sale_attributed_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_attribution_touches_user_id_kilocode_users_id_fk": { + "name": "kiloclaw_attribution_touches_user_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_attribution_touches", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_kiloclaw_attribution_touches_dedupe_key": { + "name": "UQ_kiloclaw_attribution_touches_dedupe_key", + "nullsNotDistinct": false, + "columns": [ + "dedupe_key" + ] + } + }, + "policies": {}, + "checkConstraints": { + "kiloclaw_attribution_touches_touch_type_check": { + "name": "kiloclaw_attribution_touches_touch_type_check", + "value": "\"kiloclaw_attribution_touches\".\"touch_type\" IN ('affiliate', 'referral')" + }, + "kiloclaw_attribution_touches_provider_check": { + "name": "kiloclaw_attribution_touches_provider_check", + "value": "\"kiloclaw_attribution_touches\".\"provider\" IN ('impact_performance', 'impact_advocate')" + }, + "kiloclaw_attribution_touches_tracking_value_length_non_negative_check": { + "name": "kiloclaw_attribution_touches_tracking_value_length_non_negative_check", + "value": "\"kiloclaw_attribution_touches\".\"tracking_value_length\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.kiloclaw_cli_runs": { + "name": "kiloclaw_cli_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "instance_id": { + "name": "instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "initiated_by_admin_id": { + "name": "initiated_by_admin_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'running'" + }, + "exit_code": { + "name": "exit_code", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "output": { + "name": "output", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "IDX_kiloclaw_cli_runs_user_id": { + "name": "IDX_kiloclaw_cli_runs_user_id", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_cli_runs_started_at": { + "name": "IDX_kiloclaw_cli_runs_started_at", + "columns": [ + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_cli_runs_instance_id": { + "name": "IDX_kiloclaw_cli_runs_instance_id", + "columns": [ + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_cli_runs_user_id_kilocode_users_id_fk": { + "name": "kiloclaw_cli_runs_user_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_cli_runs", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "kiloclaw_cli_runs_instance_id_kiloclaw_instances_id_fk": { + "name": "kiloclaw_cli_runs_instance_id_kiloclaw_instances_id_fk", + "tableFrom": "kiloclaw_cli_runs", + "tableTo": "kiloclaw_instances", + "columnsFrom": [ + "instance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "kiloclaw_cli_runs_initiated_by_admin_id_kilocode_users_id_fk": { + "name": "kiloclaw_cli_runs_initiated_by_admin_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_cli_runs", + "tableTo": "kilocode_users", + "columnsFrom": [ + "initiated_by_admin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_composio_identities": { + "name": "kiloclaw_composio_identities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "owner_type": { + "name": "owner_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "composio_agent_key_encrypted": { + "name": "composio_agent_key_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composio_user_api_key_encrypted": { + "name": "composio_user_api_key_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composio_api_key_encrypted": { + "name": "composio_api_key_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composio_org_id": { + "name": "composio_org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composio_org_name": { + "name": "composio_org_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composio_project_id": { + "name": "composio_project_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composio_consumer_user_id": { + "name": "composio_consumer_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "google_calendar_connected_account_id": { + "name": "google_calendar_connected_account_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composio_agent_email": { + "name": "composio_agent_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "UQ_kiloclaw_composio_identities_current_user": { + "name": "UQ_kiloclaw_composio_identities_current_user", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kiloclaw_composio_identities\".\"owner_type\" = 'user' AND \"kiloclaw_composio_identities\".\"revoked_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kiloclaw_composio_identities_current_org_user": { + "name": "UQ_kiloclaw_composio_identities_current_org_user", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kiloclaw_composio_identities\".\"owner_type\" = 'organization_user' AND \"kiloclaw_composio_identities\".\"revoked_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_composio_identities_user": { + "name": "IDX_kiloclaw_composio_identities_user", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_composio_identities_organization": { + "name": "IDX_kiloclaw_composio_identities_organization", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_composio_identities_user_id_kilocode_users_id_fk": { + "name": "kiloclaw_composio_identities_user_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_composio_identities", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "kiloclaw_composio_identities_organization_id_organizations_id_fk": { + "name": "kiloclaw_composio_identities_organization_id_organizations_id_fk", + "tableFrom": "kiloclaw_composio_identities", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kiloclaw_composio_identities_owner_type_check": { + "name": "kiloclaw_composio_identities_owner_type_check", + "value": "\"kiloclaw_composio_identities\".\"owner_type\" IN ('user', 'organization_user')" + }, + "kiloclaw_composio_identities_status_check": { + "name": "kiloclaw_composio_identities_status_check", + "value": "\"kiloclaw_composio_identities\".\"status\" IN ('pending', 'active', 'revoked')" + }, + "kiloclaw_composio_identities_owner_scope_check": { + "name": "kiloclaw_composio_identities_owner_scope_check", + "value": "(\"kiloclaw_composio_identities\".\"owner_type\" = 'user' AND \"kiloclaw_composio_identities\".\"organization_id\" IS NULL) OR (\"kiloclaw_composio_identities\".\"owner_type\" = 'organization_user' AND \"kiloclaw_composio_identities\".\"organization_id\" IS NOT NULL)" + }, + "kiloclaw_composio_identities_active_complete_check": { + "name": "kiloclaw_composio_identities_active_complete_check", + "value": "\"kiloclaw_composio_identities\".\"status\" <> 'active' OR (\"kiloclaw_composio_identities\".\"composio_agent_key_encrypted\" IS NOT NULL AND \"kiloclaw_composio_identities\".\"composio_user_api_key_encrypted\" IS NOT NULL AND \"kiloclaw_composio_identities\".\"composio_org_id\" IS NOT NULL AND \"kiloclaw_composio_identities\".\"composio_project_id\" IS NOT NULL AND \"kiloclaw_composio_identities\".\"composio_consumer_user_id\" IS NOT NULL AND \"kiloclaw_composio_identities\".\"revoked_at\" IS NULL)" + } + }, + "isRLSEnabled": false + }, + "public.kiloclaw_earlybird_purchases": { + "name": "kiloclaw_earlybird_purchases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_charge_id": { + "name": "stripe_charge_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "manual_payment_id": { + "name": "manual_payment_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "amount_cents": { + "name": "amount_cents", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "kiloclaw_earlybird_purchases_user_id_kilocode_users_id_fk": { + "name": "kiloclaw_earlybird_purchases_user_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_earlybird_purchases", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "kiloclaw_earlybird_purchases_user_id_unique": { + "name": "kiloclaw_earlybird_purchases_user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id" + ] + }, + "kiloclaw_earlybird_purchases_stripe_charge_id_unique": { + "name": "kiloclaw_earlybird_purchases_stripe_charge_id_unique", + "nullsNotDistinct": false, + "columns": [ + "stripe_charge_id" + ] + }, + "kiloclaw_earlybird_purchases_manual_payment_id_unique": { + "name": "kiloclaw_earlybird_purchases_manual_payment_id_unique", + "nullsNotDistinct": false, + "columns": [ + "manual_payment_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_email_log": { + "name": "kiloclaw_email_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "instance_id": { + "name": "instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "email_type": { + "name": "email_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "period_start": { + "name": "period_start", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "'epoch'" + }, + "sent_at": { + "name": "sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_kiloclaw_email_log_user_type_global": { + "name": "UQ_kiloclaw_email_log_user_type_global", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "email_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kiloclaw_email_log\".\"instance_id\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kiloclaw_email_log_user_instance_type_period": { + "name": "UQ_kiloclaw_email_log_user_instance_type_period", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "email_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "period_start", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kiloclaw_email_log\".\"instance_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_email_log_type_sent_instance": { + "name": "IDX_kiloclaw_email_log_type_sent_instance", + "columns": [ + { + "expression": "email_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sent_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_email_log\".\"instance_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_email_log_user_id_kilocode_users_id_fk": { + "name": "kiloclaw_email_log_user_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_email_log", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "kiloclaw_email_log_instance_id_kiloclaw_instances_id_fk": { + "name": "kiloclaw_email_log_instance_id_kiloclaw_instances_id_fk", + "tableFrom": "kiloclaw_email_log", + "tableTo": "kiloclaw_instances", + "columnsFrom": [ + "instance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_google_oauth_connections": { + "name": "kiloclaw_google_oauth_connections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "instance_id": { + "name": "instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'google'" + }, + "account_email": { + "name": "account_email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "account_subject": { + "name": "account_subject", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "oauth_client_id": { + "name": "oauth_client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "oauth_client_secret_encrypted": { + "name": "oauth_client_secret_encrypted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "credential_profile": { + "name": "credential_profile", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'kilo_owned'" + }, + "refresh_token_encrypted": { + "name": "refresh_token_encrypted", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "grants_by_source": { + "name": "grants_by_source", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "capabilities": { + "name": "capabilities", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_error_at": { + "name": "last_error_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "connected_at": { + "name": "connected_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_kiloclaw_google_oauth_connections_instance": { + "name": "UQ_kiloclaw_google_oauth_connections_instance", + "columns": [ + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_google_oauth_connections_status": { + "name": "IDX_kiloclaw_google_oauth_connections_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_google_oauth_connections_provider": { + "name": "IDX_kiloclaw_google_oauth_connections_provider", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_google_oauth_connections_instance_id_kiloclaw_instances_id_fk": { + "name": "kiloclaw_google_oauth_connections_instance_id_kiloclaw_instances_id_fk", + "tableFrom": "kiloclaw_google_oauth_connections", + "tableTo": "kiloclaw_instances", + "columnsFrom": [ + "instance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kiloclaw_google_oauth_connections_status_check": { + "name": "kiloclaw_google_oauth_connections_status_check", + "value": "\"kiloclaw_google_oauth_connections\".\"status\" IN ('active', 'action_required', 'disconnected')" + }, + "kiloclaw_google_oauth_connections_credential_profile_check": { + "name": "kiloclaw_google_oauth_connections_credential_profile_check", + "value": "\"kiloclaw_google_oauth_connections\".\"credential_profile\" IN ('legacy', 'kilo_owned')" + } + }, + "isRLSEnabled": false + }, + "public.kiloclaw_image_catalog": { + "name": "kiloclaw_image_catalog", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "openclaw_version": { + "name": "openclaw_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "variant": { + "name": "variant", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'default'" + }, + "image_tag": { + "name": "image_tag", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image_digest": { + "name": "image_digest", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'available'" + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "published_at": { + "name": "published_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "synced_at": { + "name": "synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "rollout_percent": { + "name": "rollout_percent", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "is_latest": { + "name": "is_latest", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "IDX_kiloclaw_image_catalog_status": { + "name": "IDX_kiloclaw_image_catalog_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_image_catalog_variant": { + "name": "IDX_kiloclaw_image_catalog_variant", + "columns": [ + { + "expression": "variant", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kiloclaw_image_catalog_one_latest_per_variant": { + "name": "UQ_kiloclaw_image_catalog_one_latest_per_variant", + "columns": [ + { + "expression": "variant", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kiloclaw_image_catalog\".\"is_latest\" = true", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kiloclaw_image_catalog_one_candidate_per_variant": { + "name": "UQ_kiloclaw_image_catalog_one_candidate_per_variant", + "columns": [ + { + "expression": "variant", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kiloclaw_image_catalog\".\"is_latest\" = false AND \"kiloclaw_image_catalog\".\"rollout_percent\" > 0 AND \"kiloclaw_image_catalog\".\"status\" = 'available'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "kiloclaw_image_catalog_image_tag_unique": { + "name": "kiloclaw_image_catalog_image_tag_unique", + "nullsNotDistinct": false, + "columns": [ + "image_tag" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_inbound_email_aliases": { + "name": "kiloclaw_inbound_email_aliases", + "schema": "", + "columns": { + "alias": { + "name": "alias", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "instance_id": { + "name": "instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "retired_at": { + "name": "retired_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "IDX_kiloclaw_inbound_email_aliases_instance_id": { + "name": "IDX_kiloclaw_inbound_email_aliases_instance_id", + "columns": [ + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kiloclaw_inbound_email_aliases_active_instance": { + "name": "UQ_kiloclaw_inbound_email_aliases_active_instance", + "columns": [ + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kiloclaw_inbound_email_aliases\".\"retired_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_inbound_email_aliases_instance_id_kiloclaw_instances_id_fk": { + "name": "kiloclaw_inbound_email_aliases_instance_id_kiloclaw_instances_id_fk", + "tableFrom": "kiloclaw_inbound_email_aliases", + "tableTo": "kiloclaw_instances", + "columnsFrom": [ + "instance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_inbound_email_reserved_aliases": { + "name": "kiloclaw_inbound_email_reserved_aliases", + "schema": "", + "columns": { + "alias": { + "name": "alias", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_instances": { + "name": "kiloclaw_instances", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sandbox_id": { + "name": "sandbox_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'fly'" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "inbound_email_enabled": { + "name": "inbound_email_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "inactive_trial_stopped_at": { + "name": "inactive_trial_stopped_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "destroyed_at": { + "name": "destroyed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "tracked_image_tag": { + "name": "tracked_image_tag", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "instance_type": { + "name": "instance_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "admin_size_override": { + "name": "admin_size_override", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "composio_config_source": { + "name": "composio_config_source", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "UQ_kiloclaw_instances_active": { + "name": "UQ_kiloclaw_instances_active", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sandbox_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kiloclaw_instances\".\"destroyed_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_instances_active_personal_by_user": { + "name": "IDX_kiloclaw_instances_active_personal_by_user", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_instances\".\"organization_id\" IS NULL AND \"kiloclaw_instances\".\"destroyed_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_instances_active_org_by_user_org": { + "name": "IDX_kiloclaw_instances_active_org_by_user_org", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_instances\".\"organization_id\" IS NOT NULL AND \"kiloclaw_instances\".\"destroyed_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_instances_user_id_created_at": { + "name": "IDX_kiloclaw_instances_user_id_created_at", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_instances_tracked_image_tag": { + "name": "IDX_kiloclaw_instances_tracked_image_tag", + "columns": [ + { + "expression": "tracked_image_tag", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_instances\".\"destroyed_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_instances_instance_type": { + "name": "IDX_kiloclaw_instances_instance_type", + "columns": [ + { + "expression": "instance_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_instances\".\"destroyed_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_instances_admin_size_override": { + "name": "IDX_kiloclaw_instances_admin_size_override", + "columns": [ + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_instances\".\"admin_size_override\" IS NOT NULL AND \"kiloclaw_instances\".\"destroyed_at\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_instances_user_id_kilocode_users_id_fk": { + "name": "kiloclaw_instances_user_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_instances", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "kiloclaw_instances_organization_id_organizations_id_fk": { + "name": "kiloclaw_instances_organization_id_organizations_id_fk", + "tableFrom": "kiloclaw_instances", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "CHK_kiloclaw_instances_instance_type": { + "name": "CHK_kiloclaw_instances_instance_type", + "value": "\"kiloclaw_instances\".\"instance_type\" IS NULL OR \"kiloclaw_instances\".\"instance_type\" IN ('perf-1-3', 'perf-4-8', 'perf-4-16', 'shared-2-3', 'shared-2-4', 'custom')" + }, + "kiloclaw_instances_composio_config_source_check": { + "name": "kiloclaw_instances_composio_config_source_check", + "value": "\"kiloclaw_instances\".\"composio_config_source\" IS NULL OR \"kiloclaw_instances\".\"composio_config_source\" IN ('managed', 'manual')" + } + }, + "isRLSEnabled": false + }, + "public.kiloclaw_morning_briefing_configs": { + "name": "kiloclaw_morning_briefing_configs", + "schema": "", + "columns": { + "instance_id": { + "name": "instance_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cron": { + "name": "cron", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'0 7 * * *'" + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'UTC'" + }, + "interest_topics": { + "name": "interest_topics", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_kiloclaw_morning_briefing_configs_enabled": { + "name": "IDX_kiloclaw_morning_briefing_configs_enabled", + "columns": [ + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_morning_briefing_configs\".\"enabled\" = true", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_morning_briefing_configs_instance_id_kiloclaw_instances_id_fk": { + "name": "kiloclaw_morning_briefing_configs_instance_id_kiloclaw_instances_id_fk", + "tableFrom": "kiloclaw_morning_briefing_configs", + "tableTo": "kiloclaw_instances", + "columnsFrom": [ + "instance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_referral_conversions": { + "name": "kiloclaw_referral_conversions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "referee_user_id": { + "name": "referee_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "referrer_user_id": { + "name": "referrer_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_touch_id": { + "name": "source_touch_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "winning_touch_type": { + "name": "winning_touch_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_payment_id": { + "name": "source_payment_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "qualified": { + "name": "qualified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "disqualification_reason": { + "name": "disqualification_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "converted_at": { + "name": "converted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_kiloclaw_referral_conversions_referee_user_id": { + "name": "IDX_kiloclaw_referral_conversions_referee_user_id", + "columns": [ + { + "expression": "referee_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_referral_conversions_referrer_user_id": { + "name": "IDX_kiloclaw_referral_conversions_referrer_user_id", + "columns": [ + { + "expression": "referrer_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_referral_conversions_referee_user_id_kilocode_users_id_fk": { + "name": "kiloclaw_referral_conversions_referee_user_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_referral_conversions", + "tableTo": "kilocode_users", + "columnsFrom": [ + "referee_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "kiloclaw_referral_conversions_referrer_user_id_kilocode_users_id_fk": { + "name": "kiloclaw_referral_conversions_referrer_user_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_referral_conversions", + "tableTo": "kilocode_users", + "columnsFrom": [ + "referrer_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "kiloclaw_referral_conversions_source_touch_id_kiloclaw_attribution_touches_id_fk": { + "name": "kiloclaw_referral_conversions_source_touch_id_kiloclaw_attribution_touches_id_fk", + "tableFrom": "kiloclaw_referral_conversions", + "tableTo": "kiloclaw_attribution_touches", + "columnsFrom": [ + "source_touch_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_kiloclaw_referral_conversions_source_payment_id": { + "name": "UQ_kiloclaw_referral_conversions_source_payment_id", + "nullsNotDistinct": false, + "columns": [ + "source_payment_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "kiloclaw_referral_conversions_winning_touch_type_check": { + "name": "kiloclaw_referral_conversions_winning_touch_type_check", + "value": "\"kiloclaw_referral_conversions\".\"winning_touch_type\" IN ('referral', 'affiliate', 'none')" + } + }, + "isRLSEnabled": false + }, + "public.kiloclaw_referral_reward_applications": { + "name": "kiloclaw_referral_reward_applications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "reward_id": { + "name": "reward_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "beneficiary_user_id": { + "name": "beneficiary_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subscription_id": { + "name": "subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "previous_renewal_boundary": { + "name": "previous_renewal_boundary", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "new_renewal_boundary": { + "name": "new_renewal_boundary", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "local_operation_id": { + "name": "local_operation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_operation_id": { + "name": "stripe_operation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_idempotency_key": { + "name": "stripe_idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applied_at": { + "name": "applied_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_kiloclaw_referral_reward_applications_reward_id": { + "name": "IDX_kiloclaw_referral_reward_applications_reward_id", + "columns": [ + { + "expression": "reward_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_referral_reward_applications_beneficiary_user_id": { + "name": "IDX_kiloclaw_referral_reward_applications_beneficiary_user_id", + "columns": [ + { + "expression": "beneficiary_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_referral_reward_applications_reward_id_kiloclaw_referral_rewards_id_fk": { + "name": "kiloclaw_referral_reward_applications_reward_id_kiloclaw_referral_rewards_id_fk", + "tableFrom": "kiloclaw_referral_reward_applications", + "tableTo": "kiloclaw_referral_rewards", + "columnsFrom": [ + "reward_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "kiloclaw_referral_reward_applications_beneficiary_user_id_kilocode_users_id_fk": { + "name": "kiloclaw_referral_reward_applications_beneficiary_user_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_referral_reward_applications", + "tableTo": "kilocode_users", + "columnsFrom": [ + "beneficiary_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_referral_reward_decisions": { + "name": "kiloclaw_referral_reward_decisions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "conversion_id": { + "name": "conversion_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "beneficiary_user_id": { + "name": "beneficiary_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "beneficiary_role": { + "name": "beneficiary_role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "outcome": { + "name": "outcome", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "months_granted": { + "name": "months_granted", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_kiloclaw_referral_reward_decisions_beneficiary_user_id": { + "name": "IDX_kiloclaw_referral_reward_decisions_beneficiary_user_id", + "columns": [ + { + "expression": "beneficiary_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_referral_reward_decisions_conversion_id_kiloclaw_referral_conversions_id_fk": { + "name": "kiloclaw_referral_reward_decisions_conversion_id_kiloclaw_referral_conversions_id_fk", + "tableFrom": "kiloclaw_referral_reward_decisions", + "tableTo": "kiloclaw_referral_conversions", + "columnsFrom": [ + "conversion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "kiloclaw_referral_reward_decisions_beneficiary_user_id_kilocode_users_id_fk": { + "name": "kiloclaw_referral_reward_decisions_beneficiary_user_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_referral_reward_decisions", + "tableTo": "kilocode_users", + "columnsFrom": [ + "beneficiary_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_kiloclaw_referral_reward_decisions_conversion_role": { + "name": "UQ_kiloclaw_referral_reward_decisions_conversion_role", + "nullsNotDistinct": false, + "columns": [ + "conversion_id", + "beneficiary_role" + ] + } + }, + "policies": {}, + "checkConstraints": { + "kiloclaw_referral_reward_decisions_beneficiary_role_check": { + "name": "kiloclaw_referral_reward_decisions_beneficiary_role_check", + "value": "\"kiloclaw_referral_reward_decisions\".\"beneficiary_role\" IN ('referrer', 'referee')" + }, + "kiloclaw_referral_reward_decisions_outcome_check": { + "name": "kiloclaw_referral_reward_decisions_outcome_check", + "value": "\"kiloclaw_referral_reward_decisions\".\"outcome\" IN ('granted', 'cap_limited', 'disqualified')" + }, + "kiloclaw_referral_reward_decisions_months_granted_non_negative_check": { + "name": "kiloclaw_referral_reward_decisions_months_granted_non_negative_check", + "value": "\"kiloclaw_referral_reward_decisions\".\"months_granted\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.kiloclaw_referral_rewards": { + "name": "kiloclaw_referral_rewards", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "conversion_id": { + "name": "conversion_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "decision_id": { + "name": "decision_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "beneficiary_user_id": { + "name": "beneficiary_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "beneficiary_role": { + "name": "beneficiary_role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "months_granted": { + "name": "months_granted", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "applies_to_subscription_id": { + "name": "applies_to_subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "earned_at": { + "name": "earned_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "applied_at": { + "name": "applied_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "reversed_at": { + "name": "reversed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "review_reason": { + "name": "review_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_kiloclaw_referral_rewards_beneficiary_user_id": { + "name": "IDX_kiloclaw_referral_rewards_beneficiary_user_id", + "columns": [ + { + "expression": "beneficiary_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_referral_rewards_status": { + "name": "IDX_kiloclaw_referral_rewards_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_referral_rewards_conversion_id_kiloclaw_referral_conversions_id_fk": { + "name": "kiloclaw_referral_rewards_conversion_id_kiloclaw_referral_conversions_id_fk", + "tableFrom": "kiloclaw_referral_rewards", + "tableTo": "kiloclaw_referral_conversions", + "columnsFrom": [ + "conversion_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "kiloclaw_referral_rewards_decision_id_kiloclaw_referral_reward_decisions_id_fk": { + "name": "kiloclaw_referral_rewards_decision_id_kiloclaw_referral_reward_decisions_id_fk", + "tableFrom": "kiloclaw_referral_rewards", + "tableTo": "kiloclaw_referral_reward_decisions", + "columnsFrom": [ + "decision_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "kiloclaw_referral_rewards_beneficiary_user_id_kilocode_users_id_fk": { + "name": "kiloclaw_referral_rewards_beneficiary_user_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_referral_rewards", + "tableTo": "kilocode_users", + "columnsFrom": [ + "beneficiary_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_kiloclaw_referral_rewards_conversion_role": { + "name": "UQ_kiloclaw_referral_rewards_conversion_role", + "nullsNotDistinct": false, + "columns": [ + "conversion_id", + "beneficiary_role" + ] + }, + "UQ_kiloclaw_referral_rewards_decision_id": { + "name": "UQ_kiloclaw_referral_rewards_decision_id", + "nullsNotDistinct": false, + "columns": [ + "decision_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "kiloclaw_referral_rewards_beneficiary_role_check": { + "name": "kiloclaw_referral_rewards_beneficiary_role_check", + "value": "\"kiloclaw_referral_rewards\".\"beneficiary_role\" IN ('referrer', 'referee')" + }, + "kiloclaw_referral_rewards_status_check": { + "name": "kiloclaw_referral_rewards_status_check", + "value": "\"kiloclaw_referral_rewards\".\"status\" IN ('pending', 'earned', 'applied', 'reversed', 'expired', 'canceled', 'review_required')" + }, + "kiloclaw_referral_rewards_months_granted_positive_check": { + "name": "kiloclaw_referral_rewards_months_granted_positive_check", + "value": "\"kiloclaw_referral_rewards\".\"months_granted\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.kiloclaw_referrals": { + "name": "kiloclaw_referrals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "referee_user_id": { + "name": "referee_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "referrer_user_id": { + "name": "referrer_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_touch_id": { + "name": "source_touch_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "impact_referral_id": { + "name": "impact_referral_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_kiloclaw_referrals_referrer_user_id": { + "name": "IDX_kiloclaw_referrals_referrer_user_id", + "columns": [ + { + "expression": "referrer_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_referrals_source_touch_id": { + "name": "IDX_kiloclaw_referrals_source_touch_id", + "columns": [ + { + "expression": "source_touch_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_referrals_referee_user_id_kilocode_users_id_fk": { + "name": "kiloclaw_referrals_referee_user_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_referrals", + "tableTo": "kilocode_users", + "columnsFrom": [ + "referee_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "kiloclaw_referrals_referrer_user_id_kilocode_users_id_fk": { + "name": "kiloclaw_referrals_referrer_user_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_referrals", + "tableTo": "kilocode_users", + "columnsFrom": [ + "referrer_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "kiloclaw_referrals_source_touch_id_kiloclaw_attribution_touches_id_fk": { + "name": "kiloclaw_referrals_source_touch_id_kiloclaw_attribution_touches_id_fk", + "tableFrom": "kiloclaw_referrals", + "tableTo": "kiloclaw_attribution_touches", + "columnsFrom": [ + "source_touch_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_kiloclaw_referrals_referee_user_id": { + "name": "UQ_kiloclaw_referrals_referee_user_id", + "nullsNotDistinct": false, + "columns": [ + "referee_user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_scheduled_action_notifications": { + "name": "kiloclaw_scheduled_action_notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "target_id": { + "name": "target_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'notice'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "sent_at": { + "name": "sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "UQ_kiloclaw_scheduled_action_notifications_target_kind_channel": { + "name": "UQ_kiloclaw_scheduled_action_notifications_target_kind_channel", + "columns": [ + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "channel", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_scheduled_action_notifications_pending": { + "name": "IDX_kiloclaw_scheduled_action_notifications_pending", + "columns": [ + { + "expression": "target_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_scheduled_action_notifications\".\"status\" = 'pending'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_scheduled_action_notifications_target_id_kiloclaw_scheduled_action_targets_id_fk": { + "name": "kiloclaw_scheduled_action_notifications_target_id_kiloclaw_scheduled_action_targets_id_fk", + "tableFrom": "kiloclaw_scheduled_action_notifications", + "tableTo": "kiloclaw_scheduled_action_targets", + "columnsFrom": [ + "target_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_scheduled_action_stages": { + "name": "kiloclaw_scheduled_action_stages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "scheduled_action_id": { + "name": "scheduled_action_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "stage_index": { + "name": "stage_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "scheduled_at": { + "name": "scheduled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "notice_sent_at": { + "name": "notice_sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "applied_count": { + "name": "applied_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "skipped_count": { + "name": "skipped_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "failed_count": { + "name": "failed_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "UQ_kiloclaw_scheduled_action_stages_parent_index": { + "name": "UQ_kiloclaw_scheduled_action_stages_parent_index", + "columns": [ + { + "expression": "scheduled_action_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "stage_index", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_scheduled_action_stages_notice_due": { + "name": "IDX_kiloclaw_scheduled_action_stages_notice_due", + "columns": [ + { + "expression": "scheduled_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_scheduled_action_stages\".\"notice_sent_at\" IS NULL AND \"kiloclaw_scheduled_action_stages\".\"status\" = 'pending'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_scheduled_action_stages_scheduled_action_id_kiloclaw_scheduled_actions_id_fk": { + "name": "kiloclaw_scheduled_action_stages_scheduled_action_id_kiloclaw_scheduled_actions_id_fk", + "tableFrom": "kiloclaw_scheduled_action_stages", + "tableTo": "kiloclaw_scheduled_actions", + "columnsFrom": [ + "scheduled_action_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_scheduled_action_targets": { + "name": "kiloclaw_scheduled_action_targets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "scheduled_action_id": { + "name": "scheduled_action_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "stage_id": { + "name": "stage_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "instance_id": { + "name": "instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source_image_tag": { + "name": "source_image_tag", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_image_tag": { + "name": "target_image_tag", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applied_at": { + "name": "applied_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "skip_reason": { + "name": "skip_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "UQ_kiloclaw_scheduled_action_targets_parent_instance": { + "name": "UQ_kiloclaw_scheduled_action_targets_parent_instance", + "columns": [ + { + "expression": "scheduled_action_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_scheduled_action_targets_stage": { + "name": "IDX_kiloclaw_scheduled_action_targets_stage", + "columns": [ + { + "expression": "stage_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_scheduled_action_targets_pending_by_instance": { + "name": "IDX_kiloclaw_scheduled_action_targets_pending_by_instance", + "columns": [ + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_scheduled_action_targets\".\"status\" = 'pending'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_scheduled_action_targets_scheduled_action_id_kiloclaw_scheduled_actions_id_fk": { + "name": "kiloclaw_scheduled_action_targets_scheduled_action_id_kiloclaw_scheduled_actions_id_fk", + "tableFrom": "kiloclaw_scheduled_action_targets", + "tableTo": "kiloclaw_scheduled_actions", + "columnsFrom": [ + "scheduled_action_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "kiloclaw_scheduled_action_targets_stage_id_kiloclaw_scheduled_action_stages_id_fk": { + "name": "kiloclaw_scheduled_action_targets_stage_id_kiloclaw_scheduled_action_stages_id_fk", + "tableFrom": "kiloclaw_scheduled_action_targets", + "tableTo": "kiloclaw_scheduled_action_stages", + "columnsFrom": [ + "stage_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "kiloclaw_scheduled_action_targets_instance_id_kiloclaw_instances_id_fk": { + "name": "kiloclaw_scheduled_action_targets_instance_id_kiloclaw_instances_id_fk", + "tableFrom": "kiloclaw_scheduled_action_targets", + "tableTo": "kiloclaw_instances", + "columnsFrom": [ + "instance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "kiloclaw_scheduled_action_targets_user_id_kilocode_users_id_fk": { + "name": "kiloclaw_scheduled_action_targets_user_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_scheduled_action_targets", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_scheduled_actions": { + "name": "kiloclaw_scheduled_actions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "action_type": { + "name": "action_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_image_tag": { + "name": "target_image_tag", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "override_pins": { + "name": "override_pins", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notice_lead_hours": { + "name": "notice_lead_hours", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 24 + }, + "notice_subject": { + "name": "notice_subject", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "notice_body": { + "name": "notice_body", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'scheduled'" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "cancelled_at": { + "name": "cancelled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "total_count": { + "name": "total_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "applied_count": { + "name": "applied_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "skipped_count": { + "name": "skipped_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "failed_count": { + "name": "failed_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "IDX_kiloclaw_scheduled_actions_status": { + "name": "IDX_kiloclaw_scheduled_actions_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_scheduled_actions_action_type": { + "name": "IDX_kiloclaw_scheduled_actions_action_type", + "columns": [ + { + "expression": "action_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_scheduled_actions_created_by": { + "name": "IDX_kiloclaw_scheduled_actions_created_by", + "columns": [ + { + "expression": "created_by", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_scheduled_actions_target_image_tag_kiloclaw_image_catalog_image_tag_fk": { + "name": "kiloclaw_scheduled_actions_target_image_tag_kiloclaw_image_catalog_image_tag_fk", + "tableFrom": "kiloclaw_scheduled_actions", + "tableTo": "kiloclaw_image_catalog", + "columnsFrom": [ + "target_image_tag" + ], + "columnsTo": [ + "image_tag" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "kiloclaw_scheduled_actions_created_by_kilocode_users_id_fk": { + "name": "kiloclaw_scheduled_actions_created_by_kilocode_users_id_fk", + "tableFrom": "kiloclaw_scheduled_actions", + "tableTo": "kilocode_users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kiloclaw_subscription_change_log": { + "name": "kiloclaw_subscription_change_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "subscription_id": { + "name": "subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "actor_type": { + "name": "actor_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "before_state": { + "name": "before_state", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "after_state": { + "name": "after_state", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "IDX_kiloclaw_subscription_change_log_subscription_created_at": { + "name": "IDX_kiloclaw_subscription_change_log_subscription_created_at", + "columns": [ + { + "expression": "subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_subscription_change_log_created_at": { + "name": "IDX_kiloclaw_subscription_change_log_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_subscription_change_log_subscription_id_kiloclaw_subscriptions_id_fk": { + "name": "kiloclaw_subscription_change_log_subscription_id_kiloclaw_subscriptions_id_fk", + "tableFrom": "kiloclaw_subscription_change_log", + "tableTo": "kiloclaw_subscriptions", + "columnsFrom": [ + "subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kiloclaw_subscription_change_log_actor_type_check": { + "name": "kiloclaw_subscription_change_log_actor_type_check", + "value": "\"kiloclaw_subscription_change_log\".\"actor_type\" IN ('user', 'system')" + }, + "kiloclaw_subscription_change_log_action_check": { + "name": "kiloclaw_subscription_change_log_action_check", + "value": "\"kiloclaw_subscription_change_log\".\"action\" IN ('created', 'status_changed', 'plan_switched', 'period_advanced', 'canceled', 'reactivated', 'suspended', 'destruction_scheduled', 'reassigned', 'backfilled', 'payment_source_changed', 'schedule_changed', 'admin_override')" + } + }, + "isRLSEnabled": false + }, + "public.kiloclaw_subscriptions": { + "name": "kiloclaw_subscriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_subscription_id": { + "name": "stripe_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_schedule_id": { + "name": "stripe_schedule_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "transferred_to_subscription_id": { + "name": "transferred_to_subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "instance_id": { + "name": "instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "access_origin": { + "name": "access_origin", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payment_source": { + "name": "payment_source", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "kiloclaw_price_version": { + "name": "kiloclaw_price_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scheduled_plan": { + "name": "scheduled_plan", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scheduled_by": { + "name": "scheduled_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cancel_at_period_end": { + "name": "cancel_at_period_end", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "pending_conversion": { + "name": "pending_conversion", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "trial_started_at": { + "name": "trial_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "trial_ends_at": { + "name": "trial_ends_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "current_period_start": { + "name": "current_period_start", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "current_period_end": { + "name": "current_period_end", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "credit_renewal_at": { + "name": "credit_renewal_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "commit_ends_at": { + "name": "commit_ends_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "past_due_since": { + "name": "past_due_since", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "suspended_at": { + "name": "suspended_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "destruction_deadline": { + "name": "destruction_deadline", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "auto_resume_requested_at": { + "name": "auto_resume_requested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "auto_resume_retry_after": { + "name": "auto_resume_retry_after", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "auto_resume_attempt_count": { + "name": "auto_resume_attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "auto_top_up_triggered_for_period": { + "name": "auto_top_up_triggered_for_period", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_kiloclaw_subscriptions_status": { + "name": "IDX_kiloclaw_subscriptions_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_subscriptions_user_id": { + "name": "IDX_kiloclaw_subscriptions_user_id", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_subscriptions_user_status": { + "name": "IDX_kiloclaw_subscriptions_user_status", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_subscriptions_price_version": { + "name": "IDX_kiloclaw_subscriptions_price_version", + "columns": [ + { + "expression": "kiloclaw_price_version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_subscriptions_transferred_to": { + "name": "IDX_kiloclaw_subscriptions_transferred_to", + "columns": [ + { + "expression": "transferred_to_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_subscriptions_stripe_schedule_id": { + "name": "IDX_kiloclaw_subscriptions_stripe_schedule_id", + "columns": [ + { + "expression": "stripe_schedule_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_subscriptions_auto_resume_retry_after": { + "name": "IDX_kiloclaw_subscriptions_auto_resume_retry_after", + "columns": [ + { + "expression": "auto_resume_retry_after", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kiloclaw_subscriptions_instance": { + "name": "UQ_kiloclaw_subscriptions_instance", + "columns": [ + { + "expression": "instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kiloclaw_subscriptions\".\"instance_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kiloclaw_subscriptions_transferred_to": { + "name": "UQ_kiloclaw_subscriptions_transferred_to", + "columns": [ + { + "expression": "transferred_to_subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kiloclaw_subscriptions\".\"transferred_to_subscription_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_subscriptions_earlybird_origin": { + "name": "IDX_kiloclaw_subscriptions_earlybird_origin", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "access_origin", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_subscriptions\".\"access_origin\" = 'earlybird'", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_subscriptions_user_id_kilocode_users_id_fk": { + "name": "kiloclaw_subscriptions_user_id_kilocode_users_id_fk", + "tableFrom": "kiloclaw_subscriptions", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "kiloclaw_subscriptions_transferred_to_subscription_id_kiloclaw_subscriptions_id_fk": { + "name": "kiloclaw_subscriptions_transferred_to_subscription_id_kiloclaw_subscriptions_id_fk", + "tableFrom": "kiloclaw_subscriptions", + "tableTo": "kiloclaw_subscriptions", + "columnsFrom": [ + "transferred_to_subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "kiloclaw_subscriptions_instance_id_kiloclaw_instances_id_fk": { + "name": "kiloclaw_subscriptions_instance_id_kiloclaw_instances_id_fk", + "tableFrom": "kiloclaw_subscriptions", + "tableTo": "kiloclaw_instances", + "columnsFrom": [ + "instance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "kiloclaw_subscriptions_stripe_subscription_id_unique": { + "name": "kiloclaw_subscriptions_stripe_subscription_id_unique", + "nullsNotDistinct": false, + "columns": [ + "stripe_subscription_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "kiloclaw_subscriptions_price_version_check": { + "name": "kiloclaw_subscriptions_price_version_check", + "value": "\"kiloclaw_subscriptions\".\"kiloclaw_price_version\" IN ('2026-03-19', '2026-05-10')" + }, + "kiloclaw_subscriptions_plan_check": { + "name": "kiloclaw_subscriptions_plan_check", + "value": "\"kiloclaw_subscriptions\".\"plan\" IN ('trial', 'commit', 'standard')" + }, + "kiloclaw_subscriptions_scheduled_plan_check": { + "name": "kiloclaw_subscriptions_scheduled_plan_check", + "value": "\"kiloclaw_subscriptions\".\"scheduled_plan\" IN ('commit', 'standard')" + }, + "kiloclaw_subscriptions_scheduled_by_check": { + "name": "kiloclaw_subscriptions_scheduled_by_check", + "value": "\"kiloclaw_subscriptions\".\"scheduled_by\" IN ('auto', 'user')" + }, + "kiloclaw_subscriptions_status_check": { + "name": "kiloclaw_subscriptions_status_check", + "value": "\"kiloclaw_subscriptions\".\"status\" IN ('trialing', 'active', 'past_due', 'canceled', 'unpaid')" + }, + "kiloclaw_subscriptions_access_origin_check": { + "name": "kiloclaw_subscriptions_access_origin_check", + "value": "\"kiloclaw_subscriptions\".\"access_origin\" IN ('earlybird')" + }, + "kiloclaw_subscriptions_payment_source_check": { + "name": "kiloclaw_subscriptions_payment_source_check", + "value": "\"kiloclaw_subscriptions\".\"payment_source\" IN ('stripe', 'credits')" + } + }, + "isRLSEnabled": false + }, + "public.kiloclaw_terminal_renewal_failures": { + "name": "kiloclaw_terminal_renewal_failures", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "subscription_id": { + "name": "subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "renewal_boundary": { + "name": "renewal_boundary", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unresolved'" + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "first_failure_at": { + "name": "first_failure_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_failure_at": { + "name": "last_failure_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "last_failure_code": { + "name": "last_failure_code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_failure_message": { + "name": "last_failure_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resolution_actor_type": { + "name": "resolution_actor_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resolution_actor_id": { + "name": "resolution_actor_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resolution_at": { + "name": "resolution_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "resolution_reason": { + "name": "resolution_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_kiloclaw_terminal_renewal_failures_subscription_boundary": { + "name": "UQ_kiloclaw_terminal_renewal_failures_subscription_boundary", + "columns": [ + { + "expression": "subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "renewal_boundary", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_terminal_renewal_failures_unresolved": { + "name": "IDX_kiloclaw_terminal_renewal_failures_unresolved", + "columns": [ + { + "expression": "subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "renewal_boundary", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"kiloclaw_terminal_renewal_failures\".\"status\" = 'unresolved'", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kiloclaw_terminal_renewal_failures_status_last_failure_at": { + "name": "IDX_kiloclaw_terminal_renewal_failures_status_last_failure_at", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "last_failure_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "kiloclaw_terminal_renewal_failures_subscription_id_kiloclaw_subscriptions_id_fk": { + "name": "kiloclaw_terminal_renewal_failures_subscription_id_kiloclaw_subscriptions_id_fk", + "tableFrom": "kiloclaw_terminal_renewal_failures", + "tableTo": "kiloclaw_subscriptions", + "columnsFrom": [ + "subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "kiloclaw_terminal_renewal_failures_status_check": { + "name": "kiloclaw_terminal_renewal_failures_status_check", + "value": "\"kiloclaw_terminal_renewal_failures\".\"status\" IN ('unresolved', 'resolved', 'waived', 'superseded')" + }, + "kiloclaw_terminal_renewal_failures_last_failure_code_check": { + "name": "kiloclaw_terminal_renewal_failures_last_failure_code_check", + "value": "\"kiloclaw_terminal_renewal_failures\".\"last_failure_code\" IN ('credit_balance_read_failed', 'renewal_transaction_failed', 'auto_top_up_marker_write_failed', 'worker_timeout', 'poison_payload', 'queue_delivery_exhausted')" + }, + "kiloclaw_terminal_renewal_failures_resolution_actor_type_check": { + "name": "kiloclaw_terminal_renewal_failures_resolution_actor_type_check", + "value": "\"kiloclaw_terminal_renewal_failures\".\"resolution_actor_type\" IN ('operator', 'system')" + } + }, + "isRLSEnabled": false + }, + "public.kiloclaw_version_pins": { + "name": "kiloclaw_version_pins", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "instance_id": { + "name": "instance_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "image_tag": { + "name": "image_tag", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pinned_by": { + "name": "pinned_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "kiloclaw_version_pins_instance_id_kiloclaw_instances_id_fk": { + "name": "kiloclaw_version_pins_instance_id_kiloclaw_instances_id_fk", + "tableFrom": "kiloclaw_version_pins", + "tableTo": "kiloclaw_instances", + "columnsFrom": [ + "instance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "kiloclaw_version_pins_image_tag_kiloclaw_image_catalog_image_tag_fk": { + "name": "kiloclaw_version_pins_image_tag_kiloclaw_image_catalog_image_tag_fk", + "tableFrom": "kiloclaw_version_pins", + "tableTo": "kiloclaw_image_catalog", + "columnsFrom": [ + "image_tag" + ], + "columnsTo": [ + "image_tag" + ], + "onDelete": "restrict", + "onUpdate": "no action" + }, + "kiloclaw_version_pins_pinned_by_kilocode_users_id_fk": { + "name": "kiloclaw_version_pins_pinned_by_kilocode_users_id_fk", + "tableFrom": "kiloclaw_version_pins", + "tableTo": "kilocode_users", + "columnsFrom": [ + "pinned_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "kiloclaw_version_pins_instance_id_unique": { + "name": "kiloclaw_version_pins_instance_id_unique", + "nullsNotDistinct": false, + "columns": [ + "instance_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.kilocode_users": { + "name": "kilocode_users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "google_user_email": { + "name": "google_user_email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "google_user_name": { + "name": "google_user_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "google_user_image_url": { + "name": "google_user_image_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "hosted_domain": { + "name": "hosted_domain", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "microdollars_used": { + "name": "microdollars_used", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "kilo_pass_threshold": { + "name": "kilo_pass_threshold", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "app_store_account_token": { + "name": "app_store_account_token", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "is_admin": { + "name": "is_admin", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "total_microdollars_acquired": { + "name": "total_microdollars_acquired", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "next_credit_expiration_at": { + "name": "next_credit_expiration_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "has_validation_stytch": { + "name": "has_validation_stytch", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "has_validation_novel_card_with_hold": { + "name": "has_validation_novel_card_with_hold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "blocked_reason": { + "name": "blocked_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "blocked_at": { + "name": "blocked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "blocked_by_kilo_user_id": { + "name": "blocked_by_kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "api_token_pepper": { + "name": "api_token_pepper", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "web_session_pepper": { + "name": "web_session_pepper", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "auto_top_up_enabled": { + "name": "auto_top_up_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_bot": { + "name": "is_bot", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "kiloclaw_early_access": { + "name": "kiloclaw_early_access", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "default_model": { + "name": "default_model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cohorts": { + "name": "cohorts", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "completed_welcome_form": { + "name": "completed_welcome_form", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "linkedin_url": { + "name": "linkedin_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "github_url": { + "name": "github_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discord_server_membership_verified_at": { + "name": "discord_server_membership_verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "openrouter_upstream_safety_identifier": { + "name": "openrouter_upstream_safety_identifier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "vercel_downstream_safety_identifier": { + "name": "vercel_downstream_safety_identifier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customer_source": { + "name": "customer_source", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "signup_ip": { + "name": "signup_ip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "account_deletion_requested_at": { + "name": "account_deletion_requested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "normalized_email": { + "name": "normalized_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email_domain": { + "name": "email_domain", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "IDX_kilocode_users_signup_ip_created_at": { + "name": "IDX_kilocode_users_signup_ip_created_at", + "columns": [ + { + "expression": "signup_ip", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilocode_users_blocked_at": { + "name": "IDX_kilocode_users_blocked_at", + "columns": [ + { + "expression": "blocked_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilocode_users_blocked_by_kilo_user_id": { + "name": "IDX_kilocode_users_blocked_by_kilo_user_id", + "columns": [ + { + "expression": "blocked_by_kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kilocode_users_openrouter_upstream_safety_identifier": { + "name": "UQ_kilocode_users_openrouter_upstream_safety_identifier", + "columns": [ + { + "expression": "openrouter_upstream_safety_identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kilocode_users\".\"openrouter_upstream_safety_identifier\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_kilocode_users_vercel_downstream_safety_identifier": { + "name": "UQ_kilocode_users_vercel_downstream_safety_identifier", + "columns": [ + { + "expression": "vercel_downstream_safety_identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"kilocode_users\".\"vercel_downstream_safety_identifier\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilocode_users_normalized_email": { + "name": "IDX_kilocode_users_normalized_email", + "columns": [ + { + "expression": "normalized_email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_kilocode_users_email_domain": { + "name": "IDX_kilocode_users_email_domain", + "columns": [ + { + "expression": "email_domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "kilocode_users_app_store_account_token_unique": { + "name": "kilocode_users_app_store_account_token_unique", + "nullsNotDistinct": false, + "columns": [ + "app_store_account_token" + ] + }, + "UQ_b1afacbcf43f2c7c4cb9f7e7faa": { + "name": "UQ_b1afacbcf43f2c7c4cb9f7e7faa", + "nullsNotDistinct": false, + "columns": [ + "google_user_email" + ] + } + }, + "policies": {}, + "checkConstraints": { + "blocked_reason_not_empty": { + "name": "blocked_reason_not_empty", + "value": "length(blocked_reason) > 0" + } + }, + "isRLSEnabled": false + }, + "public.magic_link_tokens": { + "name": "magic_link_tokens", + "schema": "", + "columns": { + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "consumed_at": { + "name": "consumed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_magic_link_tokens_email": { + "name": "idx_magic_link_tokens_email", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_magic_link_tokens_expires_at": { + "name": "idx_magic_link_tokens_expires_at", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "check_expires_at_future": { + "name": "check_expires_at_future", + "value": "\"magic_link_tokens\".\"expires_at\" > \"magic_link_tokens\".\"created_at\"" + } + }, + "isRLSEnabled": false + }, + "public.microdollar_usage": { + "name": "microdollar_usage", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cost": { + "name": "cost", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "input_tokens": { + "name": "input_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "output_tokens": { + "name": "output_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "cache_write_tokens": { + "name": "cache_write_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "cache_hit_tokens": { + "name": "cache_hit_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "requested_model": { + "name": "requested_model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cache_discount": { + "name": "cache_discount", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "has_error": { + "name": "has_error", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "abuse_classification": { + "name": "abuse_classification", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "inference_provider": { + "name": "inference_provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_created_at": { + "name": "idx_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_abuse_classification": { + "name": "idx_abuse_classification", + "columns": [ + { + "expression": "abuse_classification", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_kilo_user_id_created_at2": { + "name": "idx_kilo_user_id_created_at2", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_microdollar_usage_organization_id": { + "name": "idx_microdollar_usage_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"microdollar_usage\".\"organization_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.microdollar_usage_daily": { + "name": "microdollar_usage_daily", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "usage_date": { + "name": "usage_date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "total_cost_microdollars": { + "name": "total_cost_microdollars", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_microdollar_usage_daily_personal": { + "name": "idx_microdollar_usage_daily_personal", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "usage_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"microdollar_usage_daily\".\"organization_id\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_microdollar_usage_daily_org": { + "name": "idx_microdollar_usage_daily_org", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "usage_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"microdollar_usage_daily\".\"organization_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.microdollar_usage_metadata": { + "name": "microdollar_usage_metadata", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "message_id": { + "name": "message_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "http_user_agent_id": { + "name": "http_user_agent_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "http_ip_id": { + "name": "http_ip_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "vercel_ip_city_id": { + "name": "vercel_ip_city_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "vercel_ip_country_id": { + "name": "vercel_ip_country_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "vercel_ip_latitude": { + "name": "vercel_ip_latitude", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "vercel_ip_longitude": { + "name": "vercel_ip_longitude", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "ja4_digest_id": { + "name": "ja4_digest_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "user_prompt_prefix": { + "name": "user_prompt_prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "system_prompt_prefix_id": { + "name": "system_prompt_prefix_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "system_prompt_length": { + "name": "system_prompt_length", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "max_tokens": { + "name": "max_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "has_middle_out_transform": { + "name": "has_middle_out_transform", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "status_code": { + "name": "status_code", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "upstream_id": { + "name": "upstream_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "finish_reason_id": { + "name": "finish_reason_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "latency": { + "name": "latency", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "moderation_latency": { + "name": "moderation_latency", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "generation_time": { + "name": "generation_time", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "is_byok": { + "name": "is_byok", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "is_user_byok": { + "name": "is_user_byok", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "streamed": { + "name": "streamed", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "cancelled": { + "name": "cancelled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "editor_name_id": { + "name": "editor_name_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "api_kind_id": { + "name": "api_kind_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "has_tools": { + "name": "has_tools", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "machine_id": { + "name": "machine_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "feature_id": { + "name": "feature_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mode_id": { + "name": "mode_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "auto_model_id": { + "name": "auto_model_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "market_cost": { + "name": "market_cost", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "is_free": { + "name": "is_free", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_microdollar_usage_metadata_created_at": { + "name": "idx_microdollar_usage_metadata_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_microdollar_usage_metadata_session_id": { + "name": "idx_microdollar_usage_metadata_session_id", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"microdollar_usage_metadata\".\"session_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "microdollar_usage_metadata_http_user_agent_id_http_user_agent_http_user_agent_id_fk": { + "name": "microdollar_usage_metadata_http_user_agent_id_http_user_agent_http_user_agent_id_fk", + "tableFrom": "microdollar_usage_metadata", + "tableTo": "http_user_agent", + "columnsFrom": [ + "http_user_agent_id" + ], + "columnsTo": [ + "http_user_agent_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "microdollar_usage_metadata_http_ip_id_http_ip_http_ip_id_fk": { + "name": "microdollar_usage_metadata_http_ip_id_http_ip_http_ip_id_fk", + "tableFrom": "microdollar_usage_metadata", + "tableTo": "http_ip", + "columnsFrom": [ + "http_ip_id" + ], + "columnsTo": [ + "http_ip_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "microdollar_usage_metadata_vercel_ip_city_id_vercel_ip_city_vercel_ip_city_id_fk": { + "name": "microdollar_usage_metadata_vercel_ip_city_id_vercel_ip_city_vercel_ip_city_id_fk", + "tableFrom": "microdollar_usage_metadata", + "tableTo": "vercel_ip_city", + "columnsFrom": [ + "vercel_ip_city_id" + ], + "columnsTo": [ + "vercel_ip_city_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "microdollar_usage_metadata_vercel_ip_country_id_vercel_ip_country_vercel_ip_country_id_fk": { + "name": "microdollar_usage_metadata_vercel_ip_country_id_vercel_ip_country_vercel_ip_country_id_fk", + "tableFrom": "microdollar_usage_metadata", + "tableTo": "vercel_ip_country", + "columnsFrom": [ + "vercel_ip_country_id" + ], + "columnsTo": [ + "vercel_ip_country_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "microdollar_usage_metadata_ja4_digest_id_ja4_digest_ja4_digest_id_fk": { + "name": "microdollar_usage_metadata_ja4_digest_id_ja4_digest_ja4_digest_id_fk", + "tableFrom": "microdollar_usage_metadata", + "tableTo": "ja4_digest", + "columnsFrom": [ + "ja4_digest_id" + ], + "columnsTo": [ + "ja4_digest_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "microdollar_usage_metadata_system_prompt_prefix_id_system_prompt_prefix_system_prompt_prefix_id_fk": { + "name": "microdollar_usage_metadata_system_prompt_prefix_id_system_prompt_prefix_system_prompt_prefix_id_fk", + "tableFrom": "microdollar_usage_metadata", + "tableTo": "system_prompt_prefix", + "columnsFrom": [ + "system_prompt_prefix_id" + ], + "columnsTo": [ + "system_prompt_prefix_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mode": { + "name": "mode", + "schema": "", + "columns": { + "mode_id": { + "name": "mode_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UQ_mode": { + "name": "UQ_mode", + "columns": [ + { + "expression": "mode", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.model_stats": { + "name": "model_stats", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "is_featured": { + "name": "is_featured", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_stealth": { + "name": "is_stealth", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_recommended": { + "name": "is_recommended", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "openrouter_id": { + "name": "openrouter_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "aa_slug": { + "name": "aa_slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model_creator": { + "name": "model_creator", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "creator_slug": { + "name": "creator_slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "release_date": { + "name": "release_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "price_input": { + "name": "price_input", + "type": "numeric(10, 6)", + "primaryKey": false, + "notNull": false + }, + "price_output": { + "name": "price_output", + "type": "numeric(10, 6)", + "primaryKey": false, + "notNull": false + }, + "coding_index": { + "name": "coding_index", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": false + }, + "speed_tokens_per_sec": { + "name": "speed_tokens_per_sec", + "type": "numeric(8, 2)", + "primaryKey": false, + "notNull": false + }, + "context_length": { + "name": "context_length", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "max_output_tokens": { + "name": "max_output_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "input_modalities": { + "name": "input_modalities", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "openrouter_data": { + "name": "openrouter_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "benchmarks": { + "name": "benchmarks", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "chart_data": { + "name": "chart_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_model_stats_openrouter_id": { + "name": "IDX_model_stats_openrouter_id", + "columns": [ + { + "expression": "openrouter_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_model_stats_slug": { + "name": "IDX_model_stats_slug", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_model_stats_is_active": { + "name": "IDX_model_stats_is_active", + "columns": [ + { + "expression": "is_active", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_model_stats_creator_slug": { + "name": "IDX_model_stats_creator_slug", + "columns": [ + { + "expression": "creator_slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_model_stats_price_input": { + "name": "IDX_model_stats_price_input", + "columns": [ + { + "expression": "price_input", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_model_stats_coding_index": { + "name": "IDX_model_stats_coding_index", + "columns": [ + { + "expression": "coding_index", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_model_stats_context_length": { + "name": "IDX_model_stats_context_length", + "columns": [ + { + "expression": "context_length", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "model_stats_openrouter_id_unique": { + "name": "model_stats_openrouter_id_unique", + "nullsNotDistinct": false, + "columns": [ + "openrouter_id" + ] + }, + "model_stats_slug_unique": { + "name": "model_stats_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.model_eval_ingestions": { + "name": "model_eval_ingestions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "bench_eval_name": { + "name": "bench_eval_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bench_eval_url": { + "name": "bench_eval_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model_stats_id": { + "name": "model_stats_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "variant": { + "name": "variant", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "task_source": { + "name": "task_source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "n_total_trials": { + "name": "n_total_trials", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "total_score": { + "name": "total_score", + "type": "numeric(14, 6)", + "primaryKey": false, + "notNull": true + }, + "overall_score": { + "name": "overall_score", + "type": "numeric(12, 8)", + "primaryKey": false, + "notNull": true + }, + "n_errored": { + "name": "n_errored", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "avg_cost_microdollars": { + "name": "avg_cost_microdollars", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "avg_input_tokens": { + "name": "avg_input_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "avg_output_tokens": { + "name": "avg_output_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "avg_cache_read_tokens": { + "name": "avg_cache_read_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "avg_execution_ms": { + "name": "avg_execution_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "promoted_at": { + "name": "promoted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "promoted_by_email": { + "name": "promoted_by_email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "promotion_note": { + "name": "promotion_note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_model_eval_ingestions_lookup": { + "name": "IDX_model_eval_ingestions_lookup", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "model", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "variant", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "task_source", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "promoted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_model_eval_ingestions_model_stats": { + "name": "IDX_model_eval_ingestions_model_stats", + "columns": [ + { + "expression": "model_stats_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_model_eval_ingestions_promoted_by_email_lower": { + "name": "IDX_model_eval_ingestions_promoted_by_email_lower", + "columns": [ + { + "expression": "LOWER(\"promoted_by_email\")", + "asc": true, + "isExpression": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "model_eval_ingestions_model_stats_id_model_stats_id_fk": { + "name": "model_eval_ingestions_model_stats_id_model_stats_id_fk", + "tableFrom": "model_eval_ingestions", + "tableTo": "model_stats", + "columnsFrom": [ + "model_stats_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "model_eval_ingestions_bench_eval_name_unique": { + "name": "model_eval_ingestions_bench_eval_name_unique", + "nullsNotDistinct": false, + "columns": [ + "bench_eval_name" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.model_experiment": { + "name": "model_experiment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "public_model_id": { + "name": "public_model_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "is_archived": { + "name": "is_archived", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "UQ_model_experiment_public_model_id_routing": { + "name": "UQ_model_experiment_public_model_id_routing", + "columns": [ + { + "expression": "public_model_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"model_experiment\".\"status\" IN ('active', 'paused')", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_model_experiment_status": { + "name": "IDX_model_experiment_status", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "model_experiment_created_by_user_id_kilocode_users_id_fk": { + "name": "model_experiment_created_by_user_id_kilocode_users_id_fk", + "tableFrom": "model_experiment", + "tableTo": "kilocode_users", + "columnsFrom": [ + "created_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "model_experiment_status_valid": { + "name": "model_experiment_status_valid", + "value": "\"model_experiment\".\"status\" IN ('draft', 'active', 'paused', 'completed')" + }, + "model_experiment_active_not_archived": { + "name": "model_experiment_active_not_archived", + "value": "\"model_experiment\".\"status\" <> 'active' OR \"model_experiment\".\"is_archived\" = false" + } + }, + "isRLSEnabled": false + }, + "public.model_experiment_request": { + "name": "model_experiment_request", + "schema": "", + "columns": { + "usage_id": { + "name": "usage_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "variant_version_id": { + "name": "variant_version_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "allocation_subject": { + "name": "allocation_subject", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "client_request_id": { + "name": "client_request_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "request_kind": { + "name": "request_kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "request_body_sha256": { + "name": "request_body_sha256", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "was_truncated": { + "name": "was_truncated", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_model_experiment_request_variant_version_created_at": { + "name": "IDX_model_experiment_request_variant_version_created_at", + "columns": [ + { + "expression": "variant_version_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_model_experiment_request_client_request_id": { + "name": "IDX_model_experiment_request_client_request_id", + "columns": [ + { + "expression": "client_request_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"model_experiment_request\".\"client_request_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "model_experiment_request_usage_id_microdollar_usage_id_fk": { + "name": "model_experiment_request_usage_id_microdollar_usage_id_fk", + "tableFrom": "model_experiment_request", + "tableTo": "microdollar_usage", + "columnsFrom": [ + "usage_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "model_experiment_request_variant_version_id_model_experiment_variant_version_id_fk": { + "name": "model_experiment_request_variant_version_id_model_experiment_variant_version_id_fk", + "tableFrom": "model_experiment_request", + "tableTo": "model_experiment_variant_version", + "columnsFrom": [ + "variant_version_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "model_experiment_request_allocation_subject_valid": { + "name": "model_experiment_request_allocation_subject_valid", + "value": "\"model_experiment_request\".\"allocation_subject\" IN ('user', 'machine', 'ip')" + }, + "model_experiment_request_request_kind_valid": { + "name": "model_experiment_request_request_kind_valid", + "value": "\"model_experiment_request\".\"request_kind\" IN ('chat_completions', 'messages', 'responses')" + }, + "model_experiment_request_request_body_sha256_format": { + "name": "model_experiment_request_request_body_sha256_format", + "value": "\"model_experiment_request\".\"request_body_sha256\" ~ '^[0-9a-f]{64}$' OR \"model_experiment_request\".\"request_body_sha256\" IN ('__failed__', '__deleted__')" + } + }, + "isRLSEnabled": false + }, + "public.model_experiment_variant": { + "name": "model_experiment_variant", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "experiment_id": { + "name": "experiment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "weight": { + "name": "weight", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_model_experiment_variant_experiment_id": { + "name": "IDX_model_experiment_variant_experiment_id", + "columns": [ + { + "expression": "experiment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "model_experiment_variant_experiment_id_model_experiment_id_fk": { + "name": "model_experiment_variant_experiment_id_model_experiment_id_fk", + "tableFrom": "model_experiment_variant", + "tableTo": "model_experiment", + "columnsFrom": [ + "experiment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_model_experiment_variant_experiment_label": { + "name": "UQ_model_experiment_variant_experiment_label", + "nullsNotDistinct": false, + "columns": [ + "experiment_id", + "label" + ] + } + }, + "policies": {}, + "checkConstraints": { + "model_experiment_variant_weight_positive": { + "name": "model_experiment_variant_weight_positive", + "value": "\"model_experiment_variant\".\"weight\" > 0" + } + }, + "isRLSEnabled": false + }, + "public.model_experiment_variant_version": { + "name": "model_experiment_variant_version", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "variant_id": { + "name": "variant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "upstream": { + "name": "upstream", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "encrypted_api_key": { + "name": "encrypted_api_key", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "effective_at": { + "name": "effective_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_model_experiment_variant_version_variant_effective": { + "name": "IDX_model_experiment_variant_version_variant_effective", + "columns": [ + { + "expression": "variant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "effective_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "model_experiment_variant_version_variant_id_model_experiment_variant_id_fk": { + "name": "model_experiment_variant_version_variant_id_model_experiment_variant_id_fk", + "tableFrom": "model_experiment_variant_version", + "tableTo": "model_experiment_variant", + "columnsFrom": [ + "variant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "model_experiment_variant_version_created_by_kilocode_users_id_fk": { + "name": "model_experiment_variant_version_created_by_kilocode_users_id_fk", + "tableFrom": "model_experiment_variant_version", + "tableTo": "kilocode_users", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.models_by_provider": { + "name": "models_by_provider", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "openrouter": { + "name": "openrouter", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "vercel": { + "name": "vercel", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_audit_logs": { + "name": "organization_audit_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_email": { + "name": "actor_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_name": { + "name": "actor_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_organization_audit_logs_organization_id": { + "name": "IDX_organization_audit_logs_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_audit_logs_action": { + "name": "IDX_organization_audit_logs_action", + "columns": [ + { + "expression": "action", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_audit_logs_actor_id": { + "name": "IDX_organization_audit_logs_actor_id", + "columns": [ + { + "expression": "actor_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_audit_logs_created_at": { + "name": "IDX_organization_audit_logs_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_invitations": { + "name": "organization_invitations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invited_by": { + "name": "invited_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "accepted_at": { + "name": "accepted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_organization_invitations_token": { + "name": "UQ_organization_invitations_token", + "columns": [ + { + "expression": "token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_invitations_org_id": { + "name": "IDX_organization_invitations_org_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_invitations_email": { + "name": "IDX_organization_invitations_email", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_invitations_expires_at": { + "name": "IDX_organization_invitations_expires_at", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_membership_removals": { + "name": "organization_membership_removals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "removed_at": { + "name": "removed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "removed_by": { + "name": "removed_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previous_role": { + "name": "previous_role", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "IDX_org_membership_removals_org_id": { + "name": "IDX_org_membership_removals_org_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_org_membership_removals_user_id": { + "name": "IDX_org_membership_removals_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_org_membership_removals_org_user": { + "name": "UQ_org_membership_removals_org_user", + "nullsNotDistinct": false, + "columns": [ + "organization_id", + "kilo_user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_memberships": { + "name": "organization_memberships", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "joined_at": { + "name": "joined_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "invited_by": { + "name": "invited_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_organization_memberships_org_id": { + "name": "IDX_organization_memberships_org_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_memberships_user_id": { + "name": "IDX_organization_memberships_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_organization_memberships_org_user": { + "name": "UQ_organization_memberships_org_user", + "nullsNotDistinct": false, + "columns": [ + "organization_id", + "kilo_user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_seats_purchases": { + "name": "organization_seats_purchases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "subscription_stripe_id": { + "name": "subscription_stripe_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "seat_count": { + "name": "seat_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "amount_usd": { + "name": "amount_usd", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "subscription_status": { + "name": "subscription_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "starts_at": { + "name": "starts_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "billing_cycle": { + "name": "billing_cycle", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'monthly'" + } + }, + "indexes": { + "IDX_organization_seats_org_id": { + "name": "IDX_organization_seats_org_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_seats_expires_at": { + "name": "IDX_organization_seats_expires_at", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_seats_created_at": { + "name": "IDX_organization_seats_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_seats_updated_at": { + "name": "IDX_organization_seats_updated_at", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_seats_starts_at": { + "name": "IDX_organization_seats_starts_at", + "columns": [ + { + "expression": "starts_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_organization_seats_idempotency_key": { + "name": "UQ_organization_seats_idempotency_key", + "nullsNotDistinct": false, + "columns": [ + "idempotency_key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_user_limits": { + "name": "organization_user_limits", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "limit_type": { + "name": "limit_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "microdollar_limit": { + "name": "microdollar_limit", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_organization_user_limits_org_id": { + "name": "IDX_organization_user_limits_org_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_user_limits_user_id": { + "name": "IDX_organization_user_limits_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_organization_user_limits_org_user": { + "name": "UQ_organization_user_limits_org_user", + "nullsNotDistinct": false, + "columns": [ + "organization_id", + "kilo_user_id", + "limit_type" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization_user_usage": { + "name": "organization_user_usage", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "usage_date": { + "name": "usage_date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "limit_type": { + "name": "limit_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "microdollar_usage": { + "name": "microdollar_usage", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_organization_user_daily_usage_org_id": { + "name": "IDX_organization_user_daily_usage_org_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_organization_user_daily_usage_user_id": { + "name": "IDX_organization_user_daily_usage_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_organization_user_daily_usage_org_user_date": { + "name": "UQ_organization_user_daily_usage_org_user_date", + "nullsNotDistinct": false, + "columns": [ + "organization_id", + "kilo_user_id", + "limit_type", + "usage_date" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organizations": { + "name": "organizations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "microdollars_used": { + "name": "microdollars_used", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "microdollars_balance": { + "name": "microdollars_balance", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "total_microdollars_acquired": { + "name": "total_microdollars_acquired", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "next_credit_expiration_at": { + "name": "next_credit_expiration_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "auto_top_up_enabled": { + "name": "auto_top_up_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "settings": { + "name": "settings", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "seat_count": { + "name": "seat_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "require_seats": { + "name": "require_seats", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_by_kilo_user_id": { + "name": "created_by_kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "sso_domain": { + "name": "sso_domain", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'teams'" + }, + "free_trial_end_at": { + "name": "free_trial_end_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "company_domain": { + "name": "company_domain", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "IDX_organizations_sso_domain": { + "name": "IDX_organizations_sso_domain", + "columns": [ + { + "expression": "sso_domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "organizations_name_not_empty_check": { + "name": "organizations_name_not_empty_check", + "value": "length(trim(\"organizations\".\"name\")) > 0" + } + }, + "isRLSEnabled": false + }, + "public.organization_modes": { + "name": "organization_modes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + } + }, + "indexes": { + "IDX_organization_modes_organization_id": { + "name": "IDX_organization_modes_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_organization_modes_org_id_slug": { + "name": "UQ_organization_modes_org_id_slug", + "nullsNotDistinct": false, + "columns": [ + "organization_id", + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payment_methods": { + "name": "payment_methods", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "stripe_fingerprint": { + "name": "stripe_fingerprint", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_id": { + "name": "stripe_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last4": { + "name": "last4", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "brand": { + "name": "brand", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_line1": { + "name": "address_line1", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_line2": { + "name": "address_line2", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_city": { + "name": "address_city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_state": { + "name": "address_state", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_zip": { + "name": "address_zip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_country": { + "name": "address_country", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "three_d_secure_supported": { + "name": "three_d_secure_supported", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "funding": { + "name": "funding", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "regulated_status": { + "name": "regulated_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_line1_check_status": { + "name": "address_line1_check_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postal_code_check_status": { + "name": "postal_code_check_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "http_x_forwarded_for": { + "name": "http_x_forwarded_for", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ip_city": { + "name": "http_x_vercel_ip_city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ip_country": { + "name": "http_x_vercel_ip_country", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ip_latitude": { + "name": "http_x_vercel_ip_latitude", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ip_longitude": { + "name": "http_x_vercel_ip_longitude", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ja4_digest": { + "name": "http_x_vercel_ja4_digest", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "eligible_for_free_credits": { + "name": "eligible_for_free_credits", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "stripe_data": { + "name": "stripe_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "IDX_d7d7fb15569674aaadcfbc0428": { + "name": "IDX_d7d7fb15569674aaadcfbc0428", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_e1feb919d0ab8a36381d5d5138": { + "name": "IDX_e1feb919d0ab8a36381d5d5138", + "columns": [ + { + "expression": "stripe_fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_payment_methods_organization_id": { + "name": "IDX_payment_methods_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_29df1b0403df5792c96bbbfdbe6": { + "name": "UQ_29df1b0403df5792c96bbbfdbe6", + "nullsNotDistinct": false, + "columns": [ + "user_id", + "stripe_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pending_impact_sale_reversals": { + "name": "pending_impact_sale_reversals", + "schema": "", + "columns": { + "stripe_charge_id": { + "name": "stripe_charge_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "dispute_id": { + "name": "dispute_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_date": { + "name": "event_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_attempt_at": { + "name": "last_attempt_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "pending_impact_sale_reversals_attempt_count_non_negative_check": { + "name": "pending_impact_sale_reversals_attempt_count_non_negative_check", + "value": "\"pending_impact_sale_reversals\".\"attempt_count\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.platform_integrations": { + "name": "platform_integrations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "integration_type": { + "name": "integration_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform_installation_id": { + "name": "platform_installation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "platform_account_id": { + "name": "platform_account_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "platform_account_login": { + "name": "platform_account_login", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "permissions": { + "name": "permissions", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "scopes": { + "name": "scopes", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "repository_access": { + "name": "repository_access", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "repositories": { + "name": "repositories", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "repositories_synced_at": { + "name": "repositories_synced_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "kilo_requester_user_id": { + "name": "kilo_requester_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "platform_requester_account_id": { + "name": "platform_requester_account_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "integration_status": { + "name": "integration_status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "suspended_at": { + "name": "suspended_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "suspended_by": { + "name": "suspended_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "github_app_type": { + "name": "github_app_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'standard'" + }, + "installed_at": { + "name": "installed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_platform_integrations_owned_by_org_platform_inst": { + "name": "UQ_platform_integrations_owned_by_org_platform_inst", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform_installation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"platform_integrations\".\"owned_by_organization_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_platform_integrations_owned_by_user_platform_inst": { + "name": "UQ_platform_integrations_owned_by_user_platform_inst", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform_installation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"platform_integrations\".\"owned_by_user_id\" is not null", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_platform_integrations_slack_platform_inst": { + "name": "UQ_platform_integrations_slack_platform_inst", + "columns": [ + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform_installation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"platform_integrations\".\"platform\" = 'slack' AND \"platform_integrations\".\"platform_installation_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_platform_integrations_linear_platform_inst": { + "name": "UQ_platform_integrations_linear_platform_inst", + "columns": [ + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform_installation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"platform_integrations\".\"platform\" = 'linear' AND \"platform_integrations\".\"platform_installation_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_platform_integrations_owned_by_org_id": { + "name": "IDX_platform_integrations_owned_by_org_id", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_platform_integrations_owned_by_user_id": { + "name": "IDX_platform_integrations_owned_by_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_platform_integrations_platform_inst_id": { + "name": "IDX_platform_integrations_platform_inst_id", + "columns": [ + { + "expression": "platform_installation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_platform_integrations_platform": { + "name": "IDX_platform_integrations_platform", + "columns": [ + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_platform_integrations_owned_by_org_platform": { + "name": "IDX_platform_integrations_owned_by_org_platform", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_platform_integrations_owned_by_user_platform": { + "name": "IDX_platform_integrations_owned_by_user_platform", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_platform_integrations_integration_status": { + "name": "IDX_platform_integrations_integration_status", + "columns": [ + { + "expression": "integration_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_platform_integrations_kilo_requester": { + "name": "IDX_platform_integrations_kilo_requester", + "columns": [ + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kilo_requester_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "integration_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_platform_integrations_platform_requester": { + "name": "IDX_platform_integrations_platform_requester", + "columns": [ + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "platform_requester_account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "integration_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "platform_integrations_owned_by_organization_id_organizations_id_fk": { + "name": "platform_integrations_owned_by_organization_id_organizations_id_fk", + "tableFrom": "platform_integrations", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "platform_integrations_owned_by_user_id_kilocode_users_id_fk": { + "name": "platform_integrations_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "platform_integrations", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "platform_integrations_owner_check": { + "name": "platform_integrations_owner_check", + "value": "(\n (\"platform_integrations\".\"owned_by_user_id\" IS NOT NULL AND \"platform_integrations\".\"owned_by_organization_id\" IS NULL) OR\n (\"platform_integrations\".\"owned_by_user_id\" IS NULL AND \"platform_integrations\".\"owned_by_organization_id\" IS NOT NULL)\n )" + } + }, + "isRLSEnabled": false + }, + "public.referral_code_usages": { + "name": "referral_code_usages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "referring_kilo_user_id": { + "name": "referring_kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "redeeming_kilo_user_id": { + "name": "redeeming_kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "amount_usd": { + "name": "amount_usd", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "paid_at": { + "name": "paid_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_referral_code_usages_redeeming_kilo_user_id": { + "name": "IDX_referral_code_usages_redeeming_kilo_user_id", + "columns": [ + { + "expression": "redeeming_kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_referral_code_usages_redeeming_user_id_code": { + "name": "UQ_referral_code_usages_redeeming_user_id_code", + "nullsNotDistinct": false, + "columns": [ + "redeeming_kilo_user_id", + "referring_kilo_user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.referral_codes": { + "name": "referral_codes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "max_redemptions": { + "name": "max_redemptions", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 10 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_referral_codes_kilo_user_id": { + "name": "UQ_referral_codes_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_referral_codes_code": { + "name": "IDX_referral_codes_code", + "columns": [ + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security_advisor_check_catalog": { + "name": "security_advisor_check_catalog", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "check_id": { + "name": "check_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "severity": { + "name": "severity", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "explanation": { + "name": "explanation", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "risk": { + "name": "risk", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_advisor_check_catalog_check_id_unique": { + "name": "security_advisor_check_catalog_check_id_unique", + "nullsNotDistinct": false, + "columns": [ + "check_id" + ] + } + }, + "policies": {}, + "checkConstraints": { + "security_advisor_check_catalog_severity_check": { + "name": "security_advisor_check_catalog_severity_check", + "value": "\"security_advisor_check_catalog\".\"severity\" in ('critical', 'warn', 'info')" + } + }, + "isRLSEnabled": false + }, + "public.security_advisor_content": { + "name": "security_advisor_content", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_advisor_content_key_unique": { + "name": "security_advisor_content_key_unique", + "nullsNotDistinct": false, + "columns": [ + "key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security_advisor_kiloclaw_coverage": { + "name": "security_advisor_kiloclaw_coverage", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "area": { + "name": "area", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "detail": { + "name": "detail", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "match_check_ids": { + "name": "match_check_ids", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_advisor_kiloclaw_coverage_area_unique": { + "name": "security_advisor_kiloclaw_coverage_area_unique", + "nullsNotDistinct": false, + "columns": [ + "area" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security_advisor_scans": { + "name": "security_advisor_scans", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_platform": { + "name": "source_platform", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_method": { + "name": "source_method", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plugin_version": { + "name": "plugin_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "openclaw_version": { + "name": "openclaw_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "public_ip": { + "name": "public_ip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "findings_critical": { + "name": "findings_critical", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "findings_warn": { + "name": "findings_warn", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "findings_info": { + "name": "findings_info", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "idx_security_advisor_scans_user_created_at": { + "name": "idx_security_advisor_scans_user_created_at", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_advisor_scans_created_at": { + "name": "idx_security_advisor_scans_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_security_advisor_scans_platform": { + "name": "idx_security_advisor_scans_platform", + "columns": [ + { + "expression": "source_platform", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security_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_hardware_fingerprint": { + "name": "idx_hardware_fingerprint", + "columns": [ + { + "expression": "hardware_fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_kilo_user_id": { + "name": "idx_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_stytch_fingerprints_reasons_gin": { + "name": "idx_stytch_fingerprints_reasons_gin", + "columns": [ + { + "expression": "reasons", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "idx_verdict_action": { + "name": "idx_verdict_action", + "columns": [ + { + "expression": "verdict_action", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_visitor_fingerprint": { + "name": "idx_visitor_fingerprint", + "columns": [ + { + "expression": "visitor_fingerprint", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.system_prompt_prefix": { + "name": "system_prompt_prefix", + "schema": "", + "columns": { + "system_prompt_prefix_id": { + "name": "system_prompt_prefix_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "system_prompt_prefix": { + "name": "system_prompt_prefix", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UQ_system_prompt_prefix": { + "name": "UQ_system_prompt_prefix", + "columns": [ + { + "expression": "system_prompt_prefix", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.transactional_email_log": { + "name": "transactional_email_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "email_type": { + "name": "email_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sent_at": { + "name": "sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_transactional_email_log_type_idempotency_key": { + "name": "UQ_transactional_email_log_type_idempotency_key", + "columns": [ + { + "expression": "email_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_transactional_email_log_user_id": { + "name": "IDX_transactional_email_log_user_id", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_transactional_email_log_organization_id": { + "name": "IDX_transactional_email_log_organization_id", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "transactional_email_log_user_id_kilocode_users_id_fk": { + "name": "transactional_email_log_user_id_kilocode_users_id_fk", + "tableFrom": "transactional_email_log", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "transactional_email_log_organization_id_organizations_id_fk": { + "name": "transactional_email_log_organization_id_organizations_id_fk", + "tableFrom": "transactional_email_log", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "CHK_transactional_email_log_owner": { + "name": "CHK_transactional_email_log_owner", + "value": "\"transactional_email_log\".\"user_id\" IS NOT NULL OR \"transactional_email_log\".\"organization_id\" IS NOT NULL" + } + }, + "isRLSEnabled": false + }, + "public.user_admin_notes": { + "name": "user_admin_notes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "note_content": { + "name": "note_content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "admin_kilo_user_id": { + "name": "admin_kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_34517df0b385234babc38fe81b": { + "name": "IDX_34517df0b385234babc38fe81b", + "columns": [ + { + "expression": "admin_kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_ccbde98c4c14046daa5682ec4f": { + "name": "IDX_ccbde98c4c14046daa5682ec4f", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_d0270eb24ef6442d65a0b7853c": { + "name": "IDX_d0270eb24ef6442d65a0b7853c", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_affiliate_attributions": { + "name": "user_affiliate_attributions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tracking_id": { + "name": "tracking_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_user_affiliate_attributions_user_id": { + "name": "IDX_user_affiliate_attributions_user_id", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_affiliate_attributions_user_id_kilocode_users_id_fk": { + "name": "user_affiliate_attributions_user_id_kilocode_users_id_fk", + "tableFrom": "user_affiliate_attributions", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_user_affiliate_attributions_user_provider": { + "name": "UQ_user_affiliate_attributions_user_provider", + "nullsNotDistinct": false, + "columns": [ + "user_id", + "provider" + ] + } + }, + "policies": {}, + "checkConstraints": { + "user_affiliate_attributions_provider_check": { + "name": "user_affiliate_attributions_provider_check", + "value": "\"user_affiliate_attributions\".\"provider\" IN ('impact')" + } + }, + "isRLSEnabled": false + }, + "public.user_affiliate_events": { + "name": "user_affiliate_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dedupe_key": { + "name": "dedupe_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_event_id": { + "name": "parent_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "delivery_state": { + "name": "delivery_state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "payload_json": { + "name": "payload_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "stripe_charge_id": { + "name": "stripe_charge_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "impact_action_id": { + "name": "impact_action_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "impact_submission_uri": { + "name": "impact_submission_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "next_retry_at": { + "name": "next_retry_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_user_affiliate_events_claim_path": { + "name": "IDX_user_affiliate_events_claim_path", + "columns": [ + { + "expression": "delivery_state", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "coalesce(\"next_retry_at\", '-infinity'::timestamptz)", + "asc": true, + "isExpression": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_user_affiliate_events_parent_event_id": { + "name": "IDX_user_affiliate_events_parent_event_id", + "columns": [ + { + "expression": "parent_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_user_affiliate_events_provider_event_type_charge": { + "name": "IDX_user_affiliate_events_provider_event_type_charge", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "event_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "stripe_charge_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_affiliate_events_user_id_kilocode_users_id_fk": { + "name": "user_affiliate_events_user_id_kilocode_users_id_fk", + "tableFrom": "user_affiliate_events", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "user_affiliate_events_parent_event_id_fk": { + "name": "user_affiliate_events_parent_event_id_fk", + "tableFrom": "user_affiliate_events", + "tableTo": "user_affiliate_events", + "columnsFrom": [ + "parent_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_user_affiliate_events_dedupe_key": { + "name": "UQ_user_affiliate_events_dedupe_key", + "nullsNotDistinct": false, + "columns": [ + "dedupe_key" + ] + } + }, + "policies": {}, + "checkConstraints": { + "user_affiliate_events_provider_check": { + "name": "user_affiliate_events_provider_check", + "value": "\"user_affiliate_events\".\"provider\" IN ('impact')" + }, + "user_affiliate_events_event_type_check": { + "name": "user_affiliate_events_event_type_check", + "value": "\"user_affiliate_events\".\"event_type\" IN ('signup', 'trial_start', 'trial_end', 'sale', 'sale_reversal')" + }, + "user_affiliate_events_delivery_state_check": { + "name": "user_affiliate_events_delivery_state_check", + "value": "\"user_affiliate_events\".\"delivery_state\" IN ('queued', 'blocked', 'sending', 'delivered', 'failed')" + }, + "user_affiliate_events_attempt_count_non_negative_check": { + "name": "user_affiliate_events_attempt_count_non_negative_check", + "value": "\"user_affiliate_events\".\"attempt_count\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.user_auth_provider": { + "name": "user_auth_provider", + "schema": "", + "columns": { + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_account_id": { + "name": "provider_account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "hosted_domain": { + "name": "hosted_domain", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_user_auth_provider_kilo_user_id": { + "name": "IDX_user_auth_provider_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_user_auth_provider_hosted_domain": { + "name": "IDX_user_auth_provider_hosted_domain", + "columns": [ + { + "expression": "hosted_domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "user_auth_provider_provider_provider_account_id_pk": { + "name": "user_auth_provider_provider_provider_account_id_pk", + "columns": [ + "provider", + "provider_account_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_feedback": { + "name": "user_feedback", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "feedback_text": { + "name": "feedback_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "feedback_for": { + "name": "feedback_for", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "feedback_batch": { + "name": "feedback_batch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "context_json": { + "name": "context_json", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_user_feedback_created_at": { + "name": "IDX_user_feedback_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_user_feedback_kilo_user_id": { + "name": "IDX_user_feedback_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_user_feedback_feedback_for": { + "name": "IDX_user_feedback_feedback_for", + "columns": [ + { + "expression": "feedback_for", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_user_feedback_feedback_batch": { + "name": "IDX_user_feedback_feedback_batch", + "columns": [ + { + "expression": "feedback_batch", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_user_feedback_source": { + "name": "IDX_user_feedback_source", + "columns": [ + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_feedback_kilo_user_id_kilocode_users_id_fk": { + "name": "user_feedback_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "user_feedback", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_period_cache": { + "name": "user_period_cache", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cache_type": { + "name": "cache_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "period_type": { + "name": "period_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "period_key": { + "name": "period_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "computed_at": { + "name": "computed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "shared_url_token": { + "name": "shared_url_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shared_at": { + "name": "shared_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "IDX_user_period_cache_kilo_user_id": { + "name": "IDX_user_period_cache_kilo_user_id", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_user_period_cache": { + "name": "UQ_user_period_cache", + "columns": [ + { + "expression": "kilo_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "cache_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "period_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "period_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_user_period_cache_lookup": { + "name": "IDX_user_period_cache_lookup", + "columns": [ + { + "expression": "cache_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "period_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "period_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UQ_user_period_cache_share_token": { + "name": "UQ_user_period_cache_share_token", + "columns": [ + { + "expression": "shared_url_token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"user_period_cache\".\"shared_url_token\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_period_cache_kilo_user_id_kilocode_users_id_fk": { + "name": "user_period_cache_kilo_user_id_kilocode_users_id_fk", + "tableFrom": "user_period_cache", + "tableTo": "kilocode_users", + "columnsFrom": [ + "kilo_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "user_period_cache_period_type_check": { + "name": "user_period_cache_period_type_check", + "value": "\"user_period_cache\".\"period_type\" IN ('year', 'quarter', 'month', 'week', 'custom')" + } + }, + "isRLSEnabled": false + }, + "public.user_push_tokens": { + "name": "user_push_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "UQ_user_push_tokens_token": { + "name": "UQ_user_push_tokens_token", + "columns": [ + { + "expression": "token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_user_push_tokens_user_id": { + "name": "IDX_user_push_tokens_user_id", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_push_tokens_user_id_kilocode_users_id_fk": { + "name": "user_push_tokens_user_id_kilocode_users_id_fk", + "tableFrom": "user_push_tokens", + "tableTo": "kilocode_users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.vercel_ip_city": { + "name": "vercel_ip_city", + "schema": "", + "columns": { + "vercel_ip_city_id": { + "name": "vercel_ip_city_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "vercel_ip_city": { + "name": "vercel_ip_city", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UQ_vercel_ip_city": { + "name": "UQ_vercel_ip_city", + "columns": [ + { + "expression": "vercel_ip_city", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.vercel_ip_country": { + "name": "vercel_ip_country", + "schema": "", + "columns": { + "vercel_ip_country_id": { + "name": "vercel_ip_country_id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "vercel_ip_country": { + "name": "vercel_ip_country", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UQ_vercel_ip_country": { + "name": "UQ_vercel_ip_country", + "columns": [ + { + "expression": "vercel_ip_country", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.webhook_events": { + "name": "webhook_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "pg_catalog.gen_random_uuid()" + }, + "owned_by_organization_id": { + "name": "owned_by_organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owned_by_user_id": { + "name": "owned_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event_action": { + "name": "event_action", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "headers": { + "name": "headers", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "processed": { + "name": "processed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "handlers_triggered": { + "name": "handlers_triggered", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "errors": { + "name": "errors", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "event_signature": { + "name": "event_signature", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "IDX_webhook_events_owned_by_org_id": { + "name": "IDX_webhook_events_owned_by_org_id", + "columns": [ + { + "expression": "owned_by_organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_webhook_events_owned_by_user_id": { + "name": "IDX_webhook_events_owned_by_user_id", + "columns": [ + { + "expression": "owned_by_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_webhook_events_platform": { + "name": "IDX_webhook_events_platform", + "columns": [ + { + "expression": "platform", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_webhook_events_event_type": { + "name": "IDX_webhook_events_event_type", + "columns": [ + { + "expression": "event_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "IDX_webhook_events_created_at": { + "name": "IDX_webhook_events_created_at", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "webhook_events_owned_by_organization_id_organizations_id_fk": { + "name": "webhook_events_owned_by_organization_id_organizations_id_fk", + "tableFrom": "webhook_events", + "tableTo": "organizations", + "columnsFrom": [ + "owned_by_organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "webhook_events_owned_by_user_id_kilocode_users_id_fk": { + "name": "webhook_events_owned_by_user_id_kilocode_users_id_fk", + "tableFrom": "webhook_events", + "tableTo": "kilocode_users", + "columnsFrom": [ + "owned_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "UQ_webhook_events_signature": { + "name": "UQ_webhook_events_signature", + "nullsNotDistinct": false, + "columns": [ + "event_signature" + ] + } + }, + "policies": {}, + "checkConstraints": { + "webhook_events_owner_check": { + "name": "webhook_events_owner_check", + "value": "(\n (\"webhook_events\".\"owned_by_user_id\" IS NOT NULL AND \"webhook_events\".\"owned_by_organization_id\" IS NULL) OR\n (\"webhook_events\".\"owned_by_user_id\" IS NULL AND \"webhook_events\".\"owned_by_organization_id\" IS NOT NULL)\n )" + } + }, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": { + "public.microdollar_usage_view": { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kilo_user_id": { + "name": "kilo_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cost": { + "name": "cost", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "input_tokens": { + "name": "input_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "output_tokens": { + "name": "output_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "cache_write_tokens": { + "name": "cache_write_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "cache_hit_tokens": { + "name": "cache_hit_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "http_x_forwarded_for": { + "name": "http_x_forwarded_for", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ip_city": { + "name": "http_x_vercel_ip_city", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ip_country": { + "name": "http_x_vercel_ip_country", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ip_latitude": { + "name": "http_x_vercel_ip_latitude", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ip_longitude": { + "name": "http_x_vercel_ip_longitude", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "http_x_vercel_ja4_digest": { + "name": "http_x_vercel_ja4_digest", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "requested_model": { + "name": "requested_model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_prompt_prefix": { + "name": "user_prompt_prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "system_prompt_prefix": { + "name": "system_prompt_prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "system_prompt_length": { + "name": "system_prompt_length", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "http_user_agent": { + "name": "http_user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cache_discount": { + "name": "cache_discount", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "max_tokens": { + "name": "max_tokens", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "has_middle_out_transform": { + "name": "has_middle_out_transform", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "has_error": { + "name": "has_error", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "abuse_classification": { + "name": "abuse_classification", + "type": "smallint", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "inference_provider": { + "name": "inference_provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status_code": { + "name": "status_code", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "upstream_id": { + "name": "upstream_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "finish_reason": { + "name": "finish_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "latency": { + "name": "latency", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "moderation_latency": { + "name": "moderation_latency", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "generation_time": { + "name": "generation_time", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "is_byok": { + "name": "is_byok", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "is_user_byok": { + "name": "is_user_byok", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "streamed": { + "name": "streamed", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "cancelled": { + "name": "cancelled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "editor_name": { + "name": "editor_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "api_kind": { + "name": "api_kind", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "has_tools": { + "name": "has_tools", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "machine_id": { + "name": "machine_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "feature": { + "name": "feature", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "auto_model": { + "name": "auto_model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "market_cost": { + "name": "market_cost", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "is_free": { + "name": "is_free", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "definition": "\n SELECT\n mu.id,\n mu.kilo_user_id,\n meta.message_id,\n mu.cost,\n mu.input_tokens,\n mu.output_tokens,\n mu.cache_write_tokens,\n mu.cache_hit_tokens,\n mu.created_at,\n ip.http_ip AS http_x_forwarded_for,\n city.vercel_ip_city AS http_x_vercel_ip_city,\n country.vercel_ip_country AS http_x_vercel_ip_country,\n meta.vercel_ip_latitude AS http_x_vercel_ip_latitude,\n meta.vercel_ip_longitude AS http_x_vercel_ip_longitude,\n ja4.ja4_digest AS http_x_vercel_ja4_digest,\n mu.provider,\n mu.model,\n mu.requested_model,\n meta.user_prompt_prefix,\n spp.system_prompt_prefix,\n meta.system_prompt_length,\n ua.http_user_agent,\n mu.cache_discount,\n meta.max_tokens,\n meta.has_middle_out_transform,\n mu.has_error,\n mu.abuse_classification,\n mu.organization_id,\n mu.inference_provider,\n mu.project_id,\n meta.status_code,\n meta.upstream_id,\n frfr.finish_reason,\n meta.latency,\n meta.moderation_latency,\n meta.generation_time,\n meta.is_byok,\n meta.is_user_byok,\n meta.streamed,\n meta.cancelled,\n edit.editor_name,\n ak.api_kind,\n meta.has_tools,\n meta.machine_id,\n feat.feature,\n meta.session_id,\n md.mode,\n am.auto_model,\n meta.market_cost,\n meta.is_free\n FROM \"microdollar_usage\" mu\n LEFT JOIN \"microdollar_usage_metadata\" meta ON mu.id = meta.id\n LEFT JOIN \"http_ip\" ip ON meta.http_ip_id = ip.http_ip_id\n LEFT JOIN \"vercel_ip_city\" city ON meta.vercel_ip_city_id = city.vercel_ip_city_id\n LEFT JOIN \"vercel_ip_country\" country ON meta.vercel_ip_country_id = country.vercel_ip_country_id\n LEFT JOIN \"ja4_digest\" ja4 ON meta.ja4_digest_id = ja4.ja4_digest_id\n LEFT JOIN \"system_prompt_prefix\" spp ON meta.system_prompt_prefix_id = spp.system_prompt_prefix_id\n LEFT JOIN \"http_user_agent\" ua ON meta.http_user_agent_id = ua.http_user_agent_id\n LEFT JOIN \"finish_reason\" frfr ON meta.finish_reason_id = frfr.finish_reason_id\n LEFT JOIN \"editor_name\" edit ON meta.editor_name_id = edit.editor_name_id\n LEFT JOIN \"api_kind\" ak ON meta.api_kind_id = ak.api_kind_id\n LEFT JOIN \"feature\" feat ON meta.feature_id = feat.feature_id\n LEFT JOIN \"mode\" md ON meta.mode_id = md.mode_id\n LEFT JOIN \"auto_model\" am ON meta.auto_model_id = am.auto_model_id\n", + "name": "microdollar_usage_view", + "schema": "public", + "isExisting": false, + "materialized": false + } + }, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/packages/db/src/migrations/meta/_journal.json b/packages/db/src/migrations/meta/_journal.json index 9ce86e3b91..9009e676e1 100644 --- a/packages/db/src/migrations/meta/_journal.json +++ b/packages/db/src/migrations/meta/_journal.json @@ -988,6 +988,13 @@ "when": 1779362869833, "tag": "0140_awesome_sphinx", "breakpoints": true + }, + { + "idx": 141, + "version": "7", + "when": 1779472818032, + "tag": "0141_chunky_captain_america", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/db/src/schema-types.ts b/packages/db/src/schema-types.ts index 271ce40ac0..2130401258 100644 --- a/packages/db/src/schema-types.ts +++ b/packages/db/src/schema-types.ts @@ -1201,3 +1201,111 @@ export const CODE_REVIEW_BENIGN_TERMINAL_REASONS = [ ] as const satisfies readonly CodeReviewTerminalReason[]; export type CodeReviewBenignTerminalReason = (typeof CODE_REVIEW_BENIGN_TERMINAL_REASONS)[number]; + +// --- Review Memory --- + +export const REVIEW_MEMORY_PLATFORMS = ['github', 'gitlab'] as const; +export type ReviewMemoryPlatform = (typeof REVIEW_MEMORY_PLATFORMS)[number]; + +export const REVIEW_MEMORY_SUBJECT_TYPES = [ + 'inline_comment', + 'summary_comment', + 'review', + 'discussion', +] as const; +export type ReviewMemorySubjectType = (typeof REVIEW_MEMORY_SUBJECT_TYPES)[number]; + +export const REVIEW_MEMORY_SUBJECT_STATES = [ + 'active', + 'outdated', + 'resolved', + 'dismissed', + 'unknown', +] as const; +export type ReviewMemorySubjectState = (typeof REVIEW_MEMORY_SUBJECT_STATES)[number]; + +export const REVIEW_MEMORY_FEEDBACK_EVENT_SOURCES = [ + 'github_webhook', + 'gitlab_webhook', + 'auto_fix', + 'backfill', + 'sync', +] as const; +export type ReviewMemoryFeedbackEventSource = (typeof REVIEW_MEMORY_FEEDBACK_EVENT_SOURCES)[number]; + +export const REVIEW_MEMORY_SIGNAL_KINDS = [ + 'positive_reaction', + 'negative_reaction', + 'corrective_reply', + 'supportive_reply', + 'review_dismissed', + 'thread_resolved', + 'thread_unresolved', + 'autofix_requested', + 'autofix_completed', + 'autofix_failed', + 'comment_outdated', + 'mr_approved', + 'mr_unapproved', + 'pr_approved', + 'pr_changes_requested', +] as const; +export type ReviewMemorySignalKind = (typeof REVIEW_MEMORY_SIGNAL_KINDS)[number]; + +export const REVIEW_MEMORY_SENTIMENTS = ['positive', 'negative', 'neutral'] as const; +export type ReviewMemorySentiment = (typeof REVIEW_MEMORY_SENTIMENTS)[number]; + +export const REVIEW_MEMORY_EVENT_AGGREGATION_STATES = ['fresh', 'included', 'ignored'] as const; +export type ReviewMemoryEventAggregationState = + (typeof REVIEW_MEMORY_EVENT_AGGREGATION_STATES)[number]; + +export const REVIEW_MEMORY_AGGREGATION_SCOPE_STATUSES = [ + 'idle', + 'eligible', + 'running', + 'failed', +] as const; +export type ReviewMemoryAggregationScopeStatus = + (typeof REVIEW_MEMORY_AGGREGATION_SCOPE_STATUSES)[number]; + +export const REVIEW_MEMORY_AGGREGATION_RUN_TRIGGERS = ['cron', 'manual', 'backfill'] as const; +export type ReviewMemoryAggregationRunTrigger = + (typeof REVIEW_MEMORY_AGGREGATION_RUN_TRIGGERS)[number]; + +export const REVIEW_MEMORY_AGGREGATION_RUN_STATUSES = [ + 'running', + 'completed', + 'failed', + 'skipped', +] as const; +export type ReviewMemoryAggregationRunStatus = + (typeof REVIEW_MEMORY_AGGREGATION_RUN_STATUSES)[number]; + +export const REVIEW_MEMORY_PROPOSAL_SCOPE_KINDS = [ + 'repository', + 'path_glob', + 'file', + 'language', +] as const; +export type ReviewMemoryProposalScopeKind = (typeof REVIEW_MEMORY_PROPOSAL_SCOPE_KINDS)[number]; + +export const REVIEW_MEMORY_PROPOSAL_TYPES = ['suppress', 'clarify', 'narrow', 'reinforce'] as const; +export type ReviewMemoryProposalType = (typeof REVIEW_MEMORY_PROPOSAL_TYPES)[number]; + +export const REVIEW_MEMORY_PROPOSAL_STATUSES = [ + 'open', + 'edited', + 'approved', + 'rejected', + 'opening_change_request', + 'change_request_opened', + 'change_request_failed', + 'superseded', +] as const; +export type ReviewMemoryProposalStatus = (typeof REVIEW_MEMORY_PROPOSAL_STATUSES)[number]; + +export const REVIEW_MEMORY_CHANGE_REQUEST_TYPES = ['github_pr', 'gitlab_mr'] as const; +export type ReviewMemoryChangeRequestType = (typeof REVIEW_MEMORY_CHANGE_REQUEST_TYPES)[number]; + +export const REVIEW_MEMORY_EVIDENCE_ROLES = ['primary', 'supporting', 'contradictory'] as const; +export type ReviewMemoryEvidenceRole = (typeof REVIEW_MEMORY_EVIDENCE_ROLES)[number]; diff --git a/packages/db/src/schema.ts b/packages/db/src/schema.ts index bd76585b40..6d06966d2b 100644 --- a/packages/db/src/schema.ts +++ b/packages/db/src/schema.ts @@ -97,6 +97,21 @@ import type { StoredModel, GatewayApiKind, ContributorChampionTier, + ReviewMemoryPlatform, + ReviewMemorySubjectType, + ReviewMemorySubjectState, + ReviewMemoryFeedbackEventSource, + ReviewMemorySignalKind, + ReviewMemorySentiment, + ReviewMemoryEventAggregationState, + ReviewMemoryAggregationScopeStatus, + ReviewMemoryAggregationRunTrigger, + ReviewMemoryAggregationRunStatus, + ReviewMemoryProposalScopeKind, + ReviewMemoryProposalType, + ReviewMemoryProposalStatus, + ReviewMemoryChangeRequestType, + ReviewMemoryEvidenceRole, } from './schema-types'; import type { AnyPgColumn as DrizzleAnyPgColumn } from 'drizzle-orm/pg-core'; import { INSTANCE_TYPE_VALUES } from '@kilocode/kiloclaw-instance-tiers'; @@ -3363,6 +3378,330 @@ export const cloud_agent_code_review_attempts = pgTable( export type CloudAgentCodeReviewAttempt = typeof cloud_agent_code_review_attempts.$inferSelect; +export const code_review_feedback_subjects = pgTable( + 'code_review_feedback_subjects', + { + id: idPrimaryKeyColumn, + owned_by_organization_id: uuid().references(() => organizations.id, { onDelete: 'cascade' }), + owned_by_user_id: text().references(() => kilocode_users.id, { onDelete: 'cascade' }), + platform: text().$type().notNull(), + platform_integration_id: uuid().references(() => platform_integrations.id, { + onDelete: 'set null', + }), + code_review_id: uuid().references(() => cloud_agent_code_reviews.id, { onDelete: 'set null' }), + subject_type: text().$type().notNull(), + external_id: text().notNull(), + external_thread_id: text(), + external_url: text(), + repo_full_name: text().notNull(), + platform_project_id: integer(), + pr_number: integer(), + pr_url: text(), + head_sha: text(), + file_path: text(), + line_number: integer(), + diff_hunk: text(), + body_excerpt: text(), + severity: text(), + finding_title: text(), + finding_fingerprint: text(), + state: text().$type().notNull().default('unknown'), + first_seen_at: timestamp({ withTimezone: true, mode: 'string' }).defaultNow().notNull(), + last_seen_at: timestamp({ withTimezone: true, mode: 'string' }).defaultNow().notNull(), + created_at: timestamp({ withTimezone: true, mode: 'string' }).defaultNow().notNull(), + updated_at: timestamp({ withTimezone: true, mode: 'string' }) + .defaultNow() + .notNull() + .$onUpdateFn(() => sql`now()`), + }, + table => [ + uniqueIndex('UQ_code_review_feedback_subjects_platform_external').on( + table.platform, + table.repo_full_name, + table.subject_type, + table.external_id + ), + index('idx_code_review_feedback_subjects_owned_by_org_id').on(table.owned_by_organization_id), + index('idx_code_review_feedback_subjects_owned_by_user_id').on(table.owned_by_user_id), + index('idx_code_review_feedback_subjects_platform_repo').on( + table.platform, + table.repo_full_name + ), + index('idx_code_review_feedback_subjects_pr').on(table.repo_full_name, table.pr_number), + index('idx_code_review_feedback_subjects_code_review_id').on(table.code_review_id), + index('idx_code_review_feedback_subjects_fingerprint').on(table.finding_fingerprint), + index('idx_code_review_feedback_subjects_state').on(table.state), + index('idx_code_review_feedback_subjects_thread').on(table.external_thread_id), + index('idx_code_review_feedback_subjects_last_seen_at').on(table.last_seen_at), + check( + 'code_review_feedback_subjects_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) + )` + ), + ] +); + +export type CodeReviewFeedbackSubject = typeof code_review_feedback_subjects.$inferSelect; + +export const code_review_feedback_events = pgTable( + 'code_review_feedback_events', + { + id: idPrimaryKeyColumn, + owned_by_organization_id: uuid().references(() => organizations.id, { onDelete: 'cascade' }), + owned_by_user_id: text().references(() => kilocode_users.id, { onDelete: 'cascade' }), + platform: text().$type().notNull(), + platform_integration_id: uuid().references(() => platform_integrations.id, { + onDelete: 'set null', + }), + subject_id: uuid().references(() => code_review_feedback_subjects.id, { onDelete: 'set null' }), + code_review_id: uuid().references(() => cloud_agent_code_reviews.id, { onDelete: 'set null' }), + repo_full_name: text().notNull(), + platform_project_id: integer(), + pr_number: integer(), + pr_url: text(), + event_source: text().$type().notNull(), + signal_kind: text().$type().notNull(), + sentiment: text().$type().notNull(), + strength: smallint().notNull().default(1), + external_event_id: text(), + dedupe_hash: text().notNull(), + external_url: text(), + evidence_excerpt: text(), + metadata: jsonb().$type>().notNull().default({}), + aggregation_state: text().$type().notNull().default('fresh'), + occurred_at: timestamp({ withTimezone: true, mode: 'string' }).defaultNow().notNull(), + created_at: timestamp({ withTimezone: true, mode: 'string' }).defaultNow().notNull(), + }, + table => [ + uniqueIndex('UQ_code_review_feedback_events_external_event_id') + .on(table.external_event_id) + .where(isNotNull(table.external_event_id)), + uniqueIndex('UQ_code_review_feedback_events_dedupe_hash').on(table.dedupe_hash), + index('idx_code_review_feedback_events_owned_by_org_id').on(table.owned_by_organization_id), + index('idx_code_review_feedback_events_owned_by_user_id').on(table.owned_by_user_id), + index('idx_code_review_feedback_events_platform_repo').on(table.platform, table.repo_full_name), + index('idx_code_review_feedback_events_subject_id').on(table.subject_id), + index('idx_code_review_feedback_events_code_review_id').on(table.code_review_id), + index('idx_code_review_feedback_events_sentiment').on(table.sentiment), + index('idx_code_review_feedback_events_signal_kind').on(table.signal_kind), + index('idx_code_review_feedback_events_aggregation_state').on(table.aggregation_state), + index('idx_code_review_feedback_events_created_at').on(table.created_at), + check( + 'code_review_feedback_events_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('code_review_feedback_events_strength_check', sql`${table.strength} > 0`), + ] +); + +export type CodeReviewFeedbackEvent = typeof code_review_feedback_events.$inferSelect; + +export const code_review_memory_aggregation_state = pgTable( + 'code_review_memory_aggregation_state', + { + id: idPrimaryKeyColumn, + owned_by_organization_id: uuid().references(() => organizations.id, { onDelete: 'cascade' }), + owned_by_user_id: text().references(() => kilocode_users.id, { onDelete: 'cascade' }), + platform: text().$type().notNull(), + repo_full_name: text().notNull(), + platform_project_id: integer(), + last_successful_run_at: timestamp({ withTimezone: true, mode: 'string' }), + last_attempted_run_at: timestamp({ withTimezone: true, mode: 'string' }), + last_included_event_created_at: timestamp({ withTimezone: true, mode: 'string' }), + fresh_event_count: integer().notNull().default(0), + fresh_weight: integer().notNull().default(0), + fresh_distinct_subject_count: integer().notNull().default(0), + fresh_distinct_pr_count: integer().notNull().default(0), + next_eligible_at: timestamp({ withTimezone: true, mode: 'string' }).defaultNow().notNull(), + status: text().$type().notNull().default('idle'), + claimed_at: timestamp({ withTimezone: true, mode: 'string' }), + claim_token: text(), + last_model_slug: text(), + last_error_message: 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_code_review_memory_aggregation_state_org_scope') + .on(table.owned_by_organization_id, table.platform, table.repo_full_name) + .where(isNotNull(table.owned_by_organization_id)), + uniqueIndex('UQ_code_review_memory_aggregation_state_user_scope') + .on(table.owned_by_user_id, table.platform, table.repo_full_name) + .where(isNotNull(table.owned_by_user_id)), + index('idx_code_review_memory_aggregation_state_status').on(table.status), + index('idx_code_review_memory_aggregation_state_next_eligible_at').on(table.next_eligible_at), + index('idx_code_review_memory_aggregation_state_fresh_counts').on( + table.fresh_event_count, + table.fresh_weight + ), + check( + 'code_review_memory_aggregation_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) + )` + ), + ] +); + +export type CodeReviewMemoryAggregationState = + typeof code_review_memory_aggregation_state.$inferSelect; + +export const code_review_memory_aggregation_runs = pgTable( + 'code_review_memory_aggregation_runs', + { + id: idPrimaryKeyColumn, + owned_by_organization_id: uuid().references(() => organizations.id, { onDelete: 'cascade' }), + owned_by_user_id: text().references(() => kilocode_users.id, { onDelete: 'cascade' }), + platform: text().$type().notNull(), + repo_full_name: text().notNull(), + platform_project_id: integer(), + model_slug: text().notNull(), + trigger: text().$type().notNull(), + input_event_count: integer().notNull().default(0), + input_subject_count: integer().notNull().default(0), + input_cluster_count: integer().notNull().default(0), + fresh_event_cutoff_at: timestamp({ withTimezone: true, mode: 'string' }), + status: text().$type().notNull().default('running'), + skip_reason: text(), + tokens_in: integer(), + tokens_out: integer(), + total_cost_musd: integer(), + error_message: text(), + started_at: timestamp({ withTimezone: true, mode: 'string' }).defaultNow().notNull(), + completed_at: timestamp({ withTimezone: true, mode: 'string' }), + created_at: timestamp({ withTimezone: true, mode: 'string' }).defaultNow().notNull(), + }, + table => [ + index('idx_code_review_memory_aggregation_runs_owned_by_org_id').on( + table.owned_by_organization_id + ), + index('idx_code_review_memory_aggregation_runs_owned_by_user_id').on(table.owned_by_user_id), + index('idx_code_review_memory_aggregation_runs_scope').on(table.platform, table.repo_full_name), + index('idx_code_review_memory_aggregation_runs_status').on(table.status), + index('idx_code_review_memory_aggregation_runs_created_at').on(table.created_at), + check( + 'code_review_memory_aggregation_runs_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) + )` + ), + ] +); + +export type CodeReviewMemoryAggregationRun = + typeof code_review_memory_aggregation_runs.$inferSelect; + +export const code_review_memory_proposals = pgTable( + 'code_review_memory_proposals', + { + id: idPrimaryKeyColumn, + owned_by_organization_id: uuid().references(() => organizations.id, { onDelete: 'cascade' }), + owned_by_user_id: text().references(() => kilocode_users.id, { onDelete: 'cascade' }), + platform: text().$type().notNull(), + platform_integration_id: uuid().references(() => platform_integrations.id, { + onDelete: 'set null', + }), + repo_full_name: text().notNull(), + platform_project_id: integer(), + aggregation_run_id: uuid().references(() => code_review_memory_aggregation_runs.id, { + onDelete: 'set null', + }), + target_file_path: text().notNull().default('REVIEW.md'), + scope_kind: text().$type().notNull().default('repository'), + scope_value: text(), + proposal_type: text().$type().notNull(), + status: text().$type().notNull().default('open'), + title: text().notNull(), + rationale: text().notNull(), + proposed_markdown: text().notNull(), + dedupe_key: text().notNull(), + llm_confidence: real(), + positive_count: integer().notNull().default(0), + negative_count: integer().notNull().default(0), + neutral_count: integer().notNull().default(0), + distinct_pr_count: integer().notNull().default(0), + distinct_subject_count: integer().notNull().default(0), + contradictory_count: integer().notNull().default(0), + edited_by_user_id: text().references(() => kilocode_users.id, { onDelete: 'set null' }), + approved_by_user_id: text().references(() => kilocode_users.id, { onDelete: 'set null' }), + rejected_by_user_id: text().references(() => kilocode_users.id, { onDelete: 'set null' }), + approved_at: timestamp({ withTimezone: true, mode: 'string' }), + rejected_at: timestamp({ withTimezone: true, mode: 'string' }), + change_request_type: text().$type(), + branch_name: text(), + change_request_number: integer(), + change_request_url: text(), + change_request_error_message: text(), + created_at: timestamp({ withTimezone: true, mode: 'string' }).defaultNow().notNull(), + updated_at: timestamp({ withTimezone: true, mode: 'string' }) + .defaultNow() + .notNull() + .$onUpdateFn(() => sql`now()`), + }, + table => [ + index('idx_code_review_memory_proposals_owned_by_org_id').on(table.owned_by_organization_id), + index('idx_code_review_memory_proposals_owned_by_user_id').on(table.owned_by_user_id), + index('idx_code_review_memory_proposals_platform_repo_status').on( + table.platform, + table.repo_full_name, + table.status + ), + index('idx_code_review_memory_proposals_proposal_type').on(table.proposal_type), + index('idx_code_review_memory_proposals_created_at').on(table.created_at), + uniqueIndex('UQ_code_review_memory_proposals_org_active_dedupe') + .on(table.owned_by_organization_id, table.platform, table.repo_full_name, table.dedupe_key) + .where( + sql`${table.owned_by_organization_id} IS NOT NULL AND ${table.status} IN ('open', 'edited', 'approved', 'opening_change_request', 'change_request_opened')` + ), + uniqueIndex('UQ_code_review_memory_proposals_user_active_dedupe') + .on(table.owned_by_user_id, table.platform, table.repo_full_name, table.dedupe_key) + .where( + sql`${table.owned_by_user_id} IS NOT NULL AND ${table.status} IN ('open', 'edited', 'approved', 'opening_change_request', 'change_request_opened')` + ), + check( + 'code_review_memory_proposals_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) + )` + ), + ] +); + +export type CodeReviewMemoryProposal = typeof code_review_memory_proposals.$inferSelect; + +export const code_review_memory_proposal_evidence = pgTable( + 'code_review_memory_proposal_evidence', + { + proposal_id: uuid() + .notNull() + .references(() => code_review_memory_proposals.id, { onDelete: 'cascade' }), + feedback_event_id: uuid() + .notNull() + .references(() => code_review_feedback_events.id, { onDelete: 'cascade' }), + evidence_role: text().$type().notNull().default('supporting'), + created_at: timestamp({ withTimezone: true, mode: 'string' }).defaultNow().notNull(), + }, + table => [ + primaryKey({ columns: [table.proposal_id, table.feedback_event_id] }), + index('idx_code_review_memory_proposal_evidence_feedback_event_id').on(table.feedback_event_id), + index('idx_code_review_memory_proposal_evidence_role').on(table.evidence_role), + ] +); + +export type CodeReviewMemoryProposalEvidence = + typeof code_review_memory_proposal_evidence.$inferSelect; + export const cliSessions = pgTable( 'cli_sessions', { From 7c514826fe4a359c59b2a44e5996de109a4e4a27 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Fri, 22 May 2026 21:15:41 +0300 Subject: [PATCH 02/23] feat(review-memory): sync review subjects --- .../[reviewId]/route.test.ts | 28 ++ .../code-review-status/[reviewId]/route.ts | 38 +++ .../review-memory/sync-subjects.test.ts | 168 ++++++++++ .../review-memory/sync-subjects.ts | 310 ++++++++++++++++++ .../integrations/platforms/github/adapter.ts | 6 + 5 files changed, 550 insertions(+) create mode 100644 apps/web/src/lib/code-reviews/review-memory/sync-subjects.test.ts create mode 100644 apps/web/src/lib/code-reviews/review-memory/sync-subjects.ts diff --git a/apps/web/src/app/api/internal/code-review-status/[reviewId]/route.test.ts b/apps/web/src/app/api/internal/code-review-status/[reviewId]/route.test.ts index 8f1dcc7535..8b8f2a4bc7 100644 --- a/apps/web/src/app/api/internal/code-review-status/[reviewId]/route.test.ts +++ b/apps/web/src/app/api/internal/code-review-status/[reviewId]/route.test.ts @@ -71,6 +71,10 @@ const mockCaptureMessage = jest.fn(); const mockAppendReviewSummaryFooter = jest.fn(); // eslint-disable-next-line @typescript-eslint/no-explicit-any const mockRetryReviewFresh = jest.fn(); +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const mockSyncGitHubReviewMemorySubjects = jest.fn(); +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const mockSyncGitLabReviewMemorySubjects = jest.fn(); // --- Module mocks --- @@ -138,6 +142,13 @@ jest.mock('@/lib/code-reviews/summary/usage-footer', () => ({ appendReviewSummaryFooter: (...args: unknown[]) => mockAppendReviewSummaryFooter(...args), })); +jest.mock('@/lib/code-reviews/review-memory/sync-subjects', () => ({ + syncGitHubReviewMemorySubjects: (...args: unknown[]) => + mockSyncGitHubReviewMemorySubjects(...args), + syncGitLabReviewMemorySubjects: (...args: unknown[]) => + mockSyncGitLabReviewMemorySubjects(...args), +})); + jest.mock('@/lib/constants', () => ({ APP_URL: 'https://test.kilo.ai', })); @@ -326,6 +337,8 @@ beforeEach(async () => { mockGetSessionUsageFromBilling.mockResolvedValue(null); mockUpdateCodeReviewUsage.mockResolvedValue(undefined); mockAppendReviewSummaryFooter.mockReturnValue('body with footer'); + mockSyncGitHubReviewMemorySubjects.mockResolvedValue({ summarySynced: true, inlineSynced: 0 }); + mockSyncGitLabReviewMemorySubjects.mockResolvedValue({ summarySynced: true, inlineSynced: 0 }); ({ POST } = await import('./route')); }); @@ -1263,6 +1276,11 @@ describe('POST /api/internal/code-review-status/[reviewId]', () => { 'body with footer', 'standard' ); + expect(mockSyncGitHubReviewMemorySubjects).toHaveBeenCalledWith({ + review, + installationId: 'inst-1', + appType: 'standard', + }); }); it('updates completed GitLab summary with REVIEW.md guidance metadata when used', async () => { @@ -1299,6 +1317,11 @@ describe('POST /api/internal/code-review-status/[reviewId]', () => { 'body with footer', 'https://gitlab.com' ); + expect(mockSyncGitLabReviewMemorySubjects).toHaveBeenCalledWith({ + review, + accessToken: 'mock-token', + instanceUrl: 'https://gitlab.com', + }); }); it('updates guidance footer when usage data is unavailable', async () => { @@ -1336,6 +1359,11 @@ describe('POST /api/internal/code-review-status/[reviewId]', () => { expect(mockAppendReviewSummaryFooter).not.toHaveBeenCalled(); expect(mockUpdateKiloReviewComment).not.toHaveBeenCalled(); + expect(mockSyncGitHubReviewMemorySubjects).toHaveBeenCalledWith({ + review, + installationId: 'inst-1', + appType: 'standard', + }); }); }); }); diff --git a/apps/web/src/app/api/internal/code-review-status/[reviewId]/route.ts b/apps/web/src/app/api/internal/code-review-status/[reviewId]/route.ts index d94ff7237f..8dd132040c 100644 --- a/apps/web/src/app/api/internal/code-review-status/[reviewId]/route.ts +++ b/apps/web/src/app/api/internal/code-review-status/[reviewId]/route.ts @@ -59,6 +59,10 @@ import { CALLBACK_TOKEN_SECRET } from '@/lib/config.server'; import { verifyCallbackToken } from '@kilocode/worker-utils/callback-token'; import { PLATFORM } from '@/lib/integrations/core/constants'; import { appendReviewSummaryFooter } from '@/lib/code-reviews/summary/usage-footer'; +import { + syncGitHubReviewMemorySubjects, + syncGitLabReviewMemorySubjects, +} from '@/lib/code-reviews/review-memory/sync-subjects'; import { APP_URL } from '@/lib/constants'; import type { CloudAgentCodeReview, PlatformIntegration } from '@kilocode/db/schema'; import type { GitHubAppType } from '@/lib/integrations/platforms/github/app-selector'; @@ -958,6 +962,23 @@ export async function POST( } ); } + + try { + await syncGitHubReviewMemorySubjects({ + review, + installationId: integration.platform_installation_id, + appType, + }); + } catch (subjectSyncError) { + logExceptInTest('[code-review-status] Failed to sync GitHub review subjects:', { + reviewId, + error: subjectSyncError, + }); + captureException(subjectSyncError, { + tags: { source: 'code-review-status-review-memory-sync' }, + extra: { reviewId, platform }, + }); + } } } else if (platform === PLATFORM.GITLAB) { const instanceUrl = getGitLabInstanceUrl(integration); @@ -1048,6 +1069,23 @@ export async function POST( } ); } + + try { + await syncGitLabReviewMemorySubjects({ + review, + accessToken, + instanceUrl, + }); + } catch (subjectSyncError) { + logExceptInTest('[code-review-status] Failed to sync GitLab review subjects:', { + reviewId, + error: subjectSyncError, + }); + captureException(subjectSyncError, { + tags: { source: 'code-review-status-review-memory-sync' }, + extra: { reviewId, platform }, + }); + } } } } catch (postCompletionError) { diff --git a/apps/web/src/lib/code-reviews/review-memory/sync-subjects.test.ts b/apps/web/src/lib/code-reviews/review-memory/sync-subjects.test.ts new file mode 100644 index 0000000000..d425432326 --- /dev/null +++ b/apps/web/src/lib/code-reviews/review-memory/sync-subjects.test.ts @@ -0,0 +1,168 @@ +/* eslint-disable drizzle/enforce-delete-with-where */ +import { db } from '@/lib/drizzle'; +import { createCodeReview, getCodeReviewById } from '@/lib/code-reviews/db/code-reviews'; +import { insertTestUser } from '@/tests/helpers/user.helper'; +import { + cloud_agent_code_reviews, + code_review_feedback_subjects, + kilocode_users, +} from '@kilocode/db/schema'; +import { eq } from 'drizzle-orm'; +import { parseReviewFindingMetadata, syncFetchedReviewMemorySubjects } from './sync-subjects'; + +describe('review memory subject sync', () => { + afterEach(async () => { + await db.delete(code_review_feedback_subjects); + await db.delete(cloud_agent_code_reviews); + await db.delete(kilocode_users); + }); + + it('syncs GitHub summary and likely Kilo inline subjects', async () => { + const user = await insertTestUser(); + const reviewId = await createCodeReview({ + owner: { type: 'user', id: user.id, userId: user.id }, + repoFullName: 'owner/repo', + prNumber: 42, + prUrl: 'https://github.com/owner/repo/pull/42', + prTitle: 'Review memory', + prAuthor: 'octocat', + baseRef: 'main', + headRef: 'feature/review-memory', + headSha: 'abc1234', + platform: 'github', + }); + const review = await getCodeReviewById(reviewId); + if (!review) throw new Error('Expected review to be created'); + + const result = await syncFetchedReviewMemorySubjects({ + review, + platform: 'github', + summary: { + externalId: '1001', + body: '\n## Code Review Summary\n\n**Status:** 1 Issues Found', + externalUrl: 'https://github.com/owner/repo/pull/42#issuecomment-1001', + }, + inlineComments: [ + { + externalId: '2001', + externalUrl: 'https://github.com/owner/repo/pull/42#discussion_r2001', + filePath: 'src/example.ts', + lineNumber: 12, + diffHunk: '@@ -10,3 +10,3 @@', + body: '**WARNING:** Missing error handling for failed requests.', + isOutdated: false, + }, + { + externalId: 'human-comment', + body: 'Can we rename this variable?', + isOutdated: false, + }, + { + externalId: '2002', + filePath: 'src/other.ts', + lineNumber: 20, + body: '**CRITICAL:** Null pointer when the payload is empty.', + isOutdated: true, + }, + ], + }); + + expect(result).toEqual({ summarySynced: true, inlineSynced: 2 }); + + const subjects = await db + .select() + .from(code_review_feedback_subjects) + .where(eq(code_review_feedback_subjects.repo_full_name, 'owner/repo')); + expect(subjects).toHaveLength(3); + expect(subjects.find(subject => subject.external_id === '1001')).toEqual( + expect.objectContaining({ subject_type: 'summary_comment', state: 'active' }) + ); + expect(subjects.find(subject => subject.external_id === '2001')).toEqual( + expect.objectContaining({ + subject_type: 'inline_comment', + file_path: 'src/example.ts', + line_number: 12, + severity: 'warning', + state: 'active', + }) + ); + expect(subjects.find(subject => subject.external_id === '2002')).toEqual( + expect.objectContaining({ severity: 'critical', state: 'outdated' }) + ); + expect(subjects.find(subject => subject.external_id === 'human-comment')).toBeUndefined(); + }); + + it('updates existing GitLab discussion subjects when resolved state changes', async () => { + const user = await insertTestUser(); + const reviewId = await createCodeReview({ + owner: { type: 'user', id: user.id, userId: user.id }, + repoFullName: 'group/project', + prNumber: 7, + prUrl: 'https://gitlab.com/group/project/-/merge_requests/7', + prTitle: 'Review memory', + prAuthor: 'octocat', + baseRef: 'main', + headRef: 'feature/review-memory', + headSha: 'def5678', + platform: 'gitlab', + platformProjectId: 123, + }); + const review = await getCodeReviewById(reviewId); + if (!review) throw new Error('Expected review to be created'); + + await syncFetchedReviewMemorySubjects({ + review, + platform: 'gitlab', + summary: null, + inlineComments: [ + { + externalId: '3001', + externalThreadId: 'discussion-1', + filePath: 'src/gitlab.ts', + lineNumber: 5, + body: '**SUGGESTION:** Prefer a narrower guard here.', + isOutdated: false, + }, + ], + }); + await syncFetchedReviewMemorySubjects({ + review, + platform: 'gitlab', + summary: null, + inlineComments: [ + { + externalId: '3001', + externalThreadId: 'discussion-1', + filePath: 'src/gitlab.ts', + lineNumber: 5, + body: '**SUGGESTION:** Prefer a narrower validation guard here.', + isOutdated: true, + }, + ], + }); + + const subjects = await db.select().from(code_review_feedback_subjects); + expect(subjects).toHaveLength(1); + expect(subjects[0]).toEqual( + expect.objectContaining({ + subject_type: 'discussion', + external_thread_id: 'discussion-1', + body_excerpt: '**SUGGESTION:** Prefer a narrower validation guard here.', + state: 'outdated', + }) + ); + }); + + it('parses severity, title, and stable fingerprints from review comment bodies', () => { + const first = parseReviewFindingMetadata( + '**WARNING:** Missing guard in `src/file.ts` at line 42 for commit abc1234' + ); + const second = parseReviewFindingMetadata( + '**WARNING:** Missing guard in `src/other.ts` at line 98 for commit def5678' + ); + + expect(first.severity).toBe('warning'); + expect(first.findingTitle).toContain('Missing guard'); + expect(first.findingFingerprint).toBe(second.findingFingerprint); + }); +}); diff --git a/apps/web/src/lib/code-reviews/review-memory/sync-subjects.ts b/apps/web/src/lib/code-reviews/review-memory/sync-subjects.ts new file mode 100644 index 0000000000..05edcf38c3 --- /dev/null +++ b/apps/web/src/lib/code-reviews/review-memory/sync-subjects.ts @@ -0,0 +1,310 @@ +import type { CloudAgentCodeReview } from '@kilocode/db/schema'; +import type { GitHubAppType } from '@/lib/integrations/platforms/github/app-selector'; +import { + fetchPRInlineComments, + findKiloReviewComment, +} from '@/lib/integrations/platforms/github/adapter'; +import { + fetchMRInlineComments, + findKiloReviewNote, +} from '@/lib/integrations/platforms/gitlab/adapter'; +import { logExceptInTest } from '@/lib/utils.server'; +import { + createReviewMemoryDedupeHash, + type ReviewMemoryDatabase, + type ReviewMemoryOwner, + upsertFeedbackSubject, +} from './db'; + +type ReviewSubjectSyncReview = Pick< + CloudAgentCodeReview, + | 'id' + | 'owned_by_organization_id' + | 'owned_by_user_id' + | 'platform_integration_id' + | 'repo_full_name' + | 'platform_project_id' + | 'pr_number' + | 'pr_url' + | 'head_sha' +>; + +export type FetchedReviewSummarySubject = { + externalId: string; + body: string; + externalUrl?: string | null; +}; + +export type FetchedInlineReviewSubject = { + externalId: string; + externalThreadId?: string | null; + externalUrl?: string | null; + filePath?: string | null; + lineNumber?: number | null; + diffHunk?: string | null; + body: string; + isOutdated?: boolean; +}; + +export type SyncFetchedReviewMemorySubjectsInput = { + review: ReviewSubjectSyncReview; + platform: 'github' | 'gitlab'; + summary?: FetchedReviewSummarySubject | null; + inlineComments: FetchedInlineReviewSubject[]; + database?: ReviewMemoryDatabase; +}; + +export type SyncReviewMemorySubjectsResult = { + summarySynced: boolean; + inlineSynced: number; +}; + +const KILO_REVIEW_MARKER = ''; + +function ownerFromReview(review: ReviewSubjectSyncReview): ReviewMemoryOwner | null { + if (review.owned_by_organization_id) { + return { type: 'org', id: review.owned_by_organization_id }; + } + if (review.owned_by_user_id) { + return { type: 'user', id: review.owned_by_user_id }; + } + return null; +} + +function normalizeFingerprintText(value: string): string { + return value + .toLowerCase() + .replace(/`[^`]+`/g, '`code`') + .replace(/[a-f0-9]{7,40}/g, '') + .replace(/\d+/g, '') + .replace(/\s+/g, ' ') + .trim(); +} + +export function parseReviewFindingMetadata(body: string): { + severity: string | null; + findingTitle: string | null; + findingFingerprint: string | null; +} { + const firstMeaningfulLine = body + .split('\n') + .map(line => line.trim()) + .find(line => line.length > 0); + if (!firstMeaningfulLine) { + return { severity: null, findingTitle: null, findingFingerprint: null }; + } + + const severityMatch = firstMeaningfulLine.match( + /^\*\*(CRITICAL|WARNING|SUGGESTION|NITPICK|ERROR|INFO)\s*:?\*\*:?\s*(.*)$/i + ); + const fallbackSeverityMatch = firstMeaningfulLine.match( + /^(CRITICAL|WARNING|SUGGESTION|NITPICK|ERROR|INFO):\s*(.*)$/i + ); + const matchedSeverity = severityMatch?.[1] ?? fallbackSeverityMatch?.[1] ?? null; + const remainingTitle = severityMatch?.[2] ?? fallbackSeverityMatch?.[2] ?? firstMeaningfulLine; + const findingTitle = remainingTitle.replace(/^[-:]+\s*/, '').trim() || firstMeaningfulLine; + const fingerprint = createReviewMemoryDedupeHash([ + matchedSeverity?.toLowerCase() ?? 'unknown', + normalizeFingerprintText(findingTitle), + ]); + + return { + severity: matchedSeverity?.toLowerCase() ?? null, + findingTitle, + findingFingerprint: fingerprint, + }; +} + +function isLikelyKiloInlineReviewBody(body: string): boolean { + if (body.includes(KILO_REVIEW_MARKER)) return true; + const { severity } = parseReviewFindingMetadata(body); + if (severity) return true; + return /```suggestion/.test(body) && /\b(CRITICAL|WARNING|SUGGESTION|NITPICK)\b/i.test(body); +} + +function githubSummaryUrl(review: ReviewSubjectSyncReview, commentId: string): string { + return `${review.pr_url}#issuecomment-${commentId}`; +} + +function githubInlineUrl(review: ReviewSubjectSyncReview, commentId: string): string { + return `${review.pr_url}#discussion_r${commentId}`; +} + +function gitlabNoteUrl(review: ReviewSubjectSyncReview, noteId: string): string { + return `${review.pr_url}#note_${noteId}`; +} + +export async function syncFetchedReviewMemorySubjects( + input: SyncFetchedReviewMemorySubjectsInput +): Promise { + const owner = ownerFromReview(input.review); + if (!owner) return { summarySynced: false, inlineSynced: 0 }; + + let summarySynced = false; + if (input.summary?.body.includes(KILO_REVIEW_MARKER)) { + await upsertFeedbackSubject({ + owner, + platform: input.platform, + platformIntegrationId: input.review.platform_integration_id, + codeReviewId: input.review.id, + subjectType: 'summary_comment', + externalId: input.summary.externalId, + externalUrl: input.summary.externalUrl ?? null, + repoFullName: input.review.repo_full_name, + platformProjectId: input.review.platform_project_id, + prNumber: input.review.pr_number, + prUrl: input.review.pr_url, + headSha: input.review.head_sha, + bodyExcerpt: input.summary.body, + state: 'active', + database: input.database, + }); + summarySynced = true; + } + + let inlineSynced = 0; + for (const comment of input.inlineComments) { + if (!isLikelyKiloInlineReviewBody(comment.body)) continue; + const metadata = parseReviewFindingMetadata(comment.body); + await upsertFeedbackSubject({ + owner, + platform: input.platform, + platformIntegrationId: input.review.platform_integration_id, + codeReviewId: input.review.id, + subjectType: input.platform === 'gitlab' ? 'discussion' : 'inline_comment', + externalId: comment.externalId, + externalThreadId: comment.externalThreadId ?? null, + externalUrl: comment.externalUrl ?? null, + repoFullName: input.review.repo_full_name, + platformProjectId: input.review.platform_project_id, + prNumber: input.review.pr_number, + prUrl: input.review.pr_url, + headSha: input.review.head_sha, + filePath: comment.filePath ?? null, + lineNumber: comment.lineNumber ?? null, + diffHunk: comment.diffHunk ?? null, + bodyExcerpt: comment.body, + severity: metadata.severity, + findingTitle: metadata.findingTitle, + findingFingerprint: metadata.findingFingerprint, + state: comment.isOutdated ? 'outdated' : 'active', + database: input.database, + }); + inlineSynced += 1; + } + + return { summarySynced, inlineSynced }; +} + +export async function syncGitHubReviewMemorySubjects(input: { + review: ReviewSubjectSyncReview; + installationId: string; + appType: GitHubAppType; + database?: ReviewMemoryDatabase; +}): Promise { + const [repoOwner, repoName] = input.review.repo_full_name.split('/'); + if (!repoOwner || !repoName) { + return { summarySynced: false, inlineSynced: 0 }; + } + + const [summaryComment, inlineComments] = await Promise.all([ + findKiloReviewComment( + input.installationId, + repoOwner, + repoName, + input.review.pr_number, + input.appType + ), + fetchPRInlineComments( + input.installationId, + repoOwner, + repoName, + input.review.pr_number, + input.appType + ), + ]); + + const result = await syncFetchedReviewMemorySubjects({ + review: input.review, + platform: 'github', + summary: summaryComment + ? { + externalId: String(summaryComment.commentId), + body: summaryComment.body, + externalUrl: githubSummaryUrl(input.review, String(summaryComment.commentId)), + } + : null, + inlineComments: inlineComments.map(comment => ({ + externalId: String(comment.id), + externalUrl: comment.htmlUrl ?? githubInlineUrl(input.review, String(comment.id)), + filePath: comment.path, + lineNumber: comment.line, + diffHunk: comment.diffHunk, + body: comment.body, + isOutdated: comment.isOutdated, + })), + database: input.database, + }); + + logExceptInTest('[review-memory] Synced GitHub review subjects', { + reviewId: input.review.id, + repoFullName: input.review.repo_full_name, + prNumber: input.review.pr_number, + result, + }); + + return result; +} + +export async function syncGitLabReviewMemorySubjects(input: { + review: ReviewSubjectSyncReview; + accessToken: string; + instanceUrl: string; + database?: ReviewMemoryDatabase; +}): Promise { + const [summaryNote, inlineComments] = await Promise.all([ + findKiloReviewNote( + input.accessToken, + input.review.repo_full_name, + input.review.pr_number, + input.instanceUrl + ), + fetchMRInlineComments( + input.accessToken, + input.review.repo_full_name, + input.review.pr_number, + input.instanceUrl + ), + ]); + + const result = await syncFetchedReviewMemorySubjects({ + review: input.review, + platform: 'gitlab', + summary: summaryNote + ? { + externalId: String(summaryNote.noteId), + body: summaryNote.body, + externalUrl: gitlabNoteUrl(input.review, String(summaryNote.noteId)), + } + : null, + inlineComments: inlineComments.map(comment => ({ + externalId: String(comment.id), + externalThreadId: comment.discussionId, + externalUrl: gitlabNoteUrl(input.review, String(comment.id)), + filePath: comment.path, + lineNumber: comment.line, + body: comment.body, + isOutdated: comment.isOutdated, + })), + database: input.database, + }); + + logExceptInTest('[review-memory] Synced GitLab review subjects', { + reviewId: input.review.id, + repoFullName: input.review.repo_full_name, + prNumber: input.review.pr_number, + result, + }); + + return result; +} diff --git a/apps/web/src/lib/integrations/platforms/github/adapter.ts b/apps/web/src/lib/integrations/platforms/github/adapter.ts index 7a223cf093..16cf7d3d43 100644 --- a/apps/web/src/lib/integrations/platforms/github/adapter.ts +++ b/apps/web/src/lib/integrations/platforms/github/adapter.ts @@ -551,6 +551,8 @@ export async function fetchPRInlineComments( path: string; line: number | null; body: string; + htmlUrl: string | null; + diffHunk: string | null; isOutdated: boolean; user: { login: string }; }> @@ -563,6 +565,8 @@ export async function fetchPRInlineComments( path: string; line: number | null; body: string; + htmlUrl: string | null; + diffHunk: string | null; isOutdated: boolean; user: { login: string }; }> = []; @@ -585,6 +589,8 @@ export async function fetchPRInlineComments( path: c.path, line: c.line ?? null, body: c.body, + htmlUrl: c.html_url ?? null, + diffHunk: c.diff_hunk ?? null, isOutdated: c.position === null, // null position = outdated user: { login: c.user?.login ?? 'unknown' }, })) From 998208250c113b225ec870a43282856649f82fb4 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Fri, 22 May 2026 21:32:45 +0300 Subject: [PATCH 03/23] feat(review-memory): ingest GitHub feedback signals --- .../auto-fix/github/handle-comment-reply.ts | 40 +- .../src/lib/code-reviews/review-memory/db.ts | 5 +- .../review-memory/github-feedback.test.ts | 326 +++++++++++ .../review-memory/github-feedback.ts | 508 ++++++++++++++++++ .../review-memory/sync-subjects.ts | 2 +- .../src/lib/integrations/core/constants.ts | 3 + .../platforms/github/webhook-handler.test.ts | 199 ++++++- .../platforms/github/webhook-handler.ts | 246 +++++++-- .../platforms/github/webhook-schemas.ts | 71 +++ 9 files changed, 1342 insertions(+), 58 deletions(-) create mode 100644 apps/web/src/lib/code-reviews/review-memory/github-feedback.test.ts create mode 100644 apps/web/src/lib/code-reviews/review-memory/github-feedback.ts diff --git a/apps/web/src/lib/auto-fix/github/handle-comment-reply.ts b/apps/web/src/lib/auto-fix/github/handle-comment-reply.ts index a226d4f057..9d6b0c0d65 100644 --- a/apps/web/src/lib/auto-fix/github/handle-comment-reply.ts +++ b/apps/web/src/lib/auto-fix/github/handle-comment-reply.ts @@ -15,6 +15,7 @@ import { getPRHeadCommit, } from '@/lib/integrations/platforms/github/adapter'; import { getIntegrationById } from '@/lib/integrations/db/platform-integrations'; +import { recordGitHubAutoFixFeedback } from '@/lib/code-reviews/review-memory/github-feedback'; import { z } from 'zod'; export const CommentReplyPayloadSchema = z.object({ @@ -55,6 +56,28 @@ function buildSuccessReplyBody(params: { return `Implemented the requested fix around ${params.fixTarget} and pushed it to this PR branch.`; } +async function recordAutoFixFeedbackSafely(input: { + ticket: Awaited>; + outcome: 'success' | 'failed'; + errorMessage?: string; +}) { + if (!input.ticket) return; + + try { + await recordGitHubAutoFixFeedback({ + ticket: input.ticket, + outcome: input.outcome, + errorMessage: input.errorMessage, + }); + } catch (error) { + errorExceptInTest('[auto-fix-comment-reply] Failed to record review memory feedback:', error); + captureException(error, { + tags: { operation: 'auto-fix-comment-reply', step: 'review-memory-feedback' }, + extra: { ticketId: input.ticket.id, outcome: input.outcome }, + }); + } +} + const PUBLIC_ERROR_MAX_LENGTH = 500; /** Strip URLs, file paths, and stack traces that may leak infra details. */ @@ -256,6 +279,8 @@ export async function handleCommentReply( completedAt: new Date(), }); + await recordAutoFixFeedbackSafely({ ticket, outcome: 'success' }); + return { ok: true, action: 'reaction_and_reply' }; } @@ -316,6 +341,12 @@ export async function handleCommentReply( completedAt: new Date(), }); + await recordAutoFixFeedbackSafely({ + ticket, + outcome: 'failed', + errorMessage: sanitizedReason, + }); + return { ok: true, action: 'reply' }; } catch (replyError) { errorExceptInTest('[auto-fix-comment-reply] Failed to notify review comment:', replyError); @@ -337,12 +368,19 @@ export async function handleCommentReply( // Best-effort reaction } + const notificationFailureMessage = `Failed to notify review comment: ${replyError instanceof Error ? replyError.message : String(replyError)}`; await updateFixTicketStatus(ticketId, 'failed', { sessionId, - errorMessage: `Failed to notify review comment: ${replyError instanceof Error ? replyError.message : String(replyError)}`, + errorMessage: notificationFailureMessage, completedAt: new Date(), }); + await recordAutoFixFeedbackSafely({ + ticket, + outcome: 'failed', + errorMessage: sanitizePublicErrorMessage(notificationFailureMessage), + }); + return { ok: false, error: replyError instanceof Error ? replyError.message : String(replyError), diff --git a/apps/web/src/lib/code-reviews/review-memory/db.ts b/apps/web/src/lib/code-reviews/review-memory/db.ts index 2e4f1ebaa8..8bb013a228 100644 --- a/apps/web/src/lib/code-reviews/review-memory/db.ts +++ b/apps/web/src/lib/code-reviews/review-memory/db.ts @@ -16,6 +16,7 @@ import type { CodeReviewMemoryProposal, } from '@kilocode/db/schema'; import type { + ReviewMemoryAggregationScopeStatus, ReviewMemoryAggregationRunStatus, ReviewMemoryAggregationRunTrigger, ReviewMemoryChangeRequestType, @@ -389,7 +390,8 @@ export async function refreshAggregationStateForScope( .limit(1); const eventCount = rollup?.eventCount ?? 0; - const status = existing?.status === 'running' ? 'running' : eventCount > 0 ? 'eligible' : 'idle'; + const status: ReviewMemoryAggregationScopeStatus = + existing?.status === 'running' ? 'running' : eventCount > 0 ? 'eligible' : 'idle'; const now = new Date().toISOString(); const stateValues = { fresh_event_count: eventCount, @@ -419,7 +421,6 @@ export async function refreshAggregationStateForScope( ...ownerColumns(input.owner), platform: input.platform, repo_full_name: input.repoFullName, - platform_project_id: input.platformProjectId ?? null, ...stateValues, next_eligible_at: now, }) diff --git a/apps/web/src/lib/code-reviews/review-memory/github-feedback.test.ts b/apps/web/src/lib/code-reviews/review-memory/github-feedback.test.ts new file mode 100644 index 0000000000..4bba7a79c2 --- /dev/null +++ b/apps/web/src/lib/code-reviews/review-memory/github-feedback.test.ts @@ -0,0 +1,326 @@ +/* eslint-disable drizzle/enforce-delete-with-where */ +import { createFixTicket, getFixTicketById } from '@/lib/auto-fix/db/fix-tickets'; +import { db } from '@/lib/drizzle'; +import type { + PullRequestReviewCommentPayload, + PullRequestReviewPayload, + PullRequestReviewThreadPayload, + ReactionPayload, +} from '@/lib/integrations/platforms/github/webhook-schemas'; +import { insertTestUser } from '@/tests/helpers/user.helper'; +import { + auto_fix_tickets, + code_review_feedback_events, + code_review_feedback_subjects, + code_review_memory_aggregation_state, + kilocode_users, + platform_integrations, +} from '@kilocode/db/schema'; +import { eq } from 'drizzle-orm'; +import { + handleGitHubReactionFeedback, + handleGitHubReviewCommentFeedback, + handleGitHubReviewFeedback, + handleGitHubReviewThreadFeedback, + recordGitHubAutoFixFeedback, +} from './github-feedback'; +import { upsertFeedbackSubject, type ReviewMemoryOwner } from './db'; + +describe('GitHub review memory feedback', () => { + afterEach(async () => { + await db.delete(code_review_feedback_events); + await db.delete(code_review_feedback_subjects); + await db.delete(code_review_memory_aggregation_state); + await db.delete(auto_fix_tickets); + await db.delete(platform_integrations); + await db.delete(kilocode_users); + }); + + async function seedIntegration() { + const user = await insertTestUser(); + const [integration] = await db + .insert(platform_integrations) + .values({ + owned_by_user_id: user.id, + platform: 'github', + integration_type: 'app', + platform_installation_id: `review-memory-${Date.now()}-${Math.random()}`, + github_app_type: 'standard', + }) + .returning(); + + if (!integration) throw new Error('Failed to seed platform integration'); + return { user, owner: { type: 'user' as const, id: user.id }, integration }; + } + + function repository() { + return { + id: 123, + name: 'widgets', + full_name: 'acme/widgets', + owner: { login: 'acme' }, + }; + } + + function pullRequest() { + return { + number: 42, + html_url: 'https://github.com/acme/widgets/pull/42', + head: { sha: 'abc123', ref: 'feature/widgets' }, + }; + } + + async function seedInlineSubject(owner: ReviewMemoryOwner, externalId = '500') { + return await upsertFeedbackSubject({ + owner, + platform: 'github', + repoFullName: 'acme/widgets', + subjectType: 'inline_comment', + externalId, + prNumber: 42, + prUrl: 'https://github.com/acme/widgets/pull/42', + bodyExcerpt: '**WARNING**: Avoid this pattern', + state: 'active', + }); + } + + it('records reactions on Kilo review comments', async () => { + const { integration } = await seedIntegration(); + const payload = { + action: 'created', + reaction: { + id: 900, + content: '-1', + created_at: '2026-01-01T00:00:00.000Z', + user: { login: 'maintainer', type: 'User' }, + }, + comment: { + id: 500, + body: '**WARNING**: Avoid this pattern', + html_url: 'https://github.com/acme/widgets/pull/42#discussion_r500', + path: 'src/widget.ts', + line: 12, + diff_hunk: '@@ -1 +1 @@', + }, + pull_request: pullRequest(), + repository: repository(), + installation: { id: 98765 }, + sender: { login: 'maintainer', type: 'User' }, + } satisfies ReactionPayload; + + const result = await handleGitHubReactionFeedback({ + payload, + integration, + deliveryId: 'delivery-reaction-negative', + }); + + expect(result.recorded).toBe(true); + const [event] = await db.select().from(code_review_feedback_events); + expect(event.signal_kind).toBe('negative_reaction'); + expect(event.sentiment).toBe('negative'); + expect(event.strength).toBe(3); + + const [subject] = await db.select().from(code_review_feedback_subjects); + expect(subject.external_id).toBe('500'); + expect(subject.severity).toBe('warning'); + }); + + it('records corrective and supportive replies to Kilo inline comments', async () => { + const { owner, integration } = await seedIntegration(); + const subject = await seedInlineSubject(owner); + + const correctivePayload = { + action: 'created', + comment: { + id: 501, + in_reply_to_id: 500, + body: 'This is a false positive in this repository.', + user: { login: 'maintainer', type: 'User' }, + html_url: 'https://github.com/acme/widgets/pull/42#discussion_r501', + path: 'src/widget.ts', + line: 12, + diff_hunk: '@@ -1 +1 @@', + author_association: 'MEMBER', + }, + pull_request: { + ...pullRequest(), + title: 'Add widgets', + user: { login: 'author' }, + base: { ref: 'main' }, + }, + repository: repository(), + installation: { id: 98765 }, + sender: { login: 'maintainer' }, + } satisfies PullRequestReviewCommentPayload; + + const supportivePayload = { + ...correctivePayload, + comment: { + ...correctivePayload.comment, + id: 502, + body: 'Good catch, fixed this now.', + html_url: 'https://github.com/acme/widgets/pull/42#discussion_r502', + }, + } satisfies PullRequestReviewCommentPayload; + + await handleGitHubReviewCommentFeedback({ + payload: correctivePayload, + integration, + deliveryId: 'delivery-corrective-reply', + }); + await handleGitHubReviewCommentFeedback({ + payload: supportivePayload, + integration, + deliveryId: 'delivery-supportive-reply', + }); + + const events = await db.select().from(code_review_feedback_events); + expect(events).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + subject_id: subject.id, + signal_kind: 'corrective_reply', + sentiment: 'negative', + }), + expect.objectContaining({ + subject_id: subject.id, + signal_kind: 'supportive_reply', + sentiment: 'positive', + }), + ]) + ); + }); + + it('records Kilo review dismissals and review-thread resolution', async () => { + const { integration } = await seedIntegration(); + const dismissedReview = { + action: 'dismissed', + review: { + id: 700, + state: 'dismissed', + user: { login: 'kilo-code[bot]' }, + }, + pull_request: { + number: 42, + state: 'open', + merged: false, + html_url: 'https://github.com/acme/widgets/pull/42', + title: 'Add widgets', + head: { + sha: 'abc123', + ref: 'feature/widgets', + repo: { + full_name: 'acme/widgets', + clone_url: 'https://github.com/acme/widgets.git', + html_url: 'https://github.com/acme/widgets', + }, + }, + }, + repository: repository(), + installation: { id: 98765 }, + } satisfies PullRequestReviewPayload; + const resolvedThread = { + action: 'resolved', + thread: { + id: 'thread-1', + is_resolved: true, + comments: [ + { + id: 500, + body: '**WARNING**: Avoid this pattern', + html_url: 'https://github.com/acme/widgets/pull/42#discussion_r500', + path: 'src/widget.ts', + line: 12, + diff_hunk: '@@ -1 +1 @@', + }, + ], + }, + pull_request: pullRequest(), + repository: repository(), + installation: { id: 98765 }, + sender: { login: 'maintainer', type: 'User' }, + } satisfies PullRequestReviewThreadPayload; + + await handleGitHubReviewFeedback({ + payload: dismissedReview, + integration, + deliveryId: 'delivery-review-dismissed', + }); + await handleGitHubReviewThreadFeedback({ + payload: resolvedThread, + integration, + deliveryId: 'delivery-thread-resolved', + }); + + const events = await db.select().from(code_review_feedback_events); + expect(events).toEqual( + expect.arrayContaining([ + expect.objectContaining({ signal_kind: 'review_dismissed', sentiment: 'negative' }), + expect.objectContaining({ signal_kind: 'thread_resolved', sentiment: 'positive' }), + ]) + ); + + const [threadSubject] = await db + .select() + .from(code_review_feedback_subjects) + .where(eq(code_review_feedback_subjects.external_id, 'thread-1')); + expect(threadSubject.state).toBe('resolved'); + }); + + it('records Auto Fix completion and operational failure evidence', async () => { + const { owner, integration } = await seedIntegration(); + await seedInlineSubject(owner, '800'); + const successTicketId = await createFixTicket({ + owner: { type: 'user', id: owner.id, userId: owner.id }, + platformIntegrationId: integration.id, + repoFullName: 'acme/widgets', + issueNumber: 42, + issueUrl: 'https://github.com/acme/widgets/pull/42', + issueTitle: 'Add widgets', + issueBody: null, + issueAuthor: 'author', + issueLabels: [], + triggerSource: 'review_comment', + reviewCommentId: 800, + reviewCommentBody: '@kilo fix this', + filePath: 'src/widget.ts', + lineNumber: 12, + diffHunk: '@@ -1 +1 @@', + }); + const failedTicketId = await createFixTicket({ + owner: { type: 'user', id: owner.id, userId: owner.id }, + platformIntegrationId: integration.id, + repoFullName: 'acme/widgets', + issueNumber: 43, + issueUrl: 'https://github.com/acme/widgets/pull/43', + issueTitle: 'Add more widgets', + issueBody: null, + issueAuthor: 'author', + issueLabels: [], + triggerSource: 'review_comment', + reviewCommentId: 801, + reviewCommentBody: '@kilo fix this', + filePath: 'src/widget.ts', + lineNumber: 14, + diffHunk: '@@ -1 +1 @@', + }); + const successTicket = await getFixTicketById(successTicketId); + const failedTicket = await getFixTicketById(failedTicketId); + if (!successTicket || !failedTicket) throw new Error('Failed to read seeded fix tickets'); + + await recordGitHubAutoFixFeedback({ ticket: successTicket, outcome: 'success' }); + await recordGitHubAutoFixFeedback({ + ticket: failedTicket, + outcome: 'failed', + errorMessage: 'The auto-fix run timed out.', + }); + + const events = await db.select().from(code_review_feedback_events); + expect(events).toEqual( + expect.arrayContaining([ + expect.objectContaining({ signal_kind: 'autofix_completed', sentiment: 'positive' }), + expect.objectContaining({ signal_kind: 'autofix_failed', sentiment: 'neutral' }), + ]) + ); + }); +}); diff --git a/apps/web/src/lib/code-reviews/review-memory/github-feedback.ts b/apps/web/src/lib/code-reviews/review-memory/github-feedback.ts new file mode 100644 index 0000000000..6c82a3b70f --- /dev/null +++ b/apps/web/src/lib/code-reviews/review-memory/github-feedback.ts @@ -0,0 +1,508 @@ +import type { AutoFixTicket, PlatformIntegration } from '@kilocode/db/schema'; +import type { + PullRequestReviewCommentPayload, + PullRequestReviewPayload, + PullRequestReviewThreadPayload, + ReactionPayload, +} from '@/lib/integrations/platforms/github/webhook-schemas'; +import { + createReviewMemoryDedupeHash, + findFeedbackSubject, + recordFeedbackEvent, + upsertFeedbackSubject, + type ReviewMemoryOwner, +} from './db'; +import { isLikelyKiloInlineReviewBody, parseReviewFindingMetadata } from './sync-subjects'; + +type GitHubFeedbackResult = + | { recorded: boolean; eventId: string } + | { recorded: false; reason: string }; + +type GitHubActor = + | { + login?: string; + type?: string; + } + | null + | undefined; + +const POSITIVE_REACTIONS = new Set(['+1', 'heart', 'hooray', 'rocket']); +const NEGATIVE_REACTIONS = new Set(['-1', 'confused']); + +function ownerFromIntegration(integration: PlatformIntegration): ReviewMemoryOwner | null { + if (integration.owned_by_organization_id) { + return { type: 'org', id: integration.owned_by_organization_id }; + } + if (integration.owned_by_user_id) { + return { type: 'user', id: integration.owned_by_user_id }; + } + return null; +} + +function ownerFromAutoFixTicket(ticket: AutoFixTicket): ReviewMemoryOwner | null { + if (ticket.owned_by_organization_id) { + return { type: 'org', id: ticket.owned_by_organization_id }; + } + if (ticket.owned_by_user_id) { + return { type: 'user', id: ticket.owned_by_user_id }; + } + return null; +} + +function isLikelyKiloBotActor(actor: GitHubActor): boolean { + const login = actor?.login?.toLowerCase() ?? ''; + return actor?.type === 'Bot' || login.endsWith('[bot]') || login.includes('kilo'); +} + +function pullNumberFromUrl(url: string | undefined): number | null { + if (!url) return null; + const match = url.match(/\/pull\/(\d+)/); + if (!match?.[1]) return null; + const parsed = Number(match[1]); + return Number.isInteger(parsed) ? parsed : null; +} + +function reactionSentiment(content: string): { + signalKind: 'positive_reaction' | 'negative_reaction'; + sentiment: 'positive' | 'negative'; + strength: number; +} | null { + if (POSITIVE_REACTIONS.has(content)) { + return { signalKind: 'positive_reaction', sentiment: 'positive', strength: 2 }; + } + if (NEGATIVE_REACTIONS.has(content)) { + return { signalKind: 'negative_reaction', sentiment: 'negative', strength: 3 }; + } + return null; +} + +function classifyGitHubAutoFixFailure(errorMessage: string | undefined): { + sentiment: 'negative' | 'neutral'; + strength: number; + category: string; +} { + const normalized = errorMessage?.toLowerCase() ?? ''; + if ( + normalized.includes('balance') || + normalized.includes('permission') || + normalized.includes('timeout') || + normalized.includes('timed out') || + normalized.includes('internal_server_error') || + normalized.includes('cloud agent returned 500') + ) { + return { sentiment: 'neutral', strength: 1, category: 'operational' }; + } + + return { sentiment: 'negative', strength: 2, category: 'unclassified' }; +} + +export function classifyGitHubReviewCommentReply(body: string): { + signalKind: 'corrective_reply' | 'supportive_reply' | 'autofix_requested'; + sentiment: 'positive' | 'negative' | 'neutral'; + strength: number; +} { + const normalized = body.toLowerCase(); + if (/@kilo\s+(fix|patch)\b/i.test(body)) { + return { signalKind: 'autofix_requested', sentiment: 'negative', strength: 5 }; + } + + const correctivePatterns = [ + 'false positive', + 'not an issue', + 'incorrect', + 'wrong', + 'this is expected', + 'expected behavior', + 'does not apply', + 'please remove', + 'unhelpful', + ]; + if (correctivePatterns.some(pattern => normalized.includes(pattern))) { + return { signalKind: 'corrective_reply', sentiment: 'negative', strength: 3 }; + } + + const supportivePatterns = [ + 'good catch', + 'nice catch', + 'thanks', + 'thank you', + 'fixed', + 'resolved', + 'agreed', + 'makes sense', + ]; + if (supportivePatterns.some(pattern => normalized.includes(pattern))) { + return { signalKind: 'supportive_reply', sentiment: 'positive', strength: 2 }; + } + + return { signalKind: 'supportive_reply', sentiment: 'neutral', strength: 1 }; +} + +async function findOrUpsertCommentSubject(params: { + owner: ReviewMemoryOwner; + integration: PlatformIntegration; + repoFullName: string; + prNumber: number | null; + prUrl: string | null; + headSha?: string | null; + comment: { + id: number; + body?: string; + html_url?: string; + path?: string; + line?: number | null; + diff_hunk?: string; + }; +}) { + const externalId = String(params.comment.id); + const body = params.comment.body ?? ''; + const existingInline = await findFeedbackSubject({ + owner: params.owner, + platform: 'github', + repoFullName: params.repoFullName, + subjectType: 'inline_comment', + externalId, + }); + if (existingInline) return existingInline; + + const existingSummary = await findFeedbackSubject({ + owner: params.owner, + platform: 'github', + repoFullName: params.repoFullName, + subjectType: 'summary_comment', + externalId, + }); + if (existingSummary) return existingSummary; + + const isSummary = body.includes(''); + const isInline = isLikelyKiloInlineReviewBody(body); + if (!isSummary && !isInline) return null; + + const metadata = isInline ? parseReviewFindingMetadata(body) : null; + return await upsertFeedbackSubject({ + owner: params.owner, + platform: 'github', + platformIntegrationId: params.integration.id, + subjectType: isSummary ? 'summary_comment' : 'inline_comment', + externalId, + externalUrl: params.comment.html_url ?? null, + repoFullName: params.repoFullName, + prNumber: params.prNumber, + prUrl: params.prUrl, + headSha: params.headSha, + filePath: params.comment.path ?? null, + lineNumber: params.comment.line ?? null, + diffHunk: params.comment.diff_hunk ?? null, + bodyExcerpt: body, + severity: metadata?.severity ?? null, + findingTitle: metadata?.findingTitle ?? null, + findingFingerprint: metadata?.findingFingerprint ?? null, + state: 'active', + }); +} + +export async function handleGitHubReactionFeedback(input: { + payload: ReactionPayload; + integration: PlatformIntegration; + deliveryId: string; +}): Promise { + if (input.payload.action !== 'created') + return { recorded: false, reason: 'reaction-not-created' }; + if (isLikelyKiloBotActor(input.payload.sender ?? input.payload.reaction.user)) { + return { recorded: false, reason: 'bot-authored-reaction' }; + } + + const owner = ownerFromIntegration(input.integration); + if (!owner) return { recorded: false, reason: 'missing-owner' }; + + const sentiment = reactionSentiment(input.payload.reaction.content); + if (!sentiment) return { recorded: false, reason: 'unsupported-reaction' }; + if (!input.payload.comment) return { recorded: false, reason: 'missing-comment' }; + + const prNumber = + input.payload.pull_request?.number ?? + input.payload.issue?.number ?? + pullNumberFromUrl(input.payload.comment.html_url); + const prUrl = + input.payload.pull_request?.html_url ?? + input.payload.issue?.html_url ?? + input.payload.comment.html_url ?? + null; + const subject = await findOrUpsertCommentSubject({ + owner, + integration: input.integration, + repoFullName: input.payload.repository.full_name, + prNumber, + prUrl, + headSha: input.payload.pull_request?.head?.sha ?? null, + comment: input.payload.comment, + }); + + if (!subject) return { recorded: false, reason: 'not-kilo-subject' }; + + const result = await recordFeedbackEvent({ + owner, + platform: 'github', + platformIntegrationId: input.integration.id, + subjectId: subject.id, + codeReviewId: subject.code_review_id, + repoFullName: input.payload.repository.full_name, + prNumber, + prUrl, + eventSource: 'github_webhook', + signalKind: sentiment.signalKind, + sentiment: sentiment.sentiment, + strength: sentiment.strength, + externalEventId: `github:${input.deliveryId}:reaction:${input.payload.reaction.id}`, + externalUrl: input.payload.comment.html_url ?? prUrl, + evidenceExcerpt: `Reaction: ${input.payload.reaction.content}`, + metadata: { reaction: input.payload.reaction.content }, + occurredAt: input.payload.reaction.created_at ?? null, + }); + + return { recorded: result.created, eventId: result.event.id }; +} + +export async function handleGitHubReviewCommentFeedback(input: { + payload: PullRequestReviewCommentPayload; + integration: PlatformIntegration; + deliveryId: string; +}): Promise { + if (input.payload.action !== 'created') return { recorded: false, reason: 'comment-not-created' }; + if (isLikelyKiloBotActor(input.payload.comment.user)) { + return { recorded: false, reason: 'bot-authored-comment' }; + } + + const owner = ownerFromIntegration(input.integration); + if (!owner) return { recorded: false, reason: 'missing-owner' }; + + const classification = classifyGitHubReviewCommentReply(input.payload.comment.body); + const parentCommentId = input.payload.comment.in_reply_to_id ?? null; + if (!parentCommentId) return { recorded: false, reason: 'not-review-comment-reply' }; + + const subject = parentCommentId + ? await findFeedbackSubject({ + owner, + platform: 'github', + repoFullName: input.payload.repository.full_name, + subjectType: 'inline_comment', + externalId: String(parentCommentId), + }) + : null; + + if (!subject && classification.signalKind !== 'autofix_requested') { + return { recorded: false, reason: 'not-kilo-subject' }; + } + + const prUrl = input.payload.pull_request.html_url ?? null; + const result = await recordFeedbackEvent({ + owner, + platform: 'github', + platformIntegrationId: input.integration.id, + subjectId: subject?.id ?? null, + codeReviewId: subject?.code_review_id ?? null, + repoFullName: input.payload.repository.full_name, + prNumber: input.payload.pull_request.number, + prUrl, + eventSource: 'github_webhook', + signalKind: classification.signalKind, + sentiment: classification.sentiment, + strength: classification.strength, + externalEventId: `github:${input.deliveryId}:review-comment:${input.payload.comment.id}`, + externalUrl: input.payload.comment.html_url, + evidenceExcerpt: input.payload.comment.body, + metadata: parentCommentId ? { parent_review_comment_id: parentCommentId } : {}, + }); + + return { recorded: result.created, eventId: result.event.id }; +} + +export async function handleGitHubReviewFeedback(input: { + payload: PullRequestReviewPayload; + integration: PlatformIntegration; + deliveryId: string; +}): Promise { + const owner = ownerFromIntegration(input.integration); + if (!owner) return { recorded: false, reason: 'missing-owner' }; + + if (input.payload.action === 'dismissed') { + if (!isLikelyKiloBotActor(input.payload.review.user)) { + return { recorded: false, reason: 'non-kilo-review' }; + } + const subject = await upsertFeedbackSubject({ + owner, + platform: 'github', + platformIntegrationId: input.integration.id, + subjectType: 'review', + externalId: String(input.payload.review.id), + externalUrl: input.payload.pull_request.html_url, + repoFullName: input.payload.repository.full_name, + prNumber: input.payload.pull_request.number, + prUrl: input.payload.pull_request.html_url, + headSha: input.payload.pull_request.head.sha, + state: 'dismissed', + }); + const result = await recordFeedbackEvent({ + owner, + platform: 'github', + platformIntegrationId: input.integration.id, + subjectId: subject.id, + repoFullName: input.payload.repository.full_name, + prNumber: input.payload.pull_request.number, + prUrl: input.payload.pull_request.html_url, + eventSource: 'github_webhook', + signalKind: 'review_dismissed', + sentiment: 'negative', + strength: 4, + externalEventId: `github:${input.deliveryId}:review-dismissed:${input.payload.review.id}`, + externalUrl: input.payload.pull_request.html_url, + evidenceExcerpt: 'Kilo review was dismissed.', + }); + return { recorded: result.created, eventId: result.event.id }; + } + + if (input.payload.action === 'submitted') { + const state = input.payload.review.state; + if (state !== 'approved' && state !== 'changes_requested') { + return { recorded: false, reason: 'unsupported-review-state' }; + } + const result = await recordFeedbackEvent({ + owner, + platform: 'github', + platformIntegrationId: input.integration.id, + repoFullName: input.payload.repository.full_name, + prNumber: input.payload.pull_request.number, + prUrl: input.payload.pull_request.html_url, + eventSource: 'github_webhook', + signalKind: state === 'approved' ? 'pr_approved' : 'pr_changes_requested', + sentiment: state === 'approved' ? 'positive' : 'negative', + strength: 1, + externalEventId: `github:${input.deliveryId}:review:${input.payload.review.id}:${state}`, + externalUrl: input.payload.pull_request.html_url, + evidenceExcerpt: state === 'approved' ? 'Pull request approved.' : 'Changes requested.', + metadata: { review_state: state }, + }); + return { recorded: result.created, eventId: result.event.id }; + } + + return { recorded: false, reason: 'unsupported-review-action' }; +} + +export async function handleGitHubReviewThreadFeedback(input: { + payload: PullRequestReviewThreadPayload; + integration: PlatformIntegration; + deliveryId: string; +}): Promise { + if (isLikelyKiloBotActor(input.payload.sender)) { + return { recorded: false, reason: 'bot-authored-thread-event' }; + } + const owner = ownerFromIntegration(input.integration); + if (!owner) return { recorded: false, reason: 'missing-owner' }; + + const kiloComment = input.payload.thread.comments.find(comment => + isLikelyKiloInlineReviewBody(comment.body) + ); + if (!kiloComment) return { recorded: false, reason: 'not-kilo-thread' }; + + const metadata = parseReviewFindingMetadata(kiloComment.body); + const subject = await upsertFeedbackSubject({ + owner, + platform: 'github', + platformIntegrationId: input.integration.id, + subjectType: 'discussion', + externalId: input.payload.thread.id, + externalThreadId: input.payload.thread.id, + externalUrl: kiloComment.html_url ?? input.payload.pull_request.html_url ?? null, + repoFullName: input.payload.repository.full_name, + prNumber: input.payload.pull_request.number, + prUrl: input.payload.pull_request.html_url ?? null, + headSha: input.payload.pull_request.head?.sha ?? null, + filePath: kiloComment.path ?? null, + lineNumber: kiloComment.line ?? null, + diffHunk: kiloComment.diff_hunk ?? null, + bodyExcerpt: kiloComment.body, + severity: metadata.severity, + findingTitle: metadata.findingTitle, + findingFingerprint: metadata.findingFingerprint, + state: input.payload.action === 'resolved' ? 'resolved' : 'active', + }); + + const signalKind = input.payload.action === 'resolved' ? 'thread_resolved' : 'thread_unresolved'; + const result = await recordFeedbackEvent({ + owner, + platform: 'github', + platformIntegrationId: input.integration.id, + subjectId: subject.id, + codeReviewId: subject.code_review_id, + repoFullName: input.payload.repository.full_name, + prNumber: input.payload.pull_request.number, + prUrl: input.payload.pull_request.html_url ?? null, + eventSource: 'github_webhook', + signalKind, + sentiment: input.payload.action === 'resolved' ? 'positive' : 'negative', + strength: 2, + externalEventId: `github:${input.deliveryId}:thread:${input.payload.thread.id}:${input.payload.action}`, + externalUrl: kiloComment.html_url ?? input.payload.pull_request.html_url ?? null, + evidenceExcerpt: + input.payload.action === 'resolved' ? 'Review thread resolved.' : 'Review thread reopened.', + metadata: { thread_id: input.payload.thread.id }, + }); + + return { recorded: result.created, eventId: result.event.id }; +} + +export async function recordGitHubAutoFixFeedback(input: { + ticket: AutoFixTicket; + outcome: 'success' | 'failed'; + errorMessage?: string; +}): Promise { + if (!input.ticket.review_comment_id) { + return { recorded: false, reason: 'missing-review-comment-id' }; + } + + const owner = ownerFromAutoFixTicket(input.ticket); + if (!owner) return { recorded: false, reason: 'missing-owner' }; + + const subject = await findFeedbackSubject({ + owner, + platform: 'github', + repoFullName: input.ticket.repo_full_name, + subjectType: 'inline_comment', + externalId: String(input.ticket.review_comment_id), + }); + const failure = + input.outcome === 'failed' ? classifyGitHubAutoFixFailure(input.errorMessage) : null; + const signalKind = input.outcome === 'success' ? 'autofix_completed' : 'autofix_failed'; + const externalEventId = `github:auto-fix:${input.ticket.id}:${signalKind}`; + const result = await recordFeedbackEvent({ + owner, + platform: 'github', + platformIntegrationId: input.ticket.platform_integration_id, + subjectId: subject?.id ?? null, + codeReviewId: subject?.code_review_id ?? null, + repoFullName: input.ticket.repo_full_name, + prNumber: input.ticket.issue_number, + prUrl: input.ticket.issue_url, + eventSource: 'auto_fix', + signalKind, + sentiment: input.outcome === 'success' ? 'positive' : (failure?.sentiment ?? 'negative'), + strength: input.outcome === 'success' ? 2 : (failure?.strength ?? 2), + externalEventId, + dedupeHash: createReviewMemoryDedupeHash([externalEventId]), + externalUrl: input.ticket.issue_url, + evidenceExcerpt: + input.outcome === 'success' + ? 'Auto Fix completed for requested review feedback.' + : input.errorMessage + ? `Auto Fix failed: ${input.errorMessage}` + : 'Auto Fix failed for requested review feedback.', + metadata: { + ticket_id: input.ticket.id, + review_comment_id: input.ticket.review_comment_id, + failure_category: failure?.category, + }, + occurredAt: input.ticket.completed_at ?? null, + }); + + return { recorded: result.created, eventId: result.event.id }; +} diff --git a/apps/web/src/lib/code-reviews/review-memory/sync-subjects.ts b/apps/web/src/lib/code-reviews/review-memory/sync-subjects.ts index 05edcf38c3..aae4e4cff0 100644 --- a/apps/web/src/lib/code-reviews/review-memory/sync-subjects.ts +++ b/apps/web/src/lib/code-reviews/review-memory/sync-subjects.ts @@ -115,7 +115,7 @@ export function parseReviewFindingMetadata(body: string): { }; } -function isLikelyKiloInlineReviewBody(body: string): boolean { +export function isLikelyKiloInlineReviewBody(body: string): boolean { if (body.includes(KILO_REVIEW_MARKER)) return true; const { severity } = parseReviewFindingMetadata(body); if (severity) return true; diff --git a/apps/web/src/lib/integrations/core/constants.ts b/apps/web/src/lib/integrations/core/constants.ts index 6a87aed1b7..3ea6ca7134 100644 --- a/apps/web/src/lib/integrations/core/constants.ts +++ b/apps/web/src/lib/integrations/core/constants.ts @@ -41,6 +41,7 @@ export const GITHUB_EVENT = { PULL_REQUEST_REVIEW: 'pull_request_review', PULL_REQUEST_REVIEW_COMMENT: 'pull_request_review_comment', PULL_REQUEST_REVIEW_THREAD: 'pull_request_review_thread', + REACTION: 'reaction', // Push and commit events PUSH: 'push', @@ -83,6 +84,8 @@ export const GITHUB_ACTION = { // Review actions SUBMITTED: 'submitted', DISMISSED: 'dismissed', + RESOLVED: 'resolved', + UNRESOLVED: 'unresolved', // Workflow actions REQUESTED: 'requested', diff --git a/apps/web/src/lib/integrations/platforms/github/webhook-handler.test.ts b/apps/web/src/lib/integrations/platforms/github/webhook-handler.test.ts index 7175678617..64197189cb 100644 --- a/apps/web/src/lib/integrations/platforms/github/webhook-handler.test.ts +++ b/apps/web/src/lib/integrations/platforms/github/webhook-handler.test.ts @@ -8,6 +8,11 @@ const mockLogWebhookEvent = jest.fn(); const mockUpdateWebhookEvent = jest.fn(); const mockHandlePullRequest = jest.fn(); const mockHandlePRReviewComment = jest.fn(); +const mockHandleGitHubReactionFeedback = jest.fn(); +const mockHandleGitHubReviewCommentFeedback = jest.fn(); +const mockHandleGitHubReviewFeedback = jest.fn(); +const mockHandleGitHubReviewThreadFeedback = jest.fn(); +const mockAfterPromises: Promise[] = []; jest.mock('@/lib/integrations/platforms/github/adapter', () => ({ verifyGitHubWebhookSignature: (payload: string, signature: string, appType: string) => @@ -41,16 +46,31 @@ jest.mock('@/lib/integrations/platforms/github/webhook-handlers', () => ({ upsertCliSessionPullRequestReviewFromWebhook: jest.fn(), })); +jest.mock('@/lib/code-reviews/review-memory/github-feedback', () => ({ + handleGitHubReactionFeedback: (input: unknown) => mockHandleGitHubReactionFeedback(input), + handleGitHubReviewCommentFeedback: (input: unknown) => + mockHandleGitHubReviewCommentFeedback(input), + handleGitHubReviewFeedback: (input: unknown) => mockHandleGitHubReviewFeedback(input), + handleGitHubReviewThreadFeedback: (input: unknown) => mockHandleGitHubReviewThreadFeedback(input), +})); + jest.mock('next/server', () => { const actual = jest.requireActual('next/server'); return { ...actual, - after: (fn: () => unknown) => fn(), + after: (fn: () => unknown) => { + mockAfterPromises.push(Promise.resolve(fn())); + }, }; }); import { handleGitHubWebhook } from './webhook-handler'; +async function flushAfterCallbacks() { + const pending = mockAfterPromises.splice(0); + await Promise.all(pending); +} + const integration = { id: 'pi_github', owned_by_organization_id: 'org_1', @@ -128,6 +148,109 @@ function reviewCommentPayload(overrides: Record = {}) { }; } +function pullRequestReviewPayload(overrides: Record = {}) { + return { + action: 'submitted', + installation: { id: 98765 }, + repository: { + id: 123, + name: 'widgets', + full_name: 'acme/widgets', + owner: { login: 'acme' }, + }, + review: { + id: 321, + state: 'approved', + user: { login: 'alice' }, + }, + pull_request: { + number: 42, + state: 'open', + merged: false, + html_url: 'https://github.com/acme/widgets/pull/42', + title: 'Add widgets', + head: { + sha: 'abc123', + ref: 'feature/widgets', + repo: { + full_name: 'acme/widgets', + clone_url: 'https://github.com/acme/widgets.git', + html_url: 'https://github.com/acme/widgets', + }, + }, + }, + ...overrides, + }; +} + +function reactionPayload(overrides: Record = {}) { + return { + action: 'created', + installation: { id: 98765 }, + repository: { + id: 123, + name: 'widgets', + full_name: 'acme/widgets', + owner: { login: 'acme' }, + }, + reaction: { + id: 654, + content: '-1', + created_at: '2026-01-01T00:00:00.000Z', + user: { login: 'alice', type: 'User' }, + }, + comment: { + id: 456, + body: '**WARNING**: Check this path', + html_url: 'https://github.com/acme/widgets/pull/42#discussion_r456', + path: 'src/widget.ts', + line: 10, + diff_hunk: '@@ -1 +1 @@', + }, + pull_request: { + number: 42, + html_url: 'https://github.com/acme/widgets/pull/42', + head: { sha: 'abc123', ref: 'feature/widgets' }, + }, + sender: { login: 'alice', type: 'User' }, + ...overrides, + }; +} + +function reviewThreadPayload(overrides: Record = {}) { + return { + action: 'resolved', + installation: { id: 98765 }, + repository: { + id: 123, + name: 'widgets', + full_name: 'acme/widgets', + owner: { login: 'acme' }, + }, + thread: { + id: 'PRRT_kwDOthread', + is_resolved: true, + comments: [ + { + id: 456, + body: '**WARNING**: Check this path', + html_url: 'https://github.com/acme/widgets/pull/42#discussion_r456', + path: 'src/widget.ts', + line: 10, + diff_hunk: '@@ -1 +1 @@', + }, + ], + }, + pull_request: { + number: 42, + html_url: 'https://github.com/acme/widgets/pull/42', + head: { sha: 'abc123', ref: 'feature/widgets' }, + }, + sender: { login: 'alice', type: 'User' }, + ...overrides, + }; +} + function issueCommentPayload(overrides: Record = {}) { return { action: 'created', @@ -159,12 +282,17 @@ function issueCommentPayload(overrides: Record = {}) { describe('handleGitHubWebhook', () => { beforeEach(() => { jest.clearAllMocks(); + mockAfterPromises.length = 0; mockVerifyGitHubWebhookSignature.mockReturnValue(true); mockFindIntegrationByInstallationId.mockResolvedValue(integration); mockLogWebhookEvent.mockResolvedValue({ id: 'we_1', isDuplicate: false }); mockUpdateWebhookEvent.mockResolvedValue(undefined); mockHandlePullRequest.mockResolvedValue(Response.json({ message: 'review queued' })); mockHandlePRReviewComment.mockResolvedValue(undefined); + mockHandleGitHubReactionFeedback.mockResolvedValue({ recorded: true, eventId: 'event_1' }); + mockHandleGitHubReviewCommentFeedback.mockResolvedValue({ recorded: true, eventId: 'event_1' }); + mockHandleGitHubReviewFeedback.mockResolvedValue({ recorded: true, eventId: 'event_1' }); + mockHandleGitHubReviewThreadFeedback.mockResolvedValue({ recorded: true, eventId: 'event_1' }); }); it('keeps pull_request webhooks on the code review path', async () => { @@ -191,6 +319,7 @@ describe('handleGitHubWebhook', () => { signedGitHubRequest('pull_request_review_comment', reviewCommentPayload()), 'standard' ); + await flushAfterCallbacks(); expect(response.status).toBe(200); expect(mockHandlePullRequest).not.toHaveBeenCalled(); @@ -198,9 +327,75 @@ describe('handleGitHubWebhook', () => { expect.objectContaining({ action: 'created' }), integration ); + expect(mockHandleGitHubReviewCommentFeedback).toHaveBeenCalledWith({ + payload: expect.objectContaining({ action: 'created' }), + integration, + deliveryId: 'delivery-pull_request_review_comment', + }); + expect(mockUpdateWebhookEvent).toHaveBeenCalledWith( + 'we_1', + expect.objectContaining({ + handlers_triggered: ['pr_review_comment_fix', 'review_memory_feedback'], + }) + ); + }); + + it('routes pull_request_review events to review memory feedback', async () => { + const response = await handleGitHubWebhook( + signedGitHubRequest('pull_request_review', pullRequestReviewPayload()), + 'standard' + ); + await flushAfterCallbacks(); + + expect(response.status).toBe(200); + expect(mockHandleGitHubReviewFeedback).toHaveBeenCalledWith({ + payload: expect.objectContaining({ action: 'submitted' }), + integration, + deliveryId: 'delivery-pull_request_review', + }); + expect(mockUpdateWebhookEvent).toHaveBeenCalledWith( + 'we_1', + expect.objectContaining({ + handlers_triggered: ['cli_session_pr_review_upsert', 'review_memory_feedback'], + }) + ); + }); + + it('routes reaction events to review memory feedback', async () => { + const response = await handleGitHubWebhook( + signedGitHubRequest('reaction', reactionPayload()), + 'standard' + ); + await flushAfterCallbacks(); + + expect(response.status).toBe(200); + expect(mockHandleGitHubReactionFeedback).toHaveBeenCalledWith({ + payload: expect.objectContaining({ action: 'created' }), + integration, + deliveryId: 'delivery-reaction', + }); + expect(mockUpdateWebhookEvent).toHaveBeenCalledWith( + 'we_1', + expect.objectContaining({ handlers_triggered: ['review_memory_feedback'] }) + ); + }); + + it('routes review thread events to review memory feedback', async () => { + const response = await handleGitHubWebhook( + signedGitHubRequest('pull_request_review_thread', reviewThreadPayload()), + 'standard' + ); + await flushAfterCallbacks(); + + expect(response.status).toBe(200); + expect(mockHandleGitHubReviewThreadFeedback).toHaveBeenCalledWith({ + payload: expect.objectContaining({ action: 'resolved' }), + integration, + deliveryId: 'delivery-pull_request_review_thread', + }); expect(mockUpdateWebhookEvent).toHaveBeenCalledWith( 'we_1', - expect.objectContaining({ handlers_triggered: ['pr_review_comment_fix'] }) + expect.objectContaining({ handlers_triggered: ['review_memory_feedback'] }) ); }); diff --git a/apps/web/src/lib/integrations/platforms/github/webhook-handler.ts b/apps/web/src/lib/integrations/platforms/github/webhook-handler.ts index abf607a698..7100f5397d 100644 --- a/apps/web/src/lib/integrations/platforms/github/webhook-handler.ts +++ b/apps/web/src/lib/integrations/platforms/github/webhook-handler.ts @@ -13,6 +13,8 @@ import { IssuePayloadSchema, PullRequestReviewCommentPayloadSchema, PullRequestReviewPayloadSchema, + PullRequestReviewThreadPayloadSchema, + ReactionPayloadSchema, } from '@/lib/integrations/platforms/github/webhook-schemas'; import { findIntegrationByInstallationId } from '@/lib/integrations/db/platform-integrations'; import { @@ -34,6 +36,26 @@ import { logWebhookEvent, updateWebhookEvent } from '@/lib/integrations/db/webho import type { Owner } from '@/lib/integrations/core/types'; import type { GitHubAppType } from './app-selector'; import { redactSensitiveHeaders } from '@kilocode/worker-utils/redact-headers'; +import { + handleGitHubReactionFeedback, + handleGitHubReviewCommentFeedback, + handleGitHubReviewFeedback, + handleGitHubReviewThreadFeedback, +} from '@/lib/code-reviews/review-memory/github-feedback'; + +type WebhookHandlerError = { + message: string; + handler: string; + stack?: string; +}; + +function toWebhookHandlerError(handler: string, error: unknown): WebhookHandlerError { + return { + message: error instanceof Error ? error.message : String(error), + handler, + stack: error instanceof Error ? error.stack : undefined, + }; +} /** * Shared GitHub App Webhook Handler @@ -496,44 +518,51 @@ export async function handleGitHubWebhook( ? ({ kind: 'user', userId: integration.owned_by_user_id } as const) : null; - if (upsertOwner) { - after(async () => { + after(async () => { + const errors: WebhookHandlerError[] = []; + const handlersTriggered: string[] = []; + + if (upsertOwner) { + handlersTriggered.push('cli_session_pr_review_upsert'); try { await upsertCliSessionPullRequestReviewFromWebhook(parseResult.data, upsertOwner); - if (logResult.webhookEventId) { - await updateWebhookEvent(logResult.webhookEventId, { - processed: true, - processed_at: new Date().toISOString(), - handlers_triggered: ['cli_session_pr_review_upsert'], - errors: null, - }); - } } catch (error) { logExceptInTest(`Error handling pull_request_review${logSuffix}:`, error); captureException(error, { tags: { source: `${sentryPrefix}webhook_pr_review` }, }); - if (logResult.webhookEventId) { - try { - await updateWebhookEvent(logResult.webhookEventId, { - processed: true, - processed_at: new Date().toISOString(), - handlers_triggered: ['cli_session_pr_review_upsert'], - errors: [ - { - message: error instanceof Error ? error.message : String(error), - handler: 'cli_session_pr_review_upsert', - stack: error instanceof Error ? error.stack : undefined, - }, - ], - }); - } catch { - // Best-effort logging - } - } + errors.push(toWebhookHandlerError('cli_session_pr_review_upsert', error)); } - }); - } + } + + handlersTriggered.push('review_memory_feedback'); + try { + await handleGitHubReviewFeedback({ + payload: parseResult.data, + integration, + deliveryId: eventSignature, + }); + } catch (error) { + logExceptInTest(`Error recording pull_request_review feedback${logSuffix}:`, error); + captureException(error, { + tags: { source: `${sentryPrefix}webhook_review_memory_feedback` }, + }); + errors.push(toWebhookHandlerError('review_memory_feedback', error)); + } + + if (logResult.webhookEventId) { + try { + await updateWebhookEvent(logResult.webhookEventId, { + processed: true, + processed_at: new Date().toISOString(), + handlers_triggered: handlersTriggered, + errors: errors.length > 0 ? errors : null, + }); + } catch { + // Best-effort logging + } + } + }); return NextResponse.json({ message: 'Event received' }, { status: 200 }); } @@ -572,38 +601,151 @@ export async function handleGitHubWebhook( // Process asynchronously to return 200 within GitHub's timeout after(async () => { + const errors: WebhookHandlerError[] = []; try { await handlePRReviewComment(parseResult.data, integration); - if (logResult.webhookEventId) { + } catch (error) { + logExceptInTest(`Error handling PR review comment${logSuffix}:`, error); + captureException(error, { + tags: { source: `${sentryPrefix}webhook_pr_review_comment` }, + }); + errors.push(toWebhookHandlerError('pr_review_comment_fix', error)); + } + + try { + await handleGitHubReviewCommentFeedback({ + payload: parseResult.data, + integration, + deliveryId: eventSignature, + }); + } catch (error) { + logExceptInTest(`Error recording PR review comment feedback${logSuffix}:`, error); + captureException(error, { + tags: { source: `${sentryPrefix}webhook_review_memory_feedback` }, + }); + errors.push(toWebhookHandlerError('review_memory_feedback', error)); + } + + if (logResult.webhookEventId) { + try { await updateWebhookEvent(logResult.webhookEventId, { processed: true, processed_at: new Date().toISOString(), - handlers_triggered: ['pr_review_comment_fix'], - errors: null, + handlers_triggered: ['pr_review_comment_fix', 'review_memory_feedback'], + errors: errors.length > 0 ? errors : null, }); + } catch (error) { + logExceptInTest(`Error updating webhook event${logSuffix}:`, error); } + } + }); + + return NextResponse.json({ message: 'Event received' }, { status: 200 }); + } + + // Handle reaction events for review memory feedback + if (eventType === GITHUB_EVENT.REACTION) { + const parseResult = ReactionPayloadSchema.safeParse(payload); + if (!parseResult.success) { + logExceptInTest(`Invalid reaction payload${logSuffix}:`, parseResult.error); + captureMessage('Invalid GitHub webhook payload structure', { + level: 'error', + tags: { source: `${sentryPrefix}webhook_validation`, event: 'reaction' }, + extra: { errors: parseResult.error.issues }, + }); + return NextResponse.json({ error: 'Invalid payload' }, { status: 400 }); + } + + const action = parseResult.data.action; + const logResult = await logWebhook(integration, action); + if (logResult.isDuplicate) { + return NextResponse.json({ message: 'Duplicate event' }, { status: 200 }); + } + + after(async () => { + const errors: WebhookHandlerError[] = []; + try { + await handleGitHubReactionFeedback({ + payload: parseResult.data, + integration, + deliveryId: eventSignature, + }); } catch (error) { - logExceptInTest(`Error handling PR review comment${logSuffix}:`, error); + logExceptInTest(`Error recording reaction feedback${logSuffix}:`, error); captureException(error, { - tags: { source: `${sentryPrefix}webhook_pr_review_comment` }, + tags: { source: `${sentryPrefix}webhook_review_memory_feedback` }, }); - if (logResult.webhookEventId) { - try { - await updateWebhookEvent(logResult.webhookEventId, { - processed: true, - processed_at: new Date().toISOString(), - handlers_triggered: ['pr_review_comment_fix'], - errors: [ - { - message: error instanceof Error ? error.message : String(error), - handler: 'pr_review_comment_fix', - stack: error instanceof Error ? error.stack : undefined, - }, - ], - }); - } catch { - // Best-effort logging - } + errors.push(toWebhookHandlerError('review_memory_feedback', error)); + } + + if (logResult.webhookEventId) { + try { + await updateWebhookEvent(logResult.webhookEventId, { + processed: true, + processed_at: new Date().toISOString(), + handlers_triggered: ['review_memory_feedback'], + errors: errors.length > 0 ? errors : null, + }); + } catch (error) { + logExceptInTest(`Error updating webhook event${logSuffix}:`, error); + } + } + }); + + return NextResponse.json({ message: 'Event received' }, { status: 200 }); + } + + // Handle pull_request_review_thread events for review memory feedback + if (eventType === GITHUB_EVENT.PULL_REQUEST_REVIEW_THREAD) { + const parseResult = PullRequestReviewThreadPayloadSchema.safeParse(payload); + if (!parseResult.success) { + logExceptInTest( + `Invalid pull_request_review_thread payload${logSuffix}:`, + parseResult.error + ); + captureMessage('Invalid GitHub webhook payload structure', { + level: 'error', + tags: { + source: `${sentryPrefix}webhook_validation`, + event: 'pull_request_review_thread', + }, + extra: { errors: parseResult.error.issues }, + }); + return NextResponse.json({ error: 'Invalid payload' }, { status: 400 }); + } + + const action = parseResult.data.action; + const logResult = await logWebhook(integration, action); + if (logResult.isDuplicate) { + return NextResponse.json({ message: 'Duplicate event' }, { status: 200 }); + } + + after(async () => { + const errors: WebhookHandlerError[] = []; + try { + await handleGitHubReviewThreadFeedback({ + payload: parseResult.data, + integration, + deliveryId: eventSignature, + }); + } catch (error) { + logExceptInTest(`Error recording review thread feedback${logSuffix}:`, error); + captureException(error, { + tags: { source: `${sentryPrefix}webhook_review_memory_feedback` }, + }); + errors.push(toWebhookHandlerError('review_memory_feedback', error)); + } + + if (logResult.webhookEventId) { + try { + await updateWebhookEvent(logResult.webhookEventId, { + processed: true, + processed_at: new Date().toISOString(), + handlers_triggered: ['review_memory_feedback'], + errors: errors.length > 0 ? errors : null, + }); + } catch (error) { + logExceptInTest(`Error updating webhook event${logSuffix}:`, error); } } }); diff --git a/apps/web/src/lib/integrations/platforms/github/webhook-schemas.ts b/apps/web/src/lib/integrations/platforms/github/webhook-schemas.ts index 7b7fbe5b59..8c2e9d11b3 100644 --- a/apps/web/src/lib/integrations/platforms/github/webhook-schemas.ts +++ b/apps/web/src/lib/integrations/platforms/github/webhook-schemas.ts @@ -209,9 +209,11 @@ export const PullRequestReviewCommentPayloadSchema = z.object({ action: z.string(), comment: z.object({ id: z.number().int(), + in_reply_to_id: z.number().int().nullable().optional(), body: z.string(), user: z.object({ login: z.string(), + type: z.string().optional(), }), html_url: z.string(), path: z.string(), @@ -276,6 +278,73 @@ export const PullRequestReviewPayloadSchema = z.object({ installation: z.object({ id: z.number() }), }); +export const ReactionPayloadSchema = z.object({ + action: z.enum(['created', 'deleted']), + reaction: z.object({ + id: z.number().int(), + content: z.string(), + created_at: z.string().optional(), + user: z.object({ login: z.string(), type: z.string().optional() }).optional(), + }), + comment: z + .object({ + id: z.number().int(), + body: z.string().optional().default(''), + html_url: z.string().optional(), + path: z.string().optional(), + line: z.number().nullable().optional(), + diff_hunk: z.string().optional(), + user: z.object({ login: z.string(), type: z.string().optional() }).optional(), + }) + .optional(), + issue: z + .object({ + number: z.number(), + html_url: z.string().optional(), + pull_request: z.object({ url: z.string().optional() }).optional(), + }) + .optional(), + pull_request: z + .object({ + number: z.number(), + html_url: z.string().optional(), + head: z.object({ sha: z.string().optional(), ref: z.string().optional() }).optional(), + }) + .optional(), + repository: GitHubRepositorySchema, + installation: z.object({ id: z.number() }), + sender: z.object({ login: z.string(), type: z.string().optional() }).optional(), +}); + +export const PullRequestReviewThreadPayloadSchema = z.object({ + action: z.enum(['resolved', 'unresolved']), + thread: z.object({ + id: z.string(), + is_resolved: z.boolean().optional(), + comments: z + .array( + z.object({ + id: z.number().int(), + body: z.string(), + html_url: z.string().optional(), + path: z.string().optional(), + line: z.number().nullable().optional(), + diff_hunk: z.string().optional(), + user: z.object({ login: z.string(), type: z.string().optional() }).optional(), + }) + ) + .default([]), + }), + pull_request: z.object({ + number: z.number(), + html_url: z.string().optional(), + head: z.object({ sha: z.string().optional(), ref: z.string().optional() }).optional(), + }), + repository: GitHubRepositorySchema, + installation: z.object({ id: z.number() }), + sender: z.object({ login: z.string(), type: z.string().optional() }).optional(), +}); + // Type exports for use in the webhook handler export type InstallationCreatedPayload = z.infer; export type InstallationDeletedPayload = z.infer; @@ -287,4 +356,6 @@ export type PullRequestPayload = z.infer; export type IssuePayload = z.infer; export type PullRequestReviewCommentPayload = z.infer; export type PullRequestReviewPayload = z.infer; +export type ReactionPayload = z.infer; +export type PullRequestReviewThreadPayload = z.infer; export type GitHubAuthorAssociation = z.infer; From 0bffe78d816af15d6c6af1d882cf129041c721f0 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Fri, 22 May 2026 22:21:39 +0300 Subject: [PATCH 04/23] feat(review-memory): ingest GitLab feedback signals --- .../src/app/api/webhooks/gitlab/route.test.ts | 228 +++++++++ apps/web/src/app/api/webhooks/gitlab/route.ts | 142 +++++- .../src/lib/code-reviews/review-memory/db.ts | 61 +++ .../review-memory/gitlab-feedback.test.ts | 246 +++++++++ .../review-memory/gitlab-feedback.ts | 470 ++++++++++++++++++ .../src/lib/integrations/core/constants.ts | 1 + .../platforms/gitlab/adapter.test.ts | 95 ++++ .../integrations/platforms/gitlab/adapter.ts | 7 +- .../platforms/gitlab/webhook-schemas.ts | 46 ++ 9 files changed, 1289 insertions(+), 7 deletions(-) create mode 100644 apps/web/src/app/api/webhooks/gitlab/route.test.ts create mode 100644 apps/web/src/lib/code-reviews/review-memory/gitlab-feedback.test.ts create mode 100644 apps/web/src/lib/code-reviews/review-memory/gitlab-feedback.ts diff --git a/apps/web/src/app/api/webhooks/gitlab/route.test.ts b/apps/web/src/app/api/webhooks/gitlab/route.test.ts new file mode 100644 index 0000000000..5713562a2f --- /dev/null +++ b/apps/web/src/app/api/webhooks/gitlab/route.test.ts @@ -0,0 +1,228 @@ +import type { NextRequest } from 'next/server'; + +const mockVerifyGitLabWebhookToken = jest.fn((_token: string, _expected?: string) => true); +const mockFindGitLabIntegrationByWebhookToken = jest.fn(); +const mockHandleMergeRequest = jest.fn(); +const mockLogWebhookEvent = jest.fn(); +const mockUpdateWebhookEvent = jest.fn(); +const mockHandleGitLabEmojiFeedback = jest.fn(); +const mockHandleGitLabMergeRequestFeedback = jest.fn(); +const mockHandleGitLabNoteFeedback = jest.fn(); + +jest.mock('@/lib/integrations/platforms/gitlab/adapter', () => ({ + verifyGitLabWebhookToken: (token: string, expected?: string) => + mockVerifyGitLabWebhookToken(token, expected), +})); + +jest.mock('@/lib/integrations/db/platform-integrations', () => ({ + findGitLabIntegrationByWebhookToken: (token: string) => + mockFindGitLabIntegrationByWebhookToken(token), +})); + +jest.mock('@/lib/integrations/platforms/gitlab/webhook-handlers', () => ({ + handleMergeRequest: (payload: unknown, integration: unknown) => + mockHandleMergeRequest(payload, integration), +})); + +jest.mock('@/lib/integrations/db/webhook-events', () => ({ + logWebhookEvent: (data: unknown) => mockLogWebhookEvent(data), + updateWebhookEvent: (eventId: string, updates: unknown) => + mockUpdateWebhookEvent(eventId, updates), +})); + +jest.mock('@/lib/code-reviews/review-memory/gitlab-feedback', () => ({ + handleGitLabEmojiFeedback: (input: unknown) => mockHandleGitLabEmojiFeedback(input), + handleGitLabMergeRequestFeedback: (input: unknown) => mockHandleGitLabMergeRequestFeedback(input), + handleGitLabNoteFeedback: (input: unknown) => mockHandleGitLabNoteFeedback(input), +})); + +import { POST } from './route'; + +const integration = { + id: 'pi_gitlab', + owned_by_organization_id: 'org_1', + owned_by_user_id: null, + suspended_at: null, + metadata: { webhook_secret: 'secret' }, +}; + +function gitLabRequest(eventType: string, payload: unknown): NextRequest { + return new Request('https://app.example.com/api/webhooks/gitlab', { + method: 'POST', + headers: { + 'content-type': 'application/json', + 'x-gitlab-token': 'secret', + 'x-gitlab-event': eventType, + 'x-gitlab-event-uuid': `delivery-${eventType}`, + }, + body: JSON.stringify(payload), + }) as NextRequest; +} + +function user() { + return { id: 7, name: 'Maintainer', username: 'maintainer' }; +} + +function project() { + return { + id: 123, + name: 'widgets', + web_url: 'https://gitlab.example.com/acme/widgets', + namespace: 'acme', + path_with_namespace: 'acme/widgets', + default_branch: 'main', + }; +} + +function mergeRequestAttributes(overrides: Record = {}) { + return { + id: 900, + iid: 42, + title: 'Add widgets', + state: 'opened', + action: 'update', + source_branch: 'feature/widgets', + target_branch: 'main', + source_project_id: 123, + target_project_id: 123, + author_id: 7, + created_at: '2026-01-01T00:00:00.000Z', + updated_at: '2026-01-01T00:01:00.000Z', + url: 'https://gitlab.example.com/acme/widgets/-/merge_requests/42', + last_commit: { + id: 'abc123', + message: 'Add widgets', + }, + ...overrides, + }; +} + +function mergeRequestPayload(overrides: Record = {}) { + return { + object_kind: 'merge_request', + event_type: 'merge_request', + user: user(), + project: project(), + object_attributes: mergeRequestAttributes(), + ...overrides, + }; +} + +function notePayload(overrides: Record = {}) { + return { + object_kind: 'note', + event_type: 'note', + user: user(), + project_id: 123, + project: project(), + object_attributes: { + id: 501, + note: 'This is a false positive.', + action: 'create', + discussion_id: 'discussion-1', + noteable_type: 'MergeRequest', + author_id: 7, + created_at: '2026-01-01T00:02:00.000Z', + updated_at: '2026-01-01T00:02:00.000Z', + project_id: 123, + system: false, + url: 'https://gitlab.example.com/acme/widgets/-/merge_requests/42#note_501', + }, + merge_request: mergeRequestAttributes(), + ...overrides, + }; +} + +function emojiPayload(overrides: Record = {}) { + return { + object_kind: 'emoji', + event_type: 'emoji', + user: user(), + project_id: 123, + project: project(), + object_attributes: { + id: 777, + name: 'thumbsdown', + action: 'award', + awardable_type: 'Note', + awardable_id: 500, + created_at: '2026-01-01T00:02:00.000Z', + }, + merge_request: mergeRequestAttributes(), + note: { + id: 500, + note: '**WARNING**: Avoid this pattern', + url: 'https://gitlab.example.com/acme/widgets/-/merge_requests/42#note_500', + noteable_type: 'MergeRequest', + }, + ...overrides, + }; +} + +describe('GitLab webhook route', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockVerifyGitLabWebhookToken.mockReturnValue(true); + mockFindGitLabIntegrationByWebhookToken.mockResolvedValue(integration); + mockHandleMergeRequest.mockResolvedValue(Response.json({ message: 'Event received' })); + mockLogWebhookEvent.mockResolvedValue({ id: 'we_1', isDuplicate: false }); + mockUpdateWebhookEvent.mockResolvedValue(undefined); + mockHandleGitLabEmojiFeedback.mockResolvedValue({ recorded: true, eventIds: ['event_1'] }); + mockHandleGitLabMergeRequestFeedback.mockResolvedValue({ + recorded: true, + eventIds: ['event_1'], + }); + mockHandleGitLabNoteFeedback.mockResolvedValue({ recorded: true, eventIds: ['event_1'] }); + }); + + it('routes merge request events to code review and review memory feedback', async () => { + const response = await POST(gitLabRequest('Merge Request Hook', mergeRequestPayload())); + + expect(response.status).toBe(200); + expect(mockHandleMergeRequest).toHaveBeenCalledWith( + expect.objectContaining({ object_kind: 'merge_request' }), + integration + ); + expect(mockHandleGitLabMergeRequestFeedback).toHaveBeenCalledWith({ + payload: expect.objectContaining({ object_kind: 'merge_request' }), + integration, + deliveryId: 'delivery-Merge Request Hook', + }); + expect(mockUpdateWebhookEvent).toHaveBeenCalledWith( + 'we_1', + expect.objectContaining({ handlers_triggered: ['code_review', 'review_memory_feedback'] }) + ); + }); + + it('routes note events to review memory feedback', async () => { + const response = await POST(gitLabRequest('Note Hook', notePayload())); + + expect(response.status).toBe(200); + expect(mockHandleMergeRequest).not.toHaveBeenCalled(); + expect(mockHandleGitLabNoteFeedback).toHaveBeenCalledWith({ + payload: expect.objectContaining({ object_kind: 'note' }), + integration, + deliveryId: 'delivery-Note Hook', + }); + expect(mockUpdateWebhookEvent).toHaveBeenCalledWith( + 'we_1', + expect.objectContaining({ handlers_triggered: ['review_memory_feedback'] }) + ); + }); + + it('routes emoji events to review memory feedback', async () => { + const response = await POST(gitLabRequest('Emoji Hook', emojiPayload())); + + expect(response.status).toBe(200); + expect(mockHandleMergeRequest).not.toHaveBeenCalled(); + expect(mockHandleGitLabEmojiFeedback).toHaveBeenCalledWith({ + payload: expect.objectContaining({ object_kind: 'emoji' }), + integration, + deliveryId: 'delivery-Emoji Hook', + }); + expect(mockUpdateWebhookEvent).toHaveBeenCalledWith( + 'we_1', + expect.objectContaining({ handlers_triggered: ['review_memory_feedback'] }) + ); + }); +}); diff --git a/apps/web/src/app/api/webhooks/gitlab/route.ts b/apps/web/src/app/api/webhooks/gitlab/route.ts index 50d20c5118..c82078b5a5 100644 --- a/apps/web/src/app/api/webhooks/gitlab/route.ts +++ b/apps/web/src/app/api/webhooks/gitlab/route.ts @@ -2,7 +2,11 @@ import type { NextRequest } from 'next/server'; import { NextResponse } from 'next/server'; import { captureException, captureMessage } from '@sentry/nextjs'; import { verifyGitLabWebhookToken } from '@/lib/integrations/platforms/gitlab/adapter'; -import { MergeRequestPayloadSchema } from '@/lib/integrations/platforms/gitlab/webhook-schemas'; +import { + EmojiEventPayloadSchema, + MergeRequestPayloadSchema, + NoteEventPayloadSchema, +} from '@/lib/integrations/platforms/gitlab/webhook-schemas'; import { findGitLabIntegrationByWebhookToken } from '@/lib/integrations/db/platform-integrations'; import { handleMergeRequest } from '@/lib/integrations/platforms/gitlab/webhook-handlers'; import { PLATFORM, GITLAB_EVENT, GITLAB_ACTION } from '@/lib/integrations/core/constants'; @@ -10,6 +14,25 @@ import { logExceptInTest } from '@/lib/utils.server'; import { logWebhookEvent, updateWebhookEvent } from '@/lib/integrations/db/webhook-events'; import type { Owner } from '@/lib/integrations/core/types'; import { redactSensitiveHeaders } from '@kilocode/worker-utils/redact-headers'; +import { + handleGitLabEmojiFeedback, + handleGitLabMergeRequestFeedback, + handleGitLabNoteFeedback, +} from '@/lib/code-reviews/review-memory/gitlab-feedback'; + +type WebhookHandlerError = { + message: string; + handler: string; + stack?: string; +}; + +function toWebhookHandlerError(handler: string, error: unknown): WebhookHandlerError { + return { + message: error instanceof Error ? error.message : String(error), + handler, + stack: error instanceof Error ? error.stack : undefined, + }; +} /** * GitLab Webhook Handler @@ -143,16 +166,31 @@ export async function POST(request: NextRequest) { return NextResponse.json({ message: 'Duplicate event' }, { status: 200 }); } + const errors: WebhookHandlerError[] = []; const result = await handleMergeRequest(parseResult.data, integration); + try { + await handleGitLabMergeRequestFeedback({ + payload: parseResult.data, + integration, + deliveryId: eventSignature, + }); + } catch (error) { + logExceptInTest('Error recording GitLab merge request feedback:', error); + captureException(error, { + tags: { source: 'gitlab_webhook_review_memory_feedback' }, + }); + errors.push(toWebhookHandlerError('review_memory_feedback', error)); + } + // Mark webhook event as processed if (logResult.webhookEventId) { try { await updateWebhookEvent(logResult.webhookEventId, { processed: true, processed_at: new Date().toISOString(), - handlers_triggered: ['code_review'], - errors: null, + handlers_triggered: ['code_review', 'review_memory_feedback'], + errors: errors.length > 0 ? errors : null, }); } catch (error) { logExceptInTest('Error updating webhook event:', error); @@ -168,9 +206,103 @@ export async function POST(request: NextRequest) { return NextResponse.json({ message: 'Event received' }, { status: 200 }); } - // Handle Note (comment) events (for future use - e.g., responding to review comments) + // Handle Note (comment) events for review memory feedback if (eventType === GITLAB_EVENT.NOTE) { - logExceptInTest('Note event received, not yet implemented'); + const parseResult = NoteEventPayloadSchema.safeParse(payload); + if (!parseResult.success) { + logExceptInTest('Invalid note payload:', parseResult.error); + captureMessage('Invalid GitLab webhook payload structure', { + level: 'error', + tags: { source: 'gitlab_webhook_validation', event: 'note' }, + extra: { errors: parseResult.error.issues }, + }); + return NextResponse.json({ error: 'Invalid payload' }, { status: 400 }); + } + + const action = parseResult.data.object_attributes.action ?? 'note'; + const logResult = await logWebhook(action); + if (logResult.isDuplicate) { + return NextResponse.json({ message: 'Duplicate event' }, { status: 200 }); + } + + const errors: WebhookHandlerError[] = []; + try { + await handleGitLabNoteFeedback({ + payload: parseResult.data, + integration, + deliveryId: eventSignature, + }); + } catch (error) { + logExceptInTest('Error recording GitLab note feedback:', error); + captureException(error, { + tags: { source: 'gitlab_webhook_review_memory_feedback' }, + }); + errors.push(toWebhookHandlerError('review_memory_feedback', error)); + } + + if (logResult.webhookEventId) { + try { + await updateWebhookEvent(logResult.webhookEventId, { + processed: true, + processed_at: new Date().toISOString(), + handlers_triggered: ['review_memory_feedback'], + errors: errors.length > 0 ? errors : null, + }); + } catch (error) { + logExceptInTest('Error updating webhook event:', error); + } + } + + return NextResponse.json({ message: 'Event received' }, { status: 200 }); + } + + // Handle Emoji events for review memory feedback + if (eventType === GITLAB_EVENT.EMOJI) { + const parseResult = EmojiEventPayloadSchema.safeParse(payload); + if (!parseResult.success) { + logExceptInTest('Invalid emoji payload:', parseResult.error); + captureMessage('Invalid GitLab webhook payload structure', { + level: 'error', + tags: { source: 'gitlab_webhook_validation', event: 'emoji' }, + extra: { errors: parseResult.error.issues }, + }); + return NextResponse.json({ error: 'Invalid payload' }, { status: 400 }); + } + + const action = parseResult.data.object_attributes.action ?? 'emoji'; + const logResult = await logWebhook(action); + if (logResult.isDuplicate) { + return NextResponse.json({ message: 'Duplicate event' }, { status: 200 }); + } + + const errors: WebhookHandlerError[] = []; + try { + await handleGitLabEmojiFeedback({ + payload: parseResult.data, + integration, + deliveryId: eventSignature, + }); + } catch (error) { + logExceptInTest('Error recording GitLab emoji feedback:', error); + captureException(error, { + tags: { source: 'gitlab_webhook_review_memory_feedback' }, + }); + errors.push(toWebhookHandlerError('review_memory_feedback', error)); + } + + if (logResult.webhookEventId) { + try { + await updateWebhookEvent(logResult.webhookEventId, { + processed: true, + processed_at: new Date().toISOString(), + handlers_triggered: ['review_memory_feedback'], + errors: errors.length > 0 ? errors : null, + }); + } catch (error) { + logExceptInTest('Error updating webhook event:', error); + } + } + return NextResponse.json({ message: 'Event received' }, { status: 200 }); } diff --git a/apps/web/src/lib/code-reviews/review-memory/db.ts b/apps/web/src/lib/code-reviews/review-memory/db.ts index 8bb013a228..da3bb7bb45 100644 --- a/apps/web/src/lib/code-reviews/review-memory/db.ts +++ b/apps/web/src/lib/code-reviews/review-memory/db.ts @@ -239,6 +239,67 @@ export async function findFeedbackSubject( return subject ?? null; } +export type FindFeedbackSubjectByExternalThreadInput = { + owner: ReviewMemoryOwner; + platform: ReviewMemoryPlatform; + repoFullName: string; + subjectType: ReviewMemorySubjectType; + externalThreadId: string; + database?: ReviewMemoryDatabase; +}; + +export async function findFeedbackSubjectByExternalThreadId( + input: FindFeedbackSubjectByExternalThreadInput +): Promise { + const database = databaseOrDefault(input.database); + const [subject] = await database + .select() + .from(code_review_feedback_subjects) + .where( + and( + subjectOwnerWhere(input.owner), + eq(code_review_feedback_subjects.platform, input.platform), + eq(code_review_feedback_subjects.repo_full_name, input.repoFullName), + eq(code_review_feedback_subjects.subject_type, input.subjectType), + eq(code_review_feedback_subjects.external_thread_id, input.externalThreadId) + ) + ) + .limit(1); + + return subject ?? null; +} + +export type ListFeedbackSubjectsForPullRequestInput = { + owner: ReviewMemoryOwner; + platform: ReviewMemoryPlatform; + repoFullName: string; + prNumber: number; + subjectTypes?: ReviewMemorySubjectType[]; + database?: ReviewMemoryDatabase; +}; + +export async function listFeedbackSubjectsForPullRequest( + input: ListFeedbackSubjectsForPullRequestInput +): Promise { + const database = databaseOrDefault(input.database); + const conditions = [ + subjectOwnerWhere(input.owner), + eq(code_review_feedback_subjects.platform, input.platform), + eq(code_review_feedback_subjects.repo_full_name, input.repoFullName), + eq(code_review_feedback_subjects.pr_number, input.prNumber), + ] satisfies SQL[]; + + if (input.subjectTypes && input.subjectTypes.length > 0) { + conditions.push(inArray(code_review_feedback_subjects.subject_type, input.subjectTypes)); + } + + return await database + .select() + .from(code_review_feedback_subjects) + .where(and(...conditions)) + .orderBy(desc(code_review_feedback_subjects.last_seen_at)); +} + export type RecordFeedbackEventInput = { owner: ReviewMemoryOwner; platform: ReviewMemoryPlatform; diff --git a/apps/web/src/lib/code-reviews/review-memory/gitlab-feedback.test.ts b/apps/web/src/lib/code-reviews/review-memory/gitlab-feedback.test.ts new file mode 100644 index 0000000000..77963ab5e9 --- /dev/null +++ b/apps/web/src/lib/code-reviews/review-memory/gitlab-feedback.test.ts @@ -0,0 +1,246 @@ +/* eslint-disable drizzle/enforce-delete-with-where */ +import { db } from '@/lib/drizzle'; +import type { + EmojiEventPayload, + MergeRequestPayload, + NoteEventPayload, +} from '@/lib/integrations/platforms/gitlab/webhook-schemas'; +import { insertTestUser } from '@/tests/helpers/user.helper'; +import { + code_review_feedback_events, + code_review_feedback_subjects, + code_review_memory_aggregation_state, + kilocode_users, + platform_integrations, +} from '@kilocode/db/schema'; +import { eq } from 'drizzle-orm'; +import { + handleGitLabEmojiFeedback, + handleGitLabMergeRequestFeedback, + handleGitLabNoteFeedback, +} from './gitlab-feedback'; +import { upsertFeedbackSubject, type ReviewMemoryOwner } from './db'; + +describe('GitLab review memory feedback', () => { + afterEach(async () => { + await db.delete(code_review_feedback_events); + await db.delete(code_review_feedback_subjects); + await db.delete(code_review_memory_aggregation_state); + await db.delete(platform_integrations); + await db.delete(kilocode_users); + }); + + async function seedIntegration() { + const user = await insertTestUser(); + const [integration] = await db + .insert(platform_integrations) + .values({ + owned_by_user_id: user.id, + platform: 'gitlab', + integration_type: 'oauth', + platform_installation_id: `gitlab-review-memory-${Date.now()}-${Math.random()}`, + metadata: { + gitlab_instance_url: 'https://gitlab.example.com', + webhook_secret: 'secret', + }, + }) + .returning(); + + if (!integration) throw new Error('Failed to seed platform integration'); + return { user, owner: { type: 'user' as const, id: user.id }, integration }; + } + + function user() { + return { id: 7, name: 'Maintainer', username: 'maintainer' }; + } + + function project() { + return { + id: 123, + name: 'widgets', + web_url: 'https://gitlab.example.com/acme/widgets', + namespace: 'acme', + path_with_namespace: 'acme/widgets', + default_branch: 'main', + }; + } + + function mergeRequestAttributes( + overrides: Partial = {} + ): MergeRequestPayload['object_attributes'] { + return { + id: 900, + iid: 42, + title: 'Add widgets', + state: 'opened', + action: 'update', + source_branch: 'feature/widgets', + target_branch: 'main', + source_project_id: 123, + target_project_id: 123, + author_id: 7, + created_at: '2026-01-01T00:00:00.000Z', + updated_at: '2026-01-01T00:01:00.000Z', + url: 'https://gitlab.example.com/acme/widgets/-/merge_requests/42', + last_commit: { + id: 'abc123', + message: 'Add widgets', + }, + ...overrides, + }; + } + + async function seedDiscussionSubject(owner: ReviewMemoryOwner) { + return await upsertFeedbackSubject({ + owner, + platform: 'gitlab', + repoFullName: 'acme/widgets', + platformProjectId: 123, + subjectType: 'discussion', + externalId: '500', + externalThreadId: 'discussion-1', + externalUrl: 'https://gitlab.example.com/acme/widgets/-/merge_requests/42#note_500', + prNumber: 42, + prUrl: 'https://gitlab.example.com/acme/widgets/-/merge_requests/42', + bodyExcerpt: '**WARNING**: Avoid this pattern', + state: 'active', + }); + } + + it('records corrective notes in Kilo discussions', async () => { + const { owner, integration } = await seedIntegration(); + const subject = await seedDiscussionSubject(owner); + const payload = { + object_kind: 'note', + event_type: 'note', + user: user(), + project_id: 123, + project: project(), + object_attributes: { + id: 501, + note: 'This is a false positive for this MR.', + action: 'create', + discussion_id: 'discussion-1', + noteable_type: 'MergeRequest', + author_id: 7, + created_at: '2026-01-01T00:02:00.000Z', + updated_at: '2026-01-01T00:02:00.000Z', + project_id: 123, + system: false, + url: 'https://gitlab.example.com/acme/widgets/-/merge_requests/42#note_501', + }, + merge_request: mergeRequestAttributes(), + } satisfies NoteEventPayload; + + const result = await handleGitLabNoteFeedback({ + payload, + integration, + deliveryId: 'delivery-note-corrective', + }); + + expect(result.recorded).toBe(true); + const [event] = await db.select().from(code_review_feedback_events); + expect(event).toEqual( + expect.objectContaining({ + subject_id: subject.id, + signal_kind: 'corrective_reply', + sentiment: 'negative', + strength: 3, + }) + ); + }); + + it('records emoji reactions on Kilo notes', async () => { + const { integration } = await seedIntegration(); + const payload = { + object_kind: 'emoji', + event_type: 'emoji', + user: user(), + project_id: 123, + project: project(), + object_attributes: { + id: 777, + name: 'thumbsdown', + action: 'award', + awardable_type: 'Note', + awardable_id: 500, + created_at: '2026-01-01T00:02:00.000Z', + }, + merge_request: mergeRequestAttributes(), + note: { + id: 500, + note: '**WARNING**: Avoid this pattern', + url: 'https://gitlab.example.com/acme/widgets/-/merge_requests/42#note_500', + noteable_type: 'MergeRequest', + position: { + new_path: 'src/widget.ts', + new_line: 12, + }, + }, + } satisfies EmojiEventPayload; + + const result = await handleGitLabEmojiFeedback({ + payload, + integration, + deliveryId: 'delivery-emoji-negative', + }); + + expect(result.recorded).toBe(true); + const [event] = await db.select().from(code_review_feedback_events); + expect(event).toEqual( + expect.objectContaining({ + signal_kind: 'negative_reaction', + sentiment: 'negative', + strength: 3, + }) + ); + + const [subject] = await db.select().from(code_review_feedback_subjects); + expect(subject.external_id).toBe('500'); + expect(subject.severity).toBe('warning'); + }); + + it('records MR-level approval evidence and discussion resolution', async () => { + const { owner, integration } = await seedIntegration(); + const subject = await seedDiscussionSubject(owner); + const payload = { + object_kind: 'merge_request', + event_type: 'merge_request', + user: user(), + project: project(), + object_attributes: mergeRequestAttributes({ action: 'approved' }), + changes: { + blocking_discussions_resolved: { + previous: false, + current: true, + }, + }, + } satisfies MergeRequestPayload; + + const result = await handleGitLabMergeRequestFeedback({ + payload, + integration, + deliveryId: 'delivery-mr-approval-resolution', + }); + + expect(result.recorded).toBe(true); + const events = await db.select().from(code_review_feedback_events); + expect(events).toEqual( + expect.arrayContaining([ + expect.objectContaining({ signal_kind: 'mr_approved', sentiment: 'positive', strength: 1 }), + expect.objectContaining({ + subject_id: subject.id, + signal_kind: 'thread_resolved', + sentiment: 'positive', + strength: 2, + }), + ]) + ); + + const [updatedSubject] = await db + .select() + .from(code_review_feedback_subjects) + .where(eq(code_review_feedback_subjects.id, subject.id)); + expect(updatedSubject.state).toBe('resolved'); + }); +}); diff --git a/apps/web/src/lib/code-reviews/review-memory/gitlab-feedback.ts b/apps/web/src/lib/code-reviews/review-memory/gitlab-feedback.ts new file mode 100644 index 0000000000..170ad5e219 --- /dev/null +++ b/apps/web/src/lib/code-reviews/review-memory/gitlab-feedback.ts @@ -0,0 +1,470 @@ +import type { CodeReviewFeedbackSubject, PlatformIntegration } from '@kilocode/db/schema'; +import type { + EmojiEventPayload, + MergeRequestPayload, + NoteEventPayload, +} from '@/lib/integrations/platforms/gitlab/webhook-schemas'; +import { + findFeedbackSubject, + findFeedbackSubjectByExternalThreadId, + listFeedbackSubjectsForPullRequest, + recordFeedbackEvent, + upsertFeedbackSubject, + type ReviewMemoryOwner, +} from './db'; +import { isLikelyKiloInlineReviewBody, parseReviewFindingMetadata } from './sync-subjects'; + +type GitLabFeedbackResult = { + recorded: boolean; + eventIds: string[]; + reason?: string; +}; + +type GitLabActor = + | { + name?: string; + username?: string; + } + | null + | undefined; + +const POSITIVE_EMOJI = new Set(['thumbsup', '+1', 'heart', 'rocket', 'tada']); +const NEGATIVE_EMOJI = new Set(['thumbsdown', '-1', 'confused']); +const CREATE_EMOJI_ACTIONS = new Set(['award', 'create', 'created']); + +function skipped(reason: string): GitLabFeedbackResult { + return { recorded: false, eventIds: [], reason }; +} + +function ownerFromIntegration(integration: PlatformIntegration): ReviewMemoryOwner | null { + if (integration.owned_by_organization_id) { + return { type: 'org', id: integration.owned_by_organization_id }; + } + if (integration.owned_by_user_id) { + return { type: 'user', id: integration.owned_by_user_id }; + } + return null; +} + +function isLikelyKiloActor(actor: GitLabActor): boolean { + const username = actor?.username?.toLowerCase() ?? ''; + const name = actor?.name?.toLowerCase() ?? ''; + return username.includes('kilo') || name.includes('kilo') || username.endsWith('bot'); +} + +function classifyGitLabNoteFeedback(body: string): { + signalKind: 'corrective_reply' | 'supportive_reply'; + sentiment: 'positive' | 'negative' | 'neutral'; + strength: number; +} { + const normalized = body.toLowerCase(); + const correctivePatterns = [ + 'false positive', + 'not an issue', + 'incorrect', + 'wrong', + 'this is expected', + 'expected behavior', + 'does not apply', + 'please remove', + 'unhelpful', + ]; + if (correctivePatterns.some(pattern => normalized.includes(pattern))) { + return { signalKind: 'corrective_reply', sentiment: 'negative', strength: 3 }; + } + + const supportivePatterns = [ + 'good catch', + 'nice catch', + 'thanks', + 'thank you', + 'fixed', + 'resolved', + 'agreed', + 'makes sense', + ]; + if (supportivePatterns.some(pattern => normalized.includes(pattern))) { + return { signalKind: 'supportive_reply', sentiment: 'positive', strength: 2 }; + } + + return { signalKind: 'supportive_reply', sentiment: 'neutral', strength: 1 }; +} + +function emojiSentiment(name: string): { + signalKind: 'positive_reaction' | 'negative_reaction'; + sentiment: 'positive' | 'negative'; + strength: number; +} | null { + if (POSITIVE_EMOJI.has(name)) { + return { signalKind: 'positive_reaction', sentiment: 'positive', strength: 2 }; + } + if (NEGATIVE_EMOJI.has(name)) { + return { signalKind: 'negative_reaction', sentiment: 'negative', strength: 3 }; + } + return null; +} + +async function findExistingGitLabNoteSubject(params: { + owner: ReviewMemoryOwner; + repoFullName: string; + noteId: number; +}): Promise { + return ( + (await findFeedbackSubject({ + owner: params.owner, + platform: 'gitlab', + repoFullName: params.repoFullName, + subjectType: 'discussion', + externalId: String(params.noteId), + })) ?? + (await findFeedbackSubject({ + owner: params.owner, + platform: 'gitlab', + repoFullName: params.repoFullName, + subjectType: 'summary_comment', + externalId: String(params.noteId), + })) + ); +} + +async function upsertKiloNoteSubject(params: { + owner: ReviewMemoryOwner; + integration: PlatformIntegration; + repoFullName: string; + platformProjectId: number; + prNumber: number | null; + prUrl: string | null; + headSha?: string | null; + note: { + id: number; + body: string; + url?: string | null; + discussionId?: string | null; + filePath?: string | null; + lineNumber?: number | null; + diffHunk?: string | null; + }; +}): Promise { + const isSummary = params.note.body.includes(''); + const isDiscussion = isLikelyKiloInlineReviewBody(params.note.body); + if (!isSummary && !isDiscussion) return null; + + const metadata = isDiscussion ? parseReviewFindingMetadata(params.note.body) : null; + return await upsertFeedbackSubject({ + owner: params.owner, + platform: 'gitlab', + platformIntegrationId: params.integration.id, + subjectType: isSummary ? 'summary_comment' : 'discussion', + externalId: String(params.note.id), + externalThreadId: params.note.discussionId ?? null, + externalUrl: params.note.url ?? null, + repoFullName: params.repoFullName, + platformProjectId: params.platformProjectId, + prNumber: params.prNumber, + prUrl: params.prUrl, + headSha: params.headSha, + filePath: params.note.filePath ?? null, + lineNumber: params.note.lineNumber ?? null, + diffHunk: params.note.diffHunk ?? null, + bodyExcerpt: params.note.body, + severity: metadata?.severity ?? null, + findingTitle: metadata?.findingTitle ?? null, + findingFingerprint: metadata?.findingFingerprint ?? null, + state: 'active', + }); +} + +function positionFilePath( + position: NoteEventPayload['object_attributes']['position'] +): string | null { + return position?.new_path ?? position?.old_path ?? null; +} + +function positionLine(position: NoteEventPayload['object_attributes']['position']): number | null { + return position?.new_line ?? position?.old_line ?? null; +} + +export async function handleGitLabNoteFeedback(input: { + payload: NoteEventPayload; + integration: PlatformIntegration; + deliveryId: string; +}): Promise { + const note = input.payload.object_attributes; + if (note.noteable_type !== 'MergeRequest' || !input.payload.merge_request) { + return skipped('not-merge-request-note'); + } + + const owner = ownerFromIntegration(input.integration); + if (!owner) return skipped('missing-owner'); + + const repoFullName = input.payload.project.path_with_namespace; + const prNumber = input.payload.merge_request.iid; + const prUrl = input.payload.merge_request.url; + const action = note.action ?? 'create'; + + if (action === 'update') { + const subject = await upsertKiloNoteSubject({ + owner, + integration: input.integration, + repoFullName, + platformProjectId: input.payload.project.id, + prNumber, + prUrl, + headSha: input.payload.merge_request.last_commit?.id ?? null, + note: { + id: note.id, + body: note.note, + url: note.url, + discussionId: note.discussion_id ?? null, + filePath: positionFilePath(note.position), + lineNumber: positionLine(note.position), + diffHunk: note.st_diff?.diff ?? null, + }, + }); + return subject ? skipped('subject-synced') : skipped('not-kilo-subject'); + } + + if (action !== 'create') return skipped('unsupported-note-action'); + if (note.system) return skipped('system-note'); + if (isLikelyKiloActor(input.payload.user)) return skipped('bot-authored-note'); + + const subject = note.discussion_id + ? await findFeedbackSubjectByExternalThreadId({ + owner, + platform: 'gitlab', + repoFullName, + subjectType: 'discussion', + externalThreadId: note.discussion_id, + }) + : await findExistingGitLabNoteSubject({ owner, repoFullName, noteId: note.id }); + + if (!subject) return skipped('not-kilo-subject'); + + const classification = classifyGitLabNoteFeedback(note.note); + const result = await recordFeedbackEvent({ + owner, + platform: 'gitlab', + platformIntegrationId: input.integration.id, + subjectId: subject.id, + codeReviewId: subject.code_review_id, + repoFullName, + platformProjectId: input.payload.project.id, + prNumber, + prUrl, + eventSource: 'gitlab_webhook', + signalKind: classification.signalKind, + sentiment: classification.sentiment, + strength: classification.strength, + externalEventId: `gitlab:${input.deliveryId}:note:${note.id}`, + externalUrl: note.url, + evidenceExcerpt: note.note, + metadata: { + note_id: note.id, + discussion_id: note.discussion_id, + }, + occurredAt: note.created_at, + }); + + return { recorded: result.created, eventIds: [result.event.id] }; +} + +export async function handleGitLabEmojiFeedback(input: { + payload: EmojiEventPayload; + integration: PlatformIntegration; + deliveryId: string; +}): Promise { + const emoji = input.payload.object_attributes; + const action = emoji.action ?? 'award'; + if (!CREATE_EMOJI_ACTIONS.has(action)) return skipped('emoji-not-created'); + if (isLikelyKiloActor(input.payload.user)) return skipped('bot-authored-emoji'); + if (emoji.awardable_type.toLowerCase() !== 'note') return skipped('unsupported-awardable-type'); + + const owner = ownerFromIntegration(input.integration); + if (!owner) return skipped('missing-owner'); + + const sentiment = emojiSentiment(emoji.name); + if (!sentiment) return skipped('unsupported-emoji'); + + const repoFullName = input.payload.project.path_with_namespace; + const noteId = input.payload.note?.id ?? emoji.awardable_id; + let subject = await findExistingGitLabNoteSubject({ owner, repoFullName, noteId }); + const noteBody = input.payload.note?.note ?? input.payload.note?.body ?? ''; + if (!subject && noteBody) { + subject = await upsertKiloNoteSubject({ + owner, + integration: input.integration, + repoFullName, + platformProjectId: input.payload.project.id, + prNumber: input.payload.merge_request?.iid ?? null, + prUrl: input.payload.merge_request?.url ?? null, + headSha: input.payload.merge_request?.last_commit?.id ?? null, + note: { + id: noteId, + body: noteBody, + url: input.payload.note?.url ?? null, + filePath: input.payload.note?.position?.new_path ?? input.payload.note?.position?.old_path, + lineNumber: + input.payload.note?.position?.new_line ?? input.payload.note?.position?.old_line, + }, + }); + } + + if (!subject) return skipped('not-kilo-subject'); + + const result = await recordFeedbackEvent({ + owner, + platform: 'gitlab', + platformIntegrationId: input.integration.id, + subjectId: subject.id, + codeReviewId: subject.code_review_id, + repoFullName, + platformProjectId: input.payload.project.id, + prNumber: input.payload.merge_request?.iid ?? subject.pr_number, + prUrl: input.payload.merge_request?.url ?? subject.pr_url, + eventSource: 'gitlab_webhook', + signalKind: sentiment.signalKind, + sentiment: sentiment.sentiment, + strength: sentiment.strength, + externalEventId: `gitlab:${input.deliveryId}:emoji:${emoji.id}`, + externalUrl: input.payload.note?.url ?? subject.external_url, + evidenceExcerpt: `Emoji: ${emoji.name}`, + metadata: { emoji: emoji.name, note_id: noteId }, + occurredAt: emoji.created_at ?? null, + }); + + return { recorded: result.created, eventIds: [result.event.id] }; +} + +async function recordGitLabMrLevelSignal(input: { + payload: MergeRequestPayload; + integration: PlatformIntegration; + owner: ReviewMemoryOwner; + deliveryId: string; + signalKind: 'mr_approved' | 'mr_unapproved'; + sentiment: 'positive' | 'negative'; + evidenceExcerpt: string; +}) { + return await recordFeedbackEvent({ + owner: input.owner, + platform: 'gitlab', + platformIntegrationId: input.integration.id, + repoFullName: input.payload.project.path_with_namespace, + platformProjectId: input.payload.project.id, + prNumber: input.payload.object_attributes.iid, + prUrl: input.payload.object_attributes.url, + eventSource: 'gitlab_webhook', + signalKind: input.signalKind, + sentiment: input.sentiment, + strength: 1, + externalEventId: `gitlab:${input.deliveryId}:merge-request:${input.payload.object_attributes.id}:${input.signalKind}`, + externalUrl: input.payload.object_attributes.url, + evidenceExcerpt: input.evidenceExcerpt, + metadata: { weak_mr_level_signal: true }, + occurredAt: input.payload.object_attributes.updated_at, + }); +} + +export async function handleGitLabMergeRequestFeedback(input: { + payload: MergeRequestPayload; + integration: PlatformIntegration; + deliveryId: string; +}): Promise { + const owner = ownerFromIntegration(input.integration); + if (!owner) return skipped('missing-owner'); + + const eventIds: string[] = []; + const action = input.payload.object_attributes.action; + if (action === 'approved' || action === 'approval') { + const result = await recordGitLabMrLevelSignal({ + payload: input.payload, + integration: input.integration, + owner, + deliveryId: input.deliveryId, + signalKind: 'mr_approved', + sentiment: 'positive', + evidenceExcerpt: 'Merge request approved.', + }); + eventIds.push(result.event.id); + } + + if (action === 'unapproved' || action === 'unapproval') { + const result = await recordGitLabMrLevelSignal({ + payload: input.payload, + integration: input.integration, + owner, + deliveryId: input.deliveryId, + signalKind: 'mr_unapproved', + sentiment: 'negative', + evidenceExcerpt: 'Merge request approval removed.', + }); + eventIds.push(result.event.id); + } + + const blockingDiscussionChange = input.payload.changes?.blocking_discussions_resolved; + if ( + blockingDiscussionChange && + blockingDiscussionChange.previous !== blockingDiscussionChange.current && + typeof blockingDiscussionChange.current === 'boolean' + ) { + const subjects = await listFeedbackSubjectsForPullRequest({ + owner, + platform: 'gitlab', + repoFullName: input.payload.project.path_with_namespace, + prNumber: input.payload.object_attributes.iid, + subjectTypes: ['discussion'], + }); + const signalKind = blockingDiscussionChange.current ? 'thread_resolved' : 'thread_unresolved'; + const sentiment = blockingDiscussionChange.current ? 'positive' : 'negative'; + + for (const subject of subjects) { + await upsertFeedbackSubject({ + owner, + platform: 'gitlab', + platformIntegrationId: input.integration.id, + codeReviewId: subject.code_review_id, + subjectType: subject.subject_type, + externalId: subject.external_id, + externalThreadId: subject.external_thread_id, + externalUrl: subject.external_url, + repoFullName: subject.repo_full_name, + platformProjectId: subject.platform_project_id, + prNumber: subject.pr_number, + prUrl: subject.pr_url, + headSha: input.payload.object_attributes.last_commit?.id ?? subject.head_sha, + filePath: subject.file_path, + lineNumber: subject.line_number, + diffHunk: subject.diff_hunk, + bodyExcerpt: subject.body_excerpt, + severity: subject.severity, + findingTitle: subject.finding_title, + findingFingerprint: subject.finding_fingerprint, + state: blockingDiscussionChange.current ? 'resolved' : 'active', + }); + const result = await recordFeedbackEvent({ + owner, + platform: 'gitlab', + platformIntegrationId: input.integration.id, + subjectId: subject.id, + codeReviewId: subject.code_review_id, + repoFullName: input.payload.project.path_with_namespace, + platformProjectId: input.payload.project.id, + prNumber: input.payload.object_attributes.iid, + prUrl: input.payload.object_attributes.url, + eventSource: 'gitlab_webhook', + signalKind, + sentiment, + strength: 2, + externalEventId: `gitlab:${input.deliveryId}:discussion:${subject.external_id}:${signalKind}`, + externalUrl: subject.external_url ?? input.payload.object_attributes.url, + evidenceExcerpt: blockingDiscussionChange.current + ? 'Review discussion resolved.' + : 'Review discussion reopened.', + metadata: { discussion_id: subject.external_thread_id }, + occurredAt: input.payload.object_attributes.updated_at, + }); + eventIds.push(result.event.id); + } + } + + return eventIds.length > 0 ? { recorded: true, eventIds } : skipped('no-feedback-signal'); +} diff --git a/apps/web/src/lib/integrations/core/constants.ts b/apps/web/src/lib/integrations/core/constants.ts index 3ea6ca7134..29be674ebf 100644 --- a/apps/web/src/lib/integrations/core/constants.ts +++ b/apps/web/src/lib/integrations/core/constants.ts @@ -108,6 +108,7 @@ export const GITLAB_EVENT = { // Note (comment) events NOTE: 'Note Hook', + EMOJI: 'Emoji Hook', // Issue events ISSUE: 'Issue Hook', diff --git a/apps/web/src/lib/integrations/platforms/gitlab/adapter.test.ts b/apps/web/src/lib/integrations/platforms/gitlab/adapter.test.ts index b6ea36f96d..7ca83ce5bb 100644 --- a/apps/web/src/lib/integrations/platforms/gitlab/adapter.test.ts +++ b/apps/web/src/lib/integrations/platforms/gitlab/adapter.test.ts @@ -6,6 +6,8 @@ import { searchGitLabProjects, normalizeGitLabSearchQuery, fetchGitLabRootTextFileAtRef, + createProjectWebhook, + updateProjectWebhook, } from './adapter'; // Mock fetch globally @@ -619,3 +621,96 @@ describe('fetchGitLabRootTextFileAtRef', () => { ).rejects.toThrow('GitLab repository file fetch failed: 500'); }); }); + +describe('GitLab webhook management', () => { + beforeEach(() => { + mockFetch.mockReset(); + }); + + function webhookResponse() { + return { + id: 10, + url: 'https://app.example.com/api/webhooks/gitlab', + project_id: 123, + push_events: false, + push_events_branch_filter: '', + issues_events: false, + confidential_issues_events: false, + merge_requests_events: true, + tag_push_events: false, + note_events: true, + emoji_events: true, + confidential_note_events: false, + job_events: false, + pipeline_events: false, + wiki_page_events: false, + deployment_events: false, + releases_events: false, + subgroup_events: false, + member_events: false, + enable_ssl_verification: true, + created_at: '2026-01-01T00:00:00.000Z', + }; + } + + it('creates project webhooks with note and emoji events enabled', async () => { + mockFetch.mockResolvedValueOnce({ + ok: true, + json: async () => webhookResponse(), + }); + + await createProjectWebhook( + 'test-token', + 123, + 'https://app.example.com/api/webhooks/gitlab', + 'secret' + ); + + expect(mockFetch).toHaveBeenCalledWith( + 'https://gitlab.com/api/v4/projects/123/hooks', + expect.objectContaining({ + method: 'POST', + body: expect.any(String), + }) + ); + const body = JSON.parse(mockFetch.mock.calls[0][1].body as string) as Record; + expect(body).toEqual( + expect.objectContaining({ + merge_requests_events: true, + note_events: true, + emoji_events: true, + }) + ); + }); + + it('updates project webhooks with note and emoji events enabled', async () => { + mockFetch.mockResolvedValueOnce({ + ok: true, + json: async () => webhookResponse(), + }); + + await updateProjectWebhook( + 'test-token', + 123, + 10, + 'https://app.example.com/api/webhooks/gitlab', + 'secret' + ); + + expect(mockFetch).toHaveBeenCalledWith( + 'https://gitlab.com/api/v4/projects/123/hooks/10', + expect.objectContaining({ + method: 'PUT', + body: expect.any(String), + }) + ); + const body = JSON.parse(mockFetch.mock.calls[0][1].body as string) as Record; + expect(body).toEqual( + expect.objectContaining({ + merge_requests_events: true, + note_events: true, + emoji_events: true, + }) + ); + }); +}); diff --git a/apps/web/src/lib/integrations/platforms/gitlab/adapter.ts b/apps/web/src/lib/integrations/platforms/gitlab/adapter.ts index 80b868fc4a..b798c8802f 100644 --- a/apps/web/src/lib/integrations/platforms/gitlab/adapter.ts +++ b/apps/web/src/lib/integrations/platforms/gitlab/adapter.ts @@ -636,6 +636,7 @@ export type GitLabWebhook = { merge_requests_events: boolean; tag_push_events: boolean; note_events: boolean; + emoji_events?: boolean; confidential_note_events: boolean; job_events: boolean; pipeline_events: boolean; @@ -729,7 +730,8 @@ export async function createProjectWebhook( issues_events: false, confidential_issues_events: false, tag_push_events: false, - note_events: false, + note_events: true, + emoji_events: true, confidential_note_events: false, job_events: false, pipeline_events: false, @@ -811,7 +813,8 @@ export async function updateProjectWebhook( issues_events: false, confidential_issues_events: false, tag_push_events: false, - note_events: false, + note_events: true, + emoji_events: true, confidential_note_events: false, job_events: false, pipeline_events: false, diff --git a/apps/web/src/lib/integrations/platforms/gitlab/webhook-schemas.ts b/apps/web/src/lib/integrations/platforms/gitlab/webhook-schemas.ts index a1de2f0431..d9c0b31603 100644 --- a/apps/web/src/lib/integrations/platforms/gitlab/webhook-schemas.ts +++ b/apps/web/src/lib/integrations/platforms/gitlab/webhook-schemas.ts @@ -175,6 +175,12 @@ export const MergeRequestPayloadSchema = z.object({ current: z.array(GitLabLabelSchema).optional(), }) .optional(), + blocking_discussions_resolved: z + .object({ + previous: z.boolean().optional(), + current: z.boolean().optional(), + }) + .optional(), }) .optional(), assignees: z.array(GitLabUserSchema).optional(), @@ -238,6 +244,8 @@ export const NoteEventPayloadSchema = z.object({ object_attributes: z.object({ id: z.number(), note: z.string(), + action: z.enum(['create', 'update']).optional(), + discussion_id: z.string().optional(), noteable_type: z.enum(['Commit', 'MergeRequest', 'Issue', 'Snippet']), author_id: z.number(), created_at: z.string(), @@ -303,6 +311,44 @@ export const NoteEventPayloadSchema = z.object({ export type NoteEventPayload = z.infer; +const GitLabNoteReferenceSchema = z.object({ + id: z.number(), + note: z.string().optional(), + body: z.string().optional(), + url: z.string().optional(), + noteable_type: z.string().optional(), + position: z + .object({ + old_path: z.string().optional(), + new_path: z.string().optional(), + old_line: z.number().nullable().optional(), + new_line: z.number().nullable().optional(), + }) + .nullable() + .optional(), +}); + +export const EmojiEventPayloadSchema = z.object({ + object_kind: z.literal('emoji'), + event_type: z.string().optional(), + user: GitLabUserSchema, + project_id: z.number().optional(), + project: GitLabProjectSchema, + object_attributes: z.object({ + id: z.number(), + name: z.string(), + action: z.enum(['award', 'revoke', 'create', 'created', 'delete', 'deleted']).optional(), + awardable_type: z.string(), + awardable_id: z.number(), + created_at: z.string().optional(), + updated_at: z.string().optional(), + }), + merge_request: MergeRequestObjectAttributesSchema.optional(), + note: GitLabNoteReferenceSchema.optional(), +}); + +export type EmojiEventPayload = z.infer; + /** * Pipeline Event Webhook Payload Schema (for future use) */ From 12b55a17b250b91a6a5b990cc9ec89494fc9e966 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Fri, 22 May 2026 22:33:39 +0300 Subject: [PATCH 05/23] feat(review-memory): aggregate feedback into proposals --- .../aggregate-review-memory/route.test.ts | 67 ++ .../api/cron/aggregate-review-memory/route.ts | 35 ++ .../review-memory/aggregation.test.ts | 195 ++++++ .../code-reviews/review-memory/aggregation.ts | 583 ++++++++++++++++++ .../src/lib/code-reviews/review-memory/db.ts | 109 ++++ apps/web/vercel.json | 4 + 6 files changed, 993 insertions(+) create mode 100644 apps/web/src/app/api/cron/aggregate-review-memory/route.test.ts create mode 100644 apps/web/src/app/api/cron/aggregate-review-memory/route.ts create mode 100644 apps/web/src/lib/code-reviews/review-memory/aggregation.test.ts create mode 100644 apps/web/src/lib/code-reviews/review-memory/aggregation.ts diff --git a/apps/web/src/app/api/cron/aggregate-review-memory/route.test.ts b/apps/web/src/app/api/cron/aggregate-review-memory/route.test.ts new file mode 100644 index 0000000000..472e787a77 --- /dev/null +++ b/apps/web/src/app/api/cron/aggregate-review-memory/route.test.ts @@ -0,0 +1,67 @@ +import { NextRequest } from 'next/server'; + +jest.mock('@/lib/config.server', () => ({ + CRON_SECRET: 'cron-secret', +})); + +jest.mock('@/lib/code-reviews/review-memory/aggregation', () => ({ + dispatchReviewMemoryAggregationCron: jest.fn(), +})); + +import { dispatchReviewMemoryAggregationCron } from '@/lib/code-reviews/review-memory/aggregation'; +import { GET } from './route'; + +const mockDispatchReviewMemoryAggregationCron = jest.mocked(dispatchReviewMemoryAggregationCron); + +describe('GET /api/cron/aggregate-review-memory', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('rejects unauthorized requests', async () => { + const response = await GET( + new NextRequest('http://localhost:3000/api/cron/aggregate-review-memory', { + method: 'GET', + }) + ); + + expect(response.status).toBe(401); + await expect(response.json()).resolves.toEqual({ error: 'Unauthorized' }); + expect(mockDispatchReviewMemoryAggregationCron).not.toHaveBeenCalled(); + }); + + it('dispatches review memory aggregation when authorized', async () => { + mockDispatchReviewMemoryAggregationCron.mockResolvedValue({ + claimed: 2, + completed: 1, + skipped: 1, + failed: 0, + proposals: 3, + }); + + const response = await GET( + new NextRequest('http://localhost:3000/api/cron/aggregate-review-memory', { + method: 'GET', + headers: { + authorization: 'Bearer cron-secret', + }, + }) + ); + + expect(response.status).toBe(200); + expect(mockDispatchReviewMemoryAggregationCron).toHaveBeenCalledTimes(1); + await expect(response.json()).resolves.toEqual( + expect.objectContaining({ + success: true, + summary: { + claimed: 2, + completed: 1, + skipped: 1, + failed: 0, + proposals: 3, + }, + timestamp: expect.any(String), + }) + ); + }); +}); diff --git a/apps/web/src/app/api/cron/aggregate-review-memory/route.ts b/apps/web/src/app/api/cron/aggregate-review-memory/route.ts new file mode 100644 index 0000000000..07b91f0f94 --- /dev/null +++ b/apps/web/src/app/api/cron/aggregate-review-memory/route.ts @@ -0,0 +1,35 @@ +import { NextResponse } from 'next/server'; + +import { CRON_SECRET } from '@/lib/config.server'; +import { dispatchReviewMemoryAggregationCron } from '@/lib/code-reviews/review-memory/aggregation'; +import { sentryLogger } from '@/lib/utils.server'; + +if (!CRON_SECRET) { + throw new Error('CRON_SECRET is not configured in environment variables'); +} + +export async function GET(request: Request) { + const authHeader = request.headers.get('authorization'); + const expectedAuth = `Bearer ${CRON_SECRET}`; + if (authHeader !== expectedAuth) { + sentryLogger( + 'cron', + 'warning' + )( + 'SECURITY: Invalid CRON job authorization attempt: ' + + (authHeader ? 'Invalid authorization header' : 'Missing authorization header') + ); + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); + } + + const summary = await dispatchReviewMemoryAggregationCron(); + + return NextResponse.json( + { + success: true, + summary, + timestamp: new Date().toISOString(), + }, + { status: 200 } + ); +} diff --git a/apps/web/src/lib/code-reviews/review-memory/aggregation.test.ts b/apps/web/src/lib/code-reviews/review-memory/aggregation.test.ts new file mode 100644 index 0000000000..220a2ea062 --- /dev/null +++ b/apps/web/src/lib/code-reviews/review-memory/aggregation.test.ts @@ -0,0 +1,195 @@ +/* eslint-disable drizzle/enforce-delete-with-where */ +import { db } from '@/lib/drizzle'; +import { insertTestUser } from '@/tests/helpers/user.helper'; +import { + code_review_feedback_events, + code_review_feedback_subjects, + code_review_memory_aggregation_runs, + code_review_memory_aggregation_state, + code_review_memory_proposal_evidence, + code_review_memory_proposals, + kilocode_users, +} from '@kilocode/db/schema'; +import { eq } from 'drizzle-orm'; +import { dispatchReviewMemoryAggregationCron } from './aggregation'; +import { + listProposalEvidence, + recordFeedbackEvent, + upsertFeedbackSubject, + type ReviewMemoryOwner, +} from './db'; + +describe('review memory aggregation', () => { + afterEach(async () => { + await db.delete(code_review_memory_proposal_evidence); + await db.delete(code_review_memory_proposals); + await db.delete(code_review_memory_aggregation_runs); + await db.delete(code_review_feedback_events); + await db.delete(code_review_feedback_subjects); + await db.delete(code_review_memory_aggregation_state); + await db.delete(kilocode_users); + }); + + async function seedSubject(owner: ReviewMemoryOwner, externalId: string, prNumber: number) { + return await upsertFeedbackSubject({ + owner, + platform: 'github', + repoFullName: 'acme/widgets', + subjectType: 'inline_comment', + externalId, + prNumber, + prUrl: `https://github.com/acme/widgets/pull/${prNumber}`, + filePath: 'src/widget.ts', + bodyExcerpt: '**WARNING**: Avoid this pattern', + findingTitle: 'Avoid this pattern', + findingFingerprint: `fingerprint-${externalId}`, + state: 'active', + }); + } + + async function seedActionableFeedback(owner: ReviewMemoryOwner) { + const subjects = await Promise.all([ + seedSubject(owner, '101', 1), + seedSubject(owner, '102', 2), + seedSubject(owner, '103', 3), + ]); + const events = []; + for (let i = 0; i < 5; i += 1) { + const subject = subjects[i % subjects.length]; + const result = await recordFeedbackEvent({ + owner, + platform: 'github', + subjectId: subject.id, + repoFullName: 'acme/widgets', + prNumber: subject.pr_number, + prUrl: subject.pr_url, + eventSource: 'github_webhook', + signalKind: 'corrective_reply', + sentiment: 'negative', + strength: 3, + externalEventId: `aggregation-actionable-${i}`, + evidenceExcerpt: `False positive feedback ${i}`, + }); + events.push(result.event); + } + return events; + } + + it('does not claim scopes below the aggregation threshold', async () => { + const user = await insertTestUser(); + const owner = { type: 'user' as const, id: user.id }; + const subject = await seedSubject(owner, 'below-threshold', 1); + await recordFeedbackEvent({ + owner, + platform: 'github', + subjectId: subject.id, + repoFullName: 'acme/widgets', + prNumber: 1, + eventSource: 'github_webhook', + signalKind: 'corrective_reply', + sentiment: 'negative', + strength: 3, + externalEventId: 'aggregation-below-threshold', + }); + + const generateOpportunities = jest.fn(); + const summary = await dispatchReviewMemoryAggregationCron({ generateOpportunities }); + + expect(summary).toEqual({ claimed: 0, completed: 0, skipped: 0, failed: 0, proposals: 0 }); + expect(generateOpportunities).not.toHaveBeenCalled(); + }); + + it('aggregates actionable feedback into proposals and included evidence', async () => { + const user = await insertTestUser(); + const owner = { type: 'user' as const, id: user.id }; + const events = await seedActionableFeedback(owner); + const generateOpportunities = jest.fn(async () => ({ + opportunities: [ + { + title: 'Clarify widget warning', + proposalType: 'clarify' as const, + scopeKind: 'file' as const, + scopeValue: 'src/widget.ts', + rationale: 'Maintainers repeatedly corrected this warning.', + proposedMarkdown: + '### Review guidance: Clarify widget warning\n\nScope: `src/widget.ts`\n\nGuidance: Avoid flagging the intentional widget pattern.\n\nWhy: Maintainers marked repeated comments as false positives.', + confidence: 'high' as const, + evidenceEventIds: [events[0].id, events[1].id], + contradictoryEventIds: [], + dedupeHint: 'clarify widget warning', + }, + ], + tokensIn: 100, + tokensOut: 50, + })); + + const summary = await dispatchReviewMemoryAggregationCron({ + now: new Date('2026-06-01T00:00:00.000Z'), + generateOpportunities, + }); + + expect(summary).toEqual({ claimed: 1, completed: 1, skipped: 0, failed: 0, proposals: 1 }); + expect(generateOpportunities).toHaveBeenCalledWith( + expect.objectContaining({ + repoFullName: 'acme/widgets', + modelSlug: expect.any(String), + clusters: expect.any(Array), + }) + ); + + const proposals = await db.select().from(code_review_memory_proposals); + expect(proposals).toHaveLength(1); + expect(proposals[0]).toEqual( + expect.objectContaining({ + title: 'Clarify widget warning', + status: 'open', + negative_count: 2, + }) + ); + const evidence = await listProposalEvidence({ proposalId: proposals[0].id }); + expect(evidence).toHaveLength(2); + + const includedEvents = await db.select().from(code_review_feedback_events); + expect(includedEvents.every(event => event.aggregation_state === 'included')).toBe(true); + + const [run] = await db.select().from(code_review_memory_aggregation_runs); + expect(run).toEqual(expect.objectContaining({ status: 'completed', tokens_in: 100 })); + }); + + it('skips weak MR-level-only feedback without calling the model', async () => { + const user = await insertTestUser(); + const owner = { type: 'user' as const, id: user.id }; + for (let i = 0; i < 5; i += 1) { + await recordFeedbackEvent({ + owner, + platform: 'gitlab', + repoFullName: 'acme/widgets', + platformProjectId: 123, + prNumber: i % 2 === 0 ? 1 : 2, + eventSource: 'gitlab_webhook', + signalKind: 'mr_approved', + sentiment: 'positive', + strength: 2, + externalEventId: `aggregation-weak-${i}`, + }); + } + + const generateOpportunities = jest.fn(); + const summary = await dispatchReviewMemoryAggregationCron({ generateOpportunities }); + + expect(summary).toEqual({ claimed: 1, completed: 0, skipped: 1, failed: 0, proposals: 0 }); + expect(generateOpportunities).not.toHaveBeenCalled(); + + const ignoredEvents = await db.select().from(code_review_feedback_events); + expect(ignoredEvents.every(event => event.aggregation_state === 'ignored')).toBe(true); + + const [run] = await db.select().from(code_review_memory_aggregation_runs); + expect(run.status).toBe('skipped'); + + const [state] = await db + .select() + .from(code_review_memory_aggregation_state) + .where(eq(code_review_memory_aggregation_state.repo_full_name, 'acme/widgets')); + expect(state.status).toBe('idle'); + }); +}); diff --git a/apps/web/src/lib/code-reviews/review-memory/aggregation.ts b/apps/web/src/lib/code-reviews/review-memory/aggregation.ts new file mode 100644 index 0000000000..4a1f571f84 --- /dev/null +++ b/apps/web/src/lib/code-reviews/review-memory/aggregation.ts @@ -0,0 +1,583 @@ +import { APP_URL } from '@/lib/constants'; +import { DEFAULT_CODE_REVIEW_MODEL } from '@/lib/code-reviews/core/constants'; +import { getAgentConfigForOwner } from '@/lib/agent-config/db/agent-configs'; +import { + CodeReviewAgentConfigSchema, + type CodeReviewAgentConfig, +} from '@/lib/agent-config/core/types'; +import { FEATURE_HEADER } from '@/lib/feature-detection'; +import { ensureBotUserForOrg } from '@/lib/bot-users/bot-user-service'; +import { findUserById } from '@/lib/user'; +import { generateApiToken } from '@/lib/tokens'; +import { captureException } from '@sentry/nextjs'; +import { createOpenAICompatible } from '@ai-sdk/openai-compatible'; +import { generateText } from 'ai'; +import * as z from 'zod'; +import type { + CodeReviewFeedbackEvent, + CodeReviewFeedbackSubject, + CodeReviewMemoryAggregationState, + User, +} from '@kilocode/db/schema'; +import type { + ReviewMemoryPlatform, + ReviewMemoryProposalScopeKind, + ReviewMemoryProposalType, +} from '@kilocode/db/schema-types'; +import { + claimEligibleAggregationStates, + createAggregationRun, + finishClaimedAggregationState, + listFeedbackEventsForAggregation, + listReviewMemoryProposals, + markFeedbackEventsAggregationState, + markFeedbackEventsIncluded, + refreshAggregationStateForScope, + updateAggregationRunStatus, + upsertReviewMemoryProposal, + type ReviewMemoryOwner, +} from './db'; + +export const REVIEW_MEMORY_AGGREGATION_THRESHOLDS = { + minFreshEvents: 5, + minFreshWeight: 8, + minDistinctSubjects: 3, + minDistinctPrs: 2, + cooldownMs: 20 * 60 * 60 * 1000, + failureRetryMs: 60 * 60 * 1000, + staleClaimMs: 30 * 60 * 1000, + maxEventsPerScope: 200, + maxClustersPerRun: 20, +} as const; + +const WeakPullRequestSignals = new Set([ + 'mr_approved', + 'mr_unapproved', + 'pr_approved', + 'pr_changes_requested', +]); + +const AggregationOpportunitySchema = z.object({ + title: z.string().min(1).max(140), + proposalType: z.enum(['suppress', 'clarify', 'narrow', 'reinforce']), + scopeKind: z.enum(['repository', 'path_glob', 'file', 'language']), + scopeValue: z.string().nullable(), + rationale: z.string().min(1).max(1_500), + proposedMarkdown: z.string().min(1).max(4_000), + confidence: z.enum(['low', 'medium', 'high']), + evidenceEventIds: z.array(z.string()).max(20), + contradictoryEventIds: z.array(z.string()).max(20).default([]), + dedupeHint: z.string().min(1).max(200), +}); + +const AggregationOutputSchema = z.object({ + opportunities: z.array(AggregationOpportunitySchema).max(10), +}); + +export type ReviewMemoryAggregationOpportunity = z.infer; + +type FeedbackEventForAggregation = CodeReviewFeedbackEvent & { + subject: CodeReviewFeedbackSubject | null; +}; + +type FeedbackCluster = { + id: string; + title: string; + eventIds: string[]; + positiveCount: number; + negativeCount: number; + neutralCount: number; + weight: number; + distinctPrs: number; + distinctSubjects: number; + samples: string[]; +}; + +export type ReviewMemoryAggregationGeneratorInput = { + owner: ReviewMemoryOwner; + platform: ReviewMemoryPlatform; + repoFullName: string; + modelSlug: string; + prompt: string; + clusters: FeedbackCluster[]; +}; + +export type ReviewMemoryAggregationGeneratorResult = { + opportunities: ReviewMemoryAggregationOpportunity[]; + tokensIn?: number | null; + tokensOut?: number | null; + totalCostMusd?: number | null; +}; + +export type DispatchReviewMemoryAggregationSummary = { + claimed: number; + completed: number; + skipped: number; + failed: number; + proposals: number; +}; + +type ProcessAggregationScopeResult = { + status: 'completed' | 'skipped' | 'failed'; + proposals: number; + reason?: string; +}; + +export type DispatchReviewMemoryAggregationOptions = { + limit?: number; + now?: Date; + generateOpportunities?: ( + input: ReviewMemoryAggregationGeneratorInput + ) => Promise; +}; + +function ownerFromState(state: CodeReviewMemoryAggregationState): ReviewMemoryOwner | null { + if (state.owned_by_organization_id) { + return { type: 'org', id: state.owned_by_organization_id }; + } + if (state.owned_by_user_id) { + return { type: 'user', id: state.owned_by_user_id }; + } + return null; +} + +function confidenceToScore(confidence: ReviewMemoryAggregationOpportunity['confidence']): number { + if (confidence === 'high') return 0.9; + if (confidence === 'medium') return 0.66; + return 0.33; +} + +function createDedupeKey(input: { + repoFullName: string; + proposalType: ReviewMemoryProposalType; + scopeKind: ReviewMemoryProposalScopeKind; + scopeValue: string | null; + dedupeHint: string; +}): string { + return [ + input.repoFullName.toLowerCase(), + input.proposalType, + input.scopeKind, + input.scopeValue?.toLowerCase() ?? 'repository', + input.dedupeHint.toLowerCase().replace(/\s+/g, ' ').trim(), + ].join(':'); +} + +function addMs(date: Date, ms: number): string { + return new Date(date.getTime() + ms).toISOString(); +} + +function hasActionableCommentEvidence(events: FeedbackEventForAggregation[]): boolean { + return events.some(event => event.subject && !WeakPullRequestSignals.has(event.signal_kind)); +} + +function clusterKey(event: FeedbackEventForAggregation): string { + const subject = event.subject; + if (subject?.finding_fingerprint) return `finding:${subject.finding_fingerprint}`; + if (subject?.file_path) return `file:${subject.file_path}:${event.signal_kind}`; + if (subject?.subject_type) return `subject:${subject.subject_type}:${event.signal_kind}`; + return `signal:${event.signal_kind}`; +} + +function clusterTitle(event: FeedbackEventForAggregation): string { + if (event.subject?.finding_title) return event.subject.finding_title; + if (event.subject?.file_path) return `${event.signal_kind} in ${event.subject.file_path}`; + return event.signal_kind.replace(/_/g, ' '); +} + +export function buildFeedbackClusters( + events: FeedbackEventForAggregation[], + maxClusters = REVIEW_MEMORY_AGGREGATION_THRESHOLDS.maxClustersPerRun +): FeedbackCluster[] { + const clusters = new Map(); + for (const event of events) { + const key = clusterKey(event); + const existing = clusters.get(key); + const cluster = + existing ?? + ({ + id: key, + title: clusterTitle(event), + eventIds: [], + positiveCount: 0, + negativeCount: 0, + neutralCount: 0, + weight: 0, + distinctPrs: 0, + distinctSubjects: 0, + samples: [], + } satisfies FeedbackCluster); + + cluster.eventIds.push(event.id); + cluster.weight += event.strength; + if (event.sentiment === 'positive') cluster.positiveCount += 1; + if (event.sentiment === 'negative') cluster.negativeCount += 1; + if (event.sentiment === 'neutral') cluster.neutralCount += 1; + if (cluster.samples.length < 6 && event.evidence_excerpt) { + cluster.samples.push(event.evidence_excerpt); + } + clusters.set(key, cluster); + } + + const distinctPrsByCluster = new Map>(); + const distinctSubjectsByCluster = new Map>(); + for (const event of events) { + const key = clusterKey(event); + if (event.pr_number != null) { + const prs = distinctPrsByCluster.get(key) ?? new Set(); + prs.add(event.pr_number); + distinctPrsByCluster.set(key, prs); + } + if (event.subject_id) { + const subjects = distinctSubjectsByCluster.get(key) ?? new Set(); + subjects.add(event.subject_id); + distinctSubjectsByCluster.set(key, subjects); + } + } + + for (const [key, cluster] of clusters) { + cluster.distinctPrs = distinctPrsByCluster.get(key)?.size ?? 0; + cluster.distinctSubjects = distinctSubjectsByCluster.get(key)?.size ?? 0; + } + + return [...clusters.values()] + .sort((a, b) => b.weight - a.weight || b.eventIds.length - a.eventIds.length) + .slice(0, maxClusters); +} + +function buildAggregationPrompt(input: { + platform: ReviewMemoryPlatform; + repoFullName: string; + clusters: FeedbackCluster[]; + existingProposalSummaries: string[]; +}): string { + return `You analyze maintainer feedback about Kilo Code Review output and draft REVIEW.md improvement opportunities. + +Return strict JSON with this shape: +{"opportunities":[{"title":"...","proposalType":"suppress|clarify|narrow|reinforce","scopeKind":"repository|path_glob|file|language","scopeValue":null,"rationale":"...","proposedMarkdown":"...","confidence":"low|medium|high","evidenceEventIds":["..."],"contradictoryEventIds":[],"dedupeHint":"..."}]} + +Rules: +- Do not create proposals from isolated weak PR/MR-level signals. +- Negative feedback should produce suppress, clarify, or narrow guidance. +- Positive feedback should produce reinforce guidance. +- Draft concise REVIEW.md markdown with a heading, scope, guidance, and why. +- Use only evidenceEventIds from the provided clusters. +- If feedback is too weak or contradictory, return an empty opportunities array. + +Platform: ${input.platform} +Repository: ${input.repoFullName} + +Existing active proposals: +${input.existingProposalSummaries.length ? input.existingProposalSummaries.join('\n') : 'None'} + +Feedback clusters: +${JSON.stringify(input.clusters, null, 2)}`; +} + +function extractJsonObject(text: string): unknown { + const trimmed = text.trim(); + if (trimmed.startsWith('{') && trimmed.endsWith('}')) return JSON.parse(trimmed); + const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/i); + if (fenced?.[1]) return JSON.parse(fenced[1]); + const start = trimmed.indexOf('{'); + const end = trimmed.lastIndexOf('}'); + if (start >= 0 && end > start) return JSON.parse(trimmed.slice(start, end + 1)); + throw new Error('Aggregation model did not return JSON'); +} + +async function resolveAggregationActor(owner: ReviewMemoryOwner): Promise { + if (owner.type === 'org') { + return await ensureBotUserForOrg(owner.id, 'code-review'); + } + + const user = await findUserById(owner.id); + if (!user) throw new Error('Review Memory aggregation owner user not found'); + return user; +} + +export async function generateReviewMemoryOpportunitiesWithGateway( + input: ReviewMemoryAggregationGeneratorInput +): Promise { + const actor = await resolveAggregationActor(input.owner); + const headers: Record = { + 'User-Agent': 'Kilo Review Memory Aggregator', + [FEATURE_HEADER]: 'code-review-memory', + }; + if (input.owner.type === 'org') { + headers['X-KiloCode-OrganizationId'] = input.owner.id; + } + + const provider = createOpenAICompatible({ + name: 'kilo-gateway', + baseURL: `${APP_URL}/api/openrouter`, + apiKey: generateApiToken(actor, { internalApiUse: true }), + headers, + }); + const result = await generateText({ + model: provider.chatModel(input.modelSlug), + prompt: input.prompt, + maxOutputTokens: 4_000, + }); + const parsed = AggregationOutputSchema.parse(extractJsonObject(result.text)); + + return { + opportunities: parsed.opportunities, + tokensIn: result.usage.inputTokens ?? null, + tokensOut: result.usage.outputTokens ?? null, + }; +} + +async function resolveAggregationModel(input: { + owner: ReviewMemoryOwner; + platform: ReviewMemoryPlatform; +}): Promise<{ modelSlug: string; config: CodeReviewAgentConfig | null }> { + const ownerForConfig = + input.owner.type === 'org' + ? { type: 'org' as const, id: input.owner.id, userId: input.owner.id } + : { type: 'user' as const, id: input.owner.id, userId: input.owner.id }; + const agentConfig = await getAgentConfigForOwner(ownerForConfig, 'code_review', input.platform); + const parsed = CodeReviewAgentConfigSchema.safeParse(agentConfig?.config); + if (!parsed.success) { + return { modelSlug: DEFAULT_CODE_REVIEW_MODEL, config: null }; + } + + return { modelSlug: parsed.data.model_slug || DEFAULT_CODE_REVIEW_MODEL, config: parsed.data }; +} + +function proposalRollups( + opportunity: ReviewMemoryAggregationOpportunity, + eventById: Map +) { + const evidenceIds = opportunity.evidenceEventIds.filter(id => eventById.has(id)); + const contradictoryIds = opportunity.contradictoryEventIds.filter(id => eventById.has(id)); + const allIds = [...new Set([...evidenceIds, ...contradictoryIds])]; + const events = allIds.map(id => eventById.get(id)).filter(event => event != null); + const distinctPrs = new Set( + events.flatMap(event => (event.pr_number == null ? [] : [event.pr_number])) + ); + const distinctSubjects = new Set( + events.flatMap(event => (event.subject_id == null ? [] : [event.subject_id])) + ); + + return { + evidenceIds, + contradictoryIds, + positiveCount: events.filter(event => event.sentiment === 'positive').length, + negativeCount: events.filter(event => event.sentiment === 'negative').length, + neutralCount: events.filter(event => event.sentiment === 'neutral').length, + distinctPrCount: distinctPrs.size, + distinctSubjectCount: distinctSubjects.size, + }; +} + +async function processClaimedAggregationScope( + state: CodeReviewMemoryAggregationState, + options: Required> +): Promise { + const owner = ownerFromState(state); + if (!owner || !state.claim_token) { + return { status: 'failed', proposals: 0, reason: 'missing-owner-or-claim' }; + } + + const { modelSlug } = await resolveAggregationModel({ owner, platform: state.platform }); + const events = await listFeedbackEventsForAggregation({ + owner, + platform: state.platform, + repoFullName: state.repo_full_name, + limit: REVIEW_MEMORY_AGGREGATION_THRESHOLDS.maxEventsPerScope, + }); + const clusters = buildFeedbackClusters(events); + const run = await createAggregationRun({ + owner, + platform: state.platform, + repoFullName: state.repo_full_name, + platformProjectId: state.platform_project_id, + modelSlug, + trigger: 'cron', + inputEventCount: events.length, + inputSubjectCount: new Set( + events.flatMap(event => (event.subject_id ? [event.subject_id] : [])) + ).size, + inputClusterCount: clusters.length, + freshEventCutoffAt: events[0]?.created_at ?? null, + }); + + try { + if (events.length === 0 || !hasActionableCommentEvidence(events) || clusters.length === 0) { + await markFeedbackEventsAggregationState({ + eventIds: events.map(event => event.id), + aggregationState: 'ignored', + }); + await updateAggregationRunStatus({ + runId: run.id, + status: 'skipped', + skipReason: 'no_actionable_comment_evidence', + }); + await finishClaimedAggregationState({ + stateId: state.id, + claimToken: state.claim_token, + status: 'idle', + nextEligibleAt: addMs(options.now, REVIEW_MEMORY_AGGREGATION_THRESHOLDS.cooldownMs), + lastSuccessfulRunAt: options.now.toISOString(), + lastModelSlug: modelSlug, + lastErrorMessage: null, + }); + await refreshAggregationStateForScope({ + owner, + platform: state.platform, + repoFullName: state.repo_full_name, + platformProjectId: state.platform_project_id, + }); + return { status: 'skipped', proposals: 0, reason: 'no_actionable_comment_evidence' }; + } + + const existingProposals = await listReviewMemoryProposals({ + owner, + platform: state.platform, + repoFullName: state.repo_full_name, + statuses: ['open', 'edited', 'approved', 'opening_change_request', 'change_request_opened'], + limit: 20, + }); + const prompt = buildAggregationPrompt({ + platform: state.platform, + repoFullName: state.repo_full_name, + clusters, + existingProposalSummaries: existingProposals.map( + proposal => `- ${proposal.title} (${proposal.proposal_type}, ${proposal.scope_kind})` + ), + }); + const eventById = new Map(events.map(event => [event.id, event])); + const generation = await options.generateOpportunities({ + owner, + platform: state.platform, + repoFullName: state.repo_full_name, + modelSlug, + prompt, + clusters, + }); + + let proposalCount = 0; + for (const opportunity of generation.opportunities) { + const rollups = proposalRollups(opportunity, eventById); + if (rollups.evidenceIds.length === 0) continue; + await upsertReviewMemoryProposal({ + owner, + platform: state.platform, + repoFullName: state.repo_full_name, + platformProjectId: state.platform_project_id, + aggregationRunId: run.id, + scopeKind: opportunity.scopeKind, + scopeValue: opportunity.scopeValue, + proposalType: opportunity.proposalType, + title: opportunity.title, + rationale: opportunity.rationale, + proposedMarkdown: opportunity.proposedMarkdown, + dedupeKey: createDedupeKey({ + repoFullName: state.repo_full_name, + proposalType: opportunity.proposalType, + scopeKind: opportunity.scopeKind, + scopeValue: opportunity.scopeValue, + dedupeHint: opportunity.dedupeHint, + }), + llmConfidence: confidenceToScore(opportunity.confidence), + positiveCount: rollups.positiveCount, + negativeCount: rollups.negativeCount, + neutralCount: rollups.neutralCount, + distinctPrCount: rollups.distinctPrCount, + distinctSubjectCount: rollups.distinctSubjectCount, + contradictoryCount: rollups.contradictoryIds.length, + evidence: [ + ...rollups.evidenceIds.map(feedbackEventId => ({ + feedbackEventId, + role: 'primary' as const, + })), + ...rollups.contradictoryIds.map(feedbackEventId => ({ + feedbackEventId, + role: 'contradictory' as const, + })), + ], + }); + proposalCount += 1; + } + + await markFeedbackEventsIncluded({ eventIds: events.map(event => event.id) }); + await updateAggregationRunStatus({ + runId: run.id, + status: 'completed', + tokensIn: generation.tokensIn, + tokensOut: generation.tokensOut, + totalCostMusd: generation.totalCostMusd, + }); + await finishClaimedAggregationState({ + stateId: state.id, + claimToken: state.claim_token, + status: 'idle', + nextEligibleAt: addMs(options.now, REVIEW_MEMORY_AGGREGATION_THRESHOLDS.cooldownMs), + lastSuccessfulRunAt: options.now.toISOString(), + lastModelSlug: modelSlug, + lastErrorMessage: null, + }); + await refreshAggregationStateForScope({ + owner, + platform: state.platform, + repoFullName: state.repo_full_name, + platformProjectId: state.platform_project_id, + }); + + return { status: 'completed', proposals: proposalCount }; + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + await updateAggregationRunStatus({ + runId: run.id, + status: 'failed', + errorMessage: message, + }); + await finishClaimedAggregationState({ + stateId: state.id, + claimToken: state.claim_token, + status: 'failed', + nextEligibleAt: addMs(options.now, REVIEW_MEMORY_AGGREGATION_THRESHOLDS.failureRetryMs), + lastErrorMessage: message, + }); + captureException(error, { + tags: { source: 'review-memory-aggregation' }, + extra: { stateId: state.id, repoFullName: state.repo_full_name, platform: state.platform }, + }); + return { status: 'failed', proposals: 0, reason: message }; + } +} + +export async function dispatchReviewMemoryAggregationCron( + options: DispatchReviewMemoryAggregationOptions = {} +): Promise { + const now = options.now ?? new Date(); + const generateOpportunities = + options.generateOpportunities ?? generateReviewMemoryOpportunitiesWithGateway; + const claimed = await claimEligibleAggregationStates({ + limit: options.limit ?? 10, + minFreshEvents: REVIEW_MEMORY_AGGREGATION_THRESHOLDS.minFreshEvents, + minFreshWeight: REVIEW_MEMORY_AGGREGATION_THRESHOLDS.minFreshWeight, + minDistinctSubjects: REVIEW_MEMORY_AGGREGATION_THRESHOLDS.minDistinctSubjects, + minDistinctPrs: REVIEW_MEMORY_AGGREGATION_THRESHOLDS.minDistinctPrs, + staleAfterMs: REVIEW_MEMORY_AGGREGATION_THRESHOLDS.staleClaimMs, + now, + }); + + const summary: DispatchReviewMemoryAggregationSummary = { + claimed: claimed.length, + completed: 0, + skipped: 0, + failed: 0, + proposals: 0, + }; + + for (const state of claimed) { + const result = await processClaimedAggregationScope(state, { now, generateOpportunities }); + summary.proposals += result.proposals; + if (result.status === 'completed') summary.completed += 1; + if (result.status === 'skipped') summary.skipped += 1; + if (result.status === 'failed') summary.failed += 1; + } + + return summary; +} diff --git a/apps/web/src/lib/code-reviews/review-memory/db.ts b/apps/web/src/lib/code-reviews/review-memory/db.ts index da3bb7bb45..1b79838312 100644 --- a/apps/web/src/lib/code-reviews/review-memory/db.ts +++ b/apps/web/src/lib/code-reviews/review-memory/db.ts @@ -22,6 +22,7 @@ import type { ReviewMemoryChangeRequestType, ReviewMemoryEvidenceRole, ReviewMemoryFeedbackEventSource, + ReviewMemoryEventAggregationState, ReviewMemoryPlatform, ReviewMemoryProposalScopeKind, ReviewMemoryProposalStatus, @@ -559,6 +560,101 @@ export async function updateAggregationRunStatus(input: { return run; } +export type ClaimEligibleAggregationStatesInput = { + limit?: number; + minFreshEvents: number; + minFreshWeight: number; + minDistinctSubjects: number; + minDistinctPrs: number; + staleAfterMs: number; + now?: Date; +}; + +export async function claimEligibleAggregationStates( + input: ClaimEligibleAggregationStatesInput +): Promise { + const now = input.now ?? new Date(); + const staleBefore = new Date(now.getTime() - input.staleAfterMs).toISOString(); + const limit = Math.max(1, Math.min(input.limit ?? 10, 50)); + const result = await db.execute(sql` + WITH candidates AS ( + SELECT id + FROM ${code_review_memory_aggregation_state} + WHERE ( + status IN ('eligible', 'failed') + AND next_eligible_at <= ${now.toISOString()} + AND (fresh_event_count >= ${input.minFreshEvents} OR fresh_weight >= ${input.minFreshWeight}) + AND ( + fresh_distinct_subject_count >= ${input.minDistinctSubjects} + OR fresh_distinct_pr_count >= ${input.minDistinctPrs} + ) + ) OR ( + status = 'running' + AND claimed_at IS NOT NULL + AND claimed_at <= ${staleBefore} + ) + ORDER BY fresh_weight DESC, fresh_event_count DESC, updated_at ASC, id ASC + FOR UPDATE SKIP LOCKED + LIMIT ${limit} + ) + UPDATE ${code_review_memory_aggregation_state} state + SET + status = 'running', + claimed_at = ${now.toISOString()}, + claim_token = gen_random_uuid()::text, + last_attempted_run_at = ${now.toISOString()}, + last_error_message = NULL, + updated_at = ${now.toISOString()} + FROM candidates + WHERE state.id = candidates.id + RETURNING state.* + `); + + return result.rows; +} + +export async function finishClaimedAggregationState(input: { + stateId: string; + claimToken: string; + status: Extract; + nextEligibleAt: string; + lastSuccessfulRunAt?: string | null; + lastModelSlug?: string | null; + lastErrorMessage?: string | null; + database?: ReviewMemoryDatabase; +}): Promise { + const database = databaseOrDefault(input.database); + const updateValues: Partial = { + status: input.status, + claimed_at: null, + claim_token: null, + next_eligible_at: input.nextEligibleAt, + updated_at: new Date().toISOString(), + }; + if (input.lastSuccessfulRunAt !== undefined) { + updateValues.last_successful_run_at = input.lastSuccessfulRunAt; + } + if (input.lastModelSlug !== undefined) { + updateValues.last_model_slug = input.lastModelSlug; + } + if (input.lastErrorMessage !== undefined) { + updateValues.last_error_message = input.lastErrorMessage; + } + + const [state] = await database + .update(code_review_memory_aggregation_state) + .set(updateValues) + .where( + and( + eq(code_review_memory_aggregation_state.id, input.stateId), + eq(code_review_memory_aggregation_state.claim_token, input.claimToken) + ) + ) + .returning(); + + return state ?? null; +} + export type UpsertProposalInput = { owner: ReviewMemoryOwner; platform: ReviewMemoryPlatform; @@ -749,6 +845,19 @@ export async function markFeedbackEventsIncluded(input: { .where(inArray(code_review_feedback_events.id, input.eventIds)); } +export async function markFeedbackEventsAggregationState(input: { + eventIds: string[]; + aggregationState: ReviewMemoryEventAggregationState; + database?: ReviewMemoryDatabase; +}): Promise { + if (input.eventIds.length === 0) return; + const database = databaseOrDefault(input.database); + await database + .update(code_review_feedback_events) + .set({ aggregation_state: input.aggregationState }) + .where(inArray(code_review_feedback_events.id, input.eventIds)); +} + export async function listAggregationStates(input: { owner: ReviewMemoryOwner; platform: ReviewMemoryPlatform; diff --git a/apps/web/vercel.json b/apps/web/vercel.json index 50777effae..4904023b0e 100644 --- a/apps/web/vercel.json +++ b/apps/web/vercel.json @@ -52,6 +52,10 @@ "path": "/api/cron/dispatch-pending-code-reviews", "schedule": "*/10 * * * *" }, + { + "path": "/api/cron/aggregate-review-memory", + "schedule": "0 4 * * *" + }, { "path": "/api/cron/exa-partition-maintenance", "schedule": "0 0 1 * *" From 7cfa8e71369277029ab655d1ddbad56e48f00ddb Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Fri, 22 May 2026 22:38:03 +0300 Subject: [PATCH 06/23] feat(review-memory): expose proposal APIs --- .../code-reviews/review-memory-router.test.ts | 155 +++++++++++++++ .../code-reviews/review-memory-router.ts | 183 ++++++++++++++++++ apps/web/src/routers/root-router.ts | 2 + 3 files changed, 340 insertions(+) create mode 100644 apps/web/src/routers/code-reviews/review-memory-router.test.ts create mode 100644 apps/web/src/routers/code-reviews/review-memory-router.ts diff --git a/apps/web/src/routers/code-reviews/review-memory-router.test.ts b/apps/web/src/routers/code-reviews/review-memory-router.test.ts new file mode 100644 index 0000000000..d4db42433c --- /dev/null +++ b/apps/web/src/routers/code-reviews/review-memory-router.test.ts @@ -0,0 +1,155 @@ +/* eslint-disable drizzle/enforce-delete-with-where */ +const mockDispatchReviewMemoryAggregationCron = jest.fn(); + +jest.mock('@/lib/code-reviews/review-memory/aggregation', () => ({ + dispatchReviewMemoryAggregationCron: () => mockDispatchReviewMemoryAggregationCron(), +})); + +import { db } from '@/lib/drizzle'; +import { createCallerForUser } from '@/routers/test-utils'; +import { insertTestUser } from '@/tests/helpers/user.helper'; +import { + code_review_feedback_events, + code_review_feedback_subjects, + code_review_memory_aggregation_runs, + code_review_memory_aggregation_state, + code_review_memory_proposal_evidence, + code_review_memory_proposals, + kilocode_users, +} from '@kilocode/db/schema'; +import { + recordFeedbackEvent, + upsertReviewMemoryProposal, + type ReviewMemoryOwner, +} from '@/lib/code-reviews/review-memory/db'; + +describe('reviewMemoryRouter', () => { + afterEach(async () => { + await db.delete(code_review_memory_proposal_evidence); + await db.delete(code_review_memory_proposals); + await db.delete(code_review_memory_aggregation_runs); + await db.delete(code_review_feedback_events); + await db.delete(code_review_feedback_subjects); + await db.delete(code_review_memory_aggregation_state); + await db.delete(kilocode_users); + }); + + beforeEach(() => { + jest.clearAllMocks(); + mockDispatchReviewMemoryAggregationCron.mockResolvedValue({ + claimed: 1, + completed: 1, + skipped: 0, + failed: 0, + proposals: 1, + }); + }); + + async function seedProposal(owner: ReviewMemoryOwner) { + return await upsertReviewMemoryProposal({ + owner, + platform: 'github', + repoFullName: 'acme/widgets', + scopeKind: 'repository', + proposalType: 'clarify', + title: 'Clarify widget guidance', + rationale: 'Maintainers corrected repeated widget comments.', + proposedMarkdown: '### Clarify widget guidance\n\nAvoid flagging intentional widgets.', + dedupeKey: `clarify-widget-${owner.id}`, + positiveCount: 0, + negativeCount: 3, + neutralCount: 0, + }); + } + + it('lists and returns personal proposals for the authenticated owner', async () => { + const user = await insertTestUser(); + const owner = { type: 'user' as const, id: user.id }; + const proposal = await seedProposal(owner); + await recordFeedbackEvent({ + owner, + platform: 'github', + repoFullName: 'acme/widgets', + prNumber: 1, + eventSource: 'github_webhook', + signalKind: 'corrective_reply', + sentiment: 'negative', + strength: 3, + externalEventId: 'router-dashboard-event', + }); + const caller = await createCallerForUser(user.id); + + await expect(caller.reviewMemory.getDashboardSummary({ platform: 'github' })).resolves.toEqual( + expect.objectContaining({ + actionableProposalCount: 1, + repositories: [expect.objectContaining({ repoFullName: 'acme/widgets' })], + }) + ); + await expect(caller.reviewMemory.listProposals({ platform: 'github' })).resolves.toEqual([ + expect.objectContaining({ id: proposal.id, title: 'Clarify widget guidance' }), + ]); + await expect(caller.reviewMemory.getProposal({ proposalId: proposal.id })).resolves.toEqual({ + proposal: expect.objectContaining({ id: proposal.id }), + evidence: [], + }); + }); + + it('prevents other users from reading proposals', async () => { + const ownerUser = await insertTestUser(); + const otherUser = await insertTestUser(); + const proposal = await seedProposal({ type: 'user', id: ownerUser.id }); + const caller = await createCallerForUser(otherUser.id); + + await expect( + caller.reviewMemory.getProposal({ proposalId: proposal.id }) + ).rejects.toMatchObject({ + code: 'NOT_FOUND', + }); + }); + + it('edits and rejects proposals for the authenticated owner', async () => { + const user = await insertTestUser(); + const owner = { type: 'user' as const, id: user.id }; + const proposal = await seedProposal(owner); + const caller = await createCallerForUser(user.id); + + const edited = await caller.reviewMemory.updateProposal({ + proposalId: proposal.id, + title: 'Edited widget guidance', + rationale: 'The rationale was reviewed by a maintainer.', + proposedMarkdown: '### Edited widget guidance\n\nNarrow this check to generated widgets.', + scopeKind: 'file', + scopeValue: 'src/widget.ts', + }); + expect(edited).toEqual( + expect.objectContaining({ + status: 'edited', + title: 'Edited widget guidance', + edited_by_user_id: user.id, + }) + ); + + const rejected = await caller.reviewMemory.rejectProposal({ proposalId: proposal.id }); + expect(rejected).toEqual( + expect.objectContaining({ + status: 'rejected', + rejected_by_user_id: user.id, + }) + ); + }); + + it('refreshes a scope and dispatches aggregation when manually triggered', async () => { + const user = await insertTestUser(); + const caller = await createCallerForUser(user.id); + + await expect( + caller.reviewMemory.triggerAnalysis({ platform: 'github', repoFullName: 'acme/widgets' }) + ).resolves.toEqual( + expect.objectContaining({ + state: expect.objectContaining({ repo_full_name: 'acme/widgets', platform: 'github' }), + summary: expect.objectContaining({ claimed: 1 }), + }) + ); + expect(mockDispatchReviewMemoryAggregationCron).toHaveBeenCalledTimes(1); + }); +}); diff --git a/apps/web/src/routers/code-reviews/review-memory-router.ts b/apps/web/src/routers/code-reviews/review-memory-router.ts new file mode 100644 index 0000000000..27adf7cf85 --- /dev/null +++ b/apps/web/src/routers/code-reviews/review-memory-router.ts @@ -0,0 +1,183 @@ +import { createTRPCRouter, baseProcedure, type TRPCContext } from '@/lib/trpc/init'; +import { ensureOrganizationAccess } from '@/routers/organizations/utils'; +import { dispatchReviewMemoryAggregationCron } from '@/lib/code-reviews/review-memory/aggregation'; +import { + countActionableProposals, + getReviewMemoryProposal, + listAggregationStates, + listProposalEvidence, + listReviewMemoryProposals, + listReviewMemoryRepositories, + refreshAggregationStateForScope, + rejectReviewMemoryProposal, + updateReviewMemoryProposal, + type ReviewMemoryOwner, +} from '@/lib/code-reviews/review-memory/db'; +import { TRPCError } from '@trpc/server'; +import * as z from 'zod'; + +const ReviewMemoryPlatformSchema = z.enum(['github', 'gitlab']); +const ReviewMemoryProposalStatusSchema = z.enum([ + 'open', + 'edited', + 'approved', + 'opening_change_request', + 'change_request_opened', + 'change_request_failed', + 'rejected', + 'superseded', +]); +const ReviewMemoryProposalTypeSchema = z.enum(['suppress', 'clarify', 'narrow', 'reinforce']); +const ReviewMemoryProposalScopeKindSchema = z.enum(['repository', 'path_glob', 'file', 'language']); + +const OwnerInputSchema = z.object({ + organizationId: z.uuid().optional(), +}); + +const PlatformOwnerInputSchema = OwnerInputSchema.extend({ + platform: ReviewMemoryPlatformSchema, +}); + +async function ownerFromInput( + ctx: TRPCContext, + input: z.infer +): Promise { + if (input.organizationId) { + await ensureOrganizationAccess(ctx, input.organizationId); + return { type: 'org', id: input.organizationId }; + } + + return { type: 'user', id: ctx.user.id }; +} + +export const reviewMemoryRouter = createTRPCRouter({ + getDashboardSummary: baseProcedure + .input( + PlatformOwnerInputSchema.extend({ + repoFullName: z.string().optional(), + }) + ) + .query(async ({ ctx, input }) => { + const owner = await ownerFromInput(ctx, input); + const [repositories, states, actionableProposalCount] = await Promise.all([ + listReviewMemoryRepositories({ owner, platform: input.platform }), + listAggregationStates({ + owner, + platform: input.platform, + repoFullName: input.repoFullName, + }), + countActionableProposals({ + owner, + platform: input.platform, + repoFullName: input.repoFullName, + }), + ]); + + return { + repositories: input.repoFullName + ? repositories.filter(repository => repository.repoFullName === input.repoFullName) + : repositories, + states, + actionableProposalCount, + }; + }), + + listProposals: baseProcedure + .input( + PlatformOwnerInputSchema.extend({ + repoFullName: z.string().optional(), + statuses: z.array(ReviewMemoryProposalStatusSchema).optional(), + proposalType: ReviewMemoryProposalTypeSchema.optional(), + limit: z.number().int().min(1).max(100).optional(), + offset: z.number().int().min(0).optional(), + }) + ) + .query(async ({ ctx, input }) => { + const owner = await ownerFromInput(ctx, input); + return await listReviewMemoryProposals({ + owner, + platform: input.platform, + repoFullName: input.repoFullName, + statuses: input.statuses, + proposalType: input.proposalType, + limit: input.limit, + offset: input.offset, + }); + }), + + getProposal: baseProcedure + .input(OwnerInputSchema.extend({ proposalId: z.uuid() })) + .query(async ({ ctx, input }) => { + const owner = await ownerFromInput(ctx, input); + const proposal = await getReviewMemoryProposal({ owner, proposalId: input.proposalId }); + if (!proposal) { + throw new TRPCError({ code: 'NOT_FOUND', message: 'Review memory proposal not found' }); + } + + const evidence = await listProposalEvidence({ proposalId: proposal.id }); + return { proposal, evidence }; + }), + + updateProposal: baseProcedure + .input( + OwnerInputSchema.extend({ + proposalId: z.uuid(), + title: z.string().min(1).max(140), + rationale: z.string().min(1).max(1_500), + proposedMarkdown: z.string().min(1).max(4_000), + scopeKind: ReviewMemoryProposalScopeKindSchema, + scopeValue: z.string().nullable().optional(), + }) + ) + .mutation(async ({ ctx, input }) => { + const owner = await ownerFromInput(ctx, input); + const proposal = await updateReviewMemoryProposal({ + owner, + proposalId: input.proposalId, + editedByUserId: ctx.user.id, + title: input.title, + rationale: input.rationale, + proposedMarkdown: input.proposedMarkdown, + scopeKind: input.scopeKind, + scopeValue: input.scopeValue, + }); + if (!proposal) { + throw new TRPCError({ code: 'NOT_FOUND', message: 'Review memory proposal not found' }); + } + return proposal; + }), + + rejectProposal: baseProcedure + .input(OwnerInputSchema.extend({ proposalId: z.uuid() })) + .mutation(async ({ ctx, input }) => { + const owner = await ownerFromInput(ctx, input); + const proposal = await rejectReviewMemoryProposal({ + owner, + proposalId: input.proposalId, + rejectedByUserId: ctx.user.id, + }); + if (!proposal) { + throw new TRPCError({ code: 'NOT_FOUND', message: 'Review memory proposal not found' }); + } + return proposal; + }), + + triggerAnalysis: baseProcedure + .input( + PlatformOwnerInputSchema.extend({ + repoFullName: z.string().min(1), + platformProjectId: z.number().int().nullable().optional(), + }) + ) + .mutation(async ({ ctx, input }) => { + const owner = await ownerFromInput(ctx, input); + const state = await refreshAggregationStateForScope({ + owner, + platform: input.platform, + repoFullName: input.repoFullName, + platformProjectId: input.platformProjectId, + }); + const summary = await dispatchReviewMemoryAggregationCron({ limit: 1 }); + return { state, summary }; + }), +}); diff --git a/apps/web/src/routers/root-router.ts b/apps/web/src/routers/root-router.ts index 4f193a0cab..84b84f5cf5 100644 --- a/apps/web/src/routers/root-router.ts +++ b/apps/web/src/routers/root-router.ts @@ -19,6 +19,7 @@ import { linearRouter } from '@/routers/linear-router'; import { dolthubRouter } from '@/routers/dolthub-router'; import { discordRouter } from '@/routers/discord-router'; import { codeReviewRouter } from '@/routers/code-reviews/code-reviews-router'; +import { reviewMemoryRouter } from '@/routers/code-reviews/review-memory-router'; import { personalReviewAgentRouter } from '@/routers/code-reviews-router'; import { byokRouter } from '@/routers/byok-router'; import { appBuilderRouter } from '@/routers/app-builder-router'; @@ -61,6 +62,7 @@ export const rootRouter = createTRPCRouter({ cloudAgent: cloudAgentRouter, cloudAgentNext: cloudAgentNextRouter, codeReviews: codeReviewRouter, + reviewMemory: reviewMemoryRouter, personalReviewAgent: personalReviewAgentRouter, byok: byokRouter, appBuilder: appBuilderRouter, From 682218b1bceb49c98539ac226fb0f1fe8f0c7b25 Mon Sep 17 00:00:00 2001 From: Alex Alecu Date: Fri, 22 May 2026 22:42:49 +0300 Subject: [PATCH 07/23] feat(review-memory): add memory tab UI --- .../code-reviews/ReviewAgentPageClient.tsx | 31 +- .../code-reviews/ReviewAgentPageClient.tsx | 31 +- .../code-reviews/ReviewMemoryPanel.tsx | 292 ++++++++++++++++++ 3 files changed, 348 insertions(+), 6 deletions(-) create mode 100644 apps/web/src/components/code-reviews/ReviewMemoryPanel.tsx diff --git a/apps/web/src/app/(app)/code-reviews/ReviewAgentPageClient.tsx b/apps/web/src/app/(app)/code-reviews/ReviewAgentPageClient.tsx index 78fa840828..c7aefb4cb6 100644 --- a/apps/web/src/app/(app)/code-reviews/ReviewAgentPageClient.tsx +++ b/apps/web/src/app/(app)/code-reviews/ReviewAgentPageClient.tsx @@ -4,12 +4,13 @@ import { useEffect } from 'react'; import { toast } from 'sonner'; import { ReviewConfigForm } from '@/components/code-reviews/ReviewConfigForm'; import { CodeReviewJobsCard } from '@/components/code-reviews/CodeReviewJobsCard'; +import { ReviewMemoryPanel } from '@/components/code-reviews/ReviewMemoryPanel'; import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { SetPageTitle } from '@/components/SetPageTitle'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; -import { Rocket, ExternalLink, Settings2, ListChecks } from 'lucide-react'; +import { Brain, Rocket, ExternalLink, Settings2, ListChecks } from 'lucide-react'; import { useTRPC } from '@/lib/trpc/utils'; import { useQuery } from '@tanstack/react-query'; import Link from 'next/link'; @@ -153,7 +154,7 @@ export function ReviewAgentPageClient({ {/* GitHub Configuration Tabs */} - + Config @@ -166,6 +167,14 @@ export function ReviewAgentPageClient({ Jobs + + + Memory + @@ -186,6 +195,10 @@ export function ReviewAgentPageClient({ )} + + + + @@ -213,7 +226,7 @@ export function ReviewAgentPageClient({ {/* GitLab Configuration Tabs */} - + Config @@ -226,6 +239,14 @@ export function ReviewAgentPageClient({ Jobs + + + Memory + @@ -261,6 +282,10 @@ export function ReviewAgentPageClient({ )} + + + + diff --git a/apps/web/src/app/(app)/organizations/[id]/code-reviews/ReviewAgentPageClient.tsx b/apps/web/src/app/(app)/organizations/[id]/code-reviews/ReviewAgentPageClient.tsx index 99131626a2..6fdc204a2a 100644 --- a/apps/web/src/app/(app)/organizations/[id]/code-reviews/ReviewAgentPageClient.tsx +++ b/apps/web/src/app/(app)/organizations/[id]/code-reviews/ReviewAgentPageClient.tsx @@ -4,12 +4,13 @@ import { useEffect } from 'react'; import { toast } from 'sonner'; import { ReviewConfigForm } from '@/components/code-reviews/ReviewConfigForm'; import { CodeReviewJobsCard } from '@/components/code-reviews/CodeReviewJobsCard'; +import { ReviewMemoryPanel } from '@/components/code-reviews/ReviewMemoryPanel'; import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { SetPageTitle } from '@/components/SetPageTitle'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; -import { Rocket, ExternalLink, Settings2, ListChecks } from 'lucide-react'; +import { Brain, Rocket, ExternalLink, Settings2, ListChecks } from 'lucide-react'; import { useTRPC } from '@/lib/trpc/utils'; import { useQuery } from '@tanstack/react-query'; import Link from 'next/link'; @@ -160,7 +161,7 @@ export function ReviewAgentPageClient({ {/* GitHub Configuration Tabs */} - + Config @@ -173,6 +174,14 @@ export function ReviewAgentPageClient({ Jobs + + + Memory + @@ -193,6 +202,10 @@ export function ReviewAgentPageClient({ )} + + + + @@ -220,7 +233,7 @@ export function ReviewAgentPageClient({ {/* GitLab Configuration Tabs */} - + Config @@ -233,6 +246,14 @@ export function ReviewAgentPageClient({ Jobs + + + Memory + @@ -269,6 +290,10 @@ export function ReviewAgentPageClient({ )} + + + + diff --git a/apps/web/src/components/code-reviews/ReviewMemoryPanel.tsx b/apps/web/src/components/code-reviews/ReviewMemoryPanel.tsx new file mode 100644 index 0000000000..01567d60ba --- /dev/null +++ b/apps/web/src/components/code-reviews/ReviewMemoryPanel.tsx @@ -0,0 +1,292 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { toast } from 'sonner'; +import { Brain, RefreshCw, Sparkles } from 'lucide-react'; +import { Badge } from '@/components/ui/badge'; +import { Button } from '@/components/ui/button'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Textarea } from '@/components/ui/textarea'; +import { useTRPC } from '@/lib/trpc/utils'; +import { cn } from '@/lib/utils'; +import type { ReviewMemoryProposalStatus } from '@kilocode/db/schema-types'; + +type Platform = 'github' | 'gitlab'; + +type ReviewMemoryPanelProps = { + organizationId?: string; + platform: Platform; +}; + +const ACTIVE_PROPOSAL_STATUSES: ReviewMemoryProposalStatus[] = [ + 'open', + 'edited', + 'approved', + 'change_request_failed', +]; + +function readable(value: string) { + return value.replace(/_/g, ' '); +} + +export function ReviewMemoryPanel({ organizationId, platform }: ReviewMemoryPanelProps) { + const trpc = useTRPC(); + const queryClient = useQueryClient(); + const [selectedProposalId, setSelectedProposalId] = useState(null); + const [isEditing, setIsEditing] = useState(false); + const [title, setTitle] = useState(''); + const [rationale, setRationale] = useState(''); + const [proposedMarkdown, setProposedMarkdown] = useState(''); + + const ownerInput = organizationId ? { organizationId, platform } : { platform }; + const proposalInput = { ...ownerInput, statuses: ACTIVE_PROPOSAL_STATUSES, limit: 50 }; + const summaryQuery = useQuery(trpc.reviewMemory.getDashboardSummary.queryOptions(ownerInput)); + const proposalsQuery = useQuery(trpc.reviewMemory.listProposals.queryOptions(proposalInput)); + const proposals = proposalsQuery.data ?? []; + const selectedProposal = + proposals.find(proposal => proposal.id === selectedProposalId) ?? proposals[0]; + const analysisRepo = + summaryQuery.data?.repositories[0]?.repoFullName ?? selectedProposal?.repo_full_name; + + useEffect(() => { + if (!selectedProposal) return; + setSelectedProposalId(selectedProposal.id); + setTitle(selectedProposal.title); + setRationale(selectedProposal.rationale); + setProposedMarkdown(selectedProposal.proposed_markdown); + setIsEditing(false); + }, [selectedProposal?.id]); + + const invalidateReviewMemory = async () => { + await Promise.all([ + queryClient.invalidateQueries({ + queryKey: trpc.reviewMemory.getDashboardSummary.queryKey(ownerInput), + }), + queryClient.invalidateQueries({ + queryKey: trpc.reviewMemory.listProposals.queryKey(proposalInput), + }), + ]); + }; + + const triggerAnalysis = useMutation( + trpc.reviewMemory.triggerAnalysis.mutationOptions({ + onSuccess: async () => { + toast.success('Review memory analysis queued'); + await invalidateReviewMemory(); + }, + onError: error => toast.error('Could not analyze feedback', { description: error.message }), + }) + ); + const updateProposal = useMutation( + trpc.reviewMemory.updateProposal.mutationOptions({ + onSuccess: async () => { + toast.success('Memory proposal updated'); + setIsEditing(false); + await invalidateReviewMemory(); + }, + onError: error => toast.error('Could not update proposal', { description: error.message }), + }) + ); + const rejectProposal = useMutation( + trpc.reviewMemory.rejectProposal.mutationOptions({ + onSuccess: async () => { + toast.success('Memory proposal dismissed'); + await invalidateReviewMemory(); + }, + onError: error => toast.error('Could not dismiss proposal', { description: error.message }), + }) + ); + + const isLoading = summaryQuery.isLoading || proposalsQuery.isLoading; + + return ( +
+ + +
+ + + Review memory + + + Turn recurring reviewer feedback into proposed REVIEW.md guidance. + +
+ +
+ + + + sum + state.fresh_event_count, 0) ?? + 0 + } + /> + +
+ +
+ + + Proposals + + Review, edit, or dismiss learned guidance before applying it. + + + + {isLoading ? ( +
+ Loading memory... +
+ ) : proposals.length === 0 ? ( +
+ No proposals yet. Kilo waits for repeated, high-signal feedback before suggesting + memory changes. +
+ ) : ( + proposals.map(proposal => ( + + )) + )} +
+
+ + + + {selectedProposal ? selectedProposal.title : 'Proposal details'} + + {selectedProposal + ? `${selectedProposal.negative_count} negative, ${selectedProposal.positive_count} positive, ${selectedProposal.neutral_count} neutral signals` + : 'Select a proposal to review its guidance.'} + + + + {selectedProposal ? ( + <> + {isEditing ? ( +
+