-
-
{message}
+
+
+
Where this was found
+
+ {uniqueLocations.length} code {uniqueLocations.length === 1 ? 'location' : 'locations'}
+
- {detail &&
{detail}
}
- {children &&
{children}
}
+
+ {visibleLocations.map(location => (
+
+
+ {location}
+
+ ))}
+
+ {uniqueLocations.length > 2 && (
+
setShowAll(current => !current)}
+ >
+ {showAll ? 'Show fewer locations' : `Show all ${uniqueLocations.length} locations`}
+
+ )}
);
}
-function ErrorPanel({
- message,
- retryLabel,
- onRetry,
- disabled,
+type RemediationPresentationSummary = {
+ prUrl: string | null;
+ prNumber: number | null;
+ prDraft: boolean | null;
+ failureCode: string | null;
+ blockedReason: string | null;
+};
+
+function getRemediationPresentation({
+ status,
+ finding,
+ latestAttempt,
+ summary,
+ canStart,
+ unavailableReason,
+ unavailableCopy,
+ isAwaitingStart,
}: {
- message: string;
- retryLabel: string;
- onRetry: () => void;
- disabled: boolean;
-}) {
- return (
-
-
{message}
-
- {retryLabel}
-
-
- );
-}
+ status: string | null;
+ finding: SecurityFinding;
+ latestAttempt: RemediationAttempt | null;
+ summary: RemediationPresentationSummary;
+ canStart: boolean;
+ unavailableReason: string | null | undefined;
+ unavailableCopy: string | null;
+ isAwaitingStart: boolean;
+}): RemediationPresentation {
+ const cancellationRequested = Boolean(latestAttempt?.cancellationRequestedAt);
+ const requester = latestAttempt?.origin === 'manual' ? 'User request' : 'Security Agent';
+ const prUrl = latestAttempt?.prUrl ?? summary.prUrl;
+ const prNumber = latestAttempt?.prNumber ?? summary.prNumber;
+ const prDraft = latestAttempt?.prDraft ?? summary.prDraft;
+ const failureCode = latestAttempt?.failureCode ?? summary.failureCode;
+ const blockedReason = latestAttempt?.blockedReason ?? summary.blockedReason;
-function EmptyPanel({ children, text }: { children?: React.ReactNode; text: string }) {
- return (
-
- );
+ if (isAwaitingStart) {
+ return {
+ hero: {
+ title: 'Queueing remediation',
+ description:
+ 'Security Agent is creating the remediation attempt and reserving its repository branch.',
+ icon: Loader2,
+ tone: 'warning',
+ spinning: true,
+ },
+ summary: [
+ {
+ label: 'Current state',
+ value: 'Request in progress',
+ detail: 'The attempt is not persisted yet',
+ icon: Clock3,
+ tone: 'warning',
+ },
+ {
+ label: 'How did it start?',
+ value: 'Manual request',
+ detail: 'Duplicate starts remain suppressed',
+ icon: UserRound,
+ tone: 'neutral',
+ },
+ {
+ label: 'What happens next?',
+ value: 'Cloud Agent queue',
+ detail: 'Status refreshes automatically',
+ icon: Sparkles,
+ tone: 'neutral',
+ },
+ ],
+ context:
+ 'Starting remediation creates an attempt. It does not mark the Security Finding fixed.',
+ action: {
+ label: 'Current status',
+ title: 'Creating the remediation attempt',
+ description: 'Wait for Security Agent to confirm the queued attempt.',
+ },
+ disclosureTitle: 'Remediation progress',
+ steps: [
+ {
+ title: 'Create remediation attempt',
+ detail: 'Security Agent is validating and saving the request.',
+ state: 'running',
+ },
+ {
+ title: 'Wait for Cloud Agent',
+ detail: 'Execution begins after the request is accepted.',
+ state: 'pending',
+ },
+ ],
+ };
+ }
+
+ if (cancellationRequested && isActiveRemediationStatus(status)) {
+ return {
+ hero: {
+ title: 'Cancellation has been requested',
+ description:
+ 'Security Agent asked Cloud Agent to stop. The attempt remains active until Cloud Agent confirms cancellation or returns another final result.',
+ icon: Loader2,
+ tone: 'warning',
+ spinning: true,
+ },
+ summary: [
+ {
+ label: 'Current state',
+ value: 'Waiting for confirmation',
+ detail: 'The attempt is not cancelled yet',
+ icon: Clock3,
+ tone: 'warning',
+ },
+ {
+ label: 'Cancellation requested',
+ value: latestAttempt?.cancellationRequestedAt
+ ? formatUtcDate(latestAttempt.cancellationRequestedAt)
+ : 'Recently',
+ detail: 'Security Agent accepted the request',
+ icon: UserRound,
+ tone: 'neutral',
+ },
+ {
+ label: 'Possible outcome',
+ value: 'Cancelled or PR opened',
+ detail: 'A pull request can still win the race',
+ icon: GitPullRequest,
+ tone: 'warning',
+ },
+ ],
+ context:
+ 'Cancellation is best effort. If Cloud Agent opens a verified pull request before stopping, Security Agent will show that result.',
+ action: {
+ label: 'Current status',
+ title: 'Waiting for Cloud Agent',
+ description:
+ 'No further action is available until Cloud Agent confirms cancellation or returns a pull request.',
+ },
+ disclosureTitle: 'Cancellation progress',
+ steps: [
+ {
+ title: 'Remediation started',
+ detail: 'Cloud Agent began repository work.',
+ state: 'done',
+ },
+ {
+ title: 'Cancellation requested',
+ detail: 'Security Agent accepted the request and asked Cloud Agent to interrupt.',
+ state: 'done',
+ },
+ {
+ title: 'Waiting for Cloud Agent',
+ detail: 'Cloud Agent has not confirmed interruption or returned a pull request.',
+ state: 'waiting',
+ },
+ ],
+ };
+ }
+
+ if (!status && canStart) {
+ return {
+ hero: {
+ title: 'Ready to prepare a fix',
+ description: `Security Agent can ask Cloud Agent to update ${finding.package_name}, review affected usage, and open a pull request for your team.`,
+ icon: GitPullRequest,
+ tone: 'neutral',
+ },
+ summary: [
+ {
+ label: 'Why available?',
+ value: 'Concrete fix path',
+ detail: 'Analysis supports a user-reviewed repository change',
+ icon: ShieldCheck,
+ tone: 'success',
+ },
+ {
+ label: 'How does it start?',
+ value: 'Manual request',
+ detail: 'This action is independent of Auto Remediation',
+ icon: UserRound,
+ tone: 'neutral',
+ },
+ {
+ label: 'What happens next?',
+ value: 'Review a pull request',
+ detail: 'The finding remains open until source sync',
+ icon: GitPullRequest,
+ tone: 'neutral',
+ },
+ ],
+ context:
+ 'Starting remediation creates a Security Remediation Attempt. It does not mark the finding fixed.',
+ action: {
+ label: 'Next step',
+ title: 'Prepare a fix',
+ description:
+ 'Cloud Agent will make the smallest safe change it can identify, run focused checks, and open a pull request for review.',
+ },
+ disclosureTitle: 'Why remediation is available',
+ steps: [
+ {
+ title: 'Finding is still open',
+ detail: 'GitHub continues to report the vulnerable package in this repository.',
+ state: 'done',
+ },
+ {
+ title: 'Analysis is current',
+ detail: 'The latest codebase analysis matches current finding data.',
+ state: 'done',
+ },
+ {
+ title: 'A concrete response is available',
+ detail: 'Analysis provides enough evidence for a user-reviewed remediation attempt.',
+ state: 'done',
+ },
+ {
+ title: 'No remediation is active',
+ detail: 'No other attempt or known remediation pull request blocks a new start.',
+ state: 'done',
+ },
+ ],
+ };
+ }
+
+ if (!status) {
+ const analysisRequired = isCodebaseAnalysisRequiredReason(unavailableReason);
+ return {
+ hero: {
+ title: analysisRequired ? 'Analyze the repository first' : 'Remediation is unavailable',
+ description:
+ unavailableCopy ||
+ 'Security Agent cannot start a remediation attempt for this finding in its current state.',
+ icon: analysisRequired ? Brain : Ban,
+ tone: 'warning',
+ },
+ summary: [
+ {
+ label: 'What is known?',
+ value: 'Published vulnerability',
+ detail: 'The source advisory remains available',
+ icon: ShieldAlert,
+ tone: 'warning',
+ },
+ {
+ label: 'What is missing?',
+ value: analysisRequired ? 'Repository analysis' : 'An eligible fix path',
+ detail: unavailableCopy || 'Current safety gates block a new attempt',
+ icon: analysisRequired ? Brain : Ban,
+ tone: 'warning',
+ },
+ {
+ label: 'What should I do?',
+ value: analysisRequired ? 'Analyze repository' : 'Review finding state',
+ detail: 'Resolve the recorded blocker before starting remediation',
+ icon: Search,
+ tone: 'neutral',
+ },
+ ],
+ context:
+ 'Security Agent derives remediation availability from server-provided safety and policy checks.',
+ action: {
+ label: 'Next step',
+ title: analysisRequired ? 'Check repository risk and fix options' : 'Resolve the blocker',
+ description:
+ unavailableCopy || 'Review analysis and finding status before trying remediation again.',
+ },
+ disclosureTitle: analysisRequired
+ ? 'What blocks remediation'
+ : 'Why remediation is unavailable',
+ steps: [
+ {
+ title: 'Source advisory is available',
+ detail: `${finding.package_name} matches the published affected range.`,
+ state: 'done',
+ },
+ {
+ title: analysisRequired
+ ? 'Repository analysis is required'
+ : 'A safety gate blocks remediation',
+ detail: unavailableCopy || 'Security Agent cannot safely admit a new attempt.',
+ state: 'attention',
+ },
+ {
+ title: 'Remediation decision is pending',
+ detail: 'A new attempt remains unavailable until the blocker is resolved.',
+ state: 'pending',
+ },
+ ],
+ };
+ }
+
+ if (status === 'queued') {
+ return {
+ hero: {
+ title: 'Remediation is queued',
+ description:
+ 'Security Agent accepted the request. Cloud Agent will begin when execution capacity is available.',
+ icon: Loader2,
+ tone: 'warning',
+ spinning: true,
+ },
+ summary: [
+ {
+ label: 'Current state',
+ value: 'Waiting for capacity',
+ detail: 'No repository changes have started',
+ icon: Clock3,
+ tone: 'warning',
+ },
+ {
+ label: 'Attempt started by',
+ value: requester,
+ detail: formatRemediationOrigin(latestAttempt?.origin ?? 'manual'),
+ icon: UserRound,
+ tone: 'neutral',
+ },
+ {
+ label: 'Next update',
+ value: 'Cloud Agent starts',
+ detail: 'Status refreshes automatically',
+ icon: Sparkles,
+ tone: 'neutral',
+ },
+ ],
+ context:
+ 'Only one active remediation attempt can exist for this finding. Another start is unavailable while this attempt is queued.',
+ action: {
+ label: 'Attempt controls',
+ title: 'Manage this attempt',
+ description:
+ 'You can cancel before Cloud Agent starts if this remediation is no longer needed.',
+ },
+ disclosureTitle: 'Remediation progress',
+ steps: [
+ {
+ title: 'Request accepted',
+ detail: `Security Agent created attempt #${latestAttempt?.attemptNumber ?? 1}.`,
+ state: 'done',
+ },
+ {
+ title: 'Waiting for Cloud Agent',
+ detail: 'The attempt will start when execution capacity is available.',
+ state: 'waiting',
+ },
+ {
+ title: 'Prepare repository change',
+ detail: 'Cloud Agent has not started repository work.',
+ state: 'pending',
+ },
+ {
+ title: 'Open pull request',
+ detail: 'No pull request exists yet.',
+ state: 'pending',
+ },
+ ],
+ };
+ }
+
+ if (status === 'launching') {
+ return {
+ hero: {
+ title: 'Cloud Agent is starting',
+ description:
+ 'Security Agent claimed the queued attempt and is opening the isolated session that will prepare the repository change.',
+ icon: Loader2,
+ tone: 'warning',
+ spinning: true,
+ },
+ summary: [
+ {
+ label: 'Current state',
+ value: 'Launching session',
+ detail: 'Repository work has not been confirmed yet',
+ icon: Sparkles,
+ tone: 'warning',
+ },
+ {
+ label: 'Attempt started by',
+ value: requester,
+ detail: formatRemediationOrigin(latestAttempt?.origin ?? 'manual'),
+ icon: UserRound,
+ tone: 'neutral',
+ },
+ {
+ label: 'Next update',
+ value: 'Repository work begins',
+ detail: 'Status refreshes automatically',
+ icon: GitBranch,
+ tone: 'neutral',
+ },
+ ],
+ context:
+ 'Launching is an active Security Remediation Attempt. A launch failure can return the attempt to queued or end it as failed.',
+ action: {
+ label: 'Attempt controls',
+ title: 'Manage this attempt',
+ description:
+ 'You can request cancellation while Security Agent finishes starting the session.',
+ },
+ disclosureTitle: 'Remediation progress',
+ steps: [
+ {
+ title: 'Request accepted',
+ detail: `Security Agent created attempt #${latestAttempt?.attemptNumber ?? 1}.`,
+ state: 'done',
+ },
+ {
+ title: 'Cloud Agent session starting',
+ detail: 'The attempt is creating an isolated execution session.',
+ state: 'running',
+ },
+ {
+ title: 'Prepare repository change',
+ detail: 'Repository work has not started yet.',
+ state: 'pending',
+ },
+ {
+ title: 'Open pull request',
+ detail: 'No pull request exists yet.',
+ state: 'pending',
+ },
+ ],
+ };
+ }
+
+ if (status === 'running') {
+ return {
+ hero: {
+ title: 'Cloud Agent is preparing a fix',
+ description:
+ 'The agent is updating the dependency, reviewing affected usage, and running focused validation before deciding whether to open a pull request.',
+ icon: Loader2,
+ tone: 'warning',
+ spinning: true,
+ },
+ summary: [
+ {
+ label: 'Current state',
+ value: 'Repository work',
+ detail: 'Changes and validation are in progress',
+ icon: Wrench,
+ tone: 'warning',
+ },
+ {
+ label: 'Attempt started by',
+ value: requester,
+ detail: formatRemediationOrigin(latestAttempt?.origin ?? 'manual'),
+ icon: UserRound,
+ tone: 'neutral',
+ },
+ {
+ label: 'Expected outcome',
+ value: 'Pull request or explanation',
+ detail: 'No-change and failure outcomes stay explicit',
+ icon: GitPullRequest,
+ tone: 'neutral',
+ },
+ ],
+ context:
+ 'A running remediation does not change finding status. The source record remains open until GitHub reports it fixed or it is dismissed.',
+ action: {
+ label: 'Attempt controls',
+ title: 'Manage this attempt',
+ description:
+ 'Status updates automatically. Cancellation asks Cloud Agent to stop but cannot guarantee it stops before opening a pull request.',
+ },
+ disclosureTitle: 'Remediation progress',
+ steps: [
+ {
+ title: 'Request accepted',
+ detail: `Security Agent created attempt #${latestAttempt?.attemptNumber ?? 1}.`,
+ state: 'done',
+ },
+ {
+ title: 'Cloud Agent started',
+ detail: 'The isolated session and remediation branch are ready.',
+ state: 'done',
+ },
+ {
+ title: 'Prepare and validate change',
+ detail: `Cloud Agent is working on ${finding.package_name} and affected usage.`,
+ state: 'running',
+ },
+ {
+ title: 'Open pull request',
+ detail: 'No verified pull request exists yet.',
+ state: 'pending',
+ },
+ ],
+ };
+ }
+
+ if (status === 'pr_opened') {
+ const draft = Boolean(prDraft);
+ return {
+ hero: {
+ title: draft
+ ? 'Draft remediation pull request is ready'
+ : 'Remediation pull request is ready',
+ description: draft
+ ? 'Cloud Agent prepared a concrete fix, but incomplete validation or recorded risk needs reviewer attention.'
+ : `Cloud Agent prepared a repository change and opened${prNumber ? ` pull request #${prNumber}` : ' a pull request'} for team review.`,
+ icon: GitPullRequest,
+ tone: draft ? 'warning' : 'success',
+ },
+ summary: [
+ {
+ label: 'Outcome',
+ value: `${draft ? 'Draft ' : ''}PR${prNumber ? ` #${prNumber}` : ' opened'}`,
+ detail: 'Expected repository and remediation branch recorded',
+ icon: GitPullRequest,
+ tone: 'success',
+ },
+ {
+ label: 'Validation',
+ value: latestAttempt?.validationEvidence?.length
+ ? 'Evidence recorded'
+ : 'Review required',
+ detail: draft
+ ? latestAttempt?.draftReason || 'Validation or risk requires reviewer attention'
+ : 'Review recorded checks before merging',
+ icon: FileCheck2,
+ tone: draft ? 'warning' : 'success',
+ },
+ {
+ label: 'Finding state',
+ value: 'Still open',
+ detail: 'GitHub confirms when the vulnerability is fixed',
+ icon: ShieldAlert,
+ tone: 'warning',
+ },
+ ],
+ context:
+ 'A pull request is not the same as a fixed finding. The finding stays open until GitHub reports the vulnerability resolved.',
+ action: {
+ label: 'Next step',
+ title: draft ? 'Review validation gaps and code changes' : 'Review the pull request',
+ description: draft
+ ? latestAttempt?.draftReason ||
+ 'Run missing validation and review recorded risks before marking the pull request ready.'
+ : 'Review the dependency update and related code changes before merging into the default branch.',
+ },
+ disclosureTitle: draft ? 'Why the pull request is a draft' : 'How remediation completed',
+ steps: [
+ {
+ title: 'Prepared a concrete fix',
+ detail: 'Cloud Agent recorded a repository change for this finding.',
+ state: 'done',
+ },
+ {
+ title: 'Ran available validation',
+ detail: latestAttempt?.validationEvidence?.length
+ ? `${latestAttempt.validationEvidence.length} validation ${latestAttempt.validationEvidence.length === 1 ? 'record' : 'records'} available in attempt history.`
+ : 'No structured validation evidence was recorded.',
+ state: draft ? 'attention' : 'done',
+ },
+ {
+ title: 'Verified pull request outcome',
+ detail: prNumber
+ ? `Pull request #${prNumber} belongs to the recorded remediation attempt.`
+ : 'A pull request URL is recorded for this remediation attempt.',
+ state: 'done',
+ },
+ ],
+ };
+ }
+
+ if (status === 'blocked') {
+ return {
+ hero: {
+ title: 'Remediation was blocked',
+ description:
+ blockedReason ||
+ 'Security Agent intentionally stopped before creating a competing or unsafe repository change.',
+ icon: Ban,
+ tone: 'warning',
+ },
+ summary: [
+ {
+ label: 'Block reason',
+ value: blockedReason || 'Safety gate stopped the attempt',
+ detail: 'Security Agent did not continue past the blocker',
+ icon: Ban,
+ tone: 'warning',
+ },
+ {
+ label: 'Repository outcome',
+ value: prUrl ? 'Related pull request recorded' : 'No remediation PR opened',
+ detail: 'Review attempt history for context',
+ icon: GitBranch,
+ tone: 'neutral',
+ },
+ {
+ label: 'What should I do?',
+ value: prUrl ? 'Review the existing PR' : 'Resolve the blocker',
+ detail: 'Retry only when current safety gates allow it',
+ icon: Search,
+ tone: 'neutral',
+ },
+ ],
+ context:
+ 'Blocked is distinct from failed. Security Agent intentionally stopped because proceeding could create unsafe or conflicting work.',
+ action: {
+ label: 'Next step',
+ title: prUrl ? 'Review the existing remediation' : 'Resolve the recorded blocker',
+ description:
+ blockedReason ||
+ 'Review the attempt evidence before deciding whether another remediation attempt is appropriate.',
+ },
+ disclosureTitle: 'Why remediation was blocked',
+ steps: [
+ {
+ title: 'Attempt admitted',
+ detail: 'Security Agent accepted remediation for this finding.',
+ state: 'done',
+ },
+ {
+ title: 'Checked safety gates',
+ detail: blockedReason || 'A blocking condition was detected.',
+ state: 'done',
+ },
+ {
+ title: 'Stopped remediation',
+ detail: 'Security Agent ended the attempt before unsafe or conflicting work continued.',
+ state: 'error',
+ },
+ ],
+ };
+ }
+
+ if (status === 'failed') {
+ return {
+ hero: {
+ title: 'Remediation did not complete',
+ description:
+ latestAttempt?.lastErrorRedacted ||
+ 'Cloud Agent could not complete the repository change or open a trustworthy pull request.',
+ icon: XCircle,
+ tone: 'destructive',
+ },
+ summary: [
+ {
+ label: 'What happened?',
+ value: failureCode?.replace(/_/g, ' ') || 'Attempt failed',
+ detail: latestAttempt?.lastErrorRedacted || 'Review attempt details before retrying',
+ icon: TriangleAlert,
+ tone: 'destructive',
+ },
+ {
+ label: 'Pull request',
+ value: 'Not opened',
+ detail: 'No trustworthy pull request outcome was recorded',
+ icon: GitBranch,
+ tone: 'neutral',
+ },
+ {
+ label: 'What should I do?',
+ value: 'Resolve the error and retry',
+ detail: 'A retry creates a new preserved attempt',
+ icon: RefreshCw,
+ tone: 'neutral',
+ },
+ ],
+ context:
+ 'Failure leaves the finding open. Retrying creates a new Security Remediation Attempt and preserves this attempt in history.',
+ action: {
+ label: 'Next step',
+ title: 'Retry after resolving the recorded error',
+ description:
+ latestAttempt?.lastErrorRedacted ||
+ 'Review repository access and attempt details before starting another attempt.',
+ },
+ disclosureTitle: 'What failed',
+ steps: [
+ {
+ title: 'Request accepted',
+ detail: `Security Agent created attempt #${latestAttempt?.attemptNumber ?? 1}.`,
+ state: 'done',
+ },
+ {
+ title: 'Cloud Agent could not complete remediation',
+ detail:
+ latestAttempt?.lastErrorRedacted ||
+ 'The attempt ended before a safe outcome was recorded.',
+ state: 'error',
+ },
+ {
+ title: 'No pull request opened',
+ detail: 'No verified code change or pull request exists.',
+ state: 'pending',
+ },
+ ],
+ };
+ }
+
+ if (status === 'no_changes_needed') {
+ return {
+ hero: {
+ title: 'Cloud Agent found no safe change to make',
+ description:
+ 'The attempt ended without a repository change. Security Agent correctly did not open a no-change pull request.',
+ icon: CheckCircle2,
+ tone: 'neutral',
+ },
+ summary: [
+ {
+ label: 'Outcome',
+ value: 'No repository changes',
+ detail: 'A no-change pull request was not opened',
+ icon: Check,
+ tone: 'neutral',
+ },
+ {
+ label: 'Finding state',
+ value: 'Still open',
+ detail: 'Source synchronization controls closure',
+ icon: ShieldAlert,
+ tone: 'warning',
+ },
+ {
+ label: 'What should I do?',
+ value: 'Review source state',
+ detail: 'Retry only after new evidence or re-analysis',
+ icon: Info,
+ tone: 'neutral',
+ },
+ ],
+ context:
+ 'No changes needed is not the same as fixed. Security Agent does not close the finding or invent a pull request.',
+ action: {
+ label: 'Next step',
+ title: 'No immediate remediation action',
+ description:
+ 'Review the finding. If source data or repository evidence changes, run fresh analysis before retrying.',
+ },
+ disclosureTitle: 'Why no changes were made',
+ steps: [
+ {
+ title: 'Inspected repository evidence',
+ detail: 'Cloud Agent reviewed the current dependency and source state.',
+ state: 'done',
+ },
+ {
+ title: 'Found no safe repository change',
+ detail: 'The attempt determined that no change should be published.',
+ state: 'done',
+ },
+ {
+ title: 'Skipped a no-change pull request',
+ detail: 'Cloud Agent correctly ended without creating repository noise.',
+ state: 'done',
+ },
+ ],
+ };
+ }
+
+ if (status === 'cancelled') {
+ return {
+ hero: {
+ title: 'Remediation was cancelled',
+ description:
+ 'Cloud Agent confirmed interruption before opening a pull request. Any partial session work was not presented as a repository outcome.',
+ icon: XCircle,
+ tone: 'neutral',
+ },
+ summary: [
+ {
+ label: 'Outcome',
+ value: 'Cancelled',
+ detail: 'Cloud Agent confirmed interruption',
+ icon: XCircle,
+ tone: 'neutral',
+ },
+ {
+ label: 'Attempt started by',
+ value: requester,
+ detail: formatRemediationOrigin(latestAttempt?.origin ?? 'manual'),
+ icon: UserRound,
+ tone: 'neutral',
+ },
+ {
+ label: 'Pull request',
+ value: 'Not opened',
+ detail: 'No repository outcome was published',
+ icon: GitPullRequest,
+ tone: 'neutral',
+ },
+ ],
+ context:
+ 'Cancelled is a terminal attempt outcome, not a finding status. The Security Finding remains open.',
+ action: {
+ label: 'Next step',
+ title: 'Start a new attempt when ready',
+ description:
+ 'Retry remains available only when current analysis and safety gates provide a concrete fix path.',
+ },
+ disclosureTitle: 'How cancellation completed',
+ steps: [
+ {
+ title: 'Remediation started',
+ detail: 'Cloud Agent began repository work.',
+ state: 'done',
+ },
+ {
+ title: 'Cancellation requested',
+ detail: 'Security Agent asked Cloud Agent to stop the attempt.',
+ state: 'done',
+ },
+ {
+ title: 'Interruption confirmed',
+ detail: 'Cloud Agent stopped before opening a pull request.',
+ state: 'done',
+ },
+ ],
+ };
+ }
+
+ return {
+ hero: {
+ title: 'Review remediation status',
+ description: `Security Agent recorded this remediation as ${formatRemediationStatus(status)}.`,
+ icon: GitPullRequest,
+ tone: 'neutral',
+ },
+ summary: [
+ {
+ label: 'Current state',
+ value: formatRemediationStatus(status),
+ detail: 'Review attempt history for more context',
+ icon: GitPullRequest,
+ tone: 'neutral',
+ },
+ {
+ label: 'Finding state',
+ value: finding.status,
+ detail: 'Remediation does not directly close findings',
+ icon: ShieldAlert,
+ tone: 'warning',
+ },
+ {
+ label: 'Next step',
+ value: 'Review recorded evidence',
+ detail: 'Use the latest source and attempt state',
+ icon: Search,
+ tone: 'neutral',
+ },
+ ],
+ context:
+ 'Security Finding status and Security Remediation status are separate recorded outcomes.',
+ action: {
+ label: 'Current status',
+ title: 'Review remediation history',
+ description: 'Use the recorded attempts to understand the current state.',
+ },
+ disclosureTitle: 'Remediation progress',
+ steps: [
+ {
+ title: 'Remediation status recorded',
+ detail: formatRemediationStatus(status),
+ state: 'done',
+ },
+ ],
+ };
}
type FindingRemediationProps = {
+ finding: SecurityFinding;
status: string | null;
- prDraft: boolean | null;
+ summary: RemediationPresentationSummary;
outcomeSummary: string | null;
- blockedReason: string | null;
- updatedAt: string | null;
- failureCopy: string | null;
+ unavailableReason: string | null | undefined;
unavailableCopy: string | null;
attempts: RemediationAttempt[];
- action: React.ReactNode;
+ canStart: boolean;
+ isAwaitingStart: boolean;
+ actionStatusMessage?: string;
+ action: ReactNode;
};
function FindingRemediation({
+ finding,
status,
- prDraft,
+ summary,
outcomeSummary,
- blockedReason,
- updatedAt,
- failureCopy,
+ unavailableReason,
unavailableCopy,
attempts,
+ canStart,
+ isAwaitingStart,
+ actionStatusMessage,
action,
}: FindingRemediationProps) {
- const isActive = isActiveRemediationStatus(status);
+ const latestAttempt = attempts[0] ?? null;
+ const presentation = getRemediationPresentation({
+ status,
+ finding,
+ latestAttempt,
+ summary,
+ canStart,
+ unavailableReason,
+ unavailableCopy,
+ isAwaitingStart,
+ });
return (
-
-
-
-
-
- {isActive ? (
-
- ) : (
-
- )}
-
{formatRemediationStatus(status)}
- {prDraft && (
-
- Draft
-
- )}
-
- {outcomeSummary &&
{outcomeSummary}
}
- {blockedReason &&
Blocked: {blockedReason}
}
- {failureCopy &&
{failureCopy}
}
- {updatedAt && (
-
- Updated {formatDistanceToNow(new Date(updatedAt), { addSuffix: true })}
-
- )}
+
+
+
+ {outcomeSummary && !isActiveRemediationStatus(status) && (
+
+
Recorded outcome
+
{outcomeSummary}
+ )}
+
+ {action}
+
+
+
{presentation.context}
+
+
+
+
+ {presentation.disclosureTitle}
+
+
+
+ {presentation.steps.map((step, index) => (
+
+ ))}
+
+
+
+
+ {attempts.length > 0 && (
+
+
+ Remediation attempt history ({attempts.length})
+
+
+
+ {attempts.map(attempt => (
+
+ ))}
+
+
+
+ )}
+
+
+
+ );
+}
+
+function getAttemptTone(attempt: RemediationAttempt): Tone {
+ if (attempt.status === 'pr_opened') return 'success';
+ if (attempt.status === 'failed') return 'destructive';
+ if (
+ attempt.status === 'queued' ||
+ attempt.status === 'launching' ||
+ attempt.status === 'running' ||
+ attempt.status === 'blocked'
+ )
+ return 'warning';
+ return 'neutral';
+}
+
+function AttemptRecord({ attempt }: { attempt: RemediationAttempt }) {
+ const tone = getAttemptTone(attempt);
+ const requestedBy = attempt.origin === 'manual' ? 'Kilo user' : 'Security Agent';
+ const outcome = attempt.blockedReason || attempt.lastErrorRedacted;
+ const validation = attempt.validationEvidence?.map(formatValidationEvidence) ?? [];
- {action &&
{action}
}
+ return (
+
+
+
+
+ #{attempt.attemptNumber}
+
+
+ {formatRemediationStatus(attempt.status, attempt.cancellationRequestedAt)}
+
+
+ {formatRemediationOrigin(attempt.origin)}
+
+
+ {formatUtcDate(attempt.updatedAt)}
+
+
- {unavailableCopy && (
-
- )}
+
+
+
+
+
- {attempts.length > 0 ? (
-
-
Attempts
-
- {attempts.map(attempt => (
-
-
-
- #{attempt.attemptNumber}
-
- {formatRemediationStatus(attempt.status)}
-
- {attempt.origin}
-
-
- {format(new Date(attempt.updatedAt), 'PPp')}
-
-
-
-
- Branch: {' '}
- {attempt.branchName}
-
-
- Model: {' '}
- {attempt.remediationModelSlug}
-
-
- {attempt.lastErrorRedacted && (
-
{attempt.lastErrorRedacted}
- )}
- {attempt.blockedReason && (
-
{attempt.blockedReason}
- )}
- {attempt.riskNotes && (
-
{attempt.riskNotes}
- )}
- {attempt.prUrl && (
-
-
- Open attempt PR
-
-
-
- )}
-
- ))}
+ {outcome && (
+
+ )}
+
+ {validation.length > 0 && (
+
+
+
+
Validation:
+
+ {validation.map(item => (
+ {item}
+ ))}
+
- ) : (
-
)}
-
- );
-}
-function FindingFooter({
- finding,
- canDismiss,
- onDismiss,
- onClose,
-}: {
- finding: SecurityFinding;
- canDismiss: boolean;
- onDismiss: () => void;
- onClose: () => void;
-}) {
- return (
-
+ )}
+
);
}
+type FindingDetailDialogProps = {
+ finding: SecurityFindingWithRemediation | null;
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+ onDismiss: (analysis: FindingAnalysis) => void;
+ canDismiss: boolean;
+ onOpenFinding: (findingId: string) => void;
+ onStartAnalysis: StartFindingAnalysis;
+ analysisAtCapacity: boolean;
+ organizationId?: string;
+ showSla?: boolean;
+};
+
export function FindingDetailDialog({
- finding,
+ finding: initialFinding,
open,
onOpenChange,
onDismiss,
canDismiss,
+ onOpenFinding,
+ onStartAnalysis,
+ analysisAtCapacity,
organizationId,
showSla = true,
}: FindingDetailDialogProps) {
const trpc = useTRPC();
const isOrg = Boolean(organizationId);
const {
- handleStartAnalysis: triggerStartAnalysis,
+ handleStartAnalysis: startAnalysisCommand,
handleStartRemediation,
handleRetryRemediation,
handleCancelRemediation,
+ trackUiInteraction,
startingAnalysisIds,
startingRemediationIds,
cancellingRemediationAttemptIds,
} = useSecurityAgent();
- const isAwaitingAnalysisStart = finding ? startingAnalysisIds.has(finding.id) : false;
- const isAwaitingRemediationStart = finding ? startingRemediationIds.has(finding.id) : false;
+ const trackedOpenFindingIdRef = useRef
(null);
+ const settledAnalysisCompletionRef = useRef(null);
+ const openerRef = useRef(null);
+ const scrollContainerRef = useRef(null);
+ const [tabState, setTabState] = useState<{ findingId: string | null; tab: FindingTab }>({
+ findingId: null,
+ tab: 'details',
+ });
+ const findingId = initialFinding?.id;
+
+ useEffect(() => {
+ if (!open || !findingId) {
+ trackedOpenFindingIdRef.current = null;
+ return;
+ }
+ if (trackedOpenFindingIdRef.current === findingId) return;
+
+ trackedOpenFindingIdRef.current = findingId;
+ trackUiInteraction('finding_detail_opened');
+ }, [findingId, open, trackUiInteraction]);
+
+ const hasActiveAnalysisStartCommand = findingId ? startingAnalysisIds.has(findingId) : false;
+ const isAwaitingRemediationStart = findingId ? startingRemediationIds.has(findingId) : false;
const pollWhileActive = (query: {
state: {
@@ -790,7 +2955,7 @@ export function FindingDetailDialog({
isActiveRemediationStatus(attempt.status)
);
if (
- isAwaitingAnalysisStart ||
+ hasActiveAnalysisStartCommand ||
isAwaitingRemediationStart ||
status === 'pending' ||
status === 'running' ||
@@ -800,26 +2965,89 @@ export function FindingDetailDialog({
}
return false as const;
};
+
const orgAnalysisQuery = useQuery({
...trpc.organizations.securityAgent.getAnalysis.queryOptions({
organizationId: organizationId ?? '',
- findingId: finding?.id ?? '',
+ findingId: findingId ?? '',
}),
- enabled: open && Boolean(finding) && isOrg,
+ enabled: open && Boolean(initialFinding) && isOrg,
refetchInterval: pollWhileActive,
});
const personalAnalysisQuery = useQuery({
...trpc.securityAgent.getAnalysis.queryOptions({
- findingId: finding?.id ?? '',
+ findingId: findingId ?? '',
}),
- enabled: open && Boolean(finding) && !isOrg,
+ enabled: open && Boolean(initialFinding) && !isOrg,
refetchInterval: pollWhileActive,
});
const analysisData = isOrg ? orgAnalysisQuery.data : personalAnalysisQuery.data;
+ const refetchAnalysis = isOrg ? orgAnalysisQuery.refetch : personalAnalysisQuery.refetch;
+ const analysisSettlementKey =
+ findingId && analysisData?.completedAt ? `${findingId}:${analysisData.completedAt}` : null;
+
+ useEffect(() => {
+ if (
+ !open ||
+ !analysisSettlementKey ||
+ analysisData?.status !== 'completed' ||
+ analysisData.findingState.status !== 'open' ||
+ settledAnalysisCompletionRef.current === analysisSettlementKey
+ ) {
+ return;
+ }
+
+ const timeoutId = window.setTimeout(() => {
+ settledAnalysisCompletionRef.current = analysisSettlementKey;
+ void refetchAnalysis();
+ }, ANALYSIS_POLL_INTERVAL_MS);
+ return () => window.clearTimeout(timeoutId);
+ }, [
+ analysisData?.findingState.status,
+ analysisData?.status,
+ analysisSettlementKey,
+ open,
+ refetchAnalysis,
+ ]);
+
+ const finding =
+ initialFinding && analysisData
+ ? {
+ ...initialFinding,
+ status: analysisData.findingState.status,
+ ignored_reason: analysisData.findingState.ignoredReason,
+ ignored_by: analysisData.findingState.ignoredBy,
+ fixed_at: analysisData.findingState.fixedAt,
+ updated_at: analysisData.findingState.updatedAt,
+ analysis_status: analysisData.status,
+ analysis_started_at: analysisData.startedAt,
+ analysis_completed_at: analysisData.completedAt,
+ analysis_error: analysisData.error,
+ analysis: analysisData.analysis,
+ session_id: analysisData.sessionId,
+ cli_session_id: analysisData.cliSessionId,
+ remediationSummary: analysisData.remediationSummary,
+ remediationCapability: analysisData.remediationCapability,
+ }
+ : initialFinding;
if (!finding) return null;
+ const selectedTab = tabState.findingId === finding.id ? tabState.tab : 'details';
+ const handleTabChange = (value: string) => {
+ if (value !== 'details' && value !== 'analysis' && value !== 'remediation') return;
+ setTabState({ findingId: finding.id, tab: value });
+ scrollContainerRef.current?.scrollTo({ top: 0 });
+ if (value === 'analysis') trackUiInteraction('finding_analysis_viewed');
+ if (value === 'remediation') trackUiInteraction('finding_remediation_viewed');
+ };
+
const analysisStatus = analysisData?.status ?? finding.analysis_status;
+ const isAwaitingAnalysisAdmission = isAwaitingManualAnalysisAdmission(
+ hasActiveAnalysisStartCommand,
+ analysisStatus
+ );
+ const isRestartingAnalysis = hasActiveAnalysisStartCommand && analysisStatus === 'running';
const analysis = analysisData?.analysis ?? finding.analysis;
const analysisError = analysisData?.error ?? finding.analysis_error;
const cliSessionId = analysisData?.cliSessionId ?? finding.cli_session_id;
@@ -833,16 +3061,9 @@ export function FindingDetailDialog({
const isEffectiveRemediationActive = isActiveRemediationStatus(effectiveRemediationStatus);
const effectiveRemediationPrUrl =
remediationSummary?.prUrl ?? latestHistoryAttempt?.prUrl ?? null;
- const effectiveRemediationPrDraft =
- remediationSummary?.prDraft ?? latestHistoryAttempt?.prDraft ?? null;
const effectiveRemediationOutcomeSummary = isEffectiveRemediationActive
? null
: (remediationSummary?.outcomeSummary ?? null);
- const effectiveRemediationBlockedReason = isEffectiveRemediationActive
- ? null
- : (latestHistoryAttempt?.blockedReason ?? remediationSummary?.blockedReason ?? null);
- const effectiveRemediationUpdatedAt =
- latestHistoryAttempt?.updatedAt ?? remediationSummary?.updatedAt ?? null;
const hasRegisteredRemediationAttempt =
remediationAttempts.length > 0 ||
Boolean(remediationSummary?.latestAttemptId ?? remediationSummary?.latestAttempt?.id);
@@ -852,8 +3073,10 @@ export function FindingDetailDialog({
remediationSummary?.latestAttemptId ??
null)
: null;
+ const cancellationRequestedAt = latestHistoryAttempt?.cancellationRequestedAt ?? null;
const isCancellingRemediation =
- !!activeRemediationAttemptId && cancellingRemediationAttemptIds.has(activeRemediationAttemptId);
+ Boolean(activeRemediationAttemptId) &&
+ cancellingRemediationAttemptIds.has(activeRemediationAttemptId ?? '');
const remediationUnavailableCopy =
remediationCapability &&
!remediationCapability.canStart &&
@@ -878,33 +3101,41 @@ export function FindingDetailDialog({
Boolean(remediationCapability?.canStart) &&
!hasRegisteredRemediationAttempt &&
!isEffectiveRemediationActive;
- const canCancelRemediation = Boolean(activeRemediationAttemptId);
+ const canCancelRemediation = Boolean(activeRemediationAttemptId) && !cancellationRequestedAt;
const canRetryRemediation =
Boolean(remediationCapability?.canRetry) &&
!isEffectiveRemediationActive &&
effectiveRemediationStatus !== 'pr_opened';
- const remediationFailureCopy = isEffectiveRemediationActive
- ? null
- : getRemediationFailureCopy(
- latestHistoryAttempt?.failureCode ?? remediationSummary?.failureCode
- );
const isAnalyzing =
- isAwaitingAnalysisStart || analysisStatus === 'pending' || analysisStatus === 'running';
- const remediationAnalysisRefreshLabel =
- isAwaitingAnalysisStart || analysisStatus === 'pending'
- ? manualAnalysisAdmissionCopy.pendingLabel
+ isAwaitingAnalysisAdmission || analysisStatus === 'pending' || analysisStatus === 'running';
+ const analysisActionDisabled = isAnalyzing || analysisAtCapacity;
+ const canDismissFinding = canDismiss && finding.status === 'open';
+ const analysisActionTitle =
+ analysisAtCapacity && !isAnalyzing ? manualAnalysisCapacityFullCopy : undefined;
+ const remediationAnalysisRefreshLabel = isAwaitingAnalysisAdmission
+ ? manualAnalysisAdmissionCopy.pendingLabel
+ : analysisStatus === 'pending'
+ ? manualAnalysisAdmissionCopy.successTitle
: analysisStatus === 'running'
? 'Analysis running'
: 'Rerun analysis';
- const codebaseAnalysisActionLabel =
- isAwaitingAnalysisStart || analysisStatus === 'pending'
- ? manualAnalysisAdmissionCopy.pendingLabel
+ const codebaseAnalysisActionLabel = isAwaitingAnalysisAdmission
+ ? manualAnalysisAdmissionCopy.pendingLabel
+ : analysisStatus === 'pending'
+ ? manualAnalysisAdmissionCopy.successTitle
: analysisStatus === 'running'
? 'Analysis running'
- : 'Run codebase analysis';
+ : 'Analyze repository';
- const handleStartAnalysis: StartAnalysis = ({ forceSandbox, retrySandboxOnly } = {}) => {
- triggerStartAnalysis(finding.id, { forceSandbox, retrySandboxOnly });
+ const handleStartAnalysis: StartAnalysis = ({
+ forceSandbox,
+ retrySandboxOnly,
+ restartActive,
+ } = {}) => {
+ onStartAnalysis(finding.id, { forceSandbox, retrySandboxOnly, restartActive });
+ };
+ const handleRestartAnalysis = () => {
+ startAnalysisCommand(finding.id, { restartActive: true });
};
const handleStartCodebaseAnalysis = () => {
handleStartAnalysis({ forceSandbox: true });
@@ -912,154 +3143,235 @@ export function FindingDetailDialog({
const handleCancelRemediationClick = () => {
if (activeRemediationAttemptId) handleCancelRemediation(activeRemediationAttemptId, finding.id);
};
+ const handleDismiss = () => onDismiss(analysis);
+ const handleDialogOpenChange = (nextOpen: boolean) => {
+ if (!nextOpen) setTabState({ findingId: null, tab: 'details' });
+ onOpenChange(nextOpen);
+ };
+
const remediationAction = effectiveRemediationPrUrl ? (
-
+
-
- View PR
+
+ View pull request
) : isAwaitingRemediationStart ? (
-
-
- Queueing
+
+
+ Queueing remediation
) : canCancelRemediation ? (
- {isCancellingRemediation ? (
-
- ) : (
-
- )}
- Cancel
+ {isCancellingRemediation ? : }
+ {isCancellingRemediation ? 'Requesting cancellation' : 'Cancel remediation'}
) : canRetryRemediation ? (
- handleRetryRemediation(finding.id)}>
-
- Retry fix
+ handleRetryRemediation(finding.id)}>
+
+ Retry remediation
) : canStartRemediation ? (
- handleStartRemediation(finding.id)}>
-
- Fix with PR
+ handleStartRemediation(finding.id)}>
+
+ Start remediation
) : remediationNeedsCodebaseAnalysis ? (
- {isAnalyzing ? (
-
- ) : (
-
- )}
+ {isAnalyzing ? : }
{codebaseAnalysisActionLabel}
) : remediationNeedsAnalysisRefresh ? (
handleStartAnalysis()}
- disabled={isAnalyzing}
+ disabled={analysisActionDisabled}
+ title={analysisActionTitle}
>
- {isAnalyzing ? (
-
- ) : (
-
- )}
+ {isAnalyzing ? : }
{remediationAnalysisRefreshLabel}
- ) : hasRegisteredRemediationAttempt || effectiveRemediationUnavailableCopy ? (
-
-
- Fix with PR
-
) : null;
- const analysisPanelProps = {
- analysis,
- analysisStatus,
- analysisError,
- isAwaitingAnalysisStart,
- isAnalyzing,
- onStartAnalysis: handleStartAnalysis,
- } satisfies AnalysisPanelProps;
+ const statuses = [
+ { label: 'Severity', ...getSeverityStatus(finding.severity) },
+ { label: 'Finding', ...getFindingStatus(finding) },
+ { label: 'Analysis', ...getAnalysisStatus(analysis, analysisStatus) },
+ ];
return (
-
-
-
+
+ {
+ if (document.activeElement instanceof HTMLElement) {
+ openerRef.current = document.activeElement;
+ }
+ }}
+ onCloseAutoFocus={event => {
+ const opener = openerRef.current;
+ openerRef.current = null;
+ if (!opener?.isConnected || opener === document.body) return;
+ event.preventDefault();
+ opener.focus();
+ }}
+ className="bg-surface-raised max-h-[calc(100dvh-1rem)] w-[calc(100vw-1rem)] max-w-5xl grid-rows-[auto_minmax(0,1fr)] gap-0 overflow-hidden p-0 sm:max-h-[calc(100dvh-3rem)]"
+ >
+
+
+
+
+
+ {finding.title}
+
+
+ Security Finding for {finding.package_name} in {finding.repo_full_name}
+
+
+
+ {finding.repo_full_name}
+
+
+ Detected {formatUtcDate(finding.first_detected_at, false)}
+
+
+ Updated {formatUtcDate(finding.updated_at, false)}
+
+
+
+
+
+
+
+
+
-
-
- Details
-
- }
- />
- Triage
-
-
- }
- />
- Analysis
-
-
- {isEffectiveRemediationActive ? (
-
- ) : (
-
- )}
- Remediation
-
-
-
-
-
-
-
- onOpenChange(false)}
- />
+
+ {statuses.map(status => (
+
+ ))}
+
+
+
+
+
+
+ Details
+
+
+ {isAnalyzing ? (
+
+ ) : (
+
+ )}
+ Analysis
+
+
+ {isEffectiveRemediationActive ? (
+
+ ) : (
+
+ )}
+ Remediation
+
+
+
+
+
+
+
+ handleStartRemediation(finding.id)}
+ onDismiss={handleDismiss}
+ onSelectTab={handleTabChange}
+ />
+
+
diff --git a/apps/web/src/components/security-agent/RepositoryFilter.tsx b/apps/web/src/components/security-agent/RepositoryFilter.tsx
index 259d2ba762..80943a5f34 100644
--- a/apps/web/src/components/security-agent/RepositoryFilter.tsx
+++ b/apps/web/src/components/security-agent/RepositoryFilter.tsx
@@ -7,6 +7,7 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
+import { cn } from '@/lib/utils';
type Repository = {
id: number;
@@ -20,6 +21,8 @@ type RepositoryFilterProps = {
value: string | undefined;
onValueChange: (value: string | undefined) => void;
isLoading?: boolean;
+ id?: string;
+ className?: string;
};
export function RepositoryFilter({
@@ -27,6 +30,8 @@ export function RepositoryFilter({
value,
onValueChange,
isLoading,
+ id,
+ className,
}: RepositoryFilterProps) {
return (
onValueChange(v === 'all' ? undefined : v)}
disabled={isLoading}
>
-
+
diff --git a/apps/web/src/components/security-agent/SecurityAgentActionBar.tsx b/apps/web/src/components/security-agent/SecurityAgentActionBar.tsx
new file mode 100644
index 0000000000..77af5284fc
--- /dev/null
+++ b/apps/web/src/components/security-agent/SecurityAgentActionBar.tsx
@@ -0,0 +1,49 @@
+'use client';
+
+import { Slot } from '@radix-ui/react-slot';
+import type { ComponentProps, ReactNode } from 'react';
+import { Label } from '@/components/ui/label';
+import { cn } from '@/lib/utils';
+
+type SecurityAgentActionBarProps = Omit, 'aria-label'> & {
+ label: string;
+ asChild?: boolean;
+};
+
+export function SecurityAgentActionBar({
+ label,
+ asChild = false,
+ className,
+ ...props
+}: SecurityAgentActionBarProps) {
+ const Component = asChild ? Slot : 'section';
+
+ return (
+
+ );
+}
+
+export function SecurityAgentActionBarField({
+ id,
+ label,
+ className,
+ children,
+}: {
+ id: string;
+ label: string;
+ className?: string;
+ children: ReactNode;
+}) {
+ return (
+
+
+ {label}
+
+ {children}
+
+ );
+}
diff --git a/apps/web/src/components/security-agent/SecurityAgentContext.test.ts b/apps/web/src/components/security-agent/SecurityAgentContext.test.ts
index be74f34be7..0b561d2594 100644
--- a/apps/web/src/components/security-agent/SecurityAgentContext.test.ts
+++ b/apps/web/src/components/security-agent/SecurityAgentContext.test.ts
@@ -6,6 +6,7 @@ import {
shouldRunSecurityAgentCommandSuccessCallback,
type SecurityAgentCommand,
} from './SecurityAgentContext';
+import { getSecurityAgentHelpContent, getSecurityAgentNavItems } from './SecurityAgentLayout';
function command(overrides: Partial): SecurityAgentCommand {
return {
@@ -19,6 +20,66 @@ function command(overrides: Partial): SecurityAgentCommand
};
}
+describe('Security Agent help content', () => {
+ const personalBasePath = '/security-agent';
+ const organizationBasePath = '/organizations/org-1/security-agent';
+
+ it.each([
+ [personalBasePath, 'Dashboard help', '#use-the-dashboard'],
+ [`${personalBasePath}/findings`, 'Findings help', '#browse-findings'],
+ [`${personalBasePath}/audit-report`, 'Audit report help', '#audit-reports'],
+ [`${personalBasePath}/config`, 'Settings help', '#configure-security-agent'],
+ ])('matches personal route %s to its page help', (pathname, title, docsAnchor) => {
+ const content = getSecurityAgentHelpContent(pathname, personalBasePath);
+
+ expect(content.title).toBe(title);
+ expect(content.docsUrl).toContain(docsAnchor);
+ });
+
+ it.each([
+ [organizationBasePath, 'Dashboard help'],
+ [`${organizationBasePath}/findings`, 'Findings help'],
+ [`${organizationBasePath}/audit-report`, 'Audit report help'],
+ [`${organizationBasePath}/config`, 'Settings help'],
+ ])('matches organization route %s to its page help', (pathname, title) => {
+ expect(getSecurityAgentHelpContent(pathname, organizationBasePath).title).toBe(title);
+ });
+
+ it('uses overview help outside known Security Agent routes', () => {
+ expect(getSecurityAgentHelpContent('/security-agent/unknown', personalBasePath).title).toBe(
+ 'Security Agent help'
+ );
+ expect(getSecurityAgentHelpContent('/another-page', personalBasePath).title).toBe(
+ 'Security Agent help'
+ );
+ });
+});
+
+describe('Security Agent navigation', () => {
+ const basePath = '/security-agent';
+
+ it('keeps historical reports available during setup', () => {
+ expect(
+ getSecurityAgentNavItems({ basePath, showSetupOnly: true, isEnabled: undefined }).map(
+ item => item.label
+ )
+ ).toEqual(['Audit report', 'Settings']);
+ });
+
+ it('preserves configured navigation', () => {
+ expect(
+ getSecurityAgentNavItems({ basePath, showSetupOnly: false, isEnabled: true }).map(
+ item => item.label
+ )
+ ).toEqual(['Dashboard', 'Findings', 'Audit report', 'Settings']);
+ expect(
+ getSecurityAgentNavItems({ basePath, showSetupOnly: false, isEnabled: false }).map(
+ item => item.label
+ )
+ ).toEqual(['Dashboard', 'Audit report', 'Settings']);
+ });
+});
+
describe('SecurityAgentContext command helpers', () => {
it('recovers active commands after reload and dedupes polled state', () => {
const recovered = command({
@@ -42,6 +103,21 @@ describe('SecurityAgentContext command helpers', () => {
]);
});
+ it('removes recovered commands after polling observes a terminal state', () => {
+ const recovered = command({
+ id: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
+ commandType: 'start_analysis',
+ findingId: 'finding-id',
+ status: 'running',
+ });
+ const terminal = command({
+ ...recovered,
+ status: 'succeeded',
+ });
+
+ expect(mergeSecurityAgentActiveCommands([recovered], [terminal])).toEqual([]);
+ });
+
it('derives active-action disabling and optimistic analysis ids', () => {
const state = getSecurityAgentActiveCommandState(
[
diff --git a/apps/web/src/components/security-agent/SecurityAgentContext.tsx b/apps/web/src/components/security-agent/SecurityAgentContext.tsx
index cbb22edb93..ee38207570 100644
--- a/apps/web/src/components/security-agent/SecurityAgentContext.tsx
+++ b/apps/web/src/components/security-agent/SecurityAgentContext.tsx
@@ -11,8 +11,11 @@ import {
} from '@tanstack/react-query';
import { toast } from 'sonner';
import type { SecurityFinding } from '@kilocode/db/schema';
+import type { SecurityRemediationAdmissionRejectionReason } from '@kilocode/worker-utils/security-remediation-policy';
+import type { SecurityAgentUiInteraction } from '@/lib/security-agent/core/schemas';
import { isGitHubIntegrationError } from '@/lib/security-agent/core/error-display';
import type { DismissReason } from './DismissFindingDialog';
+import { getRemediationUnavailableCopy } from './remediation-unavailable-copy';
import type { SlaConfig } from './security-config-types';
import {
getSecurityAgentCommandFailureTitle,
@@ -36,8 +39,10 @@ type SecurityAgentContextValue = {
isLoadingConfig: boolean;
reauthorizeUrl: string | undefined;
isEnabled: boolean | undefined;
+ hasConfig: boolean;
configData:
| {
+ hasConfig: boolean;
isEnabled: boolean;
slaCriticalDays: number;
slaHighDays: number;
@@ -74,6 +79,7 @@ type SecurityAgentContextValue = {
filteredRepositories: Array<{ id: number; fullName: string; name: string; private: boolean }>;
// Mutation handlers
+ trackUiInteraction: (interaction: SecurityAgentUiInteraction) => void;
handleSync: (repoFullName?: string) => void;
handleDismiss: (
finding: SecurityFinding,
@@ -116,7 +122,7 @@ type SecurityAgentContextValue = {
) => void;
handleStartAnalysis: (
findingId: string,
- options?: { forceSandbox?: boolean; retrySandboxOnly?: boolean }
+ options?: { forceSandbox?: boolean; retrySandboxOnly?: boolean; restartActive?: boolean }
) => void;
handleStartRemediation: (findingId: string) => void;
handleRetryRemediation: (findingId: string) => void;
@@ -187,7 +193,12 @@ export function mergeSecurityAgentActiveCommands(
if (isActiveSecurityAgentCommand(command)) activeCommands.set(command.id, command);
}
for (const command of polledCommands) {
- if (command && isActiveSecurityAgentCommand(command)) activeCommands.set(command.id, command);
+ if (!command) continue;
+ if (isActiveSecurityAgentCommand(command)) {
+ activeCommands.set(command.id, command);
+ } else {
+ activeCommands.delete(command.id);
+ }
}
return [...activeCommands.values()];
}
@@ -577,6 +588,31 @@ function useSecurityAgentProviderValue(
);
}
+ function settleRemediationAdmission(
+ result:
+ | { queued: true }
+ | { queued: false; reason: SecurityRemediationAdmissionRejectionReason },
+ findingId: string,
+ messages: { queued: string; unavailable: string }
+ ) {
+ if (result.queued) {
+ toast.success(messages.queued);
+ } else {
+ toast.error(messages.unavailable, {
+ description: getRemediationUnavailableCopy(result.reason),
+ duration: 8000,
+ });
+ }
+ dispatchProviderState({ type: 'remove-optimistic-remediation', findingId });
+ invalidateRemediationQueries();
+ }
+
+ function settleRemediationError(error: { message: string }, findingId: string, title: string) {
+ toast.error(title, { description: error.message, duration: 8000 });
+ dispatchProviderState({ type: 'remove-optimistic-remediation', findingId });
+ invalidateRemediationQueries();
+ }
+
// Permission status query
const { data: permissionData, isLoading: isLoadingPermission } = useQuery(
isOrg
@@ -711,6 +747,9 @@ function useSecurityAgentProviderValue(
]);
// ---- Mutations (org) ----
+ const { mutate: orgTrackUiInteractionMutate } = useMutation(
+ trpc.organizations.securityAgent.trackUiInteraction.mutationOptions()
+ );
const { mutate: orgSyncMutate, isPending: isOrgSyncPending } = useMutation(
trpc.organizations.securityAgent.triggerSync.mutationOptions({
onSuccess: data => {
@@ -807,20 +846,28 @@ function useSecurityAgentProviderValue(
const { mutate: orgStartAnalysisMutate } = useMutation(
trpc.organizations.securityAgent.startAnalysis.mutationOptions({
- onSuccess: async data => {
+ onSuccess: async (data, variables) => {
dispatchProviderState({ type: 'set-github-error', error: null });
- toast.success(securityAgentCommandAdmissionCopy.start_analysis.successTitle);
+ toast.success(
+ variables.restartActive
+ ? 'Analysis restart queued'
+ : securityAgentCommandAdmissionCopy.start_analysis.successTitle
+ );
trackCommand(data.commandId);
},
onError: (error, variables) => {
const message = error instanceof Error ? error.message : String(error);
+ const isRestart = variables.restartActive === true;
+ const failureTitle = isRestart
+ ? 'Failed to restart analysis'
+ : securityAgentCommandAdmissionCopy.start_analysis.failureTitle;
if (isGitHubIntegrationError(error)) {
dispatchProviderState({ type: 'set-github-error', error: message });
- toast.error('GitHub integration error', {
+ toast.error(isRestart ? failureTitle : 'GitHub integration error', {
description: 'GitHub App may have been uninstalled. Check integrations, then retry.',
});
} else {
- toast.error(securityAgentCommandAdmissionCopy.start_analysis.failureTitle, {
+ toast.error(failureTitle, {
description: message,
duration: 8000,
});
@@ -838,40 +885,28 @@ function useSecurityAgentProviderValue(
const { mutate: orgStartRemediationMutate } = useMutation(
trpc.organizations.securityAgent.startRemediation.mutationOptions({
- onSuccess: async (_data, variables) => {
- toast.success('Remediation queued');
- dispatchProviderState({
- type: 'remove-optimistic-remediation',
- findingId: variables.findingId,
+ onSuccess: (data, variables) => {
+ settleRemediationAdmission(data, variables.findingId, {
+ queued: 'Remediation queued',
+ unavailable: 'Remediation unavailable',
});
- invalidateRemediationQueries();
},
onError: (error, variables) => {
- toast.error('Failed to queue remediation', { description: error.message, duration: 8000 });
- dispatchProviderState({
- type: 'remove-optimistic-remediation',
- findingId: variables.findingId,
- });
+ settleRemediationError(error, variables.findingId, 'Failed to queue remediation');
},
})
);
const { mutate: orgRetryRemediationMutate } = useMutation(
trpc.organizations.securityAgent.retryRemediation.mutationOptions({
- onSuccess: async (_data, variables) => {
- toast.success('Remediation retry queued');
- dispatchProviderState({
- type: 'remove-optimistic-remediation',
- findingId: variables.findingId,
+ onSuccess: (data, variables) => {
+ settleRemediationAdmission(data, variables.findingId, {
+ queued: 'Remediation retry queued',
+ unavailable: 'Remediation retry unavailable',
});
- invalidateRemediationQueries();
},
onError: (error, variables) => {
- toast.error('Failed to retry remediation', { description: error.message, duration: 8000 });
- dispatchProviderState({
- type: 'remove-optimistic-remediation',
- findingId: variables.findingId,
- });
+ settleRemediationError(error, variables.findingId, 'Failed to retry remediation');
},
})
);
@@ -911,6 +946,9 @@ function useSecurityAgentProviderValue(
);
// ---- Mutations (personal) ----
+ const { mutate: personalTrackUiInteractionMutate } = useMutation(
+ trpc.securityAgent.trackUiInteraction.mutationOptions()
+ );
const { mutate: personalSyncMutate, isPending: isPersonalSyncPending } = useMutation(
trpc.securityAgent.triggerSync.mutationOptions({
onSuccess: data => {
@@ -1007,20 +1045,28 @@ function useSecurityAgentProviderValue(
const { mutate: personalStartAnalysisMutate } = useMutation(
trpc.securityAgent.startAnalysis.mutationOptions({
- onSuccess: async data => {
+ onSuccess: async (data, variables) => {
dispatchProviderState({ type: 'set-github-error', error: null });
- toast.success(securityAgentCommandAdmissionCopy.start_analysis.successTitle);
+ toast.success(
+ variables.restartActive
+ ? 'Analysis restart queued'
+ : securityAgentCommandAdmissionCopy.start_analysis.successTitle
+ );
trackCommand(data.commandId);
},
onError: (error, variables) => {
const message = error instanceof Error ? error.message : String(error);
+ const isRestart = variables.restartActive === true;
+ const failureTitle = isRestart
+ ? 'Failed to restart analysis'
+ : securityAgentCommandAdmissionCopy.start_analysis.failureTitle;
if (isGitHubIntegrationError(error)) {
dispatchProviderState({ type: 'set-github-error', error: message });
- toast.error('GitHub integration error', {
+ toast.error(isRestart ? failureTitle : 'GitHub integration error', {
description: 'GitHub App may have been uninstalled. Check integrations, then retry.',
});
} else {
- toast.error(securityAgentCommandAdmissionCopy.start_analysis.failureTitle, {
+ toast.error(failureTitle, {
description: message,
duration: 8000,
});
@@ -1038,40 +1084,28 @@ function useSecurityAgentProviderValue(
const { mutate: personalStartRemediationMutate } = useMutation(
trpc.securityAgent.startRemediation.mutationOptions({
- onSuccess: async (_data, variables) => {
- toast.success('Remediation queued');
- dispatchProviderState({
- type: 'remove-optimistic-remediation',
- findingId: variables.findingId,
+ onSuccess: (data, variables) => {
+ settleRemediationAdmission(data, variables.findingId, {
+ queued: 'Remediation queued',
+ unavailable: 'Remediation unavailable',
});
- invalidateRemediationQueries();
},
onError: (error, variables) => {
- toast.error('Failed to queue remediation', { description: error.message, duration: 8000 });
- dispatchProviderState({
- type: 'remove-optimistic-remediation',
- findingId: variables.findingId,
- });
+ settleRemediationError(error, variables.findingId, 'Failed to queue remediation');
},
})
);
const { mutate: personalRetryRemediationMutate } = useMutation(
trpc.securityAgent.retryRemediation.mutationOptions({
- onSuccess: async (_data, variables) => {
- toast.success('Remediation retry queued');
- dispatchProviderState({
- type: 'remove-optimistic-remediation',
- findingId: variables.findingId,
+ onSuccess: (data, variables) => {
+ settleRemediationAdmission(data, variables.findingId, {
+ queued: 'Remediation retry queued',
+ unavailable: 'Remediation retry unavailable',
});
- invalidateRemediationQueries();
},
onError: (error, variables) => {
- toast.error('Failed to retry remediation', { description: error.message, duration: 8000 });
- dispatchProviderState({
- type: 'remove-optimistic-remediation',
- findingId: variables.findingId,
- });
+ settleRemediationError(error, variables.findingId, 'Failed to retry remediation');
},
})
);
@@ -1264,13 +1298,24 @@ function useSecurityAgentProviderValue(
{
forceSandbox,
retrySandboxOnly,
- }: { forceSandbox?: boolean; retrySandboxOnly?: boolean } = {}
+ restartActive,
+ }: {
+ forceSandbox?: boolean;
+ retrySandboxOnly?: boolean;
+ restartActive?: boolean;
+ } = {}
) => {
dispatchProviderState({ type: 'add-optimistic-analysis', findingId });
if (isOrg && organizationId) {
- orgStartAnalysisMutate({ organizationId, findingId, forceSandbox, retrySandboxOnly });
+ orgStartAnalysisMutate({
+ organizationId,
+ findingId,
+ forceSandbox,
+ retrySandboxOnly,
+ restartActive,
+ });
} else {
- personalStartAnalysisMutate({ findingId, forceSandbox, retrySandboxOnly });
+ personalStartAnalysisMutate({ findingId, forceSandbox, retrySandboxOnly, restartActive });
}
},
[isOrg, organizationId, orgStartAnalysisMutate, personalStartAnalysisMutate]
@@ -1333,6 +1378,7 @@ function useSecurityAgentProviderValue(
const hasPermission = permissionData?.hasPermissions ?? false;
const reauthorizeUrl = permissionData?.reauthorizeUrl ?? undefined;
const isEnabled = configData ? configData.isEnabled : undefined;
+ const hasConfig = configData?.hasConfig ?? false;
const allRepositories = reposData ?? EMPTY_REPOSITORIES;
const repositorySelectionMode = configData?.repositorySelectionMode ?? 'selected';
const selectedRepositoryIds = configData?.selectedRepositoryIds ?? EMPTY_REPOSITORY_IDS;
@@ -1359,6 +1405,7 @@ function useSecurityAgentProviderValue(
isLoadingConfig,
reauthorizeUrl,
isEnabled,
+ hasConfig,
configData: configData
? {
...configData,
@@ -1383,6 +1430,13 @@ function useSecurityAgentProviderValue(
refetchConfig,
allRepositories,
filteredRepositories,
+ trackUiInteraction: interaction => {
+ if (isOrg && organizationId) {
+ orgTrackUiInteractionMutate({ organizationId, interaction });
+ } else {
+ personalTrackUiInteractionMutate({ interaction });
+ }
+ },
handleSync,
handleDismiss,
handleSaveConfig,
@@ -1413,10 +1467,13 @@ function useSecurityAgentProviderValue(
isLoadingConfig,
reauthorizeUrl,
isEnabled,
+ hasConfig,
configData,
refetchConfig,
allRepositories,
filteredRepositories,
+ orgTrackUiInteractionMutate,
+ personalTrackUiInteractionMutate,
handleSync,
handleDismiss,
handleSaveConfig,
diff --git a/apps/web/src/components/security-agent/SecurityAgentGitHubInstallCta.tsx b/apps/web/src/components/security-agent/SecurityAgentGitHubInstallCta.tsx
new file mode 100644
index 0000000000..bf4b75d1f1
--- /dev/null
+++ b/apps/web/src/components/security-agent/SecurityAgentGitHubInstallCta.tsx
@@ -0,0 +1,28 @@
+'use client';
+
+import Link from 'next/link';
+import { Shield } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+
+type SecurityAgentGitHubInstallCtaProps = {
+ installUrl: string;
+};
+
+export function SecurityAgentGitHubInstallCta({ installUrl }: SecurityAgentGitHubInstallCtaProps) {
+ return (
+
+
+
Connect GitHub to get started
+
+ Install the Kilo GitHub App to automatically sync Dependabot alerts and manage security
+ findings across your repositories.
+
+
+ Install GitHub App
+
+
+ );
+}
diff --git a/apps/web/src/components/security-agent/SecurityAgentLayout.tsx b/apps/web/src/components/security-agent/SecurityAgentLayout.tsx
index 5ff9ab8d9e..a9ea839bd8 100644
--- a/apps/web/src/components/security-agent/SecurityAgentLayout.tsx
+++ b/apps/web/src/components/security-agent/SecurityAgentLayout.tsx
@@ -2,17 +2,29 @@
import { usePathname } from 'next/navigation';
import Link from 'next/link';
-import { Badge } from '@/components/ui/badge';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
-import { SetPageTitle } from '@/components/SetPageTitle';
import { Button } from '@/components/ui/button';
+import {
+ Sheet,
+ SheetContent,
+ SheetDescription,
+ SheetHeader,
+ SheetTitle,
+ SheetTrigger,
+} from '@/components/ui/sheet';
+import { SidebarTrigger } from '@/components/ui/sidebar';
+import { HideAppTopbar } from '@/components/gastown/HideAppTopbar';
import {
AlertTriangle,
+ BookOpenText,
+ CircleHelp,
ExternalLink,
- RefreshCw,
+ FileClock,
LayoutDashboard,
ListChecks,
+ RefreshCw,
Settings2,
+ type LucideIcon,
} from 'lucide-react';
import { cn } from '@/lib/utils';
import { useTRPC } from '@/lib/trpc/utils';
@@ -20,10 +32,266 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
import { toast } from 'sonner';
import { useSecurityAgent } from './SecurityAgentContext';
+const SECURITY_AGENT_DOCS_URL = 'https://kilo.ai/docs/deploy-secure/security-reviews';
+
+const SECURITY_AGENT_HELP_CONTENT = {
+ dashboard: {
+ title: 'Dashboard help',
+ description:
+ 'Review current security posture, SLA pressure, and the Security Finding that needs attention first.',
+ sections: [
+ {
+ title: 'Review current posture',
+ items: [
+ 'Use the repository filter to update every dashboard metric for one repository or all repositories.',
+ 'With SLA tracking on, review compliance, passed deadlines, upcoming deadlines, and findings without deadlines.',
+ 'With SLA tracking off, focus on open findings, confirmed exploitability, human review, and incomplete analysis.',
+ ],
+ },
+ {
+ title: 'Choose the next action',
+ items: [
+ 'The recommended finding prioritizes overdue work, incomplete analysis, confirmed exploitability, and findings needing review.',
+ 'Open a dashboard metric or recommendation to view matching findings.',
+ ],
+ },
+ ],
+ docsUrl: `${SECURITY_AGENT_DOCS_URL}#use-the-dashboard`,
+ docsLabel: 'Read about the dashboard',
+ docsDescription: 'Metrics, filters, and prioritization',
+ },
+ findings: {
+ title: 'Findings help',
+ description:
+ 'Filter the vulnerability backlog, review evidence, and take the next safe action.',
+ sections: [
+ {
+ title: 'Focus the backlog',
+ items: [
+ 'Filter by repository, severity, or analysis outcome, then sort by severity or SLA due date.',
+ 'Each finding shows its analysis outcome, remediation status, and available next action.',
+ ],
+ },
+ {
+ title: 'Inspect and act',
+ items: [
+ 'Details shows Dependabot metadata, repository context, status, and timing.',
+ 'Analysis shows triage and sandbox evidence, progress, reasoning, suggested fixes, and retry actions.',
+ 'Remediation shows eligibility, attempt history, pull request outcomes, validation evidence, and cancellation state.',
+ ],
+ },
+ ],
+ docsUrl: `${SECURITY_AGENT_DOCS_URL}#browse-findings`,
+ docsLabel: 'Read about findings',
+ docsDescription: 'Filters, analysis, and remediation actions',
+ },
+ auditReport: {
+ title: 'Audit report help',
+ description: 'Review Security Finding activity recorded by Kilo for a selected UTC period.',
+ sections: [
+ {
+ title: 'Generate a report',
+ items: [
+ 'Choose a UTC date range of up to 90 inclusive calendar days.',
+ 'Filter finding groups by severity, recorded state, or repository. Matching groups keep their complete in-period timeline.',
+ ],
+ },
+ {
+ title: 'Understand the evidence',
+ items: [
+ 'Reports include recorded imports, status and severity changes, analysis outcomes, dismissals, remediation activity, and deletions.',
+ 'Reports do not prove complete legacy history, repository scan coverage, or aggregate historical SLA compliance.',
+ 'If report generation fails, Kilo returns no partial report. Select a shorter period and generate it again.',
+ ],
+ },
+ ],
+ docsUrl: `${SECURITY_AGENT_DOCS_URL}#audit-reports`,
+ docsLabel: 'Read about audit reports',
+ docsDescription: 'Periods, filters, evidence, and access',
+ },
+ settings: {
+ title: 'Settings help',
+ description: 'Control repository scope, analysis, automation, notifications, and SLA policy.',
+ sections: [
+ {
+ title: 'General',
+ items: [
+ 'Turn Security Agent on or off, select repositories, choose models, and set analysis mode.',
+ 'Auto mode runs triage first and starts sandbox analysis only when project-specific evidence is needed.',
+ ],
+ },
+ {
+ title: 'Automation',
+ items: [
+ 'Configure Auto Analysis, Auto Remediation, and Auto Dismiss thresholds and include-existing behavior.',
+ 'Auto Remediation is off by default and only starts work for eligible findings that pass safety checks.',
+ ],
+ },
+ {
+ title: 'Notifications and SLA',
+ items: [
+ 'New-finding Notifications email eligible recipients when Kilo first inserts a finding. Historical insertions are not replayed.',
+ 'SLA settings control severity deadlines, warning lead time, SLA Warning Notifications, and SLA Breach Notifications.',
+ 'Organization emails go only to current organization owners.',
+ ],
+ },
+ ],
+ docsUrl: `${SECURITY_AGENT_DOCS_URL}#configure-security-agent`,
+ docsLabel: 'Read about settings',
+ docsDescription: 'General, automation, notifications, and SLA',
+ },
+ overview: {
+ title: 'Security Agent help',
+ description:
+ 'Learn how Security Agent syncs Security Findings, analyzes risk, and guides remediation.',
+ sections: [
+ {
+ title: 'How Security Agent works',
+ items: [
+ 'Sync imports Dependabot alerts from repositories in scope as Security Findings.',
+ 'Triage and sandbox analysis determine project-specific risk and recommend the next action.',
+ 'Remediation can ask Cloud Agent to prepare a fix and open a pull request for an eligible finding.',
+ ],
+ },
+ ],
+ docsUrl: `${SECURITY_AGENT_DOCS_URL}#how-security-agent-works`,
+ docsLabel: 'View Security Agent documentation',
+ docsDescription: 'Setup, analysis, remediation, and settings',
+ },
+} satisfies Record<
+ string,
+ {
+ title: string;
+ description: string;
+ sections: { title: string; items: string[] }[];
+ docsUrl: string;
+ docsLabel: string;
+ docsDescription: string;
+ }
+>;
+
+export function getSecurityAgentHelpContent(pathname: string, basePath: string) {
+ const relativePath = pathname.startsWith(basePath) ? pathname.slice(basePath.length) : null;
+
+ if (relativePath === '' || relativePath === '/') return SECURITY_AGENT_HELP_CONTENT.dashboard;
+ if (relativePath === '/findings' || relativePath?.startsWith('/findings/')) {
+ return SECURITY_AGENT_HELP_CONTENT.findings;
+ }
+ if (relativePath === '/audit-report' || relativePath?.startsWith('/audit-report/')) {
+ return SECURITY_AGENT_HELP_CONTENT.auditReport;
+ }
+ if (relativePath === '/config' || relativePath?.startsWith('/config/')) {
+ return SECURITY_AGENT_HELP_CONTENT.settings;
+ }
+ return SECURITY_AGENT_HELP_CONTENT.overview;
+}
+
type SecurityAgentLayoutProps = {
children: React.ReactNode;
};
+type SecurityAgentNavItem = {
+ label: string;
+ href: string;
+ icon: LucideIcon;
+};
+
+export function getSecurityAgentNavItems({
+ basePath,
+ showSetupOnly,
+ isEnabled,
+}: {
+ basePath: string;
+ showSetupOnly: boolean;
+ isEnabled: boolean | undefined;
+}): SecurityAgentNavItem[] {
+ const auditReport = {
+ label: 'Audit report',
+ href: `${basePath}/audit-report`,
+ icon: FileClock,
+ } satisfies SecurityAgentNavItem;
+ const settings = {
+ label: 'Settings',
+ href: `${basePath}/config`,
+ icon: Settings2,
+ } satisfies SecurityAgentNavItem;
+
+ if (showSetupOnly) return [auditReport, settings];
+
+ return [
+ { label: 'Dashboard', href: basePath, icon: LayoutDashboard },
+ ...(isEnabled ? [{ label: 'Findings', href: `${basePath}/findings`, icon: ListChecks }] : []),
+ auditReport,
+ settings,
+ ];
+}
+
+type SecurityAgentHelpProps = {
+ pathname: string;
+ basePath: string;
+ initiallyOpen?: boolean;
+};
+
+export function SecurityAgentHelp({
+ pathname,
+ basePath,
+ initiallyOpen = false,
+}: SecurityAgentHelpProps) {
+ const content = getSecurityAgentHelpContent(pathname, basePath);
+
+ return (
+
+
+
+
+ Help
+
+
+
+
+ {content.title}
+ {content.description}
+
+
+ {content.sections.map(section => (
+
+ {section.title}
+
+ {section.items.map(item => (
+ {item}
+ ))}
+
+
+ ))}
+
+
+
+
+ );
+}
+
export function SecurityAgentLayout({ children }: SecurityAgentLayoutProps) {
const pathname = usePathname();
const trpc = useTRPC();
@@ -34,17 +302,17 @@ export function SecurityAgentLayout({ children }: SecurityAgentLayoutProps) {
hasIntegration,
hasPermission,
isLoadingPermission,
+ isLoadingConfig,
isEnabled,
+ hasConfig,
reauthorizeUrl,
} = useSecurityAgent();
const basePath = isOrg ? `/organizations/${organizationId}/security-agent` : '/security-agent';
+ const showSetupOnly =
+ (!isLoadingPermission && !hasIntegration) || (!isLoadingConfig && !hasConfig);
- const navItems = [
- { label: 'Dashboard', href: basePath, icon: LayoutDashboard },
- ...(isEnabled ? [{ label: 'Findings', href: `${basePath}/findings`, icon: ListChecks }] : []),
- { label: 'Settings', href: `${basePath}/config`, icon: Settings2 },
- ];
+ const navItems = getSecurityAgentNavItems({ basePath, showSetupOnly, isEnabled });
// Refresh installation mutation (only used in layout for permission alert)
const { mutate: refreshMutate, isPending: isRefreshing } = useMutation(
@@ -101,32 +369,33 @@ export function SecurityAgentLayout({ children }: SecurityAgentLayoutProps) {
}
return (
-
-
- Beta
-
- {/* Header */}
-
+
+
+
+
+
+
{navItems.map(item => {
const Icon = item.icon;
const active = isActive(item.href);
@@ -136,64 +405,65 @@ export function SecurityAgentLayout({ children }: SecurityAgentLayoutProps) {
href={item.href}
aria-current={active ? 'page' : undefined}
className={cn(
- 'focus-visible:ring-ring flex shrink-0 items-center gap-2 rounded-t-md border-b-2 px-4 py-2.5 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none',
+ 'focus-visible:ring-ring flex min-h-control-touch shrink-0 items-center gap-2 rounded-t-md border-b-2 px-4 type-body font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none',
active
- ? 'border-brand-primary text-foreground'
+ ? 'border-primary text-foreground'
: 'text-muted-foreground hover:text-foreground border-transparent hover:border-border'
)}
>
-
+
{item.label}
);
})}
-
- )}
-
- {/* Additional Permissions Required Alert */}
- {showPermissionRequired && (
-
-
- Additional permissions required
-
-
- Security Agent requires the vulnerability_alerts permission to access
- Dependabot alerts. Re-authorize GitHub App to grant this permission.
-
-
- {reauthorizeUrl && (
+
+
+
+
+ {showPermissionRequired && (
+
+
+ Additional permissions required
+
+
+ Security Agent requires the vulnerability_alerts permission to access
+ Dependabot alerts. Re-authorize GitHub App to grant this permission.
+
+
-
- Already approved permissions in GitHub? Refresh permissions to update Security Agent.
-
-
-
- )}
+
+
+ Already approved permissions in GitHub? Refresh permissions to update Security
+ Agent.
+
+
+
+ )}
- {/* Page content */}
- {children}
+ {children}
+
);
}
diff --git a/apps/web/src/components/security-agent/SecurityAuditReportPage.test.ts b/apps/web/src/components/security-agent/SecurityAuditReportPage.test.ts
new file mode 100644
index 0000000000..6ffa15e0ea
--- /dev/null
+++ b/apps/web/src/components/security-agent/SecurityAuditReportPage.test.ts
@@ -0,0 +1,443 @@
+import { describe, expect, it } from '@jest/globals';
+import { createElement } from 'react';
+import { renderToStaticMarkup } from 'react-dom/server';
+import type {
+ SecurityAgentAuditReport,
+ SecurityAgentAuditReportEvent,
+ SecurityFindingAuditSection,
+} from '@/lib/security-agent/db/security-audit-report';
+import {
+ AuditReportProvenance,
+ filterSecurityAgentAuditReport,
+ formatAuditEventTime,
+ formatDateTime24Hour,
+ getAuditEventDetails,
+ getDefaultAuditReportDateRange,
+ getAuditReportProvenance,
+ getAuditReportRepositoryHref,
+ getAuditReportRepositoryOptions,
+ hasSecurityAgentAuditReportOwnerContext,
+ normalizeAuditReportRepositoryFilter,
+ parseAuditReportFilters,
+} from './SecurityAuditReportPage';
+
+function finding(
+ overrides: Partial
&
+ Pick
+): SecurityFindingAuditSection {
+ return {
+ source: 'dependabot',
+ sourceId: '1',
+ repository: 'kilo/repo',
+ title: 'Test finding',
+ packageName: 'test-package',
+ packageEcosystem: 'npm',
+ manifestPath: 'package.json',
+ patchedVersion: null,
+ ghsaId: null,
+ cveId: null,
+ cweIds: [],
+ cvssScore: null,
+ dependabotUrl: null,
+ firstDetectedAt: '2026-06-01T00:00:00.000Z',
+ canonicalFindingId: null,
+ deleted: false,
+ sla: { status: 'unknown', deadline: null, reason: 'not recorded' },
+ events: [],
+ hasLegacySupplementalActivity: false,
+ ...overrides,
+ };
+}
+
+function event(
+ overrides: Partial & Pick
+): SecurityAgentAuditReportEvent {
+ return {
+ id: 'event-1',
+ label: 'Event',
+ occurredAt: '2026-06-15T16:04:00.000Z',
+ sourceOccurredAt: null,
+ recordedAt: '2026-06-15T16:04:00.000Z',
+ actor: { type: 'system', displayName: 'Kilo system', masked: false },
+ beforeState: null,
+ afterState: null,
+ metadata: null,
+ legacySupplemental: false,
+ ...overrides,
+ };
+}
+
+function report(findings: SecurityFindingAuditSection[]): SecurityAgentAuditReport {
+ return {
+ reportVersion: 1,
+ owner: { type: 'user', id: 'user-1', displayName: 'Test User' },
+ period: {
+ start: '2026-06-01T00:00:00.000Z',
+ endExclusive: '2026-06-16T00:00:00.000Z',
+ displayEnd: '2026-06-15',
+ timeZone: 'UTC',
+ },
+ generatedAt: '2026-06-16T00:00:00.000Z',
+ dataThrough: '2026-06-16T00:00:00.000Z',
+ reliableCoverageStart: '2026-06-12T00:00:00.000Z',
+ evidenceBasis: 'recorded_by_kilo',
+ hasLegacySupplementalActivity: true,
+ summary: {
+ findingCount: findings.length,
+ activityCount: findings.reduce((count, item) => count + item.events.length, 0),
+ bySeverity: { critical: 0, high: 2, medium: 0, low: 1 },
+ byAction: {},
+ },
+ findings,
+ };
+}
+
+describe('audit report date range', () => {
+ it('defaults to 90 UTC calendar days ending on the current day', () => {
+ expect(getDefaultAuditReportDateRange(new Date('2026-06-16T21:08:07+02:00'))).toEqual({
+ startDate: '2026-03-19',
+ endDate: '2026-06-16',
+ });
+ });
+});
+
+describe('audit report owner context', () => {
+ it('allows personal reports and gates organization reports only on organization context', () => {
+ expect(hasSecurityAgentAuditReportOwnerContext(false, undefined)).toBe(true);
+ expect(hasSecurityAgentAuditReportOwnerContext(true, 'org-1')).toBe(true);
+ expect(hasSecurityAgentAuditReportOwnerContext(true, undefined)).toBe(false);
+ });
+});
+
+describe('audit report filters', () => {
+ it('parses supported URL filters and defaults unsupported values to all', () => {
+ expect(
+ parseAuditReportFilters(
+ new URLSearchParams('severity=high&state=ignored&repoFullName=kilo%2Fweb')
+ )
+ ).toEqual({
+ severity: 'high',
+ state: 'ignored',
+ repository: 'kilo/web',
+ });
+ expect(parseAuditReportFilters(new URLSearchParams('severity=unknown&state=deleted'))).toEqual({
+ severity: 'all',
+ state: 'deleted',
+ repository: null,
+ });
+ expect(parseAuditReportFilters(new URLSearchParams('state=closed'))).toEqual({
+ severity: 'all',
+ state: 'all',
+ repository: null,
+ });
+ });
+
+ it('filters finding groups by severity, state, and repository while preserving complete timelines', () => {
+ const input = report([
+ finding({
+ findingId: 'high-open',
+ severity: 'high',
+ status: 'open',
+ events: [
+ {
+ id: 'event-1',
+ action: 'security.finding.created',
+ } as SecurityFindingAuditSection['events'][number],
+ ],
+ }),
+ finding({
+ findingId: 'high-dismissed',
+ severity: 'high',
+ status: 'ignored',
+ repository: 'kilo/web',
+ events: [
+ {
+ id: 'event-2',
+ action: 'security.finding.created',
+ } as SecurityFindingAuditSection['events'][number],
+ {
+ id: 'event-3',
+ action: 'security.finding.dismissed',
+ } as SecurityFindingAuditSection['events'][number],
+ ],
+ hasLegacySupplementalActivity: true,
+ }),
+ finding({
+ findingId: 'high-dismissed-other-repository',
+ severity: 'high',
+ status: 'ignored',
+ repository: 'kilo/api',
+ events: [
+ {
+ id: 'event-other-repository',
+ action: 'security.finding.dismissed',
+ } as SecurityFindingAuditSection['events'][number],
+ ],
+ }),
+ finding({
+ findingId: 'low-dismissed',
+ severity: 'low',
+ status: 'ignored',
+ repository: 'kilo/web',
+ events: [
+ {
+ id: 'event-4',
+ action: 'security.finding.dismissed',
+ } as SecurityFindingAuditSection['events'][number],
+ ],
+ }),
+ ]);
+
+ const filtered = filterSecurityAgentAuditReport(input, {
+ severity: 'high',
+ state: 'ignored',
+ repository: 'kilo/web',
+ });
+
+ expect(filtered.findings.map(item => item.findingId)).toEqual(['high-dismissed']);
+ expect(filtered.findings[0]?.events).toHaveLength(2);
+ expect(filtered.summary).toEqual({
+ findingCount: 1,
+ activityCount: 2,
+ bySeverity: { critical: 0, high: 1, medium: 0, low: 0 },
+ byAction: {
+ 'security.finding.created': 1,
+ 'security.finding.dismissed': 1,
+ },
+ });
+ expect(filtered.hasLegacySupplementalActivity).toBe(true);
+ });
+
+ it('filters deleted and superseded finding evidence by displayed state', () => {
+ const input = report([
+ finding({
+ findingId: 'deleted',
+ severity: 'low',
+ status: 'ignored',
+ deleted: true,
+ }),
+ finding({
+ findingId: 'superseded',
+ severity: 'high',
+ status: 'ignored',
+ canonicalFindingId: 'current-finding',
+ }),
+ finding({ findingId: 'dismissed', severity: 'high', status: 'ignored' }),
+ ]);
+
+ expect(
+ filterSecurityAgentAuditReport(input, {
+ severity: 'all',
+ state: 'deleted',
+ repository: null,
+ }).findings.map(item => item.findingId)
+ ).toEqual(['deleted']);
+ expect(
+ filterSecurityAgentAuditReport(input, {
+ severity: 'all',
+ state: 'superseded',
+ repository: null,
+ }).findings.map(item => item.findingId)
+ ).toEqual(['superseded']);
+ expect(
+ filterSecurityAgentAuditReport(input, {
+ severity: 'all',
+ state: 'ignored',
+ repository: null,
+ }).findings.map(item => item.findingId)
+ ).toEqual(['dismissed']);
+ });
+
+ it('keeps report unchanged when all values are selected', () => {
+ const input = report([]);
+ expect(
+ filterSecurityAgentAuditReport(input, {
+ severity: 'all',
+ state: 'all',
+ repository: null,
+ })
+ ).toBe(input);
+ });
+
+ it('builds safe GitHub links for recorded repository names', () => {
+ expect(getAuditReportRepositoryHref('Kilo-Org/security-agent')).toBe(
+ 'https://github.com/Kilo-Org/security-agent'
+ );
+ expect(getAuditReportRepositoryHref('invalid/repository/path')).toBeNull();
+ expect(getAuditReportRepositoryHref('owner/repository?tab=settings')).toBeNull();
+ expect(getAuditReportRepositoryHref(null)).toBeNull();
+ });
+
+ it('builds sorted repository options only from report evidence', () => {
+ const findings = [
+ finding({ findingId: '2', severity: 'high', status: 'open', repository: 'kilo/web' }),
+ finding({ findingId: '1', severity: 'low', status: 'fixed', repository: 'kilo/api' }),
+ finding({ findingId: '3', severity: 'low', status: 'fixed', repository: 'kilo/web' }),
+ finding({ findingId: '4', severity: 'low', status: null, repository: null }),
+ ];
+
+ expect(getAuditReportRepositoryOptions(findings)).toEqual(['kilo/api', 'kilo/web']);
+ });
+
+ it('normalizes stale repository filters once report evidence is available', () => {
+ const staleFilters = {
+ severity: 'high',
+ state: 'open',
+ repository: 'legacy/renamed',
+ } as const;
+ const validFilters = { ...staleFilters, repository: 'kilo/web' };
+
+ expect(normalizeAuditReportRepositoryFilter(staleFilters, ['kilo/api', 'kilo/web'])).toEqual({
+ severity: 'high',
+ state: 'open',
+ repository: null,
+ });
+ expect(normalizeAuditReportRepositoryFilter(validFilters, ['kilo/api', 'kilo/web'])).toBe(
+ validFilters
+ );
+ });
+});
+
+describe('audit report provenance', () => {
+ it('renders provenance and coverage warning for a successful empty report', () => {
+ const html = renderToStaticMarkup(createElement(AuditReportProvenance, { report: report([]) }));
+
+ expect(html).toContain('Security Finding activity recorded by Kilo.');
+ expect(html).toContain('Data cutoff');
+ expect(html).toContain('Jun 16, 2026 at 00:00 UTC');
+ expect(html).toContain('Reliable coverage starts');
+ expect(html).toContain('Jun 12, 2026 at 00:00 UTC');
+ expect(html).toContain(
+ 'This period starts before reliable event coverage, and supplemental legacy activity may be incomplete.'
+ );
+ });
+
+ it('warns independently for pre-coverage periods and supplemental legacy activity', () => {
+ const baseReport = report([]);
+ const preCoverage = {
+ ...baseReport,
+ hasLegacySupplementalActivity: false,
+ };
+ const legacySupplemental = {
+ ...baseReport,
+ period: { ...baseReport.period, start: baseReport.reliableCoverageStart },
+ };
+ const completeCoverage = {
+ ...legacySupplemental,
+ hasLegacySupplementalActivity: false,
+ };
+
+ expect(getAuditReportProvenance(preCoverage).warning).toBe(
+ 'Activity before reliable event coverage may be incomplete.'
+ );
+ expect(getAuditReportProvenance(legacySupplemental).warning).toBe(
+ 'Supplemental legacy activity may be incomplete.'
+ );
+ expect(getAuditReportProvenance(completeCoverage).warning).toBeNull();
+ });
+});
+
+describe('audit report event presentation', () => {
+ it('formats event time as 24-hour UTC', () => {
+ expect(formatAuditEventTime('2026-06-15T16:04:00.000Z')).toBe('16:04 UTC');
+ });
+
+ it('formats SLA deadlines as 24-hour UTC without AM or PM', () => {
+ expect(formatDateTime24Hour('2026-03-19T16:04:00.000Z')).toBe('Mar 19, 2026, 16:04 UTC');
+ });
+
+ it('describes not-used dismissals without claiming the dependency is absent', () => {
+ const details = getAuditEventDetails(
+ event({
+ action: 'security.finding.auto_dismissed' as SecurityAgentAuditReportEvent['action'],
+ beforeState: { status: 'open' },
+ afterState: { status: 'ignored', reason_code: 'not_used' },
+ })
+ );
+
+ expect(details).toEqual([
+ { label: 'State', value: 'Dismissed', previousValue: 'Open' },
+ { label: 'Reason', value: 'Vulnerable code is not used' },
+ ]);
+ });
+
+ it('presents successful structured analysis without unrelated triage confidence', () => {
+ const details = getAuditEventDetails(
+ event({
+ action: 'security.finding.analysis_completed' as SecurityAgentAuditReportEvent['action'],
+ afterState: {
+ analysis_status: 'completed',
+ structured_extraction_status: 'succeeded',
+ is_exploitable: false,
+ suggested_action: 'dismiss',
+ confidence: 'low',
+ },
+ })
+ );
+
+ expect(details).toEqual([
+ { label: 'Exploitability', value: 'Not exploitable' },
+ { label: 'Recommended next step', value: 'Dismiss finding' },
+ ]);
+ });
+
+ it('presents a structured extraction failure without claiming an exploitability result', () => {
+ const details = getAuditEventDetails(
+ event({
+ action: 'security.finding.analysis_completed' as SecurityAgentAuditReportEvent['action'],
+ afterState: {
+ analysis_status: 'completed',
+ structured_extraction_status: 'failed',
+ is_exploitable: 'unknown',
+ suggested_action: 'manual_review',
+ confidence: 'low',
+ },
+ })
+ );
+
+ expect(details).toEqual([
+ { label: 'Structured result', value: 'Unavailable' },
+ { label: 'Recommended next step', value: 'Manual review' },
+ ]);
+ });
+
+ it('presents remediation requests without raw state and evidence groups', () => {
+ const details = getAuditEventDetails(
+ event({
+ action: 'security.remediation.queued' as SecurityAgentAuditReportEvent['action'],
+ afterState: { remediation_status: 'queued', attempt_number: 1 },
+ metadata: { origin: 'manual' },
+ })
+ );
+
+ expect(details).toEqual([
+ { label: 'Attempt', value: '1' },
+ { label: 'Requested', value: 'Manually' },
+ ]);
+ });
+
+ it('presents pull request outcomes with one safe link and useful status', () => {
+ const details = getAuditEventDetails(
+ event({
+ action: 'security.remediation.pr_opened' as SecurityAgentAuditReportEvent['action'],
+ beforeState: { remediation_status: 'running' },
+ afterState: { remediation_status: 'pr_opened', pr_number: 5, pr_draft: false },
+ metadata: {
+ origin: 'manual',
+ pr_url: 'https://github.com/kilo/repo/pull/5',
+ validation_count: 1,
+ },
+ })
+ );
+
+ expect(details).toEqual([
+ {
+ label: 'Pull request',
+ value: '#5',
+ href: 'https://github.com/kilo/repo/pull/5',
+ },
+ { label: 'Review state', value: 'Ready for review' },
+ { label: 'Validation checks', value: '1' },
+ ]);
+ });
+});
diff --git a/apps/web/src/components/security-agent/SecurityAuditReportPage.tsx b/apps/web/src/components/security-agent/SecurityAuditReportPage.tsx
new file mode 100644
index 0000000000..715eabce86
--- /dev/null
+++ b/apps/web/src/components/security-agent/SecurityAuditReportPage.tsx
@@ -0,0 +1,1549 @@
+'use client';
+
+import { useSearchParams } from 'next/navigation';
+import React, { type FormEvent, type ReactNode, useState } from 'react';
+import { useQuery } from '@tanstack/react-query';
+import { differenceInCalendarDays, format as formatCalendarDateLabel } from 'date-fns';
+import {
+ Activity,
+ Brain,
+ CalendarDays,
+ CheckCircle2,
+ CircleDot,
+ ExternalLink,
+ FileClock,
+ GitMerge,
+ GitPullRequest,
+ Info,
+ Loader2,
+ RefreshCw,
+ ShieldCheck,
+ Trash2,
+ TriangleAlert,
+ UserRound,
+ XCircle,
+ type LucideIcon,
+} from 'lucide-react';
+import type { DateRange as DayPickerDateRange } from 'react-day-picker';
+import {
+ Accordion,
+ AccordionContent,
+ AccordionItem,
+ AccordionTrigger,
+} from '@/components/ui/accordion';
+import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
+import { Badge } from '@/components/ui/badge';
+import { Button } from '@/components/ui/button';
+import { Calendar } from '@/components/ui/calendar';
+import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from '@/components/ui/select';
+import { Skeleton } from '@/components/ui/skeleton';
+import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
+import { useTRPC } from '@/lib/trpc/utils';
+import { cn } from '@/lib/utils';
+import { useSecurityAgent } from './SecurityAgentContext';
+import { SecurityAgentActionBar, SecurityAgentActionBarField } from './SecurityAgentActionBar';
+import { SeverityBadge } from './SeverityBadge';
+import type {
+ SecurityAgentAuditReport,
+ SecurityAgentAuditReportEvent,
+ SecurityFindingAuditSection,
+} from '@/lib/security-agent/db/security-audit-report';
+
+type DateRange = {
+ startDate: string;
+ endDate: string;
+};
+
+type AuditReportSeverityFilter = 'all' | 'critical' | 'high' | 'medium' | 'low';
+type AuditReportStateFilter = 'all' | 'open' | 'fixed' | 'ignored' | 'superseded' | 'deleted';
+
+export type AuditReportFilters = {
+ severity: AuditReportSeverityFilter;
+ state: AuditReportStateFilter;
+ repository: string | null;
+};
+
+const SEVERITY_ORDER = ['critical', 'high', 'medium', 'low'] as const;
+const FINDING_SUPERSEDED_ACTION = 'security.finding.superseded';
+const MAX_AUDIT_REPORT_DAYS = 90;
+const MAX_AUDIT_REPORT_RANGE_NIGHTS = MAX_AUDIT_REPORT_DAYS - 1;
+const ALL_AUDIT_REPORT_FILTERS: AuditReportFilters = {
+ severity: 'all',
+ state: 'all',
+ repository: null,
+};
+
+type Tone = 'success' | 'warning' | 'destructive' | 'neutral';
+type FindingDisplayState = 'open' | 'fixed' | 'dismissed' | 'superseded' | 'deleted' | 'unknown';
+
+type EventPresentation = {
+ icon: LucideIcon;
+ tone: Tone;
+};
+
+const toneStyles = {
+ success: {
+ status: 'border-status-success-border bg-status-success-surface text-status-success',
+ icon: 'bg-status-success-surface text-status-success-icon ring-status-success-border',
+ text: 'text-status-success',
+ },
+ warning: {
+ status: 'border-status-warning-border bg-status-warning-surface text-status-warning',
+ icon: 'bg-status-warning-surface text-status-warning-icon ring-status-warning-border',
+ text: 'text-status-warning',
+ },
+ destructive: {
+ status:
+ 'border-status-destructive-border bg-status-destructive-surface text-status-destructive',
+ icon: 'bg-status-destructive-surface text-status-destructive-icon ring-status-destructive-border',
+ text: 'text-status-destructive',
+ },
+ neutral: {
+ status: 'border-status-neutral-border bg-status-neutral-surface text-status-neutral',
+ icon: 'bg-status-neutral-surface text-status-neutral-icon ring-status-neutral-border',
+ text: 'text-status-neutral',
+ },
+} satisfies Record;
+
+const findingStateConfig = {
+ open: { label: 'Open', tone: 'neutral', icon: CircleDot },
+ fixed: { label: 'Fixed', tone: 'success', icon: CheckCircle2 },
+ dismissed: { label: 'Dismissed', tone: 'neutral', icon: XCircle },
+ superseded: { label: 'Superseded', tone: 'neutral', icon: GitMerge },
+ deleted: { label: 'Deleted', tone: 'neutral', icon: Trash2 },
+ unknown: { label: 'Unknown', tone: 'neutral', icon: CircleDot },
+} satisfies Record;
+
+const REPORT_DATE_FORMATTER = new Intl.DateTimeFormat('en', {
+ year: 'numeric',
+ month: 'short',
+ day: 'numeric',
+ timeZone: 'UTC',
+});
+const REPORT_DATE_TIME_FORMATTER = new Intl.DateTimeFormat('en', {
+ year: 'numeric',
+ month: 'short',
+ day: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit',
+ timeZone: 'UTC',
+ timeZoneName: 'short',
+});
+const REPORT_DATE_TIME_24_HOUR_FORMATTER = new Intl.DateTimeFormat('en', {
+ year: 'numeric',
+ month: 'short',
+ day: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit',
+ hourCycle: 'h23',
+ timeZone: 'UTC',
+ timeZoneName: 'short',
+});
+const AUDIT_EVENT_TIME_FORMATTER = new Intl.DateTimeFormat('en-GB', {
+ hour: '2-digit',
+ minute: '2-digit',
+ hourCycle: 'h23',
+ timeZone: 'UTC',
+ timeZoneName: 'short',
+});
+
+export function hasSecurityAgentAuditReportOwnerContext(
+ isOrg: boolean,
+ organizationId: string | undefined
+): boolean {
+ return !isOrg || Boolean(organizationId);
+}
+
+export function SecurityAuditReportPage() {
+ const trpc = useTRPC();
+ const searchParams = useSearchParams();
+ const { isOrg, organizationId } = useSecurityAgent();
+ const requestedRange = {
+ startDate: searchParams.get('startDate') ?? '',
+ endDate: searchParams.get('endDate') ?? '',
+ };
+ const initialRange = isValidAuditReportDateRange(requestedRange)
+ ? requestedRange
+ : getDefaultAuditReportDateRange();
+ const initialFilters = parseAuditReportFilters(searchParams);
+ const [draftRange, setDraftRange] = useState(() =>
+ toDayPickerDateRange(initialRange)
+ );
+ const [submittedRange, setSubmittedRange] = useState(initialRange);
+ const [draftFilters, setDraftFilters] = useState(initialFilters);
+ const [submittedFilters, setSubmittedFilters] = useState(initialFilters);
+ const [isRangePickerOpen, setIsRangePickerOpen] = useState(false);
+ const completeDraftRange = toAuditReportDateRange(draftRange);
+ const latestSelectableDate = utcDateAsLocalCalendarDate(new Date());
+ const hasOwnerContext = hasSecurityAgentAuditReportOwnerContext(isOrg, organizationId);
+
+ const queryOptions = isOrg
+ ? trpc.organizations.securityAgent.getAuditReport.queryOptions({
+ organizationId: organizationId ?? '',
+ ...submittedRange,
+ })
+ : trpc.securityAgent.getAuditReport.queryOptions(submittedRange);
+
+ const {
+ data: reportQueryData,
+ isFetching: isReportFetching,
+ isLoading: isReportLoading,
+ isError: isReportError,
+ } = useQuery({
+ ...queryOptions,
+ enabled: hasOwnerContext,
+ });
+
+ const unfilteredReport = reportQueryData?.status === 'ok' ? reportQueryData.report : null;
+ const repositoryOptions = getAuditReportRepositoryOptions(unfilteredReport?.findings ?? []);
+ const effectiveDraftFilters = unfilteredReport
+ ? normalizeAuditReportRepositoryFilter(draftFilters, repositoryOptions)
+ : draftFilters;
+ const effectiveSubmittedFilters = unfilteredReport
+ ? normalizeAuditReportRepositoryFilter(submittedFilters, repositoryOptions)
+ : submittedFilters;
+ const report = unfilteredReport
+ ? filterSecurityAgentAuditReport(unfilteredReport, effectiveSubmittedFilters)
+ : null;
+
+ function handleGenerateReport(event: FormEvent) {
+ event.preventDefault();
+ if (!completeDraftRange) return;
+ setSubmittedRange(completeDraftRange);
+ setDraftFilters(effectiveDraftFilters);
+ setSubmittedFilters(effectiveDraftFilters);
+ }
+
+ function handleDateRangeSelect(nextRange: DayPickerDateRange | undefined) {
+ if (nextRange?.from && nextRange.to && !isWithinAuditReportRangeLimit(nextRange)) return;
+ setDraftRange(nextRange);
+ if (nextRange?.from && nextRange.to) setIsRangePickerOpen(false);
+ }
+
+ function handleClearFilters() {
+ setDraftFilters(ALL_AUDIT_REPORT_FILTERS);
+ setSubmittedFilters(ALL_AUDIT_REPORT_FILTERS);
+ }
+
+ return (
+
+ {!hasOwnerContext && (
+
+
+ Loading audit report...
+
+ )}
+
+ {hasOwnerContext && (
+ <>
+
+
+
+ >
+ )}
+
+ {hasOwnerContext && isReportLoading &&
}
+
+ {hasOwnerContext && isReportError && (
+
+
+ Audit report could not be loaded
+
+ Kilo did not return partial report content. Check your connection and generate the
+ report again.
+
+
+ )}
+
+ {hasOwnerContext && reportQueryData?.status === 'query_failed' && (
+
+
+ Report query did not finish
+
+ Kilo did not return partial report content. Choose a shorter UTC period and generate the
+ report again.
+
+
+ )}
+
+ {hasOwnerContext && report && unfilteredReport && (
+
+ )}
+
+ );
+}
+
+function AuditReportView({
+ report,
+ provenanceReport,
+ totalFindingCount,
+ hasActiveFilters,
+ onClearFilters,
+}: {
+ report: SecurityAgentAuditReport;
+ provenanceReport: SecurityAgentAuditReport;
+ totalFindingCount: number;
+ hasActiveFilters: boolean;
+ onClearFilters: () => void;
+}) {
+ return (
+
+
+
+
+ {report.findings.length === 0 ? (
+
+ ) : (
+
+ )}
+
+ );
+}
+
+export function getAuditReportProvenance(report: SecurityAgentAuditReport) {
+ const startsBeforeReliableCoverage =
+ Date.parse(report.period.start) < Date.parse(report.reliableCoverageStart);
+ let warning: string | null = null;
+
+ if (startsBeforeReliableCoverage && report.hasLegacySupplementalActivity) {
+ warning =
+ 'This period starts before reliable event coverage, and supplemental legacy activity may be incomplete.';
+ } else if (startsBeforeReliableCoverage) {
+ warning = 'Activity before reliable event coverage may be incomplete.';
+ } else if (report.hasLegacySupplementalActivity) {
+ warning = 'Supplemental legacy activity may be incomplete.';
+ }
+
+ return {
+ evidence: {
+ recorded_by_kilo: 'Security Finding activity recorded by Kilo.',
+ }[report.evidenceBasis],
+ dataThrough: formatReportDateTime(report.dataThrough),
+ reliableCoverageStart: formatReportDateTime(report.reliableCoverageStart),
+ warning,
+ };
+}
+
+export function AuditReportProvenance({ report }: { report: SecurityAgentAuditReport }) {
+ const provenance = getAuditReportProvenance(report);
+
+ return (
+
+
+
+
+
+ Report provenance
+
+
{provenance.evidence}
+
+
+
Data cutoff
+
+ {provenance.dataThrough}
+
+
+
+
Reliable coverage starts
+
+
+ {provenance.reliableCoverageStart}
+
+
+
+
+ {provenance.warning && (
+
+
+ {provenance.warning}
+
+ )}
+
+
+
+ );
+}
+
+function ReportSummary({ report }: { report: SecurityAgentAuditReport }) {
+ const metrics = [
+ { label: 'Findings', value: report.summary.findingCount },
+ { label: 'Events', value: report.summary.activityCount },
+ {
+ label: 'Superseded',
+ value: report.summary.byAction[FINDING_SUPERSEDED_ACTION] ?? 0,
+ },
+ ...SEVERITY_ORDER.map(severity => ({
+ label: titleCase(severity),
+ value: report.summary.bySeverity[severity],
+ })),
+ ];
+
+ return (
+
+
+
+ Report summary
+
+
+
+ {metrics.map(metric => (
+
+
+ {metric.value.toLocaleString()}
+
+
{metric.label}
+
+ ))}
+
+
+ );
+}
+
+function AuditReportEmptyState({
+ startDate,
+ endDate,
+ hasActiveFilters,
+ onClearFilters,
+}: {
+ startDate: string;
+ endDate: string;
+ hasActiveFilters: boolean;
+ onClearFilters: () => void;
+}) {
+ return (
+
+
+
+
+
+
+ {hasActiveFilters ? 'No findings match selected filters' : 'No recorded activity'}
+
+
+ {hasActiveFilters
+ ? 'No Security Finding groups in this report match the selected severity, state, and repository.'
+ : `Kilo has no reportable Security Finding activity from ${startDate} to ${endDate}.`}
+
+ {hasActiveFilters && (
+
+ Clear filters
+
+ )}
+
+
+ );
+}
+
+function FindingTimelineList({
+ findings,
+ totalFindingCount,
+}: {
+ findings: SecurityFindingAuditSection[];
+ totalFindingCount: number;
+}) {
+ return (
+
+
+
+ Expand a Security Finding to review its complete in-period timeline.
+
+
+ Showing {findings.length.toLocaleString()} of {totalFindingCount.toLocaleString()}
+
+
+
+
+
+ Security Finding
+ Recorded state
+ Latest activity
+
+
+ {findings.map(finding => (
+
+
+
+
+
+
+
+
+ ))}
+
+
+
+ );
+}
+
+function FindingSummary({ finding }: { finding: SecurityFindingAuditSection }) {
+ const latestEvent = finding.events[finding.events.length - 1];
+ const eventCountLabel = `${finding.events.length.toLocaleString()} ${finding.events.length === 1 ? 'event' : 'events'}`;
+
+ return (
+
+
+
+
+ {finding.severity === 'unknown' ? (
+ Unknown
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+ {latestEvent && (
+
{latestEvent.label}
+ )}
+
+ {latestEvent ? (
+ {formatDate(latestEvent.occurredAt)}
+ ) : (
+ 'No activity'
+ )}
+ ·
+ {eventCountLabel}
+
+
+
+ );
+}
+
+function FindingStateBadge({ finding }: { finding: SecurityFindingAuditSection }) {
+ const state = getFindingDisplayState(finding);
+ const config = findingStateConfig[state];
+ const Icon = config.icon;
+
+ return (
+
+
+ {config.label}
+
+ );
+}
+
+function FindingDetails({ finding }: { finding: SecurityFindingAuditSection }) {
+ return (
+
+
+
+
Chronological activity
+ UTC
+
+
+ {finding.events.map((event, index) => (
+
+ ))}
+
+
+
+
+ );
+}
+
+function FindingMetadata({ finding }: { finding: SecurityFindingAuditSection }) {
+ const advisoryReferences = [finding.cveId, finding.ghsaId].filter((value): value is string =>
+ Boolean(value)
+ );
+ const repositoryHref = getAuditReportRepositoryHref(finding.repository);
+ const sourceHref =
+ finding.dependabotUrl && isSafeHttpUrl(finding.dependabotUrl) ? finding.dependabotUrl : null;
+ const slaTone = getSlaTone(finding.sla.status);
+
+ return (
+
+ Finding record
+
+
+ {finding.canonicalFindingId && (
+
+ )}
+
+ {sourceHref ? (
+ {formatFindingSource(finding)}
+ ) : (
+ formatFindingSource(finding)
+ )}
+
+
+ {repositoryHref ? (
+
+ {finding.repository ?? 'Not recorded'}
+
+ ) : (
+ (finding.repository ?? 'Not recorded')
+ )}
+
+
+
+
+
+
+ {slaLabel(finding.sla)}
+
+
+
+
+
+
+
+ );
+}
+
+function AuditEventRow({
+ event,
+ isLast,
+}: {
+ event: SecurityAgentAuditReportEvent;
+ isLast: boolean;
+}) {
+ const presentation = getEventPresentation(event);
+ const Icon = presentation.icon;
+ const actorReference = event.actor.type === 'user' ? `user:${event.actor.id ?? 'deleted'}` : null;
+
+ return (
+
+
+ {formatDate(event.occurredAt)}
+ {formatAuditEventTime(event.occurredAt)}
+
+
+
+ {!isLast && }
+
+
+
+
+
+
+
+
{event.label}
+ {event.legacySupplemental && (
+
+
+
+
+ Legacy
+
+
+
+ This event comes from an older record mapped to this finding. Earlier activity may
+ be incomplete.
+
+
+ )}
+
+
+ {event.actor.displayName}
+ {actorReference && {actorReference}}
+
+
+
+
+ );
+}
+
+function getFindingDisplayState(finding: SecurityFindingAuditSection): FindingDisplayState {
+ if (finding.deleted) return 'deleted';
+ if (finding.canonicalFindingId) return 'superseded';
+ if (finding.status === 'open') return 'open';
+ if (finding.status === 'fixed') return 'fixed';
+ if (finding.status === 'ignored' || finding.status === 'dismissed') return 'dismissed';
+ return 'unknown';
+}
+
+function getSlaTone(status: SecurityFindingAuditSection['sla']['status']): Tone {
+ if (status === 'terminal_met') return 'success';
+ if (status === 'terminal_missed' || status === 'open_past_deadline') return 'destructive';
+ if (status === 'open_within_deadline') return 'warning';
+ return 'neutral';
+}
+
+function getEventPresentation(event: SecurityAgentAuditReportEvent): EventPresentation {
+ switch (event.action) {
+ case 'security.finding.severity_changed':
+ return { icon: TriangleAlert, tone: 'warning' };
+ case 'security.finding.status_change':
+ return event.afterState?.status === 'fixed'
+ ? { icon: CheckCircle2, tone: 'success' }
+ : { icon: XCircle, tone: 'neutral' };
+ case 'security.finding.dismissed':
+ case 'security.finding.auto_dismissed':
+ return { icon: XCircle, tone: 'neutral' };
+ case 'security.finding.superseded':
+ return { icon: GitMerge, tone: 'neutral' };
+ case 'security.finding.analysis_completed':
+ if (event.afterState?.is_exploitable === true) return { icon: Brain, tone: 'destructive' };
+ if (event.afterState?.is_exploitable === false) return { icon: Brain, tone: 'success' };
+ return { icon: Brain, tone: 'warning' };
+ case 'security.finding.analysis_failed':
+ case 'security.remediation.failed':
+ return { icon: XCircle, tone: 'destructive' };
+ case 'security.remediation.queued':
+ return { icon: event.actor.type === 'user' ? UserRound : Activity, tone: 'neutral' };
+ case 'security.remediation.pr_opened':
+ return { icon: GitPullRequest, tone: 'success' };
+ case 'security.remediation.blocked':
+ return { icon: TriangleAlert, tone: 'warning' };
+ case 'security.remediation.no_changes_needed':
+ return { icon: ShieldCheck, tone: 'success' };
+ case 'security.remediation.cancelled':
+ return { icon: XCircle, tone: 'neutral' };
+ case 'security.finding.deleted':
+ return { icon: Trash2, tone: 'neutral' };
+ default:
+ return { icon: Activity, tone: 'neutral' };
+ }
+}
+
+export type AuditEventDetail = {
+ label: string;
+ value: string;
+ previousValue?: string;
+ href?: string;
+};
+
+function eventDetail(
+ label: string,
+ value: unknown,
+ fieldKey: string,
+ previousValue?: unknown
+): AuditEventDetail | null {
+ if (value === null || value === undefined || value === '') return null;
+ const detail: AuditEventDetail = {
+ label,
+ value: formatEvidenceScalar(value, fieldKey),
+ };
+ if (previousValue !== null && previousValue !== undefined && previousValue !== '') {
+ const previousLabel = formatEvidenceScalar(previousValue, fieldKey);
+ if (previousLabel !== detail.value) detail.previousValue = previousLabel;
+ }
+ return detail;
+}
+
+function presentEventDetails(
+ details: Array
+): AuditEventDetail[] {
+ return details.filter((detail): detail is AuditEventDetail => Boolean(detail));
+}
+
+export function getAuditEventDetails(event: SecurityAgentAuditReportEvent): AuditEventDetail[] {
+ const before = event.beforeState ?? {};
+ const after = event.afterState ?? {};
+ const metadata = event.metadata ?? {};
+
+ switch (event.action) {
+ case 'security.finding.created':
+ return presentEventDetails([
+ eventDetail('Severity', after.severity, 'severity'),
+ eventDetail('State', after.status, 'status'),
+ eventDetail('Dependabot alert', metadata.source_alert_number, 'source_alert_number'),
+ ]);
+ case 'security.finding.severity_changed':
+ return presentEventDetails([
+ eventDetail('Severity', after.severity, 'severity', before.severity),
+ ]);
+ case 'security.finding.status_change':
+ return presentEventDetails([
+ eventDetail('State', after.status, 'status', before.status),
+ eventDetail('Fixed', after.fixed_at, 'fixed_at'),
+ ]);
+ case 'security.finding.dismissed':
+ case 'security.finding.auto_dismissed':
+ case 'security.finding.superseded':
+ return presentEventDetails([
+ eventDetail('State', after.status, 'status', before.status),
+ eventDetail('Reason', after.reason_code ?? metadata.reason_code, 'reason_code'),
+ ]);
+ case 'security.finding.analysis_completed': {
+ const structuredExtractionStatus = after.structured_extraction_status;
+ const structuredExtractionFailed = structuredExtractionStatus === 'failed';
+ return presentEventDetails([
+ structuredExtractionFailed
+ ? eventDetail(
+ 'Structured result',
+ structuredExtractionStatus,
+ 'structured_extraction_status'
+ )
+ : eventDetail('Exploitability', after.is_exploitable, 'is_exploitable'),
+ eventDetail('Recommended next step', after.suggested_action, 'suggested_action'),
+ eventDetail(
+ 'Confidence',
+ structuredExtractionStatus === undefined ? after.confidence : undefined,
+ 'confidence'
+ ),
+ ]);
+ }
+ case 'security.finding.analysis_failed':
+ return presentEventDetails([eventDetail('Reason', metadata.failure_code, 'failure_code')]);
+ case 'security.remediation.queued':
+ return presentEventDetails([
+ eventDetail('Attempt', after.attempt_number, 'attempt_number'),
+ eventDetail('Requested', metadata.origin, 'origin'),
+ ]);
+ case 'security.remediation.pr_opened': {
+ const pullRequest = eventDetail('Pull request', after.pr_number, 'pr_number');
+ if (pullRequest && typeof metadata.pr_url === 'string' && isSafeHttpUrl(metadata.pr_url)) {
+ pullRequest.href = metadata.pr_url;
+ }
+ return presentEventDetails([
+ pullRequest,
+ eventDetail('Review state', after.pr_draft, 'pr_draft'),
+ eventDetail('Validation checks', metadata.validation_count, 'validation_count'),
+ ]);
+ }
+ case 'security.remediation.failed':
+ return presentEventDetails([
+ eventDetail('Reason', after.failure_code ?? metadata.failure_code, 'failure_code'),
+ ]);
+ case 'security.remediation.blocked':
+ return presentEventDetails([
+ eventDetail(
+ 'Reason',
+ after.blocked_reason_code ?? metadata.blocked_reason_code,
+ 'blocked_reason_code'
+ ),
+ ]);
+ case 'security.finding.deleted':
+ return presentEventDetails([eventDetail('Previous state', before.status, 'status')]);
+ default:
+ return [];
+ }
+}
+
+function EventDetails({ event }: { event: SecurityAgentAuditReportEvent }) {
+ const details = getAuditEventDetails(event);
+ if (details.length === 0) return null;
+
+ return (
+
+ {details.map(detail => (
+
+
{detail.label}
+
+ {detail.href ? (
+ {detail.value}
+ ) : detail.previousValue ? (
+
+ {detail.previousValue}
+
+ {' '}
+ →{' '}
+
+ changed to
+ {detail.value}
+
+ ) : (
+ detail.value
+ )}
+
+
+ ))}
+
+ );
+}
+
+function isMonospaceEventDetail(label: string): boolean {
+ return label === 'Dependabot alert' || label === 'Pull request';
+}
+
+function MetadataItem({
+ label,
+ value,
+ mono = false,
+ children,
+}: {
+ label: string;
+ value?: string;
+ mono?: boolean;
+ children?: ReactNode;
+}) {
+ return (
+
+
{label}
+ {children ?? value}
+
+ );
+}
+
+function ExternalLinkValue({ href, children }: { href: string; children: ReactNode }) {
+ return (
+
+ {children}
+
+
+ );
+}
+
+function AdvisoryMetadata({
+ references,
+ cvssScore,
+}: {
+ references: string[];
+ cvssScore: SecurityFindingAuditSection['cvssScore'];
+}) {
+ const hasAdvisoryMetadata = references.length > 0 || cvssScore !== null;
+
+ return (
+
+
Advisory
+
+ {hasAdvisoryMetadata ? (
+
+ {references.map(reference => (
+
+ {reference}
+
+ ))}
+ {cvssScore !== null && (
+
+ CVSS {cvssScore}
+
+ )}
+
+ ) : (
+ Not recorded
+ )}
+
+
+ );
+}
+
+function AuditReportSkeleton() {
+ return (
+
+ Loading recorded activity
+
+
+
+
+
+
+
+
+ );
+}
+
+type SearchParamsReader = {
+ get(name: string): string | null;
+};
+
+export function parseAuditReportFilters(searchParams: SearchParamsReader): AuditReportFilters {
+ return {
+ severity: parseAuditReportSeverityFilter(searchParams.get('severity')),
+ state: parseAuditReportStateFilter(searchParams.get('state')),
+ repository: parseAuditReportRepositoryFilter(searchParams.get('repoFullName')),
+ };
+}
+
+function parseAuditReportSeverityFilter(value: string | null): AuditReportSeverityFilter {
+ if (value === 'critical' || value === 'high' || value === 'medium' || value === 'low') {
+ return value;
+ }
+ return 'all';
+}
+
+function parseAuditReportStateFilter(value: string | null): AuditReportStateFilter {
+ if (
+ value === 'open' ||
+ value === 'fixed' ||
+ value === 'ignored' ||
+ value === 'superseded' ||
+ value === 'deleted'
+ ) {
+ return value;
+ }
+ return 'all';
+}
+
+function parseAuditReportRepositoryFilter(value: string | null): string | null {
+ const repository = value?.trim();
+ return repository && repository !== 'all' ? repository : null;
+}
+
+function hasActiveAuditReportFilters(filters: AuditReportFilters): boolean {
+ return filters.severity !== 'all' || filters.state !== 'all' || filters.repository !== null;
+}
+
+export function getAuditReportRepositoryOptions(findings: SecurityFindingAuditSection[]): string[] {
+ return [
+ ...new Set(
+ findings
+ .map(finding => finding.repository)
+ .filter((repository): repository is string => Boolean(repository))
+ ),
+ ].toSorted((left, right) => left.localeCompare(right));
+}
+
+export function normalizeAuditReportRepositoryFilter(
+ filters: AuditReportFilters,
+ repositoryOptions: readonly string[]
+): AuditReportFilters {
+ if (filters.repository === null || repositoryOptions.includes(filters.repository)) return filters;
+ return { ...filters, repository: null };
+}
+
+export function filterSecurityAgentAuditReport(
+ report: SecurityAgentAuditReport,
+ filters: AuditReportFilters
+): SecurityAgentAuditReport {
+ if (!hasActiveAuditReportFilters(filters)) return report;
+
+ const findings = report.findings.filter(finding => {
+ const matchesSeverity = filters.severity === 'all' || finding.severity === filters.severity;
+ const displayState = getFindingDisplayState(finding);
+ const matchesState =
+ filters.state === 'all' ||
+ displayState === filters.state ||
+ (filters.state === 'ignored' && displayState === 'dismissed');
+ const matchesRepository =
+ filters.repository === null || finding.repository === filters.repository;
+ return matchesSeverity && matchesState && matchesRepository;
+ });
+ const bySeverity = {
+ critical: 0,
+ high: 0,
+ medium: 0,
+ low: 0,
+ } satisfies SecurityAgentAuditReport['summary']['bySeverity'];
+ const byAction: Record = {};
+ let activityCount = 0;
+
+ for (const finding of findings) {
+ if (finding.severity !== 'unknown') bySeverity[finding.severity] += 1;
+ activityCount += finding.events.length;
+ for (const event of finding.events) {
+ byAction[event.action] = (byAction[event.action] ?? 0) + 1;
+ }
+ }
+
+ return {
+ ...report,
+ hasLegacySupplementalActivity: findings.some(finding => finding.hasLegacySupplementalActivity),
+ summary: {
+ findingCount: findings.length,
+ activityCount,
+ bySeverity,
+ byAction,
+ },
+ findings,
+ };
+}
+
+function isValidAuditReportDateRange(range: DateRange, now = new Date()): boolean {
+ const dayPickerRange = toDayPickerDateRange(range);
+ if (!dayPickerRange?.from || !dayPickerRange.to) return false;
+ return (
+ isWithinAuditReportRangeLimit(dayPickerRange) &&
+ dayPickerRange.to.getTime() <= utcDateAsLocalCalendarDate(now).getTime()
+ );
+}
+
+function toDayPickerDateRange(range: DateRange): DayPickerDateRange | undefined {
+ const from = parseDateOnlyAsLocalCalendarDate(range.startDate);
+ const to = parseDateOnlyAsLocalCalendarDate(range.endDate);
+ if (!from || !to) return undefined;
+ return { from, to };
+}
+
+function toAuditReportDateRange(range: DayPickerDateRange | undefined): DateRange | null {
+ if (!range?.from || !range.to || !isWithinAuditReportRangeLimit(range)) return null;
+ return {
+ startDate: formatLocalCalendarDateAsUtcDate(range.from),
+ endDate: formatLocalCalendarDateAsUtcDate(range.to),
+ };
+}
+
+function isWithinAuditReportRangeLimit(range: DayPickerDateRange): boolean {
+ if (!range.from || !range.to) return false;
+ const inclusiveDays = differenceInCalendarDays(range.to, range.from) + 1;
+ return inclusiveDays >= 1 && inclusiveDays <= MAX_AUDIT_REPORT_DAYS;
+}
+
+function parseDateOnlyAsLocalCalendarDate(value: string): Date | undefined {
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
+ if (!match) return undefined;
+
+ const year = Number(match[1]);
+ const monthIndex = Number(match[2]) - 1;
+ const day = Number(match[3]);
+ const date = new Date(year, monthIndex, day);
+ if (date.getFullYear() !== year || date.getMonth() !== monthIndex || date.getDate() !== day) {
+ return undefined;
+ }
+ return date;
+}
+
+function formatLocalCalendarDateAsUtcDate(value: Date): string {
+ const year = value.getFullYear();
+ const month = String(value.getMonth() + 1).padStart(2, '0');
+ const day = String(value.getDate()).padStart(2, '0');
+ return `${year}-${month}-${day}`;
+}
+
+function utcDateAsLocalCalendarDate(value: Date): Date {
+ return new Date(value.getUTCFullYear(), value.getUTCMonth(), value.getUTCDate());
+}
+
+function formatDayPickerDateRange(range: DayPickerDateRange | undefined): string {
+ if (!range?.from) return 'Select date range';
+ const from = formatCalendarDateLabel(range.from, 'MMM d, yyyy');
+ if (!range.to) return `${from} - Select end date`;
+ return `${from} - ${formatCalendarDateLabel(range.to, 'MMM d, yyyy')}`;
+}
+
+export function getDefaultAuditReportDateRange(now = new Date()): DateRange {
+ const end = startOfUtcDay(now);
+ const start = new Date(end);
+ start.setUTCDate(start.getUTCDate() - 89);
+ return {
+ startDate: formatDateInput(start),
+ endDate: formatDateInput(end),
+ };
+}
+
+function startOfUtcDay(value: Date): Date {
+ return new Date(Date.UTC(value.getUTCFullYear(), value.getUTCMonth(), value.getUTCDate()));
+}
+
+function formatDateInput(value: Date): string {
+ return value.toISOString().slice(0, 10);
+}
+
+function formatDate(value: string): string {
+ return REPORT_DATE_FORMATTER.format(new Date(value));
+}
+
+function formatReportDateTime(value: string): string {
+ return formatDateTime24Hour(value).replace(/, (\d{2}:\d{2} UTC)$/, ' at $1');
+}
+
+function formatDateTime(value: string): string {
+ return REPORT_DATE_TIME_FORMATTER.format(new Date(value));
+}
+
+export function formatDateTime24Hour(value: string): string {
+ return REPORT_DATE_TIME_24_HOUR_FORMATTER.format(new Date(value));
+}
+
+export function formatAuditEventTime(value: string): string {
+ return AUDIT_EVENT_TIME_FORMATTER.format(new Date(value));
+}
+
+function formatFindingSource(finding: SecurityFindingAuditSection): string {
+ if (!finding.source) return 'Not recorded';
+ if (finding.source === 'dependabot') {
+ return finding.sourceId ? `Dependabot alert #${finding.sourceId}` : 'Dependabot';
+ }
+ return titleCase(finding.source);
+}
+
+export function getAuditReportRepositoryHref(repository: string | null): string | null {
+ if (!repository) return null;
+ const segments = repository.split('/');
+ if (segments.length !== 2 || segments.some(segment => !/^[A-Za-z0-9_.-]+$/.test(segment))) {
+ return null;
+ }
+ return `https://github.com/${segments.map(segment => encodeURIComponent(segment)).join('/')}`;
+}
+
+function formatFindingPackage(finding: SecurityFindingAuditSection): string {
+ if (!finding.packageName) return 'Not recorded';
+ if (!finding.packageEcosystem) return finding.packageName;
+ return `${finding.packageName} (${formatPackageEcosystem(finding.packageEcosystem)})`;
+}
+
+function formatPackageEcosystem(ecosystem: string): string {
+ const knownEcosystems: Record = {
+ npm: 'npm',
+ maven: 'Maven',
+ nuget: 'NuGet',
+ pip: 'pip',
+ rubygems: 'RubyGems',
+ composer: 'Composer',
+ go_modules: 'Go modules',
+ github_actions: 'GitHub Actions',
+ };
+ return knownEcosystems[ecosystem] ?? titleCase(ecosystem);
+}
+
+const EVIDENCE_VALUE_LABELS: Record> = {
+ analysis_status: {
+ unknown: 'Previous state unavailable',
+ pending: 'Pending',
+ running: 'In progress',
+ completed: 'Completed',
+ failed: 'Failed',
+ },
+ blocked_reason_code: {
+ COVERED_BY_EXISTING_REMEDIATION_PR:
+ 'An existing remediation pull request already covers this package',
+ blocked: 'Remediation could not proceed',
+ },
+ confidence: {
+ high: 'High',
+ medium: 'Medium',
+ low: 'Low',
+ },
+ failure_code: {
+ analysis_failed: 'Analysis did not complete',
+ QUEUE_ADMISSION_FAILED: 'Remediation could not be queued',
+ ACTOR_RESOLUTION_FAILED: 'Remediation requester could not be resolved',
+ INSUFFICIENT_CREDITS: 'Insufficient credits to start remediation',
+ LAUNCH_UPSTREAM_5XX: 'Remediation service was temporarily unavailable',
+ CLOUD_AGENT_INTERRUPTED: 'Remediation run was interrupted',
+ CLOUD_AGENT_FAILED: 'Cloud Agent could not complete remediation',
+ INVALID_PR_OUTCOME: 'Pull request outcome could not be verified',
+ MISSING_REMEDIATION_RESULT: 'Remediation result was unavailable',
+ },
+ is_exploitable: {
+ true: 'Exploitable',
+ false: 'Not exploitable',
+ unknown: 'Unknown',
+ },
+ origin: {
+ manual: 'Manually',
+ auto_policy: 'Automatically by policy',
+ bulk_existing: 'Automatically for existing findings',
+ },
+ reason_code: {
+ not_used: 'Vulnerable code is not used',
+ tolerable_risk: 'Risk accepted',
+ inaccurate: 'Finding is inaccurate',
+ no_bandwidth: 'Deferred due to capacity',
+ superseded: 'Superseded by another finding',
+ },
+ remediation_status: {
+ queued: 'Requested',
+ launching: 'Starting',
+ running: 'In progress',
+ pr_opened: 'Pull request opened',
+ failed: 'Failed',
+ blocked: 'Blocked',
+ no_changes_needed: 'No changes needed',
+ cancelled: 'Cancelled',
+ },
+ severity: {
+ critical: 'Critical',
+ high: 'High',
+ medium: 'Medium',
+ low: 'Low',
+ },
+ source_state: {
+ open: 'Open',
+ fixed: 'Fixed',
+ dismissed: 'Dismissed',
+ auto_dismissed: 'Automatically dismissed',
+ },
+ status: {
+ open: 'Open',
+ fixed: 'Fixed',
+ ignored: 'Dismissed',
+ },
+ structured_extraction_status: {
+ succeeded: 'Available',
+ failed: 'Unavailable',
+ },
+ suggested_action: {
+ dismiss: 'Dismiss finding',
+ analyze_codebase: 'Analyze codebase',
+ manual_review: 'Manual review',
+ open_pr: 'Open remediation pull request',
+ monitor: 'Monitor',
+ },
+};
+
+const INTERNAL_DETAIL_FALLBACK_FIELDS = new Set([
+ 'blocked_reason_code',
+ 'failure_code',
+ 'reason_code',
+]);
+
+const USER_FACING_TOKEN_FIELDS = new Set(Object.keys(EVIDENCE_VALUE_LABELS));
+
+const DATE_FIELD_PATTERN = /(^|_)(at|date|deadline|cutoff|through|start|end)$/i;
+
+function formatEvidenceScalar(value: unknown, fieldKey: string): string {
+ if (value === null || value === undefined) return 'Not recorded';
+ if (typeof value === 'boolean') {
+ if (fieldKey === 'is_exploitable') {
+ return EVIDENCE_VALUE_LABELS.is_exploitable[String(value)] ?? 'Unknown';
+ }
+ if (fieldKey === 'pr_draft') return value ? 'Draft' : 'Ready for review';
+ if (fieldKey === 'deleted') return value ? 'Deleted' : 'Active';
+ return value ? 'Yes' : 'No';
+ }
+ if (typeof value === 'number') {
+ if (fieldKey === 'pr_number' || fieldKey === 'source_alert_number') return `#${value}`;
+ return value.toLocaleString();
+ }
+ if (typeof value !== 'string') return String(value);
+
+ const trimmed = value.trim();
+ if (!trimmed) return 'Not recorded';
+
+ if (DATE_FIELD_PATTERN.test(fieldKey) && isValidDateString(trimmed)) {
+ return trimmed.length <= 10 ? formatDate(trimmed) : formatDateTime(trimmed);
+ }
+
+ const knownValue = EVIDENCE_VALUE_LABELS[fieldKey]?.[trimmed];
+ if (knownValue) return knownValue;
+ if (INTERNAL_DETAIL_FALLBACK_FIELDS.has(fieldKey)) return 'Additional details unavailable';
+ if (USER_FACING_TOKEN_FIELDS.has(fieldKey)) return 'Unknown';
+
+ return trimmed;
+}
+
+function titleCase(value: string): string {
+ return value
+ .replaceAll('_', ' ')
+ .replaceAll('-', ' ')
+ .replace(/\b\w/g, first => first.toUpperCase());
+}
+
+function isValidDateString(value: string): boolean {
+ return !Number.isNaN(Date.parse(value));
+}
+
+function isSafeHttpUrl(value: string): boolean {
+ try {
+ const url = new URL(value);
+ return url.protocol === 'https:' || url.protocol === 'http:';
+ } catch {
+ return false;
+ }
+}
+
+function slaLabel(sla: SecurityFindingAuditSection['sla']): string {
+ if (sla.status === 'unknown') return 'Unknown';
+ if (sla.status === 'terminal_met') return 'Terminal before deadline';
+ if (sla.status === 'terminal_missed') return 'Terminal after deadline';
+ if (sla.status === 'open_within_deadline') return 'Open before deadline';
+ return 'Open past deadline';
+}
diff --git a/apps/web/src/components/security-agent/SecurityConfigForm.tsx b/apps/web/src/components/security-agent/SecurityConfigForm.tsx
index 74d17a881a..9911f85d35 100644
--- a/apps/web/src/components/security-agent/SecurityConfigForm.tsx
+++ b/apps/web/src/components/security-agent/SecurityConfigForm.tsx
@@ -1,8 +1,8 @@
'use client';
-import { type SetStateAction, useEffect, useState } from 'react';
+import { type SetStateAction, useEffect, useRef, useState } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
-import { Loader2, Save } from 'lucide-react';
+import { Bell, Bot, Clock, Loader2, RotateCcw, Save, SlidersHorizontal } from 'lucide-react';
import { useOrganizationModels } from '@/components/cloud-agent/hooks/useOrganizationModels';
import type { ModelOption } from '@/components/shared/ModelCombobox';
import {
@@ -17,6 +17,8 @@ import {
} from '@/components/ui/alert-dialog';
import { Button } from '@/components/ui/button';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
+import { cn } from '@/lib/utils';
+import type { SecurityAgentUiInteraction } from '@/lib/security-agent/core/schemas';
import {
DEFAULT_SECURITY_AGENT_ANALYSIS_MODEL,
DEFAULT_SECURITY_AGENT_REMEDIATION_MODEL,
@@ -40,6 +42,8 @@ import type {
SecurityRepository,
SlaConfig,
} from './security-config-types';
+import { useSecurityAgent } from './SecurityAgentContext';
+import { SecurityAgentActionBar } from './SecurityAgentActionBar';
type SecurityConfigFormProps = {
organizationId?: string;
@@ -137,15 +141,43 @@ function configsMatch(left: SecurityConfigFormState, right: SecurityConfigFormSt
}
const SETTINGS_TAB_TRIGGER_CLASS =
- 'data-[state=active]:bg-background data-[state=active]:border-border data-[state=active]:text-foreground data-[state=active]:shadow-sm';
+ 'min-h-9 gap-2 border-0 px-3 text-muted-foreground shadow-none hover:bg-surface-hover hover:text-foreground data-[state=active]:border-0 data-[state=active]:bg-surface-selected data-[state=active]:text-foreground data-[state=active]:shadow-none';
const SETTINGS_TABS = ['config', 'automation', 'notifications', 'sla'] as const;
type SettingsTab = (typeof SETTINGS_TABS)[number];
+const SETTINGS_TAB_INTERACTIONS = {
+ config: 'settings_config_viewed',
+ automation: 'settings_automation_viewed',
+ notifications: 'settings_notifications_viewed',
+ sla: 'settings_sla_viewed',
+} satisfies Record;
+
function settingsTabFromParam(tab: string | null): SettingsTab {
return SETTINGS_TABS.find(value => value === tab) ?? 'config';
}
+function useSettingsTabTracking(enabled: boolean, initialTab: SettingsTab) {
+ const { trackUiInteraction } = useSecurityAgent();
+ const trackedInitialTabRef = useRef(false);
+
+ useEffect(() => {
+ if (!enabled) {
+ trackedInitialTabRef.current = false;
+ return;
+ }
+ if (trackedInitialTabRef.current) return;
+
+ trackedInitialTabRef.current = true;
+ trackUiInteraction(SETTINGS_TAB_INTERACTIONS[initialTab]);
+ }, [enabled, initialTab, trackUiInteraction]);
+
+ return (value: string) => {
+ const tab = SETTINGS_TABS.find(settingsTab => settingsTab === value);
+ if (tab) trackUiInteraction(SETTINGS_TAB_INTERACTIONS[tab]);
+ };
+}
+
const SECURITY_AGENT_DEFAULT_MODEL_OPTIONS: ModelOption[] = SECURITY_AGENT_MODELS.map(model => ({
id: model.id,
name: model.name,
@@ -178,7 +210,8 @@ export function SecurityConfigForm({
const { enabled, isLoadingRepositories, isSaving, isToggling } = viewState;
const router = useRouter();
const searchParams = useSearchParams();
- const defaultSettingsTab = settingsTabFromParam(searchParams.get('tab'));
+ const defaultSettingsTab = enabled ? settingsTabFromParam(searchParams.get('tab')) : 'config';
+ const handleSettingsTabChange = useSettingsTabTracking(enabled, defaultSettingsTab);
const initialConfigFingerprint = configFingerprint(initialConfig);
const [pendingNavigationHref, setPendingNavigationHref] = useState(null);
const [savingBeforeNavigation, setSavingBeforeNavigation] = useState(false);
@@ -320,38 +353,113 @@ export function SecurityConfigForm({
return (
-
- onToggleEnabled(nextEnabled, {
- repositorySelectionMode: state.repositorySelectionMode,
- selectedRepositoryIds: state.selectedRepositoryIds,
- })
- }
- />
- {enabled && (
- <>
-
-
-
- Config
-
-
- Automation
-
-
- Notifications
-
-
- SLA
-
-
-
-
+
+
+
+
+
+
+
+ General
+
+
+
+ Automation
+
+
+
+ Notifications
+
+
+
+ SLA
+
+
+
+
+ {enabled && (
+
+
+ {isSaving
+ ? 'Saving changes...'
+ : hasChanges
+ ? 'Unsaved changes'
+ : 'All changes saved'}
+
+
+ setState({
+ ...DEFAULT_FORM_CONFIG,
+ slaConfig: { ...DEFAULT_FORM_CONFIG.slaConfig },
+ selectedRepositoryIds: [],
+ })
+ }
+ disabled={isSaving}
+ >
+
+ Reset defaults
+
+ handleSave()}
+ disabled={saveDisabled}
+ >
+ {isSaving ? (
+
+ ) : (
+
+ )}
+ {isSaving ? 'Saving changes...' : 'Save changes'}
+
+
+ )}
+
+
+
+
+
+ onToggleEnabled(nextEnabled, {
+ repositorySelectionMode: state.repositorySelectionMode,
+ selectedRepositoryIds: state.selectedRepositoryIds,
+ })
+ }
+ />
+ {enabled && (
+ <>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- setState({
- ...DEFAULT_FORM_CONFIG,
- slaConfig: { ...DEFAULT_FORM_CONFIG.slaConfig },
- selectedRepositoryIds: [],
- })
- }
- disabled={isSaving}
- >
- Reset to defaults
-
- handleSave()}
- disabled={saveDisabled}
- >
- {isSaving ? (
-
- ) : (
-
- )}
- {isSaving ? 'Saving...' : 'Save changes'}
-
-
- >
- )}
+ >
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
!open && clearPendingNavigation()}
diff --git a/apps/web/src/components/security-agent/SecurityConfigPage.tsx b/apps/web/src/components/security-agent/SecurityConfigPage.tsx
index eb42054b04..f88489265d 100644
--- a/apps/web/src/components/security-agent/SecurityConfigPage.tsx
+++ b/apps/web/src/components/security-agent/SecurityConfigPage.tsx
@@ -4,6 +4,7 @@ import { Loader2 } from 'lucide-react';
import { ClearFindingsCard } from './ClearFindingsCard';
import { SecurityConfigForm } from './SecurityConfigForm';
import { useSecurityAgent } from './SecurityAgentContext';
+import { SecurityAgentGitHubInstallCta } from './SecurityAgentGitHubInstallCta';
import {
DEFAULT_SECURITY_AGENT_ANALYSIS_MODEL,
DEFAULT_SECURITY_AGENT_REMEDIATION_MODEL,
@@ -14,6 +15,9 @@ import type { SecurityConfigFormState } from './security-config-types';
export function SecurityConfigPage() {
const {
organizationId,
+ isOrg,
+ hasIntegration,
+ isLoadingPermission,
isEnabled,
configData,
allRepositories,
@@ -27,7 +31,7 @@ export function SecurityConfigPage() {
orphanedRepositories,
} = useSecurityAgent();
- if (isLoadingConfig) {
+ if (isLoadingPermission || isLoadingConfig) {
return (
@@ -36,6 +40,14 @@ export function SecurityConfigPage() {
);
}
+ if (!hasIntegration) {
+ const installUrl =
+ isOrg && organizationId
+ ? `/organizations/${organizationId}/integrations`
+ : '/integrations/github';
+ return
;
+ }
+
const initialConfig = {
slaConfig: {
critical: configData?.slaCriticalDays ?? 15,
diff --git a/apps/web/src/components/security-agent/SecurityConfigSections.tsx b/apps/web/src/components/security-agent/SecurityConfigSections.tsx
index 8757ee6097..6ac75eeda6 100644
--- a/apps/web/src/components/security-agent/SecurityConfigSections.tsx
+++ b/apps/web/src/components/security-agent/SecurityConfigSections.tsx
@@ -64,7 +64,7 @@ function SectionHeader({
)}
>
-
+
diff --git a/apps/web/src/components/security-agent/SecurityDashboard.tsx b/apps/web/src/components/security-agent/SecurityDashboard.tsx
index 2d1e8c3b89..ca5ba0dfaf 100644
--- a/apps/web/src/components/security-agent/SecurityDashboard.tsx
+++ b/apps/web/src/components/security-agent/SecurityDashboard.tsx
@@ -1,59 +1,142 @@
'use client';
-import { useState } from 'react';
-import { useSecurityAgent } from './SecurityAgentContext';
-import { useTRPC } from '@/lib/trpc/utils';
+import { useState, type ReactNode } from 'react';
+import Link from 'next/link';
import { useQuery } from '@tanstack/react-query';
-import { Shield, RefreshCw, Loader2 } from 'lucide-react';
import { formatDistanceToNow } from 'date-fns';
+import {
+ AlertCircle,
+ ArrowRight,
+ CalendarClock,
+ CheckCircle2,
+ CircleHelp,
+ Clock,
+ FileSearch,
+ Loader2,
+ RefreshCw,
+ Settings2,
+ ShieldAlert,
+ ShieldCheck,
+ Sparkles,
+ TriangleAlert,
+ type LucideIcon,
+} from 'lucide-react';
+import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { Button } from '@/components/ui/button';
-import Link from 'next/link';
+import { Progress } from '@/components/ui/progress';
+import { Skeleton } from '@/components/ui/skeleton';
+import { cn } from '@/lib/utils';
+import type { DashboardStats } from '@/lib/security-agent/db/dashboard-stats';
+import { useTRPC } from '@/lib/trpc/utils';
import { RepositoryFilter } from './RepositoryFilter';
-import { SlaComplianceHero } from './dashboard/SlaComplianceHero';
-import { SeverityBreakdown } from './dashboard/SeverityBreakdown';
-import { StatusOverview } from './dashboard/StatusOverview';
-import { AnalysisCoverage } from './dashboard/AnalysisCoverage';
-import { MeanTimeToResolution } from './dashboard/MeanTimeToResolution';
-import { OverdueFindingsTable } from './dashboard/OverdueFindingsTable';
-import { RepositoryHealthTable } from './dashboard/RepositoryHealthTable';
-
-const emptySla = {
- overall: { total: 0, withinSla: 0, overdue: 0 },
- bySeverity: {
- critical: { total: 0, withinSla: 0, overdue: 0 },
- high: { total: 0, withinSla: 0, overdue: 0 },
- medium: { total: 0, withinSla: 0, overdue: 0 },
- low: { total: 0, withinSla: 0, overdue: 0 },
+import { SecurityAgentActionBar, SecurityAgentActionBarField } from './SecurityAgentActionBar';
+import { SecurityAgentGitHubInstallCta } from './SecurityAgentGitHubInstallCta';
+import { useSecurityAgent } from './SecurityAgentContext';
+
+const emptyDashboardStats: DashboardStats = {
+ sla: {
+ overall: { total: 0, withinSla: 0, overdue: 0 },
+ bySeverity: {
+ critical: { total: 0, withinSla: 0, overdue: 0 },
+ high: { total: 0, withinSla: 0, overdue: 0 },
+ medium: { total: 0, withinSla: 0, overdue: 0 },
+ low: { total: 0, withinSla: 0, overdue: 0 },
+ },
+ dueSoon: { total: 0, exploitable: 0 },
+ untrackedCount: 0,
},
- untrackedCount: 0,
+ severity: { critical: 0, high: 0, medium: 0, low: 0 },
+ status: { open: 0, fixed: 0, ignored: 0 },
+ analysis: {
+ total: 0,
+ analyzed: 0,
+ exploitable: 0,
+ notExploitable: 0,
+ triageComplete: 0,
+ safeToDismiss: 0,
+ needsReview: 0,
+ analyzing: 0,
+ notAnalyzed: 0,
+ failed: 0,
+ },
+ mttr: {
+ bySeverity: {
+ critical: { avgDays: null, medianDays: null, count: 0, slaDays: 15 },
+ high: { avgDays: null, medianDays: null, count: 0, slaDays: 30 },
+ medium: { avgDays: null, medianDays: null, count: 0, slaDays: 45 },
+ low: { avgDays: null, medianDays: null, count: 0, slaDays: 90 },
+ },
+ },
+ overdue: [],
+ priorityFinding: null,
+ repoHealth: [],
+ repositoryCount: 0,
};
-const emptySeverity = { critical: 0, high: 0, medium: 0, low: 0 };
-
-const emptyStatus = { open: 0, fixed: 0, ignored: 0 };
-
-const emptyAnalysis = {
- total: 0,
- analyzed: 0,
- exploitable: 0,
- notExploitable: 0,
- triageComplete: 0,
- safeToDismiss: 0,
- needsReview: 0,
- analyzing: 0,
- notAnalyzed: 0,
- failed: 0,
+type Repository = {
+ id: number;
+ fullName: string;
+ name: string;
+ private: boolean;
};
-const emptyMttr = {
- bySeverity: {
- critical: { avgDays: null, medianDays: null, count: 0, slaDays: 15 },
- high: { avgDays: null, medianDays: null, count: 0, slaDays: 30 },
- medium: { avgDays: null, medianDays: null, count: 0, slaDays: 45 },
- low: { avgDays: null, medianDays: null, count: 0, slaDays: 90 },
- },
+type MetricTone = 'danger' | 'warning' | 'neutral';
+
+type DashboardMetric = {
+ label: string;
+ value: string;
+ detail: string;
+ icon: LucideIcon;
+ tone: MetricTone;
+};
+
+type SecurityDashboardViewProps = {
+ basePath: string;
+ data: DashboardStats;
+ isLoading: boolean;
+ isError: boolean;
+ slaEnabled: boolean;
+ repositories: Repository[];
+ repoFullName: string | undefined;
+ lastUpdated: string | null;
+ isSyncing: boolean;
+ onRepositoryChange: (repoFullName: string | undefined) => void;
+ onSync: () => void;
+ onRetry: () => void;
};
+type SummaryTone = 'danger' | 'warning' | 'success' | 'neutral';
+
+function getAnalysisIncompleteCount(analysis: DashboardStats['analysis']): number {
+ return analysis.triageComplete + analysis.analyzing + analysis.notAnalyzed + analysis.failed;
+}
+
+function getNeedsActionCount(analysis: DashboardStats['analysis']): number {
+ return (
+ analysis.exploitable +
+ analysis.needsReview +
+ analysis.triageComplete +
+ analysis.notAnalyzed +
+ analysis.failed
+ );
+}
+
+function findingsHref(
+ basePath: string,
+ params: Record
+): string {
+ const searchParams = new URLSearchParams();
+ for (const [key, value] of Object.entries(params)) {
+ if (value !== undefined) searchParams.set(key, String(value));
+ }
+ const query = searchParams.toString();
+ return `${basePath}/findings${query ? `?${query}` : ''}`;
+}
+
+function titleCaseSeverity(severity: string): string {
+ return severity.length > 0 ? `${severity[0].toUpperCase()}${severity.slice(1)}` : 'Security';
+}
+
export function SecurityDashboard() {
const {
hasIntegration,
@@ -67,37 +150,34 @@ export function SecurityDashboard() {
} = useSecurityAgent();
const trpc = useTRPC();
const [repoFullName, setRepoFullName] = useState(undefined);
-
const basePath = isOrg ? `/organizations/${organizationId}/security-agent` : '/security-agent';
-
- // Build a query string suffix for drill-down links so the selected repo filter carries through
- const repoFilterParam = repoFullName ? `&repoFullName=${encodeURIComponent(repoFullName)}` : '';
const slaEnabled = configData?.slaEnabled ?? true;
- const { data, isLoading } = useQuery({
+ const {
+ data: dashboardData,
+ isLoading: isDashboardLoading,
+ isError: dashboardHasError,
+ refetch: refetchDashboard,
+ } = useQuery({
...(isOrg
? trpc.organizations.securityAgent.getDashboardStats.queryOptions({
organizationId: organizationId ?? '',
repoFullName,
})
- : trpc.securityAgent.getDashboardStats.queryOptions({
- repoFullName,
- })),
+ : trpc.securityAgent.getDashboardStats.queryOptions({ repoFullName })),
staleTime: 30_000,
enabled: hasIntegration,
});
- // Use real sync time from DB rather than React Query fetch time
- const { data: lastSyncData } = useQuery(
- isOrg
+ const { data: lastSyncData } = useQuery({
+ ...(isOrg
? trpc.organizations.securityAgent.getLastSyncTime.queryOptions({
organizationId: organizationId ?? '',
repoFullName,
})
- : trpc.securityAgent.getLastSyncTime.queryOptions({
- repoFullName,
- })
- );
+ : trpc.securityAgent.getLastSyncTime.queryOptions({ repoFullName })),
+ enabled: hasIntegration,
+ });
if (isLoadingPermission) {
return (
@@ -112,120 +192,940 @@ export function SecurityDashboard() {
const installUrl = isOrg
? `/organizations/${organizationId}/integrations`
: '/integrations/github';
- return (
-
-
-
Connect GitHub to get started
-
- Install the Kilo GitHub App to automatically sync Dependabot alerts and manage security
- findings across your repositories.
-
-
- Install GitHub App
-
-
- );
+ return ;
}
- const sla = data?.sla ?? emptySla;
- const severity = data?.severity ?? emptySeverity;
- const status = data?.status ?? emptyStatus;
- const analysis = data?.analysis ?? emptyAnalysis;
- const mttr = data?.mttr ?? emptyMttr;
- const overdue = data?.overdue ?? [];
- const repoHealth = data?.repoHealth ?? [];
-
const lastSyncTime = lastSyncData?.lastSyncTime;
const lastUpdated = lastSyncTime
? `Last synced ${formatDistanceToNow(new Date(lastSyncTime), { addSuffix: true })}`
: null;
+ return (
+ handleSync(repoFullName)}
+ onRetry={() => void refetchDashboard()}
+ />
+ );
+}
+
+export function SecurityDashboardView({
+ basePath,
+ data,
+ isLoading,
+ isError,
+ slaEnabled,
+ repositories,
+ repoFullName,
+ lastUpdated,
+ isSyncing,
+ onRepositoryChange,
+ onSync,
+ onRetry,
+}: SecurityDashboardViewProps) {
+ const metrics = buildDashboardMetrics(data, slaEnabled);
+
return (
- {/* Header: filters and actions */}
-
-
-
- {lastUpdated &&
{lastUpdated} }
+
+
+ {isError ? (
+
+ ) : isLoading ? (
+
+ ) : (
+ <>
+
+ {metrics.map(metric => (
+
+ ))}
+
+
+
+
+
+ {slaEnabled ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ >
+ )}
+
+ );
+}
+
+function DashboardToolbar({
+ repositories,
+ repoFullName,
+ isLoading,
+ lastUpdated,
+ isSyncing,
+ onRepositoryChange,
+ onSync,
+}: {
+ repositories: Repository[];
+ repoFullName: string | undefined;
+ isLoading: boolean;
+ lastUpdated: string | null;
+ isSyncing: boolean;
+ onRepositoryChange: (repoFullName: string | undefined) => void;
+ onSync: () => void;
+}) {
+ return (
+
+
+
+
+
+
+ {lastUpdated && (
+
+
+ {lastUpdated}
+
+ )}
{
- handleSync(repoFullName);
- }}
- disabled={isSyncing || isLoading}
+ className="min-h-11 w-full sm:min-h-9 sm:w-auto"
+ onClick={onSync}
+ disabled={isSyncing}
>
- {isSyncing ? 'Syncing...' : 'Sync findings'}
+ {isSyncing ? 'Syncing findings...' : 'Sync findings'}
+
+ );
+}
- {slaEnabled && (
-
- )}
+function DashboardError({ onRetry }: { onRetry: () => void }) {
+ return (
+
+
+ Dashboard data could not load
+
+
+ Security Agent could not load current finding statistics. Check your connection and try
+ again.
+
+
+
+ Retry dashboard
+
+
+
+ );
+}
+
+function DashboardSkeleton() {
+ return (
+
+
+ {[0, 1, 2, 3].map(index => (
+
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+ );
+}
+
+function buildDashboardMetrics(data: DashboardStats, slaEnabled: boolean): DashboardMetric[] {
+ if (!slaEnabled) {
+ return [
+ {
+ label: 'Open findings',
+ value: String(data.analysis.total),
+ detail: `${data.severity.critical} critical, ${data.severity.high} high`,
+ icon: ShieldAlert,
+ tone: 'danger',
+ },
+ {
+ label: 'Confirmed exploitable',
+ value: String(data.analysis.exploitable),
+ detail: 'Project risk confirmed by analysis',
+ icon: TriangleAlert,
+ tone: 'danger',
+ },
+ {
+ label: 'Needs your review',
+ value: String(data.analysis.needsReview),
+ detail: 'Human decision required',
+ icon: CircleHelp,
+ tone: 'warning',
+ },
+ {
+ label: 'Analysis not complete',
+ value: String(getAnalysisIncompleteCount(data.analysis)),
+ detail: 'Project risk still unknown',
+ icon: FileSearch,
+ tone: 'neutral',
+ },
+ ];
+ }
+
+ const compliance =
+ data.sla.overall.total > 0
+ ? Math.round((data.sla.overall.withinSla / data.sla.overall.total) * 100)
+ : 100;
+
+ return [
+ {
+ label: 'SLA compliance',
+ value: `${compliance}%`,
+ detail:
+ data.sla.overall.total > 0
+ ? `${data.sla.overall.withinSla} of ${data.sla.overall.total} within deadline`
+ : 'No assigned deadlines',
+ icon: ShieldCheck,
+ tone: compliance < 70 ? 'danger' : compliance < 90 ? 'warning' : 'neutral',
+ },
+ {
+ label: 'Deadline passed',
+ value: String(data.sla.overall.overdue),
+ detail: `${data.sla.bySeverity.critical.overdue} critical, ${data.sla.bySeverity.high.overdue} high`,
+ icon: AlertCircle,
+ tone: data.sla.overall.overdue > 0 ? 'danger' : 'neutral',
+ },
+ {
+ label: 'Due this week',
+ value: String(data.sla.dueSoon.total),
+ detail: `${data.sla.dueSoon.exploitable} confirmed exploitable`,
+ icon: CalendarClock,
+ tone: data.sla.dueSoon.total > 0 ? 'warning' : 'neutral',
+ },
+ {
+ label: 'No deadline',
+ value: String(data.sla.untrackedCount),
+ detail: data.sla.untrackedCount > 0 ? 'Review SLA assignment' : 'All open findings tracked',
+ icon: CircleHelp,
+ tone: 'neutral',
+ },
+ ];
+}
+
+function DashboardMetricCard({ label, value, detail, icon: Icon, tone }: DashboardMetric) {
+ const toneClass = {
+ danger: 'text-status-destructive',
+ warning: 'text-status-warning',
+ neutral: 'text-muted-foreground',
+ }[tone];
+
+ return (
+
+
+
+ {label}
+
+
+ {value}
+
+
{detail}
+
+ );
+}
+
+function AttentionCard({
+ data,
+ basePath,
+ repoFullName,
+ slaEnabled,
+}: {
+ data: DashboardStats;
+ basePath: string;
+ repoFullName: string | undefined;
+ slaEnabled: boolean;
+}) {
+ const finding = data.priorityFinding;
+ const needsAction = getNeedsActionCount(data.analysis);
+ const closed = data.status.fixed + data.status.ignored;
+ const openFindingsHref = findingsHref(basePath, { status: 'open', repoFullName });
+
+ if (!finding) {
+ return (
+
+
+
+
+
+ No open findings need attention
+
+
+ Security Agent will rank new work here after the next sync.
+
+
+ View all findings
+
+
+
+
+ );
+ }
+
+ const severity = titleCaseSeverity(finding.severity);
+ const isOverdue = slaEnabled && finding.daysOverdue !== null;
+ let heading = `${severity} finding needs attention`;
+ let description = 'Review the current evidence and choose the next action.';
+
+ if (isOverdue) {
+ heading = `${severity} finding is overdue`;
+ description =
+ finding.analysisStatus === null || finding.analysisStatus === 'failed'
+ ? 'Its deadline passed before Security Agent confirmed project risk. Review it now and start analysis if needed.'
+ : 'Its resolution deadline has passed. Review current analysis and remediation options.';
+ } else if (finding.analysisStatus === null || finding.analysisStatus === 'failed') {
+ heading = `${severity} finding needs analysis`;
+ description =
+ 'Advisory severity is known, but project risk is not. Review code usage before choosing remediation or dismissal.';
+ } else if (finding.analysisStatus === 'pending' || finding.analysisStatus === 'running') {
+ heading = `${severity} finding analysis is in progress`;
+ description = 'Open the finding to review current analysis status and source details.';
+ } else if (finding.isExploitable === true) {
+ heading = `${severity} exploitable finding needs review`;
+ description =
+ 'Codebase analysis confirmed project risk. Review evidence and available remediation actions.';
+ } else if (finding.suggestedAction === 'manual_review') {
+ heading = `${severity} finding needs your review`;
+ description =
+ 'Security Agent found evidence that requires a human decision before the finding can proceed.';
+ }
+
+ return (
+
+
+
+
+
+ Act first
+
+
+ {heading}
+
+
{description}
+
+
{finding.title}
+
+ {finding.repoFullName}
+ {isOverdue &&
+ ` · ${finding.daysOverdue === 0 ? 'Deadline reached today' : `${finding.daysOverdue} days overdue`}`}
+
+
+
+
+
+ Review finding
+
+
+
+
+ View all open findings
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+function GuidanceFact({
+ icon: Icon,
+ label,
+ value,
+ detail,
+ tone,
+}: {
+ icon: LucideIcon;
+ label: string;
+ value: string;
+ detail: string;
+ tone: 'danger' | 'warning' | 'success';
+}) {
+ const iconClass = {
+ danger: 'text-status-destructive-icon',
+ warning: 'text-status-warning-icon',
+ success: 'text-status-success-icon',
+ }[tone];
+
+ return (
+
+
+
+ {label}
+
+
{value}
+
{detail}
+
+ );
+}
+
+function DeadlinePostureCard({ data, basePath }: { data: DashboardStats; basePath: string }) {
+ const total = data.sla.overall.total;
+ const within = data.sla.overall.withinSla;
+ const compliance = total > 0 ? Math.round((within / total) * 100) : 100;
+ const postureTone = compliance < 70 ? 'danger' : compliance < 90 ? 'warning' : 'success';
+
+ return (
+
+
+
+
+ {within} / {total}
+
+
within resolution deadline
+
+
+ {compliance}% · {postureLabel(compliance)}
+
+
+
+
+ {(['critical', 'high', 'medium', 'low'] as const).map(severity => {
+ const sla = data.sla.bySeverity[severity];
+ const severityCompliance =
+ sla.total > 0 ? Math.round((sla.withinSla / sla.total) * 100) : 100;
+ const tone =
+ sla.overdue === 0 ? 'success' : severityCompliance < 70 ? 'danger' : 'warning';
+ return (
+
+ );
+ })}
+
+
+
+
+
+ Review SLA rules
+
+
+
+
+ );
+}
+
+function ActionPostureCard({
+ data,
+ basePath,
+ repoFullName,
+}: {
+ data: DashboardStats;
+ basePath: string;
+ repoFullName: string | undefined;
+}) {
+ const analysisIncomplete = getAnalysisIncompleteCount(data.analysis);
+ const decisionsPending = data.analysis.needsReview;
+ const total = data.analysis.total;
+ const decisionPercentage = total > 0 ? Math.round((decisionsPending / total) * 100) : 0;
+ const noImmediateAction = Math.max(
+ 0,
+ total - data.analysis.exploitable - decisionsPending - analysisIncomplete
+ );
- {/* Severity Breakdown + Status Overview */}
-
-
+
+
+ Configure SLA
+
+
+ }
+ >
+
+
+
+ {decisionsPending} / {total}
+
+
need a team decision
+
+
+ {decisionsPending} decisions pending
+
+
+
+
+
+
+
-
+
+
+
+ Review open findings
+
+
+
+
+ );
+}
+
+function DashboardSection({
+ title,
+ description,
+ action,
+ children,
+}: {
+ title: string;
+ description: string;
+ action?: ReactNode;
+ children: ReactNode;
+}) {
+ const headingId = `dashboard-${title.toLowerCase().replaceAll(' ', '-')}`;
+ return (
+
+
+
+
+ {title}
+
+
{description}
+
+ {action}
+ {children}
+
+ );
+}
- {/* Analysis Coverage + MTTR */}
-
-
- {slaEnabled &&
}
+function PostureLine({ label, value, detail }: { label: string; value: number; detail: string }) {
+ return (
+
+
+ {label}
+ {detail}
+
+
{value}
+
+ );
+}
+
+function DeadlineBar({
+ label,
+ within,
+ total,
+ overdue,
+ tone,
+}: {
+ label: string;
+ within: number;
+ total: number;
+ overdue: number;
+ tone: 'danger' | 'warning' | 'success';
+}) {
+ const percentage = total > 0 ? Math.round((within / total) * 100) : 100;
+
+ return (
+
+
+ {label}
+
+ {overdue} overdue · {within} of {total} within
+
+
+
+ );
+}
- {slaEnabled && (
-
- )}
+function UnderstandingSummary({
+ data,
+ basePath,
+ repoFullName,
+ slaEnabled,
+}: {
+ data: DashboardStats;
+ basePath: string;
+ repoFullName: string | undefined;
+ slaEnabled: boolean;
+}) {
+ const progress =
+ data.analysis.total > 0
+ ? Math.round((data.analysis.analyzed / data.analysis.total) * 100)
+ : 100;
+ const analysisIncomplete = getAnalysisIncompleteCount(data.analysis);
- {/* Repository Health */}
-
+
+
+
{progress}%
+
+ {data.analysis.analyzed} of {data.analysis.total} open findings analyzed
+
+
+
+
+
+
+
+
+
+
+ {slaEnabled && (
+
+ )}
+
+
+ Severity comes from the advisory. Exploitability reflects how this repository uses the
+ affected package.
+
+
+ );
+}
+
+function SummaryLine({
+ label,
+ value,
+ tone,
+ href,
+}: {
+ label: string;
+ value: number;
+ tone: SummaryTone;
+ href: string;
+}) {
+ const dotClass = {
+ danger: 'bg-status-destructive-icon',
+ warning: 'bg-status-warning-icon',
+ success: 'bg-status-success-icon',
+ neutral: 'bg-status-neutral-icon',
+ }[tone];
+
+ return (
+
+
+
+
+ {label}
+
+
+ {value}
);
}
+
+function RepositoryActionPlan({
+ repositories,
+ basePath,
+ slaEnabled,
+}: {
+ repositories: DashboardStats['repoHealth'];
+ basePath: string;
+ slaEnabled: boolean;
+}) {
+ return (
+
+
+
+ Repository action plan
+
+
+ {slaEnabled
+ ? 'Ranked by critical severity, missed deadlines, then findings requiring action.'
+ : 'Ranked by critical severity, confirmed project risk, then findings requiring action.'}
+
+
+ {repositories.length > 0 ? (
+
+ {repositories.map((repository, index) => (
+
+ ))}
+
+ ) : (
+
+
+
No repositories have open findings
+
+ Repository priorities will appear after findings are synced.
+
+
+ )}
+
+ );
+}
+
+function RepositoryActionRow({
+ repository,
+ rank,
+ basePath,
+ slaEnabled,
+}: {
+ repository: DashboardStats['repoHealth'][number];
+ rank: number;
+ basePath: string;
+ slaEnabled: boolean;
+}) {
+ const needsActionPercentage =
+ repository.open > 0 ? Math.round((repository.needsAction / repository.open) * 100) : 0;
+ const complianceTone = repository.slaCompliancePercent < 70 ? 'danger' : 'success';
+ const actionTone = repository.needsAction > 0 ? 'warning' : 'success';
+
+ return (
+
+
+ {String(rank).padStart(2, '0')}
+
+
+
{repository.repoFullName}
+
+ {repository.open} open · {repository.critical} critical · {repository.high} high
+
+
+
+
+
+ {slaEnabled ? 'SLA compliance' : 'Needs action'}
+
+
+ {slaEnabled
+ ? `${repository.slaCompliancePercent}%`
+ : `${repository.needsAction} findings`}
+
+
+
+
+ {slaEnabled
+ ? repository.overdue > 0
+ ? `${repository.overdue} overdue · ${repository.needsAction} need action`
+ : 'No overdue findings'
+ : repository.needsAction > 0
+ ? `${repository.exploitable} exploitable · ${repository.needsAction} need action`
+ : 'No findings need action'}
+
+
+
+
+ Review
+
+
+
+
+ );
+}
+
+function postureLabel(compliance: number): string {
+ if (compliance < 70) return 'At risk';
+ if (compliance < 90) return 'Needs attention';
+ return 'On track';
+}
+
+function toneTextClass(tone: 'danger' | 'warning' | 'success'): string {
+ return {
+ danger: 'text-status-destructive',
+ warning: 'text-status-warning',
+ success: 'text-status-success',
+ }[tone];
+}
+
+function toneIndicatorClass(tone: 'danger' | 'warning' | 'success'): string {
+ return {
+ danger: 'bg-status-destructive-icon',
+ warning: 'bg-status-warning-icon',
+ success: 'bg-status-success-icon',
+ }[tone];
+}
diff --git a/apps/web/src/components/security-agent/SecurityFindingRow.test.ts b/apps/web/src/components/security-agent/SecurityFindingRow.test.ts
new file mode 100644
index 0000000000..c2da7f0b15
--- /dev/null
+++ b/apps/web/src/components/security-agent/SecurityFindingRow.test.ts
@@ -0,0 +1,102 @@
+import { describe, expect, it } from '@jest/globals';
+import type {
+ SecurityFindingWithRemediation,
+ SecurityRemediationCapability,
+} from '@/lib/security-agent/db/security-remediation';
+import {
+ getAnalysisPresentation,
+ getDeadlinePresentation,
+} from './security-finding-list-presentation';
+
+const baseRemediationCapability: SecurityRemediationCapability = {
+ canStart: false,
+ startReason: 'analysis_required',
+ canRetry: false,
+ retryReason: 'retry_not_allowed',
+ canCancel: false,
+ cancelAttemptId: null,
+};
+
+const baseFinding = {
+ id: 'finding-1',
+ status: 'open',
+ severity: 'high',
+ title: 'Command Injection in lodash',
+ analysis_status: null,
+ analysis: null,
+ analysis_error: null,
+ fixed_at: null,
+ ignored_reason: null,
+ sla_due_at: null,
+ updated_at: '2026-06-17T12:00:00.000Z',
+ remediationSummary: null,
+ remediationCapability: baseRemediationCapability,
+} as SecurityFindingWithRemediation;
+
+function findingWith(
+ overrides: Partial
+): SecurityFindingWithRemediation {
+ return { ...baseFinding, ...overrides };
+}
+
+describe('Security Finding list presentation', () => {
+ it('preserves analysis outcome for a closed finding', () => {
+ const finding = findingWith({
+ status: 'fixed',
+ analysis_status: 'completed',
+ analysis: {
+ sandboxAnalysis: {
+ isExploitable: true,
+ summary: 'Reachable vulnerable path found.',
+ },
+ } as SecurityFindingWithRemediation['analysis'],
+ });
+
+ expect(getAnalysisPresentation(finding)).toMatchObject({
+ label: 'Exploitable',
+ tone: 'destructive',
+ });
+ });
+
+ it('formats open SLA deadlines with urgency labels', () => {
+ const now = new Date('2026-06-17T12:00:00.000Z');
+
+ expect(
+ getDeadlinePresentation(findingWith({ sla_due_at: '2026-06-16T12:00:00.000Z' }), now)
+ ).toMatchObject({
+ label: '1 day overdue',
+ detail: 'Due Jun 16, 2026',
+ tone: 'destructive',
+ });
+ expect(
+ getDeadlinePresentation(findingWith({ sla_due_at: '2026-06-17T10:00:00.000Z' }), now)
+ ).toMatchObject({
+ label: 'Overdue',
+ detail: 'Due Jun 17, 2026',
+ tone: 'destructive',
+ });
+ expect(
+ getDeadlinePresentation(findingWith({ sla_due_at: '2026-06-18T12:00:00.000Z' }), now)
+ ).toMatchObject({
+ label: 'Due tomorrow',
+ detail: 'Due Jun 18, 2026',
+ tone: 'warning',
+ });
+ });
+
+ it('shows whether a fixed finding met its recorded deadline', () => {
+ const presentation = getDeadlinePresentation(
+ findingWith({
+ status: 'fixed',
+ sla_due_at: '2026-06-18T12:00:00.000Z',
+ fixed_at: '2026-06-17T12:00:00.000Z',
+ })
+ );
+
+ expect(presentation).toMatchObject({
+ label: 'Fixed before deadline',
+ detail: 'Fixed Jun 17, 2026',
+ tone: 'success',
+ });
+ });
+});
diff --git a/apps/web/src/components/security-agent/SecurityFindingRow.tsx b/apps/web/src/components/security-agent/SecurityFindingRow.tsx
index 23576be626..d854d1e6e8 100644
--- a/apps/web/src/components/security-agent/SecurityFindingRow.tsx
+++ b/apps/web/src/components/security-agent/SecurityFindingRow.tsx
@@ -1,182 +1,34 @@
'use client';
-import { differenceInDays, differenceInHours, differenceInMinutes, isPast } from 'date-fns';
import {
Brain,
- CheckCircle2,
ChevronRight,
Eye,
ExternalLink,
GitPullRequest,
Loader2,
- Package,
RotateCw,
- Shield,
- ShieldAlert,
- ShieldCheck,
- ShieldX,
XCircle,
} from 'lucide-react';
import { Button } from '@/components/ui/button';
-import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
-import type { SecurityFinding } from '@kilocode/db/schema';
+import type { SecurityFindingWithRemediation } from '@/lib/security-agent/db/security-remediation';
import { cn } from '@/lib/utils';
import { SeverityBadge } from './SeverityBadge';
-import { securityAgentCommandAdmissionCopy } from './security-agent-command-copy';
-
-type Outcome = {
- icon: typeof CheckCircle2;
- label: string;
- className: string;
- spin: boolean;
- tooltip: string | null;
-};
-
-function getOutcome(finding: SecurityFinding): Outcome | null {
- if (finding.status === 'fixed') {
- return {
- icon: CheckCircle2,
- label: 'Fixed',
- className: 'text-green-400',
- spin: false,
- tooltip: finding.fixed_at
- ? `Fixed ${formatCompactDistance(new Date(finding.fixed_at))} ago`
- : null,
- };
- }
- if (finding.status === 'ignored') {
- return {
- icon: XCircle,
- label: 'Dismissed',
- className: 'text-muted-foreground',
- spin: false,
- tooltip: finding.ignored_reason?.replace(/_/g, ' ') ?? null,
- };
- }
- if (finding.analysis_status === 'pending' || finding.analysis_status === 'running') {
- return {
- icon: Loader2,
- label: 'Analyzing',
- className: 'text-yellow-400',
- spin: true,
- tooltip: finding.analysis_status === 'pending' ? 'Analysis is queued' : 'Analysis is running',
- };
- }
- if (finding.analysis_status === 'failed') {
- return {
- icon: XCircle,
- label: 'Analysis failed',
- className: 'text-red-400',
- spin: false,
- tooltip: finding.analysis_error || 'Analysis failed. Retry to run it again.',
- };
- }
- if (finding.analysis_status !== 'completed') return null;
-
- const sandbox = finding.analysis?.sandboxAnalysis;
- const triage = finding.analysis?.triage;
- if (sandbox?.isExploitable === true) {
- return {
- icon: ShieldAlert,
- label: 'Exploitable',
- className: 'text-red-400',
- spin: false,
- tooltip: sandbox.summary || 'Codebase analysis confirmed this vulnerability is exploitable',
- };
- }
- if (sandbox?.isExploitable === false) {
- return {
- icon: ShieldCheck,
- label: 'Not exploitable',
- className: 'text-green-400',
- spin: false,
- tooltip: sandbox.summary || 'Codebase analysis determined this is not exploitable',
- };
- }
- if (triage?.suggestedAction === 'dismiss') {
- return {
- icon: ShieldX,
- label: 'Safe to dismiss',
- className: 'text-green-400',
- spin: false,
- tooltip: triage.needsSandboxReasoning || 'Triage determined this can be safely dismissed',
- };
- }
- if (triage?.suggestedAction === 'manual_review') {
- return {
- icon: Eye,
- label: 'Needs review',
- className: 'text-yellow-400',
- spin: false,
- tooltip: triage.needsSandboxReasoning || 'Triage flagged this for manual review',
- };
- }
- return {
- icon: Shield,
- label: triage ? 'Triage complete' : 'Analyzed',
- className: 'text-muted-foreground',
- spin: false,
- tooltip: triage?.needsSandboxReasoning || null,
- };
-}
-
-function OutcomeLabel({ outcome }: { outcome: Outcome }) {
- const content = (
-
-
- {outcome.label}
-
- );
- if (!outcome.tooltip) return content;
- return (
-
- {content}
-
- {outcome.tooltip}
-
-
- );
-}
+import {
+ isAwaitingManualAnalysisAdmission,
+ manualAnalysisAdmissionCopy,
+ manualAnalysisCapacityFullCopy,
+} from './manual-analysis-admission-copy';
+import {
+ getAnalysisPresentation,
+ getDeadlinePresentation,
+ getFindingListGridClass,
+ type FindingStatusPresentation,
+ type FindingTone,
+} from './security-finding-list-presentation';
type Severity = 'critical' | 'high' | 'medium' | 'low';
-function isSeverity(value: string): value is Severity {
- return ['critical', 'high', 'medium', 'low'].includes(value);
-}
-
-export type RemediationSummary = {
- status: string;
- latestAttemptId: string | null;
- prUrl: string | null;
- prNumber: number | null;
- prDraft: boolean | null;
- prHeadBranch: string | null;
- prBaseBranch: string | null;
- outcomeSummary: string | null;
- failureCode: string | null;
- blockedReason: string | null;
- completedAt: string | null;
- updatedAt: string;
- latestAttempt?: { id: string; status: string } | null;
-};
-
-export type RemediationCapability = {
- canStart: boolean;
- startReason: string;
- canRetry: boolean;
- retryReason: string;
- canCancel: boolean;
- cancelAttemptId: string | null;
-};
-
-export type SecurityFindingWithRemediation = SecurityFinding & {
- remediationSummary?: RemediationSummary | null;
- remediationCapability?: RemediationCapability;
-};
-
type SecurityFindingRowProps = {
finding: SecurityFindingWithRemediation;
onClick: () => void;
@@ -185,6 +37,7 @@ type SecurityFindingRowProps = {
options?: { forceSandbox?: boolean; retrySandboxOnly?: boolean }
) => void;
isStartingAnalysis?: boolean;
+ analysisAtCapacity?: boolean;
onStartRemediation?: (findingId: string) => void;
onRetryRemediation?: (findingId: string) => void;
onCancelRemediation?: (attemptId: string, findingId?: string) => void;
@@ -193,23 +46,64 @@ type SecurityFindingRowProps = {
slaDisplay?: 'visible' | 'hidden';
};
-function formatCompactDistance(date: Date) {
- const now = new Date();
- const days = Math.abs(differenceInDays(now, date));
- if (days >= 1) return `${days}d`;
- const hours = Math.abs(differenceInHours(now, date));
- if (hours >= 1) return `${hours}h`;
- return `${Math.abs(differenceInMinutes(now, date))}m`;
+const toneStyles: Record = {
+ success: {
+ status: 'border-status-success-border bg-status-success-surface text-status-success',
+ text: 'text-status-success',
+ },
+ warning: {
+ status: 'border-status-warning-border bg-status-warning-surface text-status-warning',
+ text: 'text-status-warning',
+ },
+ destructive: {
+ status:
+ 'border-status-destructive-border bg-status-destructive-surface text-status-destructive',
+ text: 'text-status-destructive',
+ },
+ neutral: {
+ status: 'border-status-neutral-border bg-status-neutral-surface text-status-neutral',
+ text: 'text-status-neutral',
+ },
+};
+
+function isSeverity(value: string): value is Severity {
+ return ['critical', 'high', 'medium', 'low'].includes(value);
}
-function isActiveRemediationStatus(status: string | null | undefined) {
- return status === 'queued' || status === 'launching' || status === 'running';
+function FindingStatusCell({
+ label,
+ children,
+ className,
+}: {
+ label: string;
+ children: React.ReactNode;
+ className?: string;
+}) {
+ return (
+
+ );
}
-function formatRemediationStatus(status: string | null | undefined) {
- if (status === 'pr_opened') return 'PR opened';
- if (status === 'no_changes_needed') return 'No changes';
- return status?.replace(/_/g, ' ') ?? null;
+function StatusPill({ status }: { status: FindingStatusPresentation }) {
+ const Icon = status.icon;
+ return (
+
+
+ {status.label}
+
+ );
}
export function SecurityFindingRow({
@@ -217,6 +111,7 @@ export function SecurityFindingRow({
onClick,
onStartAnalysis,
isStartingAnalysis,
+ analysisAtCapacity = false,
onStartRemediation,
onRetryRemediation,
onCancelRemediation,
@@ -225,171 +120,171 @@ export function SecurityFindingRow({
slaDisplay = 'visible',
}: SecurityFindingRowProps) {
const severity: Severity = isSeverity(finding.severity) ? finding.severity : 'medium';
- const canStartAnalysis =
+ const showAnalysisAction =
finding.status === 'open' &&
(!finding.analysis_status || finding.analysis_status === 'failed') &&
Boolean(onStartAnalysis) &&
!isStartingAnalysis;
- const outcome = getOutcome(finding);
+ const isAwaitingAnalysisAdmission = isAwaitingManualAnalysisAdmission(
+ Boolean(isStartingAnalysis),
+ finding.analysis_status
+ );
+ const analysis = getAnalysisPresentation(finding);
+ const deadline = getDeadlinePresentation(finding);
const remediation = finding.remediationSummary;
const capability = finding.remediationCapability;
const remediationStatus = remediation?.status ?? null;
- const remediationAttemptId = capability?.cancelAttemptId ?? remediation?.latestAttemptId;
- const remediationIsActive = isActiveRemediationStatus(remediationStatus) || isStartingRemediation;
- const remediationStatusLabel = formatRemediationStatus(remediationStatus);
+ const remediationAttemptId = capability.cancelAttemptId ?? remediation?.latestAttemptId;
const openRemediationPrUrl =
remediationStatus === 'pr_opened' && remediation?.prUrl ? remediation.prUrl : null;
- const isHighlighted =
- finding.status === 'open' &&
- slaDisplay === 'visible' &&
- finding.sla_due_at !== null &&
- isPast(new Date(finding.sla_due_at));
+ const showSla = slaDisplay === 'visible';
const startAnalysis = () => {
const retrySandboxOnly =
Boolean(finding.analysis?.triage) && finding.analysis_status === 'failed';
onStartAnalysis?.(finding.id, { retrySandboxOnly });
};
- const startRemediation = () => onStartRemediation?.(finding.id);
- const retryRemediation = () => onRetryRemediation?.(finding.id);
const cancelRemediation = () => {
if (remediationAttemptId) onCancelRemediation?.(remediationAttemptId, finding.id);
};
return (
-
-
-
- {finding.title}
-
-
- {finding.package_name}
-
-
-
-
- {outcome ? (
-
- ) : (
-
-
- Not analyzed
-
- )}
- {remediationStatusLabel && (
-
- {remediationIsActive ? (
-
- ) : (
-
- )}
- {remediationStatusLabel}
-
- )}
-
-
+ />
+
+
+
+
+
+
+
{finding.title}
+
+
-
+
+
+
+
+ {showSla && (
+
+
+
+
+
{deadline.label}
+
{deadline.detail}
+
+
+
+ )}
+
+
-
+
);
}
diff --git a/apps/web/src/components/security-agent/SecurityFindingsCard.tsx b/apps/web/src/components/security-agent/SecurityFindingsCard.tsx
index a0e46162d8..ee53b73c87 100644
--- a/apps/web/src/components/security-agent/SecurityFindingsCard.tsx
+++ b/apps/web/src/components/security-agent/SecurityFindingsCard.tsx
@@ -15,6 +15,7 @@ import {
RefreshCw,
Settings2,
Shield,
+ ShieldCheck,
} from 'lucide-react';
import Link from 'next/link';
import { Badge } from '@/components/ui/badge';
@@ -26,9 +27,13 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
-import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
+import { Skeleton } from '@/components/ui/skeleton';
+import type { SecurityFindingWithRemediation } from '@/lib/security-agent/db/security-remediation';
+import { cn } from '@/lib/utils';
import { RepositoryFilter } from './RepositoryFilter';
-import { SecurityFindingRow, type SecurityFindingWithRemediation } from './SecurityFindingRow';
+import { SecurityAgentActionBar, SecurityAgentActionBarField } from './SecurityAgentActionBar';
+import { SecurityFindingRow } from './SecurityFindingRow';
+import { getFindingListGridClass } from './security-finding-list-presentation';
type Repository = {
id: number;
@@ -110,6 +115,8 @@ const STATUS_IMPLYING_OUTCOMES = new Set([
'dismissed',
]);
+const skeletonRowKeys = ['skeleton-1', 'skeleton-2', 'skeleton-3', 'skeleton-4', 'skeleton-5'];
+
export function SecurityFindingsCard({
findings,
repositories,
@@ -143,6 +150,11 @@ export function SecurityFindingsCard({
const startItem = (page - 1) * pageSize + 1;
const endItem = Math.min(page * pageSize, totalCount);
const closedCount = stats.fixed + stats.ignored;
+ const analysisAtCapacity = runningCount >= concurrencyLimit;
+ const hasActiveFilters = Boolean(
+ filters.severity || filters.repoFullName || filters.outcomeFilter || filters.overdue
+ );
+ const listGridClass = getFindingListGridClass(showSla);
const handleStatusChange = (value: string) => {
const status = value === 'all' ? undefined : value;
@@ -189,175 +201,208 @@ export function SecurityFindingsCard({
return (
-
-
-
handleStatusChange(filters.status === 'open' ? 'all' : 'open')}
- aria-pressed={filters.status === 'open'}
- className="focus-visible:ring-ring flex items-center gap-2 rounded-md text-sm transition-colors focus-visible:ring-2 focus-visible:outline-none"
- >
-
-
- {stats.open} open
-
-
-
handleStatusChange(filters.status === 'closed' ? 'all' : 'closed')}
- aria-pressed={filters.status === 'closed'}
- className="focus-visible:ring-ring flex items-center gap-2 rounded-md text-sm transition-colors focus-visible:ring-2 focus-visible:outline-none"
- >
-
-
- {closedCount} closed
-
-
-
-
-
-
-
- = concurrencyLimit ? 'destructive' : 'secondary'}>
-
- {runningCount}/{concurrencyLimit}
- {' '}
- capacity
-
-
-
- {runningCount} of {concurrencyLimit} concurrent analyses running. New requests are
- rejected at capacity.
-
-
- {state.isEnabled ? (
- <>
- {lastSyncTime && (
-
-
- Last synced {formatDistanceToNow(new Date(lastSyncTime), { addSuffix: true })}
+
+
+
+ Finding state
+
+
handleStatusChange(filters.status === 'open' ? 'all' : 'open')}
+ aria-pressed={filters.status === 'open'}
+ className={cn(
+ 'focus-visible:ring-ring flex min-h-9 flex-1 items-center justify-center gap-2 rounded-md px-3 text-sm transition-colors focus-visible:ring-2 focus-visible:outline-none sm:flex-none',
+ filters.status === 'open'
+ ? 'bg-surface-selected text-foreground'
+ : 'text-muted-foreground hover:bg-surface-hover hover:text-foreground'
+ )}
+ >
+
+
+ {stats.open} open
- )}
+
+
handleStatusChange(filters.status === 'closed' ? 'all' : 'closed')}
+ aria-pressed={filters.status === 'closed'}
+ className={cn(
+ 'focus-visible:ring-ring flex min-h-9 flex-1 items-center justify-center gap-2 rounded-md px-3 text-sm transition-colors focus-visible:ring-2 focus-visible:outline-none sm:flex-none',
+ filters.status === 'closed'
+ ? 'bg-surface-selected text-foreground'
+ : 'text-muted-foreground hover:bg-surface-hover hover:text-foreground'
+ )}
+ >
+
+
+ {closedCount} closed
+
+
+
+
+
+
+
+
+ {runningCount}/{concurrencyLimit}
+ {' '}
+ analysis capacity
+
+ {state.isEnabled ? (
+ <>
+ {lastSyncTime && (
+
+
+ Last synced {formatDistanceToNow(new Date(lastSyncTime), { addSuffix: true })}
+
+ )}
+ onSync()}
+ disabled={state.isSyncing}
+ >
+ {state.isSyncing ? (
+
+ ) : (
+
+ )}
+ {state.isSyncing ? 'Syncing findings...' : 'Sync findings'}
+
+ >
+ ) : (
onSync()}
- disabled={state.isSyncing}
+ className="min-h-11 w-full sm:min-h-9 sm:w-auto"
+ onClick={onEnableClick}
>
- {state.isSyncing ? (
-
- ) : (
-
- )}
- {state.isSyncing ? 'Syncing...' : 'Sync findings'}
+
+ Enable Security Agent
- >
- ) : (
-
-
- Enable Security Agent
-
- )}
+ )}
+
-
-
-
onFiltersChange({ ...filters, repoFullName })}
- isLoading={state.isLoading}
- />
+
+
+ onFiltersChange({ ...filters, repoFullName })}
+ isLoading={state.isLoading}
+ />
+
-
- onFiltersChange({ ...filters, severity: severity === 'all' ? undefined : severity })
- }
- >
-
-
-
-
- All severities
- Critical
- High
- Medium
- Low
-
-
+
+
+ onFiltersChange({ ...filters, severity: severity === 'all' ? undefined : severity })
+ }
+ >
+
+
+
+
+ All severities
+ Critical
+ High
+ Medium
+ Low
+
+
+
-
-
-
-
-
- All outcomes
- Not analyzed
- Analysis failed
- Exploitable
- Not exploitable
- Safe to dismiss
- Needs review
- Triage complete
- Fixed
- Dismissed
-
-
+
+
+
+
+
+
+ All outcomes
+ Not analyzed
+ Analysis failed
+ Exploitable
+ Not exploitable
+ Safe to dismiss
+ Needs review
+ Triage complete
+ Fixed
+ Dismissed
+
+
+
-
-
-
-
-
-
-
-
- Severity
-
-
-
-
- Severity
-
-
- {showSla && SLA due date }
-
-
-
+
+
+
+
+
+
+
+
+
+ Severity
+
+
+
+
+ Severity
+
+
+ {showSla && SLA due date }
+
+
+
+
+
+
+
+
+ Security Finding
+ Analysis
+ {showSla && SLA Deadline }
+ Action
+
+
-
{state.isLoading ? (
-
-
- Loading findings...
-
+
) : findings.length === 0 ? (
-
-
-
No findings match current filters.
- {(filters.status ||
- filters.severity ||
- filters.repoFullName ||
- filters.outcomeFilter) && (
-
onFiltersChange({})} className="mt-2">
- Clear filters
-
- )}
-
+
onFiltersChange(filters.status ? { status: filters.status } : {})}
+ />
) : (
-
+
{findings.map(finding => (
onFindingClick(finding)}
onStartAnalysis={onStartAnalysis}
isStartingAnalysis={startingAnalysisIds?.has(finding.id)}
+ analysisAtCapacity={analysisAtCapacity}
onStartRemediation={onStartRemediation}
onRetryRemediation={onRetryRemediation}
onCancelRemediation={onCancelRemediation}
@@ -376,36 +422,36 @@ export function SecurityFindingsCard({
slaDisplay={showSla ? 'visible' : 'hidden'}
/>
))}
-
+
)}
-
+
- {totalCount > 0 && (
-
-
+ {totalCount > 0 && !state.isLoading && (
+
+
Showing {startItem}-{endItem} of {totalCount}
-
+
onPageChange(page - 1)}
disabled={page <= 1}
>
-
+
Previous
-
+
Page {page} of {totalPages}
onPageChange(page + 1)}
disabled={page >= totalPages}
>
Next
-
+
@@ -413,3 +459,77 @@ export function SecurityFindingsCard({
);
}
+
+function FindingsSkeleton({ showSla }: { showSla: boolean }) {
+ return (
+
+
Loading Security Findings
+ {skeletonRowKeys.map(rowKey => (
+
+
+
+
+
+
+ {showSla && (
+
+
+
+
+ )}
+
+
+
+
+
+ ))}
+
+ );
+}
+
+function FindingsEmptyState({
+ status,
+ hasActiveFilters,
+ onClearFilters,
+}: {
+ status?: string;
+ hasActiveFilters: boolean;
+ onClearFilters: () => void;
+}) {
+ const Icon = hasActiveFilters ? AlertTriangle : ShieldCheck;
+ const title = hasActiveFilters
+ ? 'No findings match these filters'
+ : status === 'open'
+ ? 'No open findings'
+ : status === 'closed'
+ ? 'No closed findings'
+ : 'No findings';
+ const description = hasActiveFilters
+ ? 'Change or clear filters to include more Security Findings.'
+ : status === 'open'
+ ? 'New Security Findings will appear here after the next successful sync.'
+ : status === 'closed'
+ ? 'Fixed and dismissed Security Findings will appear here.'
+ : 'Security Findings will appear here after the next successful sync.';
+
+ return (
+
+
+
+
+
{title}
+
{description}
+ {hasActiveFilters && (
+
+ Clear filters
+
+ )}
+
+ );
+}
diff --git a/apps/web/src/components/security-agent/SecurityFindingsPage.tsx b/apps/web/src/components/security-agent/SecurityFindingsPage.tsx
index 3b2b298b69..e7f736a79c 100644
--- a/apps/web/src/components/security-agent/SecurityFindingsPage.tsx
+++ b/apps/web/src/components/security-agent/SecurityFindingsPage.tsx
@@ -1,6 +1,6 @@
'use client';
-import { useReducer } from 'react';
+import { useEffect, useReducer, useRef } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import Link from 'next/link';
import { useQuery } from '@tanstack/react-query';
@@ -16,10 +16,11 @@ import {
import { DismissFindingDialog, type DismissReason } from './DismissFindingDialog';
import { FindingDetailDialog } from './FindingDetailDialog';
import { SecurityFindingsCard } from './SecurityFindingsCard';
-import type { SecurityFindingWithRemediation } from './SecurityFindingRow';
+import type { SecurityFindingWithRemediation } from '@/lib/security-agent/db/security-remediation';
import { useSecurityAgent } from './SecurityAgentContext';
+import { tryReserveManualAnalysisCapacity } from './manual-analysis-admission-copy';
-const PAGE_SIZE = 20;
+const PAGE_SIZE = 10;
const EMPTY_FINDINGS: SecurityFindingWithRemediation[] = [];
type Filters = {
@@ -47,6 +48,7 @@ type PageAction =
| { type: 'set-filters'; filters: Filters }
| { type: 'set-sort'; sortBy: SortBy }
| { type: 'open-detail'; finding: SecurityFindingWithRemediation }
+ | { type: 'open-deep-link' }
| { type: 'set-detail-open'; open: boolean }
| { type: 'open-dismiss'; finding: SecurityFindingWithRemediation }
| { type: 'set-dismiss-open'; open: boolean }
@@ -90,6 +92,13 @@ function pageReducer(state: PageState, action: PageAction): PageState {
return { ...state, sortBy: action.sortBy, page: 1 };
case 'open-detail':
return { ...state, selectedFinding: action.finding, detailDialogOpen: true };
+ case 'open-deep-link':
+ return {
+ ...state,
+ selectedFinding: null,
+ detailDialogOpen: false,
+ closedDeepLinkId: null,
+ };
case 'set-detail-open':
return {
...state,
@@ -129,6 +138,7 @@ export function SecurityFindingsPage() {
hasIntegration,
isEnabled,
filteredRepositories,
+ trackUiInteraction,
handleSync,
handleDismiss,
handleStartAnalysis,
@@ -146,6 +156,7 @@ export function SecurityFindingsPage() {
const router = useRouter();
const searchParams = useSearchParams();
const [state, dispatch] = useReducer(pageReducer, searchParams, createInitialPageState);
+ const localAnalysisReservationIdsRef = useRef
| null>(null);
const findingsEnabled = isEnabled === true;
const slaEnabled = configData?.slaEnabled ?? true;
const effectiveSortBy = slaEnabled ? state.sortBy : 'severity_desc';
@@ -232,6 +243,33 @@ export function SecurityFindingsPage() {
}
}
const runningCount = serverRunningCount + optimisticAdditional;
+ const concurrencyLimit = findingsData?.concurrencyLimit ?? 3;
+ const analysisAtCapacity = runningCount >= concurrencyLimit;
+
+ useEffect(() => {
+ const localAnalysisReservationIds = localAnalysisReservationIdsRef.current;
+ if (!localAnalysisReservationIds) return;
+ for (const findingId of startingAnalysisIds) {
+ localAnalysisReservationIds.delete(findingId);
+ }
+ }, [startingAnalysisIds]);
+
+ const handleStartAnalysisWithinCapacity = (
+ findingId: string,
+ options?: { forceSandbox?: boolean; retrySandboxOnly?: boolean }
+ ) => {
+ const localAnalysisReservationIds = localAnalysisReservationIdsRef.current ?? new Set();
+ localAnalysisReservationIdsRef.current = localAnalysisReservationIds;
+ const reserved = tryReserveManualAnalysisCapacity({
+ findingId,
+ runningCount,
+ concurrencyLimit,
+ startingAnalysisIds,
+ localReservationIds: localAnalysisReservationIds,
+ });
+ if (!reserved) return;
+ handleStartAnalysis(findingId, options);
+ };
const deepLinkIsOpen = Boolean(
deepLinkedFinding && !state.selectedFinding && state.closedDeepLinkId !== deepLinkedFinding.id
@@ -253,7 +291,23 @@ export function SecurityFindingsPage() {
handleDismiss(activeFinding, reason, comment, () => dispatch({ type: 'finish-dismiss' }));
};
+ const handleFiltersChange = (filters: Filters) => {
+ trackUiInteraction('findings_filtered');
+ dispatch({ type: 'set-filters', filters });
+ };
+
+ const handleSortByChange = (sortBy: SortBy) => {
+ trackUiInteraction('findings_filtered');
+ dispatch({ type: 'set-sort', sortBy });
+ };
+
const basePath = isOrg ? `/organizations/${organizationId}/security-agent` : '/security-agent';
+ const handleOpenFinding = (findingId: string) => {
+ const params = new URLSearchParams(searchParams.toString());
+ params.set('findingId', findingId);
+ dispatch({ type: 'open-deep-link' });
+ router.replace(`${basePath}/findings?${params.toString()}`);
+ };
const installUrl = isOrg
? `/organizations/${organizationId}/integrations`
: '/integrations/github';
@@ -324,11 +378,11 @@ export function SecurityFindingsPage() {
hasIntegration,
}}
filters={state.filters}
- onFiltersChange={filters => dispatch({ type: 'set-filters', filters })}
+ onFiltersChange={handleFiltersChange}
installUrl={installUrl}
onEnableClick={() => router.push(`${basePath}/config`)}
lastSyncTime={lastSyncData?.lastSyncTime}
- onStartAnalysis={handleStartAnalysis}
+ onStartAnalysis={handleStartAnalysisWithinCapacity}
startingAnalysisIds={startingAnalysisIds}
onStartRemediation={handleStartRemediation}
onRetryRemediation={handleRetryRemediation}
@@ -336,25 +390,31 @@ export function SecurityFindingsPage() {
startingRemediationIds={startingRemediationIds}
cancellingRemediationAttemptIds={cancellingRemediationAttemptIds}
sortBy={effectiveSortBy}
- onSortByChange={sortBy => dispatch({ type: 'set-sort', sortBy })}
+ onSortByChange={handleSortByChange}
showSla={slaEnabled}
runningCount={runningCount}
- concurrencyLimit={findingsData?.concurrencyLimit ?? 3}
+ concurrencyLimit={concurrencyLimit}
/>
{
- if (activeFinding) dispatch({ type: 'open-dismiss', finding: activeFinding });
+ onDismiss={analysis => {
+ if (activeFinding) {
+ dispatch({ type: 'open-dismiss', finding: { ...activeFinding, analysis } });
+ }
}}
canDismiss={activeFinding?.status === 'open'}
+ onOpenFinding={handleOpenFinding}
organizationId={organizationId}
showSla={slaEnabled}
/>
dispatch({ type: 'set-dismiss-open', open })}
diff --git a/apps/web/src/components/security-agent/dismiss-finding-form.ts b/apps/web/src/components/security-agent/dismiss-finding-form.ts
new file mode 100644
index 0000000000..88be07da11
--- /dev/null
+++ b/apps/web/src/components/security-agent/dismiss-finding-form.ts
@@ -0,0 +1,73 @@
+import type { SecurityFinding } from '@kilocode/db/schema';
+
+export const DISMISS_REASONS = [
+ {
+ value: 'fix_started',
+ label: 'Fix started',
+ description: 'A fix for this vulnerability has been started',
+ },
+ {
+ value: 'no_bandwidth',
+ label: 'No bandwidth',
+ description: 'No bandwidth to fix this vulnerability at this time',
+ },
+ {
+ value: 'tolerable_risk',
+ label: 'Tolerable risk',
+ description: 'The risk is tolerable for this project',
+ },
+ {
+ value: 'inaccurate',
+ label: 'Inaccurate',
+ description: 'This alert is inaccurate or incorrect',
+ },
+ {
+ value: 'not_used',
+ label: 'Not used',
+ description: 'This vulnerable code is not actually used',
+ },
+] as const;
+
+export type DismissReason = (typeof DISMISS_REASONS)[number]['value'];
+
+type DismissFindingFormDefaults = {
+ reason: DismissReason;
+ comment: string;
+};
+
+export const MAX_DISMISS_COMMENT_LENGTH = 280;
+
+const EMPTY_DISMISS_FORM: DismissFindingFormDefaults = {
+ reason: 'not_used',
+ comment: '',
+};
+
+function truncateDismissComment(comment: string): string {
+ if (comment.length <= MAX_DISMISS_COMMENT_LENGTH) return comment;
+ return `${comment.slice(0, MAX_DISMISS_COMMENT_LENGTH - 1)}…`;
+}
+
+export function getDismissFindingFormDefaults(
+ analysis: SecurityFinding['analysis'] | undefined
+): DismissFindingFormDefaults {
+ const sandbox = analysis?.sandboxAnalysis;
+ if (sandbox) {
+ if (sandbox.isExploitable === false) {
+ return {
+ reason: 'not_used',
+ comment: truncateDismissComment(sandbox.exploitabilityReasoning),
+ };
+ }
+ return EMPTY_DISMISS_FORM;
+ }
+
+ const triage = analysis?.triage;
+ if (triage?.needsSandboxAnalysis === false && triage.suggestedAction === 'dismiss') {
+ return {
+ reason: 'not_used',
+ comment: truncateDismissComment(triage.needsSandboxReasoning),
+ };
+ }
+
+ return EMPTY_DISMISS_FORM;
+}
diff --git a/apps/web/src/components/security-agent/manual-analysis-admission-copy.test.ts b/apps/web/src/components/security-agent/manual-analysis-admission-copy.test.ts
index 9e79e39247..1c5754491a 100644
--- a/apps/web/src/components/security-agent/manual-analysis-admission-copy.test.ts
+++ b/apps/web/src/components/security-agent/manual-analysis-admission-copy.test.ts
@@ -1,5 +1,9 @@
import { describe, expect, test } from '@jest/globals';
-import { manualAnalysisAdmissionCopy } from './manual-analysis-admission-copy';
+import {
+ isAwaitingManualAnalysisAdmission,
+ manualAnalysisAdmissionCopy,
+ tryReserveManualAnalysisCapacity,
+} from './manual-analysis-admission-copy';
describe('manualAnalysisAdmissionCopy', () => {
test('describes manual analysis as queued admission', () => {
@@ -7,4 +11,73 @@ describe('manualAnalysisAdmissionCopy', () => {
expect(manualAnalysisAdmissionCopy.failureTitle).toMatch(/failed to queue/i);
expect(manualAnalysisAdmissionCopy.pendingLabel).toMatch(/queue/i);
});
+
+ test('stops showing admission progress after analysis is persisted as active', () => {
+ expect(isAwaitingManualAnalysisAdmission(true, null)).toBe(true);
+ expect(isAwaitingManualAnalysisAdmission(true, 'failed')).toBe(true);
+ expect(isAwaitingManualAnalysisAdmission(true, 'completed')).toBe(true);
+ expect(isAwaitingManualAnalysisAdmission(true, 'pending')).toBe(false);
+ expect(isAwaitingManualAnalysisAdmission(true, 'running')).toBe(false);
+ expect(isAwaitingManualAnalysisAdmission(false, null)).toBe(false);
+ });
+
+ test('reserves only the available slots across rapid analysis requests', () => {
+ const localReservationIds = new Set();
+ const reserve = (findingId: string) =>
+ tryReserveManualAnalysisCapacity({
+ findingId,
+ runningCount: 1,
+ concurrencyLimit: 3,
+ startingAnalysisIds: new Set(),
+ localReservationIds,
+ });
+
+ expect(reserve('finding-1')).toBe(true);
+ expect(reserve('finding-2')).toBe(true);
+ expect(reserve('finding-3')).toBe(false);
+ expect(localReservationIds).toEqual(new Set(['finding-1', 'finding-2']));
+ });
+
+ test('does not reserve at full capacity or twice for the same finding', () => {
+ const localReservationIds = new Set();
+ const startingAnalysisIds = new Set(['starting-finding']);
+
+ expect(
+ tryReserveManualAnalysisCapacity({
+ findingId: 'full-capacity-finding',
+ runningCount: 3,
+ concurrencyLimit: 3,
+ startingAnalysisIds,
+ localReservationIds,
+ })
+ ).toBe(false);
+ expect(
+ tryReserveManualAnalysisCapacity({
+ findingId: 'starting-finding',
+ runningCount: 1,
+ concurrencyLimit: 3,
+ startingAnalysisIds,
+ localReservationIds,
+ })
+ ).toBe(false);
+ expect(
+ tryReserveManualAnalysisCapacity({
+ findingId: 'new-finding',
+ runningCount: 1,
+ concurrencyLimit: 3,
+ startingAnalysisIds,
+ localReservationIds,
+ })
+ ).toBe(true);
+ expect(
+ tryReserveManualAnalysisCapacity({
+ findingId: 'new-finding',
+ runningCount: 1,
+ concurrencyLimit: 3,
+ startingAnalysisIds,
+ localReservationIds,
+ })
+ ).toBe(false);
+ expect(localReservationIds).toEqual(new Set(['new-finding']));
+ });
});
diff --git a/apps/web/src/components/security-agent/manual-analysis-admission-copy.ts b/apps/web/src/components/security-agent/manual-analysis-admission-copy.ts
index 68445a87f8..8e50ec678d 100644
--- a/apps/web/src/components/security-agent/manual-analysis-admission-copy.ts
+++ b/apps/web/src/components/security-agent/manual-analysis-admission-copy.ts
@@ -1 +1,38 @@
export { manualAnalysisAdmissionCopy } from './security-agent-command-copy';
+
+export const manualAnalysisCapacityFullCopy =
+ 'Analysis capacity is full. Wait for an active analysis to finish.';
+
+type ManualAnalysisCapacityReservation = {
+ findingId: string;
+ runningCount: number;
+ concurrencyLimit: number;
+ startingAnalysisIds: ReadonlySet;
+ localReservationIds: Set;
+};
+
+export function tryReserveManualAnalysisCapacity({
+ findingId,
+ runningCount,
+ concurrencyLimit,
+ startingAnalysisIds,
+ localReservationIds,
+}: ManualAnalysisCapacityReservation): boolean {
+ if (startingAnalysisIds.has(findingId) || localReservationIds.has(findingId)) return false;
+
+ let unconfirmedReservationCount = 0;
+ for (const reservedFindingId of localReservationIds) {
+ if (!startingAnalysisIds.has(reservedFindingId)) unconfirmedReservationCount += 1;
+ }
+ if (runningCount + unconfirmedReservationCount >= concurrencyLimit) return false;
+
+ localReservationIds.add(findingId);
+ return true;
+}
+
+export function isAwaitingManualAnalysisAdmission(
+ hasActiveStartCommand: boolean,
+ analysisStatus: string | null | undefined
+): boolean {
+ return hasActiveStartCommand && analysisStatus !== 'pending' && analysisStatus !== 'running';
+}
diff --git a/apps/web/src/components/security-agent/remediation-unavailable-copy.test.ts b/apps/web/src/components/security-agent/remediation-unavailable-copy.test.ts
new file mode 100644
index 0000000000..7397dba7de
--- /dev/null
+++ b/apps/web/src/components/security-agent/remediation-unavailable-copy.test.ts
@@ -0,0 +1,27 @@
+import { describe, expect, it } from '@jest/globals';
+import {
+ getRemediationUnavailableCopy,
+ isCodebaseAnalysisRequiredReason,
+} from './remediation-unavailable-copy';
+
+describe('getRemediationUnavailableCopy', () => {
+ it('turns typed admission reasons into actionable remediation copy', () => {
+ expect(getRemediationUnavailableCopy('analysis_required')).toBe(
+ 'Run codebase analysis before starting remediation.'
+ );
+ expect(getRemediationUnavailableCopy('finding_not_found')).toBe(
+ 'Security finding no longer exists.'
+ );
+ });
+});
+
+describe('isCodebaseAnalysisRequiredReason', () => {
+ it('classifies only missing codebase analysis as requiring analysis', () => {
+ expect(isCodebaseAnalysisRequiredReason('analysis_required')).toBe(true);
+ expect(isCodebaseAnalysisRequiredReason('sandbox_analysis_required')).toBe(true);
+ expect(isCodebaseAnalysisRequiredReason('triage_only')).toBe(true);
+ expect(isCodebaseAnalysisRequiredReason('not_exploitable')).toBe(false);
+ expect(isCodebaseAnalysisRequiredReason('stale_analysis')).toBe(false);
+ expect(isCodebaseAnalysisRequiredReason('finding_not_open')).toBe(false);
+ });
+});
diff --git a/apps/web/src/components/security-agent/remediation-unavailable-copy.ts b/apps/web/src/components/security-agent/remediation-unavailable-copy.ts
index 2b9c615db4..defa446ad8 100644
--- a/apps/web/src/components/security-agent/remediation-unavailable-copy.ts
+++ b/apps/web/src/components/security-agent/remediation-unavailable-copy.ts
@@ -1,4 +1,10 @@
-const REMEDIATION_UNAVAILABLE_COPY: Record = {
+import {
+ SECURITY_REMEDIATION_ADMISSION_REJECTION_REASONS,
+ type SecurityRemediationAdmissionRejectionReason,
+} from '@kilocode/worker-utils/security-remediation-policy';
+
+const REMEDIATION_UNAVAILABLE_COPY: Record = {
+ finding_not_found: 'Security finding no longer exists.',
finding_not_open: 'Finding is no longer open.',
repo_not_in_scope: 'Repository is not selected for Security Agent.',
analysis_required: 'Run codebase analysis before starting remediation.',
@@ -26,7 +32,23 @@ const REMEDIATION_UNAVAILABLE_COPY: Record = {
'Analysis completed before Auto Remediation was enabled. Manual remediation can still start when safety gates pass.',
};
+function isRemediationAdmissionRejectionReason(
+ reason: string
+): reason is SecurityRemediationAdmissionRejectionReason {
+ return SECURITY_REMEDIATION_ADMISSION_REJECTION_REASONS.some(candidate => candidate === reason);
+}
+
+export function isCodebaseAnalysisRequiredReason(reason: string | null | undefined): boolean {
+ return (
+ reason === 'analysis_required' ||
+ reason === 'sandbox_analysis_required' ||
+ reason === 'triage_only'
+ );
+}
+
export function getRemediationUnavailableCopy(reason: string | null | undefined): string | null {
if (!reason || reason === 'eligible') return null;
- return REMEDIATION_UNAVAILABLE_COPY[reason] ?? 'Remediation is unavailable for this finding.';
+ return isRemediationAdmissionRejectionReason(reason)
+ ? REMEDIATION_UNAVAILABLE_COPY[reason]
+ : 'Remediation is unavailable for this finding.';
}
diff --git a/apps/web/src/components/security-agent/security-agent-command-invalidation.test.ts b/apps/web/src/components/security-agent/security-agent-command-invalidation.test.ts
index 69d40e2804..12abb82091 100644
--- a/apps/web/src/components/security-agent/security-agent-command-invalidation.test.ts
+++ b/apps/web/src/components/security-agent/security-agent-command-invalidation.test.ts
@@ -32,7 +32,7 @@ describe('getSecurityAgentInvalidationScopesForCommand', () => {
const scopes = getSecurityAgentInvalidationScopesForCommand('apply_auto_remediation');
expect(scopes).toEqual(
- expect.arrayContaining(['findings', 'findingDetails', 'stats', 'dashboardStats'])
+ expect.arrayContaining(['findings', 'findingDetails', 'analysis', 'stats', 'dashboardStats'])
);
expect(scopes).not.toContain('repositories');
expect(scopes).not.toContain('permissionStatus');
diff --git a/apps/web/src/components/security-agent/security-agent-command-invalidation.ts b/apps/web/src/components/security-agent/security-agent-command-invalidation.ts
index 783b050ac2..58638f6b70 100644
--- a/apps/web/src/components/security-agent/security-agent-command-invalidation.ts
+++ b/apps/web/src/components/security-agent/security-agent-command-invalidation.ts
@@ -49,6 +49,7 @@ const analysisScopes = [
const remediationScopes = [
'findings',
'findingDetails',
+ 'analysis',
'stats',
'dashboardStats',
] as const satisfies readonly SecurityAgentInvalidationScope[];
diff --git a/apps/web/src/components/security-agent/security-finding-list-presentation.ts b/apps/web/src/components/security-agent/security-finding-list-presentation.ts
new file mode 100644
index 0000000000..74ca36c0b7
--- /dev/null
+++ b/apps/web/src/components/security-agent/security-finding-list-presentation.ts
@@ -0,0 +1,186 @@
+import { differenceInCalendarDays, format, isAfter, isBefore } from 'date-fns';
+import {
+ AlertTriangle,
+ Brain,
+ CheckCircle2,
+ Clock3,
+ Eye,
+ Loader2,
+ Shield,
+ ShieldAlert,
+ ShieldCheck,
+ XCircle,
+ type LucideIcon,
+} from 'lucide-react';
+import type { SecurityFinding } from '@kilocode/db/schema';
+
+export type FindingTone = 'success' | 'warning' | 'destructive' | 'neutral';
+
+export type FindingStatusPresentation = {
+ label: string;
+ tone: FindingTone;
+ icon: LucideIcon;
+ spinning?: boolean;
+ tooltip?: string | null;
+};
+
+export type FindingDeadlinePresentation = FindingStatusPresentation & {
+ detail: string;
+};
+
+const gridWithSla = 'xl:grid-cols-[minmax(0,1fr)_9rem_8.5rem_minmax(9rem,auto)_2.25rem] xl:gap-x-3';
+const gridWithoutSla = 'xl:grid-cols-[minmax(0,1fr)_9rem_minmax(9rem,auto)_2.25rem] xl:gap-x-3';
+
+export function getFindingListGridClass(showSla: boolean) {
+ return showSla ? gridWithSla : gridWithoutSla;
+}
+
+function isSupersededFinding(finding: SecurityFinding) {
+ return finding.status === 'ignored' && finding.ignored_reason?.startsWith('superseded:');
+}
+
+export function getAnalysisPresentation(finding: SecurityFinding): FindingStatusPresentation {
+ if (finding.analysis_status === 'pending') {
+ return {
+ icon: Loader2,
+ label: 'Analysis queued',
+ tone: 'warning',
+ spinning: true,
+ tooltip: 'Analysis is queued',
+ };
+ }
+ if (finding.analysis_status === 'running') {
+ return {
+ icon: Loader2,
+ label: 'Analyzing',
+ tone: 'warning',
+ spinning: true,
+ tooltip: 'Analysis is running',
+ };
+ }
+ if (finding.analysis_status === 'failed') {
+ return {
+ icon: XCircle,
+ label: 'Analysis failed',
+ tone: 'destructive',
+ tooltip: finding.analysis_error || 'Analysis failed. Retry to run it again.',
+ };
+ }
+
+ const sandbox = finding.analysis?.sandboxAnalysis;
+ const triage = finding.analysis?.triage;
+ if (sandbox?.isExploitable === true) {
+ return {
+ icon: ShieldAlert,
+ label: 'Exploitable',
+ tone: 'destructive',
+ tooltip: sandbox.summary || 'Codebase analysis confirmed this vulnerability is exploitable',
+ };
+ }
+ if (sandbox?.isExploitable === false) {
+ return {
+ icon: ShieldCheck,
+ label: 'Not exploitable',
+ tone: 'success',
+ tooltip: sandbox.summary || 'Codebase analysis determined this is not exploitable',
+ };
+ }
+ if (triage?.suggestedAction === 'dismiss') {
+ return {
+ icon: ShieldCheck,
+ label: 'Safe to dismiss',
+ tone: 'success',
+ tooltip: triage.needsSandboxReasoning || 'Triage determined this can be safely dismissed',
+ };
+ }
+ if (triage?.suggestedAction === 'manual_review') {
+ return {
+ icon: Eye,
+ label: 'Needs review',
+ tone: 'warning',
+ tooltip: triage.needsSandboxReasoning || 'Triage flagged this for manual review',
+ };
+ }
+ if (finding.analysis_status === 'completed') {
+ return {
+ icon: Shield,
+ label: triage ? 'Triage complete' : 'Analyzed',
+ tone: 'neutral',
+ tooltip: triage?.needsSandboxReasoning || null,
+ };
+ }
+ return {
+ icon: Brain,
+ label: 'Not analyzed',
+ tone: 'neutral',
+ };
+}
+
+function formatFindingDate(date: Date) {
+ return format(date, 'MMM d, yyyy');
+}
+
+export function getDeadlinePresentation(
+ finding: SecurityFinding,
+ now = new Date()
+): FindingDeadlinePresentation {
+ if (finding.status === 'fixed') {
+ const fixedAt = finding.fixed_at ? new Date(finding.fixed_at) : null;
+ const deadline = finding.sla_due_at ? new Date(finding.sla_due_at) : null;
+ const fixedBeforeDeadline = fixedAt && deadline && !isAfter(fixedAt, deadline);
+ return {
+ icon: fixedBeforeDeadline ? CheckCircle2 : Clock3,
+ label: fixedBeforeDeadline ? 'Fixed before deadline' : 'Fixed',
+ detail: fixedAt ? `Fixed ${formatFindingDate(fixedAt)}` : 'Resolution recorded',
+ tone: fixedBeforeDeadline ? 'success' : 'neutral',
+ };
+ }
+
+ if (finding.status === 'ignored') {
+ const updatedAt = new Date(finding.updated_at);
+ const label = isSupersededFinding(finding) ? 'Superseded' : 'Dismissed';
+ return {
+ icon: Clock3,
+ label,
+ detail: `${label} ${formatFindingDate(updatedAt)}`,
+ tone: 'neutral',
+ };
+ }
+
+ if (!finding.sla_due_at) {
+ return {
+ icon: Clock3,
+ label: 'Deadline not set',
+ detail: 'No SLA deadline',
+ tone: 'neutral',
+ };
+ }
+
+ const deadline = new Date(finding.sla_due_at);
+ const calendarDays = differenceInCalendarDays(deadline, now);
+ const detail = `Due ${formatFindingDate(deadline)}`;
+ if (isBefore(deadline, now)) {
+ const overdueDays = Math.abs(calendarDays);
+ return {
+ icon: AlertTriangle,
+ label:
+ overdueDays === 0
+ ? 'Overdue'
+ : `${overdueDays} ${overdueDays === 1 ? 'day' : 'days'} overdue`,
+ detail,
+ tone: 'destructive',
+ };
+ }
+ if (calendarDays === 0) {
+ return { icon: Clock3, label: 'Due today', detail, tone: 'warning' };
+ }
+ if (calendarDays === 1) {
+ return { icon: Clock3, label: 'Due tomorrow', detail, tone: 'warning' };
+ }
+ return {
+ icon: Clock3,
+ label: `Due in ${calendarDays} days`,
+ detail,
+ tone: calendarDays <= 3 ? 'warning' : 'neutral',
+ };
+}
diff --git a/apps/web/src/components/ui/accordion.tsx b/apps/web/src/components/ui/accordion.tsx
index e4dc70548f..4fcf70c51a 100644
--- a/apps/web/src/components/ui/accordion.tsx
+++ b/apps/web/src/components/ui/accordion.tsx
@@ -33,7 +33,7 @@ function AccordionTrigger({
svg]:rotate-90',
+ 'focus-visible:border-ring focus-visible:ring-ring type-body flex flex-1 cursor-pointer items-start gap-2 rounded-md pt-4 text-left font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-90',
className
)}
{...props}
@@ -53,7 +53,7 @@ function AccordionContent({
return (
{children}
diff --git a/apps/web/src/components/ui/badge.tsx b/apps/web/src/components/ui/badge.tsx
index 789f95d54e..dcb6eb3614 100644
--- a/apps/web/src/components/ui/badge.tsx
+++ b/apps/web/src/components/ui/badge.tsx
@@ -5,7 +5,7 @@ import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
const badgeVariants = cva(
- 'inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden',
+ 'type-label inline-flex items-center justify-center rounded-md border px-2 py-0.5 w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring focus-visible:ring-[3px] aria-invalid:ring-destructive/20 aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden',
{
variants: {
variant: {
diff --git a/apps/web/src/components/ui/button.tsx b/apps/web/src/components/ui/button.tsx
index e0147f0780..40cd4623cc 100644
--- a/apps/web/src/components/ui/button.tsx
+++ b/apps/web/src/components/ui/button.tsx
@@ -5,24 +5,24 @@ import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
const buttonVariants = cva(
- 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors cursor-pointer focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
+ 'type-label inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md transition-colors cursor-pointer focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
{
variants: {
variant: {
- default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90',
+ default: 'bg-primary text-primary-foreground hover:bg-primary-hover',
brand:
- 'bg-brand-primary text-primary-foreground shadow hover:bg-brand-primary/90 focus-visible:ring-brand-primary/50',
- primary: 'bg-[#2B6AD2] text-white hover:bg-[#225eb9] focus:ring-[#3b7de8]',
- destructive: 'bg-destructive text-white shadow-sm hover:bg-destructive/90',
+ 'bg-brand-primary text-primary-foreground hover:bg-brand-primary-hover focus-visible:ring-brand-primary-ring',
+ primary: 'bg-primary text-primary-foreground hover:bg-primary-hover',
+ destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive-hover',
outline:
- 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
- secondary: 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
+ 'border border-input bg-input-background hover:bg-accent hover:text-accent-foreground',
+ secondary: 'bg-secondary text-secondary-foreground hover:bg-accent',
ghost: 'hover:bg-accent hover:text-accent-foreground',
- link: 'text-primary underline-offset-4 hover:underline',
+ link: 'text-link underline-offset-4 hover:text-link-hover hover:underline',
},
size: {
default: 'h-9 px-4 py-2',
- sm: 'h-8 rounded-md px-3 text-xs',
+ sm: 'h-8 rounded-md px-3',
lg: 'h-10 rounded-md px-8',
icon: 'h-9 w-9',
},
diff --git a/apps/web/src/components/ui/calendar.tsx b/apps/web/src/components/ui/calendar.tsx
new file mode 100644
index 0000000000..64522222d9
--- /dev/null
+++ b/apps/web/src/components/ui/calendar.tsx
@@ -0,0 +1,176 @@
+'use client';
+
+import * as React from 'react';
+import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-react';
+import { DayPicker, getDefaultClassNames } from 'react-day-picker';
+import type { DayButton } from 'react-day-picker';
+
+import { cn } from '@/lib/utils';
+import { Button, buttonVariants } from '@/components/ui/button';
+
+function Calendar({
+ className,
+ classNames,
+ showOutsideDays = true,
+ captionLayout = 'label',
+ buttonVariant = 'ghost',
+ formatters,
+ components,
+ ...props
+}: React.ComponentProps & {
+ buttonVariant?: React.ComponentProps['variant'];
+}) {
+ const defaultClassNames = getDefaultClassNames();
+
+ return (
+ svg]:rotate-180`,
+ String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
+ className
+ )}
+ captionLayout={captionLayout}
+ formatters={{
+ formatMonthDropdown: date => date.toLocaleString('default', { month: 'short' }),
+ ...formatters,
+ }}
+ classNames={{
+ root: cn('w-fit', defaultClassNames.root),
+ months: cn('relative flex flex-col gap-4 md:flex-row', defaultClassNames.months),
+ month: cn('flex w-56 flex-col gap-4 last:hidden md:last:flex', defaultClassNames.month),
+ nav: cn(
+ 'absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1',
+ defaultClassNames.nav
+ ),
+ button_previous: cn(
+ buttonVariants({ variant: buttonVariant }),
+ 'size-8 select-none p-0 aria-disabled:opacity-50',
+ defaultClassNames.button_previous
+ ),
+ button_next: cn(
+ buttonVariants({ variant: buttonVariant }),
+ 'size-8 select-none p-0 aria-disabled:opacity-50',
+ defaultClassNames.button_next
+ ),
+ month_caption: cn(
+ 'flex h-8 w-full items-center justify-center px-8',
+ defaultClassNames.month_caption
+ ),
+ dropdowns: cn(
+ 'flex h-8 w-full items-center justify-center gap-1.5 text-sm font-medium',
+ defaultClassNames.dropdowns
+ ),
+ dropdown_root: cn(
+ 'has-focus:border-ring border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] relative rounded-md border',
+ defaultClassNames.dropdown_root
+ ),
+ dropdown: cn('bg-popover absolute inset-0 opacity-0', defaultClassNames.dropdown),
+ caption_label: cn(
+ 'select-none font-medium',
+ captionLayout === 'label'
+ ? 'text-sm'
+ : '[&>svg]:text-muted-foreground flex h-8 items-center gap-1 rounded-md pl-2 pr-1 text-sm [&>svg]:size-3.5',
+ defaultClassNames.caption_label
+ ),
+ month_grid: cn('w-full border-collapse', defaultClassNames.month_grid),
+ weekdays: cn('flex', defaultClassNames.weekdays),
+ weekday: cn(
+ 'text-muted-foreground w-8 select-none rounded-md text-[0.8rem] font-normal',
+ defaultClassNames.weekday
+ ),
+ week: cn('mt-2 flex w-full', defaultClassNames.week),
+ week_number_header: cn('w-8 select-none', defaultClassNames.week_number_header),
+ week_number: cn(
+ 'text-muted-foreground select-none text-[0.8rem]',
+ defaultClassNames.week_number
+ ),
+ day: cn(
+ 'group/day relative size-8 select-none p-0 text-center [&:first-child[data-selected=true]_button]:rounded-l-md [&:last-child[data-selected=true]_button]:rounded-r-md',
+ defaultClassNames.day
+ ),
+ range_start: cn('bg-accent rounded-l-md', defaultClassNames.range_start),
+ range_middle: cn('rounded-none', defaultClassNames.range_middle),
+ range_end: cn('bg-accent rounded-r-md', defaultClassNames.range_end),
+ today: cn(
+ 'bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none',
+ defaultClassNames.today
+ ),
+ outside: cn(
+ 'text-muted-foreground aria-selected:text-muted-foreground',
+ defaultClassNames.outside
+ ),
+ disabled: cn('text-muted-foreground opacity-50', defaultClassNames.disabled),
+ hidden: cn('invisible', defaultClassNames.hidden),
+ ...classNames,
+ }}
+ components={{
+ Root: ({ className, rootRef, ...props }) => {
+ return
;
+ },
+ Chevron: ({ className, orientation, ...props }) => {
+ if (orientation === 'left') {
+ return ;
+ }
+
+ if (orientation === 'right') {
+ return ;
+ }
+
+ return ;
+ },
+ DayButton: CalendarDayButton,
+ WeekNumber: ({ children, ...props }) => {
+ return (
+
+ {children}
+
+ );
+ },
+ ...components,
+ }}
+ {...props}
+ />
+ );
+}
+
+function CalendarDayButton({
+ className,
+ day,
+ modifiers,
+ ...props
+}: React.ComponentProps) {
+ const defaultClassNames = getDefaultClassNames();
+
+ const ref = React.useRef(null);
+ React.useEffect(() => {
+ if (modifiers.focused) ref.current?.focus();
+ }, [modifiers.focused]);
+
+ return (
+ span]:text-xs [&>span]:opacity-70',
+ defaultClassNames.day_button,
+ className
+ )}
+ {...props}
+ />
+ );
+}
+
+export { Calendar, CalendarDayButton };
diff --git a/apps/web/src/components/ui/progress.tsx b/apps/web/src/components/ui/progress.tsx
index 83f33d408a..9e0a7878cd 100644
--- a/apps/web/src/components/ui/progress.tsx
+++ b/apps/web/src/components/ui/progress.tsx
@@ -14,6 +14,7 @@ function Progress({
return (
diff --git a/apps/web/src/lib/security-agent/core/schemas.ts b/apps/web/src/lib/security-agent/core/schemas.ts
index 9a3515ea04..3d58aae693 100644
--- a/apps/web/src/lib/security-agent/core/schemas.ts
+++ b/apps/web/src/lib/security-agent/core/schemas.ts
@@ -24,6 +24,21 @@ export const AnalysisModeSchema = z.enum(['auto', 'shallow', 'deep']);
export const AutoAnalysisMinSeveritySchema = z.enum(['critical', 'high', 'medium', 'all']);
export const AutoRemediationMinSeveritySchema = z.enum(['critical', 'high', 'medium', 'all']);
export const NotificationMinSeveritySchema = SecurityNotificationSeveritySchema;
+export const SecurityAgentUiInteractionSchema = z.enum([
+ 'finding_detail_opened',
+ 'finding_triage_viewed',
+ 'finding_analysis_viewed',
+ 'finding_remediation_viewed',
+ 'findings_filtered',
+ 'settings_config_viewed',
+ 'settings_automation_viewed',
+ 'settings_notifications_viewed',
+ 'settings_sla_viewed',
+]);
+
+export const TrackSecurityAgentUiInteractionInputSchema = z.object({
+ interaction: SecurityAgentUiInteractionSchema,
+});
export const SaveSecurityConfigInputSchema = z.object({
slaCriticalDays: z.number().min(1).max(365).optional(),
@@ -120,6 +135,7 @@ export const SandboxSuggestedActionSchema = z.enum([
export const SecurityFindingSandboxAnalysisSchema = z.object({
isExploitable: z.union([z.boolean(), z.literal('unknown')]),
+ extractionStatus: z.enum(['succeeded', 'failed']).optional(),
exploitabilityReasoning: z.string(),
usageLocations: z.array(z.string()),
suggestedFix: z.string(),
@@ -155,6 +171,7 @@ export const StartAnalysisInputSchema = z.object({
analysisModel: z.string().optional(),
forceSandbox: z.boolean().optional(),
retrySandboxOnly: z.boolean().optional(),
+ restartActive: z.boolean().optional(),
});
export const StartRemediationInputSchema = z.object({
@@ -185,6 +202,10 @@ export const GetDashboardStatsInputSchema = z.object({
repoFullName: z.string().optional(),
});
+export type SecurityAgentUiInteraction = z.infer;
+export type TrackSecurityAgentUiInteractionInput = z.infer<
+ typeof TrackSecurityAgentUiInteractionInputSchema
+>;
export type SaveSecurityConfigInput = z.infer;
export type ListFindingsInput = z.infer;
export type TriggerSyncInput = z.infer;
diff --git a/apps/web/src/lib/security-agent/db/dashboard-stats.test.ts b/apps/web/src/lib/security-agent/db/dashboard-stats.test.ts
new file mode 100644
index 0000000000..8c67e30304
--- /dev/null
+++ b/apps/web/src/lib/security-agent/db/dashboard-stats.test.ts
@@ -0,0 +1,179 @@
+import { describe, expect, it } from '@jest/globals';
+import { db } from '@/lib/drizzle';
+import { security_findings } from '@kilocode/db/schema';
+import type { NewSecurityFinding } from '@kilocode/db/schema';
+import type { SecurityFindingAnalysis } from '@kilocode/db/schema-types';
+import { insertTestUser } from '@/tests/helpers/user.helper';
+import { getDashboardStats } from './dashboard-stats';
+
+const slaConfig = {
+ slaCriticalDays: 15,
+ slaHighDays: 30,
+ slaMediumDays: 45,
+ slaLowDays: 90,
+};
+
+function finding(
+ userId: string,
+ sourceId: string,
+ overrides: Partial
+): NewSecurityFinding {
+ return {
+ owned_by_user_id: userId,
+ repo_full_name: 'Kilo-Org/cloud',
+ source: 'dependabot',
+ source_id: sourceId,
+ severity: 'high',
+ package_name: `package-${sourceId}`,
+ package_ecosystem: 'npm',
+ title: `Security Finding ${sourceId}`,
+ ...overrides,
+ };
+}
+
+function analysis({
+ isExploitable,
+ suggestedAction,
+}: {
+ isExploitable: boolean | 'unknown';
+ suggestedAction: 'dismiss' | 'open_pr' | 'manual_review' | 'monitor';
+}): SecurityFindingAnalysis {
+ return {
+ analyzedAt: '2026-06-17T00:00:00.000Z',
+ sandboxAnalysis: {
+ isExploitable,
+ exploitabilityReasoning: 'Test reasoning',
+ usageLocations: [],
+ suggestedFix: 'Update dependency',
+ suggestedAction,
+ summary: 'Test analysis',
+ rawMarkdown: 'Test analysis',
+ analysisAt: '2026-06-17T00:00:00.000Z',
+ },
+ };
+}
+
+describe('getDashboardStats', () => {
+ it('returns guided urgency, priority, and repository action data within owner scope', async () => {
+ const user = await insertTestUser();
+ const otherUser = await insertTestUser();
+ const now = Date.now();
+ const overdueAt = new Date(now - 2 * 24 * 60 * 60 * 1000).toISOString();
+ const dueSoonAt = new Date(now + 3 * 24 * 60 * 60 * 1000).toISOString();
+
+ await db.insert(security_findings).values([
+ finding(user.id, 'dashboard-overdue', {
+ severity: 'critical',
+ title: 'Critical overdue finding',
+ sla_due_at: overdueAt,
+ }),
+ finding(user.id, 'dashboard-due-soon', {
+ sla_due_at: dueSoonAt,
+ analysis_status: 'completed',
+ analysis: analysis({ isExploitable: true, suggestedAction: 'open_pr' }),
+ }),
+ finding(user.id, 'dashboard-review', {
+ repo_full_name: 'Kilo-Org/kilocode',
+ sla_due_at: null,
+ analysis_status: 'completed',
+ analysis: analysis({ isExploitable: 'unknown', suggestedAction: 'manual_review' }),
+ }),
+ finding(otherUser.id, 'dashboard-other-owner', {
+ severity: 'critical',
+ sla_due_at: overdueAt,
+ }),
+ ]);
+
+ const result = await getDashboardStats({
+ owner: { userId: user.id },
+ slaEnabled: true,
+ slaConfig,
+ });
+
+ expect(result.sla.overall).toEqual({ total: 2, withinSla: 1, overdue: 1 });
+ expect(result.sla.dueSoon).toEqual({ total: 1, exploitable: 1 });
+ expect(result.sla.untrackedCount).toBe(1);
+ expect(result.severity).toEqual({ critical: 1, high: 2, medium: 0, low: 0 });
+ expect(result.analysis).toMatchObject({
+ total: 3,
+ analyzed: 2,
+ exploitable: 1,
+ needsReview: 1,
+ notAnalyzed: 1,
+ });
+ expect(result.priorityFinding).toMatchObject({
+ severity: 'critical',
+ title: 'Critical overdue finding',
+ repoFullName: 'Kilo-Org/cloud',
+ analysisStatus: null,
+ isExploitable: null,
+ slaDueAt: overdueAt,
+ daysOverdue: 2,
+ });
+ expect(result.priorityFinding?.slaDueAt).toMatch(/T/);
+ expect(result.repositoryCount).toBe(2);
+ expect(result.repoHealth).toEqual([
+ {
+ repoFullName: 'Kilo-Org/cloud',
+ open: 2,
+ critical: 1,
+ high: 1,
+ medium: 0,
+ low: 0,
+ overdue: 1,
+ exploitable: 1,
+ needsAction: 2,
+ slaCompliancePercent: 50,
+ },
+ {
+ repoFullName: 'Kilo-Org/kilocode',
+ open: 1,
+ critical: 0,
+ high: 1,
+ medium: 0,
+ low: 0,
+ overdue: 0,
+ exploitable: 0,
+ needsAction: 1,
+ slaCompliancePercent: 100,
+ },
+ ]);
+ });
+
+ it('applies the exact repository filter and risk-first ordering when SLA is disabled', async () => {
+ const user = await insertTestUser();
+ const overdueAt = new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString();
+
+ await db.insert(security_findings).values([
+ finding(user.id, 'dashboard-filter-cloud', { repo_full_name: 'Kilo-Org/cloud' }),
+ finding(user.id, 'dashboard-filter-web-complete', {
+ repo_full_name: 'Kilo-Org/web',
+ title: 'Completed analysis with expired deadline',
+ sla_due_at: overdueAt,
+ analysis_status: 'completed',
+ analysis: analysis({ isExploitable: false, suggestedAction: 'monitor' }),
+ }),
+ finding(user.id, 'dashboard-filter-web-needs-analysis', {
+ repo_full_name: 'Kilo-Org/web',
+ title: 'Finding needing analysis',
+ }),
+ ]);
+
+ const result = await getDashboardStats({
+ owner: { userId: user.id },
+ repoFullName: 'Kilo-Org/web',
+ slaEnabled: false,
+ slaConfig,
+ });
+
+ expect(result.analysis.total).toBe(2);
+ expect(result.repositoryCount).toBe(1);
+ expect(result.repoHealth).toHaveLength(1);
+ expect(result.repoHealth[0]?.repoFullName).toBe('Kilo-Org/web');
+ expect(result.priorityFinding).toMatchObject({
+ repoFullName: 'Kilo-Org/web',
+ title: 'Finding needing analysis',
+ analysisStatus: null,
+ });
+ });
+});
diff --git a/apps/web/src/lib/security-agent/db/dashboard-stats.ts b/apps/web/src/lib/security-agent/db/dashboard-stats.ts
index 18ee79c54a..be663fa92b 100644
--- a/apps/web/src/lib/security-agent/db/dashboard-stats.ts
+++ b/apps/web/src/lib/security-agent/db/dashboard-stats.ts
@@ -10,6 +10,7 @@ export type DashboardStats = {
sla: {
overall: { total: number; withinSla: number; overdue: number };
bySeverity: Record;
+ dueSoon: { total: number; exploitable: number };
untrackedCount: number;
};
severity: Record;
@@ -46,15 +47,30 @@ export type DashboardStats = {
slaDueAt: string;
daysOverdue: number;
}>;
+ priorityFinding: {
+ id: string;
+ severity: string;
+ title: string;
+ repoFullName: string;
+ analysisStatus: string | null;
+ isExploitable: boolean | 'unknown' | null;
+ suggestedAction: string | null;
+ slaDueAt: string | null;
+ daysOverdue: number | null;
+ } | null;
repoHealth: Array<{
repoFullName: string;
+ open: number;
critical: number;
high: number;
medium: number;
low: number;
overdue: number;
+ exploitable: number;
+ needsAction: number;
slaCompliancePercent: number;
}>;
+ repositoryCount: number;
};
type Owner = { type: 'org'; id: string } | { type: 'user'; id: string };
@@ -86,6 +102,8 @@ type SlaRow = {
total: string;
within_sla: string;
overdue: string;
+ due_soon: string;
+ due_soon_exploitable: string;
untracked: string;
};
@@ -129,19 +147,36 @@ type OverdueRow = {
days_overdue: string;
};
+type PriorityFindingRow = {
+ id: string;
+ severity: string;
+ title: string;
+ repo_full_name: string;
+ analysis_status: string | null;
+ is_exploitable: string | null;
+ suggested_action: string | null;
+ sla_due_at: string | null;
+ days_overdue: string | null;
+};
+
type RepoHealthRow = {
repo_full_name: string;
+ open: string;
critical: string;
high: string;
medium: string;
low: string;
overdue: string;
+ exploitable: string;
+ needs_action: string;
sla_compliance_percent: string;
+ repository_count: string;
};
type GetDashboardStatsParams = {
owner: SecurityReviewOwner;
repoFullName?: string;
+ slaEnabled: boolean;
slaConfig: {
slaCriticalDays: number;
slaHighDays: number;
@@ -163,11 +198,34 @@ function emptySeverityRecord(defaultValue: () => T): Record {
};
}
+function parseExploitability(value: string | null): boolean | 'unknown' | null {
+ if (value === 'true') return true;
+ if (value === 'false') return false;
+ if (value === 'unknown') return 'unknown';
+ return null;
+}
+
+function toUtcIso(value: string): string {
+ return new Date(value).toISOString();
+}
+
export async function getDashboardStats(params: GetDashboardStatsParams): Promise {
try {
- const { owner, repoFullName, slaConfig } = params;
+ const { owner, repoFullName, slaEnabled, slaConfig } = params;
const ownerConverted = toOwner(owner);
const whereClause = buildWhereClause(ownerConverted, repoFullName);
+ const repositorySecondaryOrder = slaEnabled
+ ? sql`COUNT(*) FILTER (WHERE ${security_findings.status} = 'open' AND ${security_findings.sla_due_at} IS NOT NULL AND ${security_findings.sla_due_at} <= now())`
+ : sql`COUNT(*) FILTER (WHERE ${security_findings.status} = 'open' AND ${security_findings.analysis_status} = 'completed' AND (${security_findings.analysis}->'sandboxAnalysis'->>'isExploitable') = 'true')`;
+ const priorityDeadlineOrder = slaEnabled
+ ? sql`CASE
+ WHEN ${security_findings.sla_due_at} IS NOT NULL AND ${security_findings.sla_due_at} <= now() THEN 0
+ ELSE 1
+ END`
+ : sql`CASE WHEN true THEN 0 END`;
+ const priorityDeadlineDateOrder = slaEnabled
+ ? sql`${security_findings.sla_due_at} ASC NULLS LAST`
+ : sql`${security_findings.first_detected_at} ASC`;
const [
slaResult,
@@ -176,6 +234,7 @@ export async function getDashboardStats(params: GetDashboardStatsParams): Promis
analysisResult,
mttrResult,
overdueResult,
+ priorityFindingResult,
repoHealthResult,
] = await Promise.all([
// SLA query
@@ -185,6 +244,8 @@ export async function getDashboardStats(params: GetDashboardStatsParams): Promis
COUNT(*) FILTER (WHERE ${security_findings.sla_due_at} IS NOT NULL) AS total,
COUNT(*) FILTER (WHERE ${security_findings.sla_due_at} IS NOT NULL AND ${security_findings.sla_due_at} > now()) AS within_sla,
COUNT(*) FILTER (WHERE ${security_findings.sla_due_at} IS NOT NULL AND ${security_findings.sla_due_at} <= now()) AS overdue,
+ COUNT(*) FILTER (WHERE ${security_findings.sla_due_at} > now() AND ${security_findings.sla_due_at} <= now() + interval '7 days') AS due_soon,
+ COUNT(*) FILTER (WHERE ${security_findings.sla_due_at} > now() AND ${security_findings.sla_due_at} <= now() + interval '7 days' AND ${security_findings.analysis_status} = 'completed' AND (${security_findings.analysis}->'sandboxAnalysis'->>'isExploitable') = 'true') AS due_soon_exploitable,
COUNT(*) FILTER (WHERE ${security_findings.sla_due_at} IS NULL) AS untracked
FROM ${security_findings}
WHERE ${security_findings.status} = 'open' AND ${whereClause}
@@ -216,7 +277,7 @@ export async function getDashboardStats(params: GetDashboardStatsParams): Promis
COUNT(*) FILTER (WHERE ${security_findings.analysis_status} = 'completed' AND (${security_findings.analysis}->'sandboxAnalysis'->>'isExploitable') = 'false') AS not_exploitable,
COUNT(*) FILTER (WHERE ${security_findings.analysis_status} = 'completed' AND ((${security_findings.analysis}->'sandboxAnalysis'->>'isExploitable') IS NULL OR (${security_findings.analysis}->'sandboxAnalysis'->>'isExploitable') = 'unknown') AND (${security_findings.analysis}->'triage'->>'suggestedAction') = 'analyze_codebase') AS triage_complete,
COUNT(*) FILTER (WHERE ${security_findings.analysis_status} = 'completed' AND (${security_findings.analysis}->'triage'->>'suggestedAction') = 'dismiss' AND ((${security_findings.analysis}->'sandboxAnalysis'->>'isExploitable') IS NULL OR (${security_findings.analysis}->'sandboxAnalysis'->>'isExploitable') = 'unknown')) AS safe_to_dismiss,
- COUNT(*) FILTER (WHERE ${security_findings.analysis_status} = 'completed' AND (${security_findings.analysis}->'triage'->>'suggestedAction') = 'manual_review' AND ((${security_findings.analysis}->'sandboxAnalysis'->>'isExploitable') IS NULL OR (${security_findings.analysis}->'sandboxAnalysis'->>'isExploitable') = 'unknown')) AS needs_review,
+ COUNT(*) FILTER (WHERE ${security_findings.analysis_status} = 'completed' AND COALESCE(${security_findings.analysis}->'sandboxAnalysis'->>'suggestedAction', ${security_findings.analysis}->'triage'->>'suggestedAction') = 'manual_review' AND ((${security_findings.analysis}->'sandboxAnalysis'->>'isExploitable') IS NULL OR (${security_findings.analysis}->'sandboxAnalysis'->>'isExploitable') = 'unknown')) AS needs_review,
COUNT(*) FILTER (WHERE ${security_findings.analysis_status} IN ('pending', 'running')) AS analyzing,
COUNT(*) FILTER (WHERE ${security_findings.analysis_status} IS NULL) AS not_analyzed,
COUNT(*) FILTER (WHERE ${security_findings.analysis_status} = 'failed') AS failed
@@ -252,35 +313,100 @@ export async function getDashboardStats(params: GetDashboardStatsParams): Promis
FROM ${security_findings}
WHERE ${security_findings.status} = 'open'
AND ${security_findings.sla_due_at} IS NOT NULL
- AND ${security_findings.sla_due_at} < now()
+ AND ${security_findings.sla_due_at} <= now()
AND ${whereClause}
ORDER BY ${security_findings.sla_due_at} ASC
LIMIT 10
`),
+ // Highest-priority open finding for guided next action
+ db.execute(sql`
+ SELECT
+ ${security_findings.id} AS id,
+ ${security_findings.severity} AS severity,
+ ${security_findings.title} AS title,
+ ${security_findings.repo_full_name} AS repo_full_name,
+ ${security_findings.analysis_status} AS analysis_status,
+ ${security_findings.analysis}->'sandboxAnalysis'->>'isExploitable' AS is_exploitable,
+ COALESCE(
+ ${security_findings.analysis}->'sandboxAnalysis'->>'suggestedAction',
+ ${security_findings.analysis}->'triage'->>'suggestedAction'
+ ) AS suggested_action,
+ ${security_findings.sla_due_at} AS sla_due_at,
+ CASE
+ WHEN ${security_findings.sla_due_at} IS NOT NULL AND ${security_findings.sla_due_at} <= now()
+ THEN EXTRACT(EPOCH FROM (now() - ${security_findings.sla_due_at})) / 86400
+ ELSE NULL
+ END AS days_overdue
+ FROM ${security_findings}
+ WHERE ${security_findings.status} = 'open' AND ${whereClause}
+ ORDER BY
+ CASE ${security_findings.severity}
+ WHEN 'critical' THEN 0
+ WHEN 'high' THEN 1
+ WHEN 'medium' THEN 2
+ WHEN 'low' THEN 3
+ ELSE 4
+ END,
+ ${priorityDeadlineOrder},
+ CASE
+ WHEN ${security_findings.analysis_status} IS NULL OR ${security_findings.analysis_status} = 'failed' THEN 0
+ WHEN ${security_findings.analysis_status} IN ('pending', 'running') THEN 1
+ WHEN (${security_findings.analysis}->'sandboxAnalysis'->>'isExploitable') = 'true' THEN 2
+ WHEN COALESCE(${security_findings.analysis}->'sandboxAnalysis'->>'suggestedAction', ${security_findings.analysis}->'triage'->>'suggestedAction') IN ('analyze_codebase', 'manual_review') THEN 3
+ ELSE 4
+ END,
+ ${priorityDeadlineDateOrder},
+ ${security_findings.first_detected_at} ASC,
+ ${security_findings.id} ASC
+ LIMIT 1
+ `),
+
// Repo health query
db.execute(sql`
SELECT
${security_findings.repo_full_name} AS repo_full_name,
+ COUNT(*) FILTER (WHERE ${security_findings.status} = 'open') AS open,
COUNT(*) FILTER (WHERE ${security_findings.severity} = 'critical' AND ${security_findings.status} = 'open') AS critical,
COUNT(*) FILTER (WHERE ${security_findings.severity} = 'high' AND ${security_findings.status} = 'open') AS high,
COUNT(*) FILTER (WHERE ${security_findings.severity} = 'medium' AND ${security_findings.status} = 'open') AS medium,
COUNT(*) FILTER (WHERE ${security_findings.severity} = 'low' AND ${security_findings.status} = 'open') AS low,
- COUNT(*) FILTER (WHERE ${security_findings.status} = 'open' AND ${security_findings.sla_due_at} IS NOT NULL AND ${security_findings.sla_due_at} < now()) AS overdue,
+ COUNT(*) FILTER (WHERE ${security_findings.status} = 'open' AND ${security_findings.sla_due_at} IS NOT NULL AND ${security_findings.sla_due_at} <= now()) AS overdue,
+ COUNT(*) FILTER (WHERE ${security_findings.status} = 'open' AND ${security_findings.analysis_status} = 'completed' AND (${security_findings.analysis}->'sandboxAnalysis'->>'isExploitable') = 'true') AS exploitable,
+ COUNT(*) FILTER (
+ WHERE ${security_findings.status} = 'open'
+ AND (
+ ${security_findings.analysis_status} IS NULL
+ OR ${security_findings.analysis_status} = 'failed'
+ OR (${security_findings.analysis_status} = 'completed' AND (${security_findings.analysis}->'sandboxAnalysis'->>'isExploitable') = 'true')
+ OR (${security_findings.analysis_status} = 'completed' AND COALESCE(${security_findings.analysis}->'sandboxAnalysis'->>'suggestedAction', ${security_findings.analysis}->'triage'->>'suggestedAction') IN ('analyze_codebase', 'manual_review'))
+ )
+ ) AS needs_action,
CASE
WHEN COUNT(*) FILTER (WHERE ${security_findings.status} = 'open' AND ${security_findings.sla_due_at} IS NOT NULL) = 0 THEN 100
ELSE ROUND(
COUNT(*) FILTER (WHERE ${security_findings.status} = 'open' AND ${security_findings.sla_due_at} IS NOT NULL AND ${security_findings.sla_due_at} > now()) * 100.0 /
COUNT(*) FILTER (WHERE ${security_findings.status} = 'open' AND ${security_findings.sla_due_at} IS NOT NULL), 1
)
- END AS sla_compliance_percent
+ END AS sla_compliance_percent,
+ COUNT(*) OVER() AS repository_count
FROM ${security_findings}
WHERE ${whereClause}
GROUP BY ${security_findings.repo_full_name}
HAVING COUNT(*) FILTER (WHERE ${security_findings.status} = 'open') > 0
ORDER BY
COUNT(*) FILTER (WHERE ${security_findings.severity} = 'critical' AND ${security_findings.status} = 'open') DESC,
- COUNT(*) FILTER (WHERE ${security_findings.status} = 'open' AND ${security_findings.sla_due_at} IS NOT NULL AND ${security_findings.sla_due_at} < now()) DESC
+ ${repositorySecondaryOrder} DESC,
+ COUNT(*) FILTER (
+ WHERE ${security_findings.status} = 'open'
+ AND (
+ ${security_findings.analysis_status} IS NULL
+ OR ${security_findings.analysis_status} = 'failed'
+ OR (${security_findings.analysis_status} = 'completed' AND (${security_findings.analysis}->'sandboxAnalysis'->>'isExploitable') = 'true')
+ OR (${security_findings.analysis_status} = 'completed' AND COALESCE(${security_findings.analysis}->'sandboxAnalysis'->>'suggestedAction', ${security_findings.analysis}->'triage'->>'suggestedAction') IN ('analyze_codebase', 'manual_review'))
+ )
+ ) DESC,
+ ${security_findings.repo_full_name} ASC
LIMIT 10
`),
]);
@@ -290,6 +416,8 @@ export async function getDashboardStats(params: GetDashboardStatsParams): Promis
let slaOverallTotal = 0;
let slaOverallWithinSla = 0;
let slaOverallOverdue = 0;
+ let dueSoonCount = 0;
+ let dueSoonExploitableCount = 0;
let untrackedCount = 0;
for (const row of slaResult.rows) {
@@ -303,6 +431,8 @@ export async function getDashboardStats(params: GetDashboardStatsParams): Promis
slaOverallWithinSla += withinSla;
slaOverallOverdue += overdue;
}
+ dueSoonCount += Number(row.due_soon);
+ dueSoonExploitableCount += Number(row.due_soon_exploitable);
untrackedCount += Number(row.untracked);
}
@@ -387,20 +517,42 @@ export async function getDashboardStats(params: GetDashboardStatsParams): Promis
title: row.title,
repoFullName: row.repo_full_name,
packageName: row.package_name,
- slaDueAt: row.sla_due_at,
+ slaDueAt: toUtcIso(row.sla_due_at),
daysOverdue: Math.max(0, Math.floor(Number(row.days_overdue))),
}));
+ const priorityRow = priorityFindingResult.rows[0];
+ const priorityFinding = priorityRow
+ ? {
+ id: priorityRow.id,
+ severity: priorityRow.severity,
+ title: priorityRow.title,
+ repoFullName: priorityRow.repo_full_name,
+ analysisStatus: priorityRow.analysis_status,
+ isExploitable: parseExploitability(priorityRow.is_exploitable),
+ suggestedAction: priorityRow.suggested_action,
+ slaDueAt: priorityRow.sla_due_at ? toUtcIso(priorityRow.sla_due_at) : null,
+ daysOverdue:
+ priorityRow.days_overdue === null
+ ? null
+ : Math.max(0, Math.floor(Number(priorityRow.days_overdue))),
+ }
+ : null;
+
// Parse repo health results
const repoHealth = repoHealthResult.rows.map(row => ({
repoFullName: row.repo_full_name,
+ open: Number(row.open),
critical: Number(row.critical),
high: Number(row.high),
medium: Number(row.medium),
low: Number(row.low),
overdue: Number(row.overdue),
+ exploitable: Number(row.exploitable),
+ needsAction: Number(row.needs_action),
slaCompliancePercent: Number(row.sla_compliance_percent),
}));
+ const repositoryCount = Number(repoHealthResult.rows[0]?.repository_count ?? 0);
return {
sla: {
@@ -410,6 +562,7 @@ export async function getDashboardStats(params: GetDashboardStatsParams): Promis
overdue: slaOverallOverdue,
},
bySeverity: slaBySeverity,
+ dueSoon: { total: dueSoonCount, exploitable: dueSoonExploitableCount },
untrackedCount,
},
severity: severityCounts,
@@ -417,7 +570,9 @@ export async function getDashboardStats(params: GetDashboardStatsParams): Promis
analysis,
mttr: { bySeverity: mttrBySeverity },
overdue,
+ priorityFinding,
repoHealth,
+ repositoryCount,
};
} catch (error) {
captureException(error, {
diff --git a/apps/web/src/lib/security-agent/db/security-analysis.ts b/apps/web/src/lib/security-agent/db/security-analysis.ts
index a59ee7d19d..2cbc26bee6 100644
--- a/apps/web/src/lib/security-agent/db/security-analysis.ts
+++ b/apps/web/src/lib/security-agent/db/security-analysis.ts
@@ -5,6 +5,17 @@ import {
security_analysis_owner_state,
type SecurityFinding,
} from '@kilocode/db/schema';
+import {
+ SecurityAuditLogAction,
+ SecurityFindingAuditSourceContext,
+} from '@kilocode/db/schema-types';
+import {
+ SECURITY_FINDING_AUDIT_SYSTEM_ACTOR,
+ deriveSecurityFindingAuditEventKey,
+ insertSecurityFindingAuditEvent,
+ type SecurityFindingAuditEventFinding,
+ type SecurityFindingAuditOwner,
+} from '@kilocode/worker-utils/security-finding-audit';
import { eq, and, sql, count, isNotNull, desc, or, isNull, not, like } from 'drizzle-orm';
import { captureException } from '@sentry/nextjs';
import type {
@@ -28,6 +39,30 @@ function toOwner(owner: SecurityReviewOwner): Owner {
throw new Error('Invalid owner: must have either organizationId or userId');
}
+function toAuditOwner(
+ finding: Pick
+): SecurityFindingAuditOwner {
+ if (finding.owned_by_organization_id) {
+ return { type: 'organization', organizationId: finding.owned_by_organization_id };
+ }
+ if (finding.owned_by_user_id) {
+ return { type: 'user', userId: finding.owned_by_user_id };
+ }
+ throw new Error('Security finding has no audit owner');
+}
+
+function ownerAuditKeyPart(
+ finding: Pick
+): string {
+ if (finding.owned_by_organization_id) return `organization:${finding.owned_by_organization_id}`;
+ if (finding.owned_by_user_id) return `user:${finding.owned_by_user_id}`;
+ throw new Error('Security finding has no audit owner');
+}
+
+function toAuditFinding(finding: SecurityFinding): SecurityFindingAuditEventFinding {
+ return finding;
+}
+
export type AutoAnalysisQueueStatus = 'queued' | 'pending' | 'running' | 'failed' | 'completed';
export type AutoAnalysisFailureCode =
@@ -123,18 +158,20 @@ export async function updateAnalysisStatus(
updateData.analysis_completed_at = sql`now()`;
}
+ if (status === 'completed' || status === 'failed') {
+ const rows = await updateTerminalAnalysisStatusWithAuditEvent(
+ findingId,
+ status,
+ updateData,
+ updates
+ );
+ return rows.length > 0;
+ }
+
const rows = await db
.update(security_findings)
.set(updateData)
- .where(
- and(
- eq(security_findings.id, findingId),
- or(
- isNull(security_findings.ignored_reason),
- not(like(security_findings.ignored_reason, 'superseded:%'))
- )
- )
- )
+ .where(analysisStatusUpdatePredicate(findingId))
.returning({ id: security_findings.id });
return rows.length > 0;
@@ -147,6 +184,120 @@ export async function updateAnalysisStatus(
}
}
+function analysisStatusUpdatePredicate(findingId: string) {
+ return and(
+ eq(security_findings.id, findingId),
+ or(
+ isNull(security_findings.ignored_reason),
+ not(like(security_findings.ignored_reason, 'superseded:%'))
+ )
+ );
+}
+
+async function updateTerminalAnalysisStatusWithAuditEvent(
+ findingId: string,
+ status: 'completed' | 'failed',
+ updateData: Record,
+ updates: {
+ error?: string;
+ analysis?: SecurityFindingAnalysis;
+ }
+): Promise> {
+ return db.transaction(async tx => {
+ const [previousFinding] = await tx
+ .select()
+ .from(security_findings)
+ .where(analysisStatusUpdatePredicate(findingId))
+ .limit(1);
+
+ if (!previousFinding) return [];
+
+ const [updatedFinding] = await tx
+ .update(security_findings)
+ .set(updateData)
+ .where(analysisStatusUpdatePredicate(findingId))
+ .returning();
+
+ if (!updatedFinding) return [];
+
+ await insertAnalysisAuditEvent(tx, {
+ previousFinding,
+ updatedFinding,
+ status,
+ analysis: updates.analysis,
+ });
+
+ return [{ id: updatedFinding.id }];
+ });
+}
+
+async function insertAnalysisAuditEvent(
+ db: Parameters[0],
+ params: {
+ previousFinding: SecurityFinding;
+ updatedFinding: SecurityFinding;
+ status: 'completed' | 'failed';
+ analysis?: SecurityFindingAnalysis;
+ }
+): Promise {
+ const { previousFinding, updatedFinding, status, analysis } = params;
+ const action =
+ status === 'completed'
+ ? SecurityAuditLogAction.FindingAnalysisCompleted
+ : SecurityAuditLogAction.FindingAnalysisFailed;
+ const occurredAt = updatedFinding.analysis_completed_at ?? new Date().toISOString();
+ const correlationId = analysis?.correlationId ?? updatedFinding.session_id ?? 'none';
+ const modelSlug = analysis?.analysisModel ?? analysis?.modelUsed ?? analysis?.triageModel ?? null;
+ const triage = analysis?.triage;
+ const sandbox = analysis?.sandboxAnalysis;
+ const suggestedAction = sandbox?.suggestedAction ?? triage?.suggestedAction;
+
+ await insertSecurityFindingAuditEvent(db, {
+ owner: toAuditOwner(updatedFinding),
+ finding: toAuditFinding(updatedFinding),
+ actor: SECURITY_FINDING_AUDIT_SYSTEM_ACTOR,
+ action,
+ occurredAt,
+ eventKey: deriveSecurityFindingAuditEventKey([
+ ownerAuditKeyPart(updatedFinding),
+ updatedFinding.id,
+ action,
+ correlationId,
+ ]),
+ sourceContext: SecurityFindingAuditSourceContext.AnalysisWorker,
+ beforeState: { analysis_status: previousFinding.analysis_status ?? 'unknown' },
+ afterState:
+ status === 'completed'
+ ? {
+ analysis_status: 'completed',
+ ...(suggestedAction ? { suggested_action: suggestedAction } : {}),
+ ...(!sandbox && triage?.confidence ? { confidence: triage.confidence } : {}),
+ ...(sandbox?.extractionStatus
+ ? { structured_extraction_status: sandbox.extractionStatus }
+ : {}),
+ ...(sandbox?.isExploitable !== undefined && sandbox.extractionStatus !== 'failed'
+ ? { is_exploitable: sandbox.isExploitable }
+ : {}),
+ }
+ : { analysis_status: 'failed' },
+ metadata:
+ status === 'completed'
+ ? {
+ ...(analysis?.correlationId ? { correlation_id: analysis.correlationId } : {}),
+ ...(modelSlug ? { model_slug: modelSlug } : {}),
+ ...(analysis?.triageModel ? { triage_model_slug: analysis.triageModel } : {}),
+ ...(analysis?.analysisModel ? { analysis_model_slug: analysis.analysisModel } : {}),
+ ...(triage?.needsSandboxAnalysis !== undefined
+ ? { needs_sandbox_analysis: triage.needsSandboxAnalysis }
+ : {}),
+ }
+ : {
+ failure_code: 'analysis_failed',
+ ...(updatedFinding.session_id ? { session_id: updatedFinding.session_id } : {}),
+ },
+ });
+}
+
/**
* Clear analysis_status so a superseded finding no longer counts against
* the owner's concurrency cap in countRunningAnalyses().
diff --git a/apps/web/src/lib/security-agent/db/security-audit-report.test.ts b/apps/web/src/lib/security-agent/db/security-audit-report.test.ts
new file mode 100644
index 0000000000..0de563fb6a
--- /dev/null
+++ b/apps/web/src/lib/security-agent/db/security-audit-report.test.ts
@@ -0,0 +1,782 @@
+import { afterEach, describe, expect, it } from '@jest/globals';
+import { db, pool } from '@/lib/drizzle';
+import { insertTestUser } from '@/tests/helpers/user.helper';
+import { kilocode_users, security_audit_log } from '@kilocode/db/schema';
+import { SecurityAuditLogAction, SecurityAuditLogActorType } from '@kilocode/db/schema-types';
+import { inArray } from 'drizzle-orm';
+import { randomUUID } from 'node:crypto';
+import {
+ assertSecurityAgentAuditReportSerializedByteBudget,
+ buildSecurityAgentAuditReportFromRows,
+ defaultSecurityAgentAuditReportInput,
+ getSecurityAgentAuditReport,
+ normalizeSecurityAgentAuditReportPeriod,
+ resolveSecurityAgentAuditReliableCoverageStart,
+ SECURITY_AGENT_AUDIT_REPORT_MAX_EVENTS,
+ SECURITY_AGENT_AUDIT_REPORT_MAX_SERIALIZED_BYTES,
+ SECURITY_AGENT_AUDIT_REPORT_PAGE_SIZE,
+ securityAgentAuditReportSerializedByteLength,
+ securityAgentAuditReportEventCountBucket,
+ SecurityAgentAuditReportQueryError,
+ withSecurityAgentAuditReportTimeout,
+} from './security-audit-report';
+
+type AuditReportRow = Parameters[0]['rows'][number];
+
+process.env.SECURITY_AGENT_AUDIT_RELIABLE_COVERAGE_START = '2026-06-17T12:00:00.000Z';
+
+const period = normalizeSecurityAgentAuditReportPeriod(
+ { startDate: '2026-06-01', endDate: '2026-06-12' },
+ new Date('2026-06-12T15:00:00.000Z')
+);
+const integrationOwnerIds: string[] = [];
+
+async function createIntegrationOwner() {
+ const user = await insertTestUser();
+ integrationOwnerIds.push(user.id);
+ return {
+ type: 'user' as const,
+ id: user.id,
+ displayName: user.google_user_name ?? 'Test User',
+ };
+}
+
+function dateOnly(value: string): string {
+ return value.slice(0, 10);
+}
+
+function findingSnapshot(findingId: string): Record {
+ return {
+ finding_id: findingId,
+ source: 'dependabot',
+ source_id: '42',
+ repo_full_name: 'kilo/snapshot-report-test',
+ title: 'Snapshot report pagination test',
+ severity: 'high',
+ status: 'open',
+ };
+}
+
+async function waitForAuditReportReadBlockedBy(lockingPid: number): Promise {
+ const deadline = Date.now() + 5_000;
+ while (Date.now() < deadline) {
+ const result = await pool.query<{ waiting: boolean }>(
+ `SELECT EXISTS (
+ SELECT 1
+ FROM pg_locks
+ WHERE relation = 'security_audit_log'::regclass
+ AND NOT granted
+ AND $1::integer = ANY(pg_blocking_pids(pid))
+ ) AS waiting`,
+ [lockingPid]
+ );
+ if (result.rows[0]?.waiting) return;
+ await new Promise(resolve => setTimeout(resolve, 10));
+ }
+ throw new Error('Audit report read did not block on expected table lock');
+}
+
+afterEach(async () => {
+ if (integrationOwnerIds.length === 0) return;
+ await db.delete(kilocode_users).where(inArray(kilocode_users.id, integrationOwnerIds));
+ integrationOwnerIds.length = 0;
+});
+
+function row(overrides: Partial): AuditReportRow {
+ return {
+ id: '00000000-0000-4000-8000-000000000001',
+ action: SecurityAuditLogAction.FindingCreated,
+ actor_id: null,
+ actor_email: null,
+ actor_name: null,
+ actor_type: SecurityAuditLogActorType.System,
+ before_state: null,
+ after_state: null,
+ metadata: null,
+ created_at: '2026-06-01T10:00:00.000Z',
+ finding_id: '11111111-1111-4111-8111-111111111111',
+ resource_type: 'security_finding',
+ resource_id: '11111111-1111-4111-8111-111111111111',
+ occurred_at: '2026-06-01T10:00:00.000Z',
+ source_occurred_at: null,
+ finding_snapshot: {
+ finding_id: '11111111-1111-4111-8111-111111111111',
+ source: 'dependabot',
+ source_id: '42',
+ repo_full_name: 'kilo/repo',
+ title: 'Prototype Pollution in lodash',
+ severity: 'high',
+ status: 'open',
+ package_name: 'lodash',
+ package_ecosystem: 'npm',
+ first_detected_at: '2026-06-01T08:00:00.000Z',
+ sla_due_at: '2026-06-08T08:00:00.000Z',
+ },
+ effective_at: '2026-06-01T10:00:00.000Z',
+ ...overrides,
+ };
+}
+
+describe('resolveSecurityAgentAuditReliableCoverageStart', () => {
+ it('requires and normalizes deployment-time coverage configuration', () => {
+ expect(resolveSecurityAgentAuditReliableCoverageStart('2026-06-17T14:00:00+02:00')).toBe(
+ '2026-06-17T12:00:00.000Z'
+ );
+ expect(() => resolveSecurityAgentAuditReliableCoverageStart('')).toThrow(
+ 'SECURITY_AGENT_AUDIT_RELIABLE_COVERAGE_START is required'
+ );
+ expect(() => resolveSecurityAgentAuditReliableCoverageStart('2026-06-17')).toThrow(
+ 'SECURITY_AGENT_AUDIT_RELIABLE_COVERAGE_START must be an ISO timestamp'
+ );
+ });
+});
+
+describe('defaultSecurityAgentAuditReportInput', () => {
+ it('defaults to 90 UTC calendar days ending on the current day', () => {
+ expect(defaultSecurityAgentAuditReportInput(new Date('2026-06-16T21:08:07+02:00'))).toEqual({
+ startDate: '2026-03-19',
+ endDate: '2026-06-16',
+ });
+ });
+});
+
+describe('normalizeSecurityAgentAuditReportPeriod', () => {
+ it('uses inclusive UTC end date and exclusive next-day boundary', () => {
+ const normalized = normalizeSecurityAgentAuditReportPeriod(
+ { startDate: '2026-06-12', endDate: '2026-06-12' },
+ new Date('2026-06-12T15:00:00.000Z')
+ );
+
+ expect(normalized).toEqual({
+ start: '2026-06-12T00:00:00.000Z',
+ endExclusive: '2026-06-13T00:00:00.000Z',
+ displayEnd: '2026-06-12',
+ timeZone: 'UTC',
+ });
+ });
+
+ it('accepts a range of exactly 90 inclusive UTC calendar days', () => {
+ const normalized = normalizeSecurityAgentAuditReportPeriod(
+ { startDate: '2026-03-15', endDate: '2026-06-12' },
+ new Date('2026-06-12T15:00:00.000Z')
+ );
+
+ expect(normalized.start).toBe('2026-03-15T00:00:00.000Z');
+ expect(normalized.endExclusive).toBe('2026-06-13T00:00:00.000Z');
+ });
+
+ it('rejects reversed, future, invalid, and over-limit ranges', () => {
+ const now = new Date('2026-06-12T15:00:00.000Z');
+
+ expect(() =>
+ normalizeSecurityAgentAuditReportPeriod(
+ { startDate: '2026-06-12', endDate: '2026-06-11' },
+ now
+ )
+ ).toThrow('start date');
+ expect(() =>
+ normalizeSecurityAgentAuditReportPeriod(
+ { startDate: '2026-06-12', endDate: '2026-06-13' },
+ now
+ )
+ ).toThrow('future');
+ expect(() =>
+ normalizeSecurityAgentAuditReportPeriod(
+ { startDate: '2026-02-30', endDate: '2026-03-01' },
+ now
+ )
+ ).toThrow('valid UTC calendar date');
+ expect(() =>
+ normalizeSecurityAgentAuditReportPeriod(
+ { startDate: '2026-03-14', endDate: '2026-06-12' },
+ now
+ )
+ ).toThrow('90 inclusive');
+ });
+});
+
+describe('getSecurityAgentAuditReport', () => {
+ it('scans more than one page of same-timestamp events without gaps or duplicates', async () => {
+ const owner = await createIntegrationOwner();
+ const findingId = randomUUID();
+ const occurredAt = new Date(Date.now() - 1_000).toISOString();
+ const eventIds = Array.from({ length: SECURITY_AGENT_AUDIT_REPORT_PAGE_SIZE + 1 }, () =>
+ randomUUID()
+ );
+
+ await db.insert(security_audit_log).values(
+ eventIds.map(id => ({
+ id,
+ owned_by_user_id: owner.id,
+ actor_type: SecurityAuditLogActorType.System,
+ action: SecurityAuditLogAction.FindingCreated,
+ resource_type: 'security_finding',
+ resource_id: findingId,
+ finding_id: findingId,
+ occurred_at: occurredAt,
+ finding_snapshot: findingSnapshot(findingId),
+ }))
+ );
+
+ const report = await getSecurityAgentAuditReport({
+ owner,
+ input: { startDate: dateOnly(occurredAt), endDate: dateOnly(occurredAt) },
+ isRequestingUserKiloAdmin: false,
+ });
+ const scannedEventIds = report.findings.flatMap(finding =>
+ finding.events.map(event => event.id)
+ );
+
+ expect(report.summary.activityCount).toBe(SECURITY_AGENT_AUDIT_REPORT_PAGE_SIZE + 1);
+ expect(scannedEventIds).toEqual([...eventIds].sort());
+ expect(new Set(scannedEventIds).size).toBe(SECURITY_AGENT_AUDIT_REPORT_PAGE_SIZE + 1);
+ }, 20_000);
+
+ it('excludes an event committed after its snapshot and includes it in a fresh report', async () => {
+ const owner = await createIntegrationOwner();
+ const findingId = randomUUID();
+ const initialEventId = randomUUID();
+ const lateEventId = randomUUID();
+ const occurredAt = new Date(Date.now() - 1_000).toISOString();
+ const input = { startDate: dateOnly(occurredAt), endDate: dateOnly(occurredAt) };
+
+ await db.insert(security_audit_log).values({
+ id: initialEventId,
+ owned_by_user_id: owner.id,
+ actor_type: SecurityAuditLogActorType.System,
+ action: SecurityAuditLogAction.FindingCreated,
+ resource_type: 'security_finding',
+ resource_id: findingId,
+ finding_id: findingId,
+ occurred_at: occurredAt,
+ finding_snapshot: findingSnapshot(findingId),
+ });
+
+ const lockClient = await pool.connect();
+ let firstReportPromise: ReturnType | null = null;
+ try {
+ await lockClient.query('BEGIN');
+ await lockClient.query('LOCK TABLE security_audit_log IN ACCESS EXCLUSIVE MODE');
+ const pidResult = await lockClient.query<{ pid: number }>('SELECT pg_backend_pid() AS pid');
+ const lockingPid = pidResult.rows[0]?.pid;
+ if (lockingPid === undefined) throw new Error('Could not determine locking database process');
+
+ await lockClient.query(
+ `INSERT INTO security_audit_log (
+ id,
+ owned_by_user_id,
+ actor_type,
+ action,
+ resource_type,
+ resource_id,
+ finding_id,
+ occurred_at,
+ finding_snapshot
+ ) VALUES ($1, $2, $3, $4, 'security_finding', $5::text, $5::uuid, $6, $7::jsonb)`,
+ [
+ lateEventId,
+ owner.id,
+ SecurityAuditLogActorType.System,
+ SecurityAuditLogAction.FindingCreated,
+ findingId,
+ occurredAt,
+ JSON.stringify(findingSnapshot(findingId)),
+ ]
+ );
+
+ firstReportPromise = getSecurityAgentAuditReport({
+ owner,
+ input,
+ isRequestingUserKiloAdmin: false,
+ });
+ await waitForAuditReportReadBlockedBy(lockingPid);
+ await lockClient.query('COMMIT');
+
+ const firstReport = await firstReportPromise;
+ const freshReport = await getSecurityAgentAuditReport({
+ owner,
+ input,
+ isRequestingUserKiloAdmin: false,
+ });
+ const firstEventIds = firstReport.findings.flatMap(finding =>
+ finding.events.map(event => event.id)
+ );
+ const freshEventIds = freshReport.findings.flatMap(finding =>
+ finding.events.map(event => event.id)
+ );
+
+ expect(firstEventIds).toEqual([initialEventId]);
+ expect(freshEventIds).toEqual([initialEventId, lateEventId].sort());
+ } finally {
+ await lockClient.query('ROLLBACK').catch(() => undefined);
+ lockClient.release();
+ if (firstReportPromise) await firstReportPromise.catch(() => undefined);
+ }
+ }, 20_000);
+});
+
+describe('buildSecurityAgentAuditReportFromRows', () => {
+ it('builds an empty report when no reportable activity exists', () => {
+ const report = buildSecurityAgentAuditReportFromRows({
+ owner: {
+ type: 'organization',
+ id: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',
+ displayName: 'Acme',
+ },
+ period,
+ generatedAt: '2026-06-12T15:00:00.000Z',
+ dataThrough: '2026-06-12T15:00:00.000Z',
+ isRequestingUserKiloAdmin: false,
+ rows: [],
+ });
+
+ expect(report.summary).toEqual({
+ findingCount: 0,
+ activityCount: 0,
+ bySeverity: {
+ critical: 0,
+ high: 0,
+ medium: 0,
+ low: 0,
+ },
+ byAction: {},
+ });
+ expect(report.findings).toEqual([]);
+ });
+
+ it('groups events deterministically, masks internal actors, and labels legacy rows', () => {
+ const report = buildSecurityAgentAuditReportFromRows({
+ owner: {
+ type: 'organization',
+ id: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',
+ displayName: 'Acme',
+ },
+ period,
+ generatedAt: '2026-06-12T15:00:00.000Z',
+ dataThrough: '2026-06-12T15:00:00.000Z',
+ isRequestingUserKiloAdmin: false,
+ rows: [
+ row({
+ id: '00000000-0000-4000-8000-000000000003',
+ action: SecurityAuditLogAction.FindingDismissed,
+ actor_id: 'admin-user',
+ actor_email: 'ops@kilocode.ai',
+ actor_name: 'Ops User',
+ actor_type: SecurityAuditLogActorType.KiloAdmin,
+ after_state: { status: 'ignored', token: 'secret-token' },
+ metadata: { reason_code: 'not_used', actor_email: 'ops@kilocode.ai' },
+ occurred_at: '2026-06-02T10:00:00.000Z',
+ effective_at: '2026-06-02T10:00:00.000Z',
+ finding_snapshot: {
+ finding_id: '11111111-1111-4111-8111-111111111111',
+ source: 'dependabot',
+ source_id: '42',
+ repo_full_name: 'kilo/repo',
+ title: 'Prototype Pollution in lodash',
+ severity: 'high',
+ status: 'ignored',
+ fixed_at: '2026-06-07T08:00:00.000Z',
+ sla_due_at: '2026-06-08T08:00:00.000Z',
+ },
+ }),
+ row({
+ id: '00000000-0000-4000-8000-000000000002',
+ action: SecurityAuditLogAction.FindingCreated,
+ occurred_at: '2026-06-01T10:00:00.000Z',
+ effective_at: '2026-06-01T10:00:00.000Z',
+ }),
+ row({
+ id: '00000000-0000-4000-8000-000000000004',
+ action: SecurityAuditLogAction.FindingDismissed,
+ finding_id: null,
+ resource_id: '22222222-2222-4222-8222-222222222222',
+ occurred_at: null,
+ effective_at: '2026-06-03T10:00:00.000Z',
+ finding_snapshot: null,
+ }),
+ ],
+ });
+
+ expect(report.summary).toMatchObject({
+ findingCount: 2,
+ activityCount: 3,
+ });
+ expect(report.findings[0].findingId).toBe('11111111-1111-4111-8111-111111111111');
+ expect(report.findings[0].events.map(event => event.id)).toEqual([
+ '00000000-0000-4000-8000-000000000002',
+ '00000000-0000-4000-8000-000000000003',
+ ]);
+ expect(report.findings[0].events[1].actor).toEqual({
+ type: 'user',
+ id: '00000000-0000-0000-0000-000000000000',
+ displayName: 'Kilo Admin',
+ masked: true,
+ });
+ expect(report.findings[0].events[1].afterState).toEqual({ status: 'ignored' });
+ expect(report.findings[0].events[1].metadata).toEqual({ reason_code: 'not_used' });
+ expect(report.findings[0].sla.status).toBe('terminal_met');
+ expect(report.findings[1]).toMatchObject({
+ findingId: '22222222-2222-4222-8222-222222222222',
+ title: 'Legacy Security Finding',
+ hasLegacySupplementalActivity: true,
+ });
+ expect(report.hasLegacySupplementalActivity).toBe(true);
+ });
+
+ it('masks actors from persisted classification without inferring from email', () => {
+ const rows = [
+ row({
+ id: '00000000-0000-4000-8000-000000000011',
+ actor_id: 'admin-user',
+ actor_email: 'operator@example.com',
+ actor_name: 'Internal Operator',
+ actor_type: SecurityAuditLogActorType.KiloAdmin,
+ }),
+ row({
+ id: '00000000-0000-4000-8000-000000000012',
+ actor_id: 'customer-user',
+ actor_email: 'customer@kilocode.ai',
+ actor_name: 'Customer User',
+ actor_type: SecurityAuditLogActorType.CustomerUser,
+ }),
+ row({
+ id: '00000000-0000-4000-8000-000000000013',
+ actor_id: 'legacy-user',
+ actor_email: 'legacy@example.com',
+ actor_name: 'Legacy User',
+ actor_type: null,
+ }),
+ ];
+ const baseParams = {
+ owner: {
+ type: 'organization' as const,
+ id: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',
+ displayName: 'Acme',
+ },
+ period,
+ generatedAt: '2026-06-12T15:00:00.000Z',
+ dataThrough: '2026-06-12T15:00:00.000Z',
+ rows,
+ };
+
+ const customerReport = buildSecurityAgentAuditReportFromRows({
+ ...baseParams,
+ isRequestingUserKiloAdmin: false,
+ });
+ expect(customerReport.findings[0].events.map(event => event.actor)).toEqual([
+ {
+ type: 'user',
+ id: '00000000-0000-0000-0000-000000000000',
+ displayName: 'Kilo Admin',
+ masked: true,
+ },
+ {
+ type: 'user',
+ id: 'customer-user',
+ displayName: 'Customer User',
+ masked: false,
+ },
+ {
+ type: 'user',
+ id: '00000000-0000-0000-0000-000000000000',
+ displayName: 'Masked user',
+ masked: true,
+ },
+ ]);
+
+ const adminReport = buildSecurityAgentAuditReportFromRows({
+ ...baseParams,
+ isRequestingUserKiloAdmin: true,
+ });
+ expect(adminReport.findings[0].events[0].actor).toEqual({
+ type: 'user',
+ id: 'admin-user',
+ displayName: 'Internal Operator',
+ masked: false,
+ });
+ expect(adminReport.findings[0].events[2].actor).toEqual({
+ type: 'user',
+ id: 'legacy-user',
+ displayName: 'Legacy User',
+ masked: false,
+ });
+ });
+
+ it('uses earlier recorded timeline evidence when a later legacy snapshot is sparse', () => {
+ const report = buildSecurityAgentAuditReportFromRows({
+ owner: {
+ type: 'user',
+ id: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',
+ displayName: 'Ada',
+ },
+ period,
+ generatedAt: '2026-06-12T15:00:00.000Z',
+ dataThrough: '2026-06-12T15:00:00.000Z',
+ isRequestingUserKiloAdmin: false,
+ rows: [
+ row({}),
+ row({
+ id: '00000000-0000-4000-8000-000000000002',
+ action: SecurityAuditLogAction.RemediationPrOpened,
+ occurred_at: '2026-06-02T10:00:00.000Z',
+ effective_at: '2026-06-02T10:00:00.000Z',
+ finding_snapshot: {
+ finding_id: '11111111-1111-4111-8111-111111111111',
+ source: 'dependabot',
+ source_id: '42',
+ repo_full_name: 'kilo/repo',
+ title: 'Prototype Pollution in lodash',
+ severity: 'high',
+ status: 'open',
+ },
+ }),
+ ],
+ });
+
+ expect(report.findings[0].firstDetectedAt).toBe('2026-06-01T08:00:00.000Z');
+ expect(report.findings[0].sla).toEqual({
+ status: 'open_past_deadline',
+ deadline: '2026-06-08T08:00:00.000Z',
+ terminalAt: null,
+ });
+ });
+
+ it('does not reuse an earlier SLA deadline after a later snapshot records no deadline', () => {
+ const report = buildSecurityAgentAuditReportFromRows({
+ owner: {
+ type: 'user',
+ id: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',
+ displayName: 'Ada',
+ },
+ period,
+ generatedAt: '2026-06-12T15:00:00.000Z',
+ dataThrough: '2026-06-12T15:00:00.000Z',
+ isRequestingUserKiloAdmin: false,
+ rows: [
+ row({}),
+ row({
+ id: '00000000-0000-4000-8000-000000000002',
+ action: SecurityAuditLogAction.RemediationPrOpened,
+ occurred_at: '2026-06-02T10:00:00.000Z',
+ effective_at: '2026-06-02T10:00:00.000Z',
+ finding_snapshot: {
+ finding_id: '11111111-1111-4111-8111-111111111111',
+ source: 'dependabot',
+ source_id: '42',
+ repo_full_name: 'kilo/repo',
+ title: 'Prototype Pollution in lodash',
+ severity: 'high',
+ status: 'open',
+ first_detected_at: '2026-06-01T08:00:00.000Z',
+ fixed_at: null,
+ sla_due_at: null,
+ },
+ }),
+ ],
+ });
+
+ expect(report.findings[0].sla).toEqual({
+ status: 'unknown',
+ deadline: null,
+ reason: 'missing_recorded_deadline',
+ });
+ });
+
+ it('publishes structured extraction status without raw analysis content', () => {
+ const report = buildSecurityAgentAuditReportFromRows({
+ owner: {
+ type: 'user',
+ id: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',
+ displayName: 'Ada',
+ },
+ period,
+ generatedAt: '2026-06-12T15:00:00.000Z',
+ dataThrough: '2026-06-12T15:00:00.000Z',
+ isRequestingUserKiloAdmin: false,
+ rows: [
+ row({
+ action: SecurityAuditLogAction.FindingAnalysisCompleted,
+ after_state: {
+ analysis_status: 'completed',
+ structured_extraction_status: 'failed',
+ suggested_action: 'manual_review',
+ raw_markdown: '# Sensitive raw analysis',
+ },
+ metadata: {
+ model_slug: 'analysis/model',
+ },
+ }),
+ ],
+ });
+
+ expect(report.findings[0].events[0]).toMatchObject({
+ afterState: {
+ analysis_status: 'completed',
+ structured_extraction_status: 'failed',
+ suggested_action: 'manual_review',
+ },
+ metadata: null,
+ });
+ expect(report.findings[0].events[0].afterState).not.toHaveProperty('raw_markdown');
+ });
+
+ it('publishes user-facing evidence while omitting internal remediation identifiers', () => {
+ const report = buildSecurityAgentAuditReportFromRows({
+ owner: {
+ type: 'user',
+ id: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',
+ displayName: 'Ada',
+ },
+ period,
+ generatedAt: '2026-06-12T15:00:00.000Z',
+ dataThrough: '2026-06-12T15:00:00.000Z',
+ isRequestingUserKiloAdmin: false,
+ rows: [
+ row({
+ action: SecurityAuditLogAction.RemediationQueued,
+ actor_id: '4d857fd4-70b3-48a2-9130-45873d3051c4',
+ actor_type: SecurityAuditLogActorType.CustomerUser,
+ after_state: {
+ remediation_status: 'queued',
+ attempt_number: 1,
+ remediation_id: '3ade7a41-97de-4089-a331-2a6f3e5ad448',
+ },
+ metadata: {
+ origin: 'manual',
+ attempt_id: '7b04b2bc-07c2-4252-bf9f-4dffe03cc7cb',
+ branch_name: 'security-remediation/internal-branch',
+ remediation_model_slug: 'kilo-auto/balanced',
+ },
+ }),
+ ],
+ });
+
+ expect(report.findings[0].events[0]).toMatchObject({
+ actor: {
+ type: 'user',
+ displayName: 'Kilo user',
+ },
+ afterState: {
+ remediation_status: 'queued',
+ attempt_number: 1,
+ },
+ metadata: {
+ origin: 'manual',
+ },
+ });
+ expect(report.findings[0].events[0].afterState).not.toHaveProperty('remediation_id');
+ expect(report.findings[0].events[0].metadata).not.toHaveProperty('attempt_id');
+ expect(report.findings[0].events[0].metadata).not.toHaveProperty('branch_name');
+ expect(report.findings[0].events[0].metadata).not.toHaveProperty('remediation_model_slug');
+ });
+
+ it('builds a max-event report under the serialized byte budget without truncation', () => {
+ const rows = Array.from({ length: SECURITY_AGENT_AUDIT_REPORT_MAX_EVENTS }, (_, index) => {
+ const occurredAt = new Date(Date.UTC(2026, 5, 1, 0, 0, index)).toISOString();
+ return row({
+ id: `00000000-0000-4000-8000-${String(index).padStart(12, '0')}`,
+ action:
+ index % 2 === 0
+ ? SecurityAuditLogAction.FindingCreated
+ : SecurityAuditLogAction.FindingStatusChange,
+ before_state: index % 2 === 0 ? null : { status: 'open' },
+ after_state: index % 2 === 0 ? { status: 'open' } : { status: 'fixed' },
+ metadata: { reason_code: 'load_test' },
+ occurred_at: occurredAt,
+ effective_at: occurredAt,
+ });
+ });
+
+ const report = buildSecurityAgentAuditReportFromRows({
+ owner: {
+ type: 'organization',
+ id: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',
+ displayName: 'Acme',
+ },
+ period,
+ generatedAt: '2026-06-12T15:00:00.000Z',
+ dataThrough: '2026-06-12T15:00:00.000Z',
+ isRequestingUserKiloAdmin: false,
+ rows,
+ });
+
+ expect(report.summary.activityCount).toBe(SECURITY_AGENT_AUDIT_REPORT_MAX_EVENTS);
+ expect(report.findings[0].events).toHaveLength(SECURITY_AGENT_AUDIT_REPORT_MAX_EVENTS);
+ expect(securityAgentAuditReportSerializedByteLength(report)).toBeLessThanOrEqual(
+ SECURITY_AGENT_AUDIT_REPORT_MAX_SERIALIZED_BYTES
+ );
+ expect(() => assertSecurityAgentAuditReportSerializedByteBudget(report)).not.toThrow();
+ });
+
+ it('does not classify ignored or superseded findings as open SLA states', () => {
+ const report = buildSecurityAgentAuditReportFromRows({
+ owner: {
+ type: 'organization',
+ id: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',
+ displayName: 'Acme',
+ },
+ period,
+ generatedAt: '2026-06-02T15:00:00.000Z',
+ dataThrough: '2026-06-02T15:00:00.000Z',
+ isRequestingUserKiloAdmin: false,
+ rows: [
+ row({
+ action: SecurityAuditLogAction.FindingSuperseded,
+ occurred_at: '2026-06-02T10:00:00.000Z',
+ effective_at: '2026-06-02T10:00:00.000Z',
+ finding_snapshot: {
+ finding_id: '11111111-1111-4111-8111-111111111111',
+ source: 'dependabot',
+ source_id: '42',
+ repo_full_name: 'kilo/repo',
+ title: 'Prototype Pollution in lodash',
+ severity: 'high',
+ status: 'ignored',
+ sla_due_at: '2026-06-08T08:00:00.000Z',
+ canonical_finding_id: '22222222-2222-4222-8222-222222222222',
+ },
+ }),
+ ],
+ });
+
+ expect(report.findings[0].sla).toEqual({
+ status: 'unknown',
+ deadline: '2026-06-08T08:00:00.000Z',
+ reason: 'ignored_or_superseded_without_terminal_time',
+ });
+ });
+});
+
+describe('securityAgentAuditReportEventCountBucket', () => {
+ it('returns stable non-PII telemetry buckets', () => {
+ expect(securityAgentAuditReportEventCountBucket(null)).toBe('unknown');
+ expect(securityAgentAuditReportEventCountBucket(0)).toBe('0');
+ expect(securityAgentAuditReportEventCountBucket(99)).toBe('1-99');
+ expect(securityAgentAuditReportEventCountBucket(999)).toBe('100-999');
+ expect(securityAgentAuditReportEventCountBucket(4_999)).toBe('1000-4999');
+ expect(securityAgentAuditReportEventCountBucket(10_000)).toBe('5000-10000');
+ expect(securityAgentAuditReportEventCountBucket(10_001)).toBe('over-budget');
+ });
+});
+
+describe('withSecurityAgentAuditReportTimeout', () => {
+ it('fails with report query error and stage when budget expires', async () => {
+ await expect(
+ withSecurityAgentAuditReportTimeout(new Promise(() => {}), 1, 'scan')
+ ).rejects.toMatchObject({
+ name: 'SecurityAgentAuditReportQueryError',
+ message: 'Report query did not finish',
+ stage: 'scan',
+ });
+ });
+
+ it('returns resolved value before budget expires', async () => {
+ await expect(
+ withSecurityAgentAuditReportTimeout(Promise.resolve('ok'), 50, 'scan')
+ ).resolves.toBe('ok');
+ });
+
+ it('preserves exported error type for timeout callers', () => {
+ expect(new SecurityAgentAuditReportQueryError('x', 'request').stage).toBe('request');
+ });
+});
diff --git a/apps/web/src/lib/security-agent/db/security-audit-report.ts b/apps/web/src/lib/security-agent/db/security-audit-report.ts
new file mode 100644
index 0000000000..6457472d4d
--- /dev/null
+++ b/apps/web/src/lib/security-agent/db/security-audit-report.ts
@@ -0,0 +1,994 @@
+import 'server-only';
+import { captureException, startSpan } from '@sentry/nextjs';
+import { security_audit_log } from '@kilocode/db/schema';
+import {
+ SecurityAuditLogAction,
+ SecurityAuditLogActorType,
+ SecuritySeverity,
+} from '@kilocode/db/schema-types';
+import { REPORTABLE_SECURITY_FINDING_AUDIT_ACTIONS } from '@kilocode/worker-utils/security-finding-audit';
+import { and, asc, count, eq, gt, gte, inArray, isNotNull, lt, lte, or, sql } from 'drizzle-orm';
+import * as z from 'zod';
+import { db, type DrizzleTransaction } from '@/lib/drizzle';
+
+export const SECURITY_AGENT_AUDIT_REPORT_VERSION = 1;
+export const SECURITY_AGENT_AUDIT_REPORT_PAGE_SIZE = 1000;
+export const SECURITY_AGENT_AUDIT_REPORT_MAX_EVENTS = 10_000;
+export const SECURITY_AGENT_AUDIT_REPORT_MAX_SERIALIZED_BYTES = 8 * 1024 * 1024;
+export const SECURITY_AGENT_AUDIT_REPORT_REQUEST_TIMEOUT_MS = 25_000;
+export const SECURITY_AGENT_AUDIT_REPORT_QUERY_TIMEOUT_MS = 8_000;
+
+const DateOnlySchema = z.string().regex(/^\d{4}-\d{2}-\d{2}$/);
+
+export const SecurityAgentAuditReportInputSchema = z.object({
+ startDate: DateOnlySchema.optional(),
+ endDate: DateOnlySchema.optional(),
+});
+
+const ReliableCoverageStartSchema = z.string().datetime({ offset: true });
+
+export function resolveSecurityAgentAuditReliableCoverageStart(
+ value = process.env.SECURITY_AGENT_AUDIT_RELIABLE_COVERAGE_START
+): string {
+ if (!value) {
+ throw new Error('SECURITY_AGENT_AUDIT_RELIABLE_COVERAGE_START is required');
+ }
+
+ const parsed = ReliableCoverageStartSchema.safeParse(value);
+ if (!parsed.success) {
+ throw new Error('SECURITY_AGENT_AUDIT_RELIABLE_COVERAGE_START must be an ISO timestamp');
+ }
+
+ return new Date(parsed.data).toISOString();
+}
+
+export type SecurityAgentAuditReportInput = z.infer;
+
+export type SecurityAgentAuditReportOwner =
+ | { type: 'user'; id: string; displayName: string }
+ | { type: 'organization'; id: string; displayName: string };
+
+export type SecurityAgentAuditReportActor =
+ | {
+ type: 'user';
+ id: string | null;
+ displayName: string;
+ masked: boolean;
+ }
+ | { type: 'system'; displayName: string; masked: false };
+
+export type SecurityAgentAuditReportEvent = {
+ id: string;
+ action: SecurityAuditLogAction;
+ label: string;
+ occurredAt: string;
+ sourceOccurredAt: string | null;
+ recordedAt: string;
+ actor: SecurityAgentAuditReportActor;
+ beforeState: Record | null;
+ afterState: Record | null;
+ metadata: Record | null;
+ legacySupplemental: boolean;
+};
+
+export type SecurityAgentAuditSlaEvidence =
+ | { status: 'unknown'; deadline: string | null; reason: string }
+ | {
+ status: 'terminal_met' | 'terminal_missed' | 'open_within_deadline' | 'open_past_deadline';
+ deadline: string;
+ terminalAt: string | null;
+ };
+
+export type SecurityFindingAuditSection = {
+ findingId: string;
+ source: string | null;
+ sourceId: string | null;
+ repository: string | null;
+ title: string;
+ severity: SecuritySeverity | 'unknown';
+ status: string | null;
+ packageName: string | null;
+ packageEcosystem: string | null;
+ manifestPath: string | null;
+ patchedVersion: string | null;
+ ghsaId: string | null;
+ cveId: string | null;
+ cweIds: string[];
+ cvssScore: string | number | null;
+ dependabotUrl: string | null;
+ firstDetectedAt: string | null;
+ canonicalFindingId: string | null;
+ deleted: boolean;
+ sla: SecurityAgentAuditSlaEvidence;
+ events: SecurityAgentAuditReportEvent[];
+ hasLegacySupplementalActivity: boolean;
+};
+
+export type SecurityAgentAuditReport = {
+ reportVersion: typeof SECURITY_AGENT_AUDIT_REPORT_VERSION;
+ owner: SecurityAgentAuditReportOwner;
+ period: {
+ start: string;
+ endExclusive: string;
+ displayEnd: string;
+ timeZone: 'UTC';
+ };
+ generatedAt: string;
+ dataThrough: string;
+ reliableCoverageStart: string;
+ evidenceBasis: 'recorded_by_kilo';
+ hasLegacySupplementalActivity: boolean;
+ summary: {
+ findingCount: number;
+ activityCount: number;
+ bySeverity: Record;
+ byAction: Record;
+ };
+ findings: SecurityFindingAuditSection[];
+};
+
+type NormalizedAuditReportPeriod = SecurityAgentAuditReport['period'];
+
+type AuditReportRow = {
+ id: string;
+ action: SecurityAuditLogAction;
+ actor_id: string | null;
+ actor_email: string | null;
+ actor_name: string | null;
+ actor_type: SecurityAuditLogActorType | null;
+ before_state: Record | null;
+ after_state: Record | null;
+ metadata: Record | null;
+ created_at: string;
+ finding_id: string | null;
+ resource_type: string;
+ resource_id: string;
+ occurred_at: string | null;
+ source_occurred_at: string | null;
+ finding_snapshot: Record | null;
+ effective_at: string;
+};
+
+type AuditReportCursor = {
+ effectiveAt: string;
+ id: string;
+};
+
+type SecurityAgentAuditReportFailureStage =
+ | 'data_through'
+ | 'count'
+ | 'budget'
+ | 'scan'
+ | 'request';
+
+const ACTION_LABELS: Partial> = {
+ [SecurityAuditLogAction.FindingCreated]: 'Imported',
+ [SecurityAuditLogAction.FindingSeverityChanged]: 'Severity changed',
+ [SecurityAuditLogAction.FindingStatusChange]: 'Status changed',
+ [SecurityAuditLogAction.FindingDismissed]: 'Dismissed',
+ [SecurityAuditLogAction.FindingAutoDismissed]: 'Auto dismissed',
+ [SecurityAuditLogAction.FindingSuperseded]: 'Superseded',
+ [SecurityAuditLogAction.FindingAnalysisCompleted]: 'Analysis completed',
+ [SecurityAuditLogAction.FindingAnalysisFailed]: 'Analysis failed',
+ [SecurityAuditLogAction.RemediationQueued]: 'Remediation requested',
+ [SecurityAuditLogAction.RemediationPrOpened]: 'PR opened',
+ [SecurityAuditLogAction.RemediationFailed]: 'Remediation failed',
+ [SecurityAuditLogAction.RemediationBlocked]: 'Remediation blocked',
+ [SecurityAuditLogAction.RemediationNoChangesNeeded]: 'No changes needed',
+ [SecurityAuditLogAction.RemediationCancelled]: 'Cancelled',
+ [SecurityAuditLogAction.FindingDeleted]: 'Deleted',
+};
+
+const EMPTY_SEVERITY_COUNTS = {
+ [SecuritySeverity.CRITICAL]: 0,
+ [SecuritySeverity.HIGH]: 0,
+ [SecuritySeverity.MEDIUM]: 0,
+ [SecuritySeverity.LOW]: 0,
+} satisfies Record;
+
+type AuditEventEvidenceFields = {
+ beforeState: readonly string[];
+ afterState: readonly string[];
+ metadata: readonly string[];
+};
+
+const ACTION_EVIDENCE_FIELDS: Partial> = {
+ [SecurityAuditLogAction.FindingCreated]: {
+ beforeState: [],
+ afterState: ['status', 'severity'],
+ metadata: ['source_alert_number'],
+ },
+ [SecurityAuditLogAction.FindingSeverityChanged]: {
+ beforeState: ['severity'],
+ afterState: ['severity'],
+ metadata: ['source_alert_number'],
+ },
+ [SecurityAuditLogAction.FindingStatusChange]: {
+ beforeState: ['status'],
+ afterState: ['status', 'fixed_at', 'reason_code'],
+ metadata: ['source_alert_number', 'source_state'],
+ },
+ [SecurityAuditLogAction.FindingDismissed]: {
+ beforeState: ['status'],
+ afterState: ['status', 'reason_code'],
+ metadata: ['reason_code', 'source_alert_number'],
+ },
+ [SecurityAuditLogAction.FindingAutoDismissed]: {
+ beforeState: ['status'],
+ afterState: ['status', 'reason_code'],
+ metadata: ['reason_code', 'source_alert_number'],
+ },
+ [SecurityAuditLogAction.FindingSuperseded]: {
+ beforeState: ['status'],
+ afterState: ['status', 'reason_code'],
+ metadata: ['source_alert_number'],
+ },
+ [SecurityAuditLogAction.FindingAnalysisCompleted]: {
+ beforeState: ['analysis_status'],
+ afterState: [
+ 'analysis_status',
+ 'structured_extraction_status',
+ 'suggested_action',
+ 'confidence',
+ 'is_exploitable',
+ ],
+ metadata: [],
+ },
+ [SecurityAuditLogAction.FindingAnalysisFailed]: {
+ beforeState: ['analysis_status'],
+ afterState: ['analysis_status'],
+ metadata: ['failure_code'],
+ },
+ [SecurityAuditLogAction.RemediationQueued]: {
+ beforeState: [],
+ afterState: ['remediation_status', 'attempt_number'],
+ metadata: ['origin'],
+ },
+ [SecurityAuditLogAction.RemediationPrOpened]: {
+ beforeState: ['remediation_status'],
+ afterState: ['remediation_status', 'pr_number', 'pr_draft'],
+ metadata: ['origin', 'pr_url', 'validation_count'],
+ },
+ [SecurityAuditLogAction.RemediationFailed]: {
+ beforeState: ['remediation_status'],
+ afterState: ['remediation_status', 'failure_code'],
+ metadata: ['origin', 'failure_code'],
+ },
+ [SecurityAuditLogAction.RemediationBlocked]: {
+ beforeState: ['remediation_status'],
+ afterState: ['remediation_status', 'blocked_reason_code'],
+ metadata: ['origin', 'blocked_reason_code'],
+ },
+ [SecurityAuditLogAction.RemediationNoChangesNeeded]: {
+ beforeState: ['remediation_status'],
+ afterState: ['remediation_status'],
+ metadata: ['origin'],
+ },
+ [SecurityAuditLogAction.RemediationCancelled]: {
+ beforeState: ['remediation_status'],
+ afterState: ['remediation_status'],
+ metadata: ['origin'],
+ },
+ [SecurityAuditLogAction.FindingDeleted]: {
+ beforeState: ['status'],
+ afterState: ['deleted'],
+ metadata: [],
+ },
+};
+
+const EMPTY_EVIDENCE_FIELDS = {
+ beforeState: [],
+ afterState: [],
+ metadata: [],
+} satisfies AuditEventEvidenceFields;
+
+const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
+
+export class SecurityAgentAuditReportQueryError extends Error {
+ constructor(
+ message = 'Report query did not finish',
+ readonly stage: SecurityAgentAuditReportFailureStage = 'request'
+ ) {
+ super(message);
+ this.name = 'SecurityAgentAuditReportQueryError';
+ }
+}
+
+export function defaultSecurityAgentAuditReportInput(
+ now = new Date()
+): Required {
+ const endDate = formatUtcDate(startOfUtcDay(now));
+ const startDate = formatUtcDate(addUtcDays(parseUtcDateOnly(endDate), -89));
+ return { startDate, endDate };
+}
+
+export function normalizeSecurityAgentAuditReportPeriod(
+ input: SecurityAgentAuditReportInput,
+ now = new Date()
+): NormalizedAuditReportPeriod {
+ const defaults = defaultSecurityAgentAuditReportInput(now);
+ const startDate = input.startDate ?? defaults.startDate;
+ const endDate = input.endDate ?? defaults.endDate;
+
+ DateOnlySchema.parse(startDate);
+ DateOnlySchema.parse(endDate);
+
+ const start = parseUtcDateOnly(startDate);
+ const endInclusive = parseUtcDateOnly(endDate);
+ const today = startOfUtcDay(now);
+
+ if (start.getTime() > endInclusive.getTime()) {
+ throw new Error('Report start date must be before or equal to end date');
+ }
+ if (endInclusive.getTime() > today.getTime()) {
+ throw new Error('Report end date cannot be in the future');
+ }
+
+ const inclusiveDays = utcDayDiff(start, endInclusive) + 1;
+ if (inclusiveDays > 90) {
+ throw new Error('Report range cannot exceed 90 inclusive UTC calendar days');
+ }
+
+ const endExclusive = addUtcDays(endInclusive, 1);
+ return {
+ start: start.toISOString(),
+ endExclusive: endExclusive.toISOString(),
+ displayEnd: endDate,
+ timeZone: 'UTC',
+ };
+}
+
+export async function getSecurityAgentAuditReport(params: {
+ owner: SecurityAgentAuditReportOwner;
+ input: SecurityAgentAuditReportInput;
+ isRequestingUserKiloAdmin: boolean;
+}): Promise {
+ const parsedInput = SecurityAgentAuditReportInputSchema.parse(params.input);
+ const period = normalizeSecurityAgentAuditReportPeriod(parsedInput);
+ const generatedAt = new Date().toISOString();
+ const startedAt = Date.now();
+ let eventCount: number | null = null;
+
+ return await startSpan(
+ {
+ name: 'security-agent.audit-report',
+ op: 'security_agent.report',
+ },
+ async span => {
+ span.setAttribute('security_agent.audit_report.owner_type', params.owner.type);
+ span.setAttribute(
+ 'security_agent.audit_report.report_version',
+ SECURITY_AGENT_AUDIT_REPORT_VERSION
+ );
+
+ try {
+ const report = await withSecurityAgentAuditReportTimeout(
+ assembleSecurityAgentAuditReport({
+ ...params,
+ period,
+ generatedAt,
+ onEventCount: count => {
+ eventCount = count;
+ },
+ }),
+ SECURITY_AGENT_AUDIT_REPORT_REQUEST_TIMEOUT_MS,
+ 'request'
+ );
+
+ span.setAttribute('security_agent.audit_report.duration_ms', Date.now() - startedAt);
+ span.setAttribute(
+ 'security_agent.audit_report.event_count_bucket',
+ securityAgentAuditReportEventCountBucket(report.summary.activityCount)
+ );
+ span.setAttribute('security_agent.audit_report.failure_stage', 'none');
+ return report;
+ } catch (error) {
+ const failureStage =
+ error instanceof SecurityAgentAuditReportQueryError ? error.stage : 'request';
+ const eventCountBucket = securityAgentAuditReportEventCountBucket(eventCount);
+ span.setAttribute('security_agent.audit_report.duration_ms', Date.now() - startedAt);
+ span.setAttribute('security_agent.audit_report.event_count_bucket', eventCountBucket);
+ span.setAttribute('security_agent.audit_report.failure_stage', failureStage);
+ captureException(error, {
+ tags: {
+ operation: 'security_agent.audit_report',
+ owner_type: params.owner.type,
+ report_version: String(SECURITY_AGENT_AUDIT_REPORT_VERSION),
+ failure_stage: failureStage,
+ event_count_bucket: eventCountBucket,
+ },
+ });
+ throw error;
+ }
+ }
+ );
+}
+
+async function assembleSecurityAgentAuditReport(params: {
+ owner: SecurityAgentAuditReportOwner;
+ period: NormalizedAuditReportPeriod;
+ generatedAt: string;
+ isRequestingUserKiloAdmin: boolean;
+ onEventCount: (eventCount: number) => void;
+}): Promise {
+ const { dataThrough, rows } = await db.transaction(
+ async tx => {
+ const dataThrough = await getDatabaseNow(tx);
+ const eventCount = await countReportEvents(tx, params.owner, params.period, dataThrough);
+ params.onEventCount(eventCount);
+
+ if (eventCount > SECURITY_AGENT_AUDIT_REPORT_MAX_EVENTS) {
+ throw new SecurityAgentAuditReportQueryError(
+ 'Report event count exceeds v1 tested budget',
+ 'budget'
+ );
+ }
+
+ const rows = await scanReportRows(tx, params.owner, params.period, dataThrough);
+ if (rows.length !== eventCount) {
+ throw new SecurityAgentAuditReportQueryError(
+ 'Report scan row count does not match counted events',
+ 'scan'
+ );
+ }
+
+ return { dataThrough, rows };
+ },
+ { isolationLevel: 'repeatable read', accessMode: 'read only' }
+ );
+
+ const report = buildSecurityAgentAuditReportFromRows({
+ owner: params.owner,
+ period: params.period,
+ generatedAt: params.generatedAt,
+ dataThrough,
+ rows,
+ isRequestingUserKiloAdmin: params.isRequestingUserKiloAdmin,
+ });
+ assertSecurityAgentAuditReportSerializedByteBudget(report);
+ return report;
+}
+
+export function buildSecurityAgentAuditReportFromRows(params: {
+ owner: SecurityAgentAuditReportOwner;
+ period: NormalizedAuditReportPeriod;
+ generatedAt: string;
+ dataThrough: string;
+ rows: AuditReportRow[];
+ isRequestingUserKiloAdmin: boolean;
+}): SecurityAgentAuditReport {
+ const groups = new Map();
+ for (const row of params.rows) {
+ const findingId = getRowFindingId(row);
+ if (!findingId) continue;
+ const existing = groups.get(findingId) ?? [];
+ existing.push(row);
+ groups.set(findingId, existing);
+ }
+
+ const findings = Array.from(groups.entries()).map(([findingId, rows]) =>
+ buildFindingSection(findingId, rows, params)
+ );
+
+ findings.sort((left, right) => {
+ const leftFirst = left.events[0]?.occurredAt ?? '';
+ const rightFirst = right.events[0]?.occurredAt ?? '';
+ return (
+ leftFirst.localeCompare(rightFirst) ||
+ (left.repository ?? '').localeCompare(right.repository ?? '') ||
+ left.title.localeCompare(right.title) ||
+ left.findingId.localeCompare(right.findingId)
+ );
+ });
+
+ const bySeverity = { ...EMPTY_SEVERITY_COUNTS };
+ for (const finding of findings) {
+ if (finding.severity !== 'unknown') bySeverity[finding.severity] += 1;
+ }
+
+ const byAction: Record = {};
+ for (const row of params.rows) {
+ byAction[row.action] = (byAction[row.action] ?? 0) + 1;
+ }
+
+ return {
+ reportVersion: SECURITY_AGENT_AUDIT_REPORT_VERSION,
+ owner: params.owner,
+ period: params.period,
+ generatedAt: params.generatedAt,
+ dataThrough: params.dataThrough,
+ reliableCoverageStart: resolveSecurityAgentAuditReliableCoverageStart(),
+ evidenceBasis: 'recorded_by_kilo',
+ hasLegacySupplementalActivity: findings.some(finding => finding.hasLegacySupplementalActivity),
+ summary: {
+ findingCount: findings.length,
+ activityCount: params.rows.length,
+ bySeverity,
+ byAction,
+ },
+ findings,
+ };
+}
+
+async function getDatabaseNow(tx: DrizzleTransaction): Promise {
+ const { rows } = await withSecurityAgentAuditReportTimeout(
+ tx.execute<{ data_through: string }>(sql`SELECT now() AS data_through`),
+ SECURITY_AGENT_AUDIT_REPORT_QUERY_TIMEOUT_MS,
+ 'data_through'
+ );
+ return new Date(rows[0].data_through).toISOString();
+}
+
+async function countReportEvents(
+ tx: DrizzleTransaction,
+ owner: SecurityAgentAuditReportOwner,
+ period: NormalizedAuditReportPeriod,
+ dataThrough: string
+): Promise {
+ try {
+ const [row] = await withSecurityAgentAuditReportTimeout(
+ tx
+ .select({ eventCount: count(security_audit_log.id) })
+ .from(security_audit_log)
+ .where(and(...baseReportConditions(owner, period, dataThrough))),
+ SECURITY_AGENT_AUDIT_REPORT_QUERY_TIMEOUT_MS,
+ 'count'
+ );
+ return row.eventCount;
+ } catch (error) {
+ if (error instanceof SecurityAgentAuditReportQueryError) throw error;
+ throw new SecurityAgentAuditReportQueryError(
+ error instanceof Error ? error.message : 'Report query did not finish',
+ 'count'
+ );
+ }
+}
+
+async function scanReportRows(
+ tx: DrizzleTransaction,
+ owner: SecurityAgentAuditReportOwner,
+ period: NormalizedAuditReportPeriod,
+ dataThrough: string
+): Promise {
+ const rows: AuditReportRow[] = [];
+ let cursor: AuditReportCursor | null = null;
+
+ while (true) {
+ const page = await scanReportPage(tx, owner, period, dataThrough, cursor);
+ rows.push(...page);
+
+ if (page.length < SECURITY_AGENT_AUDIT_REPORT_PAGE_SIZE) return rows;
+ const last = page[page.length - 1];
+ cursor = { effectiveAt: last.effective_at, id: last.id };
+ }
+}
+
+async function scanReportPage(
+ tx: DrizzleTransaction,
+ owner: SecurityAgentAuditReportOwner,
+ period: NormalizedAuditReportPeriod,
+ dataThrough: string,
+ cursor: AuditReportCursor | null
+): Promise {
+ const effectiveAt = reportEffectiveAtSql();
+ const whereConditions = baseReportConditions(owner, period, dataThrough);
+ if (cursor) {
+ const cursorCondition = or(
+ gt(effectiveAt, cursor.effectiveAt),
+ and(eq(effectiveAt, cursor.effectiveAt), gt(security_audit_log.id, cursor.id))
+ );
+ if (cursorCondition) whereConditions.push(cursorCondition);
+ }
+
+ try {
+ return await withSecurityAgentAuditReportTimeout(
+ tx
+ .select({
+ id: security_audit_log.id,
+ action: security_audit_log.action,
+ actor_id: security_audit_log.actor_id,
+ actor_email: security_audit_log.actor_email,
+ actor_name: security_audit_log.actor_name,
+ actor_type: security_audit_log.actor_type,
+ before_state: security_audit_log.before_state,
+ after_state: security_audit_log.after_state,
+ metadata: security_audit_log.metadata,
+ created_at: security_audit_log.created_at,
+ finding_id: security_audit_log.finding_id,
+ resource_type: security_audit_log.resource_type,
+ resource_id: security_audit_log.resource_id,
+ occurred_at: security_audit_log.occurred_at,
+ source_occurred_at: security_audit_log.source_occurred_at,
+ finding_snapshot: security_audit_log.finding_snapshot,
+ effective_at: effectiveAt,
+ })
+ .from(security_audit_log)
+ .where(and(...whereConditions))
+ .orderBy(asc(effectiveAt), asc(security_audit_log.id))
+ .limit(SECURITY_AGENT_AUDIT_REPORT_PAGE_SIZE),
+ SECURITY_AGENT_AUDIT_REPORT_QUERY_TIMEOUT_MS,
+ 'scan'
+ );
+ } catch (error) {
+ if (error instanceof SecurityAgentAuditReportQueryError) throw error;
+ throw new SecurityAgentAuditReportQueryError(
+ error instanceof Error ? error.message : 'Report query did not finish',
+ 'scan'
+ );
+ }
+}
+
+function baseReportConditions(
+ owner: SecurityAgentAuditReportOwner,
+ period: NormalizedAuditReportPeriod,
+ dataThrough: string
+) {
+ const effectiveAt = reportEffectiveAtSql();
+ const findingIdentityCondition = or(
+ isNotNull(security_audit_log.finding_id),
+ and(
+ eq(security_audit_log.resource_type, 'security_finding'),
+ sql`${security_audit_log.resource_id}::text ~ ${UUID_PATTERN.source}::text`
+ )
+ );
+ if (!findingIdentityCondition) {
+ throw new SecurityAgentAuditReportQueryError('Report finding identity query failed', 'scan');
+ }
+
+ return [
+ owner.type === 'user'
+ ? eq(security_audit_log.owned_by_user_id, owner.id)
+ : eq(security_audit_log.owned_by_organization_id, owner.id),
+ inArray(security_audit_log.action, [...REPORTABLE_SECURITY_FINDING_AUDIT_ACTIONS]),
+ gte(effectiveAt, period.start),
+ lt(effectiveAt, period.endExclusive),
+ lte(security_audit_log.created_at, dataThrough),
+ findingIdentityCondition,
+ ];
+}
+
+function reportEffectiveAtSql() {
+ return sql`COALESCE(${security_audit_log.occurred_at}, ${security_audit_log.created_at})`;
+}
+
+export async function withSecurityAgentAuditReportTimeout(
+ promise: Promise,
+ timeoutMs: number,
+ stage: SecurityAgentAuditReportFailureStage
+): Promise {
+ let timeoutId: ReturnType | null = null;
+ const timeoutPromise = new Promise((_, reject) => {
+ timeoutId = setTimeout(() => {
+ reject(new SecurityAgentAuditReportQueryError('Report query did not finish', stage));
+ }, timeoutMs);
+ });
+
+ try {
+ return await Promise.race([promise, timeoutPromise]);
+ } finally {
+ if (timeoutId !== null) clearTimeout(timeoutId);
+ }
+}
+
+export function securityAgentAuditReportEventCountBucket(eventCount: number | null): string {
+ if (eventCount === null) return 'unknown';
+ if (eventCount === 0) return '0';
+ if (eventCount < 100) return '1-99';
+ if (eventCount < 1_000) return '100-999';
+ if (eventCount < 5_000) return '1000-4999';
+ if (eventCount <= SECURITY_AGENT_AUDIT_REPORT_MAX_EVENTS) return '5000-10000';
+ return 'over-budget';
+}
+
+export function securityAgentAuditReportSerializedByteLength(
+ report: SecurityAgentAuditReport
+): number {
+ return Buffer.byteLength(JSON.stringify(report), 'utf8');
+}
+
+export function assertSecurityAgentAuditReportSerializedByteBudget(
+ report: SecurityAgentAuditReport
+): void {
+ if (
+ securityAgentAuditReportSerializedByteLength(report) >
+ SECURITY_AGENT_AUDIT_REPORT_MAX_SERIALIZED_BYTES
+ ) {
+ throw new SecurityAgentAuditReportQueryError(
+ 'Report serialized size exceeds v1 tested budget',
+ 'budget'
+ );
+ }
+}
+
+function buildFindingSection(
+ findingId: string,
+ rows: AuditReportRow[],
+ reportParams: {
+ dataThrough: string;
+ isRequestingUserKiloAdmin: boolean;
+ }
+): SecurityFindingAuditSection {
+ rows.sort(
+ (left, right) =>
+ left.effective_at.localeCompare(right.effective_at) || left.id.localeCompare(right.id)
+ );
+ const latestSnapshot = latestFindingSnapshot(rows);
+ const evidenceSnapshot = withRecordedTimelineEvidence(rows, latestSnapshot);
+ const events = rows.map(row => buildReportEvent(row, reportParams.isRequestingUserKiloAdmin));
+ const deleted = rows.some(row => row.action === SecurityAuditLogAction.FindingDeleted);
+ const legacySupplemental = rows.some(row => isLegacySupplementalRow(row));
+
+ return {
+ findingId,
+ source: stringFromSnapshot(latestSnapshot, 'source'),
+ sourceId: stringFromSnapshot(latestSnapshot, 'source_id'),
+ repository: stringFromSnapshot(latestSnapshot, 'repo_full_name'),
+ title: stringFromSnapshot(latestSnapshot, 'title') ?? 'Legacy Security Finding',
+ severity: severityFromSnapshot(latestSnapshot),
+ status: stringFromSnapshot(latestSnapshot, 'status'),
+ packageName: stringFromSnapshot(latestSnapshot, 'package_name'),
+ packageEcosystem: stringFromSnapshot(latestSnapshot, 'package_ecosystem'),
+ manifestPath: stringFromSnapshot(latestSnapshot, 'manifest_path'),
+ patchedVersion: stringFromSnapshot(latestSnapshot, 'patched_version'),
+ ghsaId: stringFromSnapshot(latestSnapshot, 'ghsa_id'),
+ cveId: stringFromSnapshot(latestSnapshot, 'cve_id'),
+ cweIds: stringArrayFromSnapshot(latestSnapshot, 'cwe_ids'),
+ cvssScore: cvssFromSnapshot(latestSnapshot),
+ dependabotUrl: safeUrl(stringFromSnapshot(latestSnapshot, 'dependabot_html_url')),
+ firstDetectedAt: stringFromSnapshot(evidenceSnapshot, 'first_detected_at'),
+ canonicalFindingId: stringFromSnapshot(latestSnapshot, 'canonical_finding_id'),
+ deleted,
+ sla: buildSlaEvidence(evidenceSnapshot, reportParams.dataThrough),
+ events,
+ hasLegacySupplementalActivity: legacySupplemental,
+ };
+}
+
+function buildReportEvent(
+ row: AuditReportRow,
+ isRequestingUserKiloAdmin: boolean
+): SecurityAgentAuditReportEvent {
+ const evidenceFields = ACTION_EVIDENCE_FIELDS[row.action] ?? EMPTY_EVIDENCE_FIELDS;
+ return {
+ id: row.id,
+ action: row.action,
+ label: ACTION_LABELS[row.action] ?? row.action,
+ occurredAt: new Date(row.effective_at).toISOString(),
+ sourceOccurredAt: row.source_occurred_at
+ ? new Date(row.source_occurred_at).toISOString()
+ : null,
+ recordedAt: new Date(row.created_at).toISOString(),
+ actor: buildReportActor(row, isRequestingUserKiloAdmin),
+ beforeState: selectReportEvidence(row.before_state, evidenceFields.beforeState),
+ afterState: selectReportEvidence(row.after_state, evidenceFields.afterState),
+ metadata: selectReportEvidence(row.metadata, evidenceFields.metadata),
+ legacySupplemental: isLegacySupplementalRow(row),
+ };
+}
+
+function buildReportActor(
+ row: Pick,
+ isRequestingUserKiloAdmin: boolean
+): SecurityAgentAuditReportActor {
+ if (row.actor_type === SecurityAuditLogActorType.System) {
+ return { type: 'system', displayName: 'Kilo system', masked: false };
+ }
+
+ const hasPersistedIdentity = Boolean(row.actor_id || row.actor_name);
+ if (row.actor_type === null && !hasPersistedIdentity) {
+ return { type: 'system', displayName: 'Kilo system', masked: false };
+ }
+
+ if (
+ !isRequestingUserKiloAdmin &&
+ (row.actor_type === SecurityAuditLogActorType.KiloAdmin || row.actor_type === null)
+ ) {
+ return {
+ type: 'user',
+ id: '00000000-0000-0000-0000-000000000000',
+ displayName:
+ row.actor_type === SecurityAuditLogActorType.KiloAdmin ? 'Kilo Admin' : 'Masked user',
+ masked: true,
+ };
+ }
+
+ return {
+ type: 'user',
+ id: row.actor_id,
+ displayName: row.actor_name ?? (row.actor_id ? 'Kilo user' : 'Unknown user'),
+ masked: false,
+ };
+}
+
+function latestFindingSnapshot(rows: AuditReportRow[]): Record | null {
+ for (let index = rows.length - 1; index >= 0; index -= 1) {
+ const snapshot = rows[index].finding_snapshot;
+ if (snapshot) return snapshot;
+ }
+ return null;
+}
+
+function withRecordedTimelineEvidence(
+ rows: AuditReportRow[],
+ latestSnapshot: Record | null
+): Record | null {
+ if (!latestSnapshot) return null;
+
+ const evidenceSnapshot = { ...latestSnapshot };
+ for (const key of ['first_detected_at', 'sla_due_at'] as const) {
+ if (Object.hasOwn(evidenceSnapshot, key)) continue;
+
+ for (let index = rows.length - 1; index >= 0; index -= 1) {
+ const snapshot = rows[index].finding_snapshot;
+ if (!snapshot || !Object.hasOwn(snapshot, key)) continue;
+ evidenceSnapshot[key] = snapshot[key];
+ break;
+ }
+ }
+ return evidenceSnapshot;
+}
+
+function getRowFindingId(row: AuditReportRow): string | null {
+ if (row.finding_id) return row.finding_id;
+ if (row.resource_type === 'security_finding' && UUID_PATTERN.test(row.resource_id)) {
+ return row.resource_id;
+ }
+ return null;
+}
+
+function isLegacySupplementalRow(row: AuditReportRow): boolean {
+ return !row.occurred_at || !row.finding_id;
+}
+
+function buildSlaEvidence(
+ snapshot: Record | null,
+ dataThrough: string
+): SecurityAgentAuditSlaEvidence {
+ const deadline = stringFromSnapshot(snapshot, 'sla_due_at');
+ if (!deadline) return { status: 'unknown', deadline: null, reason: 'missing_recorded_deadline' };
+
+ const deadlineMs = Date.parse(deadline);
+ if (Number.isNaN(deadlineMs)) {
+ return { status: 'unknown', deadline, reason: 'invalid_recorded_deadline' };
+ }
+
+ const fixedAt = stringFromSnapshot(snapshot, 'fixed_at');
+ if (fixedAt) {
+ const fixedAtMs = Date.parse(fixedAt);
+ if (Number.isNaN(fixedAtMs)) {
+ return { status: 'unknown', deadline, reason: 'invalid_terminal_timestamp' };
+ }
+ return {
+ status: fixedAtMs <= deadlineMs ? 'terminal_met' : 'terminal_missed',
+ deadline,
+ terminalAt: fixedAt,
+ };
+ }
+
+ const status = stringFromSnapshot(snapshot, 'status');
+ const canonicalFindingId = stringFromSnapshot(snapshot, 'canonical_finding_id');
+ if (status === 'ignored' || canonicalFindingId) {
+ return { status: 'unknown', deadline, reason: 'ignored_or_superseded_without_terminal_time' };
+ }
+ if (status === 'fixed') {
+ return { status: 'unknown', deadline, reason: 'missing_terminal_timestamp' };
+ }
+ if (status !== 'open') {
+ return { status: 'unknown', deadline, reason: 'missing_open_status_evidence' };
+ }
+
+ const cutoffMs = Date.parse(dataThrough);
+ if (Number.isNaN(cutoffMs)) {
+ return { status: 'unknown', deadline, reason: 'invalid_report_cutoff' };
+ }
+
+ return {
+ status: cutoffMs <= deadlineMs ? 'open_within_deadline' : 'open_past_deadline',
+ deadline,
+ terminalAt: null,
+ };
+}
+
+function severityFromSnapshot(
+ snapshot: Record | null
+): SecuritySeverity | 'unknown' {
+ const severity = stringFromSnapshot(snapshot, 'severity');
+ if (
+ severity === SecuritySeverity.CRITICAL ||
+ severity === SecuritySeverity.HIGH ||
+ severity === SecuritySeverity.MEDIUM ||
+ severity === SecuritySeverity.LOW
+ ) {
+ return severity;
+ }
+ return 'unknown';
+}
+
+function stringFromSnapshot(snapshot: Record | null, key: string): string | null {
+ const value = snapshot?.[key];
+ return typeof value === 'string' && value.length > 0 ? value : null;
+}
+
+function stringArrayFromSnapshot(snapshot: Record | null, key: string): string[] {
+ const value = snapshot?.[key];
+ if (!Array.isArray(value)) return [];
+ return value.filter((item): item is string => typeof item === 'string' && item.length > 0);
+}
+
+function cvssFromSnapshot(snapshot: Record | null): string | number | null {
+ const value = snapshot?.cvss_score;
+ if (typeof value === 'string' || typeof value === 'number') return value;
+ return null;
+}
+
+function safeUrl(value: string | null): string | null {
+ if (!value) return null;
+ try {
+ const url = new URL(value);
+ if (url.protocol !== 'https:' && url.protocol !== 'http:') return null;
+ return url.toString();
+ } catch {
+ return null;
+ }
+}
+
+const REDACTED_JSON_KEY_PATTERN =
+ /(^|_)(email|recipient|prompt|rawmarkdown|raw_markdown|transcript|assistant|provider_response|authorization|auth_header|cookie|token|secret|password|credential|headers|raw_error)(_|$)/i;
+
+function selectReportEvidence(
+ value: Record | null,
+ allowedFields: readonly string[]
+): Record | null {
+ const sanitized = sanitizeJsonObject(value);
+ if (!sanitized) return null;
+
+ const selected: Record = {};
+ for (const field of allowedFields) {
+ if (Object.prototype.hasOwnProperty.call(sanitized, field)) selected[field] = sanitized[field];
+ }
+ return Object.keys(selected).length > 0 ? selected : null;
+}
+
+function sanitizeJsonObject(value: Record | null): Record | null {
+ if (!value) return null;
+ return sanitizeJson(value) as Record;
+}
+
+function sanitizeJson(value: unknown): unknown {
+ if (Array.isArray(value)) return value.map(item => sanitizeJson(item));
+ if (value && typeof value === 'object') {
+ const sanitized: Record = {};
+ for (const [key, child] of Object.entries(value)) {
+ if (REDACTED_JSON_KEY_PATTERN.test(key)) continue;
+ sanitized[key] = sanitizeJson(child);
+ }
+ return sanitized;
+ }
+ return value;
+}
+
+function parseUtcDateOnly(value: string): Date {
+ const date = new Date(`${value}T00:00:00.000Z`);
+ if (Number.isNaN(date.getTime()) || formatUtcDate(date) !== value) {
+ throw new Error('Report date must be a valid UTC calendar date');
+ }
+ return date;
+}
+
+function startOfUtcDay(value: Date): Date {
+ return new Date(Date.UTC(value.getUTCFullYear(), value.getUTCMonth(), value.getUTCDate()));
+}
+
+function addUtcDays(value: Date, days: number): Date {
+ const next = new Date(value);
+ next.setUTCDate(next.getUTCDate() + days);
+ return next;
+}
+
+function utcDayDiff(start: Date, end: Date): number {
+ return Math.floor((end.getTime() - start.getTime()) / 86_400_000);
+}
+
+function formatUtcDate(value: Date): string {
+ return value.toISOString().slice(0, 10);
+}
diff --git a/apps/web/src/lib/security-agent/db/security-findings.test.ts b/apps/web/src/lib/security-agent/db/security-findings.test.ts
index 21399d8a1d..eabb3f5e64 100644
--- a/apps/web/src/lib/security-agent/db/security-findings.test.ts
+++ b/apps/web/src/lib/security-agent/db/security-findings.test.ts
@@ -119,7 +119,14 @@ describe('upsertSecurityFinding', () => {
expect(first.wasInserted).toBe(true);
const second = await upsertSecurityFinding({
- ...makeFinding({ source_id: '10', status: 'fixed', severity: 'critical' }),
+ ...makeFinding({
+ source_id: '10',
+ status: 'fixed',
+ severity: 'critical',
+ package_name: 'lodash-es',
+ package_ecosystem: 'npm-v2',
+ manifest_path: 'apps/web/package.json',
+ }),
owner,
repoFullName: repo,
});
@@ -135,6 +142,9 @@ describe('upsertSecurityFinding', () => {
expect(row.status).toBe('fixed');
expect(row.severity).toBe('critical');
+ expect(row.package_name).toBe('lodash-es');
+ expect(row.package_ecosystem).toBe('npm-v2');
+ expect(row.manifest_path).toBe('apps/web/package.json');
});
it('returns the same row for concurrent first upserts on the same source key', async () => {
diff --git a/apps/web/src/lib/security-agent/db/security-findings.ts b/apps/web/src/lib/security-agent/db/security-findings.ts
index ba7b064acb..e4999afca5 100644
--- a/apps/web/src/lib/security-agent/db/security-findings.ts
+++ b/apps/web/src/lib/security-agent/db/security-findings.ts
@@ -4,6 +4,16 @@ import { security_findings, agent_configs } from '@kilocode/db/schema';
import { eq, and, desc, count, sql, max, or, type SQL } from 'drizzle-orm';
import { captureException } from '@sentry/nextjs';
import type { SecurityFinding, NewSecurityFinding } from '@kilocode/db/schema';
+import {
+ SecurityAuditLogAction,
+ SecurityFindingAuditSourceContext,
+} from '@kilocode/db/schema-types';
+import {
+ deriveSecurityFindingAuditEventKey,
+ insertSecurityFindingAuditEvent,
+ type SecurityFindingAuditHumanActor,
+ type SecurityFindingAuditOwner,
+} from '@kilocode/worker-utils/security-finding-audit';
import type {
SecurityReviewOwner,
SecurityFindingStatus,
@@ -37,6 +47,16 @@ function ownerConflictTarget(owner: Owner): SQL {
: sql`(${sql.identifier(security_findings.owned_by_user_id.name)}, ${sql.identifier(security_findings.repo_full_name.name)}, ${sql.identifier(security_findings.source.name)}, ${sql.identifier(security_findings.source_id.name)}) WHERE ${sql.identifier(security_findings.owned_by_user_id.name)} IS NOT NULL`;
}
+function toSecurityFindingAuditOwner(owner: Owner): SecurityFindingAuditOwner {
+ return owner.type === 'org'
+ ? { type: 'organization', organizationId: owner.id }
+ : { type: 'user', userId: owner.id };
+}
+
+function ownerAuditKeyPart(owner: Owner): string {
+ return owner.type === 'org' ? `organization:${owner.id}` : `user:${owner.id}`;
+}
+
type CreateFindingParams = ParsedSecurityFinding & {
owner: SecurityReviewOwner;
platformIntegrationId?: string;
@@ -190,8 +210,11 @@ export async function upsertSecurityFinding(
${sql.identifier(security_findings.severity.name)} = EXCLUDED.${sql.identifier(security_findings.severity.name)},
${sql.identifier(security_findings.ghsa_id.name)} = EXCLUDED.${sql.identifier(security_findings.ghsa_id.name)},
${sql.identifier(security_findings.cve_id.name)} = EXCLUDED.${sql.identifier(security_findings.cve_id.name)},
+ ${sql.identifier(security_findings.package_name.name)} = EXCLUDED.${sql.identifier(security_findings.package_name.name)},
+ ${sql.identifier(security_findings.package_ecosystem.name)} = EXCLUDED.${sql.identifier(security_findings.package_ecosystem.name)},
${sql.identifier(security_findings.vulnerable_version_range.name)} = EXCLUDED.${sql.identifier(security_findings.vulnerable_version_range.name)},
${sql.identifier(security_findings.patched_version.name)} = EXCLUDED.${sql.identifier(security_findings.patched_version.name)},
+ ${sql.identifier(security_findings.manifest_path.name)} = EXCLUDED.${sql.identifier(security_findings.manifest_path.name)},
${sql.identifier(security_findings.title.name)} = EXCLUDED.${sql.identifier(security_findings.title.name)},
${sql.identifier(security_findings.description.name)} = EXCLUDED.${sql.identifier(security_findings.description.name)},
${sql.identifier(security_findings.status.name)} = CASE
@@ -413,7 +436,7 @@ export async function listSecurityFindings(
} = params;
const ownerConverted = toOwner(owner);
- const conditions = [];
+ const conditions: Array = [];
if (ownerConverted.type === 'org') {
conditions.push(eq(security_findings.owned_by_organization_id, ownerConverted.id));
@@ -584,7 +607,7 @@ export async function countSecurityFindings(params: {
const { owner, status, severity, repoFullName } = params;
const ownerConverted = toOwner(owner);
- const conditions = [];
+ const conditions: Array = [];
// Owner condition
if (ownerConverted.type === 'org') {
@@ -874,7 +897,7 @@ export async function getOrphanedRepositoriesWithFindingCounts(params: {
const { owner, accessibleRepoFullNames } = params;
const ownerConverted = toOwner(owner);
- const conditions = [];
+ const conditions: SQL[] = [];
// Owner condition
if (ownerConverted.type === 'org') {
@@ -909,12 +932,13 @@ export async function getOrphanedRepositoriesWithFindingCounts(params: {
export async function deleteFindingsByRepository(params: {
owner: SecurityReviewOwner;
repoFullName: string;
+ actor: SecurityFindingAuditHumanActor;
}): Promise<{ deletedCount: number }> {
try {
const { owner, repoFullName } = params;
const ownerConverted = toOwner(owner);
- const conditions = [];
+ const conditions: SQL[] = [];
// Owner condition
if (ownerConverted.type === 'org') {
@@ -926,13 +950,44 @@ export async function deleteFindingsByRepository(params: {
// Repository condition
conditions.push(eq(security_findings.repo_full_name, repoFullName));
- // Delete findings and get count
- const result = await db
- .delete(security_findings)
- .where(and(...conditions))
- .returning({ id: security_findings.id });
+ const ownerForAudit = toSecurityFindingAuditOwner(ownerConverted);
+ const ownerKeyPart = ownerAuditKeyPart(ownerConverted);
+
+ const deletedCount = await db.transaction(async tx => {
+ const findings = await tx
+ .select()
+ .from(security_findings)
+ .where(and(...conditions));
+ const occurredAt = new Date().toISOString();
+
+ for (const finding of findings) {
+ await insertSecurityFindingAuditEvent(tx, {
+ owner: ownerForAudit,
+ finding,
+ actor: params.actor,
+ action: SecurityAuditLogAction.FindingDeleted,
+ occurredAt,
+ eventKey: deriveSecurityFindingAuditEventKey([
+ ownerKeyPart,
+ finding.id,
+ SecurityAuditLogAction.FindingDeleted,
+ ]),
+ sourceContext: SecurityFindingAuditSourceContext.Web,
+ beforeState: { status: finding.status },
+ afterState: { deleted: true },
+ metadata: { repo_full_name: repoFullName },
+ });
+ }
+
+ const deleted = await tx
+ .delete(security_findings)
+ .where(and(...conditions))
+ .returning({ id: security_findings.id });
+
+ return deleted.length;
+ });
- return { deletedCount: result.length };
+ return { deletedCount };
} catch (error) {
captureException(error, {
tags: { operation: 'deleteFindingsByRepository' },
diff --git a/apps/web/src/lib/security-agent/posthog-tracking.test.ts b/apps/web/src/lib/security-agent/posthog-tracking.test.ts
new file mode 100644
index 0000000000..d2f130f9cf
--- /dev/null
+++ b/apps/web/src/lib/security-agent/posthog-tracking.test.ts
@@ -0,0 +1,145 @@
+import { beforeAll, beforeEach, describe, expect, it, jest } from '@jest/globals';
+import type {
+ trackSecurityAgentRemediationAction as trackSecurityAgentRemediationActionType,
+ trackSecurityAgentUiInteraction as trackSecurityAgentUiInteractionType,
+} from './posthog-tracking';
+
+jest.mock('@/lib/posthog', () => {
+ const mockCapture = jest.fn();
+
+ return {
+ __esModule: true,
+ default: jest.fn(() => ({ capture: mockCapture })),
+ mockCapture,
+ };
+});
+
+jest.mock('@sentry/nextjs', () => {
+ const mockCaptureException = jest.fn();
+
+ return {
+ captureException: mockCaptureException,
+ mockCaptureException,
+ };
+});
+
+let trackSecurityAgentRemediationAction: typeof trackSecurityAgentRemediationActionType;
+let trackSecurityAgentUiInteraction: typeof trackSecurityAgentUiInteractionType;
+
+const posthogMock: { mockCapture: jest.Mock } = jest.requireMock('@/lib/posthog');
+const sentryMock: { mockCaptureException: jest.Mock } = jest.requireMock('@sentry/nextjs');
+const { mockCapture } = posthogMock;
+const { mockCaptureException } = sentryMock;
+
+beforeAll(async () => {
+ ({ trackSecurityAgentRemediationAction, trackSecurityAgentUiInteraction } =
+ await import('./posthog-tracking'));
+});
+
+describe('Security Agent PostHog tracking', () => {
+ beforeEach(() => {
+ mockCapture.mockReset();
+ mockCaptureException.mockReset();
+ });
+
+ it('captures UI interactions with only fixed allowlisted properties', () => {
+ const input = {
+ distinctId: 'user-123',
+ userId: 'user-123',
+ interaction: 'finding_detail_opened',
+ findingId: 'must-not-leak',
+ } as const;
+
+ trackSecurityAgentUiInteraction(input);
+
+ expect(mockCapture).toHaveBeenCalledWith({
+ distinctId: 'user-123',
+ event: 'security_agent_ui_interaction',
+ properties: {
+ interaction: 'finding_detail_opened',
+ feature: 'security-agent',
+ operation: 'ui_interaction',
+ userId: 'user-123',
+ },
+ });
+ });
+
+ it('captures remediation actions with trusted organization context only', () => {
+ const input = {
+ distinctId: 'user-123',
+ userId: 'user-123',
+ organizationId: 'organization-123',
+ action: 'retry',
+ attemptId: 'must-not-leak',
+ error: 'must-not-leak',
+ } as const;
+
+ trackSecurityAgentRemediationAction(input);
+
+ expect(mockCapture).toHaveBeenCalledWith({
+ distinctId: 'user-123',
+ event: 'security_agent_remediation_action',
+ properties: {
+ action: 'retry',
+ phase: 'accepted',
+ feature: 'security-agent',
+ operation: 'remediation_action',
+ userId: 'user-123',
+ organizationId: 'organization-123',
+ },
+ });
+ });
+
+ it('reports UI capture failures without throwing or leaking arbitrary properties', () => {
+ const error = new Error('capture failed');
+ mockCapture.mockImplementation(() => {
+ throw error;
+ });
+
+ expect(() =>
+ trackSecurityAgentUiInteraction({
+ distinctId: 'user-123',
+ userId: 'user-123',
+ interaction: 'findings_filtered',
+ })
+ ).not.toThrow();
+ expect(mockCaptureException).toHaveBeenCalledWith(error, {
+ tags: { source: 'posthog_security_agent_ui_interaction' },
+ extra: {
+ properties: {
+ interaction: 'findings_filtered',
+ feature: 'security-agent',
+ operation: 'ui_interaction',
+ userId: 'user-123',
+ },
+ },
+ });
+ });
+
+ it('reports remediation capture failures without throwing', () => {
+ const error = new Error('capture failed');
+ mockCapture.mockImplementation(() => {
+ throw error;
+ });
+
+ expect(() =>
+ trackSecurityAgentRemediationAction({
+ distinctId: 'user-123',
+ userId: 'user-123',
+ action: 'cancel',
+ })
+ ).not.toThrow();
+ expect(mockCaptureException).toHaveBeenCalledWith(error, {
+ tags: { source: 'posthog_security_agent_remediation_action' },
+ extra: {
+ properties: {
+ action: 'cancel',
+ phase: 'accepted',
+ feature: 'security-agent',
+ operation: 'remediation_action',
+ userId: 'user-123',
+ },
+ },
+ });
+ });
+});
diff --git a/apps/web/src/lib/security-agent/posthog-tracking.ts b/apps/web/src/lib/security-agent/posthog-tracking.ts
index f1c13710c0..6f27bd5bb9 100644
--- a/apps/web/src/lib/security-agent/posthog-tracking.ts
+++ b/apps/web/src/lib/security-agent/posthog-tracking.ts
@@ -7,6 +7,7 @@
import 'server-only';
import PostHogClient from '@/lib/posthog';
+import type { SecurityAgentUiInteraction } from '@/lib/security-agent/core/schemas';
import { captureException } from '@sentry/nextjs';
const posthogClient = PostHogClient();
@@ -99,6 +100,67 @@ type SecurityAgentFullSyncEvent = {
durationMs: number;
};
+type SecurityAgentUiInteractionEvent = BaseSecurityAgentEvent & {
+ interaction: SecurityAgentUiInteraction;
+};
+
+type SecurityAgentRemediationActionEvent = BaseSecurityAgentEvent & {
+ action: 'start' | 'retry' | 'cancel';
+};
+
+export function trackSecurityAgentUiInteraction(properties: SecurityAgentUiInteractionEvent): void {
+ const eventProperties = {
+ interaction: properties.interaction,
+ feature: 'security-agent',
+ operation: 'ui_interaction',
+ userId: properties.userId,
+ ...(properties.organizationId !== undefined
+ ? { organizationId: properties.organizationId }
+ : {}),
+ };
+
+ try {
+ posthogClient.capture({
+ distinctId: properties.distinctId,
+ event: 'security_agent_ui_interaction',
+ properties: eventProperties,
+ });
+ } catch (error) {
+ captureException(error, {
+ tags: { source: 'posthog_security_agent_ui_interaction' },
+ extra: { properties: eventProperties },
+ });
+ }
+}
+
+export function trackSecurityAgentRemediationAction(
+ properties: SecurityAgentRemediationActionEvent
+): void {
+ const eventProperties = {
+ action: properties.action,
+ phase: 'accepted',
+ feature: 'security-agent',
+ operation: 'remediation_action',
+ userId: properties.userId,
+ ...(properties.organizationId !== undefined
+ ? { organizationId: properties.organizationId }
+ : {}),
+ };
+
+ try {
+ posthogClient.capture({
+ distinctId: properties.distinctId,
+ event: 'security_agent_remediation_action',
+ properties: eventProperties,
+ });
+ } catch (error) {
+ captureException(error, {
+ tags: { source: 'posthog_security_agent_remediation_action' },
+ extra: { properties: eventProperties },
+ });
+ }
+}
+
/**
* Track security agent enabled/disabled
*/
diff --git a/apps/web/src/lib/security-agent/router/shared-handlers.test.ts b/apps/web/src/lib/security-agent/router/shared-handlers.test.ts
index 09810e6fa6..479d0fec67 100644
--- a/apps/web/src/lib/security-agent/router/shared-handlers.test.ts
+++ b/apps/web/src/lib/security-agent/router/shared-handlers.test.ts
@@ -3,6 +3,7 @@ import type { createSecurityAgentHandlers as createSecurityAgentHandlersType } f
import type * as manualSyncClientModule from '../services/manual-sync-client';
import type * as manualDismissClientModule from '../services/manual-dismiss-client';
import type * as manualAnalysisClientModule from '../services/manual-analysis-client';
+import type * as manualRemediationClientModule from '../services/manual-remediation-client';
const commandId = 'eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee';
const mockSubmitManualSecuritySync = jest.fn() as jest.MockedFunction<
@@ -14,12 +15,38 @@ const mockSubmitManualFindingDismissal = jest.fn() as jest.MockedFunction<
const mockSubmitManualAnalysisStart = jest.fn() as jest.MockedFunction<
typeof manualAnalysisClientModule.submitManualAnalysisStart
>;
+const mockSubmitApplyAutoRemediation = jest.fn() as jest.MockedFunction<
+ typeof manualRemediationClientModule.submitApplyAutoRemediation
+>;
+const mockSubmitManualRemediationStart = jest.fn() as jest.MockedFunction<
+ typeof manualRemediationClientModule.submitManualRemediationStart
+>;
+const mockSubmitRemediationCancellation = jest.fn() as jest.MockedFunction<
+ typeof manualRemediationClientModule.submitRemediationCancellation
+>;
const mockGetSecurityFindingById = jest.fn<() => Promise>();
-const mockCanStartAnalysis = jest.fn<() => Promise>();
+const mockCanStartAnalysis = jest.fn<(owner: unknown) => Promise>();
const mockEnqueueBacklogFindings = jest.fn<() => Promise>();
const mockGetSecurityAgentConfigWithStatus = jest.fn<() => Promise>();
+const mockDecorateFindingWithRemediation = jest.fn<() => Promise>();
+const mockDecorateFindingsWithRemediation = jest.fn<() => Promise>();
+const mockGetRemediationAttemptHistory = jest.fn<() => Promise>();
+const mockDeleteFindingsByRepository =
+ jest.fn<(params: unknown) => Promise<{ deletedCount: number }>>();
const mockTrackSecurityAgentSync = jest.fn();
+const mockTrackSecurityAgentUiInteraction = jest.fn();
+const mockTrackSecurityAgentRemediationAction = jest.fn();
const mockLogSecurityAudit = jest.fn();
+const mockCreateSecurityAuditLog = jest.fn();
+const mockUpsertSecurityAgentConfig = jest.fn();
+const mockSetSecurityAgentEnabled = jest.fn();
+const mockAutoDismissEligibleFindings =
+ jest.fn<
+ (
+ owner: unknown,
+ actor: unknown
+ ) => Promise<{ dismissed: number; skipped: number; errors: number }>
+ >();
jest.mock('../services/manual-sync-client', () => ({
submitManualSecuritySync: mockSubmitManualSecuritySync,
@@ -30,6 +57,11 @@ jest.mock('../services/manual-dismiss-client', () => ({
jest.mock('../services/manual-analysis-client', () => ({
submitManualAnalysisStart: mockSubmitManualAnalysisStart,
}));
+jest.mock('../services/manual-remediation-client', () => ({
+ submitApplyAutoRemediation: mockSubmitApplyAutoRemediation,
+ submitManualRemediationStart: mockSubmitManualRemediationStart,
+ submitRemediationCancellation: mockSubmitRemediationCancellation,
+}));
jest.mock('../github/permissions', () => ({
hasSecurityReviewPermissions: () => true,
getReauthorizeUrl: jest.fn(),
@@ -39,8 +71,11 @@ jest.mock('../posthog-tracking', () => ({
trackSecurityAgentConfigSaved: jest.fn(),
trackSecurityAgentSync: mockTrackSecurityAgentSync,
trackSecurityAgentFindingDismissed: jest.fn(),
+ trackSecurityAgentUiInteraction: mockTrackSecurityAgentUiInteraction,
+ trackSecurityAgentRemediationAction: mockTrackSecurityAgentRemediationAction,
}));
jest.mock('../services/audit-log-service', () => ({
+ createSecurityAuditLog: mockCreateSecurityAuditLog,
logSecurityAudit: mockLogSecurityAudit,
SecurityAuditLogAction: {
ConfigEnabled: 'config_enabled',
@@ -52,8 +87,8 @@ jest.mock('../services/audit-log-service', () => ({
}));
jest.mock('../db/security-config', () => ({
getSecurityAgentConfigWithStatus: mockGetSecurityAgentConfigWithStatus,
- upsertSecurityAgentConfig: jest.fn(),
- setSecurityAgentEnabled: jest.fn(),
+ upsertSecurityAgentConfig: mockUpsertSecurityAgentConfig,
+ setSecurityAgentEnabled: mockSetSecurityAgentEnabled,
}));
jest.mock('../db/security-findings', () => ({
listSecurityFindings: jest.fn(),
@@ -61,7 +96,12 @@ jest.mock('../db/security-findings', () => ({
getSecurityFindingsSummary: jest.fn(),
getLastSyncTime: jest.fn(),
getOrphanedRepositoriesWithFindingCounts: jest.fn(),
- deleteFindingsByRepository: jest.fn(),
+ deleteFindingsByRepository: mockDeleteFindingsByRepository,
+}));
+jest.mock('../db/security-remediation', () => ({
+ decorateFindingWithRemediation: mockDecorateFindingWithRemediation,
+ decorateFindingsWithRemediation: mockDecorateFindingsWithRemediation,
+ getRemediationAttemptHistory: mockGetRemediationAttemptHistory,
}));
jest.mock('../db/security-commands', () => ({
getSecurityAgentCommandStatus: jest.fn(),
@@ -73,7 +113,7 @@ jest.mock('../db/security-analysis', () => ({
enqueueBacklogFindings: mockEnqueueBacklogFindings,
}));
jest.mock('../services/auto-dismiss-service', () => ({
- autoDismissEligibleFindings: jest.fn(),
+ autoDismissEligibleFindings: mockAutoDismissEligibleFindings,
countEligibleForAutoDismiss: jest.fn(),
}));
jest.mock('@/lib/integrations/db/platform-integrations', () => ({
@@ -92,6 +132,7 @@ beforeAll(async () => {
beforeEach(() => {
jest.clearAllMocks();
mockGetSecurityAgentConfigWithStatus.mockResolvedValue(null);
+ mockGetRemediationAttemptHistory.mockResolvedValue([]);
mockEnqueueBacklogFindings.mockResolvedValue(0);
});
@@ -116,14 +157,159 @@ function createHandlers() {
});
}
+function createPersonalHandlers() {
+ return createSecurityAgentHandlers({
+ resolveOwner: () => ({ type: 'user', id: 'user-123', userId: 'user-123' }),
+ resolveSecurityOwner: () => ({ userId: 'user-123' }),
+ resolveResourceId: () => 'user-123',
+ verifyFindingOwnership: () => true,
+ getIntegration: async () =>
+ ({
+ id: 'integration-123',
+ integration_status: 'active',
+ platform_installation_id: 'installation-123',
+ repositories: [],
+ }) as never,
+ trackingExtras: () => ({}),
+ });
+}
+
+function createOrganizationTrackingHandlers() {
+ return createSecurityAgentHandlers<{ organizationId: string }>({
+ resolveOwner: (ctx, input) => ({
+ type: 'org',
+ id: input.organizationId,
+ userId: ctx.user.id,
+ }),
+ resolveSecurityOwner: (_ctx, input) => ({ organizationId: input.organizationId }),
+ resolveResourceId: (_ctx, input) => input.organizationId,
+ verifyFindingOwnership: (finding, _ctx, input) =>
+ finding.owned_by_organization_id === input.organizationId,
+ getIntegration: async () =>
+ ({
+ id: 'integration-123',
+ integration_status: 'active',
+ platform_installation_id: 'installation-123',
+ repositories: [],
+ }) as never,
+ trackingExtras: (_ctx, input) => ({ organizationId: input.organizationId }),
+ });
+}
+
const context = {
user: {
id: 'user-123',
google_user_email: 'owner@example.com',
google_user_name: 'Owner Example',
+ is_admin: false,
},
} as never;
+describe('trackUiInteraction', () => {
+ it('tracks an allowlisted interaction with authenticated personal identity', async () => {
+ await expect(
+ createPersonalHandlers().trackUiInteraction.handler({
+ ctx: context,
+ input: { interaction: 'finding_detail_opened' },
+ })
+ ).resolves.toEqual({ success: true });
+
+ expect(mockTrackSecurityAgentUiInteraction).toHaveBeenCalledWith({
+ distinctId: 'user-123',
+ userId: 'user-123',
+ organizationId: undefined,
+ interaction: 'finding_detail_opened',
+ });
+ });
+
+ it('uses trusted organization context from the router input', async () => {
+ await createOrganizationTrackingHandlers().trackUiInteraction.handler({
+ ctx: context,
+ input: {
+ organizationId: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',
+ interaction: 'settings_automation_viewed',
+ },
+ });
+
+ expect(mockTrackSecurityAgentUiInteraction).toHaveBeenCalledWith({
+ distinctId: 'user-123',
+ userId: 'user-123',
+ organizationId: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',
+ interaction: 'settings_automation_viewed',
+ });
+ });
+
+ it('rejects unsupported interaction values at the schema boundary', () => {
+ expect(
+ createPersonalHandlers().trackUiInteraction.inputSchema.safeParse({
+ interaction: 'finding_exported',
+ }).success
+ ).toBe(false);
+ });
+
+ it('does not write UI interactions to database or audit storage', async () => {
+ await createPersonalHandlers().trackUiInteraction.handler({
+ ctx: context,
+ input: { interaction: 'findings_filtered' },
+ });
+
+ expect(mockUpsertSecurityAgentConfig).not.toHaveBeenCalled();
+ expect(mockSetSecurityAgentEnabled).not.toHaveBeenCalled();
+ expect(mockCreateSecurityAuditLog).not.toHaveBeenCalled();
+ expect(mockLogSecurityAudit).not.toHaveBeenCalled();
+ });
+});
+
+describe('getConfig', () => {
+ it('marks new owners without config as setup state', async () => {
+ await expect(createHandlers().getConfig({ ctx: context, input: {} })).resolves.toMatchObject({
+ hasConfig: false,
+ isEnabled: false,
+ });
+ });
+
+ it('marks existing disabled config as configured', async () => {
+ mockGetSecurityAgentConfigWithStatus.mockResolvedValue({
+ isEnabled: false,
+ storedConfig: {},
+ config: {
+ sla_critical_days: 15,
+ sla_high_days: 30,
+ sla_medium_days: 45,
+ sla_low_days: 90,
+ sla_enabled: true,
+ auto_sync_enabled: true,
+ repository_selection_mode: 'selected',
+ selected_repository_ids: [],
+ model_slug: 'analysis-model',
+ triage_model_slug: 'triage-model',
+ analysis_model_slug: 'analysis-model',
+ analysis_mode: 'auto',
+ auto_dismiss_enabled: false,
+ auto_dismiss_confidence_threshold: 'high',
+ auto_analysis_enabled: false,
+ auto_analysis_min_severity: 'high',
+ auto_analysis_include_existing: false,
+ auto_remediation_enabled: false,
+ auto_remediation_min_severity: 'high',
+ auto_remediation_include_existing: false,
+ auto_remediation_enabled_at: null,
+ remediation_model_slug: 'remediation-model',
+ sla_notifications_enabled: false,
+ sla_notification_min_severity: 'high',
+ sla_notification_warning_days: 3,
+ new_finding_notifications_enabled: false,
+ new_finding_notification_min_severity: 'high',
+ },
+ });
+
+ await expect(createHandlers().getConfig({ ctx: context, input: {} })).resolves.toMatchObject({
+ hasConfig: true,
+ isEnabled: false,
+ });
+ });
+});
+
describe('setEnabled', () => {
it('returns initial sync command correlation after enable', async () => {
mockSubmitManualSecuritySync.mockResolvedValue({
@@ -196,6 +382,106 @@ describe('saveConfig', () => {
});
});
+describe('autoDismissEligible', () => {
+ it('attributes per-finding bulk dismissal events without writing aggregate finding activity', async () => {
+ mockAutoDismissEligibleFindings.mockResolvedValue({ dismissed: 2, skipped: 1, errors: 0 });
+
+ await expect(
+ createHandlers().autoDismissEligible({ ctx: context, input: {} })
+ ).resolves.toEqual({ dismissed: 2, skipped: 1, errors: 0 });
+
+ expect(mockAutoDismissEligibleFindings).toHaveBeenCalledWith(
+ { organizationId: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa' },
+ {
+ type: 'customer_user',
+ id: 'user-123',
+ email: 'owner@example.com',
+ name: 'Owner Example',
+ }
+ );
+ expect(mockLogSecurityAudit).not.toHaveBeenCalled();
+ });
+});
+
+describe('deleteFindingsByRepository', () => {
+ it('propagates authoritative admin classification to deletion events', async () => {
+ mockDeleteFindingsByRepository.mockResolvedValue({ deletedCount: 2 });
+ const adminContext = {
+ user: {
+ id: 'user-123',
+ google_user_email: 'operator@example.com',
+ google_user_name: 'Owner Example',
+ is_admin: true,
+ },
+ } as never;
+
+ await expect(
+ createHandlers().deleteFindingsByRepository.handler({
+ ctx: adminContext,
+ input: { repoFullName: 'kilo/repo' },
+ })
+ ).resolves.toEqual({ success: true, deletedCount: 2 });
+
+ expect(mockDeleteFindingsByRepository).toHaveBeenCalledWith({
+ owner: { organizationId: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa' },
+ repoFullName: 'kilo/repo',
+ actor: {
+ type: 'kilo_admin',
+ id: 'user-123',
+ email: 'operator@example.com',
+ name: 'Owner Example',
+ },
+ });
+ });
+});
+
+describe('getAnalysis', () => {
+ it('returns current finding state with analysis and remediation data', async () => {
+ const findingId = 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb';
+ const finding = {
+ id: findingId,
+ status: 'ignored',
+ ignored_reason: 'not_used',
+ ignored_by: 'auto-sandbox',
+ updated_at: '2026-06-17T11:45:00.000Z',
+ analysis_status: 'completed',
+ analysis_started_at: '2026-06-17T11:40:00.000Z',
+ analysis_completed_at: '2026-06-17T11:44:59.000Z',
+ analysis_error: null,
+ analysis: { analyzedAt: '2026-06-17T11:44:59.000Z' },
+ session_id: 'session-123',
+ cli_session_id: 'cli-session-123',
+ };
+ const decoratedFinding = {
+ ...finding,
+ remediationSummary: null,
+ remediationCapability: {
+ canStart: false,
+ startReason: 'finding_not_open',
+ canRetry: false,
+ retryReason: 'finding_not_open',
+ canCancel: false,
+ cancelAttemptId: null,
+ },
+ };
+ mockGetSecurityFindingById.mockResolvedValue(finding);
+ mockDecorateFindingWithRemediation.mockResolvedValue(decoratedFinding);
+
+ await expect(
+ createHandlers().getAnalysis.handler({ ctx: context, input: { findingId } })
+ ).resolves.toMatchObject({
+ findingState: {
+ status: 'ignored',
+ ignoredReason: 'not_used',
+ ignoredBy: 'auto-sandbox',
+ updatedAt: '2026-06-17T11:45:00.000Z',
+ },
+ status: 'completed',
+ remediationCapability: { startReason: 'finding_not_open' },
+ });
+ });
+});
+
describe('queue-backed handlers', () => {
it('returns sync command correlation', async () => {
mockSubmitManualSecuritySync.mockResolvedValue({
@@ -228,6 +514,9 @@ describe('queue-backed handlers', () => {
},
})
).resolves.toMatchObject({ success: true, accepted: true, commandId });
+ expect(mockSubmitManualFindingDismissal).toHaveBeenCalledWith(
+ expect.objectContaining({ actor: { id: 'user-123' } })
+ );
});
it('returns manual analysis command correlation', async () => {
@@ -241,5 +530,133 @@ describe('queue-backed handlers', () => {
input: { findingId: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb' },
})
).resolves.toEqual({ success: true, queued: true, commandId });
+
+ expect(mockCanStartAnalysis).toHaveBeenCalledWith({
+ organizationId: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',
+ });
+ });
+
+ it('bypasses owner capacity only for a validated active restart', async () => {
+ mockGetSecurityFindingById.mockResolvedValue({
+ id: 'finding-id',
+ analysis_status: 'running',
+ });
+ mockCanStartAnalysis.mockResolvedValue({ allowed: false, currentCount: 3, limit: 3 });
+ mockSubmitManualAnalysisStart.mockResolvedValue({ queued: true, commandId });
+
+ await expect(
+ createHandlers().startAnalysis.handler({
+ ctx: context,
+ input: {
+ findingId: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
+ restartActive: true,
+ },
+ })
+ ).resolves.toEqual({ success: true, queued: true, commandId });
+
+ expect(mockCanStartAnalysis).not.toHaveBeenCalled();
+ expect(mockSubmitManualAnalysisStart).toHaveBeenCalledWith(
+ expect.objectContaining({ restartActive: true })
+ );
+ });
+
+ it('rejects active restart requests after finding is no longer running', async () => {
+ mockGetSecurityFindingById.mockResolvedValue({
+ id: 'finding-id',
+ analysis_status: 'completed',
+ });
+
+ await expect(
+ createHandlers().startAnalysis.handler({
+ ctx: context,
+ input: {
+ findingId: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
+ restartActive: true,
+ },
+ })
+ ).rejects.toMatchObject({
+ code: 'PRECONDITION_FAILED',
+ message: 'Only a running Sandbox Analysis can be restarted',
+ });
+
+ expect(mockCanStartAnalysis).not.toHaveBeenCalled();
+ expect(mockSubmitManualAnalysisStart).not.toHaveBeenCalled();
+ });
+});
+
+describe('remediation action tracking', () => {
+ const findingId = 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb';
+ const attemptId = 'cccccccc-cccc-4ccc-8ccc-cccccccccccc';
+
+ it('tracks accepted start, retry, and cancel actions', async () => {
+ mockGetSecurityFindingById.mockResolvedValue({ id: findingId });
+ mockSubmitManualRemediationStart.mockResolvedValue({
+ queued: true,
+ remediationId: 'dddddddd-dddd-4ddd-8ddd-dddddddddddd',
+ attemptId,
+ attemptNumber: 1,
+ });
+ mockSubmitRemediationCancellation.mockResolvedValue({
+ success: true,
+ status: 'cancellation_requested',
+ });
+ const handlers = createHandlers();
+
+ await handlers.startRemediation.handler({ ctx: context, input: { findingId } });
+ await handlers.retryRemediation.handler({ ctx: context, input: { findingId } });
+ await handlers.cancelRemediation.handler({ ctx: context, input: { attemptId } });
+
+ expect(mockTrackSecurityAgentRemediationAction).toHaveBeenNthCalledWith(1, {
+ distinctId: 'user-123',
+ userId: 'user-123',
+ organizationId: undefined,
+ action: 'start',
+ });
+ expect(mockTrackSecurityAgentRemediationAction).toHaveBeenNthCalledWith(2, {
+ distinctId: 'user-123',
+ userId: 'user-123',
+ organizationId: undefined,
+ action: 'retry',
+ });
+ expect(mockTrackSecurityAgentRemediationAction).toHaveBeenNthCalledWith(3, {
+ distinctId: 'user-123',
+ userId: 'user-123',
+ organizationId: undefined,
+ action: 'cancel',
+ });
+ });
+
+ it('returns typed policy rejections without tracking accepted remediation', async () => {
+ mockGetSecurityFindingById.mockResolvedValue({ id: findingId });
+ mockSubmitManualRemediationStart.mockResolvedValue({
+ queued: false,
+ reason: 'analysis_required',
+ });
+ const handlers = createHandlers();
+
+ await expect(
+ handlers.startRemediation.handler({ ctx: context, input: { findingId } })
+ ).resolves.toEqual({ success: false, queued: false, reason: 'analysis_required' });
+
+ expect(mockTrackSecurityAgentRemediationAction).not.toHaveBeenCalled();
+ });
+
+ it('does not track remediation actions rejected by admission handlers', async () => {
+ mockGetSecurityFindingById.mockResolvedValue({ id: findingId });
+ mockSubmitManualRemediationStart.mockRejectedValue(new Error('not admitted'));
+ mockSubmitRemediationCancellation.mockRejectedValue(new Error('not cancellable'));
+ const handlers = createHandlers();
+
+ await expect(
+ handlers.startRemediation.handler({ ctx: context, input: { findingId } })
+ ).rejects.toThrow('not admitted');
+ await expect(
+ handlers.retryRemediation.handler({ ctx: context, input: { findingId } })
+ ).rejects.toThrow('not admitted');
+ await expect(
+ handlers.cancelRemediation.handler({ ctx: context, input: { attemptId } })
+ ).rejects.toThrow('not cancellable');
+
+ expect(mockTrackSecurityAgentRemediationAction).not.toHaveBeenCalled();
});
});
diff --git a/apps/web/src/lib/security-agent/router/shared-handlers.ts b/apps/web/src/lib/security-agent/router/shared-handlers.ts
index 1ff4d1f06d..7ccb758316 100644
--- a/apps/web/src/lib/security-agent/router/shared-handlers.ts
+++ b/apps/web/src/lib/security-agent/router/shared-handlers.ts
@@ -34,6 +34,13 @@ import {
decorateFindingsWithRemediation,
getRemediationAttemptHistory,
} from '@/lib/security-agent/db/security-remediation';
+import {
+ SecurityAgentAuditReportInputSchema,
+ SecurityAgentAuditReportQueryError,
+ getSecurityAgentAuditReport,
+ type SecurityAgentAuditReportInput,
+ type SecurityAgentAuditReportOwner,
+} from '@/lib/security-agent/db/security-audit-report';
import {
hasSecurityReviewPermissions,
getReauthorizeUrl,
@@ -51,7 +58,10 @@ import {
countEligibleForAutoDismiss,
} from '@/lib/security-agent/services/auto-dismiss-service';
import type { SecurityReviewOwner } from '@/lib/security-agent/core/types';
-import type { SecurityFinding } from '@kilocode/db/schema';
+import { organizations, type SecurityFinding } from '@kilocode/db/schema';
+import { buildSecurityFindingAuditHumanActor } from '@kilocode/worker-utils/security-finding-audit';
+import { db } from '@/lib/drizzle';
+import { eq } from 'drizzle-orm';
import {
SaveSecurityConfigInputSchema,
ListFindingsInputSchema,
@@ -67,6 +77,7 @@ import {
GetCommandStatusInputSchema,
DeleteFindingsByRepoInputSchema,
GetDashboardStatsInputSchema,
+ TrackSecurityAgentUiInteractionInputSchema,
type SaveSecurityConfigInput,
type ListFindingsInput,
type TriggerSyncInput,
@@ -81,6 +92,7 @@ import {
type GetCommandStatusInput,
type DeleteFindingsByRepoInput,
type GetDashboardStatsInput,
+ type TrackSecurityAgentUiInteractionInput,
} from '@/lib/security-agent/core/schemas';
import {
DEFAULT_SECURITY_AGENT_TRIAGE_MODEL,
@@ -93,8 +105,11 @@ import {
trackSecurityAgentConfigSaved,
trackSecurityAgentSync,
trackSecurityAgentFindingDismissed,
+ trackSecurityAgentUiInteraction,
+ trackSecurityAgentRemediationAction,
} from '@/lib/security-agent/posthog-tracking';
import {
+ createSecurityAuditLog,
logSecurityAudit,
SecurityAuditLogAction,
} from '@/lib/security-agent/services/audit-log-service';
@@ -118,7 +133,7 @@ type SecurityAgentDeps = {
resolveResourceId: (ctx: TRPCContext, input: TExtra) => string;
verifyFindingOwnership: (finding: SecurityFinding, ctx: TRPCContext, input: TExtra) => boolean;
getIntegration: (ctx: TRPCContext, input: TExtra) => Promise;
- trackingExtras: (ctx: TRPCContext, input: TExtra) => Record;
+ trackingExtras: (ctx: TRPCContext, input: TExtra) => { organizationId?: string };
};
function getRepoFullNamesInScope(
@@ -136,6 +151,99 @@ function getRepoFullNamesInScope(
.filter((name): name is string => !!name);
}
+async function resolveAuditReportOwner(
+ ctx: TRPCContext,
+ owner: Owner
+): Promise {
+ if (owner.type === 'user') {
+ return {
+ type: 'user',
+ id: ctx.user.id,
+ displayName: ctx.user.google_user_name || ctx.user.google_user_email || 'Personal owner',
+ };
+ }
+
+ const [organization] = await db
+ .select({ name: organizations.name })
+ .from(organizations)
+ .where(eq(organizations.id, owner.id))
+ .limit(1);
+
+ if (!organization) {
+ throw new TRPCError({ code: 'NOT_FOUND', message: 'Organization not found' });
+ }
+
+ return {
+ type: 'organization',
+ id: owner.id,
+ displayName: organization.name,
+ };
+}
+
+async function logPlatformAdminAuditReportAccess(params: {
+ ctx: TRPCContext;
+ owner: SecurityAgentAuditReportOwner;
+ periodStart: string;
+ periodEndExclusive: string;
+}): Promise {
+ if (!params.ctx.user.is_admin) return;
+
+ const securityOwner =
+ params.owner.type === 'organization'
+ ? { organizationId: params.owner.id }
+ : { userId: params.owner.id };
+
+ await createSecurityAuditLog({
+ owner: securityOwner,
+ actor_id: params.ctx.user.id,
+ actor_email: params.ctx.user.google_user_email,
+ actor_name: params.ctx.user.google_user_name,
+ action: SecurityAuditLogAction.AuditReportGenerated,
+ resource_type: 'security_agent_audit_report',
+ resource_id: `${params.owner.type}:${params.owner.id}`,
+ metadata: {
+ owner_type: params.owner.type,
+ period_start: params.periodStart,
+ period_end_exclusive: params.periodEndExclusive,
+ report_version: 1,
+ },
+ });
+}
+
+async function assembleAuditReportResponse(params: {
+ ctx: TRPCContext;
+ input: SecurityAgentAuditReportInput & TExtra;
+ deps: SecurityAgentDeps;
+}): Promise<
+ | { status: 'ok'; report: Awaited> }
+ | { status: 'query_failed'; message: 'Report query did not finish' }
+> {
+ const owner = await resolveAuditReportOwner(
+ params.ctx,
+ params.deps.resolveOwner(params.ctx, params.input)
+ );
+
+ try {
+ const report = await getSecurityAgentAuditReport({
+ owner,
+ input: params.input,
+ isRequestingUserKiloAdmin: params.ctx.user.is_admin,
+ });
+ await logPlatformAdminAuditReportAccess({
+ ctx: params.ctx,
+ owner,
+ periodStart: report.period.start,
+ periodEndExclusive: report.period.endExclusive,
+ });
+ return { status: 'ok', report };
+ } catch (error) {
+ if (error instanceof SecurityAgentAuditReportQueryError) {
+ return { status: 'query_failed', message: 'Report query did not finish' };
+ }
+ throw error;
+ }
+}
+
// ---------------------------------------------------------------------------
// Factory
// ---------------------------------------------------------------------------
@@ -150,6 +258,26 @@ export function createSecurityAgentHandlers(deps: SecurityAgentDeps
const toExtra = (input: unknown): TExtra => (input ?? {}) as any;
return {
+ trackUiInteraction: {
+ inputSchema: TrackSecurityAgentUiInteractionInputSchema,
+ handler: async ({
+ ctx,
+ input,
+ }: {
+ ctx: TRPCContext;
+ input: TrackSecurityAgentUiInteractionInput & TExtra;
+ }) => {
+ trackSecurityAgentUiInteraction({
+ distinctId: ctx.user.id,
+ userId: ctx.user.id,
+ organizationId: deps.trackingExtras(ctx, input).organizationId,
+ interaction: input.interaction,
+ });
+
+ return { success: true };
+ },
+ },
+
// -----------------------------------------------------------------------
// 1. getPermissionStatus
// -----------------------------------------------------------------------
@@ -195,6 +323,7 @@ export function createSecurityAgentHandlers(deps: SecurityAgentDeps
if (!result) {
return {
+ hasConfig: false,
isEnabled: false,
slaCriticalDays: 15,
slaHighDays: 30,
@@ -245,6 +374,7 @@ export function createSecurityAgentHandlers(deps: SecurityAgentDeps
DEFAULT_SECURITY_AGENT_REMEDIATION_MODEL;
return {
+ hasConfig: true,
isEnabled: result.isEnabled,
slaCriticalDays: result.config.sla_critical_days,
slaHighDays: result.config.sla_high_days,
@@ -1065,11 +1195,7 @@ export function createSecurityAgentHandlers(deps: SecurityAgentDeps
const accepted = await submitManualFindingDismissal({
owner: securityOwner,
- actor: {
- id: ctx.user.id,
- email: ctx.user.google_user_email,
- name: ctx.user.google_user_name,
- },
+ actor: { id: ctx.user.id },
findingId: input.findingId,
installationId,
reason: input.reason,
@@ -1122,16 +1248,24 @@ export function createSecurityAgentHandlers(deps: SecurityAgentDeps
});
}
- // Check concurrency limit
- const concurrencyCheck = await canStartAnalysis(securityOwner);
-
- if (!concurrencyCheck.allowed) {
+ if (input.restartActive && finding.analysis_status !== 'running') {
throw new TRPCError({
- code: 'TOO_MANY_REQUESTS',
- message: `Maximum concurrent analyses reached (${concurrencyCheck.currentCount}/${concurrencyCheck.limit}). Please wait for existing analyses to complete.`,
+ code: 'PRECONDITION_FAILED',
+ message: 'Only a running Sandbox Analysis can be restarted',
});
}
+ if (!input.restartActive) {
+ const concurrencyCheck = await canStartAnalysis(securityOwner);
+
+ if (!concurrencyCheck.allowed) {
+ throw new TRPCError({
+ code: 'TOO_MANY_REQUESTS',
+ message: `Maximum concurrent analyses reached (${concurrencyCheck.currentCount}/${concurrencyCheck.limit}). Please wait for existing analyses to complete.`,
+ });
+ }
+ }
+
const queued = await submitManualAnalysisStart({
findingId: input.findingId,
owner: securityOwner,
@@ -1143,6 +1277,7 @@ export function createSecurityAgentHandlers(deps: SecurityAgentDeps
},
forceSandbox: input.forceSandbox,
retrySandboxOnly: input.retrySandboxOnly,
+ restartActive: input.restartActive,
});
return { success: true, ...queued };
@@ -1184,6 +1319,14 @@ export function createSecurityAgentHandlers(deps: SecurityAgentDeps
owner: securityOwner,
actorUserId: ctx.user.id,
});
+ if (!queued.queued) return { success: false, ...queued };
+
+ trackSecurityAgentRemediationAction({
+ distinctId: ctx.user.id,
+ userId: ctx.user.id,
+ organizationId: deps.trackingExtras(ctx, input).organizationId,
+ action: 'start',
+ });
return { success: true, ...queued };
},
@@ -1225,6 +1368,14 @@ export function createSecurityAgentHandlers(deps: SecurityAgentDeps
actorUserId: ctx.user.id,
retry: true,
});
+ if (!queued.queued) return { success: false, ...queued };
+
+ trackSecurityAgentRemediationAction({
+ distinctId: ctx.user.id,
+ userId: ctx.user.id,
+ organizationId: deps.trackingExtras(ctx, input).organizationId,
+ action: 'retry',
+ });
return { success: true, ...queued };
},
@@ -1250,6 +1401,13 @@ export function createSecurityAgentHandlers(deps: SecurityAgentDeps
actorUserId: ctx.user.id,
});
+ trackSecurityAgentRemediationAction({
+ distinctId: ctx.user.id,
+ userId: ctx.user.id,
+ organizationId: deps.trackingExtras(ctx, input).organizationId,
+ action: 'cancel',
+ });
+
return result;
},
},
@@ -1299,6 +1457,13 @@ export function createSecurityAgentHandlers(deps: SecurityAgentDeps
});
return {
+ findingState: {
+ status: finding.status,
+ ignoredReason: finding.ignored_reason,
+ ignoredBy: finding.ignored_by,
+ fixedAt: finding.fixed_at,
+ updatedAt: finding.updated_at,
+ },
status: finding.analysis_status,
startedAt: finding.analysis_started_at,
completedAt: finding.analysis_completed_at,
@@ -1391,17 +1556,12 @@ export function createSecurityAgentHandlers(deps: SecurityAgentDeps
const result = await deleteFindingsByRepositoryDb({
owner: securityOwner,
repoFullName: input.repoFullName,
- });
-
- logSecurityAudit({
- owner: securityOwner,
- actor_id: ctx.user.id,
- actor_email: ctx.user.google_user_email,
- actor_name: ctx.user.google_user_name,
- action: SecurityAuditLogAction.FindingDeleted,
- resource_type: 'security_finding',
- resource_id: input.repoFullName,
- metadata: { repoFullName: input.repoFullName, deletedCount: result.deletedCount },
+ actor: buildSecurityFindingAuditHumanActor({
+ id: ctx.user.id,
+ email: ctx.user.google_user_email,
+ name: ctx.user.google_user_name,
+ isAdmin: ctx.user.is_admin,
+ }),
});
return {
@@ -1431,23 +1591,15 @@ export function createSecurityAgentHandlers(deps: SecurityAgentDeps
autoDismissEligible: async ({ ctx, input }: { ctx: TRPCContext; input: unknown }) => {
const extra = toExtra(input);
const securityOwner = deps.resolveSecurityOwner(ctx, extra);
- const result = await autoDismissEligibleFindings(securityOwner, ctx.user.id);
-
- logSecurityAudit({
- owner: securityOwner,
- actor_id: ctx.user.id,
- actor_email: ctx.user.google_user_email,
- actor_name: ctx.user.google_user_name,
- action: SecurityAuditLogAction.FindingAutoDismissed,
- resource_type: 'security_finding',
- resource_id: 'bulk',
- metadata: {
- source: 'bulk',
- dismissed: result.dismissed,
- skipped: result.skipped,
- errors: result.errors,
- },
- });
+ const result = await autoDismissEligibleFindings(
+ securityOwner,
+ buildSecurityFindingAuditHumanActor({
+ id: ctx.user.id,
+ email: ctx.user.google_user_email,
+ name: ctx.user.google_user_name,
+ isAdmin: ctx.user.is_admin,
+ })
+ );
return {
dismissed: result.dismissed,
@@ -1457,7 +1609,27 @@ export function createSecurityAgentHandlers(deps: SecurityAgentDeps
},
// -----------------------------------------------------------------------
- // 18. getDashboardStats
+ // 18. getAuditReport
+ // -----------------------------------------------------------------------
+ getAuditReport: {
+ inputSchema: SecurityAgentAuditReportInputSchema,
+ handler: async ({
+ ctx,
+ input: rawInput,
+ }: {
+ ctx: TRPCContext;
+ input: SecurityAgentAuditReportInput & TExtra;
+ }) => {
+ return assembleAuditReportResponse({
+ ctx,
+ input: rawInput,
+ deps,
+ });
+ },
+ },
+
+ // -----------------------------------------------------------------------
+ // 19. getDashboardStats
// -----------------------------------------------------------------------
getDashboardStats: {
inputSchema: GetDashboardStatsInputSchema,
@@ -1484,6 +1656,7 @@ export function createSecurityAgentHandlers(deps: SecurityAgentDeps
return getDashboardStats({
owner: securityOwner,
repoFullName: input.repoFullName,
+ slaEnabled: config?.config.sla_enabled ?? true,
slaConfig,
});
},
diff --git a/apps/web/src/lib/security-agent/services/analysis-service.test.ts b/apps/web/src/lib/security-agent/services/analysis-service.test.ts
index 2f977ba1ec..49217642ad 100644
--- a/apps/web/src/lib/security-agent/services/analysis-service.test.ts
+++ b/apps/web/src/lib/security-agent/services/analysis-service.test.ts
@@ -160,6 +160,9 @@ describe('analysis-service', () => {
const mockFinding = {
id: findingId,
+ source: 'dependabot',
+ source_id: '42',
+ status: 'open',
analysis_status: 'new',
repo_full_name: 'acme/repo',
package_name: 'lodash',
@@ -173,6 +176,9 @@ describe('analysis-service', () => {
vulnerable_version_range: '< 4.17.21',
patched_version: '4.17.21',
manifest_path: 'package.json',
+ cwe_ids: ['CWE-1321'],
+ cvss_score: '7.5',
+ raw_data: { updated_at: '2026-01-15T00:00:00.000Z' },
};
mockGetSecurityFindingById.mockResolvedValue(
@@ -231,6 +237,20 @@ describe('analysis-service', () => {
expect(mockTriageSecurityFinding).toHaveBeenCalledWith(
expect.objectContaining({ model: 'anthropic/claude-sonnet-4' })
);
+ expect(mockUpdateAnalysisStatus).toHaveBeenCalledWith(
+ findingId,
+ 'pending',
+ expect.objectContaining({
+ analysis: expect.objectContaining({
+ findingDataSnapshot: expect.objectContaining({
+ schemaVersion: 1,
+ sourceId: '42',
+ sourceUpdatedAt: '2026-01-15T00:00:00.000Z',
+ packageName: 'lodash',
+ }),
+ }),
+ })
+ );
expect(mockInitiateFromPreparedSession).toHaveBeenCalledWith({
cloudAgentSessionId: 'ses-agent-123',
});
diff --git a/apps/web/src/lib/security-agent/services/analysis-service.ts b/apps/web/src/lib/security-agent/services/analysis-service.ts
index 1668ce880a..1e93692827 100644
--- a/apps/web/src/lib/security-agent/services/analysis-service.ts
+++ b/apps/web/src/lib/security-agent/services/analysis-service.ts
@@ -21,6 +21,7 @@ import type { AnalysisErrorCode } from '../core/error-classification';
import { classifyAnalysisError, isUserActionableError } from '../core/error-classification';
import type { User, SecurityFinding } from '@kilocode/db/schema';
import { deriveCallbackToken } from '@kilocode/worker-utils/callback-token';
+import { buildSecurityFindingAnalysisInput } from '@kilocode/worker-utils/security-remediation-policy';
import {
trackSecurityAgentAnalysisStarted,
trackSecurityAgentAnalysisCompleted,
@@ -169,6 +170,7 @@ export async function finalizeAnalysis(
const analysis: SecurityFindingAnalysis = {
triage: existingAnalysis?.triage,
sandboxAnalysis,
+ findingDataSnapshot: existingAnalysis?.findingDataSnapshot,
rawMarkdown: existingAnalysis?.rawMarkdown,
analyzedAt: new Date().toISOString(),
modelUsed: model,
@@ -251,6 +253,7 @@ export async function startSecurityAnalysis(params: {
if (!finding) {
return { started: false, error: `Finding not found: ${findingId}` };
}
+ const findingDataSnapshot = buildSecurityFindingAnalysisInput(finding);
const leaseAcquired = await tryAcquireAnalysisStartLease(findingId);
if (!leaseAcquired) {
@@ -370,6 +373,7 @@ export async function startSecurityAnalysis(params: {
const analysis: SecurityFindingAnalysis = {
triage,
+ findingDataSnapshot,
analyzedAt: new Date().toISOString(),
modelUsed: triageModel,
triageModel,
@@ -423,6 +427,7 @@ export async function startSecurityAnalysis(params: {
const partialAnalysis: SecurityFindingAnalysis = {
triage,
+ findingDataSnapshot,
analyzedAt: new Date().toISOString(),
modelUsed: analysisModel,
triageModel,
diff --git a/apps/web/src/lib/security-agent/services/audit-log-service.ts b/apps/web/src/lib/security-agent/services/audit-log-service.ts
index 2104a2194f..f4bae59ad2 100644
--- a/apps/web/src/lib/security-agent/services/audit-log-service.ts
+++ b/apps/web/src/lib/security-agent/services/audit-log-service.ts
@@ -13,15 +13,27 @@ import { db } from '@/lib/drizzle';
import { SecurityAuditLogAction } from '../core/enums';
import type { SecurityReviewOwner } from '../core/types';
import { captureException } from '@sentry/nextjs';
+import { REPORTABLE_SECURITY_FINDING_AUDIT_ACTIONS } from '@kilocode/worker-utils/security-finding-audit';
export { SecurityAuditLogAction };
+type ReportableSecurityFindingAuditAction =
+ (typeof REPORTABLE_SECURITY_FINDING_AUDIT_ACTIONS)[number];
+type NonReportableSecurityAuditLogAction = Exclude<
+ SecurityAuditLogAction,
+ ReportableSecurityFindingAuditAction
+>;
+
+const reportableSecurityFindingAuditActions = new Set(
+ REPORTABLE_SECURITY_FINDING_AUDIT_ACTIONS
+);
+
type CreateSecurityAuditLogParams = {
owner: SecurityReviewOwner;
actor_id: string | null;
actor_email: string | null;
actor_name: string | null;
- action: SecurityAuditLogAction;
+ action: NonReportableSecurityAuditLogAction;
resource_type: string;
resource_id: string;
before_state?: Record;
@@ -33,6 +45,10 @@ type CreateSecurityAuditLogParams = {
export async function createSecurityAuditLog(
params: CreateSecurityAuditLogParams
): Promise {
+ if (reportableSecurityFindingAuditActions.has(params.action)) {
+ throw new Error('Reportable Security Finding activity requires canonical audit writer');
+ }
+
const {
owner,
actor_id,
diff --git a/apps/web/src/lib/security-agent/services/auto-dismiss-service.test.ts b/apps/web/src/lib/security-agent/services/auto-dismiss-service.test.ts
index 832eef0f1e..408a60a45c 100644
--- a/apps/web/src/lib/security-agent/services/auto-dismiss-service.test.ts
+++ b/apps/web/src/lib/security-agent/services/auto-dismiss-service.test.ts
@@ -7,8 +7,11 @@ import type * as posthogModule from '@/lib/security-agent/posthog-tracking';
import type {
writebackDependabotDismissal as writebackDependabotDismissalType,
maybeAutoDismissAnalysis as maybeAutoDismissAnalysisType,
+ autoDismissEligibleFindings as autoDismissEligibleFindingsType,
+ countEligibleForAutoDismiss as countEligibleForAutoDismissType,
} from './auto-dismiss-service';
import type { SecurityFinding } from '@kilocode/db/schema';
+import { SecurityAuditLogActorType } from '@kilocode/db/schema-types';
import type { SecurityFindingAnalysis } from '../core/types';
// ── Mocks ──────────────────────────────────────────────────────────────────
@@ -31,6 +34,10 @@ const mockDismissDependabotAlert = jest.fn() as jest.MockedFunction<
const mockTrackAutoDismiss = jest.fn() as jest.MockedFunction<
typeof posthogModule.trackSecurityAgentAutoDismiss
>;
+let mockTransactionFinding: SecurityFinding | null = null;
+const mockAuditRows: unknown[] = [];
+const mockUpdatedRows: unknown[] = [];
+const mockBulkFindings: Array<{ id: string; analysis: SecurityFindingAnalysis | null }> = [];
jest.mock('@/lib/security-agent/db/security-findings', () => ({
getSecurityFindingById: mockGetSecurityFindingById,
@@ -57,9 +64,48 @@ jest.mock('@/lib/drizzle', () => ({
db: {
select: jest.fn(() => ({
from: jest.fn(() => ({
- where: jest.fn(() => []),
+ where: jest.fn(() => mockBulkFindings),
})),
})),
+ transaction: jest.fn(async (callback: (tx: unknown) => Promise) => {
+ const tx = {
+ select: jest.fn(() => ({
+ from: jest.fn(() => ({
+ where: jest.fn(() => ({
+ for: jest.fn(() => ({
+ limit: jest.fn(async () =>
+ mockTransactionFinding ? [mockTransactionFinding] : []
+ ),
+ })),
+ })),
+ })),
+ })),
+ update: jest.fn(() => ({
+ set: jest.fn((values: Record) => ({
+ where: jest.fn(() => ({
+ returning: jest.fn(async () => {
+ if (!mockTransactionFinding) return [];
+ const updated = { ...mockTransactionFinding, ...values };
+ mockTransactionFinding = updated as SecurityFinding;
+ mockUpdatedRows.push(updated);
+ return [updated];
+ }),
+ })),
+ })),
+ })),
+ insert: jest.fn(() => ({
+ values: jest.fn((values: unknown) => ({
+ onConflictDoNothing: jest.fn(() => ({
+ returning: jest.fn(async () => {
+ mockAuditRows.push(values);
+ return [{ id: 'audit-row-1' }];
+ }),
+ })),
+ })),
+ })),
+ };
+ return callback(tx);
+ }),
},
}));
@@ -67,17 +113,23 @@ jest.mock('@/lib/drizzle', () => ({
let writebackDependabotDismissal: typeof writebackDependabotDismissalType;
let maybeAutoDismissAnalysis: typeof maybeAutoDismissAnalysisType;
+let autoDismissEligibleFindings: typeof autoDismissEligibleFindingsType;
+let countEligibleForAutoDismiss: typeof countEligibleForAutoDismissType;
beforeAll(async () => {
- ({ writebackDependabotDismissal, maybeAutoDismissAnalysis } =
- await import('./auto-dismiss-service'));
+ ({
+ writebackDependabotDismissal,
+ maybeAutoDismissAnalysis,
+ autoDismissEligibleFindings,
+ countEligibleForAutoDismiss,
+ } = await import('./auto-dismiss-service'));
});
// ── Helpers ────────────────────────────────────────────────────────────────
function makeFinding(overrides: Partial = {}): SecurityFinding {
return {
- id: 'finding-1',
+ id: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
owned_by_organization_id: null,
owned_by_user_id: 'user-1',
platform_integration_id: 'integration-1',
@@ -131,6 +183,10 @@ const userOwner = { type: 'user' as const, id: 'user-1', userId: 'user-1' };
beforeEach(() => {
jest.clearAllMocks();
+ mockTransactionFinding = makeFinding();
+ mockAuditRows.length = 0;
+ mockUpdatedRows.length = 0;
+ mockBulkFindings.length = 0;
});
describe('writebackDependabotDismissal', () => {
@@ -218,28 +274,31 @@ describe('writebackDependabotDismissal', () => {
});
describe('maybeAutoDismissAnalysis', () => {
+ const sandboxResult: NonNullable = {
+ isExploitable: false,
+ exploitabilityReasoning:
+ 'The dependency is installed, but the vulnerable template function is never called.',
+ usageLocations: ['package.json:17'],
+ suggestedFix: 'Upgrade to latest version',
+ suggestedAction: 'dismiss',
+ summary: 'The vulnerable code path is not reachable.',
+ rawMarkdown: 'raw',
+ analysisAt: '2024-01-01T00:00:00Z',
+ };
const sandboxAnalysis: SecurityFindingAnalysis = {
- sandboxAnalysis: {
- isExploitable: false,
- exploitabilityReasoning: 'Not exploitable because dev dependency',
- usageLocations: [],
- suggestedFix: 'Upgrade to latest version',
- suggestedAction: 'dismiss',
- summary: 'Dev dependency, not exploitable',
- rawMarkdown: 'raw',
- analysisAt: '2024-01-01T00:00:00Z',
- },
+ sandboxAnalysis: sandboxResult,
analyzedAt: '2024-01-01T00:00:00Z',
};
+ const triageResult: NonNullable = {
+ suggestedAction: 'dismiss',
+ confidence: 'high',
+ needsSandboxAnalysis: false,
+ needsSandboxReasoning: 'Dev dependency, not exploitable',
+ triageAt: '2024-01-01T00:00:00Z',
+ };
const triageAnalysis: SecurityFindingAnalysis = {
- triage: {
- suggestedAction: 'dismiss',
- confidence: 'high',
- needsSandboxAnalysis: false,
- needsSandboxReasoning: 'Dev dependency, not exploitable',
- triageAt: '2024-01-01T00:00:00Z',
- },
+ triage: triageResult,
analyzedAt: '2024-01-01T00:00:00Z',
};
@@ -299,6 +358,65 @@ describe('maybeAutoDismissAnalysis', () => {
);
});
+ it.each([
+ ['exploitable', true, 'open_pr'],
+ ['unknown exploitability', 'unknown', 'manual_review'],
+ ['inconsistent not-exploitable result', false, 'manual_review'],
+ ] as const)(
+ 'keeps findings open when authoritative sandbox result is %s',
+ async (_label, isExploitable, suggestedAction) => {
+ mockGetSecurityAgentConfig.mockResolvedValue({
+ auto_dismiss_enabled: true,
+ auto_dismiss_confidence_threshold: 'high',
+ } as Awaited>);
+
+ const result = await maybeAutoDismissAnalysis({
+ findingId: 'finding-1',
+ analysis: {
+ analyzedAt: '2024-01-01T00:00:00Z',
+ triage: triageResult,
+ sandboxAnalysis: {
+ ...sandboxResult,
+ isExploitable,
+ suggestedAction,
+ },
+ },
+ owner: { userId: 'user-1' },
+ userId: 'user-1',
+ });
+
+ expect(result).toEqual({ dismissed: false });
+ expect(mockUpdatedRows).toEqual([]);
+ expect(mockAuditRows).toEqual([]);
+ expect(mockDismissDependabotAlert).not.toHaveBeenCalled();
+ }
+ );
+
+ it('keeps triage findings open when triage says sandbox analysis is needed', async () => {
+ mockGetSecurityAgentConfig.mockResolvedValue({
+ auto_dismiss_enabled: true,
+ auto_dismiss_confidence_threshold: 'high',
+ } as Awaited>);
+
+ const result = await maybeAutoDismissAnalysis({
+ findingId: 'finding-1',
+ analysis: {
+ analyzedAt: '2024-01-01T00:00:00Z',
+ triage: {
+ ...triageResult,
+ needsSandboxAnalysis: true,
+ },
+ },
+ owner: { userId: 'user-1' },
+ userId: 'user-1',
+ });
+
+ expect(result).toEqual({ dismissed: false });
+ expect(mockUpdatedRows).toEqual([]);
+ expect(mockAuditRows).toEqual([]);
+ expect(mockDismissDependabotAlert).not.toHaveBeenCalled();
+ });
+
it('does not write back when auto-dismiss is disabled', async () => {
mockGetSecurityAgentConfig.mockResolvedValue({
auto_dismiss_enabled: false,
@@ -334,6 +452,129 @@ describe('maybeAutoDismissAnalysis', () => {
// Should still succeed — writeback failure is non-fatal
expect(result).toEqual({ dismissed: true, source: 'sandbox' });
- expect(mockUpdateSecurityFindingStatus).toHaveBeenCalled();
+ expect(mockUpdatedRows).toHaveLength(1);
+ expect(mockUpdatedRows[0]).toMatchObject({ status: 'ignored', ignored_reason: 'not_used' });
+ expect(mockAuditRows[0]).toMatchObject({
+ actor_type: 'system',
+ action: 'security.finding.auto_dismissed',
+ finding_id: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
+ source_context: 'web',
+ schema_version: 1,
+ });
+ });
+
+ describe('autoDismissEligibleFindings', () => {
+ it('records event-time actor and unique operation identity for bulk dismissals', async () => {
+ const analysis: SecurityFindingAnalysis = {
+ triage: {
+ suggestedAction: 'dismiss',
+ confidence: 'high',
+ needsSandboxAnalysis: false,
+ needsSandboxReasoning: 'No runtime path',
+ triageAt: '2026-06-16T10:00:00.000Z',
+ },
+ analyzedAt: '2026-06-16T10:00:00.000Z',
+ };
+ mockBulkFindings.push({ id: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb', analysis });
+ mockGetSecurityAgentConfig.mockResolvedValue({
+ auto_dismiss_enabled: true,
+ auto_dismiss_confidence_threshold: 'high',
+ } as Awaited>);
+ mockGetSecurityFindingById.mockResolvedValue(makeFinding());
+ mockGetIntegrationForOwner.mockResolvedValue(makeIntegration(undefined as unknown as string));
+
+ await expect(
+ autoDismissEligibleFindings(
+ { userId: 'user-1' },
+ {
+ type: SecurityAuditLogActorType.CustomerUser,
+ id: 'user-1',
+ email: 'owner@example.com',
+ name: 'Owner Example',
+ }
+ )
+ ).resolves.toEqual({ dismissed: 1, skipped: 0, errors: 0 });
+
+ expect(mockAuditRows).toHaveLength(1);
+ expect(mockAuditRows[0]).toMatchObject({
+ actor_id: 'user-1',
+ actor_email: 'owner@example.com',
+ actor_name: 'Owner Example',
+ actor_type: 'customer_user',
+ action: 'security.finding.auto_dismissed',
+ finding_id: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
+ source_context: 'web',
+ schema_version: 1,
+ metadata: expect.objectContaining({
+ trigger: 'auto_dismiss_policy',
+ dismiss_source: 'bulk',
+ correlation_id: expect.any(String),
+ }),
+ });
+ });
+
+ it('does not bulk-dismiss from triage when a sandbox result exists', async () => {
+ mockBulkFindings.push({
+ id: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
+ analysis: {
+ analyzedAt: '2026-06-16T10:00:00.000Z',
+ triage: triageResult,
+ sandboxAnalysis: {
+ ...sandboxResult,
+ isExploitable: true,
+ suggestedAction: 'open_pr',
+ },
+ },
+ });
+ mockGetSecurityAgentConfig.mockResolvedValue({
+ auto_dismiss_enabled: true,
+ auto_dismiss_confidence_threshold: 'high',
+ } as Awaited>);
+
+ await expect(
+ autoDismissEligibleFindings(
+ { userId: 'user-1' },
+ {
+ type: SecurityAuditLogActorType.CustomerUser,
+ id: 'user-1',
+ email: 'owner@example.com',
+ name: 'Owner Example',
+ }
+ )
+ ).resolves.toEqual({ dismissed: 0, skipped: 1, errors: 0 });
+
+ expect(mockUpdatedRows).toEqual([]);
+ expect(mockAuditRows).toEqual([]);
+ expect(mockDismissDependabotAlert).not.toHaveBeenCalled();
+ });
+
+ it('excludes sandbox-analyzed findings from triage-only eligibility counts', async () => {
+ mockBulkFindings.push(
+ {
+ id: 'triage-only',
+ analysis: {
+ analyzedAt: '2026-06-16T10:00:00.000Z',
+ triage: triageResult,
+ },
+ },
+ {
+ id: 'sandbox-completed',
+ analysis: {
+ analyzedAt: '2026-06-16T10:00:00.000Z',
+ triage: triageResult,
+ sandboxAnalysis: {
+ ...sandboxResult,
+ isExploitable: true,
+ suggestedAction: 'open_pr',
+ },
+ },
+ }
+ );
+
+ await expect(countEligibleForAutoDismiss({ userId: 'user-1' }, 'user-1')).resolves.toEqual({
+ eligible: 1,
+ byConfidence: { high: 1, medium: 0, low: 0 },
+ });
+ });
});
});
diff --git a/apps/web/src/lib/security-agent/services/auto-dismiss-service.ts b/apps/web/src/lib/security-agent/services/auto-dismiss-service.ts
index 37435c1558..f58cef9aaa 100644
--- a/apps/web/src/lib/security-agent/services/auto-dismiss-service.ts
+++ b/apps/web/src/lib/security-agent/services/auto-dismiss-service.ts
@@ -5,8 +5,8 @@
* Auto-dismiss is OFF by default and must be explicitly enabled per-organization.
*
* Unified auto-dismiss logic:
- * - After Tier 1 triage: if triage.suggestedAction === 'dismiss' (with confidence threshold)
- * - After Tier 2 sandbox: if sandboxAnalysis.isExploitable === false (no confidence threshold)
+ * - After Tier 1 triage: dismiss only when no sandbox is needed and confidence meets policy
+ * - After Tier 2 sandbox: dismiss only when analysis says not exploitable and recommends dismissal
*/
import 'server-only';
@@ -22,7 +22,19 @@ import { dismissDependabotAlert } from '../github/dependabot-api';
import type { Owner } from '@/lib/code-reviews/core';
import type { SecurityFindingAnalysis, SecurityReviewOwner } from '../core/types';
import { sentryLogger } from '@/lib/utils.server';
-import { logSecurityAudit, SecurityAuditLogAction } from './audit-log-service';
+import {
+ SecurityAuditLogAction,
+ SecurityFindingAuditSourceContext,
+} from '@kilocode/db/schema-types';
+import {
+ SECURITY_FINDING_AUDIT_SYSTEM_ACTOR,
+ deriveSecurityFindingAuditEventKey,
+ insertSecurityFindingAuditEvent,
+ type SecurityFindingAuditActor,
+ type SecurityFindingAuditEventFinding,
+ type SecurityFindingAuditHumanActor,
+ type SecurityFindingAuditOwner,
+} from '@kilocode/worker-utils/security-finding-audit';
import { parseDependabotDismissalTarget } from '@kilocode/worker-utils/dependabot-dismissal-target';
const log = sentryLogger('security-agent:auto-dismiss', 'info');
@@ -42,6 +54,39 @@ function toOwner(securityOwner: SecurityReviewOwner, userId: string): Owner {
throw new Error('Invalid owner: must have either organizationId or userId');
}
+function toAuditOwner(owner: SecurityReviewOwner): SecurityFindingAuditOwner {
+ if ('organizationId' in owner && owner.organizationId) {
+ return { type: 'organization', organizationId: owner.organizationId };
+ }
+ if ('userId' in owner && owner.userId) {
+ return { type: 'user', userId: owner.userId };
+ }
+ throw new Error('Invalid owner: must have either organizationId or userId');
+}
+
+function ownerAuditKeyPart(owner: SecurityReviewOwner): string {
+ if ('organizationId' in owner && owner.organizationId)
+ return `organization:${owner.organizationId}`;
+ if ('userId' in owner && owner.userId) return `user:${owner.userId}`;
+ throw new Error('Invalid owner: must have either organizationId or userId');
+}
+
+function ownerFindingCondition(owner: SecurityReviewOwner) {
+ if ('organizationId' in owner && owner.organizationId) {
+ return eq(security_findings.owned_by_organization_id, owner.organizationId);
+ }
+ if ('userId' in owner && owner.userId) {
+ return eq(security_findings.owned_by_user_id, owner.userId);
+ }
+ throw new Error('Invalid owner: must have either organizationId or userId');
+}
+
+function toAuditFinding(
+ finding: SecurityFindingAuditEventFinding
+): SecurityFindingAuditEventFinding {
+ return finding;
+}
+
/**
* Dismiss a security finding with the given reason
*/
@@ -59,6 +104,86 @@ export async function dismissFinding(
});
}
+async function dismissFindingWithAuditEvent(
+ findingId: string,
+ params: {
+ owner: SecurityReviewOwner;
+ reason: string;
+ comment: string;
+ dismissedBy: string;
+ dismissSource: AutoDismissSource | 'bulk';
+ confidence?: string | null;
+ correlationId?: string;
+ actor: SecurityFindingAuditActor;
+ }
+): Promise {
+ const occurredAt = new Date().toISOString();
+ return db.transaction(async tx => {
+ const [finding] = await tx
+ .select()
+ .from(security_findings)
+ .where(and(eq(security_findings.id, findingId), ownerFindingCondition(params.owner)))
+ .for('update')
+ .limit(1);
+
+ if (!finding) {
+ throw new Error('Security finding not found for owner');
+ }
+
+ if (finding.status !== 'open') {
+ return false;
+ }
+
+ const ignoredBy = params.dismissedBy || `auto-dismiss: ${params.comment}`;
+ const [updatedFinding] = await tx
+ .update(security_findings)
+ .set({
+ status: 'ignored',
+ ignored_reason: params.reason,
+ ignored_by: ignoredBy,
+ updated_at: occurredAt,
+ })
+ .where(
+ and(
+ eq(security_findings.id, findingId),
+ ownerFindingCondition(params.owner),
+ eq(security_findings.status, 'open')
+ )
+ )
+ .returning();
+
+ if (!updatedFinding) {
+ throw new Error('Security finding status update failed');
+ }
+
+ await insertSecurityFindingAuditEvent(tx, {
+ owner: toAuditOwner(params.owner),
+ finding: toAuditFinding(updatedFinding),
+ actor: params.actor,
+ action: SecurityAuditLogAction.FindingAutoDismissed,
+ occurredAt,
+ eventKey: deriveSecurityFindingAuditEventKey([
+ ownerAuditKeyPart(params.owner),
+ findingId,
+ SecurityAuditLogAction.FindingAutoDismissed,
+ params.dismissSource,
+ params.correlationId || 'none',
+ ]),
+ sourceContext: SecurityFindingAuditSourceContext.Web,
+ beforeState: { status: finding.status },
+ afterState: { status: 'ignored', reason_code: params.reason },
+ metadata: {
+ trigger: 'auto_dismiss_policy',
+ dismiss_source: params.dismissSource,
+ ...(params.confidence ? { confidence: params.confidence } : {}),
+ ...(params.correlationId ? { correlation_id: params.correlationId } : {}),
+ },
+ });
+
+ return true;
+ });
+}
+
/**
* Write back a dismissal to Dependabot on GitHub.
* Fetches the finding and integration data, then calls the Dependabot API.
@@ -130,8 +255,8 @@ type AutoDismissSource = 'triage' | 'sandbox';
* Only runs if auto-dismiss is enabled in config.
*
* Priority:
- * 1. If sandboxAnalysis exists and isExploitable === false -> dismiss (no confidence threshold)
- * 2. If triage.suggestedAction === 'dismiss' -> dismiss (with confidence threshold)
+ * 1. Treat any sandbox analysis as authoritative and dismiss only a coherent not-exploitable result
+ * 2. Without sandbox analysis, dismiss a triage result only when no sandbox is needed
*
* @param options.findingId - The ID of the finding to potentially dismiss
* @param options.analysis - The full analysis result (triage + optional sandbox)
@@ -156,24 +281,33 @@ export async function maybeAutoDismissAnalysis(options: {
return { dismissed: false };
}
- // Priority 1: Check sandbox analysis (no confidence threshold - sandbox is definitive)
- if (analysis.sandboxAnalysis?.isExploitable === false) {
- await dismissFinding(findingId, {
+ const sandbox = analysis.sandboxAnalysis;
+ if (sandbox) {
+ if (sandbox.isExploitable !== false || sandbox.suggestedAction !== 'dismiss') {
+ return { dismissed: false };
+ }
+
+ const dismissed = await dismissFindingWithAuditEvent(findingId, {
+ owner,
reason: 'not_used',
- comment: analysis.sandboxAnalysis.exploitabilityReasoning,
+ comment: sandbox.exploitabilityReasoning,
dismissedBy: 'auto-sandbox',
+ dismissSource: 'sandbox',
+ correlationId,
+ actor: SECURITY_FINDING_AUDIT_SYSTEM_ACTOR,
});
+ if (!dismissed) return { dismissed: false };
await safeWritebackDependabotDismissal(
findingId,
ownerConverted,
- analysis.sandboxAnalysis.exploitabilityReasoning
+ sandbox.exploitabilityReasoning
);
log('Auto-dismissed finding (sandbox)', {
correlationId,
findingId,
- reasoning: analysis.sandboxAnalysis.exploitabilityReasoning.slice(0, 100),
+ reasoning: sandbox.exploitabilityReasoning.slice(0, 100),
});
trackSecurityAgentAutoDismiss({
@@ -184,29 +318,11 @@ export async function maybeAutoDismissAnalysis(options: {
source: 'sandbox',
});
- logSecurityAudit({
- owner,
- actor_id: null,
- actor_email: null,
- actor_name: null,
- action: SecurityAuditLogAction.FindingAutoDismissed,
- resource_type: 'security_finding',
- resource_id: findingId,
- after_state: { status: 'ignored' },
- metadata: {
- source: 'system',
- trigger: 'auto_dismiss_policy',
- dismissSource: 'sandbox',
- correlationId,
- },
- });
-
return { dismissed: true, source: 'sandbox' };
}
- // Priority 2: Check triage (with confidence threshold)
const triage = analysis.triage;
- if (triage?.suggestedAction === 'dismiss') {
+ if (triage?.needsSandboxAnalysis === false && triage.suggestedAction === 'dismiss') {
const threshold = config.auto_dismiss_confidence_threshold ?? 'high';
// Check confidence threshold
@@ -216,11 +332,17 @@ export async function maybeAutoDismissAnalysis(options: {
(threshold === 'high' && triage.confidence === 'high');
if (meetsThreshold) {
- await dismissFinding(findingId, {
+ const dismissed = await dismissFindingWithAuditEvent(findingId, {
+ owner,
reason: 'not_used',
comment: triage.needsSandboxReasoning,
dismissedBy: 'auto-triage',
+ dismissSource: 'triage',
+ confidence: triage.confidence,
+ correlationId,
+ actor: SECURITY_FINDING_AUDIT_SYSTEM_ACTOR,
});
+ if (!dismissed) return { dismissed: false };
await safeWritebackDependabotDismissal(
findingId,
@@ -244,24 +366,6 @@ export async function maybeAutoDismissAnalysis(options: {
confidence: triage.confidence,
});
- logSecurityAudit({
- owner,
- actor_id: null,
- actor_email: null,
- actor_name: null,
- action: SecurityAuditLogAction.FindingAutoDismissed,
- resource_type: 'security_finding',
- resource_id: findingId,
- after_state: { status: 'ignored' },
- metadata: {
- source: 'system',
- trigger: 'auto_dismiss_policy',
- dismissSource: 'triage',
- confidence: triage.confidence,
- correlationId,
- },
- });
-
return { dismissed: true, source: 'triage' };
}
}
@@ -285,13 +389,15 @@ export type AutoDismissResult = {
* This is useful for processing findings that were triaged before auto-dismiss was enabled.
*
* @param owner - The security review owner (org or user)
- * @param userId - The user performing the action (for audit/permissions)
+ * @param actor - The user performing the bulk action
*/
export async function autoDismissEligibleFindings(
owner: SecurityReviewOwner,
- userId: string
+ actor: SecurityFindingAuditHumanActor
): Promise {
+ const userId = actor.id;
const ownerConverted = toOwner(owner, userId);
+ const operationId = crypto.randomUUID();
const config = await getSecurityAgentConfig(ownerConverted);
if (!config.auto_dismiss_enabled) {
@@ -331,7 +437,12 @@ export async function autoDismissEligibleFindings(
const analysis = finding.analysis;
const triage = analysis?.triage;
- if (!triage) {
+ if (
+ !triage ||
+ analysis?.sandboxAnalysis !== undefined ||
+ triage.needsSandboxAnalysis !== false ||
+ triage.suggestedAction !== 'dismiss'
+ ) {
skipped++;
continue;
}
@@ -346,11 +457,20 @@ export async function autoDismissEligibleFindings(
continue;
}
- await dismissFinding(finding.id, {
+ const didDismiss = await dismissFindingWithAuditEvent(finding.id, {
+ owner,
reason: 'not_used',
comment: triage.needsSandboxReasoning,
dismissedBy: 'auto-triage-bulk',
+ dismissSource: 'bulk',
+ confidence: triage.confidence,
+ correlationId: operationId,
+ actor,
});
+ if (!didDismiss) {
+ skipped++;
+ continue;
+ }
await safeWritebackDependabotDismissal(
finding.id,
ownerConverted,
@@ -420,17 +540,26 @@ export async function countEligibleForAutoDismiss(
);
const byConfidence = { high: 0, medium: 0, low: 0 };
+ let eligible = 0;
for (const finding of findings) {
const analysis = finding.analysis;
- const confidence = analysis?.triage?.confidence;
- if (confidence && confidence in byConfidence) {
- byConfidence[confidence]++;
+ const triage = analysis?.triage;
+ if (
+ !triage ||
+ analysis?.sandboxAnalysis !== undefined ||
+ triage.needsSandboxAnalysis !== false ||
+ triage.suggestedAction !== 'dismiss'
+ ) {
+ continue;
}
+
+ eligible++;
+ byConfidence[triage.confidence]++;
}
return {
- eligible: findings.length,
+ eligible,
byConfidence,
};
}
diff --git a/apps/web/src/lib/security-agent/services/extraction-service.ts b/apps/web/src/lib/security-agent/services/extraction-service.ts
index 7b6cec5ab4..54ddd9518e 100644
--- a/apps/web/src/lib/security-agent/services/extraction-service.ts
+++ b/apps/web/src/lib/security-agent/services/extraction-service.ts
@@ -152,7 +152,16 @@ ${rawMarkdown}
---
-Please extract the structured analysis from the report above and call the submit_analysis_extraction tool with your findings.`;
+Please extract the structured analysis from the report above and call the submit_analysis_extraction tool with your findings. If tool calls are unavailable, return only a JSON object matching the tool parameters, without markdown or prose.`;
+}
+
+function extractJsonContent(content: string | null): string | null {
+ const trimmed = content?.trim();
+ if (!trimmed) return null;
+ if (trimmed.startsWith('{') && trimmed.endsWith('}')) return trimmed;
+
+ const fencedJson = trimmed.match(/^```(?:json)?\s*\n?([\s\S]*?)\n?```$/i);
+ return fencedJson?.[1]?.trim() || null;
}
/**
@@ -213,6 +222,7 @@ function parseExtractionResult(
return {
isExploitable,
+ extractionStatus: 'succeeded',
exploitabilityReasoning: parsed.exploitabilityReasoning,
usageLocations: parsed.usageLocations.map(String),
suggestedFix: parsed.suggestedFix,
@@ -236,6 +246,7 @@ function createFallbackExtraction(
): SecurityFindingSandboxAnalysis {
return {
isExploitable: 'unknown',
+ extractionStatus: 'failed',
exploitabilityReasoning: `Extraction failed: ${reason}. Please review the raw analysis.`,
usageLocations: [],
suggestedFix: 'Review the raw analysis for fix recommendations.',
@@ -307,10 +318,7 @@ export async function extractSandboxAnalysis(options: {
model,
messages,
tools: [SUBMIT_EXTRACTION_TOOL],
- tool_choice: {
- type: 'function',
- function: { name: 'submit_analysis_extraction' },
- },
+ tool_choice: 'auto',
},
organizationId,
feature: 'security-agent',
@@ -394,28 +402,30 @@ export async function extractSandboxAnalysis(options: {
}
const message = choice.message;
- const toolCall = message.tool_calls?.[0];
-
- if (!toolCall || toolCall.type !== 'function') {
- logError('No tool call in response', { correlationId, findingId: finding.id });
- span.setAttribute('security_agent.is_fallback', true);
- return createFallbackExtraction(rawMarkdown, 'LLM did not call the extraction tool');
- }
-
- if (toolCall.function.name !== 'submit_analysis_extraction') {
- logError('Unexpected tool call', {
+ const toolCall = message.tool_calls?.find(
+ candidate =>
+ candidate.type === 'function' &&
+ candidate.function.name === 'submit_analysis_extraction'
+ );
+ const toolArguments =
+ toolCall?.type === 'function' ? toolCall.function.arguments : undefined;
+ const structuredJson =
+ toolArguments ??
+ extractJsonContent(typeof message.content === 'string' ? message.content : null);
+
+ if (!structuredJson) {
+ logError('No structured result in response', {
correlationId,
findingId: finding.id,
- tool: toolCall.function.name,
});
span.setAttribute('security_agent.is_fallback', true);
return createFallbackExtraction(
rawMarkdown,
- `Unexpected tool: ${toolCall.function.name}`
+ 'LLM did not return a structured extraction result'
);
}
- const parsed = parseExtractionResult(toolCall.function.arguments, rawMarkdown);
+ const parsed = parseExtractionResult(structuredJson, rawMarkdown);
if (!parsed) {
span.setAttribute('security_agent.is_fallback', true);
return createFallbackExtraction(rawMarkdown, 'Failed to parse extraction result');
diff --git a/apps/web/src/lib/security-agent/services/manual-analysis-client.test.ts b/apps/web/src/lib/security-agent/services/manual-analysis-client.test.ts
index 5dfa967a05..6a97fa01ae 100644
--- a/apps/web/src/lib/security-agent/services/manual-analysis-client.test.ts
+++ b/apps/web/src/lib/security-agent/services/manual-analysis-client.test.ts
@@ -33,6 +33,7 @@ describe('submitManualAnalysisStart', () => {
requestedModels: { analysisModel: 'analysis/model' },
forceSandbox: true,
retrySandboxOnly: true,
+ restartActive: true,
})
).resolves.toEqual({
queued: true,
@@ -55,6 +56,7 @@ describe('submitManualAnalysisStart', () => {
requestedModels: { analysisModel: 'analysis/model' },
forceSandbox: true,
retrySandboxOnly: true,
+ restartActive: true,
}),
})
);
diff --git a/apps/web/src/lib/security-agent/services/manual-analysis-client.ts b/apps/web/src/lib/security-agent/services/manual-analysis-client.ts
index eeaf57ee25..ef27fdbb80 100644
--- a/apps/web/src/lib/security-agent/services/manual-analysis-client.ts
+++ b/apps/web/src/lib/security-agent/services/manual-analysis-client.ts
@@ -16,6 +16,7 @@ type ManualAnalysisStartParams = {
};
forceSandbox?: boolean;
retrySandboxOnly?: boolean;
+ restartActive?: boolean;
};
type ManualAnalysisResponse = {
@@ -51,6 +52,7 @@ export async function submitManualAnalysisStart(
requestedModels: params.requestedModels,
forceSandbox: params.forceSandbox,
retrySandboxOnly: params.retrySandboxOnly,
+ restartActive: params.restartActive,
}),
}
);
diff --git a/apps/web/src/lib/security-agent/services/manual-dismiss-client.test.ts b/apps/web/src/lib/security-agent/services/manual-dismiss-client.test.ts
index 0dcb542c0a..38c2f2e449 100644
--- a/apps/web/src/lib/security-agent/services/manual-dismiss-client.test.ts
+++ b/apps/web/src/lib/security-agent/services/manual-dismiss-client.test.ts
@@ -13,7 +13,7 @@ describe('submitManualFindingDismissal', () => {
mockFetch.mockReset();
});
- it('submits dismissal actor context and returns accepted correlation ids', async () => {
+ it('submits only stable dismissal actor identity and returns accepted correlation ids', async () => {
mockFetch.mockResolvedValue({
ok: true,
status: 202,
@@ -30,7 +30,7 @@ describe('submitManualFindingDismissal', () => {
await expect(
submitManualFindingDismissal({
owner: { organizationId: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa' },
- actor: { id: 'user-123', email: 'owner@example.com', name: 'Owner Example' },
+ actor: { id: 'user-123' },
findingId: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
installationId: 'installation-123',
reason: 'not_used',
@@ -53,5 +53,10 @@ describe('submitManualFindingDismissal', () => {
},
})
);
+ const request = mockFetch.mock.calls[0]?.[1] as RequestInit;
+ expect(JSON.parse(String(request.body))).toMatchObject({
+ actor: { id: 'user-123' },
+ });
+ expect(JSON.parse(String(request.body)).actor).toEqual({ id: 'user-123' });
});
});
diff --git a/apps/web/src/lib/security-agent/services/manual-dismiss-client.ts b/apps/web/src/lib/security-agent/services/manual-dismiss-client.ts
index 9b353928ba..e869306e96 100644
--- a/apps/web/src/lib/security-agent/services/manual-dismiss-client.ts
+++ b/apps/web/src/lib/security-agent/services/manual-dismiss-client.ts
@@ -7,8 +7,6 @@ type ManualFindingDismissalOwner =
type ManualFindingDismissalActor = {
id: string;
- email?: string | null;
- name?: string | null;
};
type DismissReason = 'fix_started' | 'no_bandwidth' | 'tolerable_risk' | 'inaccurate' | 'not_used';
diff --git a/apps/web/src/lib/security-agent/services/manual-remediation-client.test.ts b/apps/web/src/lib/security-agent/services/manual-remediation-client.test.ts
new file mode 100644
index 0000000000..72c1da4ea5
--- /dev/null
+++ b/apps/web/src/lib/security-agent/services/manual-remediation-client.test.ts
@@ -0,0 +1,88 @@
+import { submitManualRemediationStart } from './manual-remediation-client';
+
+jest.mock('@/lib/config.server', () => ({
+ INTERNAL_API_SECRET: 'test-internal-secret',
+ SECURITY_AUTO_ANALYSIS_WORKER_URL: 'https://security-auto-analysis.test',
+}));
+
+const mockFetch = jest.fn();
+global.fetch = mockFetch;
+
+describe('submitManualRemediationStart', () => {
+ beforeEach(() => {
+ mockFetch.mockReset();
+ });
+
+ it('returns queued attempt correlation for accepted admission', async () => {
+ mockFetch.mockResolvedValue({
+ ok: true,
+ status: 202,
+ json: () =>
+ Promise.resolve({
+ success: true,
+ accepted: true,
+ admitted: true,
+ remediationId: 'dddddddd-dddd-4ddd-8ddd-dddddddddddd',
+ attemptId: 'eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee',
+ attemptNumber: 1,
+ }),
+ });
+
+ await expect(
+ submitManualRemediationStart({
+ findingId: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
+ owner: { userId: 'user-123' },
+ actorUserId: 'user-123',
+ })
+ ).resolves.toEqual({
+ queued: true,
+ remediationId: 'dddddddd-dddd-4ddd-8ddd-dddddddddddd',
+ attemptId: 'eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee',
+ attemptNumber: 1,
+ });
+ });
+
+ it('returns a typed policy rejection for analysis_required', async () => {
+ mockFetch.mockResolvedValue({
+ ok: false,
+ status: 409,
+ json: () =>
+ Promise.resolve({
+ success: false,
+ accepted: false,
+ admitted: false,
+ reason: 'analysis_required',
+ }),
+ });
+
+ await expect(
+ submitManualRemediationStart({
+ findingId: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
+ owner: { userId: 'user-123' },
+ actorUserId: 'user-123',
+ })
+ ).resolves.toEqual({ queued: false, reason: 'analysis_required' });
+ });
+
+ it('returns a typed not-found rejection when the finding disappears', async () => {
+ mockFetch.mockResolvedValue({
+ ok: false,
+ status: 404,
+ json: () =>
+ Promise.resolve({
+ success: false,
+ accepted: false,
+ admitted: false,
+ reason: 'finding_not_found',
+ }),
+ });
+
+ await expect(
+ submitManualRemediationStart({
+ findingId: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
+ owner: { userId: 'user-123' },
+ actorUserId: 'user-123',
+ })
+ ).resolves.toEqual({ queued: false, reason: 'finding_not_found' });
+ });
+});
diff --git a/apps/web/src/lib/security-agent/services/manual-remediation-client.ts b/apps/web/src/lib/security-agent/services/manual-remediation-client.ts
index 4ba74042d6..c618c43fed 100644
--- a/apps/web/src/lib/security-agent/services/manual-remediation-client.ts
+++ b/apps/web/src/lib/security-agent/services/manual-remediation-client.ts
@@ -1,5 +1,10 @@
import 'server-only';
import { INTERNAL_API_SECRET, SECURITY_AUTO_ANALYSIS_WORKER_URL } from '@/lib/config.server';
+import {
+ SECURITY_REMEDIATION_ADMISSION_REJECTION_REASONS,
+ type SecurityRemediationAdmissionRejectionReason,
+} from '@kilocode/worker-utils/security-remediation-policy';
+import { z } from 'zod';
type RemediationOwner =
| { organizationId: string; userId?: never }
@@ -24,16 +29,36 @@ type ApplyAutoRemediationParams = {
actorUserId: string;
};
-type ManualRemediationStartResponse = {
- success?: boolean;
- accepted?: boolean;
- admitted?: boolean;
- remediationId?: string;
- attemptId?: string;
- attemptNumber?: number;
- reason?: string;
- error?: string;
-};
+const ManualRemediationStartResponseSchema = z.discriminatedUnion('admitted', [
+ z.object({
+ success: z.literal(true),
+ accepted: z.literal(true),
+ admitted: z.literal(true),
+ remediationId: z.string(),
+ attemptId: z.string(),
+ attemptNumber: z.number(),
+ }),
+ z.object({
+ success: z.literal(false),
+ accepted: z.literal(false),
+ admitted: z.literal(false),
+ reason: z.enum(SECURITY_REMEDIATION_ADMISSION_REJECTION_REASONS),
+ }),
+]);
+
+const WorkerErrorResponseSchema = z.object({ error: z.string() });
+
+export type ManualRemediationStartResult =
+ | {
+ queued: true;
+ remediationId: string;
+ attemptId: string;
+ attemptNumber: number;
+ }
+ | {
+ queued: false;
+ reason: SecurityRemediationAdmissionRejectionReason;
+ };
type RemediationCancellationResponse = {
success?: boolean;
@@ -61,12 +86,9 @@ async function parseJsonResponse(response: Response): Promise {
return (await response.json()) as T;
}
-export async function submitManualRemediationStart(params: ManualRemediationStartParams): Promise<{
- queued: true;
- remediationId: string;
- attemptId: string;
- attemptNumber: number;
-}> {
+export async function submitManualRemediationStart(
+ params: ManualRemediationStartParams
+): Promise {
requireWorkerConfig();
const response = await fetch(`${SECURITY_AUTO_ANALYSIS_WORKER_URL}/internal/remediation/start`, {
@@ -83,27 +105,30 @@ export async function submitManualRemediationStart(params: ManualRemediationStar
retry: params.retry,
}),
});
- const body = await parseJsonResponse(response);
+ const body: unknown = await response.json();
+ const parsedBody = ManualRemediationStartResponseSchema.safeParse(body);
if (!response.ok) {
+ if (parsedBody.success && !parsedBody.data.admitted) {
+ const expectedStatus = parsedBody.data.reason === 'finding_not_found' ? 404 : 409;
+ if (response.status === expectedStatus) {
+ return { queued: false, reason: parsedBody.data.reason };
+ }
+ }
+ const parsedError = WorkerErrorResponseSchema.safeParse(body);
throw new Error(
- body.reason ?? body.error ?? `Remediation request failed with ${response.status}`
+ parsedError.success
+ ? parsedError.data.error
+ : `Remediation request failed with ${response.status}`
);
}
- if (
- body.success !== true ||
- body.accepted !== true ||
- body.admitted !== true ||
- typeof body.remediationId !== 'string' ||
- typeof body.attemptId !== 'string' ||
- typeof body.attemptNumber !== 'number'
- ) {
+ if (!parsedBody.success || !parsedBody.data.admitted) {
throw new Error('Security remediation Worker returned an invalid accepted response');
}
return {
queued: true,
- remediationId: body.remediationId,
- attemptId: body.attemptId,
- attemptNumber: body.attemptNumber,
+ remediationId: parsedBody.data.remediationId,
+ attemptId: parsedBody.data.attemptId,
+ attemptNumber: parsedBody.data.attemptNumber,
};
}
diff --git a/apps/web/src/lib/security-agent/services/triage-service.ts b/apps/web/src/lib/security-agent/services/triage-service.ts
index 17adc7baf9..6b22954c3c 100644
--- a/apps/web/src/lib/security-agent/services/triage-service.ts
+++ b/apps/web/src/lib/security-agent/services/triage-service.ts
@@ -126,7 +126,16 @@ function buildTriagePrompt(finding: SecurityFinding): string {
${rawData ? `**Additional Context**: ${JSON.stringify(rawData, null, 2).slice(0, 1000)}` : ''}
-Please analyze this vulnerability and call the submit_triage_result tool with your assessment.`;
+Please analyze this vulnerability and call the submit_triage_result tool with your assessment. If tool calls are unavailable, return only a JSON object matching the tool parameters, without markdown or prose.`;
+}
+
+function extractJsonContent(content: string | null): string | null {
+ const trimmed = content?.trim();
+ if (!trimmed) return null;
+ if (trimmed.startsWith('{') && trimmed.endsWith('}')) return trimmed;
+
+ const fencedJson = trimmed.match(/^```(?:json)?\s*\n?([\s\S]*?)\n?```$/i);
+ return fencedJson?.[1]?.trim() || null;
}
/**
@@ -243,10 +252,7 @@ export async function triageSecurityFinding(options: {
model,
messages,
tools: [SUBMIT_TRIAGE_TOOL],
- tool_choice: {
- type: 'function',
- function: { name: 'submit_triage_result' },
- },
+ tool_choice: 'auto',
},
organizationId,
feature: 'security-agent',
@@ -371,25 +377,26 @@ export async function triageSecurityFinding(options: {
}
const message = choice.message;
- const toolCall = message.tool_calls?.[0];
-
- if (!toolCall || toolCall.type !== 'function') {
- logError('No tool call in response', { correlationId, findingId: finding.id });
- span.setAttribute('security_agent.is_fallback', true);
- return createFallbackTriage('LLM did not call the triage tool');
- }
-
- if (toolCall.function.name !== 'submit_triage_result') {
- logError('Unexpected tool call', {
+ const toolCall = message.tool_calls?.find(
+ candidate =>
+ candidate.type === 'function' && candidate.function.name === 'submit_triage_result'
+ );
+ const toolArguments =
+ toolCall?.type === 'function' ? toolCall.function.arguments : undefined;
+ const structuredJson =
+ toolArguments ??
+ extractJsonContent(typeof message.content === 'string' ? message.content : null);
+
+ if (!structuredJson) {
+ logError('No structured result in response', {
correlationId,
findingId: finding.id,
- tool: toolCall.function.name,
});
span.setAttribute('security_agent.is_fallback', true);
- return createFallbackTriage(`Unexpected tool: ${toolCall.function.name}`);
+ return createFallbackTriage('LLM did not return a structured triage result');
}
- const parsed = parseTriageResult(toolCall.function.arguments);
+ const parsed = parseTriageResult(structuredJson);
if (!parsed) {
span.setAttribute('security_agent.is_fallback', true);
return createFallbackTriage('Failed to parse triage result');
diff --git a/apps/web/src/lib/user/index.test.ts b/apps/web/src/lib/user/index.test.ts
index 0daff18a09..ffa6c60dc5 100644
--- a/apps/web/src/lib/user/index.test.ts
+++ b/apps/web/src/lib/user/index.test.ts
@@ -126,6 +126,7 @@ import {
import { SecurityAuditLogAction } from '@/lib/security-agent/core/enums';
import { recordAffiliateAttributionAndQueueParentEvent } from '@/lib/impact/affiliate-events';
import {
+ SecurityAuditLogActorType,
SecurityFindingNotificationKind,
SecurityFindingNotificationStatus,
} from '@kilocode/db/schema-types';
@@ -1632,6 +1633,7 @@ describe('User', () => {
actor_id: user.id,
actor_email: user.google_user_email,
actor_name: user.google_user_name,
+ actor_type: SecurityAuditLogActorType.CustomerUser,
action: SecurityAuditLogAction.FindingDismissed,
resource_type: 'security_finding',
resource_id: randomUUID(),
@@ -1647,6 +1649,7 @@ describe('User', () => {
expect(logs[0].actor_email).toBeNull();
expect(logs[0].actor_name).toBeNull();
expect(logs[0].actor_id).toBe(user.id); // actor_id preserved
+ expect(logs[0].actor_type).toBe(SecurityAuditLogActorType.CustomerUser);
expect(logs[0].action).toBe(SecurityAuditLogAction.FindingDismissed); // action preserved
});
diff --git a/apps/web/src/routers/organizations/organization-security-agent-router.ts b/apps/web/src/routers/organizations/organization-security-agent-router.ts
index 0911f416fc..61e5ac2544 100644
--- a/apps/web/src/routers/organizations/organization-security-agent-router.ts
+++ b/apps/web/src/routers/organizations/organization-security-agent-router.ts
@@ -2,6 +2,7 @@ import { createTRPCRouter } from '@/lib/trpc/init';
import {
organizationMemberProcedure,
organizationMemberMutationProcedure,
+ organizationBillingProcedure,
organizationBillingMutationProcedure,
OrganizationIdInputSchema,
} from './utils';
@@ -29,6 +30,9 @@ const handlers = createSecurityAgentHandlers<{ organizationId: string }>({
});
export const organizationSecurityAgentRouter = createTRPCRouter({
+ trackUiInteraction: organizationMemberProcedure
+ .input(OrganizationIdInputSchema.merge(handlers.trackUiInteraction.inputSchema))
+ .mutation(handlers.trackUiInteraction.handler),
getPermissionStatus: organizationMemberProcedure.query(handlers.getPermissionStatus),
getConfig: organizationMemberProcedure.query(handlers.getConfig),
saveConfig: organizationBillingMutationProcedure
@@ -82,4 +86,7 @@ export const organizationSecurityAgentRouter = createTRPCRouter({
.mutation(handlers.deleteFindingsByRepository.handler),
getAutoDismissEligible: organizationMemberProcedure.query(handlers.getAutoDismissEligible),
autoDismissEligible: organizationBillingMutationProcedure.mutation(handlers.autoDismissEligible),
+ getAuditReport: organizationBillingProcedure
+ .input(OrganizationIdInputSchema.merge(handlers.getAuditReport.inputSchema))
+ .query(handlers.getAuditReport.handler),
});
diff --git a/apps/web/src/routers/security-agent-router.ts b/apps/web/src/routers/security-agent-router.ts
index 90ada69387..ec7ce67e39 100644
--- a/apps/web/src/routers/security-agent-router.ts
+++ b/apps/web/src/routers/security-agent-router.ts
@@ -21,6 +21,9 @@ const handlers = createSecurityAgentHandlers({
});
export const securityAgentRouter = createTRPCRouter({
+ trackUiInteraction: baseProcedure
+ .input(handlers.trackUiInteraction.inputSchema)
+ .mutation(handlers.trackUiInteraction.handler),
getPermissionStatus: baseProcedure.query(handlers.getPermissionStatus),
getConfig: baseProcedure.query(handlers.getConfig),
saveConfig: baseProcedure
@@ -74,4 +77,7 @@ export const securityAgentRouter = createTRPCRouter({
.mutation(handlers.deleteFindingsByRepository.handler),
getAutoDismissEligible: baseProcedure.query(handlers.getAutoDismissEligible),
autoDismissEligible: baseProcedure.mutation(handlers.autoDismissEligible),
+ getAuditReport: baseProcedure
+ .input(handlers.getAuditReport.inputSchema)
+ .query(handlers.getAuditReport.handler),
});
diff --git a/packages/db/src/migrations/0166_easy_iceman.sql b/packages/db/src/migrations/0166_easy_iceman.sql
new file mode 100644
index 0000000000..cb8cf7b4bf
--- /dev/null
+++ b/packages/db/src/migrations/0166_easy_iceman.sql
@@ -0,0 +1,16 @@
+ALTER TABLE "security_audit_log" DROP CONSTRAINT "security_audit_log_action_check";--> statement-breakpoint
+ALTER TABLE "security_audit_log" ADD COLUMN "actor_type" text;--> statement-breakpoint
+ALTER TABLE "security_audit_log" ADD COLUMN "finding_id" uuid;--> statement-breakpoint
+ALTER TABLE "security_audit_log" ADD COLUMN "occurred_at" timestamp with time zone;--> statement-breakpoint
+ALTER TABLE "security_audit_log" ADD COLUMN "source_occurred_at" timestamp with time zone;--> statement-breakpoint
+ALTER TABLE "security_audit_log" ADD COLUMN "event_key" text;--> statement-breakpoint
+ALTER TABLE "security_audit_log" ADD COLUMN "schema_version" smallint;--> statement-breakpoint
+ALTER TABLE "security_audit_log" ADD COLUMN "finding_snapshot" jsonb;--> statement-breakpoint
+ALTER TABLE "security_audit_log" ADD COLUMN "source_context" text;--> statement-breakpoint
+CREATE UNIQUE INDEX "UQ_security_audit_log_org_event_key" ON "security_audit_log" USING btree ("owned_by_organization_id","event_key") WHERE "security_audit_log"."owned_by_organization_id" IS NOT NULL AND "security_audit_log"."event_key" IS NOT NULL;--> statement-breakpoint
+CREATE UNIQUE INDEX "UQ_security_audit_log_user_event_key" ON "security_audit_log" USING btree ("owned_by_user_id","event_key") WHERE "security_audit_log"."owned_by_user_id" IS NOT NULL AND "security_audit_log"."event_key" IS NOT NULL;--> statement-breakpoint
+CREATE INDEX "IDX_security_audit_log_org_occurred" ON "security_audit_log" USING btree ("owned_by_organization_id","occurred_at","id") WHERE "security_audit_log"."owned_by_organization_id" IS NOT NULL AND "security_audit_log"."occurred_at" IS NOT NULL;--> statement-breakpoint
+CREATE INDEX "IDX_security_audit_log_user_occurred" ON "security_audit_log" USING btree ("owned_by_user_id","occurred_at","id") WHERE "security_audit_log"."owned_by_user_id" IS NOT NULL AND "security_audit_log"."occurred_at" IS NOT NULL;--> statement-breakpoint
+ALTER TABLE "security_audit_log" ADD CONSTRAINT "security_audit_log_actor_type_check" CHECK ("security_audit_log"."actor_type" IN ('customer_user', 'kilo_admin', 'system'));--> statement-breakpoint
+ALTER TABLE "security_audit_log" ADD CONSTRAINT "security_audit_log_source_context_check" CHECK ("security_audit_log"."source_context" IN ('security_sync', 'web', 'analysis_worker', 'remediation_callback', 'rollout_baseline'));--> statement-breakpoint
+ALTER TABLE "security_audit_log" ADD CONSTRAINT "security_audit_log_action_check" CHECK ("security_audit_log"."action" IN ('security.finding.created', 'security.finding.severity_changed', 'security.finding.status_change', 'security.finding.dismissed', 'security.finding.auto_dismissed', 'security.finding.superseded', 'security.finding.analysis_started', 'security.finding.analysis_completed', 'security.finding.analysis_failed', 'security.remediation.queued', 'security.remediation.started', 'security.remediation.pr_opened', 'security.remediation.failed', 'security.remediation.blocked', 'security.remediation.no_changes_needed', 'security.remediation.cancelled', 'security.remediation.retried', 'security.finding.deleted', 'security.config.enabled', 'security.config.disabled', 'security.config.updated', 'security.sync.triggered', 'security.sync.completed', 'security.audit_log.exported', 'security.audit_report.generated'));
\ No newline at end of file
diff --git a/packages/db/src/migrations/meta/0166_snapshot.json b/packages/db/src/migrations/meta/0166_snapshot.json
new file mode 100644
index 0000000000..957c313a4d
--- /dev/null
+++ b/packages/db/src/migrations/meta/0166_snapshot.json
@@ -0,0 +1,31219 @@
+{
+ "id": "bd75796e-13df-4695-914e-ec976dfde41e",
+ "prevId": "1b09f3ca-b736-46b2-b352-3e9ffe7c04a2",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.agent_configs": {
+ "name": "agent_configs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "owned_by_organization_id": {
+ "name": "owned_by_organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owned_by_user_id": {
+ "name": "owned_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "agent_type": {
+ "name": "agent_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "platform": {
+ "name": "platform",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "is_enabled": {
+ "name": "is_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "runtime_state": {
+ "name": "runtime_state",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{}'::jsonb"
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_agent_configs_org_id": {
+ "name": "IDX_agent_configs_org_id",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_agent_configs_owned_by_user_id": {
+ "name": "IDX_agent_configs_owned_by_user_id",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_agent_configs_agent_type": {
+ "name": "IDX_agent_configs_agent_type",
+ "columns": [
+ {
+ "expression": "agent_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_agent_configs_platform": {
+ "name": "IDX_agent_configs_platform",
+ "columns": [
+ {
+ "expression": "platform",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "agent_configs_owned_by_organization_id_organizations_id_fk": {
+ "name": "agent_configs_owned_by_organization_id_organizations_id_fk",
+ "tableFrom": "agent_configs",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "owned_by_organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "agent_configs_owned_by_user_id_kilocode_users_id_fk": {
+ "name": "agent_configs_owned_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "agent_configs",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "owned_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_agent_configs_org_agent_platform": {
+ "name": "UQ_agent_configs_org_agent_platform",
+ "nullsNotDistinct": false,
+ "columns": [
+ "owned_by_organization_id",
+ "agent_type",
+ "platform"
+ ]
+ },
+ "UQ_agent_configs_user_agent_platform": {
+ "name": "UQ_agent_configs_user_agent_platform",
+ "nullsNotDistinct": false,
+ "columns": [
+ "owned_by_user_id",
+ "agent_type",
+ "platform"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "agent_configs_owner_check": {
+ "name": "agent_configs_owner_check",
+ "value": "(\n (\"agent_configs\".\"owned_by_user_id\" IS NOT NULL AND \"agent_configs\".\"owned_by_organization_id\" IS NULL) OR\n (\"agent_configs\".\"owned_by_user_id\" IS NULL AND \"agent_configs\".\"owned_by_organization_id\" IS NOT NULL)\n )"
+ },
+ "agent_configs_agent_type_check": {
+ "name": "agent_configs_agent_type_check",
+ "value": "\"agent_configs\".\"agent_type\" IN ('code_review', 'auto_triage', 'auto_fix', 'security_scan')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.agent_environment_profile_agents": {
+ "name": "agent_environment_profile_agents",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "profile_id": {
+ "name": "profile_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_agent_env_profile_agents_profile_id": {
+ "name": "IDX_agent_env_profile_agents_profile_id",
+ "columns": [
+ {
+ "expression": "profile_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "agent_environment_profile_agents_profile_id_agent_environment_profiles_id_fk": {
+ "name": "agent_environment_profile_agents_profile_id_agent_environment_profiles_id_fk",
+ "tableFrom": "agent_environment_profile_agents",
+ "tableTo": "agent_environment_profiles",
+ "columnsFrom": [
+ "profile_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_agent_env_profile_agents_profile_slug": {
+ "name": "UQ_agent_env_profile_agents_profile_slug",
+ "nullsNotDistinct": false,
+ "columns": [
+ "profile_id",
+ "slug"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.agent_environment_profile_commands": {
+ "name": "agent_environment_profile_commands",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "profile_id": {
+ "name": "profile_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sequence": {
+ "name": "sequence",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "command": {
+ "name": "command",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_agent_env_profile_commands_profile_id": {
+ "name": "IDX_agent_env_profile_commands_profile_id",
+ "columns": [
+ {
+ "expression": "profile_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "agent_environment_profile_commands_profile_id_agent_environment_profiles_id_fk": {
+ "name": "agent_environment_profile_commands_profile_id_agent_environment_profiles_id_fk",
+ "tableFrom": "agent_environment_profile_commands",
+ "tableTo": "agent_environment_profiles",
+ "columnsFrom": [
+ "profile_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_agent_env_profile_commands_profile_sequence": {
+ "name": "UQ_agent_env_profile_commands_profile_sequence",
+ "nullsNotDistinct": false,
+ "columns": [
+ "profile_id",
+ "sequence"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.agent_environment_profile_kilo_commands": {
+ "name": "agent_environment_profile_kilo_commands",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "profile_id": {
+ "name": "profile_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "template": {
+ "name": "template",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent": {
+ "name": "agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "subtask": {
+ "name": "subtask",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "sort_order": {
+ "name": "sort_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_agent_env_profile_kilo_cmds_profile_id": {
+ "name": "IDX_agent_env_profile_kilo_cmds_profile_id",
+ "columns": [
+ {
+ "expression": "profile_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "agent_environment_profile_kilo_commands_profile_id_agent_environment_profiles_id_fk": {
+ "name": "agent_environment_profile_kilo_commands_profile_id_agent_environment_profiles_id_fk",
+ "tableFrom": "agent_environment_profile_kilo_commands",
+ "tableTo": "agent_environment_profiles",
+ "columnsFrom": [
+ "profile_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_agent_env_profile_kilo_cmds_profile_name": {
+ "name": "UQ_agent_env_profile_kilo_cmds_profile_name",
+ "nullsNotDistinct": false,
+ "columns": [
+ "profile_id",
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.agent_environment_profile_mcp_servers": {
+ "name": "agent_environment_profile_mcp_servers",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "profile_id": {
+ "name": "profile_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "timeout": {
+ "name": "timeout",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_agent_env_profile_mcp_servers_profile_id": {
+ "name": "IDX_agent_env_profile_mcp_servers_profile_id",
+ "columns": [
+ {
+ "expression": "profile_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "agent_environment_profile_mcp_servers_profile_id_agent_environment_profiles_id_fk": {
+ "name": "agent_environment_profile_mcp_servers_profile_id_agent_environment_profiles_id_fk",
+ "tableFrom": "agent_environment_profile_mcp_servers",
+ "tableTo": "agent_environment_profiles",
+ "columnsFrom": [
+ "profile_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_agent_env_profile_mcp_servers_profile_name": {
+ "name": "UQ_agent_env_profile_mcp_servers_profile_name",
+ "nullsNotDistinct": false,
+ "columns": [
+ "profile_id",
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.agent_environment_profile_repo_bindings": {
+ "name": "agent_environment_profile_repo_bindings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "repo_full_name": {
+ "name": "repo_full_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "platform": {
+ "name": "platform",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'github'"
+ },
+ "profile_id": {
+ "name": "profile_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owned_by_organization_id": {
+ "name": "owned_by_organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owned_by_user_id": {
+ "name": "owned_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_agent_env_profile_repo_bindings_user": {
+ "name": "UQ_agent_env_profile_repo_bindings_user",
+ "columns": [
+ {
+ "expression": "repo_full_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "platform",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"agent_environment_profile_repo_bindings\".\"owned_by_user_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_agent_env_profile_repo_bindings_org": {
+ "name": "UQ_agent_env_profile_repo_bindings_org",
+ "columns": [
+ {
+ "expression": "repo_full_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "platform",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"agent_environment_profile_repo_bindings\".\"owned_by_organization_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "agent_environment_profile_repo_bindings_profile_id_agent_environment_profiles_id_fk": {
+ "name": "agent_environment_profile_repo_bindings_profile_id_agent_environment_profiles_id_fk",
+ "tableFrom": "agent_environment_profile_repo_bindings",
+ "tableTo": "agent_environment_profiles",
+ "columnsFrom": [
+ "profile_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "agent_environment_profile_repo_bindings_owned_by_organization_id_organizations_id_fk": {
+ "name": "agent_environment_profile_repo_bindings_owned_by_organization_id_organizations_id_fk",
+ "tableFrom": "agent_environment_profile_repo_bindings",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "owned_by_organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "agent_environment_profile_repo_bindings_owned_by_user_id_kilocode_users_id_fk": {
+ "name": "agent_environment_profile_repo_bindings_owned_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "agent_environment_profile_repo_bindings",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "owned_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "agent_env_profile_repo_bindings_owner_check": {
+ "name": "agent_env_profile_repo_bindings_owner_check",
+ "value": "(\n (\"agent_environment_profile_repo_bindings\".\"owned_by_user_id\" IS NOT NULL AND \"agent_environment_profile_repo_bindings\".\"owned_by_organization_id\" IS NULL) OR\n (\"agent_environment_profile_repo_bindings\".\"owned_by_user_id\" IS NULL AND \"agent_environment_profile_repo_bindings\".\"owned_by_organization_id\" IS NOT NULL)\n )"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.agent_environment_profile_skills": {
+ "name": "agent_environment_profile_skills",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "profile_id": {
+ "name": "profile_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_type": {
+ "name": "source_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source_url": {
+ "name": "source_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "raw_markdown": {
+ "name": "raw_markdown",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "files": {
+ "name": "files",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_agent_env_profile_skills_profile_id": {
+ "name": "IDX_agent_env_profile_skills_profile_id",
+ "columns": [
+ {
+ "expression": "profile_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "agent_environment_profile_skills_profile_id_agent_environment_profiles_id_fk": {
+ "name": "agent_environment_profile_skills_profile_id_agent_environment_profiles_id_fk",
+ "tableFrom": "agent_environment_profile_skills",
+ "tableTo": "agent_environment_profiles",
+ "columnsFrom": [
+ "profile_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_agent_env_profile_skills_profile_name": {
+ "name": "UQ_agent_env_profile_skills_profile_name",
+ "nullsNotDistinct": false,
+ "columns": [
+ "profile_id",
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.agent_environment_profile_vars": {
+ "name": "agent_environment_profile_vars",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "profile_id": {
+ "name": "profile_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "is_secret": {
+ "name": "is_secret",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_agent_env_profile_vars_profile_id": {
+ "name": "IDX_agent_env_profile_vars_profile_id",
+ "columns": [
+ {
+ "expression": "profile_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "agent_environment_profile_vars_profile_id_agent_environment_profiles_id_fk": {
+ "name": "agent_environment_profile_vars_profile_id_agent_environment_profiles_id_fk",
+ "tableFrom": "agent_environment_profile_vars",
+ "tableTo": "agent_environment_profiles",
+ "columnsFrom": [
+ "profile_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_agent_env_profile_vars_profile_key": {
+ "name": "UQ_agent_env_profile_vars_profile_key",
+ "nullsNotDistinct": false,
+ "columns": [
+ "profile_id",
+ "key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.agent_environment_profiles": {
+ "name": "agent_environment_profiles",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "owned_by_organization_id": {
+ "name": "owned_by_organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owned_by_user_id": {
+ "name": "owned_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_user_id": {
+ "name": "created_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_default": {
+ "name": "is_default",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_agent_env_profiles_org_name": {
+ "name": "UQ_agent_env_profiles_org_name",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"agent_environment_profiles\".\"owned_by_organization_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_agent_env_profiles_user_name": {
+ "name": "UQ_agent_env_profiles_user_name",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"agent_environment_profiles\".\"owned_by_user_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_agent_env_profiles_org_default": {
+ "name": "UQ_agent_env_profiles_org_default",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"agent_environment_profiles\".\"is_default\" = true AND \"agent_environment_profiles\".\"owned_by_organization_id\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_agent_env_profiles_user_default": {
+ "name": "UQ_agent_env_profiles_user_default",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"agent_environment_profiles\".\"is_default\" = true AND \"agent_environment_profiles\".\"owned_by_user_id\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_agent_env_profiles_org_id": {
+ "name": "IDX_agent_env_profiles_org_id",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_agent_env_profiles_user_id": {
+ "name": "IDX_agent_env_profiles_user_id",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_agent_env_profiles_created_by_user_id": {
+ "name": "IDX_agent_env_profiles_created_by_user_id",
+ "columns": [
+ {
+ "expression": "created_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "agent_environment_profiles_owned_by_organization_id_organizations_id_fk": {
+ "name": "agent_environment_profiles_owned_by_organization_id_organizations_id_fk",
+ "tableFrom": "agent_environment_profiles",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "owned_by_organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "agent_environment_profiles_owned_by_user_id_kilocode_users_id_fk": {
+ "name": "agent_environment_profiles_owned_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "agent_environment_profiles",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "owned_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "agent_env_profiles_owner_check": {
+ "name": "agent_env_profiles_owner_check",
+ "value": "(\n (\"agent_environment_profiles\".\"owned_by_user_id\" IS NOT NULL AND \"agent_environment_profiles\".\"owned_by_organization_id\" IS NULL) OR\n (\"agent_environment_profiles\".\"owned_by_user_id\" IS NULL AND \"agent_environment_profiles\".\"owned_by_organization_id\" IS NOT NULL)\n )"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.api_kind": {
+ "name": "api_kind",
+ "schema": "",
+ "columns": {
+ "api_kind_id": {
+ "name": "api_kind_id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "api_kind": {
+ "name": "api_kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "UQ_api_kind": {
+ "name": "UQ_api_kind",
+ "columns": [
+ {
+ "expression": "api_kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.api_request_log": {
+ "name": "api_request_log",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigserial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "session_id": {
+ "name": "session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status_code": {
+ "name": "status_code",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "request": {
+ "name": "request",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "response": {
+ "name": "response",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error": {
+ "name": "error",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "idx_api_request_log_created_at": {
+ "name": "idx_api_request_log_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.app_builder_feedback": {
+ "name": "app_builder_feedback",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "session_id": {
+ "name": "session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "preview_status": {
+ "name": "preview_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_streaming": {
+ "name": "is_streaming",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "message_count": {
+ "name": "message_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "feedback_text": {
+ "name": "feedback_text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "recent_messages": {
+ "name": "recent_messages",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_app_builder_feedback_created_at": {
+ "name": "IDX_app_builder_feedback_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_app_builder_feedback_kilo_user_id": {
+ "name": "IDX_app_builder_feedback_kilo_user_id",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_app_builder_feedback_project_id": {
+ "name": "IDX_app_builder_feedback_project_id",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "app_builder_feedback_kilo_user_id_kilocode_users_id_fk": {
+ "name": "app_builder_feedback_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "app_builder_feedback",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "cascade"
+ },
+ "app_builder_feedback_project_id_app_builder_projects_id_fk": {
+ "name": "app_builder_feedback_project_id_app_builder_projects_id_fk",
+ "tableFrom": "app_builder_feedback",
+ "tableTo": "app_builder_projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.app_builder_project_sessions": {
+ "name": "app_builder_project_sessions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cloud_agent_session_id": {
+ "name": "cloud_agent_session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "ended_at": {
+ "name": "ended_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "reason": {
+ "name": "reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "worker_version": {
+ "name": "worker_version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'v2'"
+ }
+ },
+ "indexes": {
+ "IDX_app_builder_project_sessions_project_id": {
+ "name": "IDX_app_builder_project_sessions_project_id",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "app_builder_project_sessions_project_id_app_builder_projects_id_fk": {
+ "name": "app_builder_project_sessions_project_id_app_builder_projects_id_fk",
+ "tableFrom": "app_builder_project_sessions",
+ "tableTo": "app_builder_projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_app_builder_project_sessions_cloud_agent_session_id": {
+ "name": "UQ_app_builder_project_sessions_cloud_agent_session_id",
+ "nullsNotDistinct": false,
+ "columns": [
+ "cloud_agent_session_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.app_builder_projects": {
+ "name": "app_builder_projects",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "created_by_user_id": {
+ "name": "created_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owned_by_user_id": {
+ "name": "owned_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owned_by_organization_id": {
+ "name": "owned_by_organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "session_id": {
+ "name": "session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "model_id": {
+ "name": "model_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "template": {
+ "name": "template",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deployment_id": {
+ "name": "deployment_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_message_at": {
+ "name": "last_message_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "git_repo_full_name": {
+ "name": "git_repo_full_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "git_platform_integration_id": {
+ "name": "git_platform_integration_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "migrated_at": {
+ "name": "migrated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_app_builder_projects_created_by_user_id": {
+ "name": "IDX_app_builder_projects_created_by_user_id",
+ "columns": [
+ {
+ "expression": "created_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_app_builder_projects_owned_by_user_id": {
+ "name": "IDX_app_builder_projects_owned_by_user_id",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_app_builder_projects_owned_by_organization_id": {
+ "name": "IDX_app_builder_projects_owned_by_organization_id",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_app_builder_projects_created_at": {
+ "name": "IDX_app_builder_projects_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_app_builder_projects_last_message_at": {
+ "name": "IDX_app_builder_projects_last_message_at",
+ "columns": [
+ {
+ "expression": "last_message_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_app_builder_projects_git_repo_integration": {
+ "name": "IDX_app_builder_projects_git_repo_integration",
+ "columns": [
+ {
+ "expression": "git_repo_full_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "git_platform_integration_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"app_builder_projects\".\"git_repo_full_name\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "app_builder_projects_owned_by_user_id_kilocode_users_id_fk": {
+ "name": "app_builder_projects_owned_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "app_builder_projects",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "owned_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "app_builder_projects_owned_by_organization_id_organizations_id_fk": {
+ "name": "app_builder_projects_owned_by_organization_id_organizations_id_fk",
+ "tableFrom": "app_builder_projects",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "owned_by_organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "app_builder_projects_deployment_id_deployments_id_fk": {
+ "name": "app_builder_projects_deployment_id_deployments_id_fk",
+ "tableFrom": "app_builder_projects",
+ "tableTo": "deployments",
+ "columnsFrom": [
+ "deployment_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "app_builder_projects_git_platform_integration_id_platform_integrations_id_fk": {
+ "name": "app_builder_projects_git_platform_integration_id_platform_integrations_id_fk",
+ "tableFrom": "app_builder_projects",
+ "tableTo": "platform_integrations",
+ "columnsFrom": [
+ "git_platform_integration_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "app_builder_projects_owner_check": {
+ "name": "app_builder_projects_owner_check",
+ "value": "(\n (\"app_builder_projects\".\"owned_by_user_id\" IS NOT NULL AND \"app_builder_projects\".\"owned_by_organization_id\" IS NULL) OR\n (\"app_builder_projects\".\"owned_by_user_id\" IS NULL AND \"app_builder_projects\".\"owned_by_organization_id\" IS NOT NULL)\n )"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.app_min_versions": {
+ "name": "app_min_versions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "ios_min_version": {
+ "name": "ios_min_version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'1.0.0'"
+ },
+ "android_min_version": {
+ "name": "android_min_version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'1.0.0'"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.app_reported_messages": {
+ "name": "app_reported_messages",
+ "schema": "",
+ "columns": {
+ "report_id": {
+ "name": "report_id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "report_type": {
+ "name": "report_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "signature": {
+ "name": "signature",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "message": {
+ "name": "message",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "cli_session_id": {
+ "name": "cli_session_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "mode": {
+ "name": "mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "app_reported_messages_cli_session_id_cli_sessions_session_id_fk": {
+ "name": "app_reported_messages_cli_session_id_cli_sessions_session_id_fk",
+ "tableFrom": "app_reported_messages",
+ "tableTo": "cli_sessions",
+ "columnsFrom": [
+ "cli_session_id"
+ ],
+ "columnsTo": [
+ "session_id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.auto_fix_tickets": {
+ "name": "auto_fix_tickets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "owned_by_organization_id": {
+ "name": "owned_by_organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owned_by_user_id": {
+ "name": "owned_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "platform_integration_id": {
+ "name": "platform_integration_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "triage_ticket_id": {
+ "name": "triage_ticket_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "platform": {
+ "name": "platform",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'github'"
+ },
+ "repo_full_name": {
+ "name": "repo_full_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_number": {
+ "name": "issue_number",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_url": {
+ "name": "issue_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_title": {
+ "name": "issue_title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_body": {
+ "name": "issue_body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "issue_author": {
+ "name": "issue_author",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_labels": {
+ "name": "issue_labels",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{}'"
+ },
+ "trigger_source": {
+ "name": "trigger_source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'label'"
+ },
+ "review_comment_id": {
+ "name": "review_comment_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "review_comment_body": {
+ "name": "review_comment_body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "file_path": {
+ "name": "file_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "line_number": {
+ "name": "line_number",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "diff_hunk": {
+ "name": "diff_hunk",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pr_head_ref": {
+ "name": "pr_head_ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "classification": {
+ "name": "classification",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "confidence": {
+ "name": "confidence",
+ "type": "numeric(3, 2)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "intent_summary": {
+ "name": "intent_summary",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "related_files": {
+ "name": "related_files",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "session_id": {
+ "name": "session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cli_session_id": {
+ "name": "cli_session_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pr_number": {
+ "name": "pr_number",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pr_url": {
+ "name": "pr_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pr_branch": {
+ "name": "pr_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "error_message": {
+ "name": "error_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_auto_fix_tickets_repo_issue": {
+ "name": "UQ_auto_fix_tickets_repo_issue",
+ "columns": [
+ {
+ "expression": "repo_full_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "issue_number",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"auto_fix_tickets\".\"trigger_source\" = 'label'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_auto_fix_tickets_repo_review_comment": {
+ "name": "UQ_auto_fix_tickets_repo_review_comment",
+ "columns": [
+ {
+ "expression": "repo_full_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "review_comment_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"auto_fix_tickets\".\"review_comment_id\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_auto_fix_tickets_owned_by_org": {
+ "name": "IDX_auto_fix_tickets_owned_by_org",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_auto_fix_tickets_owned_by_user": {
+ "name": "IDX_auto_fix_tickets_owned_by_user",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_auto_fix_tickets_status": {
+ "name": "IDX_auto_fix_tickets_status",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_auto_fix_tickets_created_at": {
+ "name": "IDX_auto_fix_tickets_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_auto_fix_tickets_triage_ticket_id": {
+ "name": "IDX_auto_fix_tickets_triage_ticket_id",
+ "columns": [
+ {
+ "expression": "triage_ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_auto_fix_tickets_session_id": {
+ "name": "IDX_auto_fix_tickets_session_id",
+ "columns": [
+ {
+ "expression": "session_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "auto_fix_tickets_owned_by_organization_id_organizations_id_fk": {
+ "name": "auto_fix_tickets_owned_by_organization_id_organizations_id_fk",
+ "tableFrom": "auto_fix_tickets",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "owned_by_organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "auto_fix_tickets_owned_by_user_id_kilocode_users_id_fk": {
+ "name": "auto_fix_tickets_owned_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "auto_fix_tickets",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "owned_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "auto_fix_tickets_platform_integration_id_platform_integrations_id_fk": {
+ "name": "auto_fix_tickets_platform_integration_id_platform_integrations_id_fk",
+ "tableFrom": "auto_fix_tickets",
+ "tableTo": "platform_integrations",
+ "columnsFrom": [
+ "platform_integration_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "auto_fix_tickets_triage_ticket_id_auto_triage_tickets_id_fk": {
+ "name": "auto_fix_tickets_triage_ticket_id_auto_triage_tickets_id_fk",
+ "tableFrom": "auto_fix_tickets",
+ "tableTo": "auto_triage_tickets",
+ "columnsFrom": [
+ "triage_ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "auto_fix_tickets_cli_session_id_cli_sessions_session_id_fk": {
+ "name": "auto_fix_tickets_cli_session_id_cli_sessions_session_id_fk",
+ "tableFrom": "auto_fix_tickets",
+ "tableTo": "cli_sessions",
+ "columnsFrom": [
+ "cli_session_id"
+ ],
+ "columnsTo": [
+ "session_id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "auto_fix_tickets_owner_check": {
+ "name": "auto_fix_tickets_owner_check",
+ "value": "(\n (\"auto_fix_tickets\".\"owned_by_user_id\" IS NOT NULL AND \"auto_fix_tickets\".\"owned_by_organization_id\" IS NULL) OR\n (\"auto_fix_tickets\".\"owned_by_user_id\" IS NULL AND \"auto_fix_tickets\".\"owned_by_organization_id\" IS NOT NULL)\n )"
+ },
+ "auto_fix_tickets_status_check": {
+ "name": "auto_fix_tickets_status_check",
+ "value": "\"auto_fix_tickets\".\"status\" IN ('pending', 'running', 'completed', 'failed', 'cancelled')"
+ },
+ "auto_fix_tickets_classification_check": {
+ "name": "auto_fix_tickets_classification_check",
+ "value": "\"auto_fix_tickets\".\"classification\" IN ('bug', 'feature', 'question', 'unclear')"
+ },
+ "auto_fix_tickets_confidence_check": {
+ "name": "auto_fix_tickets_confidence_check",
+ "value": "\"auto_fix_tickets\".\"confidence\" >= 0 AND \"auto_fix_tickets\".\"confidence\" <= 1"
+ },
+ "auto_fix_tickets_trigger_source_check": {
+ "name": "auto_fix_tickets_trigger_source_check",
+ "value": "\"auto_fix_tickets\".\"trigger_source\" IN ('label', 'review_comment')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.auto_model": {
+ "name": "auto_model",
+ "schema": "",
+ "columns": {
+ "auto_model_id": {
+ "name": "auto_model_id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "auto_model": {
+ "name": "auto_model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "UQ_auto_model": {
+ "name": "UQ_auto_model",
+ "columns": [
+ {
+ "expression": "auto_model",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.auto_top_up_configs": {
+ "name": "auto_top_up_configs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "owned_by_user_id": {
+ "name": "owned_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owned_by_organization_id": {
+ "name": "owned_by_organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_user_id": {
+ "name": "created_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_payment_method_id": {
+ "name": "stripe_payment_method_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "amount_cents": {
+ "name": "amount_cents",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 5000
+ },
+ "last_auto_top_up_at": {
+ "name": "last_auto_top_up_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "attempt_started_at": {
+ "name": "attempt_started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "disabled_reason": {
+ "name": "disabled_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_auto_top_up_configs_owned_by_user_id": {
+ "name": "UQ_auto_top_up_configs_owned_by_user_id",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"auto_top_up_configs\".\"owned_by_user_id\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_auto_top_up_configs_owned_by_organization_id": {
+ "name": "UQ_auto_top_up_configs_owned_by_organization_id",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"auto_top_up_configs\".\"owned_by_organization_id\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "auto_top_up_configs_owned_by_user_id_kilocode_users_id_fk": {
+ "name": "auto_top_up_configs_owned_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "auto_top_up_configs",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "owned_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ },
+ "auto_top_up_configs_owned_by_organization_id_organizations_id_fk": {
+ "name": "auto_top_up_configs_owned_by_organization_id_organizations_id_fk",
+ "tableFrom": "auto_top_up_configs",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "owned_by_organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "auto_top_up_configs_exactly_one_owner": {
+ "name": "auto_top_up_configs_exactly_one_owner",
+ "value": "(\"auto_top_up_configs\".\"owned_by_user_id\" IS NOT NULL AND \"auto_top_up_configs\".\"owned_by_organization_id\" IS NULL) OR (\"auto_top_up_configs\".\"owned_by_user_id\" IS NULL AND \"auto_top_up_configs\".\"owned_by_organization_id\" IS NOT NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.auto_triage_tickets": {
+ "name": "auto_triage_tickets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "owned_by_organization_id": {
+ "name": "owned_by_organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owned_by_user_id": {
+ "name": "owned_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "platform_integration_id": {
+ "name": "platform_integration_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "platform": {
+ "name": "platform",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'github'"
+ },
+ "repo_full_name": {
+ "name": "repo_full_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_number": {
+ "name": "issue_number",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_url": {
+ "name": "issue_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_title": {
+ "name": "issue_title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_body": {
+ "name": "issue_body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "issue_author": {
+ "name": "issue_author",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_type": {
+ "name": "issue_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_labels": {
+ "name": "issue_labels",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{}'"
+ },
+ "classification": {
+ "name": "classification",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "confidence": {
+ "name": "confidence",
+ "type": "numeric(3, 2)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "intent_summary": {
+ "name": "intent_summary",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "related_files": {
+ "name": "related_files",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_duplicate": {
+ "name": "is_duplicate",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "duplicate_of_ticket_id": {
+ "name": "duplicate_of_ticket_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "similarity_score": {
+ "name": "similarity_score",
+ "type": "numeric(3, 2)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "qdrant_point_id": {
+ "name": "qdrant_point_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "session_id": {
+ "name": "session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "should_auto_fix": {
+ "name": "should_auto_fix",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "action_taken": {
+ "name": "action_taken",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "action_metadata": {
+ "name": "action_metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error_message": {
+ "name": "error_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_auto_triage_tickets_repo_issue": {
+ "name": "UQ_auto_triage_tickets_repo_issue",
+ "columns": [
+ {
+ "expression": "repo_full_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "issue_number",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_auto_triage_tickets_owned_by_org": {
+ "name": "IDX_auto_triage_tickets_owned_by_org",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_auto_triage_tickets_owned_by_user": {
+ "name": "IDX_auto_triage_tickets_owned_by_user",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_auto_triage_tickets_status": {
+ "name": "IDX_auto_triage_tickets_status",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_auto_triage_tickets_created_at": {
+ "name": "IDX_auto_triage_tickets_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_auto_triage_tickets_qdrant_point_id": {
+ "name": "IDX_auto_triage_tickets_qdrant_point_id",
+ "columns": [
+ {
+ "expression": "qdrant_point_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_auto_triage_tickets_owner_status_created": {
+ "name": "IDX_auto_triage_tickets_owner_status_created",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_auto_triage_tickets_user_status_created": {
+ "name": "IDX_auto_triage_tickets_user_status_created",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_auto_triage_tickets_repo_classification": {
+ "name": "IDX_auto_triage_tickets_repo_classification",
+ "columns": [
+ {
+ "expression": "repo_full_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "classification",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "auto_triage_tickets_owned_by_organization_id_organizations_id_fk": {
+ "name": "auto_triage_tickets_owned_by_organization_id_organizations_id_fk",
+ "tableFrom": "auto_triage_tickets",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "owned_by_organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "auto_triage_tickets_owned_by_user_id_kilocode_users_id_fk": {
+ "name": "auto_triage_tickets_owned_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "auto_triage_tickets",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "owned_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "auto_triage_tickets_platform_integration_id_platform_integrations_id_fk": {
+ "name": "auto_triage_tickets_platform_integration_id_platform_integrations_id_fk",
+ "tableFrom": "auto_triage_tickets",
+ "tableTo": "platform_integrations",
+ "columnsFrom": [
+ "platform_integration_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "auto_triage_tickets_duplicate_of_ticket_id_auto_triage_tickets_id_fk": {
+ "name": "auto_triage_tickets_duplicate_of_ticket_id_auto_triage_tickets_id_fk",
+ "tableFrom": "auto_triage_tickets",
+ "tableTo": "auto_triage_tickets",
+ "columnsFrom": [
+ "duplicate_of_ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "auto_triage_tickets_owner_check": {
+ "name": "auto_triage_tickets_owner_check",
+ "value": "(\n (\"auto_triage_tickets\".\"owned_by_user_id\" IS NOT NULL AND \"auto_triage_tickets\".\"owned_by_organization_id\" IS NULL) OR\n (\"auto_triage_tickets\".\"owned_by_user_id\" IS NULL AND \"auto_triage_tickets\".\"owned_by_organization_id\" IS NOT NULL)\n )"
+ },
+ "auto_triage_tickets_issue_type_check": {
+ "name": "auto_triage_tickets_issue_type_check",
+ "value": "\"auto_triage_tickets\".\"issue_type\" IN ('issue', 'pull_request')"
+ },
+ "auto_triage_tickets_classification_check": {
+ "name": "auto_triage_tickets_classification_check",
+ "value": "\"auto_triage_tickets\".\"classification\" IN ('bug', 'feature', 'question', 'duplicate', 'unclear')"
+ },
+ "auto_triage_tickets_confidence_check": {
+ "name": "auto_triage_tickets_confidence_check",
+ "value": "\"auto_triage_tickets\".\"confidence\" >= 0 AND \"auto_triage_tickets\".\"confidence\" <= 1"
+ },
+ "auto_triage_tickets_similarity_score_check": {
+ "name": "auto_triage_tickets_similarity_score_check",
+ "value": "\"auto_triage_tickets\".\"similarity_score\" >= 0 AND \"auto_triage_tickets\".\"similarity_score\" <= 1"
+ },
+ "auto_triage_tickets_status_check": {
+ "name": "auto_triage_tickets_status_check",
+ "value": "\"auto_triage_tickets\".\"status\" IN ('pending', 'analyzing', 'actioned', 'failed', 'skipped')"
+ },
+ "auto_triage_tickets_action_taken_check": {
+ "name": "auto_triage_tickets_action_taken_check",
+ "value": "\"auto_triage_tickets\".\"action_taken\" IN ('pr_created', 'comment_posted', 'closed_duplicate', 'needs_clarification')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.bot_request_cloud_agent_sessions": {
+ "name": "bot_request_cloud_agent_sessions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "bot_request_id": {
+ "name": "bot_request_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "spawn_group_id": {
+ "name": "spawn_group_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cloud_agent_session_id": {
+ "name": "cloud_agent_session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kilo_session_id": {
+ "name": "kilo_session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "execution_id": {
+ "name": "execution_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'running'"
+ },
+ "mode": {
+ "name": "mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "github_repo": {
+ "name": "github_repo",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "gitlab_project": {
+ "name": "gitlab_project",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "callback_step": {
+ "name": "callback_step",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "error_message": {
+ "name": "error_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "final_message": {
+ "name": "final_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "final_message_fetched_at": {
+ "name": "final_message_fetched_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "final_message_error": {
+ "name": "final_message_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "terminal_at": {
+ "name": "terminal_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "continuation_started_at": {
+ "name": "continuation_started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_bot_request_cas_cloud_agent_session_id": {
+ "name": "UQ_bot_request_cas_cloud_agent_session_id",
+ "columns": [
+ {
+ "expression": "cloud_agent_session_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_bot_request_cas_bot_request_id": {
+ "name": "IDX_bot_request_cas_bot_request_id",
+ "columns": [
+ {
+ "expression": "bot_request_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_bot_request_cas_bot_request_id_spawn_group_id": {
+ "name": "IDX_bot_request_cas_bot_request_id_spawn_group_id",
+ "columns": [
+ {
+ "expression": "bot_request_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "spawn_group_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_bot_request_cas_bot_request_id_spawn_group_id_status": {
+ "name": "IDX_bot_request_cas_bot_request_id_spawn_group_id_status",
+ "columns": [
+ {
+ "expression": "bot_request_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "spawn_group_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "bot_request_cloud_agent_sessions_bot_request_id_bot_requests_id_fk": {
+ "name": "bot_request_cloud_agent_sessions_bot_request_id_bot_requests_id_fk",
+ "tableFrom": "bot_request_cloud_agent_sessions",
+ "tableTo": "bot_requests",
+ "columnsFrom": [
+ "bot_request_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.bot_requests": {
+ "name": "bot_requests",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "platform_integration_id": {
+ "name": "platform_integration_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "platform": {
+ "name": "platform",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "platform_thread_id": {
+ "name": "platform_thread_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "platform_message_id": {
+ "name": "platform_message_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_message": {
+ "name": "user_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "error_message": {
+ "name": "error_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model_used": {
+ "name": "model_used",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "steps": {
+ "name": "steps",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cloud_agent_session_id": {
+ "name": "cloud_agent_session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "response_time_ms": {
+ "name": "response_time_ms",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_bot_requests_created_at": {
+ "name": "IDX_bot_requests_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_bot_requests_created_by": {
+ "name": "IDX_bot_requests_created_by",
+ "columns": [
+ {
+ "expression": "created_by",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_bot_requests_organization_id": {
+ "name": "IDX_bot_requests_organization_id",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_bot_requests_platform_integration_id": {
+ "name": "IDX_bot_requests_platform_integration_id",
+ "columns": [
+ {
+ "expression": "platform_integration_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_bot_requests_status": {
+ "name": "IDX_bot_requests_status",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "bot_requests_created_by_kilocode_users_id_fk": {
+ "name": "bot_requests_created_by_kilocode_users_id_fk",
+ "tableFrom": "bot_requests",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "created_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "bot_requests_organization_id_organizations_id_fk": {
+ "name": "bot_requests_organization_id_organizations_id_fk",
+ "tableFrom": "bot_requests",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "bot_requests_platform_integration_id_platform_integrations_id_fk": {
+ "name": "bot_requests_platform_integration_id_platform_integrations_id_fk",
+ "tableFrom": "bot_requests",
+ "tableTo": "platform_integrations",
+ "columnsFrom": [
+ "platform_integration_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.byok_api_keys": {
+ "name": "byok_api_keys",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "encrypted_api_key": {
+ "name": "encrypted_api_key",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "management_source": {
+ "name": "management_source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'user'"
+ },
+ "is_enabled": {
+ "name": "is_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "IDX_byok_api_keys_organization_id": {
+ "name": "IDX_byok_api_keys_organization_id",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_byok_api_keys_kilo_user_id": {
+ "name": "IDX_byok_api_keys_kilo_user_id",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_byok_api_keys_provider_id": {
+ "name": "IDX_byok_api_keys_provider_id",
+ "columns": [
+ {
+ "expression": "provider_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "byok_api_keys_organization_id_organizations_id_fk": {
+ "name": "byok_api_keys_organization_id_organizations_id_fk",
+ "tableFrom": "byok_api_keys",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "byok_api_keys_kilo_user_id_kilocode_users_id_fk": {
+ "name": "byok_api_keys_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "byok_api_keys",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_byok_api_keys_org_provider": {
+ "name": "UQ_byok_api_keys_org_provider",
+ "nullsNotDistinct": false,
+ "columns": [
+ "organization_id",
+ "provider_id"
+ ]
+ },
+ "UQ_byok_api_keys_user_provider": {
+ "name": "UQ_byok_api_keys_user_provider",
+ "nullsNotDistinct": false,
+ "columns": [
+ "kilo_user_id",
+ "provider_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "byok_api_keys_management_source_check": {
+ "name": "byok_api_keys_management_source_check",
+ "value": "\"byok_api_keys\".\"management_source\" IN ('user', 'coding_plan')"
+ },
+ "byok_api_keys_owner_check": {
+ "name": "byok_api_keys_owner_check",
+ "value": "(\n (\"byok_api_keys\".\"kilo_user_id\" IS NOT NULL AND \"byok_api_keys\".\"organization_id\" IS NULL) OR\n (\"byok_api_keys\".\"kilo_user_id\" IS NULL AND \"byok_api_keys\".\"organization_id\" IS NOT NULL)\n )"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.cli_sessions": {
+ "name": "cli_sessions",
+ "schema": "",
+ "columns": {
+ "session_id": {
+ "name": "session_id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_on_platform": {
+ "name": "created_on_platform",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'unknown'"
+ },
+ "api_conversation_history_blob_url": {
+ "name": "api_conversation_history_blob_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "task_metadata_blob_url": {
+ "name": "task_metadata_blob_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ui_messages_blob_url": {
+ "name": "ui_messages_blob_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "git_state_blob_url": {
+ "name": "git_state_blob_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "git_url": {
+ "name": "git_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "forked_from": {
+ "name": "forked_from",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "parent_session_id": {
+ "name": "parent_session_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cloud_agent_session_id": {
+ "name": "cloud_agent_session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_mode": {
+ "name": "last_mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_model": {
+ "name": "last_model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "version": {
+ "name": "version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_cli_sessions_kilo_user_id": {
+ "name": "IDX_cli_sessions_kilo_user_id",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cli_sessions_created_at": {
+ "name": "IDX_cli_sessions_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cli_sessions_updated_at": {
+ "name": "IDX_cli_sessions_updated_at",
+ "columns": [
+ {
+ "expression": "updated_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cli_sessions_organization_id": {
+ "name": "IDX_cli_sessions_organization_id",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cli_sessions_user_updated": {
+ "name": "IDX_cli_sessions_user_updated",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "updated_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "cli_sessions_kilo_user_id_kilocode_users_id_fk": {
+ "name": "cli_sessions_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "cli_sessions",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "cli_sessions_forked_from_cli_sessions_session_id_fk": {
+ "name": "cli_sessions_forked_from_cli_sessions_session_id_fk",
+ "tableFrom": "cli_sessions",
+ "tableTo": "cli_sessions",
+ "columnsFrom": [
+ "forked_from"
+ ],
+ "columnsTo": [
+ "session_id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "cli_sessions_parent_session_id_cli_sessions_session_id_fk": {
+ "name": "cli_sessions_parent_session_id_cli_sessions_session_id_fk",
+ "tableFrom": "cli_sessions",
+ "tableTo": "cli_sessions",
+ "columnsFrom": [
+ "parent_session_id"
+ ],
+ "columnsTo": [
+ "session_id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "cli_sessions_organization_id_organizations_id_fk": {
+ "name": "cli_sessions_organization_id_organizations_id_fk",
+ "tableFrom": "cli_sessions",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "cli_sessions_cloud_agent_session_id_unique": {
+ "name": "cli_sessions_cloud_agent_session_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "cloud_agent_session_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.cli_sessions_v2": {
+ "name": "cli_sessions_v2",
+ "schema": "",
+ "columns": {
+ "session_id": {
+ "name": "session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "version": {
+ "name": "version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "public_id": {
+ "name": "public_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "parent_session_id": {
+ "name": "parent_session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cloud_agent_session_id": {
+ "name": "cloud_agent_session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_on_platform": {
+ "name": "created_on_platform",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'unknown'"
+ },
+ "git_url": {
+ "name": "git_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "git_branch": {
+ "name": "git_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status_updated_at": {
+ "name": "status_updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_cli_sessions_v2_parent_session_id_kilo_user_id": {
+ "name": "IDX_cli_sessions_v2_parent_session_id_kilo_user_id",
+ "columns": [
+ {
+ "expression": "parent_session_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_cli_sessions_v2_public_id": {
+ "name": "UQ_cli_sessions_v2_public_id",
+ "columns": [
+ {
+ "expression": "public_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"cli_sessions_v2\".\"public_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_cli_sessions_v2_cloud_agent_session_id": {
+ "name": "UQ_cli_sessions_v2_cloud_agent_session_id",
+ "columns": [
+ {
+ "expression": "cloud_agent_session_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"cli_sessions_v2\".\"cloud_agent_session_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cli_sessions_v2_organization_id": {
+ "name": "IDX_cli_sessions_v2_organization_id",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cli_sessions_v2_kilo_user_id": {
+ "name": "IDX_cli_sessions_v2_kilo_user_id",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cli_sessions_v2_created_at": {
+ "name": "IDX_cli_sessions_v2_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cli_sessions_v2_user_updated": {
+ "name": "IDX_cli_sessions_v2_user_updated",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "updated_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "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
+ },
+ "previous_summary_body": {
+ "name": "previous_summary_body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "previous_summary_head_sha": {
+ "name": "previous_summary_head_sha",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "total_tokens_in": {
+ "name": "total_tokens_in",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "total_tokens_out": {
+ "name": "total_tokens_out",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "total_cost_musd": {
+ "name": "total_cost_musd",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_cloud_agent_code_reviews_repo_pr_sha": {
+ "name": "UQ_cloud_agent_code_reviews_repo_pr_sha",
+ "columns": [
+ {
+ "expression": "repo_full_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "pr_number",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "head_sha",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_cloud_agent_code_reviews_owned_by_org_id": {
+ "name": "idx_cloud_agent_code_reviews_owned_by_org_id",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_cloud_agent_code_reviews_owned_by_user_id": {
+ "name": "idx_cloud_agent_code_reviews_owned_by_user_id",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_cloud_agent_code_reviews_session_id": {
+ "name": "idx_cloud_agent_code_reviews_session_id",
+ "columns": [
+ {
+ "expression": "session_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_cloud_agent_code_reviews_cli_session_id": {
+ "name": "idx_cloud_agent_code_reviews_cli_session_id",
+ "columns": [
+ {
+ "expression": "cli_session_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_cloud_agent_code_reviews_status": {
+ "name": "idx_cloud_agent_code_reviews_status",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_cloud_agent_code_reviews_repo": {
+ "name": "idx_cloud_agent_code_reviews_repo",
+ "columns": [
+ {
+ "expression": "repo_full_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_cloud_agent_code_reviews_pr_number": {
+ "name": "idx_cloud_agent_code_reviews_pr_number",
+ "columns": [
+ {
+ "expression": "repo_full_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "pr_number",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_cloud_agent_code_reviews_created_at": {
+ "name": "idx_cloud_agent_code_reviews_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_cloud_agent_code_reviews_pr_author_github_id": {
+ "name": "idx_cloud_agent_code_reviews_pr_author_github_id",
+ "columns": [
+ {
+ "expression": "pr_author_github_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "cloud_agent_code_reviews_owned_by_organization_id_organizations_id_fk": {
+ "name": "cloud_agent_code_reviews_owned_by_organization_id_organizations_id_fk",
+ "tableFrom": "cloud_agent_code_reviews",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "owned_by_organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "cloud_agent_code_reviews_owned_by_user_id_kilocode_users_id_fk": {
+ "name": "cloud_agent_code_reviews_owned_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "cloud_agent_code_reviews",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "owned_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "cloud_agent_code_reviews_platform_integration_id_platform_integrations_id_fk": {
+ "name": "cloud_agent_code_reviews_platform_integration_id_platform_integrations_id_fk",
+ "tableFrom": "cloud_agent_code_reviews",
+ "tableTo": "platform_integrations",
+ "columnsFrom": [
+ "platform_integration_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "cloud_agent_code_reviews_owner_check": {
+ "name": "cloud_agent_code_reviews_owner_check",
+ "value": "(\n (\"cloud_agent_code_reviews\".\"owned_by_user_id\" IS NOT NULL AND \"cloud_agent_code_reviews\".\"owned_by_organization_id\" IS NULL) OR\n (\"cloud_agent_code_reviews\".\"owned_by_user_id\" IS NULL AND \"cloud_agent_code_reviews\".\"owned_by_organization_id\" IS NOT NULL)\n )"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.cloud_agent_feedback": {
+ "name": "cloud_agent_feedback",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cloud_agent_session_id": {
+ "name": "cloud_agent_session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "repository": {
+ "name": "repository",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_streaming": {
+ "name": "is_streaming",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "message_count": {
+ "name": "message_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "feedback_text": {
+ "name": "feedback_text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "recent_messages": {
+ "name": "recent_messages",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_cloud_agent_feedback_created_at": {
+ "name": "IDX_cloud_agent_feedback_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cloud_agent_feedback_kilo_user_id": {
+ "name": "IDX_cloud_agent_feedback_kilo_user_id",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cloud_agent_feedback_cloud_agent_session_id": {
+ "name": "IDX_cloud_agent_feedback_cloud_agent_session_id",
+ "columns": [
+ {
+ "expression": "cloud_agent_session_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "cloud_agent_feedback_kilo_user_id_kilocode_users_id_fk": {
+ "name": "cloud_agent_feedback_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "cloud_agent_feedback",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "cascade"
+ },
+ "cloud_agent_feedback_organization_id_organizations_id_fk": {
+ "name": "cloud_agent_feedback_organization_id_organizations_id_fk",
+ "tableFrom": "cloud_agent_feedback",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.cloud_agent_session_runs": {
+ "name": "cloud_agent_session_runs",
+ "schema": "",
+ "columns": {
+ "cloud_agent_session_id": {
+ "name": "cloud_agent_session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "message_id": {
+ "name": "message_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "wrapper_run_id": {
+ "name": "wrapper_run_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "queued_at": {
+ "name": "queued_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "dispatch_accepted_at": {
+ "name": "dispatch_accepted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "agent_activity_observed_at": {
+ "name": "agent_activity_observed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "terminal_at": {
+ "name": "terminal_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "failure_stage": {
+ "name": "failure_stage",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "failure_code": {
+ "name": "failure_code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error_message_redacted": {
+ "name": "error_message_redacted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error_expires_at": {
+ "name": "error_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "IDX_cloud_agent_session_runs_wrapper_run_id": {
+ "name": "IDX_cloud_agent_session_runs_wrapper_run_id",
+ "columns": [
+ {
+ "expression": "wrapper_run_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"cloud_agent_session_runs\".\"wrapper_run_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cloud_agent_session_runs_session_queued": {
+ "name": "IDX_cloud_agent_session_runs_session_queued",
+ "columns": [
+ {
+ "expression": "cloud_agent_session_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "queued_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cloud_agent_session_runs_queued_at": {
+ "name": "IDX_cloud_agent_session_runs_queued_at",
+ "columns": [
+ {
+ "expression": "queued_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cloud_agent_session_runs_terminal_at": {
+ "name": "IDX_cloud_agent_session_runs_terminal_at",
+ "columns": [
+ {
+ "expression": "terminal_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cloud_agent_session_runs_status_terminal": {
+ "name": "IDX_cloud_agent_session_runs_status_terminal",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "terminal_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cloud_agent_session_runs_failure_terminal": {
+ "name": "IDX_cloud_agent_session_runs_failure_terminal",
+ "columns": [
+ {
+ "expression": "failure_stage",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "failure_code",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "terminal_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cloud_agent_session_runs_error_expires_at": {
+ "name": "IDX_cloud_agent_session_runs_error_expires_at",
+ "columns": [
+ {
+ "expression": "error_expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"cloud_agent_session_runs\".\"error_expires_at\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "cloud_agent_session_runs_cloud_agent_session_id_cloud_agent_sessions_cloud_agent_session_id_fk": {
+ "name": "cloud_agent_session_runs_cloud_agent_session_id_cloud_agent_sessions_cloud_agent_session_id_fk",
+ "tableFrom": "cloud_agent_session_runs",
+ "tableTo": "cloud_agent_sessions",
+ "columnsFrom": [
+ "cloud_agent_session_id"
+ ],
+ "columnsTo": [
+ "cloud_agent_session_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "cloud_agent_session_runs_cloud_agent_session_id_message_id_pk": {
+ "name": "cloud_agent_session_runs_cloud_agent_session_id_message_id_pk",
+ "columns": [
+ "cloud_agent_session_id",
+ "message_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "cloud_agent_session_runs_status_check": {
+ "name": "cloud_agent_session_runs_status_check",
+ "value": "\"cloud_agent_session_runs\".\"status\" IN ('queued', 'accepted', 'completed', 'failed', 'interrupted')"
+ },
+ "cloud_agent_session_runs_failure_classification_check": {
+ "name": "cloud_agent_session_runs_failure_classification_check",
+ "value": "(\"cloud_agent_session_runs\".\"failure_stage\" IS NULL AND \"cloud_agent_session_runs\".\"failure_code\" IS NULL) OR\n (\"cloud_agent_session_runs\".\"failure_stage\" = 'pre_dispatch' AND \"cloud_agent_session_runs\".\"failure_code\" IN ('sandbox_connect_failed', 'workspace_setup_failed', 'kilo_server_failed', 'wrapper_start_failed', 'invalid_delivery_request', 'session_metadata_missing', 'model_missing', 'delivery_failure_unknown')) OR\n (\"cloud_agent_session_runs\".\"failure_stage\" = 'post_dispatch_no_activity' AND \"cloud_agent_session_runs\".\"failure_code\" IN ('wrapper_disconnected', 'wrapper_no_output', 'wrapper_ping_timeout', 'wrapper_error_before_activity', 'missing_assistant_reply')) OR\n (\"cloud_agent_session_runs\".\"failure_stage\" = 'agent_activity' AND \"cloud_agent_session_runs\".\"failure_code\" IN ('assistant_error', 'wrapper_error_after_activity')) OR\n (\"cloud_agent_session_runs\".\"failure_stage\" = 'interruption' AND \"cloud_agent_session_runs\".\"failure_code\" IN ('user_interrupt', 'container_shutdown', 'system_interrupt')) OR\n (\"cloud_agent_session_runs\".\"failure_stage\" = 'unknown' AND \"cloud_agent_session_runs\".\"failure_code\" = 'unclassified')"
+ },
+ "cloud_agent_session_runs_error_message_bounded_check": {
+ "name": "cloud_agent_session_runs_error_message_bounded_check",
+ "value": "\"cloud_agent_session_runs\".\"error_message_redacted\" IS NULL OR char_length(\"cloud_agent_session_runs\".\"error_message_redacted\") <= 4096"
+ },
+ "cloud_agent_session_runs_error_expiry_check": {
+ "name": "cloud_agent_session_runs_error_expiry_check",
+ "value": "(\"cloud_agent_session_runs\".\"error_message_redacted\" IS NULL AND \"cloud_agent_session_runs\".\"error_expires_at\" IS NULL) OR\n (\"cloud_agent_session_runs\".\"error_message_redacted\" IS NOT NULL AND \"cloud_agent_session_runs\".\"error_expires_at\" IS NOT NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.cloud_agent_sessions": {
+ "name": "cloud_agent_sessions",
+ "schema": "",
+ "columns": {
+ "cloud_agent_session_id": {
+ "name": "cloud_agent_session_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "kilo_session_id": {
+ "name": "kilo_session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "initial_message_id": {
+ "name": "initial_message_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sandbox_id": {
+ "name": "sandbox_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "failure_at": {
+ "name": "failure_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "failure_stage": {
+ "name": "failure_stage",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "failure_code": {
+ "name": "failure_code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error_message_redacted": {
+ "name": "error_message_redacted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error_expires_at": {
+ "name": "error_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "UQ_cloud_agent_sessions_kilo_session_id": {
+ "name": "UQ_cloud_agent_sessions_kilo_session_id",
+ "columns": [
+ {
+ "expression": "kilo_session_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_cloud_agent_sessions_initial_message_id": {
+ "name": "UQ_cloud_agent_sessions_initial_message_id",
+ "columns": [
+ {
+ "expression": "initial_message_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cloud_agent_sessions_sandbox_id": {
+ "name": "IDX_cloud_agent_sessions_sandbox_id",
+ "columns": [
+ {
+ "expression": "sandbox_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"cloud_agent_sessions\".\"sandbox_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cloud_agent_sessions_created_at": {
+ "name": "IDX_cloud_agent_sessions_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cloud_agent_sessions_failure_created": {
+ "name": "IDX_cloud_agent_sessions_failure_created",
+ "columns": [
+ {
+ "expression": "failure_stage",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "failure_code",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cloud_agent_sessions_failure_at": {
+ "name": "IDX_cloud_agent_sessions_failure_at",
+ "columns": [
+ {
+ "expression": "failure_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"cloud_agent_sessions\".\"failure_at\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cloud_agent_sessions_failure_classification_at": {
+ "name": "IDX_cloud_agent_sessions_failure_classification_at",
+ "columns": [
+ {
+ "expression": "failure_stage",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "failure_code",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "failure_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"cloud_agent_sessions\".\"failure_at\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cloud_agent_sessions_error_expires_at": {
+ "name": "IDX_cloud_agent_sessions_error_expires_at",
+ "columns": [
+ {
+ "expression": "error_expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"cloud_agent_sessions\".\"error_expires_at\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "cloud_agent_sessions_failure_classification_check": {
+ "name": "cloud_agent_sessions_failure_classification_check",
+ "value": "(\"cloud_agent_sessions\".\"failure_at\" IS NULL AND \"cloud_agent_sessions\".\"failure_stage\" IS NULL AND \"cloud_agent_sessions\".\"failure_code\" IS NULL) OR\n (\"cloud_agent_sessions\".\"failure_at\" IS NOT NULL AND \"cloud_agent_sessions\".\"failure_stage\" = 'sandbox_identity' AND \"cloud_agent_sessions\".\"failure_code\" = 'sandbox_id_derivation_failed') OR\n (\"cloud_agent_sessions\".\"failure_at\" IS NOT NULL AND \"cloud_agent_sessions\".\"failure_stage\" = 'registration' AND \"cloud_agent_sessions\".\"failure_code\" = 'do_registration_rejected') OR\n (\"cloud_agent_sessions\".\"failure_at\" IS NOT NULL AND \"cloud_agent_sessions\".\"failure_stage\" = 'initial_admission' AND \"cloud_agent_sessions\".\"failure_code\" IN ('initial_admission_rejected', 'initial_queue_full', 'invalid_initial_intent')) OR\n (\"cloud_agent_sessions\".\"failure_at\" IS NOT NULL AND \"cloud_agent_sessions\".\"failure_stage\" = 'transport' AND \"cloud_agent_sessions\".\"failure_code\" = 'do_rpc_outcome_unknown')"
+ },
+ "cloud_agent_sessions_error_message_bounded_check": {
+ "name": "cloud_agent_sessions_error_message_bounded_check",
+ "value": "\"cloud_agent_sessions\".\"error_message_redacted\" IS NULL OR char_length(\"cloud_agent_sessions\".\"error_message_redacted\") <= 4096"
+ },
+ "cloud_agent_sessions_error_expiry_check": {
+ "name": "cloud_agent_sessions_error_expiry_check",
+ "value": "(\"cloud_agent_sessions\".\"error_message_redacted\" IS NULL AND \"cloud_agent_sessions\".\"error_expires_at\" IS NULL) OR\n (\"cloud_agent_sessions\".\"error_message_redacted\" IS NOT NULL AND \"cloud_agent_sessions\".\"error_expires_at\" IS NOT NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.cloud_agent_webhook_triggers": {
+ "name": "cloud_agent_webhook_triggers",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "trigger_id": {
+ "name": "trigger_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "target_type": {
+ "name": "target_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'cloud_agent'"
+ },
+ "kiloclaw_instance_id": {
+ "name": "kiloclaw_instance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "activation_mode": {
+ "name": "activation_mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'webhook'"
+ },
+ "cron_expression": {
+ "name": "cron_expression",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cron_timezone": {
+ "name": "cron_timezone",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'UTC'"
+ },
+ "github_repo": {
+ "name": "github_repo",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "profile_id": {
+ "name": "profile_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_cloud_agent_webhook_triggers_user_trigger": {
+ "name": "UQ_cloud_agent_webhook_triggers_user_trigger",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "trigger_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"cloud_agent_webhook_triggers\".\"user_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_cloud_agent_webhook_triggers_org_trigger": {
+ "name": "UQ_cloud_agent_webhook_triggers_org_trigger",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "trigger_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"cloud_agent_webhook_triggers\".\"organization_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cloud_agent_webhook_triggers_user": {
+ "name": "IDX_cloud_agent_webhook_triggers_user",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cloud_agent_webhook_triggers_org": {
+ "name": "IDX_cloud_agent_webhook_triggers_org",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cloud_agent_webhook_triggers_active": {
+ "name": "IDX_cloud_agent_webhook_triggers_active",
+ "columns": [
+ {
+ "expression": "is_active",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_cloud_agent_webhook_triggers_profile": {
+ "name": "IDX_cloud_agent_webhook_triggers_profile",
+ "columns": [
+ {
+ "expression": "profile_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "cloud_agent_webhook_triggers_user_id_kilocode_users_id_fk": {
+ "name": "cloud_agent_webhook_triggers_user_id_kilocode_users_id_fk",
+ "tableFrom": "cloud_agent_webhook_triggers",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "cloud_agent_webhook_triggers_organization_id_organizations_id_fk": {
+ "name": "cloud_agent_webhook_triggers_organization_id_organizations_id_fk",
+ "tableFrom": "cloud_agent_webhook_triggers",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "cloud_agent_webhook_triggers_kiloclaw_instance_id_kiloclaw_instances_id_fk": {
+ "name": "cloud_agent_webhook_triggers_kiloclaw_instance_id_kiloclaw_instances_id_fk",
+ "tableFrom": "cloud_agent_webhook_triggers",
+ "tableTo": "kiloclaw_instances",
+ "columnsFrom": [
+ "kiloclaw_instance_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "cloud_agent_webhook_triggers_profile_id_agent_environment_profiles_id_fk": {
+ "name": "cloud_agent_webhook_triggers_profile_id_agent_environment_profiles_id_fk",
+ "tableFrom": "cloud_agent_webhook_triggers",
+ "tableTo": "agent_environment_profiles",
+ "columnsFrom": [
+ "profile_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "CHK_cloud_agent_webhook_triggers_owner": {
+ "name": "CHK_cloud_agent_webhook_triggers_owner",
+ "value": "(\n (\"cloud_agent_webhook_triggers\".\"user_id\" IS NOT NULL AND \"cloud_agent_webhook_triggers\".\"organization_id\" IS NULL) OR\n (\"cloud_agent_webhook_triggers\".\"user_id\" IS NULL AND \"cloud_agent_webhook_triggers\".\"organization_id\" IS NOT NULL)\n )"
+ },
+ "CHK_cloud_agent_webhook_triggers_cloud_agent_fields": {
+ "name": "CHK_cloud_agent_webhook_triggers_cloud_agent_fields",
+ "value": "(\n \"cloud_agent_webhook_triggers\".\"target_type\" != 'cloud_agent' OR\n (\"cloud_agent_webhook_triggers\".\"github_repo\" IS NOT NULL AND \"cloud_agent_webhook_triggers\".\"profile_id\" IS NOT NULL)\n )"
+ },
+ "CHK_cloud_agent_webhook_triggers_kiloclaw_fields": {
+ "name": "CHK_cloud_agent_webhook_triggers_kiloclaw_fields",
+ "value": "(\n \"cloud_agent_webhook_triggers\".\"target_type\" != 'kiloclaw_chat' OR\n \"cloud_agent_webhook_triggers\".\"kiloclaw_instance_id\" IS NOT NULL\n )"
+ },
+ "CHK_cloud_agent_webhook_triggers_scheduled_fields": {
+ "name": "CHK_cloud_agent_webhook_triggers_scheduled_fields",
+ "value": "(\n \"cloud_agent_webhook_triggers\".\"activation_mode\" != 'scheduled' OR\n \"cloud_agent_webhook_triggers\".\"cron_expression\" IS NOT NULL\n )"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.code_indexing_manifest": {
+ "name": "code_indexing_manifest",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "git_branch": {
+ "name": "git_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "file_hash": {
+ "name": "file_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "file_path": {
+ "name": "file_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "chunk_count": {
+ "name": "chunk_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "total_lines": {
+ "name": "total_lines",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "total_ai_lines": {
+ "name": "total_ai_lines",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_code_indexing_manifest_organization_id": {
+ "name": "IDX_code_indexing_manifest_organization_id",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_code_indexing_manifest_kilo_user_id": {
+ "name": "IDX_code_indexing_manifest_kilo_user_id",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_code_indexing_manifest_project_id": {
+ "name": "IDX_code_indexing_manifest_project_id",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_code_indexing_manifest_git_branch": {
+ "name": "IDX_code_indexing_manifest_git_branch",
+ "columns": [
+ {
+ "expression": "git_branch",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_code_indexing_manifest_created_at": {
+ "name": "IDX_code_indexing_manifest_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "code_indexing_manifest_kilo_user_id_kilocode_users_id_fk": {
+ "name": "code_indexing_manifest_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "code_indexing_manifest",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_code_indexing_manifest_org_user_project_hash_branch": {
+ "name": "UQ_code_indexing_manifest_org_user_project_hash_branch",
+ "nullsNotDistinct": true,
+ "columns": [
+ "organization_id",
+ "kilo_user_id",
+ "project_id",
+ "file_path",
+ "git_branch"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.code_indexing_search": {
+ "name": "code_indexing_search",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "query": {
+ "name": "query",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_code_indexing_search_organization_id": {
+ "name": "IDX_code_indexing_search_organization_id",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_code_indexing_search_kilo_user_id": {
+ "name": "IDX_code_indexing_search_kilo_user_id",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_code_indexing_search_project_id": {
+ "name": "IDX_code_indexing_search_project_id",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_code_indexing_search_created_at": {
+ "name": "IDX_code_indexing_search_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "code_indexing_search_kilo_user_id_kilocode_users_id_fk": {
+ "name": "code_indexing_search_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "code_indexing_search",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.code_review_feedback_events": {
+ "name": "code_review_feedback_events",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "owned_by_organization_id": {
+ "name": "owned_by_organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owned_by_user_id": {
+ "name": "owned_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "platform": {
+ "name": "platform",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "repo_full_name": {
+ "name": "repo_full_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "pr_number": {
+ "name": "pr_number",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "kilo_comment_id": {
+ "name": "kilo_comment_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reply_excerpt": {
+ "name": "reply_excerpt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kilo_comment_excerpt": {
+ "name": "kilo_comment_excerpt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "dedupe_hash": {
+ "name": "dedupe_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "occurred_at": {
+ "name": "occurred_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "idx_code_review_feedback_events_owned_by_org_id": {
+ "name": "idx_code_review_feedback_events_owned_by_org_id",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_code_review_feedback_events_owned_by_user_id": {
+ "name": "idx_code_review_feedback_events_owned_by_user_id",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_code_review_feedback_events_platform_repo": {
+ "name": "idx_code_review_feedback_events_platform_repo",
+ "columns": [
+ {
+ "expression": "platform",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "repo_full_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_code_review_feedback_events_created_at": {
+ "name": "idx_code_review_feedback_events_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "code_review_feedback_events_owned_by_organization_id_organizations_id_fk": {
+ "name": "code_review_feedback_events_owned_by_organization_id_organizations_id_fk",
+ "tableFrom": "code_review_feedback_events",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "owned_by_organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "code_review_feedback_events_owned_by_user_id_kilocode_users_id_fk": {
+ "name": "code_review_feedback_events_owned_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "code_review_feedback_events",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "owned_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_code_review_feedback_events_dedupe_hash": {
+ "name": "UQ_code_review_feedback_events_dedupe_hash",
+ "nullsNotDistinct": false,
+ "columns": [
+ "dedupe_hash"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "code_review_feedback_events_owner_check": {
+ "name": "code_review_feedback_events_owner_check",
+ "value": "(\n (\"code_review_feedback_events\".\"owned_by_user_id\" IS NOT NULL AND \"code_review_feedback_events\".\"owned_by_organization_id\" IS NULL) OR\n (\"code_review_feedback_events\".\"owned_by_user_id\" IS NULL AND \"code_review_feedback_events\".\"owned_by_organization_id\" IS NOT NULL)\n )"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.code_review_memory_proposals": {
+ "name": "code_review_memory_proposals",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "owned_by_organization_id": {
+ "name": "owned_by_organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owned_by_user_id": {
+ "name": "owned_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "platform": {
+ "name": "platform",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "repo_full_name": {
+ "name": "repo_full_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'open'"
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "rationale": {
+ "name": "rationale",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "proposed_markdown": {
+ "name": "proposed_markdown",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "evidence": {
+ "name": "evidence",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "positive_count": {
+ "name": "positive_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "negative_count": {
+ "name": "negative_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "neutral_count": {
+ "name": "neutral_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "change_request_url": {
+ "name": "change_request_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "idx_code_review_memory_proposals_owned_by_org_id": {
+ "name": "idx_code_review_memory_proposals_owned_by_org_id",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_code_review_memory_proposals_owned_by_user_id": {
+ "name": "idx_code_review_memory_proposals_owned_by_user_id",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_code_review_memory_proposals_platform_repo_status": {
+ "name": "idx_code_review_memory_proposals_platform_repo_status",
+ "columns": [
+ {
+ "expression": "platform",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "repo_full_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_code_review_memory_proposals_updated_at": {
+ "name": "idx_code_review_memory_proposals_updated_at",
+ "columns": [
+ {
+ "expression": "updated_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_code_review_memory_proposals_org_active_scope": {
+ "name": "UQ_code_review_memory_proposals_org_active_scope",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "platform",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "repo_full_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"code_review_memory_proposals\".\"owned_by_organization_id\" IS NOT NULL AND \"code_review_memory_proposals\".\"status\" IN ('open', 'edited', 'opening_change_request')",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_code_review_memory_proposals_user_active_scope": {
+ "name": "UQ_code_review_memory_proposals_user_active_scope",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "platform",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "repo_full_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"code_review_memory_proposals\".\"owned_by_user_id\" IS NOT NULL AND \"code_review_memory_proposals\".\"status\" IN ('open', 'edited', 'opening_change_request')",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "code_review_memory_proposals_owned_by_organization_id_organizations_id_fk": {
+ "name": "code_review_memory_proposals_owned_by_organization_id_organizations_id_fk",
+ "tableFrom": "code_review_memory_proposals",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "owned_by_organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "code_review_memory_proposals_owned_by_user_id_kilocode_users_id_fk": {
+ "name": "code_review_memory_proposals_owned_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "code_review_memory_proposals",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "owned_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "code_review_memory_proposals_owner_check": {
+ "name": "code_review_memory_proposals_owner_check",
+ "value": "(\n (\"code_review_memory_proposals\".\"owned_by_user_id\" IS NOT NULL AND \"code_review_memory_proposals\".\"owned_by_organization_id\" IS NULL) OR\n (\"code_review_memory_proposals\".\"owned_by_user_id\" IS NULL AND \"code_review_memory_proposals\".\"owned_by_organization_id\" IS NOT NULL)\n )"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.coding_plan_availability_intents": {
+ "name": "coding_plan_availability_intents",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "plan_id": {
+ "name": "plan_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_coding_plan_availability_intents_user_plan": {
+ "name": "UQ_coding_plan_availability_intents_user_plan",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "plan_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_coding_plan_availability_intents_plan": {
+ "name": "IDX_coding_plan_availability_intents_plan",
+ "columns": [
+ {
+ "expression": "plan_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "coding_plan_availability_intents_user_id_kilocode_users_id_fk": {
+ "name": "coding_plan_availability_intents_user_id_kilocode_users_id_fk",
+ "tableFrom": "coding_plan_availability_intents",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.coding_plan_key_inventory": {
+ "name": "coding_plan_key_inventory",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "plan_id": {
+ "name": "plan_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "upstream_plan_id": {
+ "name": "upstream_plan_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "encrypted_api_key": {
+ "name": "encrypted_api_key",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "credential_fingerprint": {
+ "name": "credential_fingerprint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'available'"
+ },
+ "assigned_to_user_id": {
+ "name": "assigned_to_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "assigned_at": {
+ "name": "assigned_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revocation_requested_at": {
+ "name": "revocation_requested_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revocation_attempt_count": {
+ "name": "revocation_attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "last_revocation_error": {
+ "name": "last_revocation_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_coding_plan_key_inv_fingerprint": {
+ "name": "UQ_coding_plan_key_inv_fingerprint",
+ "columns": [
+ {
+ "expression": "credential_fingerprint",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_coding_plan_key_inv_plan_status": {
+ "name": "IDX_coding_plan_key_inv_plan_status",
+ "columns": [
+ {
+ "expression": "plan_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_coding_plan_key_inv_available": {
+ "name": "IDX_coding_plan_key_inv_available",
+ "columns": [
+ {
+ "expression": "plan_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"coding_plan_key_inventory\".\"status\" = 'available'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "coding_plan_key_inventory_assigned_to_user_id_kilocode_users_id_fk": {
+ "name": "coding_plan_key_inventory_assigned_to_user_id_kilocode_users_id_fk",
+ "tableFrom": "coding_plan_key_inventory",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "assigned_to_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "coding_plan_key_inventory_status_check": {
+ "name": "coding_plan_key_inventory_status_check",
+ "value": "\"coding_plan_key_inventory\".\"status\" IN ('available', 'assigned', 'revocation_pending', 'revoked', 'revocation_failed')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.coding_plan_subscriptions": {
+ "name": "coding_plan_subscriptions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "plan_id": {
+ "name": "plan_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key_inventory_id": {
+ "name": "key_inventory_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "installed_byok_key_id": {
+ "name": "installed_byok_key_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cost_microdollars": {
+ "name": "cost_microdollars",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "billing_period_days": {
+ "name": "billing_period_days",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "current_period_start": {
+ "name": "current_period_start",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "current_period_end": {
+ "name": "current_period_end",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "credit_renewal_at": {
+ "name": "credit_renewal_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cancel_at_period_end": {
+ "name": "cancel_at_period_end",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "past_due_started_at": {
+ "name": "past_due_started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "payment_grace_expires_at": {
+ "name": "payment_grace_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "auto_top_up_attempted_for_due": {
+ "name": "auto_top_up_attempted_for_due",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "canceled_at": {
+ "name": "canceled_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cancellation_reason": {
+ "name": "cancellation_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_coding_plan_sub_live_user_plan": {
+ "name": "UQ_coding_plan_sub_live_user_plan",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "plan_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"coding_plan_subscriptions\".\"status\" IN ('active', 'past_due')",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_coding_plan_sub_status": {
+ "name": "IDX_coding_plan_sub_status",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_coding_plan_sub_renewal": {
+ "name": "IDX_coding_plan_sub_renewal",
+ "columns": [
+ {
+ "expression": "credit_renewal_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_coding_plan_sub_inventory": {
+ "name": "IDX_coding_plan_sub_inventory",
+ "columns": [
+ {
+ "expression": "key_inventory_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "coding_plan_subscriptions_user_id_kilocode_users_id_fk": {
+ "name": "coding_plan_subscriptions_user_id_kilocode_users_id_fk",
+ "tableFrom": "coding_plan_subscriptions",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "coding_plan_subscriptions_key_inventory_id_coding_plan_key_inventory_id_fk": {
+ "name": "coding_plan_subscriptions_key_inventory_id_coding_plan_key_inventory_id_fk",
+ "tableFrom": "coding_plan_subscriptions",
+ "tableTo": "coding_plan_key_inventory",
+ "columnsFrom": [
+ "key_inventory_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "coding_plan_subscriptions_installed_byok_key_id_byok_api_keys_id_fk": {
+ "name": "coding_plan_subscriptions_installed_byok_key_id_byok_api_keys_id_fk",
+ "tableFrom": "coding_plan_subscriptions",
+ "tableTo": "byok_api_keys",
+ "columnsFrom": [
+ "installed_byok_key_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "coding_plan_subscriptions_status_check": {
+ "name": "coding_plan_subscriptions_status_check",
+ "value": "\"coding_plan_subscriptions\".\"status\" IN ('active', 'past_due', 'canceled')"
+ },
+ "coding_plan_subscriptions_live_access_check": {
+ "name": "coding_plan_subscriptions_live_access_check",
+ "value": "\"coding_plan_subscriptions\".\"status\" = 'canceled' OR \"coding_plan_subscriptions\".\"key_inventory_id\" IS NOT NULL"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.coding_plan_terms": {
+ "name": "coding_plan_terms",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "subscription_id": {
+ "name": "subscription_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "plan_id": {
+ "name": "plan_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "idempotency_key": {
+ "name": "idempotency_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "period_start": {
+ "name": "period_start",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "period_end": {
+ "name": "period_end",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cost_microdollars": {
+ "name": "cost_microdollars",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "credit_transaction_id": {
+ "name": "credit_transaction_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_coding_plan_terms_request": {
+ "name": "UQ_coding_plan_terms_request",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "plan_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "idempotency_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_coding_plan_terms_subscription": {
+ "name": "IDX_coding_plan_terms_subscription",
+ "columns": [
+ {
+ "expression": "subscription_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "coding_plan_terms_subscription_id_coding_plan_subscriptions_id_fk": {
+ "name": "coding_plan_terms_subscription_id_coding_plan_subscriptions_id_fk",
+ "tableFrom": "coding_plan_terms",
+ "tableTo": "coding_plan_subscriptions",
+ "columnsFrom": [
+ "subscription_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "coding_plan_terms_user_id_kilocode_users_id_fk": {
+ "name": "coding_plan_terms_user_id_kilocode_users_id_fk",
+ "tableFrom": "coding_plan_terms",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "coding_plan_terms_credit_transaction_id_credit_transactions_id_fk": {
+ "name": "coding_plan_terms_credit_transaction_id_credit_transactions_id_fk",
+ "tableFrom": "coding_plan_terms",
+ "tableTo": "credit_transactions",
+ "columnsFrom": [
+ "credit_transaction_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "coding_plan_terms_kind_check": {
+ "name": "coding_plan_terms_kind_check",
+ "value": "\"coding_plan_terms\".\"kind\" IN ('activation', 'extension', 'renewal')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.contributor_champion_contributors": {
+ "name": "contributor_champion_contributors",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "github_login": {
+ "name": "github_login",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "github_profile_url": {
+ "name": "github_profile_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "github_user_id": {
+ "name": "github_user_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "first_contribution_at": {
+ "name": "first_contribution_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_contribution_at": {
+ "name": "last_contribution_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "all_time_contributions": {
+ "name": "all_time_contributions",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "manual_email": {
+ "name": "manual_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_contributor_champion_contributors_last_contribution_at": {
+ "name": "IDX_contributor_champion_contributors_last_contribution_at",
+ "columns": [
+ {
+ "expression": "last_contribution_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_contributor_champion_contributors_manual_email": {
+ "name": "IDX_contributor_champion_contributors_manual_email",
+ "columns": [
+ {
+ "expression": "manual_email",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_contributor_champion_contributors_github_login": {
+ "name": "UQ_contributor_champion_contributors_github_login",
+ "nullsNotDistinct": false,
+ "columns": [
+ "github_login"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.contributor_champion_events": {
+ "name": "contributor_champion_events",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "contributor_id": {
+ "name": "contributor_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "repo_full_name": {
+ "name": "repo_full_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "github_pr_number": {
+ "name": "github_pr_number",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "github_pr_url": {
+ "name": "github_pr_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "github_pr_title": {
+ "name": "github_pr_title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "github_author_login": {
+ "name": "github_author_login",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "github_author_email": {
+ "name": "github_author_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "merged_at": {
+ "name": "merged_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_contributor_champion_events_contributor_id": {
+ "name": "IDX_contributor_champion_events_contributor_id",
+ "columns": [
+ {
+ "expression": "contributor_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_contributor_champion_events_merged_at": {
+ "name": "IDX_contributor_champion_events_merged_at",
+ "columns": [
+ {
+ "expression": "merged_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_contributor_champion_events_author_email": {
+ "name": "IDX_contributor_champion_events_author_email",
+ "columns": [
+ {
+ "expression": "github_author_email",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "contributor_champion_events_contributor_id_contributor_champion_contributors_id_fk": {
+ "name": "contributor_champion_events_contributor_id_contributor_champion_contributors_id_fk",
+ "tableFrom": "contributor_champion_events",
+ "tableTo": "contributor_champion_contributors",
+ "columnsFrom": [
+ "contributor_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_contributor_champion_events_repo_pr": {
+ "name": "UQ_contributor_champion_events_repo_pr",
+ "nullsNotDistinct": false,
+ "columns": [
+ "repo_full_name",
+ "github_pr_number"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.contributor_champion_memberships": {
+ "name": "contributor_champion_memberships",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "contributor_id": {
+ "name": "contributor_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "selected_tier": {
+ "name": "selected_tier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enrolled_tier": {
+ "name": "enrolled_tier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enrolled_at": {
+ "name": "enrolled_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "credit_amount_microdollars": {
+ "name": "credit_amount_microdollars",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "credits_last_granted_at": {
+ "name": "credits_last_granted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "linked_kilo_user_id": {
+ "name": "linked_kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_contributor_champion_memberships_credits_due": {
+ "name": "IDX_contributor_champion_memberships_credits_due",
+ "columns": [
+ {
+ "expression": "credits_last_granted_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"contributor_champion_memberships\".\"enrolled_tier\" IS NOT NULL AND \"contributor_champion_memberships\".\"credit_amount_microdollars\" > 0",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_contributor_champion_memberships_linked_kilo_user_id": {
+ "name": "IDX_contributor_champion_memberships_linked_kilo_user_id",
+ "columns": [
+ {
+ "expression": "linked_kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "contributor_champion_memberships_contributor_id_contributor_champion_contributors_id_fk": {
+ "name": "contributor_champion_memberships_contributor_id_contributor_champion_contributors_id_fk",
+ "tableFrom": "contributor_champion_memberships",
+ "tableTo": "contributor_champion_contributors",
+ "columnsFrom": [
+ "contributor_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ },
+ "contributor_champion_memberships_linked_kilo_user_id_kilocode_users_id_fk": {
+ "name": "contributor_champion_memberships_linked_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "contributor_champion_memberships",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "linked_kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_contributor_champion_memberships_contributor_id": {
+ "name": "UQ_contributor_champion_memberships_contributor_id",
+ "nullsNotDistinct": false,
+ "columns": [
+ "contributor_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "contributor_champion_memberships_selected_tier_check": {
+ "name": "contributor_champion_memberships_selected_tier_check",
+ "value": "\"contributor_champion_memberships\".\"selected_tier\" IS NULL OR \"contributor_champion_memberships\".\"selected_tier\" IN ('contributor', 'ambassador', 'champion')"
+ },
+ "contributor_champion_memberships_enrolled_tier_check": {
+ "name": "contributor_champion_memberships_enrolled_tier_check",
+ "value": "\"contributor_champion_memberships\".\"enrolled_tier\" IS NULL OR \"contributor_champion_memberships\".\"enrolled_tier\" IN ('contributor', 'ambassador', 'champion')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.contributor_champion_sync_state": {
+ "name": "contributor_champion_sync_state",
+ "schema": "",
+ "columns": {
+ "repo_full_name": {
+ "name": "repo_full_name",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "last_merged_at": {
+ "name": "last_merged_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_synced_at": {
+ "name": "last_synced_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.credit_campaigns": {
+ "name": "credit_campaigns",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "credit_category": {
+ "name": "credit_category",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "amount_microdollars": {
+ "name": "amount_microdollars",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "credit_expiry_hours": {
+ "name": "credit_expiry_hours",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "campaign_ends_at": {
+ "name": "campaign_ends_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "total_redemptions_allowed": {
+ "name": "total_redemptions_allowed",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "active": {
+ "name": "active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by_kilo_user_id": {
+ "name": "created_by_kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_credit_campaigns_slug": {
+ "name": "UQ_credit_campaigns_slug",
+ "columns": [
+ {
+ "expression": "slug",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_credit_campaigns_credit_category": {
+ "name": "UQ_credit_campaigns_credit_category",
+ "columns": [
+ {
+ "expression": "credit_category",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "credit_campaigns_slug_format_check": {
+ "name": "credit_campaigns_slug_format_check",
+ "value": "\"credit_campaigns\".\"slug\" ~ '^[a-z0-9-]{5,40}$'"
+ },
+ "credit_campaigns_amount_positive_check": {
+ "name": "credit_campaigns_amount_positive_check",
+ "value": "\"credit_campaigns\".\"amount_microdollars\" > 0"
+ },
+ "credit_campaigns_credit_expiry_hours_positive_check": {
+ "name": "credit_campaigns_credit_expiry_hours_positive_check",
+ "value": "\"credit_campaigns\".\"credit_expiry_hours\" IS NULL OR \"credit_campaigns\".\"credit_expiry_hours\" > 0"
+ },
+ "credit_campaigns_total_redemptions_allowed_positive_check": {
+ "name": "credit_campaigns_total_redemptions_allowed_positive_check",
+ "value": "\"credit_campaigns\".\"total_redemptions_allowed\" > 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.credit_transactions": {
+ "name": "credit_transactions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "amount_microdollars": {
+ "name": "amount_microdollars",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expiration_baseline_microdollars_used": {
+ "name": "expiration_baseline_microdollars_used",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "original_baseline_microdollars_used": {
+ "name": "original_baseline_microdollars_used",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_free": {
+ "name": "is_free",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "original_transaction_id": {
+ "name": "original_transaction_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_payment_id": {
+ "name": "stripe_payment_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "coinbase_credit_block_id": {
+ "name": "coinbase_credit_block_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "credit_category": {
+ "name": "credit_category",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expiry_date": {
+ "name": "expiry_date",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_kilo_user_id": {
+ "name": "created_by_kilo_user_id",
+ "type": "text",
+ "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": {
+ "credit_transactions_created_by_kilo_user_id_kilocode_users_id_fk": {
+ "name": "credit_transactions_created_by_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "credit_transactions",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "created_by_kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.custom_llm2": {
+ "name": "custom_llm2",
+ "schema": "",
+ "columns": {
+ "public_id": {
+ "name": "public_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "definition": {
+ "name": "definition",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.deleted_user_email_tombstones": {
+ "name": "deleted_user_email_tombstones",
+ "schema": "",
+ "columns": {
+ "normalized_email_hash": {
+ "name": "normalized_email_hash",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.deployment_builds": {
+ "name": "deployment_builds",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "deployment_id": {
+ "name": "deployment_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "idx_deployment_builds_deployment_id": {
+ "name": "idx_deployment_builds_deployment_id",
+ "columns": [
+ {
+ "expression": "deployment_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_deployment_builds_status": {
+ "name": "idx_deployment_builds_status",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "deployment_builds_deployment_id_deployments_id_fk": {
+ "name": "deployment_builds_deployment_id_deployments_id_fk",
+ "tableFrom": "deployment_builds",
+ "tableTo": "deployments",
+ "columnsFrom": [
+ "deployment_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.deployment_env_vars": {
+ "name": "deployment_env_vars",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "deployment_id": {
+ "name": "deployment_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "is_secret": {
+ "name": "is_secret",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "idx_deployment_env_vars_deployment_id": {
+ "name": "idx_deployment_env_vars_deployment_id",
+ "columns": [
+ {
+ "expression": "deployment_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "deployment_env_vars_deployment_id_deployments_id_fk": {
+ "name": "deployment_env_vars_deployment_id_deployments_id_fk",
+ "tableFrom": "deployment_env_vars",
+ "tableTo": "deployments",
+ "columnsFrom": [
+ "deployment_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_deployment_env_vars_deployment_key": {
+ "name": "UQ_deployment_env_vars_deployment_key",
+ "nullsNotDistinct": false,
+ "columns": [
+ "deployment_id",
+ "key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.deployment_events": {
+ "name": "deployment_events",
+ "schema": "",
+ "columns": {
+ "build_id": {
+ "name": "build_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_id": {
+ "name": "event_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_type": {
+ "name": "event_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'log'"
+ },
+ "timestamp": {
+ "name": "timestamp",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "payload": {
+ "name": "payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "idx_deployment_events_build_id": {
+ "name": "idx_deployment_events_build_id",
+ "columns": [
+ {
+ "expression": "build_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_deployment_events_timestamp": {
+ "name": "idx_deployment_events_timestamp",
+ "columns": [
+ {
+ "expression": "timestamp",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_deployment_events_type": {
+ "name": "idx_deployment_events_type",
+ "columns": [
+ {
+ "expression": "event_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "deployment_events_build_id_deployment_builds_id_fk": {
+ "name": "deployment_events_build_id_deployment_builds_id_fk",
+ "tableFrom": "deployment_events",
+ "tableTo": "deployment_builds",
+ "columnsFrom": [
+ "build_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "deployment_events_build_id_event_id_pk": {
+ "name": "deployment_events_build_id_event_id_pk",
+ "columns": [
+ "build_id",
+ "event_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.deployment_threat_detections": {
+ "name": "deployment_threat_detections",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "deployment_id": {
+ "name": "deployment_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "build_id": {
+ "name": "build_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "threat_type": {
+ "name": "threat_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "idx_deployment_threat_detections_deployment_id": {
+ "name": "idx_deployment_threat_detections_deployment_id",
+ "columns": [
+ {
+ "expression": "deployment_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_deployment_threat_detections_created_at": {
+ "name": "idx_deployment_threat_detections_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "deployment_threat_detections_deployment_id_deployments_id_fk": {
+ "name": "deployment_threat_detections_deployment_id_deployments_id_fk",
+ "tableFrom": "deployment_threat_detections",
+ "tableTo": "deployments",
+ "columnsFrom": [
+ "deployment_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "deployment_threat_detections_build_id_deployment_builds_id_fk": {
+ "name": "deployment_threat_detections_build_id_deployment_builds_id_fk",
+ "tableFrom": "deployment_threat_detections",
+ "tableTo": "deployment_builds",
+ "columnsFrom": [
+ "build_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.deployments": {
+ "name": "deployments",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "created_by_user_id": {
+ "name": "created_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owned_by_user_id": {
+ "name": "owned_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owned_by_organization_id": {
+ "name": "owned_by_organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deployment_slug": {
+ "name": "deployment_slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "internal_worker_name": {
+ "name": "internal_worker_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "repository_source": {
+ "name": "repository_source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "branch": {
+ "name": "branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "deployment_url": {
+ "name": "deployment_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "platform_integration_id": {
+ "name": "platform_integration_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_type": {
+ "name": "source_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'github'"
+ },
+ "git_auth_token": {
+ "name": "git_auth_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "last_deployed_at": {
+ "name": "last_deployed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_build_id": {
+ "name": "last_build_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "threat_status": {
+ "name": "threat_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_from": {
+ "name": "created_from",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "idx_deployments_owned_by_user_id": {
+ "name": "idx_deployments_owned_by_user_id",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_deployments_owned_by_organization_id": {
+ "name": "idx_deployments_owned_by_organization_id",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_deployments_platform_integration_id": {
+ "name": "idx_deployments_platform_integration_id",
+ "columns": [
+ {
+ "expression": "platform_integration_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_deployments_repository_source_branch": {
+ "name": "idx_deployments_repository_source_branch",
+ "columns": [
+ {
+ "expression": "repository_source",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "branch",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_deployments_threat_status_pending": {
+ "name": "idx_deployments_threat_status_pending",
+ "columns": [
+ {
+ "expression": "threat_status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"deployments\".\"threat_status\" = 'pending_scan'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "deployments_owned_by_user_id_kilocode_users_id_fk": {
+ "name": "deployments_owned_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "deployments",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "owned_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "deployments_owned_by_organization_id_organizations_id_fk": {
+ "name": "deployments_owned_by_organization_id_organizations_id_fk",
+ "tableFrom": "deployments",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "owned_by_organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_deployments_deployment_slug": {
+ "name": "UQ_deployments_deployment_slug",
+ "nullsNotDistinct": false,
+ "columns": [
+ "deployment_slug"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "deployments_owner_check": {
+ "name": "deployments_owner_check",
+ "value": "(\n (\"deployments\".\"owned_by_user_id\" IS NOT NULL AND \"deployments\".\"owned_by_organization_id\" IS NULL) OR\n (\"deployments\".\"owned_by_user_id\" IS NULL AND \"deployments\".\"owned_by_organization_id\" IS NOT NULL)\n )"
+ },
+ "deployments_source_type_check": {
+ "name": "deployments_source_type_check",
+ "value": "\"deployments\".\"source_type\" IN ('github', 'git', 'app-builder')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.deployments_ephemeral": {
+ "name": "deployments_ephemeral",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "owned_by_user_id": {
+ "name": "owned_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_type": {
+ "name": "source_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "internal_worker_name": {
+ "name": "internal_worker_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "deployment_slug": {
+ "name": "deployment_slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "next_cleanup_at": {
+ "name": "next_cleanup_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cleanup_claim_token": {
+ "name": "cleanup_claim_token",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cleanup_claimed_until": {
+ "name": "cleanup_claimed_until",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "idx_deployments_ephemeral_owned_by_user_id": {
+ "name": "idx_deployments_ephemeral_owned_by_user_id",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_deployments_ephemeral_next_cleanup_at": {
+ "name": "idx_deployments_ephemeral_next_cleanup_at",
+ "columns": [
+ {
+ "expression": "next_cleanup_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "deployments_ephemeral_owned_by_user_id_kilocode_users_id_fk": {
+ "name": "deployments_ephemeral_owned_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "deployments_ephemeral",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "owned_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_deployments_ephemeral_internal_worker_name": {
+ "name": "UQ_deployments_ephemeral_internal_worker_name",
+ "nullsNotDistinct": false,
+ "columns": [
+ "internal_worker_name"
+ ]
+ },
+ "UQ_deployments_ephemeral_deployment_slug": {
+ "name": "UQ_deployments_ephemeral_deployment_slug",
+ "nullsNotDistinct": false,
+ "columns": [
+ "deployment_slug"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "deployments_ephemeral_source_type_check": {
+ "name": "deployments_ephemeral_source_type_check",
+ "value": "\"deployments_ephemeral\".\"source_type\" IN ('html')"
+ },
+ "deployments_ephemeral_status_check": {
+ "name": "deployments_ephemeral_status_check",
+ "value": "\"deployments_ephemeral\".\"status\" IN ('pending', 'active', 'cleanup_retry')"
+ },
+ "deployments_ephemeral_claim_fields_check": {
+ "name": "deployments_ephemeral_claim_fields_check",
+ "value": "(\"deployments_ephemeral\".\"cleanup_claim_token\" IS NULL) = (\"deployments_ephemeral\".\"cleanup_claimed_until\" IS NULL)"
+ },
+ "deployments_ephemeral_active_fields_check": {
+ "name": "deployments_ephemeral_active_fields_check",
+ "value": "\"deployments_ephemeral\".\"status\" <> 'active' OR (\"deployments_ephemeral\".\"deployment_slug\" IS NOT NULL AND \"deployments_ephemeral\".\"expires_at\" IS NOT NULL)"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.device_auth_requests": {
+ "name": "device_auth_requests",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "code": {
+ "name": "code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "approved_at": {
+ "name": "approved_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_device_auth_requests_code": {
+ "name": "UQ_device_auth_requests_code",
+ "columns": [
+ {
+ "expression": "code",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_device_auth_requests_status": {
+ "name": "IDX_device_auth_requests_status",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_device_auth_requests_expires_at": {
+ "name": "IDX_device_auth_requests_expires_at",
+ "columns": [
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_device_auth_requests_kilo_user_id": {
+ "name": "IDX_device_auth_requests_kilo_user_id",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "device_auth_requests_kilo_user_id_kilocode_users_id_fk": {
+ "name": "device_auth_requests_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "device_auth_requests",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.discord_gateway_listener": {
+ "name": "discord_gateway_listener",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "default": 1
+ },
+ "listener_id": {
+ "name": "listener_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.editor_name": {
+ "name": "editor_name",
+ "schema": "",
+ "columns": {
+ "editor_name_id": {
+ "name": "editor_name_id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "editor_name": {
+ "name": "editor_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "UQ_editor_name": {
+ "name": "UQ_editor_name",
+ "columns": [
+ {
+ "expression": "editor_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.enrichment_data": {
+ "name": "enrichment_data",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "github_enrichment_data": {
+ "name": "github_enrichment_data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "linkedin_enrichment_data": {
+ "name": "linkedin_enrichment_data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "clay_enrichment_data": {
+ "name": "clay_enrichment_data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_enrichment_data_user_id": {
+ "name": "IDX_enrichment_data_user_id",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "enrichment_data_user_id_kilocode_users_id_fk": {
+ "name": "enrichment_data_user_id_kilocode_users_id_fk",
+ "tableFrom": "enrichment_data",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_enrichment_data_user_id": {
+ "name": "UQ_enrichment_data_user_id",
+ "nullsNotDistinct": false,
+ "columns": [
+ "user_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.exa_monthly_usage": {
+ "name": "exa_monthly_usage",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "month": {
+ "name": "month",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "total_cost_microdollars": {
+ "name": "total_cost_microdollars",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "total_charged_microdollars": {
+ "name": "total_charged_microdollars",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "request_count": {
+ "name": "request_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "free_allowance_microdollars": {
+ "name": "free_allowance_microdollars",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 10000000
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "idx_exa_monthly_usage_personal": {
+ "name": "idx_exa_monthly_usage_personal",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "month",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"exa_monthly_usage\".\"organization_id\" is null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_exa_monthly_usage_org": {
+ "name": "idx_exa_monthly_usage_org",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "month",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"exa_monthly_usage\".\"organization_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.exa_usage_log": {
+ "name": "exa_usage_log",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "path": {
+ "name": "path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cost_microdollars": {
+ "name": "cost_microdollars",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "charged_to_balance": {
+ "name": "charged_to_balance",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "feature_id": {
+ "name": "feature_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "idx_exa_usage_log_user_created": {
+ "name": "idx_exa_usage_log_user_created",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "exa_usage_log_id_created_at_pk": {
+ "name": "exa_usage_log_id_created_at_pk",
+ "columns": [
+ "id",
+ "created_at"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.feature": {
+ "name": "feature",
+ "schema": "",
+ "columns": {
+ "feature_id": {
+ "name": "feature_id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "feature": {
+ "name": "feature",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "UQ_feature": {
+ "name": "UQ_feature",
+ "columns": [
+ {
+ "expression": "feature",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.finish_reason": {
+ "name": "finish_reason",
+ "schema": "",
+ "columns": {
+ "finish_reason_id": {
+ "name": "finish_reason_id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "finish_reason": {
+ "name": "finish_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "UQ_finish_reason": {
+ "name": "UQ_finish_reason",
+ "columns": [
+ {
+ "expression": "finish_reason",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.free_model_usage": {
+ "name": "free_model_usage",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "idx_free_model_usage_ip_created_at": {
+ "name": "idx_free_model_usage_ip_created_at",
+ "columns": [
+ {
+ "expression": "ip_address",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_free_model_usage_created_at": {
+ "name": "idx_free_model_usage_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.github_branch_pull_requests": {
+ "name": "github_branch_pull_requests",
+ "schema": "",
+ "columns": {
+ "git_url": {
+ "name": "git_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "git_branch": {
+ "name": "git_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owned_by_organization_id": {
+ "name": "owned_by_organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owned_by_user_id": {
+ "name": "owned_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pr_url": {
+ "name": "pr_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pr_number": {
+ "name": "pr_number",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pr_state": {
+ "name": "pr_state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pr_title": {
+ "name": "pr_title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pr_head_sha": {
+ "name": "pr_head_sha",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pr_review_decision": {
+ "name": "pr_review_decision",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "review_decision_pending": {
+ "name": "review_decision_pending",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "review_decision_fetching_at": {
+ "name": "review_decision_fetching_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pr_last_synced_at": {
+ "name": "pr_last_synced_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_github_branch_prs_org": {
+ "name": "UQ_github_branch_prs_org",
+ "columns": [
+ {
+ "expression": "git_url",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "git_branch",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"github_branch_pull_requests\".\"owned_by_organization_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_github_branch_prs_user": {
+ "name": "UQ_github_branch_prs_user",
+ "columns": [
+ {
+ "expression": "git_url",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "git_branch",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"github_branch_pull_requests\".\"owned_by_user_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "github_branch_pull_requests_owned_by_organization_id_organizations_id_fk": {
+ "name": "github_branch_pull_requests_owned_by_organization_id_organizations_id_fk",
+ "tableFrom": "github_branch_pull_requests",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "owned_by_organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "github_branch_pull_requests_owned_by_user_id_kilocode_users_id_fk": {
+ "name": "github_branch_pull_requests_owned_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "github_branch_pull_requests",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "owned_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "github_branch_pull_requests_owner_check": {
+ "name": "github_branch_pull_requests_owner_check",
+ "value": "(\n (\"github_branch_pull_requests\".\"owned_by_organization_id\" IS NOT NULL AND \"github_branch_pull_requests\".\"owned_by_user_id\" IS NULL) OR\n (\"github_branch_pull_requests\".\"owned_by_organization_id\" IS NULL AND \"github_branch_pull_requests\".\"owned_by_user_id\" IS NOT NULL)\n )"
+ },
+ "github_branch_pull_requests_review_decision_check": {
+ "name": "github_branch_pull_requests_review_decision_check",
+ "value": "\"github_branch_pull_requests\".\"pr_review_decision\" IS NULL OR \"github_branch_pull_requests\".\"pr_review_decision\" IN ('approved', 'changes_requested', 'review_required')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.http_ip": {
+ "name": "http_ip",
+ "schema": "",
+ "columns": {
+ "http_ip_id": {
+ "name": "http_ip_id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "http_ip": {
+ "name": "http_ip",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "UQ_http_ip": {
+ "name": "UQ_http_ip",
+ "columns": [
+ {
+ "expression": "http_ip",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.http_user_agent": {
+ "name": "http_user_agent",
+ "schema": "",
+ "columns": {
+ "http_user_agent_id": {
+ "name": "http_user_agent_id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "http_user_agent": {
+ "name": "http_user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "UQ_http_user_agent": {
+ "name": "UQ_http_user_agent",
+ "columns": [
+ {
+ "expression": "http_user_agent",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.impact_advocate_participants": {
+ "name": "impact_advocate_participants",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "program_key": {
+ "name": "program_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'kiloclaw'"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "advocate_id": {
+ "name": "advocate_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "advocate_account_id": {
+ "name": "advocate_account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "opaque_referral_identifier": {
+ "name": "opaque_referral_identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "contact_email": {
+ "name": "contact_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "locale": {
+ "name": "locale",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "country_code": {
+ "name": "country_code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "registration_state": {
+ "name": "registration_state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "registered_at": {
+ "name": "registered_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_registration_attempt_at": {
+ "name": "last_registration_attempt_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error_code": {
+ "name": "last_error_code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error_message": {
+ "name": "last_error_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_impact_advocate_participants_program_referral_identifier": {
+ "name": "UQ_impact_advocate_participants_program_referral_identifier",
+ "columns": [
+ {
+ "expression": "program_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "opaque_referral_identifier",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"impact_advocate_participants\".\"opaque_referral_identifier\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_impact_advocate_participants_registration_state": {
+ "name": "IDX_impact_advocate_participants_registration_state",
+ "columns": [
+ {
+ "expression": "registration_state",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "impact_advocate_participants_user_id_kilocode_users_id_fk": {
+ "name": "impact_advocate_participants_user_id_kilocode_users_id_fk",
+ "tableFrom": "impact_advocate_participants",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_impact_advocate_participants_program_user": {
+ "name": "UQ_impact_advocate_participants_program_user",
+ "nullsNotDistinct": false,
+ "columns": [
+ "program_key",
+ "user_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "impact_advocate_participants_program_key_check": {
+ "name": "impact_advocate_participants_program_key_check",
+ "value": "\"impact_advocate_participants\".\"program_key\" IN ('kiloclaw', 'kilo_pass')"
+ },
+ "impact_advocate_participants_registration_state_check": {
+ "name": "impact_advocate_participants_registration_state_check",
+ "value": "\"impact_advocate_participants\".\"registration_state\" IN ('pending', 'retrying', 'registered', 'failed')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.impact_advocate_registration_attempts": {
+ "name": "impact_advocate_registration_attempts",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "program_key": {
+ "name": "program_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'kiloclaw'"
+ },
+ "participant_id": {
+ "name": "participant_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "dedupe_key": {
+ "name": "dedupe_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "opaque_cookie_value": {
+ "name": "opaque_cookie_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cookie_value_length": {
+ "name": "cookie_value_length",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "delivery_state": {
+ "name": "delivery_state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'queued'"
+ },
+ "request_payload": {
+ "name": "request_payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "response_payload": {
+ "name": "response_payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "response_status_code": {
+ "name": "response_status_code",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "attempt_count": {
+ "name": "attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "next_retry_at": {
+ "name": "next_retry_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "claimed_at": {
+ "name": "claimed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_impact_advocate_registration_attempts_participant_id": {
+ "name": "IDX_impact_advocate_registration_attempts_participant_id",
+ "columns": [
+ {
+ "expression": "participant_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_impact_advocate_registration_attempts_delivery_state": {
+ "name": "IDX_impact_advocate_registration_attempts_delivery_state",
+ "columns": [
+ {
+ "expression": "delivery_state",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "impact_advocate_registration_attempts_participant_id_impact_advocate_participants_id_fk": {
+ "name": "impact_advocate_registration_attempts_participant_id_impact_advocate_participants_id_fk",
+ "tableFrom": "impact_advocate_registration_attempts",
+ "tableTo": "impact_advocate_participants",
+ "columnsFrom": [
+ "participant_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_impact_advocate_registration_attempts_dedupe_key": {
+ "name": "UQ_impact_advocate_registration_attempts_dedupe_key",
+ "nullsNotDistinct": false,
+ "columns": [
+ "dedupe_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "impact_advocate_registration_attempts_program_key_check": {
+ "name": "impact_advocate_registration_attempts_program_key_check",
+ "value": "\"impact_advocate_registration_attempts\".\"program_key\" IN ('kiloclaw', 'kilo_pass')"
+ },
+ "impact_advocate_registration_attempts_delivery_state_check": {
+ "name": "impact_advocate_registration_attempts_delivery_state_check",
+ "value": "\"impact_advocate_registration_attempts\".\"delivery_state\" IN ('queued', 'sending', 'succeeded', 'failed')"
+ },
+ "impact_advocate_registration_attempts_cookie_value_length_non_negative_check": {
+ "name": "impact_advocate_registration_attempts_cookie_value_length_non_negative_check",
+ "value": "\"impact_advocate_registration_attempts\".\"cookie_value_length\" >= 0"
+ },
+ "impact_advocate_registration_attempts_attempt_count_non_negative_check": {
+ "name": "impact_advocate_registration_attempts_attempt_count_non_negative_check",
+ "value": "\"impact_advocate_registration_attempts\".\"attempt_count\" >= 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.impact_advocate_reward_redemptions": {
+ "name": "impact_advocate_reward_redemptions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "reward_id": {
+ "name": "reward_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "dedupe_key": {
+ "name": "dedupe_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "beneficiary_user_id": {
+ "name": "beneficiary_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "state": {
+ "name": "state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'queued'"
+ },
+ "impact_reward_id": {
+ "name": "impact_reward_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "request_payload": {
+ "name": "request_payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lookup_response_payload": {
+ "name": "lookup_response_payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "redeem_response_payload": {
+ "name": "redeem_response_payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "response_status_code": {
+ "name": "response_status_code",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "attempt_count": {
+ "name": "attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "next_retry_at": {
+ "name": "next_retry_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "redeemed_at": {
+ "name": "redeemed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_impact_advocate_reward_redemptions_beneficiary_user_id": {
+ "name": "IDX_impact_advocate_reward_redemptions_beneficiary_user_id",
+ "columns": [
+ {
+ "expression": "beneficiary_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_impact_advocate_reward_redemptions_state": {
+ "name": "IDX_impact_advocate_reward_redemptions_state",
+ "columns": [
+ {
+ "expression": "state",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "impact_advocate_reward_redemptions_reward_id_impact_referral_rewards_id_fk": {
+ "name": "impact_advocate_reward_redemptions_reward_id_impact_referral_rewards_id_fk",
+ "tableFrom": "impact_advocate_reward_redemptions",
+ "tableTo": "impact_referral_rewards",
+ "columnsFrom": [
+ "reward_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ },
+ "impact_advocate_reward_redemptions_beneficiary_user_id_kilocode_users_id_fk": {
+ "name": "impact_advocate_reward_redemptions_beneficiary_user_id_kilocode_users_id_fk",
+ "tableFrom": "impact_advocate_reward_redemptions",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "beneficiary_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_impact_advocate_reward_redemptions_reward_id": {
+ "name": "UQ_impact_advocate_reward_redemptions_reward_id",
+ "nullsNotDistinct": false,
+ "columns": [
+ "reward_id"
+ ]
+ },
+ "UQ_impact_advocate_reward_redemptions_dedupe_key": {
+ "name": "UQ_impact_advocate_reward_redemptions_dedupe_key",
+ "nullsNotDistinct": false,
+ "columns": [
+ "dedupe_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "impact_advocate_reward_redemptions_state_check": {
+ "name": "impact_advocate_reward_redemptions_state_check",
+ "value": "\"impact_advocate_reward_redemptions\".\"state\" IN ('queued', 'retrying', 'redeemed', 'failed')"
+ },
+ "impact_advocate_reward_redemptions_attempt_count_non_negative_check": {
+ "name": "impact_advocate_reward_redemptions_attempt_count_non_negative_check",
+ "value": "\"impact_advocate_reward_redemptions\".\"attempt_count\" >= 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.impact_attribution_touches": {
+ "name": "impact_attribution_touches",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "product": {
+ "name": "product",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'kiloclaw'"
+ },
+ "program_key": {
+ "name": "program_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'kiloclaw'"
+ },
+ "dedupe_key": {
+ "name": "dedupe_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "anonymous_id": {
+ "name": "anonymous_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "touch_type": {
+ "name": "touch_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "opaque_tracking_value": {
+ "name": "opaque_tracking_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "tracking_value_length": {
+ "name": "tracking_value_length",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "is_tracking_value_accepted": {
+ "name": "is_tracking_value_accepted",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "rs_code": {
+ "name": "rs_code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "rs_share_medium": {
+ "name": "rs_share_medium",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "rs_engagement_medium": {
+ "name": "rs_engagement_medium",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "im_ref": {
+ "name": "im_ref",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "landing_path": {
+ "name": "landing_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_source": {
+ "name": "utm_source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_medium": {
+ "name": "utm_medium",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_campaign": {
+ "name": "utm_campaign",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_term": {
+ "name": "utm_term",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "utm_content": {
+ "name": "utm_content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "touched_at": {
+ "name": "touched_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sale_attributed_at": {
+ "name": "sale_attributed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_impact_attribution_touches_product_user_id": {
+ "name": "IDX_impact_attribution_touches_product_user_id",
+ "columns": [
+ {
+ "expression": "product",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_impact_attribution_touches_user_id": {
+ "name": "IDX_impact_attribution_touches_user_id",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_impact_attribution_touches_anonymous_id": {
+ "name": "IDX_impact_attribution_touches_anonymous_id",
+ "columns": [
+ {
+ "expression": "anonymous_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_impact_attribution_touches_expires_at": {
+ "name": "IDX_impact_attribution_touches_expires_at",
+ "columns": [
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_impact_attribution_touches_sale_attributed_at": {
+ "name": "IDX_impact_attribution_touches_sale_attributed_at",
+ "columns": [
+ {
+ "expression": "sale_attributed_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "impact_attribution_touches_user_id_kilocode_users_id_fk": {
+ "name": "impact_attribution_touches_user_id_kilocode_users_id_fk",
+ "tableFrom": "impact_attribution_touches",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_impact_attribution_touches_dedupe_key": {
+ "name": "UQ_impact_attribution_touches_dedupe_key",
+ "nullsNotDistinct": false,
+ "columns": [
+ "dedupe_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "impact_attribution_touches_product_check": {
+ "name": "impact_attribution_touches_product_check",
+ "value": "\"impact_attribution_touches\".\"product\" IN ('kiloclaw', 'kilo_pass')"
+ },
+ "impact_attribution_touches_program_key_check": {
+ "name": "impact_attribution_touches_program_key_check",
+ "value": "\"impact_attribution_touches\".\"program_key\" IN ('kiloclaw', 'kilo_pass')"
+ },
+ "impact_attribution_touches_touch_type_check": {
+ "name": "impact_attribution_touches_touch_type_check",
+ "value": "\"impact_attribution_touches\".\"touch_type\" IN ('affiliate', 'referral')"
+ },
+ "impact_attribution_touches_provider_check": {
+ "name": "impact_attribution_touches_provider_check",
+ "value": "\"impact_attribution_touches\".\"provider\" IN ('impact_performance', 'impact_advocate')"
+ },
+ "impact_attribution_touches_tracking_value_length_non_negative_check": {
+ "name": "impact_attribution_touches_tracking_value_length_non_negative_check",
+ "value": "\"impact_attribution_touches\".\"tracking_value_length\" >= 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.impact_conversion_reports": {
+ "name": "impact_conversion_reports",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "conversion_id": {
+ "name": "conversion_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "dedupe_key": {
+ "name": "dedupe_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action_tracker_id": {
+ "name": "action_tracker_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "order_id": {
+ "name": "order_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "state": {
+ "name": "state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'queued'"
+ },
+ "request_payload": {
+ "name": "request_payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "response_payload": {
+ "name": "response_payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "response_status_code": {
+ "name": "response_status_code",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "attempt_count": {
+ "name": "attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "next_retry_at": {
+ "name": "next_retry_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "delivered_at": {
+ "name": "delivered_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_impact_conversion_reports_conversion_id": {
+ "name": "IDX_impact_conversion_reports_conversion_id",
+ "columns": [
+ {
+ "expression": "conversion_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_impact_conversion_reports_state": {
+ "name": "IDX_impact_conversion_reports_state",
+ "columns": [
+ {
+ "expression": "state",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "impact_conversion_reports_conversion_id_impact_referral_conversions_id_fk": {
+ "name": "impact_conversion_reports_conversion_id_impact_referral_conversions_id_fk",
+ "tableFrom": "impact_conversion_reports",
+ "tableTo": "impact_referral_conversions",
+ "columnsFrom": [
+ "conversion_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_impact_conversion_reports_dedupe_key": {
+ "name": "UQ_impact_conversion_reports_dedupe_key",
+ "nullsNotDistinct": false,
+ "columns": [
+ "dedupe_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "impact_conversion_reports_state_check": {
+ "name": "impact_conversion_reports_state_check",
+ "value": "\"impact_conversion_reports\".\"state\" IN ('queued', 'retrying', 'delivered', 'failed')"
+ },
+ "impact_conversion_reports_attempt_count_non_negative_check": {
+ "name": "impact_conversion_reports_attempt_count_non_negative_check",
+ "value": "\"impact_conversion_reports\".\"attempt_count\" >= 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.impact_referral_conversions": {
+ "name": "impact_referral_conversions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "product": {
+ "name": "product",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'kiloclaw'"
+ },
+ "referee_user_id": {
+ "name": "referee_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "referrer_user_id": {
+ "name": "referrer_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_touch_id": {
+ "name": "source_touch_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "winning_touch_type": {
+ "name": "winning_touch_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "payment_provider": {
+ "name": "payment_provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'credits'"
+ },
+ "source_payment_id": {
+ "name": "source_payment_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "qualified": {
+ "name": "qualified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "disqualification_reason": {
+ "name": "disqualification_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "converted_at": {
+ "name": "converted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_impact_referral_conversions_referee_user_id": {
+ "name": "IDX_impact_referral_conversions_referee_user_id",
+ "columns": [
+ {
+ "expression": "referee_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_impact_referral_conversions_referrer_user_id": {
+ "name": "IDX_impact_referral_conversions_referrer_user_id",
+ "columns": [
+ {
+ "expression": "referrer_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "impact_referral_conversions_referee_user_id_kilocode_users_id_fk": {
+ "name": "impact_referral_conversions_referee_user_id_kilocode_users_id_fk",
+ "tableFrom": "impact_referral_conversions",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "referee_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ },
+ "impact_referral_conversions_referrer_user_id_kilocode_users_id_fk": {
+ "name": "impact_referral_conversions_referrer_user_id_kilocode_users_id_fk",
+ "tableFrom": "impact_referral_conversions",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "referrer_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "cascade"
+ },
+ "impact_referral_conversions_source_touch_id_impact_attribution_touches_id_fk": {
+ "name": "impact_referral_conversions_source_touch_id_impact_attribution_touches_id_fk",
+ "tableFrom": "impact_referral_conversions",
+ "tableTo": "impact_attribution_touches",
+ "columnsFrom": [
+ "source_touch_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_impact_referral_conversions_product_payment_source": {
+ "name": "UQ_impact_referral_conversions_product_payment_source",
+ "nullsNotDistinct": false,
+ "columns": [
+ "product",
+ "payment_provider",
+ "source_payment_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "impact_referral_conversions_product_check": {
+ "name": "impact_referral_conversions_product_check",
+ "value": "\"impact_referral_conversions\".\"product\" IN ('kiloclaw', 'kilo_pass')"
+ },
+ "impact_referral_conversions_winning_touch_type_check": {
+ "name": "impact_referral_conversions_winning_touch_type_check",
+ "value": "\"impact_referral_conversions\".\"winning_touch_type\" IN ('referral', 'affiliate', 'none')"
+ },
+ "impact_referral_conversions_payment_provider_check": {
+ "name": "impact_referral_conversions_payment_provider_check",
+ "value": "\"impact_referral_conversions\".\"payment_provider\" IN ('stripe', 'credits', 'app_store', 'google_play')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.impact_referral_reward_applications": {
+ "name": "impact_referral_reward_applications",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "product": {
+ "name": "product",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'kiloclaw'"
+ },
+ "reward_id": {
+ "name": "reward_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "beneficiary_user_id": {
+ "name": "beneficiary_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "subscription_id": {
+ "name": "subscription_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "previous_renewal_boundary": {
+ "name": "previous_renewal_boundary",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "new_renewal_boundary": {
+ "name": "new_renewal_boundary",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "local_operation_id": {
+ "name": "local_operation_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_operation_id": {
+ "name": "stripe_operation_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_idempotency_key": {
+ "name": "stripe_idempotency_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "applied_at": {
+ "name": "applied_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_impact_referral_reward_applications_reward_id": {
+ "name": "IDX_impact_referral_reward_applications_reward_id",
+ "columns": [
+ {
+ "expression": "reward_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_impact_referral_reward_applications_beneficiary_user_id": {
+ "name": "IDX_impact_referral_reward_applications_beneficiary_user_id",
+ "columns": [
+ {
+ "expression": "beneficiary_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "impact_referral_reward_applications_reward_id_impact_referral_rewards_id_fk": {
+ "name": "impact_referral_reward_applications_reward_id_impact_referral_rewards_id_fk",
+ "tableFrom": "impact_referral_reward_applications",
+ "tableTo": "impact_referral_rewards",
+ "columnsFrom": [
+ "reward_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ },
+ "impact_referral_reward_applications_beneficiary_user_id_kilocode_users_id_fk": {
+ "name": "impact_referral_reward_applications_beneficiary_user_id_kilocode_users_id_fk",
+ "tableFrom": "impact_referral_reward_applications",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "beneficiary_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "impact_referral_reward_applications_product_check": {
+ "name": "impact_referral_reward_applications_product_check",
+ "value": "\"impact_referral_reward_applications\".\"product\" IN ('kiloclaw', 'kilo_pass')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.impact_referral_reward_decisions": {
+ "name": "impact_referral_reward_decisions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "product": {
+ "name": "product",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'kiloclaw'"
+ },
+ "conversion_id": {
+ "name": "conversion_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "beneficiary_user_id": {
+ "name": "beneficiary_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "beneficiary_role": {
+ "name": "beneficiary_role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "outcome": {
+ "name": "outcome",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reason": {
+ "name": "reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "reward_kind": {
+ "name": "reward_kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'kiloclaw_free_month'"
+ },
+ "months_granted": {
+ "name": "months_granted",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "reward_percent": {
+ "name": "reward_percent",
+ "type": "numeric(6, 4)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_tier": {
+ "name": "source_tier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "reward_amount_usd": {
+ "name": "reward_amount_usd",
+ "type": "numeric(12, 2)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_impact_referral_reward_decisions_beneficiary_user_id": {
+ "name": "IDX_impact_referral_reward_decisions_beneficiary_user_id",
+ "columns": [
+ {
+ "expression": "beneficiary_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "impact_referral_reward_decisions_conversion_id_impact_referral_conversions_id_fk": {
+ "name": "impact_referral_reward_decisions_conversion_id_impact_referral_conversions_id_fk",
+ "tableFrom": "impact_referral_reward_decisions",
+ "tableTo": "impact_referral_conversions",
+ "columnsFrom": [
+ "conversion_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ },
+ "impact_referral_reward_decisions_beneficiary_user_id_kilocode_users_id_fk": {
+ "name": "impact_referral_reward_decisions_beneficiary_user_id_kilocode_users_id_fk",
+ "tableFrom": "impact_referral_reward_decisions",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "beneficiary_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_impact_referral_reward_decisions_conversion_role": {
+ "name": "UQ_impact_referral_reward_decisions_conversion_role",
+ "nullsNotDistinct": false,
+ "columns": [
+ "conversion_id",
+ "beneficiary_role"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "impact_referral_reward_decisions_product_check": {
+ "name": "impact_referral_reward_decisions_product_check",
+ "value": "\"impact_referral_reward_decisions\".\"product\" IN ('kiloclaw', 'kilo_pass')"
+ },
+ "impact_referral_reward_decisions_beneficiary_role_check": {
+ "name": "impact_referral_reward_decisions_beneficiary_role_check",
+ "value": "\"impact_referral_reward_decisions\".\"beneficiary_role\" IN ('referrer', 'referee')"
+ },
+ "impact_referral_reward_decisions_outcome_check": {
+ "name": "impact_referral_reward_decisions_outcome_check",
+ "value": "\"impact_referral_reward_decisions\".\"outcome\" IN ('granted', 'cap_limited', 'disqualified')"
+ },
+ "impact_referral_reward_decisions_reward_kind_check": {
+ "name": "impact_referral_reward_decisions_reward_kind_check",
+ "value": "\"impact_referral_reward_decisions\".\"reward_kind\" IN ('kiloclaw_free_month', 'kilo_pass_bonus')"
+ },
+ "impact_referral_reward_decisions_months_granted_non_negative_check": {
+ "name": "impact_referral_reward_decisions_months_granted_non_negative_check",
+ "value": "\"impact_referral_reward_decisions\".\"months_granted\" >= 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.impact_referral_rewards": {
+ "name": "impact_referral_rewards",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "product": {
+ "name": "product",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'kiloclaw'"
+ },
+ "conversion_id": {
+ "name": "conversion_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "decision_id": {
+ "name": "decision_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "beneficiary_user_id": {
+ "name": "beneficiary_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "beneficiary_role": {
+ "name": "beneficiary_role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reward_kind": {
+ "name": "reward_kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'kiloclaw_free_month'"
+ },
+ "months_granted": {
+ "name": "months_granted",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "reward_percent": {
+ "name": "reward_percent",
+ "type": "numeric(6, 4)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_tier": {
+ "name": "source_tier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "reward_amount_usd": {
+ "name": "reward_amount_usd",
+ "type": "numeric(12, 2)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "applies_to_subscription_id": {
+ "name": "applies_to_subscription_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "applies_to_kilo_pass_subscription_id": {
+ "name": "applies_to_kilo_pass_subscription_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "consumed_kilo_pass_issuance_id": {
+ "name": "consumed_kilo_pass_issuance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "consumed_kilo_pass_issuance_item_id": {
+ "name": "consumed_kilo_pass_issuance_item_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "earned_at": {
+ "name": "earned_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "applied_at": {
+ "name": "applied_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "reversed_at": {
+ "name": "reversed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "review_reason": {
+ "name": "review_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_impact_referral_rewards_beneficiary_user_id": {
+ "name": "IDX_impact_referral_rewards_beneficiary_user_id",
+ "columns": [
+ {
+ "expression": "beneficiary_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_impact_referral_rewards_status": {
+ "name": "IDX_impact_referral_rewards_status",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "impact_referral_rewards_conversion_id_impact_referral_conversions_id_fk": {
+ "name": "impact_referral_rewards_conversion_id_impact_referral_conversions_id_fk",
+ "tableFrom": "impact_referral_rewards",
+ "tableTo": "impact_referral_conversions",
+ "columnsFrom": [
+ "conversion_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ },
+ "impact_referral_rewards_decision_id_impact_referral_reward_decisions_id_fk": {
+ "name": "impact_referral_rewards_decision_id_impact_referral_reward_decisions_id_fk",
+ "tableFrom": "impact_referral_rewards",
+ "tableTo": "impact_referral_reward_decisions",
+ "columnsFrom": [
+ "decision_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ },
+ "impact_referral_rewards_beneficiary_user_id_kilocode_users_id_fk": {
+ "name": "impact_referral_rewards_beneficiary_user_id_kilocode_users_id_fk",
+ "tableFrom": "impact_referral_rewards",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "beneficiary_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ },
+ "FK_impact_referral_rewards_kilo_pass_subscription": {
+ "name": "FK_impact_referral_rewards_kilo_pass_subscription",
+ "tableFrom": "impact_referral_rewards",
+ "tableTo": "kilo_pass_subscriptions",
+ "columnsFrom": [
+ "applies_to_kilo_pass_subscription_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "cascade"
+ },
+ "FK_impact_referral_rewards_kilo_pass_issuance": {
+ "name": "FK_impact_referral_rewards_kilo_pass_issuance",
+ "tableFrom": "impact_referral_rewards",
+ "tableTo": "kilo_pass_issuances",
+ "columnsFrom": [
+ "consumed_kilo_pass_issuance_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "cascade"
+ },
+ "FK_impact_referral_rewards_kilo_pass_issuance_item": {
+ "name": "FK_impact_referral_rewards_kilo_pass_issuance_item",
+ "tableFrom": "impact_referral_rewards",
+ "tableTo": "kilo_pass_issuance_items",
+ "columnsFrom": [
+ "consumed_kilo_pass_issuance_item_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_impact_referral_rewards_conversion_role": {
+ "name": "UQ_impact_referral_rewards_conversion_role",
+ "nullsNotDistinct": false,
+ "columns": [
+ "conversion_id",
+ "beneficiary_role"
+ ]
+ },
+ "UQ_impact_referral_rewards_decision_id": {
+ "name": "UQ_impact_referral_rewards_decision_id",
+ "nullsNotDistinct": false,
+ "columns": [
+ "decision_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "impact_referral_rewards_product_check": {
+ "name": "impact_referral_rewards_product_check",
+ "value": "\"impact_referral_rewards\".\"product\" IN ('kiloclaw', 'kilo_pass')"
+ },
+ "impact_referral_rewards_beneficiary_role_check": {
+ "name": "impact_referral_rewards_beneficiary_role_check",
+ "value": "\"impact_referral_rewards\".\"beneficiary_role\" IN ('referrer', 'referee')"
+ },
+ "impact_referral_rewards_reward_kind_check": {
+ "name": "impact_referral_rewards_reward_kind_check",
+ "value": "\"impact_referral_rewards\".\"reward_kind\" IN ('kiloclaw_free_month', 'kilo_pass_bonus')"
+ },
+ "impact_referral_rewards_status_check": {
+ "name": "impact_referral_rewards_status_check",
+ "value": "\"impact_referral_rewards\".\"status\" IN ('pending', 'earned', 'applied', 'reversed', 'expired', 'canceled', 'review_required')"
+ },
+ "impact_referral_rewards_months_granted_non_negative_check": {
+ "name": "impact_referral_rewards_months_granted_non_negative_check",
+ "value": "\"impact_referral_rewards\".\"months_granted\" >= 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.impact_referrals": {
+ "name": "impact_referrals",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "product": {
+ "name": "product",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'kiloclaw'"
+ },
+ "referee_user_id": {
+ "name": "referee_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "referrer_user_id": {
+ "name": "referrer_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_touch_id": {
+ "name": "source_touch_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "impact_referral_id": {
+ "name": "impact_referral_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_impact_referrals_referrer_user_id": {
+ "name": "IDX_impact_referrals_referrer_user_id",
+ "columns": [
+ {
+ "expression": "referrer_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_impact_referrals_source_touch_id": {
+ "name": "IDX_impact_referrals_source_touch_id",
+ "columns": [
+ {
+ "expression": "source_touch_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "impact_referrals_referee_user_id_kilocode_users_id_fk": {
+ "name": "impact_referrals_referee_user_id_kilocode_users_id_fk",
+ "tableFrom": "impact_referrals",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "referee_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ },
+ "impact_referrals_referrer_user_id_kilocode_users_id_fk": {
+ "name": "impact_referrals_referrer_user_id_kilocode_users_id_fk",
+ "tableFrom": "impact_referrals",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "referrer_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "cascade"
+ },
+ "impact_referrals_source_touch_id_impact_attribution_touches_id_fk": {
+ "name": "impact_referrals_source_touch_id_impact_attribution_touches_id_fk",
+ "tableFrom": "impact_referrals",
+ "tableTo": "impact_attribution_touches",
+ "columnsFrom": [
+ "source_touch_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_impact_referrals_product_referee_user_id": {
+ "name": "UQ_impact_referrals_product_referee_user_id",
+ "nullsNotDistinct": false,
+ "columns": [
+ "product",
+ "referee_user_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "impact_referrals_product_check": {
+ "name": "impact_referrals_product_check",
+ "value": "\"impact_referrals\".\"product\" IN ('kiloclaw', 'kilo_pass')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.ja4_digest": {
+ "name": "ja4_digest",
+ "schema": "",
+ "columns": {
+ "ja4_digest_id": {
+ "name": "ja4_digest_id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "ja4_digest": {
+ "name": "ja4_digest",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "UQ_ja4_digest": {
+ "name": "UQ_ja4_digest",
+ "columns": [
+ {
+ "expression": "ja4_digest",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.kilo_pass_audit_log": {
+ "name": "kilo_pass_audit_log",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "kilo_pass_subscription_id": {
+ "name": "kilo_pass_subscription_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "action": {
+ "name": "action",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "result": {
+ "name": "result",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "idempotency_key": {
+ "name": "idempotency_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_event_id": {
+ "name": "stripe_event_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_invoice_id": {
+ "name": "stripe_invoice_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_subscription_id": {
+ "name": "stripe_subscription_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "related_credit_transaction_id": {
+ "name": "related_credit_transaction_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "related_monthly_issuance_id": {
+ "name": "related_monthly_issuance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "payload_json": {
+ "name": "payload_json",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ }
+ },
+ "indexes": {
+ "IDX_kilo_pass_audit_log_created_at": {
+ "name": "IDX_kilo_pass_audit_log_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_audit_log_kilo_user_id": {
+ "name": "IDX_kilo_pass_audit_log_kilo_user_id",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_audit_log_kilo_pass_subscription_id": {
+ "name": "IDX_kilo_pass_audit_log_kilo_pass_subscription_id",
+ "columns": [
+ {
+ "expression": "kilo_pass_subscription_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_audit_log_action": {
+ "name": "IDX_kilo_pass_audit_log_action",
+ "columns": [
+ {
+ "expression": "action",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_audit_log_result": {
+ "name": "IDX_kilo_pass_audit_log_result",
+ "columns": [
+ {
+ "expression": "result",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_audit_log_idempotency_key": {
+ "name": "IDX_kilo_pass_audit_log_idempotency_key",
+ "columns": [
+ {
+ "expression": "idempotency_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_audit_log_stripe_event_id": {
+ "name": "IDX_kilo_pass_audit_log_stripe_event_id",
+ "columns": [
+ {
+ "expression": "stripe_event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_audit_log_stripe_invoice_id": {
+ "name": "IDX_kilo_pass_audit_log_stripe_invoice_id",
+ "columns": [
+ {
+ "expression": "stripe_invoice_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_audit_log_stripe_subscription_id": {
+ "name": "IDX_kilo_pass_audit_log_stripe_subscription_id",
+ "columns": [
+ {
+ "expression": "stripe_subscription_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_audit_log_related_credit_transaction_id": {
+ "name": "IDX_kilo_pass_audit_log_related_credit_transaction_id",
+ "columns": [
+ {
+ "expression": "related_credit_transaction_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_audit_log_related_monthly_issuance_id": {
+ "name": "IDX_kilo_pass_audit_log_related_monthly_issuance_id",
+ "columns": [
+ {
+ "expression": "related_monthly_issuance_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "kilo_pass_audit_log_kilo_user_id_kilocode_users_id_fk": {
+ "name": "kilo_pass_audit_log_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "kilo_pass_audit_log",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "cascade"
+ },
+ "kilo_pass_audit_log_kilo_pass_subscription_id_kilo_pass_subscriptions_id_fk": {
+ "name": "kilo_pass_audit_log_kilo_pass_subscription_id_kilo_pass_subscriptions_id_fk",
+ "tableFrom": "kilo_pass_audit_log",
+ "tableTo": "kilo_pass_subscriptions",
+ "columnsFrom": [
+ "kilo_pass_subscription_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "cascade"
+ },
+ "kilo_pass_audit_log_related_credit_transaction_id_credit_transactions_id_fk": {
+ "name": "kilo_pass_audit_log_related_credit_transaction_id_credit_transactions_id_fk",
+ "tableFrom": "kilo_pass_audit_log",
+ "tableTo": "credit_transactions",
+ "columnsFrom": [
+ "related_credit_transaction_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "cascade"
+ },
+ "kilo_pass_audit_log_related_monthly_issuance_id_kilo_pass_issuances_id_fk": {
+ "name": "kilo_pass_audit_log_related_monthly_issuance_id_kilo_pass_issuances_id_fk",
+ "tableFrom": "kilo_pass_audit_log",
+ "tableTo": "kilo_pass_issuances",
+ "columnsFrom": [
+ "related_monthly_issuance_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "kilo_pass_audit_log_action_check": {
+ "name": "kilo_pass_audit_log_action_check",
+ "value": "\"kilo_pass_audit_log\".\"action\" IN ('stripe_webhook_received', 'kilo_pass_invoice_paid_handled', 'store_purchase_completed', 'store_notification_received', 'store_subscription_renewed', 'store_subscription_canceled', 'store_subscription_expired', 'store_subscription_refunded', 'base_credits_issued', 'bonus_credits_issued', 'bonus_credits_skipped_idempotent', 'first_month_50pct_promo_issued', 'yearly_monthly_base_cron_started', 'yearly_monthly_base_cron_completed', 'issue_yearly_remaining_credits', 'duplicate_card_subscription_canceled', 'yearly_monthly_bonus_cron_started', 'yearly_monthly_bonus_cron_completed')"
+ },
+ "kilo_pass_audit_log_result_check": {
+ "name": "kilo_pass_audit_log_result_check",
+ "value": "\"kilo_pass_audit_log\".\"result\" IN ('success', 'skipped_idempotent', 'failed')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.kilo_pass_issuance_items": {
+ "name": "kilo_pass_issuance_items",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "kilo_pass_issuance_id": {
+ "name": "kilo_pass_issuance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "credit_transaction_id": {
+ "name": "credit_transaction_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "amount_usd": {
+ "name": "amount_usd",
+ "type": "numeric(12, 2)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "bonus_percent_applied": {
+ "name": "bonus_percent_applied",
+ "type": "numeric(6, 4)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_kilo_pass_issuance_items_issuance_id": {
+ "name": "IDX_kilo_pass_issuance_items_issuance_id",
+ "columns": [
+ {
+ "expression": "kilo_pass_issuance_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_issuance_items_credit_transaction_id": {
+ "name": "IDX_kilo_pass_issuance_items_credit_transaction_id",
+ "columns": [
+ {
+ "expression": "credit_transaction_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "kilo_pass_issuance_items_kilo_pass_issuance_id_kilo_pass_issuances_id_fk": {
+ "name": "kilo_pass_issuance_items_kilo_pass_issuance_id_kilo_pass_issuances_id_fk",
+ "tableFrom": "kilo_pass_issuance_items",
+ "tableTo": "kilo_pass_issuances",
+ "columnsFrom": [
+ "kilo_pass_issuance_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ },
+ "kilo_pass_issuance_items_credit_transaction_id_credit_transactions_id_fk": {
+ "name": "kilo_pass_issuance_items_credit_transaction_id_credit_transactions_id_fk",
+ "tableFrom": "kilo_pass_issuance_items",
+ "tableTo": "credit_transactions",
+ "columnsFrom": [
+ "credit_transaction_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "restrict",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "kilo_pass_issuance_items_credit_transaction_id_unique": {
+ "name": "kilo_pass_issuance_items_credit_transaction_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "credit_transaction_id"
+ ]
+ },
+ "UQ_kilo_pass_issuance_items_issuance_kind": {
+ "name": "UQ_kilo_pass_issuance_items_issuance_kind",
+ "nullsNotDistinct": false,
+ "columns": [
+ "kilo_pass_issuance_id",
+ "kind"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "kilo_pass_issuance_items_bonus_percent_applied_range_check": {
+ "name": "kilo_pass_issuance_items_bonus_percent_applied_range_check",
+ "value": "\"kilo_pass_issuance_items\".\"bonus_percent_applied\" IS NULL OR (\"kilo_pass_issuance_items\".\"bonus_percent_applied\" >= 0 AND \"kilo_pass_issuance_items\".\"bonus_percent_applied\" <= 1)"
+ },
+ "kilo_pass_issuance_items_amount_usd_non_negative_check": {
+ "name": "kilo_pass_issuance_items_amount_usd_non_negative_check",
+ "value": "\"kilo_pass_issuance_items\".\"amount_usd\" >= 0"
+ },
+ "kilo_pass_issuance_items_kind_check": {
+ "name": "kilo_pass_issuance_items_kind_check",
+ "value": "\"kilo_pass_issuance_items\".\"kind\" IN ('base', 'bonus', 'promo_first_month_50pct', 'referral_bonus')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.kilo_pass_issuances": {
+ "name": "kilo_pass_issuances",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "kilo_pass_subscription_id": {
+ "name": "kilo_pass_subscription_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "issue_month": {
+ "name": "issue_month",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source": {
+ "name": "source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "stripe_invoice_id": {
+ "name": "stripe_invoice_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "initial_welcome_promo_eligibility_reason": {
+ "name": "initial_welcome_promo_eligibility_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_kilo_pass_issuances_stripe_invoice_id": {
+ "name": "UQ_kilo_pass_issuances_stripe_invoice_id",
+ "columns": [
+ {
+ "expression": "stripe_invoice_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"kilo_pass_issuances\".\"stripe_invoice_id\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_issuances_subscription_id": {
+ "name": "IDX_kilo_pass_issuances_subscription_id",
+ "columns": [
+ {
+ "expression": "kilo_pass_subscription_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_issuances_issue_month": {
+ "name": "IDX_kilo_pass_issuances_issue_month",
+ "columns": [
+ {
+ "expression": "issue_month",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "kilo_pass_issuances_kilo_pass_subscription_id_kilo_pass_subscriptions_id_fk": {
+ "name": "kilo_pass_issuances_kilo_pass_subscription_id_kilo_pass_subscriptions_id_fk",
+ "tableFrom": "kilo_pass_issuances",
+ "tableTo": "kilo_pass_subscriptions",
+ "columnsFrom": [
+ "kilo_pass_subscription_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_kilo_pass_issuances_subscription_issue_month": {
+ "name": "UQ_kilo_pass_issuances_subscription_issue_month",
+ "nullsNotDistinct": false,
+ "columns": [
+ "kilo_pass_subscription_id",
+ "issue_month"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "kilo_pass_issuances_issue_month_day_one_check": {
+ "name": "kilo_pass_issuances_issue_month_day_one_check",
+ "value": "EXTRACT(DAY FROM \"kilo_pass_issuances\".\"issue_month\") = 1"
+ },
+ "kilo_pass_issuances_source_check": {
+ "name": "kilo_pass_issuances_source_check",
+ "value": "\"kilo_pass_issuances\".\"source\" IN ('stripe_invoice', 'app_store_transaction', 'google_play_transaction', 'cron')"
+ },
+ "kilo_pass_issuances_initial_welcome_promo_reason_check": {
+ "name": "kilo_pass_issuances_initial_welcome_promo_reason_check",
+ "value": "\"kilo_pass_issuances\".\"initial_welcome_promo_eligibility_reason\" IN ('first_payment_fingerprint_claim', 'fingerprint_previously_claimed', 'missing_fingerprint', 'no_supported_fingerprint', 'no_positive_settlement', 'settlement_unresolved')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.kilo_pass_pause_events": {
+ "name": "kilo_pass_pause_events",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "kilo_pass_subscription_id": {
+ "name": "kilo_pass_subscription_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "paused_at": {
+ "name": "paused_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "resumes_at": {
+ "name": "resumes_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resumed_at": {
+ "name": "resumed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_kilo_pass_pause_events_subscription_id": {
+ "name": "IDX_kilo_pass_pause_events_subscription_id",
+ "columns": [
+ {
+ "expression": "kilo_pass_subscription_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_kilo_pass_pause_events_one_open_per_sub": {
+ "name": "UQ_kilo_pass_pause_events_one_open_per_sub",
+ "columns": [
+ {
+ "expression": "kilo_pass_subscription_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"kilo_pass_pause_events\".\"resumed_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "kilo_pass_pause_events_kilo_pass_subscription_id_kilo_pass_subscriptions_id_fk": {
+ "name": "kilo_pass_pause_events_kilo_pass_subscription_id_kilo_pass_subscriptions_id_fk",
+ "tableFrom": "kilo_pass_pause_events",
+ "tableTo": "kilo_pass_subscriptions",
+ "columnsFrom": [
+ "kilo_pass_subscription_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "kilo_pass_pause_events_resumed_at_after_paused_at_check": {
+ "name": "kilo_pass_pause_events_resumed_at_after_paused_at_check",
+ "value": "\"kilo_pass_pause_events\".\"resumed_at\" IS NULL OR \"kilo_pass_pause_events\".\"resumed_at\" >= \"kilo_pass_pause_events\".\"paused_at\""
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.kilo_pass_scheduled_changes": {
+ "name": "kilo_pass_scheduled_changes",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "stripe_subscription_id": {
+ "name": "stripe_subscription_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "from_tier": {
+ "name": "from_tier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "from_cadence": {
+ "name": "from_cadence",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "to_tier": {
+ "name": "to_tier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "to_cadence": {
+ "name": "to_cadence",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "stripe_schedule_id": {
+ "name": "stripe_schedule_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "effective_at": {
+ "name": "effective_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_kilo_pass_scheduled_changes_kilo_user_id": {
+ "name": "IDX_kilo_pass_scheduled_changes_kilo_user_id",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_scheduled_changes_status": {
+ "name": "IDX_kilo_pass_scheduled_changes_status",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_scheduled_changes_stripe_subscription_id": {
+ "name": "IDX_kilo_pass_scheduled_changes_stripe_subscription_id",
+ "columns": [
+ {
+ "expression": "stripe_subscription_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_kilo_pass_scheduled_changes_active_stripe_subscription_id": {
+ "name": "UQ_kilo_pass_scheduled_changes_active_stripe_subscription_id",
+ "columns": [
+ {
+ "expression": "stripe_subscription_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"kilo_pass_scheduled_changes\".\"deleted_at\" is null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_scheduled_changes_effective_at": {
+ "name": "IDX_kilo_pass_scheduled_changes_effective_at",
+ "columns": [
+ {
+ "expression": "effective_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_scheduled_changes_deleted_at": {
+ "name": "IDX_kilo_pass_scheduled_changes_deleted_at",
+ "columns": [
+ {
+ "expression": "deleted_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "kilo_pass_scheduled_changes_kilo_user_id_kilocode_users_id_fk": {
+ "name": "kilo_pass_scheduled_changes_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "kilo_pass_scheduled_changes",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ },
+ "kilo_pass_scheduled_changes_stripe_subscription_id_kilo_pass_subscriptions_stripe_subscription_id_fk": {
+ "name": "kilo_pass_scheduled_changes_stripe_subscription_id_kilo_pass_subscriptions_stripe_subscription_id_fk",
+ "tableFrom": "kilo_pass_scheduled_changes",
+ "tableTo": "kilo_pass_subscriptions",
+ "columnsFrom": [
+ "stripe_subscription_id"
+ ],
+ "columnsTo": [
+ "stripe_subscription_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "kilo_pass_scheduled_changes_from_tier_check": {
+ "name": "kilo_pass_scheduled_changes_from_tier_check",
+ "value": "\"kilo_pass_scheduled_changes\".\"from_tier\" IN ('tier_19', 'tier_49', 'tier_199')"
+ },
+ "kilo_pass_scheduled_changes_from_cadence_check": {
+ "name": "kilo_pass_scheduled_changes_from_cadence_check",
+ "value": "\"kilo_pass_scheduled_changes\".\"from_cadence\" IN ('monthly', 'yearly')"
+ },
+ "kilo_pass_scheduled_changes_to_tier_check": {
+ "name": "kilo_pass_scheduled_changes_to_tier_check",
+ "value": "\"kilo_pass_scheduled_changes\".\"to_tier\" IN ('tier_19', 'tier_49', 'tier_199')"
+ },
+ "kilo_pass_scheduled_changes_to_cadence_check": {
+ "name": "kilo_pass_scheduled_changes_to_cadence_check",
+ "value": "\"kilo_pass_scheduled_changes\".\"to_cadence\" IN ('monthly', 'yearly')"
+ },
+ "kilo_pass_scheduled_changes_status_check": {
+ "name": "kilo_pass_scheduled_changes_status_check",
+ "value": "\"kilo_pass_scheduled_changes\".\"status\" IN ('not_started', 'active', 'completed', 'released', 'canceled')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.kilo_pass_store_events": {
+ "name": "kilo_pass_store_events",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "payment_provider": {
+ "name": "payment_provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_id": {
+ "name": "event_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_subscription_id": {
+ "name": "provider_subscription_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider_transaction_id": {
+ "name": "provider_transaction_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "app_account_token": {
+ "name": "app_account_token",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "product_id": {
+ "name": "product_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "environment": {
+ "name": "environment",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "payload_json": {
+ "name": "payload_json",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "processing_started_at": {
+ "name": "processing_started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "processed_at": {
+ "name": "processed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_kilo_pass_store_events_provider_event": {
+ "name": "UQ_kilo_pass_store_events_provider_event",
+ "columns": [
+ {
+ "expression": "payment_provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_store_events_provider_subscription": {
+ "name": "IDX_kilo_pass_store_events_provider_subscription",
+ "columns": [
+ {
+ "expression": "payment_provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "provider_subscription_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_store_events_app_account_token": {
+ "name": "IDX_kilo_pass_store_events_app_account_token",
+ "columns": [
+ {
+ "expression": "app_account_token",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "kilo_pass_store_events_payment_provider_check": {
+ "name": "kilo_pass_store_events_payment_provider_check",
+ "value": "\"kilo_pass_store_events\".\"payment_provider\" IN ('stripe', 'app_store', 'google_play')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.kilo_pass_store_purchases": {
+ "name": "kilo_pass_store_purchases",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "kilo_pass_subscription_id": {
+ "name": "kilo_pass_subscription_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "payment_provider": {
+ "name": "payment_provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "product_id": {
+ "name": "product_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_subscription_id": {
+ "name": "provider_subscription_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_transaction_id": {
+ "name": "provider_transaction_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_original_transaction_id": {
+ "name": "provider_original_transaction_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "app_account_token": {
+ "name": "app_account_token",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "purchase_token": {
+ "name": "purchase_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "environment": {
+ "name": "environment",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "purchased_at": {
+ "name": "purchased_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "raw_payload_json": {
+ "name": "raw_payload_json",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_kilo_pass_store_purchases_provider_transaction": {
+ "name": "UQ_kilo_pass_store_purchases_provider_transaction",
+ "columns": [
+ {
+ "expression": "payment_provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "provider_transaction_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_store_purchases_subscription_id": {
+ "name": "IDX_kilo_pass_store_purchases_subscription_id",
+ "columns": [
+ {
+ "expression": "kilo_pass_subscription_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_store_purchases_user_id": {
+ "name": "IDX_kilo_pass_store_purchases_user_id",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_store_purchases_app_account_token": {
+ "name": "IDX_kilo_pass_store_purchases_app_account_token",
+ "columns": [
+ {
+ "expression": "app_account_token",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_store_purchases_latest_subscription_purchase": {
+ "name": "IDX_kilo_pass_store_purchases_latest_subscription_purchase",
+ "columns": [
+ {
+ "expression": "payment_provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "provider_subscription_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "purchased_at",
+ "isExpression": false,
+ "asc": false,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "kilo_pass_store_purchases_kilo_pass_subscription_id_kilo_pass_subscriptions_id_fk": {
+ "name": "kilo_pass_store_purchases_kilo_pass_subscription_id_kilo_pass_subscriptions_id_fk",
+ "tableFrom": "kilo_pass_store_purchases",
+ "tableTo": "kilo_pass_subscriptions",
+ "columnsFrom": [
+ "kilo_pass_subscription_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ },
+ "kilo_pass_store_purchases_kilo_user_id_kilocode_users_id_fk": {
+ "name": "kilo_pass_store_purchases_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "kilo_pass_store_purchases",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ },
+ "FK_kilo_pass_store_purchases_subscription_owner_provider": {
+ "name": "FK_kilo_pass_store_purchases_subscription_owner_provider",
+ "tableFrom": "kilo_pass_store_purchases",
+ "tableTo": "kilo_pass_subscriptions",
+ "columnsFrom": [
+ "kilo_pass_subscription_id",
+ "kilo_user_id",
+ "payment_provider",
+ "provider_subscription_id"
+ ],
+ "columnsTo": [
+ "id",
+ "kilo_user_id",
+ "payment_provider",
+ "provider_subscription_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "kilo_pass_store_purchases_store_provider_check": {
+ "name": "kilo_pass_store_purchases_store_provider_check",
+ "value": "\"kilo_pass_store_purchases\".\"payment_provider\" IN ('app_store', 'google_play')"
+ },
+ "kilo_pass_store_purchases_payment_provider_check": {
+ "name": "kilo_pass_store_purchases_payment_provider_check",
+ "value": "\"kilo_pass_store_purchases\".\"payment_provider\" IN ('stripe', 'app_store', 'google_play')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.kilo_pass_subscriptions": {
+ "name": "kilo_pass_subscriptions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "payment_provider": {
+ "name": "payment_provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'stripe'"
+ },
+ "provider_subscription_id": {
+ "name": "provider_subscription_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_subscription_id": {
+ "name": "stripe_subscription_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "tier": {
+ "name": "tier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cadence": {
+ "name": "cadence",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cancel_at_period_end": {
+ "name": "cancel_at_period_end",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ended_at": {
+ "name": "ended_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "current_streak_months": {
+ "name": "current_streak_months",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "next_yearly_issue_at": {
+ "name": "next_yearly_issue_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_kilo_pass_subscriptions_kilo_user_id": {
+ "name": "IDX_kilo_pass_subscriptions_kilo_user_id",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_subscriptions_payment_provider": {
+ "name": "IDX_kilo_pass_subscriptions_payment_provider",
+ "columns": [
+ {
+ "expression": "payment_provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_subscriptions_status": {
+ "name": "IDX_kilo_pass_subscriptions_status",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kilo_pass_subscriptions_cadence": {
+ "name": "IDX_kilo_pass_subscriptions_cadence",
+ "columns": [
+ {
+ "expression": "cadence",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_kilo_pass_subscriptions_provider_subscription": {
+ "name": "UQ_kilo_pass_subscriptions_provider_subscription",
+ "columns": [
+ {
+ "expression": "payment_provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "provider_subscription_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"kilo_pass_subscriptions\".\"provider_subscription_id\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_kilo_pass_subscriptions_store_purchase_reference": {
+ "name": "UQ_kilo_pass_subscriptions_store_purchase_reference",
+ "columns": [
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "payment_provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "provider_subscription_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "kilo_pass_subscriptions_kilo_user_id_kilocode_users_id_fk": {
+ "name": "kilo_pass_subscriptions_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "kilo_pass_subscriptions",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "kilo_pass_subscriptions_stripe_subscription_id_unique": {
+ "name": "kilo_pass_subscriptions_stripe_subscription_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "stripe_subscription_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "kilo_pass_subscriptions_current_streak_months_non_negative_check": {
+ "name": "kilo_pass_subscriptions_current_streak_months_non_negative_check",
+ "value": "\"kilo_pass_subscriptions\".\"current_streak_months\" >= 0"
+ },
+ "kilo_pass_subscriptions_provider_ids_check": {
+ "name": "kilo_pass_subscriptions_provider_ids_check",
+ "value": "(\n \"kilo_pass_subscriptions\".\"payment_provider\" = 'stripe'\n AND \"kilo_pass_subscriptions\".\"provider_subscription_id\" IS NOT NULL\n AND \"kilo_pass_subscriptions\".\"stripe_subscription_id\" IS NOT NULL\n AND \"kilo_pass_subscriptions\".\"provider_subscription_id\" = \"kilo_pass_subscriptions\".\"stripe_subscription_id\"\n ) OR (\n \"kilo_pass_subscriptions\".\"payment_provider\" IN ('app_store', 'google_play')\n AND \"kilo_pass_subscriptions\".\"provider_subscription_id\" IS NOT NULL\n AND \"kilo_pass_subscriptions\".\"stripe_subscription_id\" IS NULL\n )"
+ },
+ "kilo_pass_subscriptions_payment_provider_check": {
+ "name": "kilo_pass_subscriptions_payment_provider_check",
+ "value": "\"kilo_pass_subscriptions\".\"payment_provider\" IN ('stripe', 'app_store', 'google_play')"
+ },
+ "kilo_pass_subscriptions_tier_check": {
+ "name": "kilo_pass_subscriptions_tier_check",
+ "value": "\"kilo_pass_subscriptions\".\"tier\" IN ('tier_19', 'tier_49', 'tier_199')"
+ },
+ "kilo_pass_subscriptions_cadence_check": {
+ "name": "kilo_pass_subscriptions_cadence_check",
+ "value": "\"kilo_pass_subscriptions\".\"cadence\" IN ('monthly', 'yearly')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.kilo_pass_welcome_promo_payment_fingerprint_claims": {
+ "name": "kilo_pass_welcome_promo_payment_fingerprint_claims",
+ "schema": "",
+ "columns": {
+ "stripe_payment_method_type": {
+ "name": "stripe_payment_method_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "stripe_fingerprint": {
+ "name": "stripe_fingerprint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source_stripe_invoice_id": {
+ "name": "source_stripe_invoice_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "claimed_at": {
+ "name": "claimed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "kilo_pass_welcome_promo_payment_fingerprint_claims_stripe_payment_method_type_stripe_fingerprint_pk": {
+ "name": "kilo_pass_welcome_promo_payment_fingerprint_claims_stripe_payment_method_type_stripe_fingerprint_pk",
+ "columns": [
+ "stripe_payment_method_type",
+ "stripe_fingerprint"
+ ]
+ }
+ },
+ "uniqueConstraints": {
+ "UQ_kilo_pass_welcome_promo_payment_fingerprint_claims_source_invoice_id": {
+ "name": "UQ_kilo_pass_welcome_promo_payment_fingerprint_claims_source_invoice_id",
+ "nullsNotDistinct": false,
+ "columns": [
+ "source_stripe_invoice_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "kilo_pass_welcome_promo_payment_fingerprint_claims_type_check": {
+ "name": "kilo_pass_welcome_promo_payment_fingerprint_claims_type_check",
+ "value": "\"kilo_pass_welcome_promo_payment_fingerprint_claims\".\"stripe_payment_method_type\" IN ('card', 'sepa_debit', 'us_bank_account', 'bacs_debit', 'au_becs_debit')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.kiloclaw_access_codes": {
+ "name": "kiloclaw_access_codes",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "code": {
+ "name": "code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'active'"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "redeemed_at": {
+ "name": "redeemed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_kiloclaw_access_codes_code": {
+ "name": "UQ_kiloclaw_access_codes_code",
+ "columns": [
+ {
+ "expression": "code",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_access_codes_user_status": {
+ "name": "IDX_kiloclaw_access_codes_user_status",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_kiloclaw_access_codes_one_active_per_user": {
+ "name": "UQ_kiloclaw_access_codes_one_active_per_user",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "status = 'active'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "kiloclaw_access_codes_kilo_user_id_kilocode_users_id_fk": {
+ "name": "kiloclaw_access_codes_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "kiloclaw_access_codes",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.kiloclaw_admin_audit_logs": {
+ "name": "kiloclaw_admin_audit_logs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "action": {
+ "name": "action",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "actor_id": {
+ "name": "actor_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "actor_email": {
+ "name": "actor_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "actor_name": {
+ "name": "actor_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "target_user_id": {
+ "name": "target_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "message": {
+ "name": "message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_kiloclaw_admin_audit_logs_target_user_id": {
+ "name": "IDX_kiloclaw_admin_audit_logs_target_user_id",
+ "columns": [
+ {
+ "expression": "target_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_admin_audit_logs_action": {
+ "name": "IDX_kiloclaw_admin_audit_logs_action",
+ "columns": [
+ {
+ "expression": "action",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_admin_audit_logs_created_at": {
+ "name": "IDX_kiloclaw_admin_audit_logs_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.kiloclaw_cli_runs": {
+ "name": "kiloclaw_cli_runs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "instance_id": {
+ "name": "instance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "initiated_by_admin_id": {
+ "name": "initiated_by_admin_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "prompt": {
+ "name": "prompt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'running'"
+ },
+ "exit_code": {
+ "name": "exit_code",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "output": {
+ "name": "output",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "IDX_kiloclaw_cli_runs_user_id": {
+ "name": "IDX_kiloclaw_cli_runs_user_id",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_cli_runs_started_at": {
+ "name": "IDX_kiloclaw_cli_runs_started_at",
+ "columns": [
+ {
+ "expression": "started_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_cli_runs_instance_id": {
+ "name": "IDX_kiloclaw_cli_runs_instance_id",
+ "columns": [
+ {
+ "expression": "instance_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "kiloclaw_cli_runs_user_id_kilocode_users_id_fk": {
+ "name": "kiloclaw_cli_runs_user_id_kilocode_users_id_fk",
+ "tableFrom": "kiloclaw_cli_runs",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "kiloclaw_cli_runs_instance_id_kiloclaw_instances_id_fk": {
+ "name": "kiloclaw_cli_runs_instance_id_kiloclaw_instances_id_fk",
+ "tableFrom": "kiloclaw_cli_runs",
+ "tableTo": "kiloclaw_instances",
+ "columnsFrom": [
+ "instance_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "kiloclaw_cli_runs_initiated_by_admin_id_kilocode_users_id_fk": {
+ "name": "kiloclaw_cli_runs_initiated_by_admin_id_kilocode_users_id_fk",
+ "tableFrom": "kiloclaw_cli_runs",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "initiated_by_admin_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.kiloclaw_earlybird_purchases": {
+ "name": "kiloclaw_earlybird_purchases",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "stripe_charge_id": {
+ "name": "stripe_charge_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "manual_payment_id": {
+ "name": "manual_payment_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "amount_cents": {
+ "name": "amount_cents",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "kiloclaw_earlybird_purchases_user_id_kilocode_users_id_fk": {
+ "name": "kiloclaw_earlybird_purchases_user_id_kilocode_users_id_fk",
+ "tableFrom": "kiloclaw_earlybird_purchases",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "kiloclaw_earlybird_purchases_user_id_unique": {
+ "name": "kiloclaw_earlybird_purchases_user_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "user_id"
+ ]
+ },
+ "kiloclaw_earlybird_purchases_stripe_charge_id_unique": {
+ "name": "kiloclaw_earlybird_purchases_stripe_charge_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "stripe_charge_id"
+ ]
+ },
+ "kiloclaw_earlybird_purchases_manual_payment_id_unique": {
+ "name": "kiloclaw_earlybird_purchases_manual_payment_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "manual_payment_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.kiloclaw_email_log": {
+ "name": "kiloclaw_email_log",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "instance_id": {
+ "name": "instance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "email_type": {
+ "name": "email_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "period_start": {
+ "name": "period_start",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'epoch'"
+ },
+ "sent_at": {
+ "name": "sent_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_kiloclaw_email_log_user_type_global": {
+ "name": "UQ_kiloclaw_email_log_user_type_global",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "email_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"kiloclaw_email_log\".\"instance_id\" is null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_kiloclaw_email_log_user_instance_type_period": {
+ "name": "UQ_kiloclaw_email_log_user_instance_type_period",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "instance_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "email_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "period_start",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"kiloclaw_email_log\".\"instance_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_email_log_type_sent_instance": {
+ "name": "IDX_kiloclaw_email_log_type_sent_instance",
+ "columns": [
+ {
+ "expression": "email_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "sent_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "instance_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"kiloclaw_email_log\".\"instance_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "kiloclaw_email_log_user_id_kilocode_users_id_fk": {
+ "name": "kiloclaw_email_log_user_id_kilocode_users_id_fk",
+ "tableFrom": "kiloclaw_email_log",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "kiloclaw_email_log_instance_id_kiloclaw_instances_id_fk": {
+ "name": "kiloclaw_email_log_instance_id_kiloclaw_instances_id_fk",
+ "tableFrom": "kiloclaw_email_log",
+ "tableTo": "kiloclaw_instances",
+ "columnsFrom": [
+ "instance_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.kiloclaw_google_oauth_connections": {
+ "name": "kiloclaw_google_oauth_connections",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "instance_id": {
+ "name": "instance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'google'"
+ },
+ "account_email": {
+ "name": "account_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "account_subject": {
+ "name": "account_subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "oauth_client_id": {
+ "name": "oauth_client_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "oauth_client_secret_encrypted": {
+ "name": "oauth_client_secret_encrypted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "credential_profile": {
+ "name": "credential_profile",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'kilo_owned'"
+ },
+ "refresh_token_encrypted": {
+ "name": "refresh_token_encrypted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scopes": {
+ "name": "scopes",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::text[]"
+ },
+ "grants_by_source": {
+ "name": "grants_by_source",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "capabilities": {
+ "name": "capabilities",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::text[]"
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'active'"
+ },
+ "last_error": {
+ "name": "last_error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error_at": {
+ "name": "last_error_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "connected_at": {
+ "name": "connected_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_kiloclaw_google_oauth_connections_instance": {
+ "name": "UQ_kiloclaw_google_oauth_connections_instance",
+ "columns": [
+ {
+ "expression": "instance_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_google_oauth_connections_status": {
+ "name": "IDX_kiloclaw_google_oauth_connections_status",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_google_oauth_connections_provider": {
+ "name": "IDX_kiloclaw_google_oauth_connections_provider",
+ "columns": [
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "kiloclaw_google_oauth_connections_instance_id_kiloclaw_instances_id_fk": {
+ "name": "kiloclaw_google_oauth_connections_instance_id_kiloclaw_instances_id_fk",
+ "tableFrom": "kiloclaw_google_oauth_connections",
+ "tableTo": "kiloclaw_instances",
+ "columnsFrom": [
+ "instance_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "kiloclaw_google_oauth_connections_status_check": {
+ "name": "kiloclaw_google_oauth_connections_status_check",
+ "value": "\"kiloclaw_google_oauth_connections\".\"status\" IN ('active', 'action_required', 'disconnected')"
+ },
+ "kiloclaw_google_oauth_connections_credential_profile_check": {
+ "name": "kiloclaw_google_oauth_connections_credential_profile_check",
+ "value": "\"kiloclaw_google_oauth_connections\".\"credential_profile\" IN ('legacy', 'kilo_owned')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.kiloclaw_image_catalog": {
+ "name": "kiloclaw_image_catalog",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "openclaw_version": {
+ "name": "openclaw_version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "variant": {
+ "name": "variant",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'default'"
+ },
+ "image_tag": {
+ "name": "image_tag",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "image_digest": {
+ "name": "image_digest",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'available'"
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_by": {
+ "name": "updated_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "published_at": {
+ "name": "published_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "synced_at": {
+ "name": "synced_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "rollout_percent": {
+ "name": "rollout_percent",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "is_latest": {
+ "name": "is_latest",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {
+ "IDX_kiloclaw_image_catalog_status": {
+ "name": "IDX_kiloclaw_image_catalog_status",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_image_catalog_variant": {
+ "name": "IDX_kiloclaw_image_catalog_variant",
+ "columns": [
+ {
+ "expression": "variant",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_kiloclaw_image_catalog_one_latest_per_variant": {
+ "name": "UQ_kiloclaw_image_catalog_one_latest_per_variant",
+ "columns": [
+ {
+ "expression": "variant",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"kiloclaw_image_catalog\".\"is_latest\" = true",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_kiloclaw_image_catalog_one_candidate_per_variant": {
+ "name": "UQ_kiloclaw_image_catalog_one_candidate_per_variant",
+ "columns": [
+ {
+ "expression": "variant",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"kiloclaw_image_catalog\".\"is_latest\" = false AND \"kiloclaw_image_catalog\".\"rollout_percent\" > 0 AND \"kiloclaw_image_catalog\".\"status\" = 'available'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "kiloclaw_image_catalog_image_tag_unique": {
+ "name": "kiloclaw_image_catalog_image_tag_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "image_tag"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.kiloclaw_inbound_email_aliases": {
+ "name": "kiloclaw_inbound_email_aliases",
+ "schema": "",
+ "columns": {
+ "alias": {
+ "name": "alias",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "instance_id": {
+ "name": "instance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "retired_at": {
+ "name": "retired_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "IDX_kiloclaw_inbound_email_aliases_instance_id": {
+ "name": "IDX_kiloclaw_inbound_email_aliases_instance_id",
+ "columns": [
+ {
+ "expression": "instance_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_kiloclaw_inbound_email_aliases_active_instance": {
+ "name": "UQ_kiloclaw_inbound_email_aliases_active_instance",
+ "columns": [
+ {
+ "expression": "instance_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"kiloclaw_inbound_email_aliases\".\"retired_at\" is null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "kiloclaw_inbound_email_aliases_instance_id_kiloclaw_instances_id_fk": {
+ "name": "kiloclaw_inbound_email_aliases_instance_id_kiloclaw_instances_id_fk",
+ "tableFrom": "kiloclaw_inbound_email_aliases",
+ "tableTo": "kiloclaw_instances",
+ "columnsFrom": [
+ "instance_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.kiloclaw_inbound_email_reserved_aliases": {
+ "name": "kiloclaw_inbound_email_reserved_aliases",
+ "schema": "",
+ "columns": {
+ "alias": {
+ "name": "alias",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.kiloclaw_instances": {
+ "name": "kiloclaw_instances",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sandbox_id": {
+ "name": "sandbox_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'fly'"
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "inbound_email_enabled": {
+ "name": "inbound_email_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "inactive_trial_stopped_at": {
+ "name": "inactive_trial_stopped_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "destroyed_at": {
+ "name": "destroyed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "tracked_image_tag": {
+ "name": "tracked_image_tag",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "instance_type": {
+ "name": "instance_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "admin_size_override": {
+ "name": "admin_size_override",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "UQ_kiloclaw_instances_active": {
+ "name": "UQ_kiloclaw_instances_active",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "sandbox_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"kiloclaw_instances\".\"destroyed_at\" is null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_instances_active_personal_by_user": {
+ "name": "IDX_kiloclaw_instances_active_personal_by_user",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"kiloclaw_instances\".\"organization_id\" IS NULL AND \"kiloclaw_instances\".\"destroyed_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_instances_active_org_by_user_org": {
+ "name": "IDX_kiloclaw_instances_active_org_by_user_org",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"kiloclaw_instances\".\"organization_id\" IS NOT NULL AND \"kiloclaw_instances\".\"destroyed_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_instances_active_org_by_org_created": {
+ "name": "IDX_kiloclaw_instances_active_org_by_org_created",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"kiloclaw_instances\".\"organization_id\" IS NOT NULL AND \"kiloclaw_instances\".\"destroyed_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_instances_user_id_created_at": {
+ "name": "IDX_kiloclaw_instances_user_id_created_at",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_instances_tracked_image_tag": {
+ "name": "IDX_kiloclaw_instances_tracked_image_tag",
+ "columns": [
+ {
+ "expression": "tracked_image_tag",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"kiloclaw_instances\".\"destroyed_at\" is null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_instances_instance_type": {
+ "name": "IDX_kiloclaw_instances_instance_type",
+ "columns": [
+ {
+ "expression": "instance_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"kiloclaw_instances\".\"destroyed_at\" is null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_instances_admin_size_override": {
+ "name": "IDX_kiloclaw_instances_admin_size_override",
+ "columns": [
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"kiloclaw_instances\".\"admin_size_override\" IS NOT NULL AND \"kiloclaw_instances\".\"destroyed_at\" IS NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "kiloclaw_instances_user_id_kilocode_users_id_fk": {
+ "name": "kiloclaw_instances_user_id_kilocode_users_id_fk",
+ "tableFrom": "kiloclaw_instances",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "kiloclaw_instances_organization_id_organizations_id_fk": {
+ "name": "kiloclaw_instances_organization_id_organizations_id_fk",
+ "tableFrom": "kiloclaw_instances",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "CHK_kiloclaw_instances_instance_type": {
+ "name": "CHK_kiloclaw_instances_instance_type",
+ "value": "\"kiloclaw_instances\".\"instance_type\" IS NULL OR \"kiloclaw_instances\".\"instance_type\" IN ('perf-1-3', 'perf-4-8', 'perf-4-16', 'shared-2-3', 'shared-2-4', 'custom')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.kiloclaw_morning_briefing_configs": {
+ "name": "kiloclaw_morning_briefing_configs",
+ "schema": "",
+ "columns": {
+ "instance_id": {
+ "name": "instance_id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "cron": {
+ "name": "cron",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0 7 * * *'"
+ },
+ "timezone": {
+ "name": "timezone",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'UTC'"
+ },
+ "interest_topics": {
+ "name": "interest_topics",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::text[]"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_kiloclaw_morning_briefing_configs_enabled": {
+ "name": "IDX_kiloclaw_morning_briefing_configs_enabled",
+ "columns": [
+ {
+ "expression": "instance_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"kiloclaw_morning_briefing_configs\".\"enabled\" = true",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "kiloclaw_morning_briefing_configs_instance_id_kiloclaw_instances_id_fk": {
+ "name": "kiloclaw_morning_briefing_configs_instance_id_kiloclaw_instances_id_fk",
+ "tableFrom": "kiloclaw_morning_briefing_configs",
+ "tableTo": "kiloclaw_instances",
+ "columnsFrom": [
+ "instance_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.kiloclaw_scheduled_action_notifications": {
+ "name": "kiloclaw_scheduled_action_notifications",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "target_id": {
+ "name": "target_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel": {
+ "name": "channel",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'notice'"
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "claimed_at": {
+ "name": "claimed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sent_at": {
+ "name": "sent_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error_message": {
+ "name": "error_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "UQ_kiloclaw_scheduled_action_notifications_target_kind_channel": {
+ "name": "UQ_kiloclaw_scheduled_action_notifications_target_kind_channel",
+ "columns": [
+ {
+ "expression": "target_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "channel",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_scheduled_action_notifications_pending": {
+ "name": "IDX_kiloclaw_scheduled_action_notifications_pending",
+ "columns": [
+ {
+ "expression": "target_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"kiloclaw_scheduled_action_notifications\".\"status\" = 'pending'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "kiloclaw_scheduled_action_notifications_target_id_kiloclaw_scheduled_action_targets_id_fk": {
+ "name": "kiloclaw_scheduled_action_notifications_target_id_kiloclaw_scheduled_action_targets_id_fk",
+ "tableFrom": "kiloclaw_scheduled_action_notifications",
+ "tableTo": "kiloclaw_scheduled_action_targets",
+ "columnsFrom": [
+ "target_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.kiloclaw_scheduled_action_stages": {
+ "name": "kiloclaw_scheduled_action_stages",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "scheduled_action_id": {
+ "name": "scheduled_action_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "stage_index": {
+ "name": "stage_index",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scheduled_at": {
+ "name": "scheduled_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "notice_sent_at": {
+ "name": "notice_sent_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "applied_count": {
+ "name": "applied_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "skipped_count": {
+ "name": "skipped_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "failed_count": {
+ "name": "failed_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ }
+ },
+ "indexes": {
+ "UQ_kiloclaw_scheduled_action_stages_parent_index": {
+ "name": "UQ_kiloclaw_scheduled_action_stages_parent_index",
+ "columns": [
+ {
+ "expression": "scheduled_action_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "stage_index",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_scheduled_action_stages_notice_due": {
+ "name": "IDX_kiloclaw_scheduled_action_stages_notice_due",
+ "columns": [
+ {
+ "expression": "scheduled_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"kiloclaw_scheduled_action_stages\".\"notice_sent_at\" IS NULL AND \"kiloclaw_scheduled_action_stages\".\"status\" = 'pending'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "kiloclaw_scheduled_action_stages_scheduled_action_id_kiloclaw_scheduled_actions_id_fk": {
+ "name": "kiloclaw_scheduled_action_stages_scheduled_action_id_kiloclaw_scheduled_actions_id_fk",
+ "tableFrom": "kiloclaw_scheduled_action_stages",
+ "tableTo": "kiloclaw_scheduled_actions",
+ "columnsFrom": [
+ "scheduled_action_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.kiloclaw_scheduled_action_targets": {
+ "name": "kiloclaw_scheduled_action_targets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "scheduled_action_id": {
+ "name": "scheduled_action_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "stage_id": {
+ "name": "stage_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "instance_id": {
+ "name": "instance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source_image_tag": {
+ "name": "source_image_tag",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "target_image_tag": {
+ "name": "target_image_tag",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "applied_at": {
+ "name": "applied_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "skip_reason": {
+ "name": "skip_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error_message": {
+ "name": "error_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "UQ_kiloclaw_scheduled_action_targets_parent_instance": {
+ "name": "UQ_kiloclaw_scheduled_action_targets_parent_instance",
+ "columns": [
+ {
+ "expression": "scheduled_action_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "instance_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_scheduled_action_targets_stage": {
+ "name": "IDX_kiloclaw_scheduled_action_targets_stage",
+ "columns": [
+ {
+ "expression": "stage_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_scheduled_action_targets_pending_by_instance": {
+ "name": "IDX_kiloclaw_scheduled_action_targets_pending_by_instance",
+ "columns": [
+ {
+ "expression": "instance_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"kiloclaw_scheduled_action_targets\".\"status\" = 'pending'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "kiloclaw_scheduled_action_targets_scheduled_action_id_kiloclaw_scheduled_actions_id_fk": {
+ "name": "kiloclaw_scheduled_action_targets_scheduled_action_id_kiloclaw_scheduled_actions_id_fk",
+ "tableFrom": "kiloclaw_scheduled_action_targets",
+ "tableTo": "kiloclaw_scheduled_actions",
+ "columnsFrom": [
+ "scheduled_action_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "kiloclaw_scheduled_action_targets_stage_id_kiloclaw_scheduled_action_stages_id_fk": {
+ "name": "kiloclaw_scheduled_action_targets_stage_id_kiloclaw_scheduled_action_stages_id_fk",
+ "tableFrom": "kiloclaw_scheduled_action_targets",
+ "tableTo": "kiloclaw_scheduled_action_stages",
+ "columnsFrom": [
+ "stage_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "kiloclaw_scheduled_action_targets_instance_id_kiloclaw_instances_id_fk": {
+ "name": "kiloclaw_scheduled_action_targets_instance_id_kiloclaw_instances_id_fk",
+ "tableFrom": "kiloclaw_scheduled_action_targets",
+ "tableTo": "kiloclaw_instances",
+ "columnsFrom": [
+ "instance_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "kiloclaw_scheduled_action_targets_user_id_kilocode_users_id_fk": {
+ "name": "kiloclaw_scheduled_action_targets_user_id_kilocode_users_id_fk",
+ "tableFrom": "kiloclaw_scheduled_action_targets",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.kiloclaw_scheduled_actions": {
+ "name": "kiloclaw_scheduled_actions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "action_type": {
+ "name": "action_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_image_tag": {
+ "name": "target_image_tag",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "override_pins": {
+ "name": "override_pins",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "notice_lead_hours": {
+ "name": "notice_lead_hours",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 24
+ },
+ "notice_subject": {
+ "name": "notice_subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "notice_body": {
+ "name": "notice_body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "reason": {
+ "name": "reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'scheduled'"
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cancelled_at": {
+ "name": "cancelled_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "total_count": {
+ "name": "total_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "applied_count": {
+ "name": "applied_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "skipped_count": {
+ "name": "skipped_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "failed_count": {
+ "name": "failed_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ }
+ },
+ "indexes": {
+ "IDX_kiloclaw_scheduled_actions_status": {
+ "name": "IDX_kiloclaw_scheduled_actions_status",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_scheduled_actions_action_type": {
+ "name": "IDX_kiloclaw_scheduled_actions_action_type",
+ "columns": [
+ {
+ "expression": "action_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_scheduled_actions_created_by": {
+ "name": "IDX_kiloclaw_scheduled_actions_created_by",
+ "columns": [
+ {
+ "expression": "created_by",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "kiloclaw_scheduled_actions_target_image_tag_kiloclaw_image_catalog_image_tag_fk": {
+ "name": "kiloclaw_scheduled_actions_target_image_tag_kiloclaw_image_catalog_image_tag_fk",
+ "tableFrom": "kiloclaw_scheduled_actions",
+ "tableTo": "kiloclaw_image_catalog",
+ "columnsFrom": [
+ "target_image_tag"
+ ],
+ "columnsTo": [
+ "image_tag"
+ ],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "kiloclaw_scheduled_actions_created_by_kilocode_users_id_fk": {
+ "name": "kiloclaw_scheduled_actions_created_by_kilocode_users_id_fk",
+ "tableFrom": "kiloclaw_scheduled_actions",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "created_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.kiloclaw_subscription_change_log": {
+ "name": "kiloclaw_subscription_change_log",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "subscription_id": {
+ "name": "subscription_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "actor_type": {
+ "name": "actor_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "actor_id": {
+ "name": "actor_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action": {
+ "name": "action",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reason": {
+ "name": "reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "before_state": {
+ "name": "before_state",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "after_state": {
+ "name": "after_state",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "IDX_kiloclaw_subscription_change_log_subscription_created_at": {
+ "name": "IDX_kiloclaw_subscription_change_log_subscription_created_at",
+ "columns": [
+ {
+ "expression": "subscription_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_subscription_change_log_created_at": {
+ "name": "IDX_kiloclaw_subscription_change_log_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "kiloclaw_subscription_change_log_subscription_id_kiloclaw_subscriptions_id_fk": {
+ "name": "kiloclaw_subscription_change_log_subscription_id_kiloclaw_subscriptions_id_fk",
+ "tableFrom": "kiloclaw_subscription_change_log",
+ "tableTo": "kiloclaw_subscriptions",
+ "columnsFrom": [
+ "subscription_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "kiloclaw_subscription_change_log_actor_type_check": {
+ "name": "kiloclaw_subscription_change_log_actor_type_check",
+ "value": "\"kiloclaw_subscription_change_log\".\"actor_type\" IN ('user', 'system')"
+ },
+ "kiloclaw_subscription_change_log_action_check": {
+ "name": "kiloclaw_subscription_change_log_action_check",
+ "value": "\"kiloclaw_subscription_change_log\".\"action\" IN ('created', 'status_changed', 'plan_switched', 'period_advanced', 'canceled', 'reactivated', 'suspended', 'destruction_scheduled', 'reassigned', 'backfilled', 'payment_source_changed', 'schedule_changed', 'admin_override')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.kiloclaw_subscriptions": {
+ "name": "kiloclaw_subscriptions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "stripe_subscription_id": {
+ "name": "stripe_subscription_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_schedule_id": {
+ "name": "stripe_schedule_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "transferred_to_subscription_id": {
+ "name": "transferred_to_subscription_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "instance_id": {
+ "name": "instance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "access_origin": {
+ "name": "access_origin",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "payment_source": {
+ "name": "payment_source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "kiloclaw_price_version": {
+ "name": "kiloclaw_price_version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "plan": {
+ "name": "plan",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "scheduled_plan": {
+ "name": "scheduled_plan",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scheduled_by": {
+ "name": "scheduled_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cancel_at_period_end": {
+ "name": "cancel_at_period_end",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "pending_conversion": {
+ "name": "pending_conversion",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "trial_started_at": {
+ "name": "trial_started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "trial_ends_at": {
+ "name": "trial_ends_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "current_period_start": {
+ "name": "current_period_start",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "current_period_end": {
+ "name": "current_period_end",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "credit_renewal_at": {
+ "name": "credit_renewal_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "commit_ends_at": {
+ "name": "commit_ends_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "past_due_since": {
+ "name": "past_due_since",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "suspended_at": {
+ "name": "suspended_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "destruction_deadline": {
+ "name": "destruction_deadline",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "auto_resume_requested_at": {
+ "name": "auto_resume_requested_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "auto_resume_retry_after": {
+ "name": "auto_resume_retry_after",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "auto_resume_attempt_count": {
+ "name": "auto_resume_attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "auto_top_up_triggered_for_period": {
+ "name": "auto_top_up_triggered_for_period",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_kiloclaw_subscriptions_status": {
+ "name": "IDX_kiloclaw_subscriptions_status",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_subscriptions_user_id": {
+ "name": "IDX_kiloclaw_subscriptions_user_id",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_subscriptions_user_status": {
+ "name": "IDX_kiloclaw_subscriptions_user_status",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_subscriptions_price_version": {
+ "name": "IDX_kiloclaw_subscriptions_price_version",
+ "columns": [
+ {
+ "expression": "kiloclaw_price_version",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_subscriptions_transferred_to": {
+ "name": "IDX_kiloclaw_subscriptions_transferred_to",
+ "columns": [
+ {
+ "expression": "transferred_to_subscription_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_subscriptions_stripe_schedule_id": {
+ "name": "IDX_kiloclaw_subscriptions_stripe_schedule_id",
+ "columns": [
+ {
+ "expression": "stripe_schedule_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_subscriptions_auto_resume_retry_after": {
+ "name": "IDX_kiloclaw_subscriptions_auto_resume_retry_after",
+ "columns": [
+ {
+ "expression": "auto_resume_retry_after",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_kiloclaw_subscriptions_instance": {
+ "name": "UQ_kiloclaw_subscriptions_instance",
+ "columns": [
+ {
+ "expression": "instance_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"kiloclaw_subscriptions\".\"instance_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_kiloclaw_subscriptions_transferred_to": {
+ "name": "UQ_kiloclaw_subscriptions_transferred_to",
+ "columns": [
+ {
+ "expression": "transferred_to_subscription_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"kiloclaw_subscriptions\".\"transferred_to_subscription_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_subscriptions_earlybird_origin": {
+ "name": "IDX_kiloclaw_subscriptions_earlybird_origin",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "access_origin",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"kiloclaw_subscriptions\".\"access_origin\" = 'earlybird'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "kiloclaw_subscriptions_user_id_kilocode_users_id_fk": {
+ "name": "kiloclaw_subscriptions_user_id_kilocode_users_id_fk",
+ "tableFrom": "kiloclaw_subscriptions",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "kiloclaw_subscriptions_transferred_to_subscription_id_kiloclaw_subscriptions_id_fk": {
+ "name": "kiloclaw_subscriptions_transferred_to_subscription_id_kiloclaw_subscriptions_id_fk",
+ "tableFrom": "kiloclaw_subscriptions",
+ "tableTo": "kiloclaw_subscriptions",
+ "columnsFrom": [
+ "transferred_to_subscription_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "kiloclaw_subscriptions_instance_id_kiloclaw_instances_id_fk": {
+ "name": "kiloclaw_subscriptions_instance_id_kiloclaw_instances_id_fk",
+ "tableFrom": "kiloclaw_subscriptions",
+ "tableTo": "kiloclaw_instances",
+ "columnsFrom": [
+ "instance_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "kiloclaw_subscriptions_stripe_subscription_id_unique": {
+ "name": "kiloclaw_subscriptions_stripe_subscription_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "stripe_subscription_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "kiloclaw_subscriptions_price_version_check": {
+ "name": "kiloclaw_subscriptions_price_version_check",
+ "value": "\"kiloclaw_subscriptions\".\"kiloclaw_price_version\" IN ('2026-03-19', '2026-05-10')"
+ },
+ "kiloclaw_subscriptions_plan_check": {
+ "name": "kiloclaw_subscriptions_plan_check",
+ "value": "\"kiloclaw_subscriptions\".\"plan\" IN ('trial', 'commit', 'standard')"
+ },
+ "kiloclaw_subscriptions_scheduled_plan_check": {
+ "name": "kiloclaw_subscriptions_scheduled_plan_check",
+ "value": "\"kiloclaw_subscriptions\".\"scheduled_plan\" IN ('commit', 'standard')"
+ },
+ "kiloclaw_subscriptions_scheduled_by_check": {
+ "name": "kiloclaw_subscriptions_scheduled_by_check",
+ "value": "\"kiloclaw_subscriptions\".\"scheduled_by\" IN ('auto', 'user')"
+ },
+ "kiloclaw_subscriptions_status_check": {
+ "name": "kiloclaw_subscriptions_status_check",
+ "value": "\"kiloclaw_subscriptions\".\"status\" IN ('trialing', 'active', 'past_due', 'canceled', 'unpaid')"
+ },
+ "kiloclaw_subscriptions_access_origin_check": {
+ "name": "kiloclaw_subscriptions_access_origin_check",
+ "value": "\"kiloclaw_subscriptions\".\"access_origin\" IN ('earlybird')"
+ },
+ "kiloclaw_subscriptions_payment_source_check": {
+ "name": "kiloclaw_subscriptions_payment_source_check",
+ "value": "\"kiloclaw_subscriptions\".\"payment_source\" IN ('stripe', 'credits')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.kiloclaw_terminal_renewal_failures": {
+ "name": "kiloclaw_terminal_renewal_failures",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "subscription_id": {
+ "name": "subscription_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "renewal_boundary": {
+ "name": "renewal_boundary",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'unresolved'"
+ },
+ "attempt_count": {
+ "name": "attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "first_failure_at": {
+ "name": "first_failure_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_failure_at": {
+ "name": "last_failure_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_failure_code": {
+ "name": "last_failure_code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_failure_message": {
+ "name": "last_failure_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resolution_actor_type": {
+ "name": "resolution_actor_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resolution_actor_id": {
+ "name": "resolution_actor_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resolution_at": {
+ "name": "resolution_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resolution_reason": {
+ "name": "resolution_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_kiloclaw_terminal_renewal_failures_subscription_boundary": {
+ "name": "UQ_kiloclaw_terminal_renewal_failures_subscription_boundary",
+ "columns": [
+ {
+ "expression": "subscription_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "renewal_boundary",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_terminal_renewal_failures_unresolved": {
+ "name": "IDX_kiloclaw_terminal_renewal_failures_unresolved",
+ "columns": [
+ {
+ "expression": "subscription_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "renewal_boundary",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"kiloclaw_terminal_renewal_failures\".\"status\" = 'unresolved'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_kiloclaw_terminal_renewal_failures_status_last_failure_at": {
+ "name": "IDX_kiloclaw_terminal_renewal_failures_status_last_failure_at",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "last_failure_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "kiloclaw_terminal_renewal_failures_subscription_id_kiloclaw_subscriptions_id_fk": {
+ "name": "kiloclaw_terminal_renewal_failures_subscription_id_kiloclaw_subscriptions_id_fk",
+ "tableFrom": "kiloclaw_terminal_renewal_failures",
+ "tableTo": "kiloclaw_subscriptions",
+ "columnsFrom": [
+ "subscription_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "kiloclaw_terminal_renewal_failures_status_check": {
+ "name": "kiloclaw_terminal_renewal_failures_status_check",
+ "value": "\"kiloclaw_terminal_renewal_failures\".\"status\" IN ('unresolved', 'resolved', 'waived', 'superseded')"
+ },
+ "kiloclaw_terminal_renewal_failures_last_failure_code_check": {
+ "name": "kiloclaw_terminal_renewal_failures_last_failure_code_check",
+ "value": "\"kiloclaw_terminal_renewal_failures\".\"last_failure_code\" IN ('credit_balance_read_failed', 'renewal_transaction_failed', 'auto_top_up_marker_write_failed', 'worker_timeout', 'poison_payload', 'queue_delivery_exhausted')"
+ },
+ "kiloclaw_terminal_renewal_failures_resolution_actor_type_check": {
+ "name": "kiloclaw_terminal_renewal_failures_resolution_actor_type_check",
+ "value": "\"kiloclaw_terminal_renewal_failures\".\"resolution_actor_type\" IN ('operator', 'system')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.kiloclaw_version_pins": {
+ "name": "kiloclaw_version_pins",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "instance_id": {
+ "name": "instance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "image_tag": {
+ "name": "image_tag",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "pinned_by": {
+ "name": "pinned_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reason": {
+ "name": "reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "kiloclaw_version_pins_instance_id_kiloclaw_instances_id_fk": {
+ "name": "kiloclaw_version_pins_instance_id_kiloclaw_instances_id_fk",
+ "tableFrom": "kiloclaw_version_pins",
+ "tableTo": "kiloclaw_instances",
+ "columnsFrom": [
+ "instance_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "kiloclaw_version_pins_image_tag_kiloclaw_image_catalog_image_tag_fk": {
+ "name": "kiloclaw_version_pins_image_tag_kiloclaw_image_catalog_image_tag_fk",
+ "tableFrom": "kiloclaw_version_pins",
+ "tableTo": "kiloclaw_image_catalog",
+ "columnsFrom": [
+ "image_tag"
+ ],
+ "columnsTo": [
+ "image_tag"
+ ],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ },
+ "kiloclaw_version_pins_pinned_by_kilocode_users_id_fk": {
+ "name": "kiloclaw_version_pins_pinned_by_kilocode_users_id_fk",
+ "tableFrom": "kiloclaw_version_pins",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "pinned_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "kiloclaw_version_pins_instance_id_unique": {
+ "name": "kiloclaw_version_pins_instance_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "instance_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.kilocode_users": {
+ "name": "kilocode_users",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "google_user_email": {
+ "name": "google_user_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "google_user_name": {
+ "name": "google_user_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "google_user_image_url": {
+ "name": "google_user_image_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "hosted_domain": {
+ "name": "hosted_domain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "microdollars_used": {
+ "name": "microdollars_used",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0'"
+ },
+ "kilo_pass_threshold": {
+ "name": "kilo_pass_threshold",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_customer_id": {
+ "name": "stripe_customer_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "app_store_account_token": {
+ "name": "app_store_account_token",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "is_admin": {
+ "name": "is_admin",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "can_manage_credits": {
+ "name": "can_manage_credits",
+ "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"
+ },
+ "kilocode_users_can_manage_credits_requires_admin_check": {
+ "name": "kilocode_users_can_manage_credits_requires_admin_check",
+ "value": "NOT \"kilocode_users\".\"can_manage_credits\" OR \"kilocode_users\".\"is_admin\""
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.magic_link_tokens": {
+ "name": "magic_link_tokens",
+ "schema": "",
+ "columns": {
+ "token_hash": {
+ "name": "token_hash",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "consumed_at": {
+ "name": "consumed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "idx_magic_link_tokens_email": {
+ "name": "idx_magic_link_tokens_email",
+ "columns": [
+ {
+ "expression": "email",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_magic_link_tokens_expires_at": {
+ "name": "idx_magic_link_tokens_expires_at",
+ "columns": [
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "check_expires_at_future": {
+ "name": "check_expires_at_future",
+ "value": "\"magic_link_tokens\".\"expires_at\" > \"magic_link_tokens\".\"created_at\""
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.mcp_gateway_assignments": {
+ "name": "mcp_gateway_assignments",
+ "schema": "",
+ "columns": {
+ "assignment_id": {
+ "name": "assignment_id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "config_id": {
+ "name": "config_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "assigned_by_kilo_user_id": {
+ "name": "assigned_by_kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "single_user_slot": {
+ "name": "single_user_slot",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_mcp_gateway_assignments_active": {
+ "name": "UQ_mcp_gateway_assignments_active",
+ "columns": [
+ {
+ "expression": "config_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"mcp_gateway_assignments\".\"revoked_at\" is null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_mcp_gateway_assignments_single_user_slot": {
+ "name": "UQ_mcp_gateway_assignments_single_user_slot",
+ "columns": [
+ {
+ "expression": "config_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "single_user_slot",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"mcp_gateway_assignments\".\"revoked_at\" is null and \"mcp_gateway_assignments\".\"single_user_slot\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_mcp_gateway_assignments_config": {
+ "name": "IDX_mcp_gateway_assignments_config",
+ "columns": [
+ {
+ "expression": "config_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_mcp_gateway_assignments_user": {
+ "name": "IDX_mcp_gateway_assignments_user",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "mcp_gateway_assignments_config_id_mcp_gateway_configs_config_id_fk": {
+ "name": "mcp_gateway_assignments_config_id_mcp_gateway_configs_config_id_fk",
+ "tableFrom": "mcp_gateway_assignments",
+ "tableTo": "mcp_gateway_configs",
+ "columnsFrom": [
+ "config_id"
+ ],
+ "columnsTo": [
+ "config_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "mcp_gateway_assignments_kilo_user_id_kilocode_users_id_fk": {
+ "name": "mcp_gateway_assignments_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "mcp_gateway_assignments",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "mcp_gateway_assignments_assigned_by_kilo_user_id_kilocode_users_id_fk": {
+ "name": "mcp_gateway_assignments_assigned_by_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "mcp_gateway_assignments",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "assigned_by_kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.mcp_gateway_audit_events": {
+ "name": "mcp_gateway_audit_events",
+ "schema": "",
+ "columns": {
+ "audit_event_id": {
+ "name": "audit_event_id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "actor_kilo_user_id": {
+ "name": "actor_kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owner_scope": {
+ "name": "owner_scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "config_id": {
+ "name": "config_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "connect_resource_id": {
+ "name": "connect_resource_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "instance_id": {
+ "name": "instance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "event_type": {
+ "name": "event_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "outcome": {
+ "name": "outcome",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "correlation_metadata": {
+ "name": "correlation_metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_mcp_gateway_audit_events_config": {
+ "name": "IDX_mcp_gateway_audit_events_config",
+ "columns": [
+ {
+ "expression": "config_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_mcp_gateway_audit_events_owner": {
+ "name": "IDX_mcp_gateway_audit_events_owner",
+ "columns": [
+ {
+ "expression": "owner_scope",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "owner_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_mcp_gateway_audit_events_created_at": {
+ "name": "IDX_mcp_gateway_audit_events_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "mcp_gateway_audit_events_actor_kilo_user_id_kilocode_users_id_fk": {
+ "name": "mcp_gateway_audit_events_actor_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "mcp_gateway_audit_events",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "actor_kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "mcp_gateway_audit_events_config_id_mcp_gateway_configs_config_id_fk": {
+ "name": "mcp_gateway_audit_events_config_id_mcp_gateway_configs_config_id_fk",
+ "tableFrom": "mcp_gateway_audit_events",
+ "tableTo": "mcp_gateway_configs",
+ "columnsFrom": [
+ "config_id"
+ ],
+ "columnsTo": [
+ "config_id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "mcp_gateway_audit_events_connect_resource_id_mcp_gateway_connect_resources_connect_resource_id_fk": {
+ "name": "mcp_gateway_audit_events_connect_resource_id_mcp_gateway_connect_resources_connect_resource_id_fk",
+ "tableFrom": "mcp_gateway_audit_events",
+ "tableTo": "mcp_gateway_connect_resources",
+ "columnsFrom": [
+ "connect_resource_id"
+ ],
+ "columnsTo": [
+ "connect_resource_id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "mcp_gateway_audit_events_instance_id_mcp_gateway_connection_instances_instance_id_fk": {
+ "name": "mcp_gateway_audit_events_instance_id_mcp_gateway_connection_instances_instance_id_fk",
+ "tableFrom": "mcp_gateway_audit_events",
+ "tableTo": "mcp_gateway_connection_instances",
+ "columnsFrom": [
+ "instance_id"
+ ],
+ "columnsTo": [
+ "instance_id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "mcp_gateway_audit_events_owner_scope": {
+ "name": "mcp_gateway_audit_events_owner_scope",
+ "value": "\"mcp_gateway_audit_events\".\"owner_scope\" IN ('personal', 'organization')"
+ },
+ "mcp_gateway_audit_events_outcome": {
+ "name": "mcp_gateway_audit_events_outcome",
+ "value": "\"mcp_gateway_audit_events\".\"outcome\" IN ('success', 'failure', 'blocked')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.mcp_gateway_authorization_codes": {
+ "name": "mcp_gateway_authorization_codes",
+ "schema": "",
+ "columns": {
+ "authorization_code_id": {
+ "name": "authorization_code_id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "code_hash": {
+ "name": "code_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "authorization_request_id": {
+ "name": "authorization_request_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "oauth_client_id": {
+ "name": "oauth_client_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "client_id": {
+ "name": "client_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_scope": {
+ "name": "owner_scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "config_id": {
+ "name": "config_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "route_key": {
+ "name": "route_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "canonical_resource_url": {
+ "name": "canonical_resource_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "redirect_uri": {
+ "name": "redirect_uri",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "granted_scopes": {
+ "name": "granted_scopes",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "code_challenge": {
+ "name": "code_challenge",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "code_challenge_method": {
+ "name": "code_challenge_method",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'S256'"
+ },
+ "execution_context": {
+ "name": "execution_context",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "instance_id": {
+ "name": "instance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "consumed_at": {
+ "name": "consumed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_mcp_gateway_authorization_codes_code_hash": {
+ "name": "UQ_mcp_gateway_authorization_codes_code_hash",
+ "columns": [
+ {
+ "expression": "code_hash",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_mcp_gateway_authorization_codes_expires_at": {
+ "name": "IDX_mcp_gateway_authorization_codes_expires_at",
+ "columns": [
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_mcp_gateway_authorization_codes_client": {
+ "name": "IDX_mcp_gateway_authorization_codes_client",
+ "columns": [
+ {
+ "expression": "oauth_client_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "mcp_gateway_authorization_codes_authorization_request_id_mcp_gateway_authorization_requests_authorization_request_id_fk": {
+ "name": "mcp_gateway_authorization_codes_authorization_request_id_mcp_gateway_authorization_requests_authorization_request_id_fk",
+ "tableFrom": "mcp_gateway_authorization_codes",
+ "tableTo": "mcp_gateway_authorization_requests",
+ "columnsFrom": [
+ "authorization_request_id"
+ ],
+ "columnsTo": [
+ "authorization_request_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "mcp_gateway_authorization_codes_oauth_client_id_mcp_gateway_oauth_clients_oauth_client_id_fk": {
+ "name": "mcp_gateway_authorization_codes_oauth_client_id_mcp_gateway_oauth_clients_oauth_client_id_fk",
+ "tableFrom": "mcp_gateway_authorization_codes",
+ "tableTo": "mcp_gateway_oauth_clients",
+ "columnsFrom": [
+ "oauth_client_id"
+ ],
+ "columnsTo": [
+ "oauth_client_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "mcp_gateway_authorization_codes_config_id_mcp_gateway_configs_config_id_fk": {
+ "name": "mcp_gateway_authorization_codes_config_id_mcp_gateway_configs_config_id_fk",
+ "tableFrom": "mcp_gateway_authorization_codes",
+ "tableTo": "mcp_gateway_configs",
+ "columnsFrom": [
+ "config_id"
+ ],
+ "columnsTo": [
+ "config_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "mcp_gateway_authorization_codes_kilo_user_id_kilocode_users_id_fk": {
+ "name": "mcp_gateway_authorization_codes_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "mcp_gateway_authorization_codes",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "mcp_gateway_authorization_codes_instance_id_mcp_gateway_connection_instances_instance_id_fk": {
+ "name": "mcp_gateway_authorization_codes_instance_id_mcp_gateway_connection_instances_instance_id_fk",
+ "tableFrom": "mcp_gateway_authorization_codes",
+ "tableTo": "mcp_gateway_connection_instances",
+ "columnsFrom": [
+ "instance_id"
+ ],
+ "columnsTo": [
+ "instance_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "mcp_gateway_authorization_codes_owner_scope": {
+ "name": "mcp_gateway_authorization_codes_owner_scope",
+ "value": "\"mcp_gateway_authorization_codes\".\"owner_scope\" IN ('personal', 'organization')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.mcp_gateway_authorization_requests": {
+ "name": "mcp_gateway_authorization_requests",
+ "schema": "",
+ "columns": {
+ "authorization_request_id": {
+ "name": "authorization_request_id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "request_state_hash": {
+ "name": "request_state_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "oauth_client_id": {
+ "name": "oauth_client_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "client_id": {
+ "name": "client_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_scope": {
+ "name": "owner_scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "config_id": {
+ "name": "config_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "route_key": {
+ "name": "route_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "canonical_resource_url": {
+ "name": "canonical_resource_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "redirect_uri": {
+ "name": "redirect_uri",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "requested_scopes": {
+ "name": "requested_scopes",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "granted_scopes": {
+ "name": "granted_scopes",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "oauth_state": {
+ "name": "oauth_state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "code_challenge": {
+ "name": "code_challenge",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "code_challenge_method": {
+ "name": "code_challenge_method",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'S256'"
+ },
+ "execution_context": {
+ "name": "execution_context",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "instance_id": {
+ "name": "instance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "request_status": {
+ "name": "request_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "consumed_at": {
+ "name": "consumed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_mcp_gateway_authorization_requests_state_hash": {
+ "name": "UQ_mcp_gateway_authorization_requests_state_hash",
+ "columns": [
+ {
+ "expression": "request_state_hash",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_mcp_gateway_authorization_requests_config": {
+ "name": "IDX_mcp_gateway_authorization_requests_config",
+ "columns": [
+ {
+ "expression": "config_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_mcp_gateway_authorization_requests_user": {
+ "name": "IDX_mcp_gateway_authorization_requests_user",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_mcp_gateway_authorization_requests_expires_at": {
+ "name": "IDX_mcp_gateway_authorization_requests_expires_at",
+ "columns": [
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "mcp_gateway_authorization_requests_oauth_client_id_mcp_gateway_oauth_clients_oauth_client_id_fk": {
+ "name": "mcp_gateway_authorization_requests_oauth_client_id_mcp_gateway_oauth_clients_oauth_client_id_fk",
+ "tableFrom": "mcp_gateway_authorization_requests",
+ "tableTo": "mcp_gateway_oauth_clients",
+ "columnsFrom": [
+ "oauth_client_id"
+ ],
+ "columnsTo": [
+ "oauth_client_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "mcp_gateway_authorization_requests_config_id_mcp_gateway_configs_config_id_fk": {
+ "name": "mcp_gateway_authorization_requests_config_id_mcp_gateway_configs_config_id_fk",
+ "tableFrom": "mcp_gateway_authorization_requests",
+ "tableTo": "mcp_gateway_configs",
+ "columnsFrom": [
+ "config_id"
+ ],
+ "columnsTo": [
+ "config_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "mcp_gateway_authorization_requests_kilo_user_id_kilocode_users_id_fk": {
+ "name": "mcp_gateway_authorization_requests_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "mcp_gateway_authorization_requests",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "mcp_gateway_authorization_requests_instance_id_mcp_gateway_connection_instances_instance_id_fk": {
+ "name": "mcp_gateway_authorization_requests_instance_id_mcp_gateway_connection_instances_instance_id_fk",
+ "tableFrom": "mcp_gateway_authorization_requests",
+ "tableTo": "mcp_gateway_connection_instances",
+ "columnsFrom": [
+ "instance_id"
+ ],
+ "columnsTo": [
+ "instance_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "mcp_gateway_authorization_requests_owner_scope": {
+ "name": "mcp_gateway_authorization_requests_owner_scope",
+ "value": "\"mcp_gateway_authorization_requests\".\"owner_scope\" IN ('personal', 'organization')"
+ },
+ "mcp_gateway_authorization_requests_status": {
+ "name": "mcp_gateway_authorization_requests_status",
+ "value": "\"mcp_gateway_authorization_requests\".\"request_status\" IN ('pending', 'completed', 'error')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.mcp_gateway_config_secrets": {
+ "name": "mcp_gateway_config_secrets",
+ "schema": "",
+ "columns": {
+ "config_secret_id": {
+ "name": "config_secret_id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "config_id": {
+ "name": "config_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "secret_kind": {
+ "name": "secret_kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "encrypted_secret": {
+ "name": "encrypted_secret",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "secret_version": {
+ "name": "secret_version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_mcp_gateway_config_secrets_active_kind": {
+ "name": "UQ_mcp_gateway_config_secrets_active_kind",
+ "columns": [
+ {
+ "expression": "config_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "secret_kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"mcp_gateway_config_secrets\".\"revoked_at\" is null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_mcp_gateway_config_secrets_config": {
+ "name": "IDX_mcp_gateway_config_secrets_config",
+ "columns": [
+ {
+ "expression": "config_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "mcp_gateway_config_secrets_config_id_mcp_gateway_configs_config_id_fk": {
+ "name": "mcp_gateway_config_secrets_config_id_mcp_gateway_configs_config_id_fk",
+ "tableFrom": "mcp_gateway_config_secrets",
+ "tableTo": "mcp_gateway_configs",
+ "columnsFrom": [
+ "config_id"
+ ],
+ "columnsTo": [
+ "config_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "mcp_gateway_config_secrets_version_positive": {
+ "name": "mcp_gateway_config_secrets_version_positive",
+ "value": "\"mcp_gateway_config_secrets\".\"secret_version\" > 0"
+ },
+ "mcp_gateway_config_secrets_kind": {
+ "name": "mcp_gateway_config_secrets_kind",
+ "value": "\"mcp_gateway_config_secrets\".\"secret_kind\" IN ('static_provider_credentials', 'dynamic_registration', 'static_headers')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.mcp_gateway_configs": {
+ "name": "mcp_gateway_configs",
+ "schema": "",
+ "columns": {
+ "config_id": {
+ "name": "config_id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "owner_scope": {
+ "name": "owner_scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "remote_url": {
+ "name": "remote_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "auth_mode": {
+ "name": "auth_mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sharing_mode": {
+ "name": "sharing_mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_scopes": {
+ "name": "provider_scopes",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider_scope_source": {
+ "name": "provider_scope_source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'none'"
+ },
+ "provider_resource": {
+ "name": "provider_resource",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "path_passthrough": {
+ "name": "path_passthrough",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "config_version": {
+ "name": "config_version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "discovered_provider_metadata": {
+ "name": "discovered_provider_metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "registry_metadata": {
+ "name": "registry_metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "auxiliary_headers": {
+ "name": "auxiliary_headers",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "created_by_kilo_user_id": {
+ "name": "created_by_kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_mcp_gateway_configs_owner": {
+ "name": "IDX_mcp_gateway_configs_owner",
+ "columns": [
+ {
+ "expression": "owner_scope",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "owner_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_mcp_gateway_configs_enabled": {
+ "name": "IDX_mcp_gateway_configs_enabled",
+ "columns": [
+ {
+ "expression": "enabled",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_mcp_gateway_configs_remote_url": {
+ "name": "IDX_mcp_gateway_configs_remote_url",
+ "columns": [
+ {
+ "expression": "remote_url",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "mcp_gateway_configs_created_by_kilo_user_id_kilocode_users_id_fk": {
+ "name": "mcp_gateway_configs_created_by_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "mcp_gateway_configs",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "created_by_kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "mcp_gateway_configs_name_not_empty": {
+ "name": "mcp_gateway_configs_name_not_empty",
+ "value": "length(trim(\"mcp_gateway_configs\".\"name\")) > 0"
+ },
+ "mcp_gateway_configs_config_version_positive": {
+ "name": "mcp_gateway_configs_config_version_positive",
+ "value": "\"mcp_gateway_configs\".\"config_version\" > 0"
+ },
+ "mcp_gateway_configs_personal_single_user": {
+ "name": "mcp_gateway_configs_personal_single_user",
+ "value": "\"mcp_gateway_configs\".\"owner_scope\" <> 'personal' OR \"mcp_gateway_configs\".\"sharing_mode\" = 'single_user'"
+ },
+ "mcp_gateway_configs_owner_scope": {
+ "name": "mcp_gateway_configs_owner_scope",
+ "value": "\"mcp_gateway_configs\".\"owner_scope\" IN ('personal', 'organization')"
+ },
+ "mcp_gateway_configs_auth_mode": {
+ "name": "mcp_gateway_configs_auth_mode",
+ "value": "\"mcp_gateway_configs\".\"auth_mode\" IN ('none', 'static_headers', 'oauth_dynamic', 'oauth_static')"
+ },
+ "mcp_gateway_configs_sharing_mode": {
+ "name": "mcp_gateway_configs_sharing_mode",
+ "value": "\"mcp_gateway_configs\".\"sharing_mode\" IN ('single_user', 'multi_user')"
+ },
+ "mcp_gateway_configs_provider_scope_source": {
+ "name": "mcp_gateway_configs_provider_scope_source",
+ "value": "\"mcp_gateway_configs\".\"provider_scope_source\" IN ('none', 'discovered', 'override')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.mcp_gateway_connect_resources": {
+ "name": "mcp_gateway_connect_resources",
+ "schema": "",
+ "columns": {
+ "connect_resource_id": {
+ "name": "connect_resource_id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "config_id": {
+ "name": "config_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_scope": {
+ "name": "owner_scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "route_key": {
+ "name": "route_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "canonical_url": {
+ "name": "canonical_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "route_status": {
+ "name": "route_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'active'"
+ },
+ "route_version": {
+ "name": "route_version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "rotated_at": {
+ "name": "rotated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_mcp_gateway_connect_resources_route_key": {
+ "name": "UQ_mcp_gateway_connect_resources_route_key",
+ "columns": [
+ {
+ "expression": "route_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_mcp_gateway_connect_resources_active_config": {
+ "name": "UQ_mcp_gateway_connect_resources_active_config",
+ "columns": [
+ {
+ "expression": "config_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"mcp_gateway_connect_resources\".\"route_status\" = 'active'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_mcp_gateway_connect_resources_config": {
+ "name": "IDX_mcp_gateway_connect_resources_config",
+ "columns": [
+ {
+ "expression": "config_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_mcp_gateway_connect_resources_canonical_url": {
+ "name": "IDX_mcp_gateway_connect_resources_canonical_url",
+ "columns": [
+ {
+ "expression": "canonical_url",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "mcp_gateway_connect_resources_config_id_mcp_gateway_configs_config_id_fk": {
+ "name": "mcp_gateway_connect_resources_config_id_mcp_gateway_configs_config_id_fk",
+ "tableFrom": "mcp_gateway_connect_resources",
+ "tableTo": "mcp_gateway_configs",
+ "columnsFrom": [
+ "config_id"
+ ],
+ "columnsTo": [
+ "config_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "mcp_gateway_connect_resources_route_key_format": {
+ "name": "mcp_gateway_connect_resources_route_key_format",
+ "value": "\"mcp_gateway_connect_resources\".\"route_key\" ~ '^[A-Za-z0-9_-]{32,}$'"
+ },
+ "mcp_gateway_connect_resources_route_version_positive": {
+ "name": "mcp_gateway_connect_resources_route_version_positive",
+ "value": "\"mcp_gateway_connect_resources\".\"route_version\" > 0"
+ },
+ "mcp_gateway_connect_resources_owner_scope": {
+ "name": "mcp_gateway_connect_resources_owner_scope",
+ "value": "\"mcp_gateway_connect_resources\".\"owner_scope\" IN ('personal', 'organization')"
+ },
+ "mcp_gateway_connect_resources_route_status": {
+ "name": "mcp_gateway_connect_resources_route_status",
+ "value": "\"mcp_gateway_connect_resources\".\"route_status\" IN ('active', 'rotated', 'revoked')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.mcp_gateway_connection_instances": {
+ "name": "mcp_gateway_connection_instances",
+ "schema": "",
+ "columns": {
+ "instance_id": {
+ "name": "instance_id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "config_id": {
+ "name": "config_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_scope": {
+ "name": "owner_scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "instance_status": {
+ "name": "instance_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'active'"
+ },
+ "instance_version": {
+ "name": "instance_version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "last_used_at": {
+ "name": "last_used_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "removed_at": {
+ "name": "removed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_mcp_gateway_connection_instances_non_terminal": {
+ "name": "UQ_mcp_gateway_connection_instances_non_terminal",
+ "columns": [
+ {
+ "expression": "owner_scope",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "owner_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "config_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"mcp_gateway_connection_instances\".\"instance_status\" IN ('active', 'needs_reauth')",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_mcp_gateway_connection_instances_config": {
+ "name": "IDX_mcp_gateway_connection_instances_config",
+ "columns": [
+ {
+ "expression": "config_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_mcp_gateway_connection_instances_user": {
+ "name": "IDX_mcp_gateway_connection_instances_user",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "mcp_gateway_connection_instances_config_id_mcp_gateway_configs_config_id_fk": {
+ "name": "mcp_gateway_connection_instances_config_id_mcp_gateway_configs_config_id_fk",
+ "tableFrom": "mcp_gateway_connection_instances",
+ "tableTo": "mcp_gateway_configs",
+ "columnsFrom": [
+ "config_id"
+ ],
+ "columnsTo": [
+ "config_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "mcp_gateway_connection_instances_kilo_user_id_kilocode_users_id_fk": {
+ "name": "mcp_gateway_connection_instances_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "mcp_gateway_connection_instances",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "mcp_gateway_connection_instances_version_positive": {
+ "name": "mcp_gateway_connection_instances_version_positive",
+ "value": "\"mcp_gateway_connection_instances\".\"instance_version\" > 0"
+ },
+ "mcp_gateway_connection_instances_owner_scope": {
+ "name": "mcp_gateway_connection_instances_owner_scope",
+ "value": "\"mcp_gateway_connection_instances\".\"owner_scope\" IN ('personal', 'organization')"
+ },
+ "mcp_gateway_connection_instances_status": {
+ "name": "mcp_gateway_connection_instances_status",
+ "value": "\"mcp_gateway_connection_instances\".\"instance_status\" IN ('active', 'needs_reauth', 'revoked', 'removed')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.mcp_gateway_oauth_clients": {
+ "name": "mcp_gateway_oauth_clients",
+ "schema": "",
+ "columns": {
+ "oauth_client_id": {
+ "name": "oauth_client_id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "client_id": {
+ "name": "client_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "client_name": {
+ "name": "client_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "registration_token_hash": {
+ "name": "registration_token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "client_secret_hash": {
+ "name": "client_secret_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "token_endpoint_auth_method": {
+ "name": "token_endpoint_auth_method",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "redirect_uris": {
+ "name": "redirect_uris",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "grant_types": {
+ "name": "grant_types",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "response_types": {
+ "name": "response_types",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "declared_scopes": {
+ "name": "declared_scopes",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "registration_access_token_expires_at": {
+ "name": "registration_access_token_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_mcp_gateway_oauth_clients_client_id": {
+ "name": "UQ_mcp_gateway_oauth_clients_client_id",
+ "columns": [
+ {
+ "expression": "client_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_mcp_gateway_oauth_clients_registration_token_hash": {
+ "name": "UQ_mcp_gateway_oauth_clients_registration_token_hash",
+ "columns": [
+ {
+ "expression": "registration_token_hash",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_mcp_gateway_oauth_clients_deleted_at": {
+ "name": "IDX_mcp_gateway_oauth_clients_deleted_at",
+ "columns": [
+ {
+ "expression": "deleted_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "mcp_gateway_oauth_clients_client_id_format": {
+ "name": "mcp_gateway_oauth_clients_client_id_format",
+ "value": "\"mcp_gateway_oauth_clients\".\"client_id\" ~ '^[A-Za-z0-9._-]+:[A-Za-z0-9._-]+$'"
+ },
+ "mcp_gateway_oauth_clients_auth_method": {
+ "name": "mcp_gateway_oauth_clients_auth_method",
+ "value": "\"mcp_gateway_oauth_clients\".\"token_endpoint_auth_method\" IN ('none', 'client_secret_post', 'client_secret_basic')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.mcp_gateway_pending_provider_authorizations": {
+ "name": "mcp_gateway_pending_provider_authorizations",
+ "schema": "",
+ "columns": {
+ "pending_provider_authorization_id": {
+ "name": "pending_provider_authorization_id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "state_hash": {
+ "name": "state_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "authorization_request_id": {
+ "name": "authorization_request_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "config_id": {
+ "name": "config_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "instance_id": {
+ "name": "instance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_scope": {
+ "name": "owner_scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "route_key": {
+ "name": "route_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "canonical_resource_url": {
+ "name": "canonical_resource_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "remote_url": {
+ "name": "remote_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "auth_mode": {
+ "name": "auth_mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_authorization_endpoint": {
+ "name": "provider_authorization_endpoint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_token_endpoint": {
+ "name": "provider_token_endpoint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "encrypted_state": {
+ "name": "encrypted_state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "execution_context": {
+ "name": "execution_context",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "config_version": {
+ "name": "config_version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "pending_status": {
+ "name": "pending_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "consumed_at": {
+ "name": "consumed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_mcp_gateway_pending_provider_authorizations_state_hash": {
+ "name": "UQ_mcp_gateway_pending_provider_authorizations_state_hash",
+ "columns": [
+ {
+ "expression": "state_hash",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_mcp_gateway_pending_provider_authorizations_config": {
+ "name": "IDX_mcp_gateway_pending_provider_authorizations_config",
+ "columns": [
+ {
+ "expression": "config_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_mcp_gateway_pending_provider_authorizations_expires_at": {
+ "name": "IDX_mcp_gateway_pending_provider_authorizations_expires_at",
+ "columns": [
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "mcp_gateway_pending_provider_authorizations_authorization_request_id_mcp_gateway_authorization_requests_authorization_request_id_fk": {
+ "name": "mcp_gateway_pending_provider_authorizations_authorization_request_id_mcp_gateway_authorization_requests_authorization_request_id_fk",
+ "tableFrom": "mcp_gateway_pending_provider_authorizations",
+ "tableTo": "mcp_gateway_authorization_requests",
+ "columnsFrom": [
+ "authorization_request_id"
+ ],
+ "columnsTo": [
+ "authorization_request_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "mcp_gateway_pending_provider_authorizations_config_id_mcp_gateway_configs_config_id_fk": {
+ "name": "mcp_gateway_pending_provider_authorizations_config_id_mcp_gateway_configs_config_id_fk",
+ "tableFrom": "mcp_gateway_pending_provider_authorizations",
+ "tableTo": "mcp_gateway_configs",
+ "columnsFrom": [
+ "config_id"
+ ],
+ "columnsTo": [
+ "config_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "mcp_gateway_pending_provider_authorizations_instance_id_mcp_gateway_connection_instances_instance_id_fk": {
+ "name": "mcp_gateway_pending_provider_authorizations_instance_id_mcp_gateway_connection_instances_instance_id_fk",
+ "tableFrom": "mcp_gateway_pending_provider_authorizations",
+ "tableTo": "mcp_gateway_connection_instances",
+ "columnsFrom": [
+ "instance_id"
+ ],
+ "columnsTo": [
+ "instance_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "mcp_gateway_pending_provider_authorizations_kilo_user_id_kilocode_users_id_fk": {
+ "name": "mcp_gateway_pending_provider_authorizations_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "mcp_gateway_pending_provider_authorizations",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "mcp_gateway_pending_provider_authorizations_config_version_positive": {
+ "name": "mcp_gateway_pending_provider_authorizations_config_version_positive",
+ "value": "\"mcp_gateway_pending_provider_authorizations\".\"config_version\" > 0"
+ },
+ "mcp_gateway_pending_provider_authorizations_owner_scope": {
+ "name": "mcp_gateway_pending_provider_authorizations_owner_scope",
+ "value": "\"mcp_gateway_pending_provider_authorizations\".\"owner_scope\" IN ('personal', 'organization')"
+ },
+ "mcp_gateway_pending_provider_authorizations_auth_mode": {
+ "name": "mcp_gateway_pending_provider_authorizations_auth_mode",
+ "value": "\"mcp_gateway_pending_provider_authorizations\".\"auth_mode\" IN ('none', 'static_headers', 'oauth_dynamic', 'oauth_static')"
+ },
+ "mcp_gateway_pending_provider_authorizations_status": {
+ "name": "mcp_gateway_pending_provider_authorizations_status",
+ "value": "\"mcp_gateway_pending_provider_authorizations\".\"pending_status\" IN ('pending', 'completed', 'error')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.mcp_gateway_provider_grants": {
+ "name": "mcp_gateway_provider_grants",
+ "schema": "",
+ "columns": {
+ "provider_grant_id": {
+ "name": "provider_grant_id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "instance_id": {
+ "name": "instance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "encrypted_grant": {
+ "name": "encrypted_grant",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_subject": {
+ "name": "provider_subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "grant_scope": {
+ "name": "grant_scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "grant_status": {
+ "name": "grant_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'active'"
+ },
+ "grant_version": {
+ "name": "grant_version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "last_used_at": {
+ "name": "last_used_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_mcp_gateway_provider_grants_active_instance": {
+ "name": "UQ_mcp_gateway_provider_grants_active_instance",
+ "columns": [
+ {
+ "expression": "instance_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"mcp_gateway_provider_grants\".\"grant_status\" = 'active'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_mcp_gateway_provider_grants_instance": {
+ "name": "IDX_mcp_gateway_provider_grants_instance",
+ "columns": [
+ {
+ "expression": "instance_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "mcp_gateway_provider_grants_instance_id_mcp_gateway_connection_instances_instance_id_fk": {
+ "name": "mcp_gateway_provider_grants_instance_id_mcp_gateway_connection_instances_instance_id_fk",
+ "tableFrom": "mcp_gateway_provider_grants",
+ "tableTo": "mcp_gateway_connection_instances",
+ "columnsFrom": [
+ "instance_id"
+ ],
+ "columnsTo": [
+ "instance_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "mcp_gateway_provider_grants_version_positive": {
+ "name": "mcp_gateway_provider_grants_version_positive",
+ "value": "\"mcp_gateway_provider_grants\".\"grant_version\" > 0"
+ },
+ "mcp_gateway_provider_grants_status": {
+ "name": "mcp_gateway_provider_grants_status",
+ "value": "\"mcp_gateway_provider_grants\".\"grant_status\" IN ('active', 'revoked')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.mcp_gateway_rate_limit_windows": {
+ "name": "mcp_gateway_rate_limit_windows",
+ "schema": "",
+ "columns": {
+ "rate_limit_window_id": {
+ "name": "rate_limit_window_id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "ip_hash": {
+ "name": "ip_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "window_started_at": {
+ "name": "window_started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "attempt_count": {
+ "name": "attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_mcp_gateway_rate_limit_windows_ip_window": {
+ "name": "UQ_mcp_gateway_rate_limit_windows_ip_window",
+ "columns": [
+ {
+ "expression": "ip_hash",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "window_started_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_mcp_gateway_rate_limit_windows_window": {
+ "name": "IDX_mcp_gateway_rate_limit_windows_window",
+ "columns": [
+ {
+ "expression": "window_started_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "mcp_gateway_rate_limit_windows_attempt_count_non_negative": {
+ "name": "mcp_gateway_rate_limit_windows_attempt_count_non_negative",
+ "value": "\"mcp_gateway_rate_limit_windows\".\"attempt_count\" >= 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.mcp_gateway_refresh_tokens": {
+ "name": "mcp_gateway_refresh_tokens",
+ "schema": "",
+ "columns": {
+ "refresh_token_id": {
+ "name": "refresh_token_id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "token_hash": {
+ "name": "token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "rotated_from_refresh_token_id": {
+ "name": "rotated_from_refresh_token_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "oauth_client_id": {
+ "name": "oauth_client_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "client_id": {
+ "name": "client_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_scope": {
+ "name": "owner_scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "config_id": {
+ "name": "config_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "route_key": {
+ "name": "route_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "canonical_resource_url": {
+ "name": "canonical_resource_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "granted_scopes": {
+ "name": "granted_scopes",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "execution_context": {
+ "name": "execution_context",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "instance_id": {
+ "name": "instance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "consumed_at": {
+ "name": "consumed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_mcp_gateway_refresh_tokens_token_hash": {
+ "name": "UQ_mcp_gateway_refresh_tokens_token_hash",
+ "columns": [
+ {
+ "expression": "token_hash",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_mcp_gateway_refresh_tokens_user": {
+ "name": "IDX_mcp_gateway_refresh_tokens_user",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_mcp_gateway_refresh_tokens_config": {
+ "name": "IDX_mcp_gateway_refresh_tokens_config",
+ "columns": [
+ {
+ "expression": "config_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_mcp_gateway_refresh_tokens_consumed_at": {
+ "name": "IDX_mcp_gateway_refresh_tokens_consumed_at",
+ "columns": [
+ {
+ "expression": "consumed_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "mcp_gateway_refresh_tokens_oauth_client_id_mcp_gateway_oauth_clients_oauth_client_id_fk": {
+ "name": "mcp_gateway_refresh_tokens_oauth_client_id_mcp_gateway_oauth_clients_oauth_client_id_fk",
+ "tableFrom": "mcp_gateway_refresh_tokens",
+ "tableTo": "mcp_gateway_oauth_clients",
+ "columnsFrom": [
+ "oauth_client_id"
+ ],
+ "columnsTo": [
+ "oauth_client_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "mcp_gateway_refresh_tokens_config_id_mcp_gateway_configs_config_id_fk": {
+ "name": "mcp_gateway_refresh_tokens_config_id_mcp_gateway_configs_config_id_fk",
+ "tableFrom": "mcp_gateway_refresh_tokens",
+ "tableTo": "mcp_gateway_configs",
+ "columnsFrom": [
+ "config_id"
+ ],
+ "columnsTo": [
+ "config_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "mcp_gateway_refresh_tokens_kilo_user_id_kilocode_users_id_fk": {
+ "name": "mcp_gateway_refresh_tokens_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "mcp_gateway_refresh_tokens",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "mcp_gateway_refresh_tokens_instance_id_mcp_gateway_connection_instances_instance_id_fk": {
+ "name": "mcp_gateway_refresh_tokens_instance_id_mcp_gateway_connection_instances_instance_id_fk",
+ "tableFrom": "mcp_gateway_refresh_tokens",
+ "tableTo": "mcp_gateway_connection_instances",
+ "columnsFrom": [
+ "instance_id"
+ ],
+ "columnsTo": [
+ "instance_id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "mcp_gateway_refresh_tokens_owner_scope": {
+ "name": "mcp_gateway_refresh_tokens_owner_scope",
+ "value": "\"mcp_gateway_refresh_tokens\".\"owner_scope\" IN ('personal', 'organization')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.microdollar_usage": {
+ "name": "microdollar_usage",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cost": {
+ "name": "cost",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "input_tokens": {
+ "name": "input_tokens",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "output_tokens": {
+ "name": "output_tokens",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cache_write_tokens": {
+ "name": "cache_write_tokens",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cache_hit_tokens": {
+ "name": "cache_hit_tokens",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "requested_model": {
+ "name": "requested_model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cache_discount": {
+ "name": "cache_discount",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "has_error": {
+ "name": "has_error",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "abuse_classification": {
+ "name": "abuse_classification",
+ "type": "smallint",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "inference_provider": {
+ "name": "inference_provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "idx_created_at": {
+ "name": "idx_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_abuse_classification": {
+ "name": "idx_abuse_classification",
+ "columns": [
+ {
+ "expression": "abuse_classification",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_kilo_user_id_created_at2": {
+ "name": "idx_kilo_user_id_created_at2",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_microdollar_usage_organization_id": {
+ "name": "idx_microdollar_usage_organization_id",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"microdollar_usage\".\"organization_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.microdollar_usage_daily": {
+ "name": "microdollar_usage_daily",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "usage_date": {
+ "name": "usage_date",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "total_cost_microdollars": {
+ "name": "total_cost_microdollars",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "idx_microdollar_usage_daily_personal": {
+ "name": "idx_microdollar_usage_daily_personal",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "usage_date",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"microdollar_usage_daily\".\"organization_id\" is null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_microdollar_usage_daily_org": {
+ "name": "idx_microdollar_usage_daily_org",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "usage_date",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"microdollar_usage_daily\".\"organization_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.microdollar_usage_metadata": {
+ "name": "microdollar_usage_metadata",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "message_id": {
+ "name": "message_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "http_user_agent_id": {
+ "name": "http_user_agent_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "http_ip_id": {
+ "name": "http_ip_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "vercel_ip_city_id": {
+ "name": "vercel_ip_city_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "vercel_ip_country_id": {
+ "name": "vercel_ip_country_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "vercel_ip_latitude": {
+ "name": "vercel_ip_latitude",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "vercel_ip_longitude": {
+ "name": "vercel_ip_longitude",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ja4_digest_id": {
+ "name": "ja4_digest_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_prompt_prefix": {
+ "name": "user_prompt_prefix",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "system_prompt_prefix_id": {
+ "name": "system_prompt_prefix_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "system_prompt_length": {
+ "name": "system_prompt_length",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "max_tokens": {
+ "name": "max_tokens",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "has_middle_out_transform": {
+ "name": "has_middle_out_transform",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status_code": {
+ "name": "status_code",
+ "type": "smallint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "upstream_id": {
+ "name": "upstream_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "finish_reason_id": {
+ "name": "finish_reason_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "latency": {
+ "name": "latency",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "moderation_latency": {
+ "name": "moderation_latency",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "generation_time": {
+ "name": "generation_time",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_byok": {
+ "name": "is_byok",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_user_byok": {
+ "name": "is_user_byok",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "streamed": {
+ "name": "streamed",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cancelled": {
+ "name": "cancelled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "editor_name_id": {
+ "name": "editor_name_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "api_kind_id": {
+ "name": "api_kind_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "has_tools": {
+ "name": "has_tools",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "machine_id": {
+ "name": "machine_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "feature_id": {
+ "name": "feature_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "session_id": {
+ "name": "session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "mode_id": {
+ "name": "mode_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "auto_model_id": {
+ "name": "auto_model_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "market_cost": {
+ "name": "market_cost",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_free": {
+ "name": "is_free",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "abuse_delay": {
+ "name": "abuse_delay",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "abuse_downgraded_from": {
+ "name": "abuse_downgraded_from",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "idx_microdollar_usage_metadata_created_at": {
+ "name": "idx_microdollar_usage_metadata_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_microdollar_usage_metadata_session_id": {
+ "name": "idx_microdollar_usage_metadata_session_id",
+ "columns": [
+ {
+ "expression": "session_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"microdollar_usage_metadata\".\"session_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "microdollar_usage_metadata_http_user_agent_id_http_user_agent_http_user_agent_id_fk": {
+ "name": "microdollar_usage_metadata_http_user_agent_id_http_user_agent_http_user_agent_id_fk",
+ "tableFrom": "microdollar_usage_metadata",
+ "tableTo": "http_user_agent",
+ "columnsFrom": [
+ "http_user_agent_id"
+ ],
+ "columnsTo": [
+ "http_user_agent_id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "microdollar_usage_metadata_http_ip_id_http_ip_http_ip_id_fk": {
+ "name": "microdollar_usage_metadata_http_ip_id_http_ip_http_ip_id_fk",
+ "tableFrom": "microdollar_usage_metadata",
+ "tableTo": "http_ip",
+ "columnsFrom": [
+ "http_ip_id"
+ ],
+ "columnsTo": [
+ "http_ip_id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "microdollar_usage_metadata_vercel_ip_city_id_vercel_ip_city_vercel_ip_city_id_fk": {
+ "name": "microdollar_usage_metadata_vercel_ip_city_id_vercel_ip_city_vercel_ip_city_id_fk",
+ "tableFrom": "microdollar_usage_metadata",
+ "tableTo": "vercel_ip_city",
+ "columnsFrom": [
+ "vercel_ip_city_id"
+ ],
+ "columnsTo": [
+ "vercel_ip_city_id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "microdollar_usage_metadata_vercel_ip_country_id_vercel_ip_country_vercel_ip_country_id_fk": {
+ "name": "microdollar_usage_metadata_vercel_ip_country_id_vercel_ip_country_vercel_ip_country_id_fk",
+ "tableFrom": "microdollar_usage_metadata",
+ "tableTo": "vercel_ip_country",
+ "columnsFrom": [
+ "vercel_ip_country_id"
+ ],
+ "columnsTo": [
+ "vercel_ip_country_id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "microdollar_usage_metadata_ja4_digest_id_ja4_digest_ja4_digest_id_fk": {
+ "name": "microdollar_usage_metadata_ja4_digest_id_ja4_digest_ja4_digest_id_fk",
+ "tableFrom": "microdollar_usage_metadata",
+ "tableTo": "ja4_digest",
+ "columnsFrom": [
+ "ja4_digest_id"
+ ],
+ "columnsTo": [
+ "ja4_digest_id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "microdollar_usage_metadata_system_prompt_prefix_id_system_prompt_prefix_system_prompt_prefix_id_fk": {
+ "name": "microdollar_usage_metadata_system_prompt_prefix_id_system_prompt_prefix_system_prompt_prefix_id_fk",
+ "tableFrom": "microdollar_usage_metadata",
+ "tableTo": "system_prompt_prefix",
+ "columnsFrom": [
+ "system_prompt_prefix_id"
+ ],
+ "columnsTo": [
+ "system_prompt_prefix_id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.mode": {
+ "name": "mode",
+ "schema": "",
+ "columns": {
+ "mode_id": {
+ "name": "mode_id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "mode": {
+ "name": "mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "UQ_mode": {
+ "name": "UQ_mode",
+ "columns": [
+ {
+ "expression": "mode",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.model_stats": {
+ "name": "model_stats",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": true
+ },
+ "is_featured": {
+ "name": "is_featured",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "is_stealth": {
+ "name": "is_stealth",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "is_recommended": {
+ "name": "is_recommended",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "openrouter_id": {
+ "name": "openrouter_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "aa_slug": {
+ "name": "aa_slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model_creator": {
+ "name": "model_creator",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creator_slug": {
+ "name": "creator_slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "release_date": {
+ "name": "release_date",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "price_input": {
+ "name": "price_input",
+ "type": "numeric(10, 6)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "price_output": {
+ "name": "price_output",
+ "type": "numeric(10, 6)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "coding_index": {
+ "name": "coding_index",
+ "type": "numeric(5, 2)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "speed_tokens_per_sec": {
+ "name": "speed_tokens_per_sec",
+ "type": "numeric(8, 2)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "context_length": {
+ "name": "context_length",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "max_output_tokens": {
+ "name": "max_output_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "input_modalities": {
+ "name": "input_modalities",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "openrouter_data": {
+ "name": "openrouter_data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "benchmarks": {
+ "name": "benchmarks",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "chart_data": {
+ "name": "chart_data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_model_stats_openrouter_id": {
+ "name": "IDX_model_stats_openrouter_id",
+ "columns": [
+ {
+ "expression": "openrouter_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_model_stats_slug": {
+ "name": "IDX_model_stats_slug",
+ "columns": [
+ {
+ "expression": "slug",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_model_stats_is_active": {
+ "name": "IDX_model_stats_is_active",
+ "columns": [
+ {
+ "expression": "is_active",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_model_stats_creator_slug": {
+ "name": "IDX_model_stats_creator_slug",
+ "columns": [
+ {
+ "expression": "creator_slug",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_model_stats_price_input": {
+ "name": "IDX_model_stats_price_input",
+ "columns": [
+ {
+ "expression": "price_input",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_model_stats_coding_index": {
+ "name": "IDX_model_stats_coding_index",
+ "columns": [
+ {
+ "expression": "coding_index",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_model_stats_context_length": {
+ "name": "IDX_model_stats_context_length",
+ "columns": [
+ {
+ "expression": "context_length",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "model_stats_openrouter_id_unique": {
+ "name": "model_stats_openrouter_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "openrouter_id"
+ ]
+ },
+ "model_stats_slug_unique": {
+ "name": "model_stats_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "slug"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.model_eval_ingestions": {
+ "name": "model_eval_ingestions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "bench_eval_name": {
+ "name": "bench_eval_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "bench_eval_url": {
+ "name": "bench_eval_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "model_stats_id": {
+ "name": "model_stats_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "variant": {
+ "name": "variant",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "task_source": {
+ "name": "task_source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "n_total_trials": {
+ "name": "n_total_trials",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "n_attempts": {
+ "name": "n_attempts",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "total_score": {
+ "name": "total_score",
+ "type": "numeric(14, 6)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "overall_score": {
+ "name": "overall_score",
+ "type": "numeric(12, 8)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "n_errored": {
+ "name": "n_errored",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "avg_cost_microdollars": {
+ "name": "avg_cost_microdollars",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "total_cost_microdollars": {
+ "name": "total_cost_microdollars",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "avg_input_tokens": {
+ "name": "avg_input_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "total_input_tokens": {
+ "name": "total_input_tokens",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "avg_output_tokens": {
+ "name": "avg_output_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "total_output_tokens": {
+ "name": "total_output_tokens",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "avg_cache_read_tokens": {
+ "name": "avg_cache_read_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "total_cache_read_tokens": {
+ "name": "total_cache_read_tokens",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "avg_execution_ms": {
+ "name": "avg_execution_ms",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "promoted_at": {
+ "name": "promoted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "promoted_by_email": {
+ "name": "promoted_by_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "promotion_note": {
+ "name": "promotion_note",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_model_eval_ingestions_lookup": {
+ "name": "IDX_model_eval_ingestions_lookup",
+ "columns": [
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "model",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "variant",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "task_source",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "promoted_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_model_eval_ingestions_model_stats": {
+ "name": "IDX_model_eval_ingestions_model_stats",
+ "columns": [
+ {
+ "expression": "model_stats_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_model_eval_ingestions_promoted_by_email_lower": {
+ "name": "IDX_model_eval_ingestions_promoted_by_email_lower",
+ "columns": [
+ {
+ "expression": "LOWER(\"promoted_by_email\")",
+ "asc": true,
+ "isExpression": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "model_eval_ingestions_model_stats_id_model_stats_id_fk": {
+ "name": "model_eval_ingestions_model_stats_id_model_stats_id_fk",
+ "tableFrom": "model_eval_ingestions",
+ "tableTo": "model_stats",
+ "columnsFrom": [
+ "model_stats_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "model_eval_ingestions_bench_eval_name_unique": {
+ "name": "model_eval_ingestions_bench_eval_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "bench_eval_name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.model_experiment": {
+ "name": "model_experiment",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "public_model_id": {
+ "name": "public_model_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'draft'"
+ },
+ "is_archived": {
+ "name": "is_archived",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_by_user_id": {
+ "name": "created_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ended_at": {
+ "name": "ended_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "UQ_model_experiment_public_model_id_routing": {
+ "name": "UQ_model_experiment_public_model_id_routing",
+ "columns": [
+ {
+ "expression": "public_model_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"model_experiment\".\"status\" IN ('active', 'paused')",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_model_experiment_status": {
+ "name": "IDX_model_experiment_status",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "model_experiment_created_by_user_id_kilocode_users_id_fk": {
+ "name": "model_experiment_created_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "model_experiment",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "created_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "model_experiment_status_valid": {
+ "name": "model_experiment_status_valid",
+ "value": "\"model_experiment\".\"status\" IN ('draft', 'active', 'paused', 'completed')"
+ },
+ "model_experiment_active_not_archived": {
+ "name": "model_experiment_active_not_archived",
+ "value": "\"model_experiment\".\"status\" <> 'active' OR \"model_experiment\".\"is_archived\" = false"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.model_experiment_request": {
+ "name": "model_experiment_request",
+ "schema": "",
+ "columns": {
+ "usage_id": {
+ "name": "usage_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "variant_version_id": {
+ "name": "variant_version_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "allocation_subject": {
+ "name": "allocation_subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "client_request_id": {
+ "name": "client_request_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "request_kind": {
+ "name": "request_kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "request_body_sha256": {
+ "name": "request_body_sha256",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "was_truncated": {
+ "name": "was_truncated",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_model_experiment_request_variant_version_created_at": {
+ "name": "IDX_model_experiment_request_variant_version_created_at",
+ "columns": [
+ {
+ "expression": "variant_version_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_model_experiment_request_client_request_id": {
+ "name": "IDX_model_experiment_request_client_request_id",
+ "columns": [
+ {
+ "expression": "client_request_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"model_experiment_request\".\"client_request_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "model_experiment_request_usage_id_microdollar_usage_id_fk": {
+ "name": "model_experiment_request_usage_id_microdollar_usage_id_fk",
+ "tableFrom": "model_experiment_request",
+ "tableTo": "microdollar_usage",
+ "columnsFrom": [
+ "usage_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "model_experiment_request_variant_version_id_model_experiment_variant_version_id_fk": {
+ "name": "model_experiment_request_variant_version_id_model_experiment_variant_version_id_fk",
+ "tableFrom": "model_experiment_request",
+ "tableTo": "model_experiment_variant_version",
+ "columnsFrom": [
+ "variant_version_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "model_experiment_request_usage_id_created_at_pk": {
+ "name": "model_experiment_request_usage_id_created_at_pk",
+ "columns": [
+ "usage_id",
+ "created_at"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "model_experiment_request_allocation_subject_valid": {
+ "name": "model_experiment_request_allocation_subject_valid",
+ "value": "\"model_experiment_request\".\"allocation_subject\" IN ('user', 'machine', 'ip')"
+ },
+ "model_experiment_request_request_kind_valid": {
+ "name": "model_experiment_request_request_kind_valid",
+ "value": "\"model_experiment_request\".\"request_kind\" IN ('chat_completions', 'messages', 'responses')"
+ },
+ "model_experiment_request_request_body_sha256_format": {
+ "name": "model_experiment_request_request_body_sha256_format",
+ "value": "\"model_experiment_request\".\"request_body_sha256\" ~ '^[0-9a-f]{64}$' OR \"model_experiment_request\".\"request_body_sha256\" IN ('__failed__', '__deleted__')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.model_experiment_variant": {
+ "name": "model_experiment_variant",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "experiment_id": {
+ "name": "experiment_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "label": {
+ "name": "label",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "weight": {
+ "name": "weight",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_model_experiment_variant_experiment_id": {
+ "name": "IDX_model_experiment_variant_experiment_id",
+ "columns": [
+ {
+ "expression": "experiment_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "model_experiment_variant_experiment_id_model_experiment_id_fk": {
+ "name": "model_experiment_variant_experiment_id_model_experiment_id_fk",
+ "tableFrom": "model_experiment_variant",
+ "tableTo": "model_experiment",
+ "columnsFrom": [
+ "experiment_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_model_experiment_variant_experiment_label": {
+ "name": "UQ_model_experiment_variant_experiment_label",
+ "nullsNotDistinct": false,
+ "columns": [
+ "experiment_id",
+ "label"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "model_experiment_variant_weight_positive": {
+ "name": "model_experiment_variant_weight_positive",
+ "value": "\"model_experiment_variant\".\"weight\" > 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.model_experiment_variant_version": {
+ "name": "model_experiment_variant_version",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "variant_id": {
+ "name": "variant_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "upstream": {
+ "name": "upstream",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "encrypted_api_key": {
+ "name": "encrypted_api_key",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "effective_at": {
+ "name": "effective_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_model_experiment_variant_version_variant_effective": {
+ "name": "IDX_model_experiment_variant_version_variant_effective",
+ "columns": [
+ {
+ "expression": "variant_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "effective_at",
+ "isExpression": false,
+ "asc": false,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "model_experiment_variant_version_variant_id_model_experiment_variant_id_fk": {
+ "name": "model_experiment_variant_version_variant_id_model_experiment_variant_id_fk",
+ "tableFrom": "model_experiment_variant_version",
+ "tableTo": "model_experiment_variant",
+ "columnsFrom": [
+ "variant_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "model_experiment_variant_version_created_by_kilocode_users_id_fk": {
+ "name": "model_experiment_variant_version_created_by_kilocode_users_id_fk",
+ "tableFrom": "model_experiment_variant_version",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "created_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.models_by_provider": {
+ "name": "models_by_provider",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "data": {
+ "name": "data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "openrouter": {
+ "name": "openrouter",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "vercel": {
+ "name": "vercel",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.organization_audit_logs": {
+ "name": "organization_audit_logs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "action": {
+ "name": "action",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "actor_id": {
+ "name": "actor_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "actor_email": {
+ "name": "actor_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "actor_name": {
+ "name": "actor_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "message": {
+ "name": "message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_organization_audit_logs_organization_id": {
+ "name": "IDX_organization_audit_logs_organization_id",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_organization_audit_logs_action": {
+ "name": "IDX_organization_audit_logs_action",
+ "columns": [
+ {
+ "expression": "action",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_organization_audit_logs_actor_id": {
+ "name": "IDX_organization_audit_logs_actor_id",
+ "columns": [
+ {
+ "expression": "actor_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_organization_audit_logs_created_at": {
+ "name": "IDX_organization_audit_logs_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.organization_invitations": {
+ "name": "organization_invitations",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "invited_by": {
+ "name": "invited_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "accepted_at": {
+ "name": "accepted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_organization_invitations_token": {
+ "name": "UQ_organization_invitations_token",
+ "columns": [
+ {
+ "expression": "token",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_organization_invitations_org_id": {
+ "name": "IDX_organization_invitations_org_id",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_organization_invitations_email": {
+ "name": "IDX_organization_invitations_email",
+ "columns": [
+ {
+ "expression": "email",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_organization_invitations_expires_at": {
+ "name": "IDX_organization_invitations_expires_at",
+ "columns": [
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.organization_membership_removals": {
+ "name": "organization_membership_removals",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "removed_at": {
+ "name": "removed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "removed_by": {
+ "name": "removed_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "previous_role": {
+ "name": "previous_role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "IDX_org_membership_removals_org_id": {
+ "name": "IDX_org_membership_removals_org_id",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_org_membership_removals_user_id": {
+ "name": "IDX_org_membership_removals_user_id",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_org_membership_removals_org_user": {
+ "name": "UQ_org_membership_removals_org_user",
+ "nullsNotDistinct": false,
+ "columns": [
+ "organization_id",
+ "kilo_user_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.organization_memberships": {
+ "name": "organization_memberships",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "joined_at": {
+ "name": "joined_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "invited_by": {
+ "name": "invited_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_organization_memberships_org_id": {
+ "name": "IDX_organization_memberships_org_id",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_organization_memberships_user_id": {
+ "name": "IDX_organization_memberships_user_id",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_organization_memberships_org_user": {
+ "name": "UQ_organization_memberships_org_user",
+ "nullsNotDistinct": false,
+ "columns": [
+ "organization_id",
+ "kilo_user_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.organization_seats_purchases": {
+ "name": "organization_seats_purchases",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "subscription_stripe_id": {
+ "name": "subscription_stripe_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "seat_count": {
+ "name": "seat_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "amount_usd": {
+ "name": "amount_usd",
+ "type": "numeric",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "subscription_status": {
+ "name": "subscription_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'active'"
+ },
+ "idempotency_key": {
+ "name": "idempotency_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "starts_at": {
+ "name": "starts_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "billing_cycle": {
+ "name": "billing_cycle",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'monthly'"
+ }
+ },
+ "indexes": {
+ "IDX_organization_seats_org_id": {
+ "name": "IDX_organization_seats_org_id",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_organization_seats_expires_at": {
+ "name": "IDX_organization_seats_expires_at",
+ "columns": [
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_organization_seats_created_at": {
+ "name": "IDX_organization_seats_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_organization_seats_updated_at": {
+ "name": "IDX_organization_seats_updated_at",
+ "columns": [
+ {
+ "expression": "updated_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_organization_seats_starts_at": {
+ "name": "IDX_organization_seats_starts_at",
+ "columns": [
+ {
+ "expression": "starts_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_organization_seats_idempotency_key": {
+ "name": "UQ_organization_seats_idempotency_key",
+ "nullsNotDistinct": false,
+ "columns": [
+ "idempotency_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.organization_user_limits": {
+ "name": "organization_user_limits",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "limit_type": {
+ "name": "limit_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "microdollar_limit": {
+ "name": "microdollar_limit",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_organization_user_limits_org_id": {
+ "name": "IDX_organization_user_limits_org_id",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_organization_user_limits_user_id": {
+ "name": "IDX_organization_user_limits_user_id",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_organization_user_limits_org_user": {
+ "name": "UQ_organization_user_limits_org_user",
+ "nullsNotDistinct": false,
+ "columns": [
+ "organization_id",
+ "kilo_user_id",
+ "limit_type"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.organization_user_usage": {
+ "name": "organization_user_usage",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "usage_date": {
+ "name": "usage_date",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "limit_type": {
+ "name": "limit_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "microdollar_usage": {
+ "name": "microdollar_usage",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_organization_user_daily_usage_org_id": {
+ "name": "IDX_organization_user_daily_usage_org_id",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_organization_user_daily_usage_user_id": {
+ "name": "IDX_organization_user_daily_usage_user_id",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_organization_user_daily_usage_org_user_date": {
+ "name": "UQ_organization_user_daily_usage_org_user_date",
+ "nullsNotDistinct": false,
+ "columns": [
+ "organization_id",
+ "kilo_user_id",
+ "limit_type",
+ "usage_date"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.organizations": {
+ "name": "organizations",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "microdollars_used": {
+ "name": "microdollars_used",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0'"
+ },
+ "microdollars_balance": {
+ "name": "microdollars_balance",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0'"
+ },
+ "total_microdollars_acquired": {
+ "name": "total_microdollars_acquired",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'0'"
+ },
+ "next_credit_expiration_at": {
+ "name": "next_credit_expiration_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_customer_id": {
+ "name": "stripe_customer_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "auto_top_up_enabled": {
+ "name": "auto_top_up_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "settings": {
+ "name": "settings",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "seat_count": {
+ "name": "seat_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "require_seats": {
+ "name": "require_seats",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_by_kilo_user_id": {
+ "name": "created_by_kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sso_domain": {
+ "name": "sso_domain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "plan": {
+ "name": "plan",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'teams'"
+ },
+ "free_trial_end_at": {
+ "name": "free_trial_end_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "company_domain": {
+ "name": "company_domain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "IDX_organizations_sso_domain": {
+ "name": "IDX_organizations_sso_domain",
+ "columns": [
+ {
+ "expression": "sso_domain",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "organizations_name_not_empty_check": {
+ "name": "organizations_name_not_empty_check",
+ "value": "length(trim(\"organizations\".\"name\")) > 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.organization_modes": {
+ "name": "organization_modes",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ }
+ },
+ "indexes": {
+ "IDX_organization_modes_organization_id": {
+ "name": "IDX_organization_modes_organization_id",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_organization_modes_org_id_slug": {
+ "name": "UQ_organization_modes_org_id_slug",
+ "nullsNotDistinct": false,
+ "columns": [
+ "organization_id",
+ "slug"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.payment_methods": {
+ "name": "payment_methods",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "stripe_fingerprint": {
+ "name": "stripe_fingerprint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "stripe_id": {
+ "name": "stripe_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "last4": {
+ "name": "last4",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "brand": {
+ "name": "brand",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "address_line1": {
+ "name": "address_line1",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "address_line2": {
+ "name": "address_line2",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "address_city": {
+ "name": "address_city",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "address_state": {
+ "name": "address_state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "address_zip": {
+ "name": "address_zip",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "address_country": {
+ "name": "address_country",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "three_d_secure_supported": {
+ "name": "three_d_secure_supported",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "funding": {
+ "name": "funding",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "regulated_status": {
+ "name": "regulated_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "address_line1_check_status": {
+ "name": "address_line1_check_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "postal_code_check_status": {
+ "name": "postal_code_check_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "http_x_forwarded_for": {
+ "name": "http_x_forwarded_for",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "http_x_vercel_ip_city": {
+ "name": "http_x_vercel_ip_city",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "http_x_vercel_ip_country": {
+ "name": "http_x_vercel_ip_country",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "http_x_vercel_ip_latitude": {
+ "name": "http_x_vercel_ip_latitude",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "http_x_vercel_ip_longitude": {
+ "name": "http_x_vercel_ip_longitude",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "http_x_vercel_ja4_digest": {
+ "name": "http_x_vercel_ja4_digest",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "eligible_for_free_credits": {
+ "name": "eligible_for_free_credits",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_data": {
+ "name": "stripe_data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "IDX_d7d7fb15569674aaadcfbc0428": {
+ "name": "IDX_d7d7fb15569674aaadcfbc0428",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_e1feb919d0ab8a36381d5d5138": {
+ "name": "IDX_e1feb919d0ab8a36381d5d5138",
+ "columns": [
+ {
+ "expression": "stripe_fingerprint",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_payment_methods_organization_id": {
+ "name": "IDX_payment_methods_organization_id",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_29df1b0403df5792c96bbbfdbe6": {
+ "name": "UQ_29df1b0403df5792c96bbbfdbe6",
+ "nullsNotDistinct": false,
+ "columns": [
+ "user_id",
+ "stripe_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.pending_impact_sale_reversals": {
+ "name": "pending_impact_sale_reversals",
+ "schema": "",
+ "columns": {
+ "stripe_charge_id": {
+ "name": "stripe_charge_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "dispute_id": {
+ "name": "dispute_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "amount": {
+ "name": "amount",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "currency": {
+ "name": "currency",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_date": {
+ "name": "event_date",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "attempt_count": {
+ "name": "attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "last_attempt_at": {
+ "name": "last_attempt_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "pending_impact_sale_reversals_attempt_count_non_negative_check": {
+ "name": "pending_impact_sale_reversals_attempt_count_non_negative_check",
+ "value": "\"pending_impact_sale_reversals\".\"attempt_count\" >= 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.platform_integrations": {
+ "name": "platform_integrations",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "owned_by_organization_id": {
+ "name": "owned_by_organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owned_by_user_id": {
+ "name": "owned_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by_user_id": {
+ "name": "created_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "platform": {
+ "name": "platform",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "integration_type": {
+ "name": "integration_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "platform_installation_id": {
+ "name": "platform_installation_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "platform_account_id": {
+ "name": "platform_account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "platform_account_login": {
+ "name": "platform_account_login",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "permissions": {
+ "name": "permissions",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scopes": {
+ "name": "scopes",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "repository_access": {
+ "name": "repository_access",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "repositories": {
+ "name": "repositories",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "repositories_synced_at": {
+ "name": "repositories_synced_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "auth_invalid_at": {
+ "name": "auth_invalid_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "auth_invalid_reason": {
+ "name": "auth_invalid_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "kilo_requester_user_id": {
+ "name": "kilo_requester_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "platform_requester_account_id": {
+ "name": "platform_requester_account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "integration_status": {
+ "name": "integration_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "suspended_at": {
+ "name": "suspended_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "suspended_by": {
+ "name": "suspended_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "github_app_type": {
+ "name": "github_app_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'standard'"
+ },
+ "installed_at": {
+ "name": "installed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_platform_integrations_owned_by_org_platform_inst": {
+ "name": "UQ_platform_integrations_owned_by_org_platform_inst",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "platform",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "platform_installation_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"platform_integrations\".\"owned_by_organization_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_platform_integrations_owned_by_user_platform_inst": {
+ "name": "UQ_platform_integrations_owned_by_user_platform_inst",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "platform",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "platform_installation_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"platform_integrations\".\"owned_by_user_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_platform_integrations_slack_platform_inst": {
+ "name": "UQ_platform_integrations_slack_platform_inst",
+ "columns": [
+ {
+ "expression": "platform",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "platform_installation_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"platform_integrations\".\"platform\" = 'slack' AND \"platform_integrations\".\"platform_installation_id\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_platform_integrations_linear_platform_inst": {
+ "name": "UQ_platform_integrations_linear_platform_inst",
+ "columns": [
+ {
+ "expression": "platform",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "platform_installation_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"platform_integrations\".\"platform\" = 'linear' AND \"platform_integrations\".\"platform_installation_id\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_platform_integrations_owned_by_org_id": {
+ "name": "IDX_platform_integrations_owned_by_org_id",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_platform_integrations_owned_by_user_id": {
+ "name": "IDX_platform_integrations_owned_by_user_id",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_platform_integrations_platform_inst_id": {
+ "name": "IDX_platform_integrations_platform_inst_id",
+ "columns": [
+ {
+ "expression": "platform_installation_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_platform_integrations_platform": {
+ "name": "IDX_platform_integrations_platform",
+ "columns": [
+ {
+ "expression": "platform",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_platform_integrations_owned_by_org_platform": {
+ "name": "IDX_platform_integrations_owned_by_org_platform",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "platform",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_platform_integrations_owned_by_user_platform": {
+ "name": "IDX_platform_integrations_owned_by_user_platform",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "platform",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_platform_integrations_integration_status": {
+ "name": "IDX_platform_integrations_integration_status",
+ "columns": [
+ {
+ "expression": "integration_status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_platform_integrations_kilo_requester": {
+ "name": "IDX_platform_integrations_kilo_requester",
+ "columns": [
+ {
+ "expression": "platform",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "kilo_requester_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "integration_status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_platform_integrations_platform_requester": {
+ "name": "IDX_platform_integrations_platform_requester",
+ "columns": [
+ {
+ "expression": "platform",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "platform_requester_account_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "integration_status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "platform_integrations_owned_by_organization_id_organizations_id_fk": {
+ "name": "platform_integrations_owned_by_organization_id_organizations_id_fk",
+ "tableFrom": "platform_integrations",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "owned_by_organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "platform_integrations_owned_by_user_id_kilocode_users_id_fk": {
+ "name": "platform_integrations_owned_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "platform_integrations",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "owned_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "platform_integrations_owner_check": {
+ "name": "platform_integrations_owner_check",
+ "value": "(\n (\"platform_integrations\".\"owned_by_user_id\" IS NOT NULL AND \"platform_integrations\".\"owned_by_organization_id\" IS NULL) OR\n (\"platform_integrations\".\"owned_by_user_id\" IS NULL AND \"platform_integrations\".\"owned_by_organization_id\" IS NOT NULL)\n )"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.referral_code_usages": {
+ "name": "referral_code_usages",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "referring_kilo_user_id": {
+ "name": "referring_kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "redeeming_kilo_user_id": {
+ "name": "redeeming_kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "code": {
+ "name": "code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "amount_usd": {
+ "name": "amount_usd",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "paid_at": {
+ "name": "paid_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_referral_code_usages_redeeming_kilo_user_id": {
+ "name": "IDX_referral_code_usages_redeeming_kilo_user_id",
+ "columns": [
+ {
+ "expression": "redeeming_kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_referral_code_usages_redeeming_user_id_code": {
+ "name": "UQ_referral_code_usages_redeeming_user_id_code",
+ "nullsNotDistinct": false,
+ "columns": [
+ "redeeming_kilo_user_id",
+ "referring_kilo_user_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.referral_codes": {
+ "name": "referral_codes",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "code": {
+ "name": "code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "max_redemptions": {
+ "name": "max_redemptions",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 10
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_referral_codes_kilo_user_id": {
+ "name": "UQ_referral_codes_kilo_user_id",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_referral_codes_code": {
+ "name": "IDX_referral_codes_code",
+ "columns": [
+ {
+ "expression": "code",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.security_advisor_check_catalog": {
+ "name": "security_advisor_check_catalog",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "check_id": {
+ "name": "check_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "severity": {
+ "name": "severity",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "explanation": {
+ "name": "explanation",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "risk": {
+ "name": "risk",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "security_advisor_check_catalog_check_id_unique": {
+ "name": "security_advisor_check_catalog_check_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "check_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "security_advisor_check_catalog_severity_check": {
+ "name": "security_advisor_check_catalog_severity_check",
+ "value": "\"security_advisor_check_catalog\".\"severity\" in ('critical', 'warn', 'info')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.security_advisor_content": {
+ "name": "security_advisor_content",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "security_advisor_content_key_unique": {
+ "name": "security_advisor_content_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.security_advisor_kiloclaw_coverage": {
+ "name": "security_advisor_kiloclaw_coverage",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "area": {
+ "name": "area",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "summary": {
+ "name": "summary",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "detail": {
+ "name": "detail",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "match_check_ids": {
+ "name": "match_check_ids",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::text[]"
+ },
+ "is_active": {
+ "name": "is_active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "security_advisor_kiloclaw_coverage_area_unique": {
+ "name": "security_advisor_kiloclaw_coverage_area_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "area"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.security_advisor_scans": {
+ "name": "security_advisor_scans",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_platform": {
+ "name": "source_platform",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source_method": {
+ "name": "source_method",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "plugin_version": {
+ "name": "plugin_version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "openclaw_version": {
+ "name": "openclaw_version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "public_ip": {
+ "name": "public_ip",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "findings_critical": {
+ "name": "findings_critical",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "findings_warn": {
+ "name": "findings_warn",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "findings_info": {
+ "name": "findings_info",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "idx_security_advisor_scans_user_created_at": {
+ "name": "idx_security_advisor_scans_user_created_at",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_advisor_scans_created_at": {
+ "name": "idx_security_advisor_scans_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_advisor_scans_platform": {
+ "name": "idx_security_advisor_scans_platform",
+ "columns": [
+ {
+ "expression": "source_platform",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.security_agent_commands": {
+ "name": "security_agent_commands",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "command_type": {
+ "name": "command_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "origin": {
+ "name": "origin",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owned_by_organization_id": {
+ "name": "owned_by_organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owned_by_user_id": {
+ "name": "owned_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "finding_id": {
+ "name": "finding_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "repo_full_name": {
+ "name": "repo_full_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'accepted'"
+ },
+ "result_code": {
+ "name": "result_code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "result_metadata": {
+ "name": "result_metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error_redacted": {
+ "name": "last_error_redacted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "accepted_at": {
+ "name": "accepted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "idx_security_agent_commands_org_created": {
+ "name": "idx_security_agent_commands_org_created",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": false,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_agent_commands_user_created": {
+ "name": "idx_security_agent_commands_user_created",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": false,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_agent_commands_status_updated": {
+ "name": "idx_security_agent_commands_status_updated",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "updated_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_agent_commands_finding_created": {
+ "name": "idx_security_agent_commands_finding_created",
+ "columns": [
+ {
+ "expression": "finding_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": false,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "security_agent_commands_owned_by_organization_id_organizations_id_fk": {
+ "name": "security_agent_commands_owned_by_organization_id_organizations_id_fk",
+ "tableFrom": "security_agent_commands",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "owned_by_organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "security_agent_commands_owned_by_user_id_kilocode_users_id_fk": {
+ "name": "security_agent_commands_owned_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "security_agent_commands",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "owned_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "security_agent_commands_finding_id_security_findings_id_fk": {
+ "name": "security_agent_commands_finding_id_security_findings_id_fk",
+ "tableFrom": "security_agent_commands",
+ "tableTo": "security_findings",
+ "columnsFrom": [
+ "finding_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "security_agent_commands_owner_check": {
+ "name": "security_agent_commands_owner_check",
+ "value": "(\n (\"security_agent_commands\".\"owned_by_user_id\" IS NOT NULL AND \"security_agent_commands\".\"owned_by_organization_id\" IS NULL) OR\n (\"security_agent_commands\".\"owned_by_user_id\" IS NULL AND \"security_agent_commands\".\"owned_by_organization_id\" IS NOT NULL)\n )"
+ },
+ "security_agent_commands_type_check": {
+ "name": "security_agent_commands_type_check",
+ "value": "\"security_agent_commands\".\"command_type\" IN ('sync', 'dismiss_finding', 'start_analysis', 'apply_auto_remediation')"
+ },
+ "security_agent_commands_origin_check": {
+ "name": "security_agent_commands_origin_check",
+ "value": "\"security_agent_commands\".\"origin\" IN ('manual', 'dashboard_refresh', 'enable_initial_sync', 'settings_include_existing')"
+ },
+ "security_agent_commands_status_check": {
+ "name": "security_agent_commands_status_check",
+ "value": "\"security_agent_commands\".\"status\" IN ('accepted', 'running', 'succeeded', 'failed', 'no_op')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.security_agent_repository_sync_state": {
+ "name": "security_agent_repository_sync_state",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "owned_by_organization_id": {
+ "name": "owned_by_organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owned_by_user_id": {
+ "name": "owned_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "repo_full_name": {
+ "name": "repo_full_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_attempted_at": {
+ "name": "last_attempted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_succeeded_at": {
+ "name": "last_succeeded_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_failure_code": {
+ "name": "last_failure_code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_security_agent_repository_sync_state_org_repo": {
+ "name": "UQ_security_agent_repository_sync_state_org_repo",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "repo_full_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"security_agent_repository_sync_state\".\"owned_by_organization_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_security_agent_repository_sync_state_user_repo": {
+ "name": "UQ_security_agent_repository_sync_state_user_repo",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "repo_full_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"security_agent_repository_sync_state\".\"owned_by_user_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "security_agent_repository_sync_state_owned_by_organization_id_organizations_id_fk": {
+ "name": "security_agent_repository_sync_state_owned_by_organization_id_organizations_id_fk",
+ "tableFrom": "security_agent_repository_sync_state",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "owned_by_organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "security_agent_repository_sync_state_owned_by_user_id_kilocode_users_id_fk": {
+ "name": "security_agent_repository_sync_state_owned_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "security_agent_repository_sync_state",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "owned_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "security_agent_repository_sync_state_owner_check": {
+ "name": "security_agent_repository_sync_state_owner_check",
+ "value": "(\n (\"security_agent_repository_sync_state\".\"owned_by_user_id\" IS NOT NULL AND \"security_agent_repository_sync_state\".\"owned_by_organization_id\" IS NULL) OR\n (\"security_agent_repository_sync_state\".\"owned_by_user_id\" IS NULL AND \"security_agent_repository_sync_state\".\"owned_by_organization_id\" IS NOT NULL)\n )"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.security_analysis_owner_state": {
+ "name": "security_analysis_owner_state",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "owned_by_organization_id": {
+ "name": "owned_by_organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owned_by_user_id": {
+ "name": "owned_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "auto_analysis_enabled_at": {
+ "name": "auto_analysis_enabled_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "blocked_until": {
+ "name": "blocked_until",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "block_reason": {
+ "name": "block_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "consecutive_actor_resolution_failures": {
+ "name": "consecutive_actor_resolution_failures",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "last_actor_resolution_failure_at": {
+ "name": "last_actor_resolution_failure_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_security_analysis_owner_state_org_owner": {
+ "name": "UQ_security_analysis_owner_state_org_owner",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"security_analysis_owner_state\".\"owned_by_organization_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_security_analysis_owner_state_user_owner": {
+ "name": "UQ_security_analysis_owner_state_user_owner",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"security_analysis_owner_state\".\"owned_by_user_id\" is not null",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "security_analysis_owner_state_owned_by_organization_id_organizations_id_fk": {
+ "name": "security_analysis_owner_state_owned_by_organization_id_organizations_id_fk",
+ "tableFrom": "security_analysis_owner_state",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "owned_by_organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "security_analysis_owner_state_owned_by_user_id_kilocode_users_id_fk": {
+ "name": "security_analysis_owner_state_owned_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "security_analysis_owner_state",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "owned_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "security_analysis_owner_state_owner_check": {
+ "name": "security_analysis_owner_state_owner_check",
+ "value": "(\n (\"security_analysis_owner_state\".\"owned_by_user_id\" IS NOT NULL AND \"security_analysis_owner_state\".\"owned_by_organization_id\" IS NULL) OR\n (\"security_analysis_owner_state\".\"owned_by_user_id\" IS NULL AND \"security_analysis_owner_state\".\"owned_by_organization_id\" IS NOT NULL)\n )"
+ },
+ "security_analysis_owner_state_block_reason_check": {
+ "name": "security_analysis_owner_state_block_reason_check",
+ "value": "\"security_analysis_owner_state\".\"block_reason\" IS NULL OR \"security_analysis_owner_state\".\"block_reason\" IN ('INSUFFICIENT_CREDITS', 'ACTOR_RESOLUTION_FAILED', 'OPERATOR_PAUSE')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.security_analysis_queue": {
+ "name": "security_analysis_queue",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "finding_id": {
+ "name": "finding_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owned_by_organization_id": {
+ "name": "owned_by_organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owned_by_user_id": {
+ "name": "owned_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "queue_status": {
+ "name": "queue_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "severity_rank": {
+ "name": "severity_rank",
+ "type": "smallint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "queued_at": {
+ "name": "queued_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "claimed_at": {
+ "name": "claimed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "claimed_by_job_id": {
+ "name": "claimed_by_job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "claim_token": {
+ "name": "claim_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "attempt_count": {
+ "name": "attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "reopen_requeue_count": {
+ "name": "reopen_requeue_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "next_retry_at": {
+ "name": "next_retry_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "failure_code": {
+ "name": "failure_code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error_redacted": {
+ "name": "last_error_redacted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_security_analysis_queue_finding_id": {
+ "name": "UQ_security_analysis_queue_finding_id",
+ "columns": [
+ {
+ "expression": "finding_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_analysis_queue_claim_path_org": {
+ "name": "idx_security_analysis_queue_claim_path_org",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "coalesce(\"next_retry_at\", '-infinity'::timestamptz)",
+ "asc": true,
+ "isExpression": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "severity_rank",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "queued_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"security_analysis_queue\".\"queue_status\" = 'queued'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_analysis_queue_claim_path_user": {
+ "name": "idx_security_analysis_queue_claim_path_user",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "coalesce(\"next_retry_at\", '-infinity'::timestamptz)",
+ "asc": true,
+ "isExpression": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "severity_rank",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "queued_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"security_analysis_queue\".\"queue_status\" = 'queued'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_analysis_queue_in_flight_org": {
+ "name": "idx_security_analysis_queue_in_flight_org",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "queue_status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "claimed_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"security_analysis_queue\".\"queue_status\" IN ('pending', 'running')",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_analysis_queue_in_flight_user": {
+ "name": "idx_security_analysis_queue_in_flight_user",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "queue_status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "claimed_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"security_analysis_queue\".\"queue_status\" IN ('pending', 'running')",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_analysis_queue_lag_dashboards": {
+ "name": "idx_security_analysis_queue_lag_dashboards",
+ "columns": [
+ {
+ "expression": "queued_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"security_analysis_queue\".\"queue_status\" = 'queued'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_analysis_queue_pending_reconciliation": {
+ "name": "idx_security_analysis_queue_pending_reconciliation",
+ "columns": [
+ {
+ "expression": "claimed_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"security_analysis_queue\".\"queue_status\" = 'pending'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_analysis_queue_running_reconciliation": {
+ "name": "idx_security_analysis_queue_running_reconciliation",
+ "columns": [
+ {
+ "expression": "updated_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"security_analysis_queue\".\"queue_status\" = 'running'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_analysis_queue_failure_trend": {
+ "name": "idx_security_analysis_queue_failure_trend",
+ "columns": [
+ {
+ "expression": "failure_code",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "updated_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"security_analysis_queue\".\"failure_code\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "security_analysis_queue_finding_id_security_findings_id_fk": {
+ "name": "security_analysis_queue_finding_id_security_findings_id_fk",
+ "tableFrom": "security_analysis_queue",
+ "tableTo": "security_findings",
+ "columnsFrom": [
+ "finding_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "security_analysis_queue_owned_by_organization_id_organizations_id_fk": {
+ "name": "security_analysis_queue_owned_by_organization_id_organizations_id_fk",
+ "tableFrom": "security_analysis_queue",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "owned_by_organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "security_analysis_queue_owned_by_user_id_kilocode_users_id_fk": {
+ "name": "security_analysis_queue_owned_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "security_analysis_queue",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "owned_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "security_analysis_queue_owner_check": {
+ "name": "security_analysis_queue_owner_check",
+ "value": "(\n (\"security_analysis_queue\".\"owned_by_user_id\" IS NOT NULL AND \"security_analysis_queue\".\"owned_by_organization_id\" IS NULL) OR\n (\"security_analysis_queue\".\"owned_by_user_id\" IS NULL AND \"security_analysis_queue\".\"owned_by_organization_id\" IS NOT NULL)\n )"
+ },
+ "security_analysis_queue_status_check": {
+ "name": "security_analysis_queue_status_check",
+ "value": "\"security_analysis_queue\".\"queue_status\" IN ('queued', 'pending', 'running', 'failed', 'completed')"
+ },
+ "security_analysis_queue_claim_token_required_check": {
+ "name": "security_analysis_queue_claim_token_required_check",
+ "value": "\"security_analysis_queue\".\"queue_status\" NOT IN ('pending', 'running') OR \"security_analysis_queue\".\"claim_token\" IS NOT NULL"
+ },
+ "security_analysis_queue_attempt_count_non_negative_check": {
+ "name": "security_analysis_queue_attempt_count_non_negative_check",
+ "value": "\"security_analysis_queue\".\"attempt_count\" >= 0"
+ },
+ "security_analysis_queue_reopen_requeue_count_non_negative_check": {
+ "name": "security_analysis_queue_reopen_requeue_count_non_negative_check",
+ "value": "\"security_analysis_queue\".\"reopen_requeue_count\" >= 0"
+ },
+ "security_analysis_queue_severity_rank_check": {
+ "name": "security_analysis_queue_severity_rank_check",
+ "value": "\"security_analysis_queue\".\"severity_rank\" IN (0, 1, 2, 3)"
+ },
+ "security_analysis_queue_failure_code_check": {
+ "name": "security_analysis_queue_failure_code_check",
+ "value": "\"security_analysis_queue\".\"failure_code\" IS NULL OR \"security_analysis_queue\".\"failure_code\" IN (\n 'NETWORK_TIMEOUT',\n 'UPSTREAM_5XX',\n 'TEMP_TOKEN_FAILURE',\n 'START_CALL_AMBIGUOUS',\n 'REQUEUE_TEMPORARY_PRECONDITION',\n 'ACTOR_RESOLUTION_FAILED',\n 'GITHUB_TOKEN_UNAVAILABLE',\n 'INVALID_CONFIG',\n 'MISSING_OWNERSHIP',\n 'PERMISSION_DENIED_PERMANENT',\n 'UNSUPPORTED_SEVERITY',\n 'INSUFFICIENT_CREDITS',\n 'STATE_GUARD_REJECTED',\n 'SKIPPED_ALREADY_IN_PROGRESS',\n 'SKIPPED_NO_LONGER_ELIGIBLE',\n 'REOPEN_LOOP_GUARD',\n 'RUN_LOST'\n )"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.security_audit_log": {
+ "name": "security_audit_log",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "owned_by_organization_id": {
+ "name": "owned_by_organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owned_by_user_id": {
+ "name": "owned_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "actor_id": {
+ "name": "actor_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "actor_email": {
+ "name": "actor_email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "actor_name": {
+ "name": "actor_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "actor_type": {
+ "name": "actor_type",
+ "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
+ },
+ "finding_id": {
+ "name": "finding_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "occurred_at": {
+ "name": "occurred_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_occurred_at": {
+ "name": "source_occurred_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "event_key": {
+ "name": "event_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "schema_version": {
+ "name": "schema_version",
+ "type": "smallint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "finding_snapshot": {
+ "name": "finding_snapshot",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_context": {
+ "name": "source_context",
+ "type": "text",
+ "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": {}
+ },
+ "UQ_security_audit_log_org_event_key": {
+ "name": "UQ_security_audit_log_org_event_key",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "event_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"security_audit_log\".\"owned_by_organization_id\" IS NOT NULL AND \"security_audit_log\".\"event_key\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_security_audit_log_user_event_key": {
+ "name": "UQ_security_audit_log_user_event_key",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "event_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"security_audit_log\".\"owned_by_user_id\" IS NOT NULL AND \"security_audit_log\".\"event_key\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_security_audit_log_org_occurred": {
+ "name": "IDX_security_audit_log_org_occurred",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "occurred_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"security_audit_log\".\"owned_by_organization_id\" IS NOT NULL AND \"security_audit_log\".\"occurred_at\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_security_audit_log_user_occurred": {
+ "name": "IDX_security_audit_log_user_occurred",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "occurred_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"security_audit_log\".\"owned_by_user_id\" IS NOT NULL AND \"security_audit_log\".\"occurred_at\" IS NOT NULL",
+ "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.severity_changed', 'security.finding.status_change', 'security.finding.dismissed', 'security.finding.auto_dismissed', 'security.finding.superseded', 'security.finding.analysis_started', 'security.finding.analysis_completed', 'security.finding.analysis_failed', 'security.remediation.queued', 'security.remediation.started', 'security.remediation.pr_opened', 'security.remediation.failed', 'security.remediation.blocked', 'security.remediation.no_changes_needed', 'security.remediation.cancelled', 'security.remediation.retried', 'security.finding.deleted', 'security.config.enabled', 'security.config.disabled', 'security.config.updated', 'security.sync.triggered', 'security.sync.completed', 'security.audit_log.exported', 'security.audit_report.generated')"
+ },
+ "security_audit_log_actor_type_check": {
+ "name": "security_audit_log_actor_type_check",
+ "value": "\"security_audit_log\".\"actor_type\" IN ('customer_user', 'kilo_admin', 'system')"
+ },
+ "security_audit_log_source_context_check": {
+ "name": "security_audit_log_source_context_check",
+ "value": "\"security_audit_log\".\"source_context\" IN ('security_sync', 'web', 'analysis_worker', 'remediation_callback', 'rollout_baseline')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.security_finding_notifications": {
+ "name": "security_finding_notifications",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "finding_id": {
+ "name": "finding_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "recipient_user_id": {
+ "name": "recipient_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'staged'"
+ },
+ "attempt_count": {
+ "name": "attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "next_attempt_at": {
+ "name": "next_attempt_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "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
+ },
+ "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_finding_notifications_finding_recipient_kind": {
+ "name": "uq_security_finding_notifications_finding_recipient_kind",
+ "columns": [
+ {
+ "expression": "finding_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "recipient_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "kind",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_finding_notifications_pending": {
+ "name": "idx_security_finding_notifications_pending",
+ "columns": [
+ {
+ "expression": "next_attempt_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"security_finding_notifications\".\"status\" = 'pending'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_finding_notifications_staged": {
+ "name": "idx_security_finding_notifications_staged",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"security_finding_notifications\".\"status\" = 'staged'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_finding_notifications_finding_id": {
+ "name": "idx_security_finding_notifications_finding_id",
+ "columns": [
+ {
+ "expression": "finding_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_finding_notifications_recipient_user_id": {
+ "name": "idx_security_finding_notifications_recipient_user_id",
+ "columns": [
+ {
+ "expression": "recipient_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "security_finding_notifications_finding_fk": {
+ "name": "security_finding_notifications_finding_fk",
+ "tableFrom": "security_finding_notifications",
+ "tableTo": "security_findings",
+ "columnsFrom": [
+ "finding_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "security_finding_notifications_recipient_fk": {
+ "name": "security_finding_notifications_recipient_fk",
+ "tableFrom": "security_finding_notifications",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "recipient_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "security_finding_notifications_kind_check": {
+ "name": "security_finding_notifications_kind_check",
+ "value": "\"security_finding_notifications\".\"kind\" IN ('new_finding', 'sla_warning', 'sla_breach')"
+ },
+ "security_finding_notifications_status_check": {
+ "name": "security_finding_notifications_status_check",
+ "value": "\"security_finding_notifications\".\"status\" IN ('staged', 'pending', 'sending', 'sent', 'failed', 'cancelled')"
+ },
+ "security_finding_notifications_attempt_count_check": {
+ "name": "security_finding_notifications_attempt_count_check",
+ "value": "\"security_finding_notifications\".\"attempt_count\" >= 0"
+ },
+ "security_finding_notifications_claimed_at_check": {
+ "name": "security_finding_notifications_claimed_at_check",
+ "value": "(\n (\"security_finding_notifications\".\"status\" = 'sending' AND \"security_finding_notifications\".\"claimed_at\" IS NOT NULL) OR\n (\"security_finding_notifications\".\"status\" <> 'sending' AND \"security_finding_notifications\".\"claimed_at\" IS NULL)\n )"
+ },
+ "security_finding_notifications_sent_at_check": {
+ "name": "security_finding_notifications_sent_at_check",
+ "value": "(\n (\"security_finding_notifications\".\"status\" = 'sent' AND \"security_finding_notifications\".\"sent_at\" IS NOT NULL) OR\n (\"security_finding_notifications\".\"status\" <> 'sent' AND \"security_finding_notifications\".\"sent_at\" IS NULL)\n )"
+ },
+ "security_finding_notifications_error_message_length_check": {
+ "name": "security_finding_notifications_error_message_length_check",
+ "value": "\"security_finding_notifications\".\"error_message\" IS NULL OR length(\"security_finding_notifications\".\"error_message\") <= 500"
+ }
+ },
+ "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": {
+ "uq_security_findings_user_source": {
+ "name": "uq_security_findings_user_source",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "repo_full_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "source",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "source_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"security_findings\".\"owned_by_user_id\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "uq_security_findings_org_source": {
+ "name": "uq_security_findings_org_source",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "repo_full_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "source",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "source_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"security_findings\".\"owned_by_organization_id\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "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": {},
+ "policies": {},
+ "checkConstraints": {
+ "security_findings_owner_check": {
+ "name": "security_findings_owner_check",
+ "value": "(\n (\"security_findings\".\"owned_by_user_id\" IS NOT NULL AND \"security_findings\".\"owned_by_organization_id\" IS NULL) OR\n (\"security_findings\".\"owned_by_user_id\" IS NULL AND \"security_findings\".\"owned_by_organization_id\" IS NOT NULL)\n )"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.security_remediation_attempts": {
+ "name": "security_remediation_attempts",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "remediation_id": {
+ "name": "remediation_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "finding_id": {
+ "name": "finding_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owned_by_organization_id": {
+ "name": "owned_by_organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owned_by_user_id": {
+ "name": "owned_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "repo_full_name": {
+ "name": "repo_full_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "origin": {
+ "name": "origin",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'queued'"
+ },
+ "attempt_number": {
+ "name": "attempt_number",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "retry_of_attempt_id": {
+ "name": "retry_of_attempt_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "requested_by_user_id": {
+ "name": "requested_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "analysis_fingerprint": {
+ "name": "analysis_fingerprint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "analysis_completed_at": {
+ "name": "analysis_completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "remediation_model_slug": {
+ "name": "remediation_model_slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "branch_name": {
+ "name": "branch_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cloud_agent_session_id": {
+ "name": "cloud_agent_session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "kilo_session_id": {
+ "name": "kilo_session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "execution_id": {
+ "name": "execution_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "priority": {
+ "name": "priority",
+ "type": "smallint",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 50
+ },
+ "claim_token": {
+ "name": "claim_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "claimed_at": {
+ "name": "claimed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "claimed_by_job_id": {
+ "name": "claimed_by_job_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "launch_attempt_count": {
+ "name": "launch_attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "next_retry_at": {
+ "name": "next_retry_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "callback_attempt_token_hash": {
+ "name": "callback_attempt_token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "failure_code": {
+ "name": "failure_code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "blocked_reason": {
+ "name": "blocked_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_error_redacted": {
+ "name": "last_error_redacted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "structured_result": {
+ "name": "structured_result",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "final_assistant_message": {
+ "name": "final_assistant_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "validation_evidence": {
+ "name": "validation_evidence",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "risk_notes": {
+ "name": "risk_notes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "draft_reason": {
+ "name": "draft_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pr_url": {
+ "name": "pr_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pr_number": {
+ "name": "pr_number",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pr_draft": {
+ "name": "pr_draft",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pr_head_branch": {
+ "name": "pr_head_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pr_base_branch": {
+ "name": "pr_base_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cancellation_requested_at": {
+ "name": "cancellation_requested_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cancellation_requested_by_user_id": {
+ "name": "cancellation_requested_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "queued_at": {
+ "name": "queued_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "launched_at": {
+ "name": "launched_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_security_remediation_attempts_number": {
+ "name": "UQ_security_remediation_attempts_number",
+ "columns": [
+ {
+ "expression": "remediation_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "attempt_number",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_security_remediation_attempts_active_finding": {
+ "name": "UQ_security_remediation_attempts_active_finding",
+ "columns": [
+ {
+ "expression": "finding_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"security_remediation_attempts\".\"status\" IN ('queued', 'launching', 'running')",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_security_remediation_attempts_active_remediation": {
+ "name": "UQ_security_remediation_attempts_active_remediation",
+ "columns": [
+ {
+ "expression": "remediation_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"security_remediation_attempts\".\"status\" IN ('queued', 'launching', 'running')",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_security_remediation_attempts_finding_fingerprint_terminal": {
+ "name": "UQ_security_remediation_attempts_finding_fingerprint_terminal",
+ "columns": [
+ {
+ "expression": "finding_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "analysis_fingerprint",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"security_remediation_attempts\".\"status\" IN ('queued', 'launching', 'running', 'pr_opened')",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_remediation_attempts_org_claim": {
+ "name": "idx_security_remediation_attempts_org_claim",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "coalesce(\"next_retry_at\", '-infinity'::timestamptz)",
+ "asc": true,
+ "isExpression": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "priority",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "queued_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"security_remediation_attempts\".\"status\" = 'queued'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_remediation_attempts_user_claim": {
+ "name": "idx_security_remediation_attempts_user_claim",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "coalesce(\"next_retry_at\", '-infinity'::timestamptz)",
+ "asc": true,
+ "isExpression": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "priority",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "queued_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"security_remediation_attempts\".\"status\" = 'queued'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_remediation_attempts_repo_claim": {
+ "name": "idx_security_remediation_attempts_repo_claim",
+ "columns": [
+ {
+ "expression": "repo_full_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "coalesce(\"next_retry_at\", '-infinity'::timestamptz)",
+ "asc": true,
+ "isExpression": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "priority",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "queued_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"security_remediation_attempts\".\"status\" = 'queued'",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_remediation_attempts_org_inflight": {
+ "name": "idx_security_remediation_attempts_org_inflight",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "claimed_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"security_remediation_attempts\".\"status\" IN ('launching', 'running')",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_remediation_attempts_user_inflight": {
+ "name": "idx_security_remediation_attempts_user_inflight",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "claimed_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"security_remediation_attempts\".\"status\" IN ('launching', 'running')",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_remediation_attempts_repo_inflight": {
+ "name": "idx_security_remediation_attempts_repo_inflight",
+ "columns": [
+ {
+ "expression": "repo_full_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "claimed_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "where": "\"security_remediation_attempts\".\"status\" IN ('launching', 'running')",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_remediation_attempts_cloud_agent_session": {
+ "name": "idx_security_remediation_attempts_cloud_agent_session",
+ "columns": [
+ {
+ "expression": "cloud_agent_session_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_remediation_attempts_finding_fingerprint": {
+ "name": "idx_security_remediation_attempts_finding_fingerprint",
+ "columns": [
+ {
+ "expression": "finding_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "analysis_fingerprint",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "security_remediation_attempts_remediation_id_security_remediations_id_fk": {
+ "name": "security_remediation_attempts_remediation_id_security_remediations_id_fk",
+ "tableFrom": "security_remediation_attempts",
+ "tableTo": "security_remediations",
+ "columnsFrom": [
+ "remediation_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "security_remediation_attempts_finding_id_security_findings_id_fk": {
+ "name": "security_remediation_attempts_finding_id_security_findings_id_fk",
+ "tableFrom": "security_remediation_attempts",
+ "tableTo": "security_findings",
+ "columnsFrom": [
+ "finding_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "security_remediation_attempts_owned_by_organization_id_organizations_id_fk": {
+ "name": "security_remediation_attempts_owned_by_organization_id_organizations_id_fk",
+ "tableFrom": "security_remediation_attempts",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "owned_by_organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "security_remediation_attempts_owned_by_user_id_kilocode_users_id_fk": {
+ "name": "security_remediation_attempts_owned_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "security_remediation_attempts",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "owned_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "security_remediation_attempts_requested_by_user_id_kilocode_users_id_fk": {
+ "name": "security_remediation_attempts_requested_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "security_remediation_attempts",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "requested_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "security_remediation_attempts_cancellation_requested_by_user_id_kilocode_users_id_fk": {
+ "name": "security_remediation_attempts_cancellation_requested_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "security_remediation_attempts",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "cancellation_requested_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "security_remediation_attempts_owner_check": {
+ "name": "security_remediation_attempts_owner_check",
+ "value": "(\n (\"security_remediation_attempts\".\"owned_by_user_id\" IS NOT NULL AND \"security_remediation_attempts\".\"owned_by_organization_id\" IS NULL) OR\n (\"security_remediation_attempts\".\"owned_by_user_id\" IS NULL AND \"security_remediation_attempts\".\"owned_by_organization_id\" IS NOT NULL)\n )"
+ },
+ "security_remediation_attempts_status_check": {
+ "name": "security_remediation_attempts_status_check",
+ "value": "\"security_remediation_attempts\".\"status\" IN ('queued', 'launching', 'running', 'pr_opened', 'failed', 'blocked', 'no_changes_needed', 'cancelled')"
+ },
+ "security_remediation_attempts_origin_check": {
+ "name": "security_remediation_attempts_origin_check",
+ "value": "\"security_remediation_attempts\".\"origin\" IN ('auto_policy', 'bulk_existing', 'manual')"
+ },
+ "security_remediation_attempts_attempt_number_check": {
+ "name": "security_remediation_attempts_attempt_number_check",
+ "value": "\"security_remediation_attempts\".\"attempt_number\" >= 1"
+ },
+ "security_remediation_attempts_launch_attempt_count_check": {
+ "name": "security_remediation_attempts_launch_attempt_count_check",
+ "value": "\"security_remediation_attempts\".\"launch_attempt_count\" >= 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.security_remediations": {
+ "name": "security_remediations",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "owned_by_organization_id": {
+ "name": "owned_by_organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owned_by_user_id": {
+ "name": "owned_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "finding_id": {
+ "name": "finding_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "repo_full_name": {
+ "name": "repo_full_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'queued'"
+ },
+ "latest_attempt_id": {
+ "name": "latest_attempt_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "latest_analysis_fingerprint": {
+ "name": "latest_analysis_fingerprint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "latest_analysis_completed_at": {
+ "name": "latest_analysis_completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pr_url": {
+ "name": "pr_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pr_number": {
+ "name": "pr_number",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pr_draft": {
+ "name": "pr_draft",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pr_head_branch": {
+ "name": "pr_head_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "pr_base_branch": {
+ "name": "pr_base_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "failure_code": {
+ "name": "failure_code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "blocked_reason": {
+ "name": "blocked_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "outcome_summary": {
+ "name": "outcome_summary",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_security_remediations_finding_id": {
+ "name": "UQ_security_remediations_finding_id",
+ "columns": [
+ {
+ "expression": "finding_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_remediations_org_status": {
+ "name": "idx_security_remediations_org_status",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_remediations_user_status": {
+ "name": "idx_security_remediations_user_status",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_remediations_repo_status": {
+ "name": "idx_security_remediations_repo_status",
+ "columns": [
+ {
+ "expression": "repo_full_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_security_remediations_latest_attempt": {
+ "name": "idx_security_remediations_latest_attempt",
+ "columns": [
+ {
+ "expression": "latest_attempt_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "security_remediations_owned_by_organization_id_organizations_id_fk": {
+ "name": "security_remediations_owned_by_organization_id_organizations_id_fk",
+ "tableFrom": "security_remediations",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "owned_by_organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "security_remediations_owned_by_user_id_kilocode_users_id_fk": {
+ "name": "security_remediations_owned_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "security_remediations",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "owned_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "security_remediations_finding_id_security_findings_id_fk": {
+ "name": "security_remediations_finding_id_security_findings_id_fk",
+ "tableFrom": "security_remediations",
+ "tableTo": "security_findings",
+ "columnsFrom": [
+ "finding_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "security_remediations_owner_check": {
+ "name": "security_remediations_owner_check",
+ "value": "(\n (\"security_remediations\".\"owned_by_user_id\" IS NOT NULL AND \"security_remediations\".\"owned_by_organization_id\" IS NULL) OR\n (\"security_remediations\".\"owned_by_user_id\" IS NULL AND \"security_remediations\".\"owned_by_organization_id\" IS NOT NULL)\n )"
+ },
+ "security_remediations_status_check": {
+ "name": "security_remediations_status_check",
+ "value": "\"security_remediations\".\"status\" IN ('queued', 'running', 'pr_opened', 'failed', 'blocked', 'no_changes_needed', 'cancelled')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.shared_cli_sessions": {
+ "name": "shared_cli_sessions",
+ "schema": "",
+ "columns": {
+ "share_id": {
+ "name": "share_id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "session_id": {
+ "name": "session_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "shared_state": {
+ "name": "shared_state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'public'"
+ },
+ "api_conversation_history_blob_url": {
+ "name": "api_conversation_history_blob_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "task_metadata_blob_url": {
+ "name": "task_metadata_blob_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ui_messages_blob_url": {
+ "name": "ui_messages_blob_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "git_state_blob_url": {
+ "name": "git_state_blob_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_shared_cli_sessions_session_id": {
+ "name": "IDX_shared_cli_sessions_session_id",
+ "columns": [
+ {
+ "expression": "session_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_shared_cli_sessions_created_at": {
+ "name": "IDX_shared_cli_sessions_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "shared_cli_sessions_session_id_cli_sessions_session_id_fk": {
+ "name": "shared_cli_sessions_session_id_cli_sessions_session_id_fk",
+ "tableFrom": "shared_cli_sessions",
+ "tableTo": "cli_sessions",
+ "columnsFrom": [
+ "session_id"
+ ],
+ "columnsTo": [
+ "session_id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "shared_cli_sessions_kilo_user_id_kilocode_users_id_fk": {
+ "name": "shared_cli_sessions_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "shared_cli_sessions",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "restrict",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "shared_cli_sessions_shared_state_check": {
+ "name": "shared_cli_sessions_shared_state_check",
+ "value": "\"shared_cli_sessions\".\"shared_state\" IN ('public', 'organization')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.slack_bot_requests": {
+ "name": "slack_bot_requests",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "owned_by_organization_id": {
+ "name": "owned_by_organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owned_by_user_id": {
+ "name": "owned_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "platform_integration_id": {
+ "name": "platform_integration_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "slack_team_id": {
+ "name": "slack_team_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slack_team_name": {
+ "name": "slack_team_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "slack_channel_id": {
+ "name": "slack_channel_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slack_user_id": {
+ "name": "slack_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slack_thread_ts": {
+ "name": "slack_thread_ts",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "event_type": {
+ "name": "event_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_message": {
+ "name": "user_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_message_truncated": {
+ "name": "user_message_truncated",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "error_message": {
+ "name": "error_message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "response_time_ms": {
+ "name": "response_time_ms",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model_used": {
+ "name": "model_used",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "tool_calls_made": {
+ "name": "tool_calls_made",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cloud_agent_session_id": {
+ "name": "cloud_agent_session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "idx_slack_bot_requests_created_at": {
+ "name": "idx_slack_bot_requests_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_slack_bot_requests_slack_team_id": {
+ "name": "idx_slack_bot_requests_slack_team_id",
+ "columns": [
+ {
+ "expression": "slack_team_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_slack_bot_requests_owned_by_org_id": {
+ "name": "idx_slack_bot_requests_owned_by_org_id",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_slack_bot_requests_owned_by_user_id": {
+ "name": "idx_slack_bot_requests_owned_by_user_id",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_slack_bot_requests_status": {
+ "name": "idx_slack_bot_requests_status",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_slack_bot_requests_event_type": {
+ "name": "idx_slack_bot_requests_event_type",
+ "columns": [
+ {
+ "expression": "event_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_slack_bot_requests_team_created": {
+ "name": "idx_slack_bot_requests_team_created",
+ "columns": [
+ {
+ "expression": "slack_team_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "slack_bot_requests_owned_by_organization_id_organizations_id_fk": {
+ "name": "slack_bot_requests_owned_by_organization_id_organizations_id_fk",
+ "tableFrom": "slack_bot_requests",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "owned_by_organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "slack_bot_requests_owned_by_user_id_kilocode_users_id_fk": {
+ "name": "slack_bot_requests_owned_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "slack_bot_requests",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "owned_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "slack_bot_requests_platform_integration_id_platform_integrations_id_fk": {
+ "name": "slack_bot_requests_platform_integration_id_platform_integrations_id_fk",
+ "tableFrom": "slack_bot_requests",
+ "tableTo": "platform_integrations",
+ "columnsFrom": [
+ "platform_integration_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "slack_bot_requests_owner_check": {
+ "name": "slack_bot_requests_owner_check",
+ "value": "(\n (\"slack_bot_requests\".\"owned_by_user_id\" IS NOT NULL AND \"slack_bot_requests\".\"owned_by_organization_id\" IS NULL) OR\n (\"slack_bot_requests\".\"owned_by_user_id\" IS NULL AND \"slack_bot_requests\".\"owned_by_organization_id\" IS NOT NULL) OR\n (\"slack_bot_requests\".\"owned_by_user_id\" IS NULL AND \"slack_bot_requests\".\"owned_by_organization_id\" IS NULL)\n )"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.source_embeddings": {
+ "name": "source_embeddings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "embedding": {
+ "name": "embedding",
+ "type": "vector(1536)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "file_path": {
+ "name": "file_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "file_hash": {
+ "name": "file_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "start_line": {
+ "name": "start_line",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "end_line": {
+ "name": "end_line",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "git_branch": {
+ "name": "git_branch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'main'"
+ },
+ "is_base_branch": {
+ "name": "is_base_branch",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_source_embeddings_organization_id": {
+ "name": "IDX_source_embeddings_organization_id",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_source_embeddings_kilo_user_id": {
+ "name": "IDX_source_embeddings_kilo_user_id",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_source_embeddings_project_id": {
+ "name": "IDX_source_embeddings_project_id",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_source_embeddings_created_at": {
+ "name": "IDX_source_embeddings_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_source_embeddings_updated_at": {
+ "name": "IDX_source_embeddings_updated_at",
+ "columns": [
+ {
+ "expression": "updated_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_source_embeddings_file_path_lower": {
+ "name": "IDX_source_embeddings_file_path_lower",
+ "columns": [
+ {
+ "expression": "LOWER(\"file_path\")",
+ "asc": true,
+ "isExpression": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_source_embeddings_git_branch": {
+ "name": "IDX_source_embeddings_git_branch",
+ "columns": [
+ {
+ "expression": "git_branch",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_source_embeddings_org_project_branch": {
+ "name": "IDX_source_embeddings_org_project_branch",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "git_branch",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "source_embeddings_organization_id_organizations_id_fk": {
+ "name": "source_embeddings_organization_id_organizations_id_fk",
+ "tableFrom": "source_embeddings",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "source_embeddings_kilo_user_id_kilocode_users_id_fk": {
+ "name": "source_embeddings_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "source_embeddings",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_source_embeddings_org_project_branch_file_lines": {
+ "name": "UQ_source_embeddings_org_project_branch_file_lines",
+ "nullsNotDistinct": false,
+ "columns": [
+ "organization_id",
+ "project_id",
+ "git_branch",
+ "file_path",
+ "start_line",
+ "end_line"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.stripe_dispute_actions": {
+ "name": "stripe_dispute_actions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "case_id": {
+ "name": "case_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action_type": {
+ "name": "action_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_key": {
+ "name": "target_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'queued'"
+ },
+ "attempt_count": {
+ "name": "attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "next_retry_at": {
+ "name": "next_retry_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "claimed_at": {
+ "name": "claimed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_attempt_at": {
+ "name": "last_attempt_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "terminal_at": {
+ "name": "terminal_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "result_code": {
+ "name": "result_code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "result_reference_id": {
+ "name": "result_reference_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "failure_context": {
+ "name": "failure_context",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_stripe_dispute_actions_case_id": {
+ "name": "IDX_stripe_dispute_actions_case_id",
+ "columns": [
+ {
+ "expression": "case_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_stripe_dispute_actions_claim_path": {
+ "name": "IDX_stripe_dispute_actions_claim_path",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "coalesce(\"next_retry_at\", '-infinity'::timestamptz)",
+ "asc": true,
+ "isExpression": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "stripe_dispute_actions_case_id_stripe_dispute_cases_id_fk": {
+ "name": "stripe_dispute_actions_case_id_stripe_dispute_cases_id_fk",
+ "tableFrom": "stripe_dispute_actions",
+ "tableTo": "stripe_dispute_cases",
+ "columnsFrom": [
+ "case_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "restrict",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_stripe_dispute_actions_case_type_target": {
+ "name": "UQ_stripe_dispute_actions_case_type_target",
+ "nullsNotDistinct": false,
+ "columns": [
+ "case_id",
+ "action_type",
+ "target_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "stripe_dispute_actions_action_type_check": {
+ "name": "stripe_dispute_actions_action_type_check",
+ "value": "\"stripe_dispute_actions\".\"action_type\" IN ('stripe_acceptance', 'user_block', 'auto_top_up_disable', 'credit_balance_reset', 'subscription_cancellation', 'access_termination', 'kiloclaw_suspension')"
+ },
+ "stripe_dispute_actions_status_check": {
+ "name": "stripe_dispute_actions_status_check",
+ "value": "\"stripe_dispute_actions\".\"status\" IN ('queued', 'processing', 'completed', 'failed', 'skipped')"
+ },
+ "stripe_dispute_actions_attempt_count_non_negative_check": {
+ "name": "stripe_dispute_actions_attempt_count_non_negative_check",
+ "value": "\"stripe_dispute_actions\".\"attempt_count\" >= 0"
+ },
+ "stripe_dispute_actions_target_key_not_empty_check": {
+ "name": "stripe_dispute_actions_target_key_not_empty_check",
+ "value": "length(\"stripe_dispute_actions\".\"target_key\") > 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.stripe_dispute_cases": {
+ "name": "stripe_dispute_cases",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "stripe_dispute_id": {
+ "name": "stripe_dispute_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "stripe_event_id": {
+ "name": "stripe_event_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_event_created_at": {
+ "name": "stripe_event_created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_charge_id": {
+ "name": "stripe_charge_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_payment_intent_id": {
+ "name": "stripe_payment_intent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_customer_id": {
+ "name": "stripe_customer_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "amount_minor_units": {
+ "name": "amount_minor_units",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "currency": {
+ "name": "currency",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "dispute_reason": {
+ "name": "dispute_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_status": {
+ "name": "stripe_status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owner_classification": {
+ "name": "owner_classification",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'needs_action'"
+ },
+ "status_reason": {
+ "name": "status_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "failure_context": {
+ "name": "failure_context",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_created_at": {
+ "name": "stripe_created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "evidence_due_by": {
+ "name": "evidence_due_by",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "synced_at": {
+ "name": "synced_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "accepted_by_kilo_user_id": {
+ "name": "accepted_by_kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "acceptance_started_at": {
+ "name": "acceptance_started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "next_retry_at": {
+ "name": "next_retry_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "accepted_at": {
+ "name": "accepted_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enforcement_completed_at": {
+ "name": "enforcement_completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "review_required_at": {
+ "name": "review_required_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "closed_at": {
+ "name": "closed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_stripe_dispute_cases_event_id": {
+ "name": "IDX_stripe_dispute_cases_event_id",
+ "columns": [
+ {
+ "expression": "stripe_event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_stripe_dispute_cases_charge_id": {
+ "name": "IDX_stripe_dispute_cases_charge_id",
+ "columns": [
+ {
+ "expression": "stripe_charge_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_stripe_dispute_cases_payment_intent_id": {
+ "name": "IDX_stripe_dispute_cases_payment_intent_id",
+ "columns": [
+ {
+ "expression": "stripe_payment_intent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_stripe_dispute_cases_customer_id": {
+ "name": "IDX_stripe_dispute_cases_customer_id",
+ "columns": [
+ {
+ "expression": "stripe_customer_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_stripe_dispute_cases_kilo_user_id": {
+ "name": "IDX_stripe_dispute_cases_kilo_user_id",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_stripe_dispute_cases_organization_id": {
+ "name": "IDX_stripe_dispute_cases_organization_id",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_stripe_dispute_cases_status_due_by": {
+ "name": "IDX_stripe_dispute_cases_status_due_by",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "evidence_due_by",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "stripe_created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "stripe_dispute_cases_kilo_user_id_kilocode_users_id_fk": {
+ "name": "stripe_dispute_cases_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "stripe_dispute_cases",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "cascade"
+ },
+ "stripe_dispute_cases_organization_id_organizations_id_fk": {
+ "name": "stripe_dispute_cases_organization_id_organizations_id_fk",
+ "tableFrom": "stripe_dispute_cases",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "cascade"
+ },
+ "stripe_dispute_cases_accepted_by_kilo_user_id_kilocode_users_id_fk": {
+ "name": "stripe_dispute_cases_accepted_by_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "stripe_dispute_cases",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "accepted_by_kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_stripe_dispute_cases_dispute_id": {
+ "name": "UQ_stripe_dispute_cases_dispute_id",
+ "nullsNotDistinct": false,
+ "columns": [
+ "stripe_dispute_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "stripe_dispute_cases_owner_classification_check": {
+ "name": "stripe_dispute_cases_owner_classification_check",
+ "value": "\"stripe_dispute_cases\".\"owner_classification\" IN ('personal', 'organization', 'ambiguous', 'unmatched')"
+ },
+ "stripe_dispute_cases_status_check": {
+ "name": "stripe_dispute_cases_status_check",
+ "value": "\"stripe_dispute_cases\".\"status\" IN ('needs_action', 'processing', 'accepted', 'acceptance_failed', 'enforcement_failed', 'review_required', 'closed')"
+ },
+ "stripe_dispute_cases_amount_minor_units_non_negative_check": {
+ "name": "stripe_dispute_cases_amount_minor_units_non_negative_check",
+ "value": "\"stripe_dispute_cases\".\"amount_minor_units\" IS NULL OR \"stripe_dispute_cases\".\"amount_minor_units\" >= 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.stripe_early_fraud_warning_actions": {
+ "name": "stripe_early_fraud_warning_actions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "case_id": {
+ "name": "case_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action_type": {
+ "name": "action_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_key": {
+ "name": "target_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'queued'"
+ },
+ "attempt_count": {
+ "name": "attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "next_retry_at": {
+ "name": "next_retry_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "claimed_at": {
+ "name": "claimed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_attempt_at": {
+ "name": "last_attempt_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "terminal_at": {
+ "name": "terminal_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "result_code": {
+ "name": "result_code",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "result_reference_id": {
+ "name": "result_reference_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "failure_context": {
+ "name": "failure_context",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_stripe_early_fraud_warning_actions_case_id": {
+ "name": "IDX_stripe_early_fraud_warning_actions_case_id",
+ "columns": [
+ {
+ "expression": "case_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_stripe_early_fraud_warning_actions_claim_path": {
+ "name": "IDX_stripe_early_fraud_warning_actions_claim_path",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "coalesce(\"next_retry_at\", '-infinity'::timestamptz)",
+ "asc": true,
+ "isExpression": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "stripe_early_fraud_warning_actions_case_id_stripe_early_fraud_warning_cases_id_fk": {
+ "name": "stripe_early_fraud_warning_actions_case_id_stripe_early_fraud_warning_cases_id_fk",
+ "tableFrom": "stripe_early_fraud_warning_actions",
+ "tableTo": "stripe_early_fraud_warning_cases",
+ "columnsFrom": [
+ "case_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "restrict",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_stripe_early_fraud_warning_actions_case_type_target": {
+ "name": "UQ_stripe_early_fraud_warning_actions_case_type_target",
+ "nullsNotDistinct": false,
+ "columns": [
+ "case_id",
+ "action_type",
+ "target_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "stripe_early_fraud_warning_actions_action_type_check": {
+ "name": "stripe_early_fraud_warning_actions_action_type_check",
+ "value": "\"stripe_early_fraud_warning_actions\".\"action_type\" IN ('containment', 'refund', 'payment_value_clawback', 'subscription_termination', 'access_termination', 'kiloclaw_suspension', 'affiliate_payout_reversal', 'referral_reward_reversal', 'user_notice')"
+ },
+ "stripe_early_fraud_warning_actions_status_check": {
+ "name": "stripe_early_fraud_warning_actions_status_check",
+ "value": "\"stripe_early_fraud_warning_actions\".\"status\" IN ('queued', 'processing', 'completed', 'failed', 'review_required', 'dismissed')"
+ },
+ "stripe_early_fraud_warning_actions_attempt_count_non_negative_check": {
+ "name": "stripe_early_fraud_warning_actions_attempt_count_non_negative_check",
+ "value": "\"stripe_early_fraud_warning_actions\".\"attempt_count\" >= 0"
+ },
+ "stripe_early_fraud_warning_actions_target_key_not_empty_check": {
+ "name": "stripe_early_fraud_warning_actions_target_key_not_empty_check",
+ "value": "length(\"stripe_early_fraud_warning_actions\".\"target_key\") > 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.stripe_early_fraud_warning_cases": {
+ "name": "stripe_early_fraud_warning_cases",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "stripe_early_fraud_warning_id": {
+ "name": "stripe_early_fraud_warning_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "stripe_event_id": {
+ "name": "stripe_event_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "stripe_charge_id": {
+ "name": "stripe_charge_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_payment_intent_id": {
+ "name": "stripe_payment_intent_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stripe_customer_id": {
+ "name": "stripe_customer_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "amount_minor_units": {
+ "name": "amount_minor_units",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "currency": {
+ "name": "currency",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owner_classification": {
+ "name": "owner_classification",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'queued'"
+ },
+ "reason": {
+ "name": "reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "failure_context": {
+ "name": "failure_context",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "warning_created_at": {
+ "name": "warning_created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "contained_at": {
+ "name": "contained_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "processing_started_at": {
+ "name": "processing_started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "completed_at": {
+ "name": "completed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "review_required_at": {
+ "name": "review_required_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "remediated_at": {
+ "name": "remediated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "dismissed_at": {
+ "name": "dismissed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_stripe_early_fraud_warning_cases_event_id": {
+ "name": "IDX_stripe_early_fraud_warning_cases_event_id",
+ "columns": [
+ {
+ "expression": "stripe_event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_stripe_early_fraud_warning_cases_charge_id": {
+ "name": "IDX_stripe_early_fraud_warning_cases_charge_id",
+ "columns": [
+ {
+ "expression": "stripe_charge_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_stripe_early_fraud_warning_cases_payment_intent_id": {
+ "name": "IDX_stripe_early_fraud_warning_cases_payment_intent_id",
+ "columns": [
+ {
+ "expression": "stripe_payment_intent_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_stripe_early_fraud_warning_cases_customer_id": {
+ "name": "IDX_stripe_early_fraud_warning_cases_customer_id",
+ "columns": [
+ {
+ "expression": "stripe_customer_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_stripe_early_fraud_warning_cases_kilo_user_id": {
+ "name": "IDX_stripe_early_fraud_warning_cases_kilo_user_id",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_stripe_early_fraud_warning_cases_organization_id": {
+ "name": "IDX_stripe_early_fraud_warning_cases_organization_id",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_stripe_early_fraud_warning_cases_status_created_at": {
+ "name": "IDX_stripe_early_fraud_warning_cases_status_created_at",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "stripe_early_fraud_warning_cases_kilo_user_id_kilocode_users_id_fk": {
+ "name": "stripe_early_fraud_warning_cases_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "stripe_early_fraud_warning_cases",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "cascade"
+ },
+ "stripe_early_fraud_warning_cases_organization_id_organizations_id_fk": {
+ "name": "stripe_early_fraud_warning_cases_organization_id_organizations_id_fk",
+ "tableFrom": "stripe_early_fraud_warning_cases",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_stripe_early_fraud_warning_cases_warning_id": {
+ "name": "UQ_stripe_early_fraud_warning_cases_warning_id",
+ "nullsNotDistinct": false,
+ "columns": [
+ "stripe_early_fraud_warning_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "stripe_early_fraud_warning_cases_owner_classification_check": {
+ "name": "stripe_early_fraud_warning_cases_owner_classification_check",
+ "value": "\"stripe_early_fraud_warning_cases\".\"owner_classification\" IN ('personal', 'organization', 'ambiguous', 'unmatched')"
+ },
+ "stripe_early_fraud_warning_cases_status_check": {
+ "name": "stripe_early_fraud_warning_cases_status_check",
+ "value": "\"stripe_early_fraud_warning_cases\".\"status\" IN ('queued', 'contained', 'processing', 'completed', 'review_required', 'failed', 'remediated', 'dismissed')"
+ },
+ "stripe_early_fraud_warning_cases_amount_minor_units_non_negative_check": {
+ "name": "stripe_early_fraud_warning_cases_amount_minor_units_non_negative_check",
+ "value": "\"stripe_early_fraud_warning_cases\".\"amount_minor_units\" IS NULL OR \"stripe_early_fraud_warning_cases\".\"amount_minor_units\" >= 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.stytch_fingerprints": {
+ "name": "stytch_fingerprints",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "visitor_fingerprint": {
+ "name": "visitor_fingerprint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "browser_fingerprint": {
+ "name": "browser_fingerprint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "browser_id": {
+ "name": "browser_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "hardware_fingerprint": {
+ "name": "hardware_fingerprint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "network_fingerprint": {
+ "name": "network_fingerprint",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "visitor_id": {
+ "name": "visitor_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "verdict_action": {
+ "name": "verdict_action",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "detected_device_type": {
+ "name": "detected_device_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "is_authentic_device": {
+ "name": "is_authentic_device",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reasons": {
+ "name": "reasons",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{\"\"}'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "status_code": {
+ "name": "status_code",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "fingerprint_data": {
+ "name": "fingerprint_data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kilo_free_tier_allowed": {
+ "name": "kilo_free_tier_allowed",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "http_x_forwarded_for": {
+ "name": "http_x_forwarded_for",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "http_x_vercel_ip_city": {
+ "name": "http_x_vercel_ip_city",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "http_x_vercel_ip_country": {
+ "name": "http_x_vercel_ip_country",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "http_x_vercel_ip_latitude": {
+ "name": "http_x_vercel_ip_latitude",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "http_x_vercel_ip_longitude": {
+ "name": "http_x_vercel_ip_longitude",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "http_x_vercel_ja4_digest": {
+ "name": "http_x_vercel_ja4_digest",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "http_user_agent": {
+ "name": "http_user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "idx_hardware_fingerprint": {
+ "name": "idx_hardware_fingerprint",
+ "columns": [
+ {
+ "expression": "hardware_fingerprint",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_kilo_user_id": {
+ "name": "idx_kilo_user_id",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_stytch_fingerprints_reasons_gin": {
+ "name": "idx_stytch_fingerprints_reasons_gin",
+ "columns": [
+ {
+ "expression": "reasons",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "gin",
+ "with": {}
+ },
+ "idx_verdict_action": {
+ "name": "idx_verdict_action",
+ "columns": [
+ {
+ "expression": "verdict_action",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "idx_visitor_fingerprint": {
+ "name": "idx_visitor_fingerprint",
+ "columns": [
+ {
+ "expression": "visitor_fingerprint",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.system_prompt_prefix": {
+ "name": "system_prompt_prefix",
+ "schema": "",
+ "columns": {
+ "system_prompt_prefix_id": {
+ "name": "system_prompt_prefix_id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "system_prompt_prefix": {
+ "name": "system_prompt_prefix",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "UQ_system_prompt_prefix": {
+ "name": "UQ_system_prompt_prefix",
+ "columns": [
+ {
+ "expression": "system_prompt_prefix",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.transactional_email_log": {
+ "name": "transactional_email_log",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "email_type": {
+ "name": "email_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "idempotency_key": {
+ "name": "idempotency_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sent_at": {
+ "name": "sent_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_transactional_email_log_type_idempotency_key": {
+ "name": "UQ_transactional_email_log_type_idempotency_key",
+ "columns": [
+ {
+ "expression": "email_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "idempotency_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_transactional_email_log_user_id": {
+ "name": "IDX_transactional_email_log_user_id",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_transactional_email_log_organization_id": {
+ "name": "IDX_transactional_email_log_organization_id",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "transactional_email_log_user_id_kilocode_users_id_fk": {
+ "name": "transactional_email_log_user_id_kilocode_users_id_fk",
+ "tableFrom": "transactional_email_log",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "transactional_email_log_organization_id_organizations_id_fk": {
+ "name": "transactional_email_log_organization_id_organizations_id_fk",
+ "tableFrom": "transactional_email_log",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "CHK_transactional_email_log_owner": {
+ "name": "CHK_transactional_email_log_owner",
+ "value": "\"transactional_email_log\".\"user_id\" IS NOT NULL OR \"transactional_email_log\".\"organization_id\" IS NOT NULL"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.user_admin_notes": {
+ "name": "user_admin_notes",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "note_content": {
+ "name": "note_content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "admin_kilo_user_id": {
+ "name": "admin_kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_34517df0b385234babc38fe81b": {
+ "name": "IDX_34517df0b385234babc38fe81b",
+ "columns": [
+ {
+ "expression": "admin_kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_ccbde98c4c14046daa5682ec4f": {
+ "name": "IDX_ccbde98c4c14046daa5682ec4f",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_d0270eb24ef6442d65a0b7853c": {
+ "name": "IDX_d0270eb24ef6442d65a0b7853c",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_affiliate_attributions": {
+ "name": "user_affiliate_attributions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "tracking_id": {
+ "name": "tracking_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_user_affiliate_attributions_user_id": {
+ "name": "IDX_user_affiliate_attributions_user_id",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "user_affiliate_attributions_user_id_kilocode_users_id_fk": {
+ "name": "user_affiliate_attributions_user_id_kilocode_users_id_fk",
+ "tableFrom": "user_affiliate_attributions",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_user_affiliate_attributions_user_provider": {
+ "name": "UQ_user_affiliate_attributions_user_provider",
+ "nullsNotDistinct": false,
+ "columns": [
+ "user_id",
+ "provider"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "user_affiliate_attributions_provider_check": {
+ "name": "user_affiliate_attributions_provider_check",
+ "value": "\"user_affiliate_attributions\".\"provider\" IN ('impact')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.user_affiliate_events": {
+ "name": "user_affiliate_events",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_type": {
+ "name": "event_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "dedupe_key": {
+ "name": "dedupe_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "parent_event_id": {
+ "name": "parent_event_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "delivery_state": {
+ "name": "delivery_state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'queued'"
+ },
+ "payload_json": {
+ "name": "payload_json",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "stripe_charge_id": {
+ "name": "stripe_charge_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "impact_action_id": {
+ "name": "impact_action_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "impact_submission_uri": {
+ "name": "impact_submission_uri",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "attempt_count": {
+ "name": "attempt_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "next_retry_at": {
+ "name": "next_retry_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "claimed_at": {
+ "name": "claimed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_user_affiliate_events_claim_path": {
+ "name": "IDX_user_affiliate_events_claim_path",
+ "columns": [
+ {
+ "expression": "delivery_state",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "coalesce(\"next_retry_at\", '-infinity'::timestamptz)",
+ "asc": true,
+ "isExpression": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_user_affiliate_events_parent_event_id": {
+ "name": "IDX_user_affiliate_events_parent_event_id",
+ "columns": [
+ {
+ "expression": "parent_event_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_user_affiliate_events_provider_event_type_charge": {
+ "name": "IDX_user_affiliate_events_provider_event_type_charge",
+ "columns": [
+ {
+ "expression": "provider",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "event_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "stripe_charge_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "user_affiliate_events_user_id_kilocode_users_id_fk": {
+ "name": "user_affiliate_events_user_id_kilocode_users_id_fk",
+ "tableFrom": "user_affiliate_events",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ },
+ "user_affiliate_events_parent_event_id_fk": {
+ "name": "user_affiliate_events_parent_event_id_fk",
+ "tableFrom": "user_affiliate_events",
+ "tableTo": "user_affiliate_events",
+ "columnsFrom": [
+ "parent_event_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_user_affiliate_events_dedupe_key": {
+ "name": "UQ_user_affiliate_events_dedupe_key",
+ "nullsNotDistinct": false,
+ "columns": [
+ "dedupe_key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "user_affiliate_events_provider_check": {
+ "name": "user_affiliate_events_provider_check",
+ "value": "\"user_affiliate_events\".\"provider\" IN ('impact')"
+ },
+ "user_affiliate_events_event_type_check": {
+ "name": "user_affiliate_events_event_type_check",
+ "value": "\"user_affiliate_events\".\"event_type\" IN ('signup', 'trial_start', 'trial_end', 'sale', 'sale_reversal')"
+ },
+ "user_affiliate_events_delivery_state_check": {
+ "name": "user_affiliate_events_delivery_state_check",
+ "value": "\"user_affiliate_events\".\"delivery_state\" IN ('queued', 'blocked', 'sending', 'delivered', 'failed')"
+ },
+ "user_affiliate_events_attempt_count_non_negative_check": {
+ "name": "user_affiliate_events_attempt_count_non_negative_check",
+ "value": "\"user_affiliate_events\".\"attempt_count\" >= 0"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.user_auth_provider": {
+ "name": "user_auth_provider",
+ "schema": "",
+ "columns": {
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_account_id": {
+ "name": "provider_account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "avatar_url": {
+ "name": "avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "display_name": {
+ "name": "display_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "hosted_domain": {
+ "name": "hosted_domain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_user_auth_provider_kilo_user_id": {
+ "name": "IDX_user_auth_provider_kilo_user_id",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_user_auth_provider_hosted_domain": {
+ "name": "IDX_user_auth_provider_hosted_domain",
+ "columns": [
+ {
+ "expression": "hosted_domain",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "user_auth_provider_provider_provider_account_id_pk": {
+ "name": "user_auth_provider_provider_provider_account_id_pk",
+ "columns": [
+ "provider",
+ "provider_account_id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_feedback": {
+ "name": "user_feedback",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "feedback_text": {
+ "name": "feedback_text",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "feedback_for": {
+ "name": "feedback_for",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'unknown'"
+ },
+ "feedback_batch": {
+ "name": "feedback_batch",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source": {
+ "name": "source",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'unknown'"
+ },
+ "context_json": {
+ "name": "context_json",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_user_feedback_created_at": {
+ "name": "IDX_user_feedback_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_user_feedback_kilo_user_id": {
+ "name": "IDX_user_feedback_kilo_user_id",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_user_feedback_feedback_for": {
+ "name": "IDX_user_feedback_feedback_for",
+ "columns": [
+ {
+ "expression": "feedback_for",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_user_feedback_feedback_batch": {
+ "name": "IDX_user_feedback_feedback_batch",
+ "columns": [
+ {
+ "expression": "feedback_batch",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_user_feedback_source": {
+ "name": "IDX_user_feedback_source",
+ "columns": [
+ {
+ "expression": "source",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "user_feedback_kilo_user_id_kilocode_users_id_fk": {
+ "name": "user_feedback_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "user_feedback",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "cascade"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_github_app_tokens": {
+ "name": "user_github_app_tokens",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "github_app_type": {
+ "name": "github_app_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'standard'"
+ },
+ "github_user_id": {
+ "name": "github_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "github_login": {
+ "name": "github_login",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "access_token_encrypted": {
+ "name": "access_token_encrypted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "access_token_expires_at": {
+ "name": "access_token_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "refresh_token_encrypted": {
+ "name": "refresh_token_encrypted",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "refresh_token_expires_at": {
+ "name": "refresh_token_expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "credential_version": {
+ "name": "credential_version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revocation_reason": {
+ "name": "revocation_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_used_at": {
+ "name": "last_used_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_user_github_app_tokens_user_app": {
+ "name": "UQ_user_github_app_tokens_user_app",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "github_app_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_user_github_app_tokens_github_user_app": {
+ "name": "UQ_user_github_app_tokens_github_user_app",
+ "columns": [
+ {
+ "expression": "github_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "github_app_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "user_github_app_tokens_kilo_user_id_kilocode_users_id_fk": {
+ "name": "user_github_app_tokens_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "user_github_app_tokens",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "user_github_app_tokens_app_type_check": {
+ "name": "user_github_app_tokens_app_type_check",
+ "value": "\"user_github_app_tokens\".\"github_app_type\" IN ('standard', 'lite')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.user_period_cache": {
+ "name": "user_period_cache",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cache_type": {
+ "name": "cache_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "period_type": {
+ "name": "period_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "period_key": {
+ "name": "period_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "data": {
+ "name": "data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "computed_at": {
+ "name": "computed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "version": {
+ "name": "version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "shared_url_token": {
+ "name": "shared_url_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "shared_at": {
+ "name": "shared_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "IDX_user_period_cache_kilo_user_id": {
+ "name": "IDX_user_period_cache_kilo_user_id",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_user_period_cache": {
+ "name": "UQ_user_period_cache",
+ "columns": [
+ {
+ "expression": "kilo_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "cache_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "period_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "period_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_user_period_cache_lookup": {
+ "name": "IDX_user_period_cache_lookup",
+ "columns": [
+ {
+ "expression": "cache_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "period_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "period_key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "UQ_user_period_cache_share_token": {
+ "name": "UQ_user_period_cache_share_token",
+ "columns": [
+ {
+ "expression": "shared_url_token",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "where": "\"user_period_cache\".\"shared_url_token\" IS NOT NULL",
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "user_period_cache_kilo_user_id_kilocode_users_id_fk": {
+ "name": "user_period_cache_kilo_user_id_kilocode_users_id_fk",
+ "tableFrom": "user_period_cache",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "kilo_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "user_period_cache_period_type_check": {
+ "name": "user_period_cache_period_type_check",
+ "value": "\"user_period_cache\".\"period_type\" IN ('year', 'quarter', 'month', 'week', 'custom')"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.user_push_tokens": {
+ "name": "user_push_tokens",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "platform": {
+ "name": "platform",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "UQ_user_push_tokens_token": {
+ "name": "UQ_user_push_tokens_token",
+ "columns": [
+ {
+ "expression": "token",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_user_push_tokens_user_id": {
+ "name": "IDX_user_push_tokens_user_id",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "user_push_tokens_user_id_kilocode_users_id_fk": {
+ "name": "user_push_tokens_user_id_kilocode_users_id_fk",
+ "tableFrom": "user_push_tokens",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.vercel_ip_city": {
+ "name": "vercel_ip_city",
+ "schema": "",
+ "columns": {
+ "vercel_ip_city_id": {
+ "name": "vercel_ip_city_id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "vercel_ip_city": {
+ "name": "vercel_ip_city",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "UQ_vercel_ip_city": {
+ "name": "UQ_vercel_ip_city",
+ "columns": [
+ {
+ "expression": "vercel_ip_city",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.vercel_ip_country": {
+ "name": "vercel_ip_country",
+ "schema": "",
+ "columns": {
+ "vercel_ip_country_id": {
+ "name": "vercel_ip_country_id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "vercel_ip_country": {
+ "name": "vercel_ip_country",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "UQ_vercel_ip_country": {
+ "name": "UQ_vercel_ip_country",
+ "columns": [
+ {
+ "expression": "vercel_ip_country",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.webhook_events": {
+ "name": "webhook_events",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "pg_catalog.gen_random_uuid()"
+ },
+ "owned_by_organization_id": {
+ "name": "owned_by_organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "owned_by_user_id": {
+ "name": "owned_by_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "platform": {
+ "name": "platform",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_type": {
+ "name": "event_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_action": {
+ "name": "event_action",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "payload": {
+ "name": "payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "headers": {
+ "name": "headers",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "processed": {
+ "name": "processed",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "processed_at": {
+ "name": "processed_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "handlers_triggered": {
+ "name": "handlers_triggered",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'"
+ },
+ "errors": {
+ "name": "errors",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "event_signature": {
+ "name": "event_signature",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "IDX_webhook_events_owned_by_org_id": {
+ "name": "IDX_webhook_events_owned_by_org_id",
+ "columns": [
+ {
+ "expression": "owned_by_organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_webhook_events_owned_by_user_id": {
+ "name": "IDX_webhook_events_owned_by_user_id",
+ "columns": [
+ {
+ "expression": "owned_by_user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_webhook_events_platform": {
+ "name": "IDX_webhook_events_platform",
+ "columns": [
+ {
+ "expression": "platform",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_webhook_events_event_type": {
+ "name": "IDX_webhook_events_event_type",
+ "columns": [
+ {
+ "expression": "event_type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "IDX_webhook_events_created_at": {
+ "name": "IDX_webhook_events_created_at",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "webhook_events_owned_by_organization_id_organizations_id_fk": {
+ "name": "webhook_events_owned_by_organization_id_organizations_id_fk",
+ "tableFrom": "webhook_events",
+ "tableTo": "organizations",
+ "columnsFrom": [
+ "owned_by_organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "webhook_events_owned_by_user_id_kilocode_users_id_fk": {
+ "name": "webhook_events_owned_by_user_id_kilocode_users_id_fk",
+ "tableFrom": "webhook_events",
+ "tableTo": "kilocode_users",
+ "columnsFrom": [
+ "owned_by_user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "UQ_webhook_events_signature": {
+ "name": "UQ_webhook_events_signature",
+ "nullsNotDistinct": false,
+ "columns": [
+ "event_signature"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {
+ "webhook_events_owner_check": {
+ "name": "webhook_events_owner_check",
+ "value": "(\n (\"webhook_events\".\"owned_by_user_id\" IS NOT NULL AND \"webhook_events\".\"owned_by_organization_id\" IS NULL) OR\n (\"webhook_events\".\"owned_by_user_id\" IS NULL AND \"webhook_events\".\"owned_by_organization_id\" IS NOT NULL)\n )"
+ }
+ },
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {},
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {
+ "public.microdollar_usage_view": {
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "kilo_user_id": {
+ "name": "kilo_user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "message_id": {
+ "name": "message_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cost": {
+ "name": "cost",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "input_tokens": {
+ "name": "input_tokens",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "output_tokens": {
+ "name": "output_tokens",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cache_write_tokens": {
+ "name": "cache_write_tokens",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cache_hit_tokens": {
+ "name": "cache_hit_tokens",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "http_x_forwarded_for": {
+ "name": "http_x_forwarded_for",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "http_x_vercel_ip_city": {
+ "name": "http_x_vercel_ip_city",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "http_x_vercel_ip_country": {
+ "name": "http_x_vercel_ip_country",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "http_x_vercel_ip_latitude": {
+ "name": "http_x_vercel_ip_latitude",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "http_x_vercel_ip_longitude": {
+ "name": "http_x_vercel_ip_longitude",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "http_x_vercel_ja4_digest": {
+ "name": "http_x_vercel_ja4_digest",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model": {
+ "name": "model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "requested_model": {
+ "name": "requested_model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_prompt_prefix": {
+ "name": "user_prompt_prefix",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "system_prompt_prefix": {
+ "name": "system_prompt_prefix",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "system_prompt_length": {
+ "name": "system_prompt_length",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "http_user_agent": {
+ "name": "http_user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cache_discount": {
+ "name": "cache_discount",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "max_tokens": {
+ "name": "max_tokens",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "has_middle_out_transform": {
+ "name": "has_middle_out_transform",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "has_error": {
+ "name": "has_error",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "abuse_classification": {
+ "name": "abuse_classification",
+ "type": "smallint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "inference_provider": {
+ "name": "inference_provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status_code": {
+ "name": "status_code",
+ "type": "smallint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "upstream_id": {
+ "name": "upstream_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "finish_reason": {
+ "name": "finish_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "latency": {
+ "name": "latency",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "moderation_latency": {
+ "name": "moderation_latency",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "generation_time": {
+ "name": "generation_time",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_byok": {
+ "name": "is_byok",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_user_byok": {
+ "name": "is_user_byok",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "streamed": {
+ "name": "streamed",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cancelled": {
+ "name": "cancelled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "editor_name": {
+ "name": "editor_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "api_kind": {
+ "name": "api_kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "has_tools": {
+ "name": "has_tools",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "machine_id": {
+ "name": "machine_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "feature": {
+ "name": "feature",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "session_id": {
+ "name": "session_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "mode": {
+ "name": "mode",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "auto_model": {
+ "name": "auto_model",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "market_cost": {
+ "name": "market_cost",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_free": {
+ "name": "is_free",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "abuse_delay": {
+ "name": "abuse_delay",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "abuse_downgraded_from": {
+ "name": "abuse_downgraded_from",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "definition": "\n SELECT\n mu.id,\n mu.kilo_user_id,\n meta.message_id,\n mu.cost,\n mu.input_tokens,\n mu.output_tokens,\n mu.cache_write_tokens,\n mu.cache_hit_tokens,\n mu.created_at,\n ip.http_ip AS http_x_forwarded_for,\n city.vercel_ip_city AS http_x_vercel_ip_city,\n country.vercel_ip_country AS http_x_vercel_ip_country,\n meta.vercel_ip_latitude AS http_x_vercel_ip_latitude,\n meta.vercel_ip_longitude AS http_x_vercel_ip_longitude,\n ja4.ja4_digest AS http_x_vercel_ja4_digest,\n mu.provider,\n mu.model,\n mu.requested_model,\n meta.user_prompt_prefix,\n spp.system_prompt_prefix,\n meta.system_prompt_length,\n ua.http_user_agent,\n mu.cache_discount,\n meta.max_tokens,\n meta.has_middle_out_transform,\n mu.has_error,\n mu.abuse_classification,\n mu.organization_id,\n mu.inference_provider,\n mu.project_id,\n meta.status_code,\n meta.upstream_id,\n frfr.finish_reason,\n meta.latency,\n meta.moderation_latency,\n meta.generation_time,\n meta.is_byok,\n meta.is_user_byok,\n meta.streamed,\n meta.cancelled,\n edit.editor_name,\n ak.api_kind,\n meta.has_tools,\n meta.machine_id,\n feat.feature,\n meta.session_id,\n md.mode,\n am.auto_model,\n meta.market_cost,\n meta.is_free,\n meta.abuse_delay,\n meta.abuse_downgraded_from\n FROM \"microdollar_usage\" mu\n LEFT JOIN \"microdollar_usage_metadata\" meta ON mu.id = meta.id\n LEFT JOIN \"http_ip\" ip ON meta.http_ip_id = ip.http_ip_id\n LEFT JOIN \"vercel_ip_city\" city ON meta.vercel_ip_city_id = city.vercel_ip_city_id\n LEFT JOIN \"vercel_ip_country\" country ON meta.vercel_ip_country_id = country.vercel_ip_country_id\n LEFT JOIN \"ja4_digest\" ja4 ON meta.ja4_digest_id = ja4.ja4_digest_id\n LEFT JOIN \"system_prompt_prefix\" spp ON meta.system_prompt_prefix_id = spp.system_prompt_prefix_id\n LEFT JOIN \"http_user_agent\" ua ON meta.http_user_agent_id = ua.http_user_agent_id\n LEFT JOIN \"finish_reason\" frfr ON meta.finish_reason_id = frfr.finish_reason_id\n LEFT JOIN \"editor_name\" edit ON meta.editor_name_id = edit.editor_name_id\n LEFT JOIN \"api_kind\" ak ON meta.api_kind_id = ak.api_kind_id\n LEFT JOIN \"feature\" feat ON meta.feature_id = feat.feature_id\n LEFT JOIN \"mode\" md ON meta.mode_id = md.mode_id\n LEFT JOIN \"auto_model\" am ON meta.auto_model_id = am.auto_model_id\n",
+ "name": "microdollar_usage_view",
+ "schema": "public",
+ "isExisting": false,
+ "materialized": false
+ }
+ },
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/packages/db/src/migrations/meta/_journal.json b/packages/db/src/migrations/meta/_journal.json
index 7e24ad7d8a..11d9ba88f0 100644
--- a/packages/db/src/migrations/meta/_journal.json
+++ b/packages/db/src/migrations/meta/_journal.json
@@ -1163,6 +1163,13 @@
"when": 1781527420968,
"tag": "0165_clear_golden_guardian",
"breakpoints": true
+ },
+ {
+ "idx": 166,
+ "version": "7",
+ "when": 1781717948486,
+ "tag": "0166_easy_iceman",
+ "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 7e77545a0a..bc30d2dc12 100644
--- a/packages/db/src/schema-types.ts
+++ b/packages/db/src/schema-types.ts
@@ -131,11 +131,14 @@ export enum CliSessionSharedState {
*/
export enum SecurityAuditLogAction {
FindingCreated = 'security.finding.created',
+ FindingSeverityChanged = 'security.finding.severity_changed',
FindingStatusChange = 'security.finding.status_change',
FindingDismissed = 'security.finding.dismissed',
FindingAutoDismissed = 'security.finding.auto_dismissed',
+ FindingSuperseded = 'security.finding.superseded',
FindingAnalysisStarted = 'security.finding.analysis_started',
FindingAnalysisCompleted = 'security.finding.analysis_completed',
+ FindingAnalysisFailed = 'security.finding.analysis_failed',
RemediationQueued = 'security.remediation.queued',
RemediationStarted = 'security.remediation.started',
RemediationPrOpened = 'security.remediation.pr_opened',
@@ -151,6 +154,21 @@ export enum SecurityAuditLogAction {
SyncTriggered = 'security.sync.triggered',
SyncCompleted = 'security.sync.completed',
AuditLogExported = 'security.audit_log.exported',
+ AuditReportGenerated = 'security.audit_report.generated',
+}
+
+export enum SecurityFindingAuditSourceContext {
+ SecuritySync = 'security_sync',
+ Web = 'web',
+ AnalysisWorker = 'analysis_worker',
+ RemediationCallback = 'remediation_callback',
+ RolloutBaseline = 'rollout_baseline',
+}
+
+export enum SecurityAuditLogActorType {
+ CustomerUser = 'customer_user',
+ KiloAdmin = 'kilo_admin',
+ System = 'system',
}
// --- KiloClaw enums ---
@@ -1138,6 +1156,7 @@ export type SandboxSuggestedAction =
export type SecurityFindingSandboxAnalysis = {
isExploitable: boolean | 'unknown';
+ extractionStatus?: 'succeeded' | 'failed';
exploitabilityReasoning: string;
usageLocations: string[];
suggestedFix: string;
@@ -1148,9 +1167,32 @@ export type SecurityFindingSandboxAnalysis = {
modelUsed?: string;
};
+export type SecurityFindingAnalysisInput = {
+ schemaVersion: 1;
+ source: string;
+ sourceId: string;
+ sourceUpdatedAt: string | null;
+ repoFullName: string;
+ status: string;
+ severity: string | null;
+ packageName: string;
+ packageEcosystem: string;
+ dependencyScope: string | null;
+ cveId: string | null;
+ ghsaId: string | null;
+ cweIds: string[];
+ cvssScore: string | null;
+ title: string;
+ description: string | null;
+ vulnerableVersionRange: string | null;
+ patchedVersion: string | null;
+ manifestPath: string | null;
+};
+
export type SecurityFindingAnalysis = {
triage?: SecurityFindingTriage;
sandboxAnalysis?: SecurityFindingSandboxAnalysis;
+ findingDataSnapshot?: SecurityFindingAnalysisInput;
rawMarkdown?: string;
analyzedAt: string;
modelUsed?: string;
diff --git a/packages/db/src/schema.test.ts b/packages/db/src/schema.test.ts
index 6cf1d92c38..140dc5bd9b 100644
--- a/packages/db/src/schema.test.ts
+++ b/packages/db/src/schema.test.ts
@@ -267,11 +267,22 @@ describe('database schema', () => {
CliSessionSharedState: ['public', 'organization'],
SecurityAuditLogAction: [
'security.finding.created',
+ 'security.finding.severity_changed',
'security.finding.status_change',
'security.finding.dismissed',
'security.finding.auto_dismissed',
+ 'security.finding.superseded',
'security.finding.analysis_started',
'security.finding.analysis_completed',
+ 'security.finding.analysis_failed',
+ 'security.remediation.queued',
+ 'security.remediation.started',
+ 'security.remediation.pr_opened',
+ 'security.remediation.failed',
+ 'security.remediation.blocked',
+ 'security.remediation.no_changes_needed',
+ 'security.remediation.cancelled',
+ 'security.remediation.retried',
'security.finding.deleted',
'security.config.enabled',
'security.config.disabled',
@@ -279,6 +290,15 @@ describe('database schema', () => {
'security.sync.triggered',
'security.sync.completed',
'security.audit_log.exported',
+ 'security.audit_report.generated',
+ ],
+ SecurityAuditLogActorType: ['customer_user', 'kilo_admin', 'system'],
+ SecurityFindingAuditSourceContext: [
+ 'security_sync',
+ 'web',
+ 'analysis_worker',
+ 'remediation_callback',
+ 'rollout_baseline',
],
KiloClawPlan: ['trial', 'commit', 'standard'],
KiloClawScheduledPlan: ['commit', 'standard'],
diff --git a/packages/db/src/schema.ts b/packages/db/src/schema.ts
index d5cd4d6792..af2d5d87de 100644
--- a/packages/db/src/schema.ts
+++ b/packages/db/src/schema.ts
@@ -40,6 +40,8 @@ import {
FeedbackSource,
CliSessionSharedState,
SecurityAuditLogAction,
+ SecurityAuditLogActorType,
+ SecurityFindingAuditSourceContext,
SecurityFindingNotificationKind,
SecurityFindingNotificationStatus,
KiloClawPlan,
@@ -179,6 +181,8 @@ export const SCHEMA_CHECK_ENUMS = {
KiloPassScheduledChangeStatus,
CliSessionSharedState,
SecurityAuditLogAction,
+ SecurityAuditLogActorType,
+ SecurityFindingAuditSourceContext,
KiloClawPlan,
KiloClawScheduledPlan,
KiloClawScheduledBy,
@@ -5430,12 +5434,20 @@ export const security_audit_log = pgTable(
actor_id: text(),
actor_email: text(),
actor_name: text(),
+ actor_type: text().$type(),
action: text().$type().notNull(),
resource_type: text().notNull(),
resource_id: text().notNull(),
before_state: jsonb().$type>(),
after_state: jsonb().$type>(),
metadata: jsonb().$type>(),
+ finding_id: uuid(),
+ occurred_at: timestamp({ withTimezone: true, mode: 'string' }),
+ source_occurred_at: timestamp({ withTimezone: true, mode: 'string' }),
+ event_key: text(),
+ schema_version: smallint(),
+ finding_snapshot: jsonb().$type>(),
+ source_context: text().$type(),
created_at: timestamp({ withTimezone: true, mode: 'string' }).defaultNow().notNull(),
},
table => [
@@ -5444,6 +5456,12 @@ export const security_audit_log = pgTable(
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)`
),
enumCheck('security_audit_log_action_check', table.action, SecurityAuditLogAction),
+ enumCheck('security_audit_log_actor_type_check', table.actor_type, SecurityAuditLogActorType),
+ enumCheck(
+ 'security_audit_log_source_context_check',
+ table.source_context,
+ SecurityFindingAuditSourceContext
+ ),
index('IDX_security_audit_log_org_created').on(
table.owned_by_organization_id,
table.created_at
@@ -5452,6 +5470,20 @@ export const security_audit_log = pgTable(
index('IDX_security_audit_log_resource').on(table.resource_type, table.resource_id),
index('IDX_security_audit_log_actor').on(table.actor_id, table.created_at),
index('IDX_security_audit_log_action').on(table.action, table.created_at),
+ uniqueIndex('UQ_security_audit_log_org_event_key')
+ .on(table.owned_by_organization_id, table.event_key)
+ .where(sql`${table.owned_by_organization_id} IS NOT NULL AND ${table.event_key} IS NOT NULL`),
+ uniqueIndex('UQ_security_audit_log_user_event_key')
+ .on(table.owned_by_user_id, table.event_key)
+ .where(sql`${table.owned_by_user_id} IS NOT NULL AND ${table.event_key} IS NOT NULL`),
+ index('IDX_security_audit_log_org_occurred')
+ .on(table.owned_by_organization_id, table.occurred_at, table.id)
+ .where(
+ sql`${table.owned_by_organization_id} IS NOT NULL AND ${table.occurred_at} IS NOT NULL`
+ ),
+ index('IDX_security_audit_log_user_occurred')
+ .on(table.owned_by_user_id, table.occurred_at, table.id)
+ .where(sql`${table.owned_by_user_id} IS NOT NULL AND ${table.occurred_at} IS NOT NULL`),
]
);
diff --git a/packages/worker-utils/package.json b/packages/worker-utils/package.json
index 1f8620ac9e..1f18ff362c 100644
--- a/packages/worker-utils/package.json
+++ b/packages/worker-utils/package.json
@@ -23,6 +23,7 @@
"./security-auto-analysis-policy": "./src/security-auto-analysis-policy.ts",
"./security-remediation-policy": "./src/security-remediation-policy.ts",
"./security-notification-policy": "./src/security-notification-policy.ts",
+ "./security-finding-audit": "./src/security-finding-audit.ts",
"./dependabot-dismissal-target": "./src/dependabot-dismissal-target.ts",
"./client-error": "./src/client-error.ts"
},
diff --git a/packages/worker-utils/src/index.ts b/packages/worker-utils/src/index.ts
index 066a693587..61ed9b6876 100644
--- a/packages/worker-utils/src/index.ts
+++ b/packages/worker-utils/src/index.ts
@@ -112,3 +112,33 @@ export type {
CloudAgentQueueReport,
CloudAgentRunStateReport,
} from './cloud-agent-queue-report.js';
+
+export {
+ REPORTABLE_SECURITY_FINDING_AUDIT_ACTIONS,
+ SECURITY_FINDING_AUDIT_EVENT_KEY_PREFIX,
+ SECURITY_FINDING_AUDIT_SCHEMA_VERSION,
+ SECURITY_FINDING_AUDIT_SYSTEM_ACTOR,
+ SecurityFindingAuditActorSchema,
+ SecurityFindingAuditEventSchema,
+ SecurityFindingAuditHumanActorSchema,
+ SecurityFindingAuditOwnerSchema,
+ SecurityFindingAuditSnapshotSchema,
+ buildSecurityFindingAuditHumanActor,
+ buildSecurityFindingAuditLogValues,
+ buildSecurityFindingAuditSnapshot,
+ deriveSecurityFindingAuditEventKey,
+ insertSecurityFindingAuditEvent,
+} from './security-finding-audit.js';
+export type {
+ NewSecurityFindingAuditLogValues,
+ SecurityFindingAuditActor,
+ SecurityFindingAuditEventFinding,
+ SecurityFindingAuditEventInput,
+ SecurityFindingAuditHumanActor,
+ SecurityFindingAuditLogEntry,
+ SecurityFindingAuditOwner,
+ SecurityFindingAuditSnapshot,
+ SecurityFindingAuditSnapshotExtras,
+ SecurityFindingAuditSnapshotSource,
+ SecurityFindingAuditWriterDb,
+} from './security-finding-audit.js';
diff --git a/packages/worker-utils/src/security-finding-audit.test.ts b/packages/worker-utils/src/security-finding-audit.test.ts
new file mode 100644
index 0000000000..6a50321da9
--- /dev/null
+++ b/packages/worker-utils/src/security-finding-audit.test.ts
@@ -0,0 +1,236 @@
+import { describe, expect, it } from 'vitest';
+import {
+ SecurityAuditLogAction,
+ SecurityAuditLogActorType,
+ SecurityFindingAuditSourceContext,
+} from '@kilocode/db/schema-types';
+import {
+ SECURITY_FINDING_AUDIT_SCHEMA_VERSION,
+ SECURITY_FINDING_AUDIT_SYSTEM_ACTOR,
+ buildSecurityFindingAuditHumanActor,
+ buildSecurityFindingAuditLogValues,
+ buildSecurityFindingAuditSnapshot,
+ deriveSecurityFindingAuditEventKey,
+ insertSecurityFindingAuditEvent,
+ type NewSecurityFindingAuditLogValues,
+ type SecurityFindingAuditEventFinding,
+ type SecurityFindingAuditWriterDb,
+} from './security-finding-audit';
+
+const finding = {
+ id: '11111111-1111-4111-8111-111111111111',
+ owned_by_user_id: 'user_123',
+ owned_by_organization_id: null,
+ source: 'dependabot',
+ source_id: '42',
+ repo_full_name: 'kilo/example',
+ title: 'lodash vulnerable to prototype pollution',
+ severity: 'high',
+ status: 'open',
+ package_name: 'lodash',
+ package_ecosystem: 'npm',
+ manifest_path: 'package.json',
+ patched_version: '4.17.21',
+ ghsa_id: 'GHSA-xxxx-yyyy-zzzz',
+ cve_id: 'CVE-2026-1234',
+ cwe_ids: ['CWE-1321'],
+ cvss_score: '7.5',
+ dependabot_html_url: 'https://github.com/kilo/example/security/dependabot/42',
+ first_detected_at: '2026-06-01 12:30:00.000+00',
+ fixed_at: null,
+ sla_due_at: '2026-06-08 12:30:00.000+00',
+ session_id: 'ses_123',
+} satisfies SecurityFindingAuditEventFinding;
+
+const baseInput = {
+ owner: { type: 'user' as const, userId: 'user_123' },
+ finding,
+ actor: buildSecurityFindingAuditHumanActor({
+ id: 'user_123',
+ email: 'owner@example.com',
+ name: 'Owner User',
+ isAdmin: false,
+ }),
+ action: SecurityAuditLogAction.FindingCreated,
+ occurredAt: '2026-06-12T10:00:00.000Z',
+ eventKey: deriveSecurityFindingAuditEventKey([
+ 'user',
+ 'user_123',
+ finding.id,
+ SecurityAuditLogAction.FindingCreated,
+ 'source:42',
+ ]),
+ sourceContext: SecurityFindingAuditSourceContext.SecuritySync,
+ metadata: { source: 'dependabot', alert_number: 42 },
+};
+
+describe('security finding audit contract', () => {
+ it('builds compact snapshots with normalized timestamps only', () => {
+ const snapshot = buildSecurityFindingAuditSnapshot(finding);
+
+ expect(snapshot).toMatchObject({
+ finding_id: finding.id,
+ source: 'dependabot',
+ source_id: '42',
+ repo_full_name: 'kilo/example',
+ title: finding.title,
+ severity: 'high',
+ status: 'open',
+ first_detected_at: '2026-06-01T12:30:00.000Z',
+ sla_due_at: '2026-06-08T12:30:00.000Z',
+ });
+ expect(snapshot.fixed_at).toBeNull();
+ });
+
+ it('builds insert values with owner, finding, event, and snapshot fields', () => {
+ const values = buildSecurityFindingAuditLogValues(baseInput);
+
+ expect(values).toMatchObject({
+ owned_by_user_id: 'user_123',
+ owned_by_organization_id: null,
+ actor_id: 'user_123',
+ actor_email: 'owner@example.com',
+ actor_name: 'Owner User',
+ actor_type: SecurityAuditLogActorType.CustomerUser,
+ action: SecurityAuditLogAction.FindingCreated,
+ resource_type: 'security_finding',
+ resource_id: finding.id,
+ finding_id: finding.id,
+ occurred_at: '2026-06-12T10:00:00.000Z',
+ event_key: baseInput.eventKey,
+ schema_version: SECURITY_FINDING_AUDIT_SCHEMA_VERSION,
+ source_context: SecurityFindingAuditSourceContext.SecuritySync,
+ });
+ });
+
+ it('persists authoritative admin and system actor classifications', () => {
+ const adminValues = buildSecurityFindingAuditLogValues({
+ ...baseInput,
+ actor: buildSecurityFindingAuditHumanActor({
+ id: 'admin_123',
+ email: 'operator@example.com',
+ name: 'Operator',
+ isAdmin: true,
+ }),
+ });
+ expect(adminValues).toMatchObject({
+ actor_id: 'admin_123',
+ actor_email: 'operator@example.com',
+ actor_name: 'Operator',
+ actor_type: SecurityAuditLogActorType.KiloAdmin,
+ });
+
+ const systemValues = buildSecurityFindingAuditLogValues({
+ ...baseInput,
+ actor: SECURITY_FINDING_AUDIT_SYSTEM_ACTOR,
+ });
+ expect(systemValues).toMatchObject({
+ actor_id: null,
+ actor_email: null,
+ actor_name: null,
+ actor_type: SecurityAuditLogActorType.System,
+ });
+ });
+
+ it('requires a typed actor with stable identity for human events', () => {
+ expect(() =>
+ buildSecurityFindingAuditLogValues({
+ ...baseInput,
+ actor: undefined,
+ } as never)
+ ).toThrow();
+ expect(() =>
+ buildSecurityFindingAuditLogValues({
+ ...baseInput,
+ actor: {
+ type: SecurityAuditLogActorType.CustomerUser,
+ id: '',
+ email: null,
+ name: null,
+ },
+ })
+ ).toThrow();
+ expect(() =>
+ buildSecurityFindingAuditLogValues({
+ ...baseInput,
+ actor: {
+ type: SecurityAuditLogActorType.System,
+ id: 'unexpected-human-id',
+ },
+ } as never)
+ ).toThrow();
+ });
+
+ it('rejects owner mismatch before insert', () => {
+ expect(() =>
+ buildSecurityFindingAuditLogValues({
+ ...baseInput,
+ owner: { type: 'user', userId: 'other_user' },
+ })
+ ).toThrow('owner does not match');
+ });
+
+ it('rejects snapshot finding mismatch', () => {
+ expect(() =>
+ buildSecurityFindingAuditLogValues({
+ ...baseInput,
+ snapshot: {
+ ...buildSecurityFindingAuditSnapshot(finding),
+ finding_id: '22222222-2222-4222-8222-222222222222',
+ },
+ })
+ ).toThrow('snapshot finding_id must match');
+ });
+
+ it('rejects non-reportable actions', () => {
+ expect(() =>
+ buildSecurityFindingAuditLogValues({
+ ...baseInput,
+ action: SecurityAuditLogAction.FindingAnalysisStarted,
+ })
+ ).toThrow('Action is not reportable');
+ });
+
+ it('rejects identity and sensitive values in JSON payloads', () => {
+ expect(() =>
+ buildSecurityFindingAuditLogValues({
+ ...baseInput,
+ metadata: { actor_email: 'owner@example.com' },
+ })
+ ).toThrow('Audit JSON field is not allowed');
+
+ expect(() =>
+ buildSecurityFindingAuditLogValues({
+ ...baseInput,
+ metadata: { rationale: 'contact owner@example.com' },
+ })
+ ).toThrow('appears to contain an email');
+ });
+
+ it('uses deterministic escaped event keys', () => {
+ expect(deriveSecurityFindingAuditEventKey(['owner:user_123', 'finding/42'])).toBe(
+ 'security_finding_audit:v1:owner%3Auser_123:finding%2F42'
+ );
+ });
+
+ it('inserts idempotently through caller-provided db or transaction', async () => {
+ const insertedValues: NewSecurityFindingAuditLogValues[] = [];
+ const db: SecurityFindingAuditWriterDb = {
+ insert: () => ({
+ values: values => ({
+ onConflictDoNothing: () => ({
+ returning: async () => {
+ insertedValues.push(values);
+ return [{ id: 'audit_1' }];
+ },
+ }),
+ }),
+ }),
+ };
+
+ const result = await insertSecurityFindingAuditEvent(db, baseInput);
+
+ expect(result).toEqual({ inserted: true, id: 'audit_1' });
+ expect(insertedValues[0]?.event_key).toBe(baseInput.eventKey);
+ });
+});
diff --git a/packages/worker-utils/src/security-finding-audit.ts b/packages/worker-utils/src/security-finding-audit.ts
new file mode 100644
index 0000000000..a105fd15a5
--- /dev/null
+++ b/packages/worker-utils/src/security-finding-audit.ts
@@ -0,0 +1,463 @@
+import { security_audit_log } from '@kilocode/db/schema';
+import type { SecurityAuditLogEntry } from '@kilocode/db/schema';
+import {
+ SecurityAuditLogAction,
+ SecurityAuditLogActorType,
+ SecurityFindingAuditSourceContext,
+ SecuritySeverity,
+} from '@kilocode/db/schema-types';
+import * as z from 'zod';
+
+export const SECURITY_FINDING_AUDIT_SCHEMA_VERSION = 1;
+export const SECURITY_FINDING_AUDIT_EVENT_KEY_PREFIX = 'security_finding_audit:v1';
+
+export const REPORTABLE_SECURITY_FINDING_AUDIT_ACTIONS = [
+ SecurityAuditLogAction.FindingCreated,
+ SecurityAuditLogAction.FindingSeverityChanged,
+ SecurityAuditLogAction.FindingStatusChange,
+ SecurityAuditLogAction.FindingDismissed,
+ SecurityAuditLogAction.FindingAutoDismissed,
+ SecurityAuditLogAction.FindingSuperseded,
+ SecurityAuditLogAction.FindingAnalysisCompleted,
+ SecurityAuditLogAction.FindingAnalysisFailed,
+ SecurityAuditLogAction.RemediationQueued,
+ SecurityAuditLogAction.RemediationPrOpened,
+ SecurityAuditLogAction.RemediationFailed,
+ SecurityAuditLogAction.RemediationBlocked,
+ SecurityAuditLogAction.RemediationNoChangesNeeded,
+ SecurityAuditLogAction.RemediationCancelled,
+ SecurityAuditLogAction.FindingDeleted,
+] as const;
+
+const ReportableSecurityFindingAuditActionSchema = z
+ .nativeEnum(SecurityAuditLogAction)
+ .refine(
+ (action): action is (typeof REPORTABLE_SECURITY_FINDING_AUDIT_ACTIONS)[number] =>
+ REPORTABLE_SECURITY_FINDING_AUDIT_ACTIONS.includes(
+ action as (typeof REPORTABLE_SECURITY_FINDING_AUDIT_ACTIONS)[number]
+ ),
+ 'Action is not reportable Security Finding activity'
+ );
+
+const IsoTimestampSchema = z.string().datetime({ offset: true });
+const NonEmptyStringSchema = z.string().trim().min(1);
+const UuidSchema = z.string().uuid();
+
+export const SecurityFindingAuditOwnerSchema = z.discriminatedUnion('type', [
+ z.object({ type: z.literal('user'), userId: NonEmptyStringSchema }),
+ z.object({ type: z.literal('organization'), organizationId: UuidSchema }),
+]);
+
+export type SecurityFindingAuditOwner = z.infer;
+
+const SecurityFindingAuditCustomerActorSchema = z
+ .object({
+ type: z.literal(SecurityAuditLogActorType.CustomerUser),
+ id: NonEmptyStringSchema,
+ email: z.string().email().nullable(),
+ name: NonEmptyStringSchema.nullable(),
+ })
+ .strict();
+
+const SecurityFindingAuditAdminActorSchema = z
+ .object({
+ type: z.literal(SecurityAuditLogActorType.KiloAdmin),
+ id: NonEmptyStringSchema,
+ email: z.string().email().nullable(),
+ name: NonEmptyStringSchema.nullable(),
+ })
+ .strict();
+
+const SecurityFindingAuditSystemActorSchema = z
+ .object({ type: z.literal(SecurityAuditLogActorType.System) })
+ .strict();
+
+export const SecurityFindingAuditHumanActorSchema = z.discriminatedUnion('type', [
+ SecurityFindingAuditCustomerActorSchema,
+ SecurityFindingAuditAdminActorSchema,
+]);
+
+export const SecurityFindingAuditActorSchema = z.discriminatedUnion('type', [
+ SecurityFindingAuditCustomerActorSchema,
+ SecurityFindingAuditAdminActorSchema,
+ SecurityFindingAuditSystemActorSchema,
+]);
+
+export type SecurityFindingAuditActor = z.infer;
+export type SecurityFindingAuditHumanActor = z.infer;
+
+export const SECURITY_FINDING_AUDIT_SYSTEM_ACTOR = {
+ type: SecurityAuditLogActorType.System,
+} satisfies SecurityFindingAuditActor;
+
+export function buildSecurityFindingAuditHumanActor(params: {
+ id: string;
+ email?: string | null;
+ name?: string | null;
+ isAdmin: boolean;
+}): SecurityFindingAuditHumanActor {
+ return SecurityFindingAuditHumanActorSchema.parse({
+ type: params.isAdmin
+ ? SecurityAuditLogActorType.KiloAdmin
+ : SecurityAuditLogActorType.CustomerUser,
+ id: params.id,
+ email: params.email ?? null,
+ name: params.name ?? null,
+ });
+}
+
+type SanitizedJsonValue =
+ | string
+ | number
+ | boolean
+ | null
+ | SanitizedJsonValue[]
+ | { [key: string]: SanitizedJsonValue };
+
+const SanitizedJsonValueSchema: z.ZodType = z.lazy(() =>
+ z.union([
+ z.string(),
+ z.number().finite(),
+ z.boolean(),
+ z.null(),
+ z.array(SanitizedJsonValueSchema),
+ z.record(z.string(), SanitizedJsonValueSchema),
+ ])
+);
+
+const SanitizedJsonObjectSchema = z
+ .record(z.string(), SanitizedJsonValueSchema)
+ .superRefine((value, ctx) => {
+ validateSafeAuditJson(value, ctx);
+ });
+
+export const SecurityFindingAuditSnapshotSchema = z
+ .object({
+ finding_id: UuidSchema,
+ source: NonEmptyStringSchema,
+ source_id: NonEmptyStringSchema,
+ repo_full_name: NonEmptyStringSchema,
+ title: NonEmptyStringSchema,
+ severity: z.enum([
+ SecuritySeverity.CRITICAL,
+ SecuritySeverity.HIGH,
+ SecuritySeverity.MEDIUM,
+ SecuritySeverity.LOW,
+ ]),
+ status: NonEmptyStringSchema,
+ package_name: NonEmptyStringSchema.optional(),
+ package_ecosystem: NonEmptyStringSchema.optional(),
+ manifest_path: NonEmptyStringSchema.optional(),
+ patched_version: NonEmptyStringSchema.optional(),
+ ghsa_id: NonEmptyStringSchema.optional(),
+ cve_id: NonEmptyStringSchema.optional(),
+ cwe_ids: z.array(NonEmptyStringSchema).optional(),
+ cvss_score: z.union([z.string(), z.number().finite()]).optional(),
+ dependabot_html_url: z.string().url().optional(),
+ first_detected_at: IsoTimestampSchema,
+ fixed_at: IsoTimestampSchema.nullable(),
+ sla_due_at: IsoTimestampSchema.nullable(),
+ canonical_finding_id: UuidSchema.optional(),
+ remediation_attempt_id: UuidSchema.optional(),
+ session_id: NonEmptyStringSchema.optional(),
+ notification_id: UuidSchema.optional(),
+ })
+ .strict()
+ .superRefine((value, ctx) => {
+ validateSafeAuditJson(value, ctx);
+ });
+
+export type SecurityFindingAuditSnapshot = z.infer;
+
+export type SecurityFindingAuditSnapshotSource = {
+ id: string;
+ source: string;
+ source_id: string;
+ repo_full_name: string;
+ title: string;
+ severity: string;
+ status: string;
+ package_name?: string | null;
+ package_ecosystem?: string | null;
+ manifest_path?: string | null;
+ patched_version?: string | null;
+ ghsa_id?: string | null;
+ cve_id?: string | null;
+ cwe_ids?: string[] | null;
+ cvss_score?: string | number | null;
+ dependabot_html_url?: string | null;
+ first_detected_at: string | Date;
+ fixed_at: string | Date | null;
+ sla_due_at: string | Date | null;
+ session_id?: string | null;
+};
+
+export type SecurityFindingAuditSnapshotExtras = {
+ canonical_finding_id?: string | null;
+ remediation_attempt_id?: string | null;
+ session_id?: string | null;
+ notification_id?: string | null;
+};
+
+export type SecurityFindingAuditEventFinding = SecurityFindingAuditSnapshotSource & {
+ owned_by_user_id: string | null;
+ owned_by_organization_id: string | null;
+};
+
+export const SecurityFindingAuditEventSchema = z.object({
+ owner: SecurityFindingAuditOwnerSchema,
+ finding: z.object({
+ id: UuidSchema,
+ owned_by_user_id: z.string().min(1).nullable(),
+ owned_by_organization_id: UuidSchema.nullable(),
+ }),
+ actor: SecurityFindingAuditActorSchema,
+ action: ReportableSecurityFindingAuditActionSchema,
+ occurredAt: IsoTimestampSchema,
+ sourceOccurredAt: IsoTimestampSchema.nullable().optional(),
+ eventKey: NonEmptyStringSchema,
+ sourceContext: z.nativeEnum(SecurityFindingAuditSourceContext),
+ snapshot: SecurityFindingAuditSnapshotSchema,
+ beforeState: SanitizedJsonObjectSchema.optional(),
+ afterState: SanitizedJsonObjectSchema.optional(),
+ metadata: SanitizedJsonObjectSchema.optional(),
+});
+
+export type SecurityFindingAuditEventInput = {
+ owner: SecurityFindingAuditOwner;
+ finding: SecurityFindingAuditEventFinding;
+ actor: SecurityFindingAuditActor;
+ action: SecurityAuditLogAction;
+ occurredAt: string | Date;
+ sourceOccurredAt?: string | Date | null;
+ eventKey: string;
+ sourceContext: SecurityFindingAuditSourceContext;
+ snapshot?: SecurityFindingAuditSnapshot;
+ snapshotExtras?: SecurityFindingAuditSnapshotExtras;
+ beforeState?: Record;
+ afterState?: Record;
+ metadata?: Record;
+};
+
+export type NewSecurityFindingAuditLogValues = typeof security_audit_log.$inferInsert;
+
+type SecurityFindingAuditInsertReturning = {
+ returning(selection: { id: typeof security_audit_log.id }): Promise>;
+};
+
+type SecurityFindingAuditInsertConflict = {
+ onConflictDoNothing(): SecurityFindingAuditInsertReturning;
+};
+
+type SecurityFindingAuditInsertValues = {
+ values(values: NewSecurityFindingAuditLogValues): SecurityFindingAuditInsertConflict;
+};
+
+export type SecurityFindingAuditWriterDb = {
+ insert(table: typeof security_audit_log): SecurityFindingAuditInsertValues;
+};
+
+export function buildSecurityFindingAuditSnapshot(
+ finding: SecurityFindingAuditSnapshotSource,
+ extras: SecurityFindingAuditSnapshotExtras = {}
+): SecurityFindingAuditSnapshot {
+ const snapshot = {
+ finding_id: finding.id,
+ source: finding.source,
+ source_id: finding.source_id,
+ repo_full_name: finding.repo_full_name,
+ title: finding.title,
+ severity: finding.severity,
+ status: finding.status,
+ first_detected_at: normalizeAuditTimestamp(finding.first_detected_at),
+ fixed_at: normalizeAuditTimestamp(finding.fixed_at) ?? null,
+ sla_due_at: normalizeAuditTimestamp(finding.sla_due_at) ?? null,
+ ...pickPresent({
+ package_name: finding.package_name,
+ package_ecosystem: finding.package_ecosystem,
+ manifest_path: finding.manifest_path,
+ patched_version: finding.patched_version,
+ ghsa_id: finding.ghsa_id,
+ cve_id: finding.cve_id,
+ cwe_ids: finding.cwe_ids,
+ cvss_score: finding.cvss_score,
+ dependabot_html_url: finding.dependabot_html_url,
+ canonical_finding_id: extras.canonical_finding_id,
+ remediation_attempt_id: extras.remediation_attempt_id,
+ session_id: extras.session_id ?? finding.session_id,
+ notification_id: extras.notification_id,
+ }),
+ };
+
+ return SecurityFindingAuditSnapshotSchema.parse(snapshot);
+}
+
+export function buildSecurityFindingAuditLogValues(
+ input: SecurityFindingAuditEventInput
+): NewSecurityFindingAuditLogValues {
+ const snapshot =
+ input.snapshot ?? buildSecurityFindingAuditSnapshot(input.finding, input.snapshotExtras);
+ const occurredAt = normalizeAuditTimestamp(input.occurredAt);
+ if (!occurredAt) throw new Error('occurredAt is required');
+
+ const sourceOccurredAt = normalizeAuditTimestamp(input.sourceOccurredAt);
+ const parsed = SecurityFindingAuditEventSchema.parse({
+ owner: input.owner,
+ finding: {
+ id: input.finding.id,
+ owned_by_user_id: input.finding.owned_by_user_id,
+ owned_by_organization_id: input.finding.owned_by_organization_id,
+ },
+ actor: input.actor,
+ action: input.action,
+ occurredAt,
+ sourceOccurredAt,
+ eventKey: input.eventKey,
+ sourceContext: input.sourceContext,
+ snapshot,
+ beforeState: input.beforeState,
+ afterState: input.afterState,
+ metadata: input.metadata,
+ });
+
+ assertFindingOwnerMatchesEventOwner(parsed.owner, input.finding);
+ if (parsed.snapshot.finding_id !== input.finding.id) {
+ throw new Error('Security Finding audit snapshot finding_id must match finding_id');
+ }
+
+ const ownerColumns = getAuditOwnerColumns(parsed.owner);
+ const humanActor = parsed.actor.type === SecurityAuditLogActorType.System ? null : parsed.actor;
+
+ return {
+ ...ownerColumns,
+ actor_id: humanActor?.id ?? null,
+ actor_email: humanActor?.email ?? null,
+ actor_name: humanActor?.name ?? null,
+ actor_type: parsed.actor.type,
+ action: parsed.action,
+ resource_type: 'security_finding',
+ resource_id: input.finding.id,
+ before_state: parsed.beforeState,
+ after_state: parsed.afterState,
+ metadata: parsed.metadata,
+ finding_id: input.finding.id,
+ occurred_at: parsed.occurredAt,
+ source_occurred_at: parsed.sourceOccurredAt ?? null,
+ event_key: parsed.eventKey,
+ schema_version: SECURITY_FINDING_AUDIT_SCHEMA_VERSION,
+ finding_snapshot: parsed.snapshot,
+ source_context: parsed.sourceContext,
+ };
+}
+
+export async function insertSecurityFindingAuditEvent(
+ db: SecurityFindingAuditWriterDb,
+ input: SecurityFindingAuditEventInput
+): Promise<{ inserted: boolean; id: string | null }> {
+ const values = buildSecurityFindingAuditLogValues(input);
+ const inserted = await db
+ .insert(security_audit_log)
+ .values(values)
+ .onConflictDoNothing()
+ .returning({ id: security_audit_log.id });
+
+ return { inserted: inserted.length > 0, id: inserted[0]?.id ?? null };
+}
+
+export function deriveSecurityFindingAuditEventKey(parts: readonly string[]): string {
+ if (parts.length === 0) throw new Error('Security Finding audit event key requires parts');
+ return [
+ SECURITY_FINDING_AUDIT_EVENT_KEY_PREFIX,
+ ...parts.map(part => encodeURIComponent(part)),
+ ].join(':');
+}
+
+function assertFindingOwnerMatchesEventOwner(
+ owner: SecurityFindingAuditOwner,
+ finding: Pick<
+ SecurityFindingAuditEventFinding,
+ 'owned_by_user_id' | 'owned_by_organization_id' | 'id'
+ >
+): void {
+ if (owner.type === 'user') {
+ if (finding.owned_by_user_id !== owner.userId || finding.owned_by_organization_id !== null) {
+ throw new Error('Security Finding audit event owner does not match finding owner');
+ }
+ return;
+ }
+
+ if (
+ finding.owned_by_organization_id !== owner.organizationId ||
+ finding.owned_by_user_id !== null
+ ) {
+ throw new Error('Security Finding audit event owner does not match finding owner');
+ }
+}
+
+function getAuditOwnerColumns(owner: SecurityFindingAuditOwner): {
+ owned_by_user_id: string | null;
+ owned_by_organization_id: string | null;
+} {
+ if (owner.type === 'user') {
+ return { owned_by_user_id: owner.userId, owned_by_organization_id: null };
+ }
+ return { owned_by_user_id: null, owned_by_organization_id: owner.organizationId };
+}
+
+function normalizeAuditTimestamp(value: string | Date | null | undefined): string | undefined {
+ if (value === null || value === undefined) return undefined;
+ const date = value instanceof Date ? value : new Date(value);
+ if (Number.isNaN(date.getTime())) throw new Error(`Invalid audit timestamp: ${String(value)}`);
+ return date.toISOString();
+}
+
+function pickPresent>(values: T): Partial {
+ const entries = Object.entries(values).filter(([, value]) => {
+ if (value === null || value === undefined) return false;
+ if (Array.isArray(value) && value.length === 0) return false;
+ return value !== '';
+ });
+ return Object.fromEntries(entries) as Partial;
+}
+
+const DISALLOWED_AUDIT_JSON_KEY_PATTERN =
+ /(^|_)(actor|recipient|email|prompt|rawmarkdown|raw_markdown|transcript|assistant|provider_response|authorization|auth_header|cookie|token|secret|password|credential|headers|raw_error)(_|$)/i;
+const EMAIL_VALUE_PATTERN = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i;
+
+function validateSafeAuditJson(
+ value: unknown,
+ ctx: z.RefinementCtx,
+ path: (string | number)[] = []
+) {
+ if (Array.isArray(value)) {
+ for (const [index, item] of value.entries()) {
+ validateSafeAuditJson(item, ctx, [...path, index]);
+ }
+ return;
+ }
+
+ if (value && typeof value === 'object') {
+ for (const [key, child] of Object.entries(value)) {
+ const childPath = [...path, key];
+ if (DISALLOWED_AUDIT_JSON_KEY_PATTERN.test(key)) {
+ ctx.addIssue({
+ code: z.ZodIssueCode.custom,
+ message: `Audit JSON field is not allowed: ${childPath.join('.')}`,
+ path: childPath,
+ });
+ }
+ validateSafeAuditJson(child, ctx, childPath);
+ }
+ return;
+ }
+
+ if (typeof value === 'string' && EMAIL_VALUE_PATTERN.test(value)) {
+ ctx.addIssue({
+ code: z.ZodIssueCode.custom,
+ message: `Audit JSON value appears to contain an email address: ${path.join('.')}`,
+ path,
+ });
+ }
+}
+
+export type SecurityFindingAuditLogEntry = SecurityAuditLogEntry;
diff --git a/packages/worker-utils/src/security-remediation-policy.test.ts b/packages/worker-utils/src/security-remediation-policy.test.ts
index a48df43bf9..2be12640a8 100644
--- a/packages/worker-utils/src/security-remediation-policy.test.ts
+++ b/packages/worker-utils/src/security-remediation-policy.test.ts
@@ -1,5 +1,6 @@
import { describe, expect, it } from 'vitest';
import {
+ buildSecurityFindingAnalysisInput,
computeSecurityRemediationAnalysisFingerprint,
decideSecurityRemediationEligibility,
type SecurityRemediationConfig,
@@ -14,20 +15,36 @@ const baseConfig: SecurityRemediationConfig = {
auto_remediation_enabled_at: '2026-01-01T00:00:00.000Z',
};
-const baseFinding: SecurityRemediationFinding = {
+const baseFindingData = {
id: 'finding-1',
+ source: 'dependabot',
+ source_id: '42',
status: 'open',
severity: 'high',
repo_full_name: 'kilo/repo',
package_name: 'lodash',
package_ecosystem: 'npm',
+ dependency_scope: 'runtime',
+ cve_id: 'CVE-2021-23337',
+ ghsa_id: 'GHSA-35jh-r3h4-6jhm',
+ cwe_ids: ['CWE-1321'],
+ cvss_score: '7.2',
+ title: 'Command Injection in lodash',
+ description: 'Versions before 4.17.21 are vulnerable.',
+ vulnerable_version_range: '< 4.17.21',
patched_version: '4.17.21',
manifest_path: 'package.json',
+ raw_data: { updated_at: '2026-01-02T00:00:00.000Z' },
+};
+
+const baseFinding: SecurityRemediationFinding = {
+ ...baseFindingData,
last_synced_at: '2026-01-02T00:00:00.000Z',
analysis_status: 'completed',
analysis_completed_at: '2026-01-02T00:05:00.000Z',
analysis: {
analyzedAt: '2026-01-02T00:05:00.000Z',
+ findingDataSnapshot: buildSecurityFindingAnalysisInput(baseFindingData),
sandboxAnalysis: {
isExploitable: true,
suggestedAction: 'open_pr',
@@ -41,6 +58,20 @@ const baseFinding: SecurityRemediationFinding = {
},
};
+function withCurrentFindingDataSnapshot(
+ finding: SecurityRemediationFinding
+): SecurityRemediationFinding {
+ return {
+ ...finding,
+ analysis: finding.analysis
+ ? {
+ ...finding.analysis,
+ findingDataSnapshot: buildSecurityFindingAnalysisInput(finding),
+ }
+ : null,
+ };
+}
+
const emptyBlockState = {
hasActiveAttempt: false,
hasPrOpened: false,
@@ -69,10 +100,94 @@ describe('decideSecurityRemediationEligibility', () => {
);
});
- it('rejects stale analysis after later sync', () => {
+ it('keeps analysis fresh when a later sync observes unchanged finding data', () => {
+ const decision = decideSecurityRemediationEligibility({
+ finding: {
+ ...baseFinding,
+ last_synced_at: '2026-01-03T00:00:00.000Z',
+ },
+ config: baseConfig,
+ isAgentEnabled: true,
+ repoFullNamesInScope: ['kilo/repo'],
+ origin: 'manual',
+ blockState: emptyBlockState,
+ });
+
+ expect(decision).toMatchObject({ eligible: true, reason: 'eligible' });
+ });
+
+ it('uses the database completion timestamp when legacy analysis JSON omits timestamps', () => {
+ const decision = decideSecurityRemediationEligibility({
+ finding: {
+ ...baseFinding,
+ last_synced_at: '2026-01-03T00:00:00.000Z',
+ analysis: {
+ ...baseFinding.analysis,
+ analyzedAt: null,
+ sandboxAnalysis: {
+ ...baseFinding.analysis?.sandboxAnalysis,
+ isExploitable: true,
+ suggestedAction: 'open_pr',
+ analysisAt: null,
+ },
+ },
+ },
+ config: baseConfig,
+ isAgentEnabled: true,
+ repoFullNamesInScope: ['kilo/repo'],
+ origin: 'manual',
+ blockState: emptyBlockState,
+ });
+
+ expect(decision).toMatchObject({
+ eligible: true,
+ reason: 'eligible',
+ analysisCompletedAt: '2026-01-02T00:05:00.000Z',
+ });
+ });
+
+ it('rejects analysis when material finding data changed', () => {
+ const decision = decideSecurityRemediationEligibility({
+ finding: {
+ ...baseFinding,
+ patched_version: '4.17.22',
+ last_synced_at: '2026-01-03T00:01:00.000Z',
+ },
+ config: baseConfig,
+ isAgentEnabled: true,
+ repoFullNamesInScope: ['kilo/repo'],
+ origin: 'manual',
+ blockState: emptyBlockState,
+ });
+
+ expect(decision).toMatchObject({ eligible: false, reason: 'stale_analysis' });
+ });
+
+ it('rejects analysis when the source revision changed during analysis', () => {
const decision = decideSecurityRemediationEligibility({
finding: {
...baseFinding,
+ raw_data: { updated_at: '2026-01-02T00:03:00.000Z' },
+ },
+ config: baseConfig,
+ isAgentEnabled: true,
+ repoFullNamesInScope: ['kilo/repo'],
+ origin: 'manual',
+ blockState: emptyBlockState,
+ });
+
+ expect(decision).toMatchObject({ eligible: false, reason: 'stale_analysis' });
+ });
+
+ it('uses source revision time for legacy analysis without a finding snapshot', () => {
+ const legacyAnalysis = {
+ ...baseFinding.analysis,
+ findingDataSnapshot: undefined,
+ };
+ const unchangedDecision = decideSecurityRemediationEligibility({
+ finding: {
+ ...baseFinding,
+ analysis: legacyAnalysis,
last_synced_at: '2026-01-03T00:00:00.000Z',
},
config: baseConfig,
@@ -81,12 +196,45 @@ describe('decideSecurityRemediationEligibility', () => {
origin: 'manual',
blockState: emptyBlockState,
});
+ const changedDecision = decideSecurityRemediationEligibility({
+ finding: {
+ ...baseFinding,
+ analysis: legacyAnalysis,
+ raw_data: { updated_at: '2026-01-03T00:00:00.000Z' },
+ },
+ config: baseConfig,
+ isAgentEnabled: true,
+ repoFullNamesInScope: ['kilo/repo'],
+ origin: 'manual',
+ blockState: emptyBlockState,
+ });
+
+ expect(unchangedDecision).toMatchObject({ eligible: true, reason: 'eligible' });
+ expect(changedDecision).toMatchObject({ eligible: false, reason: 'stale_analysis' });
+ });
+
+ it('fails closed when legacy analysis has no source revision', () => {
+ const decision = decideSecurityRemediationEligibility({
+ finding: {
+ ...baseFinding,
+ raw_data: null,
+ analysis: {
+ ...baseFinding.analysis,
+ findingDataSnapshot: undefined,
+ },
+ },
+ config: baseConfig,
+ isAgentEnabled: true,
+ repoFullNamesInScope: ['kilo/repo'],
+ origin: 'manual',
+ blockState: emptyBlockState,
+ });
expect(decision).toMatchObject({ eligible: false, reason: 'stale_analysis' });
});
it('allows manual remediation after review when exploitability is unknown but patch path is concrete', () => {
- const manualReviewFinding: SecurityRemediationFinding = {
+ const manualReviewFinding = withCurrentFindingDataSnapshot({
...baseFinding,
package_name: 'handlebars',
patched_version: '4.7.7',
@@ -101,7 +249,7 @@ describe('decideSecurityRemediationEligibility', () => {
usageLocations: [],
},
},
- };
+ });
const decision = decideSecurityRemediationEligibility({
finding: manualReviewFinding,
@@ -140,7 +288,7 @@ describe('decideSecurityRemediationEligibility', () => {
it('rejects manual review override without a concrete fix path', () => {
const decision = decideSecurityRemediationEligibility({
- finding: {
+ finding: withCurrentFindingDataSnapshot({
...baseFinding,
patched_version: null,
manifest_path: null,
@@ -154,7 +302,7 @@ describe('decideSecurityRemediationEligibility', () => {
usageLocations: [],
},
},
- },
+ }),
config: baseConfig,
isAgentEnabled: true,
repoFullNamesInScope: ['kilo/repo'],
@@ -201,7 +349,7 @@ describe('decideSecurityRemediationEligibility', () => {
it('gates automatic policy by threshold and enablement time', () => {
const belowThreshold = decideSecurityRemediationEligibility({
- finding: { ...baseFinding, severity: 'medium' },
+ finding: withCurrentFindingDataSnapshot({ ...baseFinding, severity: 'medium' }),
config: baseConfig,
isAgentEnabled: true,
repoFullNamesInScope: ['kilo/repo'],
diff --git a/packages/worker-utils/src/security-remediation-policy.ts b/packages/worker-utils/src/security-remediation-policy.ts
index 8b7fece973..42652bfc5f 100644
--- a/packages/worker-utils/src/security-remediation-policy.ts
+++ b/packages/worker-utils/src/security-remediation-policy.ts
@@ -1,3 +1,5 @@
+import type { SecurityFindingAnalysisInput } from '@kilocode/db/schema-types';
+
export type SecurityRemediationOrigin = 'auto_policy' | 'bulk_existing' | 'manual';
export type SecurityRemediationMinSeverity = 'critical' | 'high' | 'medium' | 'all';
export type SecurityRemediationSeverityRank = 0 | 1 | 2 | 3;
@@ -23,6 +25,7 @@ export type SecurityRemediationSandboxAnalysis = {
export type SecurityRemediationAnalysis = {
sandboxAnalysis?: SecurityRemediationSandboxAnalysis;
+ findingDataSnapshot?: SecurityFindingAnalysisInput;
analyzedAt?: string | null;
modelUsed?: string | null;
analysisModel?: string | null;
@@ -30,19 +33,33 @@ export type SecurityRemediationAnalysis = {
correlationId?: string | null;
};
-export type SecurityRemediationFinding = {
- id: string;
+export type SecurityFindingAnalysisInputSource = {
+ source: string;
+ source_id: string;
status: string;
severity: string | null;
repo_full_name: string;
package_name: string;
package_ecosystem: string;
- patched_version?: string | null;
- manifest_path?: string | null;
- last_synced_at?: string | null;
- analysis_status?: string | null;
- analysis_completed_at?: string | null;
- analysis?: SecurityRemediationAnalysis | null;
+ dependency_scope: string | null;
+ cve_id: string | null;
+ ghsa_id: string | null;
+ cwe_ids: string[] | null;
+ cvss_score: string | number | null;
+ title: string;
+ description: string | null;
+ vulnerable_version_range: string | null;
+ patched_version: string | null;
+ manifest_path: string | null;
+ raw_data: { updated_at?: string | null } | null;
+};
+
+export type SecurityRemediationFinding = SecurityFindingAnalysisInputSource & {
+ id: string;
+ last_synced_at: string | null;
+ analysis_status: string | null;
+ analysis_completed_at: string | null;
+ analysis: SecurityRemediationAnalysis | null;
};
export type SecurityRemediationBlockState = {
@@ -52,28 +69,40 @@ export type SecurityRemediationBlockState = {
hasRetryableTerminalForFinding?: boolean;
};
-export type SecurityRemediationCapabilityReason =
- | 'eligible'
- | 'finding_not_open'
- | 'repo_not_in_scope'
- | 'analysis_required'
- | 'sandbox_analysis_required'
- | 'stale_analysis'
- | 'not_exploitable'
- | 'exploitability_unknown'
- | 'manual_review_required'
- | 'monitor_required'
- | 'triage_only'
- | 'action_not_concrete'
- | 'remediation_active'
- | 'pr_already_opened'
- | 'duplicate_analysis_result'
- | 'retry_not_allowed'
- | 'security_agent_disabled'
- | 'auto_remediation_disabled'
- | 'include_existing_disabled'
- | 'below_threshold'
- | 'before_enablement';
+export const SECURITY_REMEDIATION_REJECTION_REASONS = [
+ 'finding_not_open',
+ 'repo_not_in_scope',
+ 'analysis_required',
+ 'sandbox_analysis_required',
+ 'stale_analysis',
+ 'not_exploitable',
+ 'exploitability_unknown',
+ 'manual_review_required',
+ 'monitor_required',
+ 'triage_only',
+ 'action_not_concrete',
+ 'remediation_active',
+ 'pr_already_opened',
+ 'duplicate_analysis_result',
+ 'retry_not_allowed',
+ 'security_agent_disabled',
+ 'auto_remediation_disabled',
+ 'include_existing_disabled',
+ 'below_threshold',
+ 'before_enablement',
+] as const;
+
+export type SecurityRemediationRejectionReason =
+ (typeof SECURITY_REMEDIATION_REJECTION_REASONS)[number];
+export type SecurityRemediationCapabilityReason = 'eligible' | SecurityRemediationRejectionReason;
+
+export const SECURITY_REMEDIATION_ADMISSION_REJECTION_REASONS = [
+ ...SECURITY_REMEDIATION_REJECTION_REASONS,
+ 'finding_not_found',
+] as const;
+
+export type SecurityRemediationAdmissionRejectionReason =
+ (typeof SECURITY_REMEDIATION_ADMISSION_REJECTION_REASONS)[number];
export type SecurityRemediationEligibilityParams = {
finding: SecurityRemediationFinding;
@@ -133,6 +162,71 @@ function normalizeTimestamp(value: string | null | undefined): string | null {
return Number.isFinite(time) ? new Date(time).toISOString() : null;
}
+export function buildSecurityFindingAnalysisInput(
+ finding: SecurityFindingAnalysisInputSource
+): SecurityFindingAnalysisInput {
+ return {
+ schemaVersion: 1,
+ source: finding.source,
+ sourceId: finding.source_id,
+ sourceUpdatedAt: normalizeTimestamp(finding.raw_data?.updated_at),
+ repoFullName: finding.repo_full_name,
+ status: finding.status,
+ severity: finding.severity,
+ packageName: finding.package_name,
+ packageEcosystem: finding.package_ecosystem,
+ dependencyScope: finding.dependency_scope,
+ cveId: finding.cve_id,
+ ghsaId: finding.ghsa_id,
+ cweIds: [...new Set(finding.cwe_ids ?? [])].sort(),
+ cvssScore: finding.cvss_score === null ? null : String(finding.cvss_score),
+ title: finding.title,
+ description: finding.description,
+ vulnerableVersionRange: finding.vulnerable_version_range,
+ patchedVersion: finding.patched_version,
+ manifestPath: finding.manifest_path,
+ };
+}
+
+function serializeSecurityFindingAnalysisInput(
+ input: SecurityFindingAnalysisInput | undefined
+): string | null {
+ if (!input || input.schemaVersion !== 1 || !Array.isArray(input.cweIds)) return null;
+ return JSON.stringify([
+ input.schemaVersion,
+ input.source,
+ input.sourceId,
+ input.sourceUpdatedAt,
+ input.repoFullName,
+ input.status,
+ input.severity,
+ input.packageName,
+ input.packageEcosystem,
+ input.dependencyScope,
+ input.cveId,
+ input.ghsaId,
+ input.cweIds,
+ input.cvssScore,
+ input.title,
+ input.description,
+ input.vulnerableVersionRange,
+ input.patchedVersion,
+ input.manifestPath,
+ ]);
+}
+
+function securityFindingAnalysisInputMatches(
+ analyzedInput: SecurityFindingAnalysisInput,
+ finding: SecurityFindingAnalysisInputSource
+): boolean {
+ const serializedAnalyzedInput = serializeSecurityFindingAnalysisInput(analyzedInput);
+ if (!serializedAnalyzedInput) return false;
+ return (
+ serializedAnalyzedInput ===
+ serializeSecurityFindingAnalysisInput(buildSecurityFindingAnalysisInput(finding))
+ );
+}
+
export function getSecurityRemediationAnalysisCompletedAt(
finding: SecurityRemediationFinding
): string | null {
@@ -207,9 +301,14 @@ function hasConcreteRemediationPath(finding: SecurityRemediationFinding): boolea
}
function isAnalysisFresh(finding: SecurityRemediationFinding, completedAt: string): boolean {
- const lastSyncedAt = normalizeTimestamp(finding.last_synced_at);
- if (!lastSyncedAt) return true;
- return Date.parse(completedAt) >= Date.parse(lastSyncedAt);
+ const analyzedInput = finding.analysis?.findingDataSnapshot;
+ if (analyzedInput !== undefined) {
+ return securityFindingAnalysisInputMatches(analyzedInput, finding);
+ }
+
+ const sourceUpdatedAt = normalizeTimestamp(finding.raw_data?.updated_at);
+ if (!sourceUpdatedAt) return false;
+ return Date.parse(completedAt) >= Date.parse(sourceUpdatedAt);
}
function isRepoInScope(params: {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0b3aecd4da..d1aad7b940 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -857,6 +857,9 @@ importers:
react-countup:
specifier: 6.5.3
version: 6.5.3(react@19.2.6)
+ react-day-picker:
+ specifier: 10.0.1
+ version: 10.0.1(@types/react@19.2.14)(react@19.2.6)
react-dom:
specifier: 19.2.6
version: 19.2.6(react@19.2.6)
@@ -4141,6 +4144,9 @@ packages:
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
engines: {node: '>=12'}
+ '@date-fns/tz@1.5.0':
+ resolution: {integrity: sha512-lwYN/vDPeNRULcepoE/LO2Pgx+7/RV+S9ARfbc9lr2DtGkOD7pAiruHvbR1RX3Qyf6ja47EWJDMsNK5vK08DJg==}
+
'@dependents/detective-less@5.0.1':
resolution: {integrity: sha512-Y6+WUMsTFWE5jb20IFP4YGa5IrGY/+a/FbOSjDF/wz9gepU2hwCYSXRHP/vPwBvwcY3SVMASt4yXxbXNXigmZQ==}
engines: {node: '>=18'}
@@ -14304,6 +14310,16 @@ packages:
peerDependencies:
react: '>= 16.3.0'
+ react-day-picker@10.0.1:
+ resolution: {integrity: sha512-eNh6BlwcYInWaJtRv18mXQ06Ys/H6rdTZAnTaSdOYJuTpwP1JMCHNd1FDRadA+gbeinq+psdULN5Xnowy9mV8w==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@types/react': '>=16.8.0'
+ react: '>=16.8.0'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
react-devtools-core@6.1.5:
resolution: {integrity: sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==}
@@ -17884,9 +17900,9 @@ snapshots:
'@chromaui/rrweb-snapshot': 2.0.0-alpha.18-noAbsolute
'@playwright/test': 1.58.2
'@segment/analytics-node': 2.1.3
- '@storybook/addon-essentials': 8.5.8(@types/react@19.2.14)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
+ '@storybook/addon-essentials': 8.5.8(@types/react@19.2.14)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
'@storybook/csf': 0.1.13
- '@storybook/manager-api': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
+ '@storybook/manager-api': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
'@storybook/server-webpack5': 8.5.8(@swc/core@1.15.18)(esbuild@0.27.4)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))(typescript@5.9.3)
storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))
ts-dedent: 2.2.0
@@ -17912,9 +17928,9 @@ snapshots:
'@chromaui/rrweb-snapshot': 2.0.0-alpha.18-noAbsolute
'@playwright/test': 1.58.2
'@segment/analytics-node': 2.1.3
- '@storybook/addon-essentials': 8.5.8(@types/react@19.2.14)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
+ '@storybook/addon-essentials': 8.5.8(@types/react@19.2.14)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
'@storybook/csf': 0.1.13
- '@storybook/manager-api': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
+ '@storybook/manager-api': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
'@storybook/server-webpack5': 8.5.8(esbuild@0.27.4)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))(typescript@5.9.3)
storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))
ts-dedent: 2.2.0
@@ -18007,7 +18023,7 @@ snapshots:
cjs-module-lexer: 1.2.3
esbuild: 0.27.4
miniflare: 4.20260603.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)
- vitest: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@24.12.4)(@vitest/coverage-v8@4.1.6)(@vitest/ui@4.1.6)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)
+ vitest: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@25.5.2)(@vitest/coverage-v8@4.1.6)(@vitest/ui@4.1.6)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)
wrangler: 4.98.0(@cloudflare/workers-types@4.20260605.1)(bufferutil@4.1.0)(utf-8-validate@6.0.6)
zod: 3.25.76
transitivePeerDependencies:
@@ -18333,6 +18349,8 @@ snapshots:
dependencies:
'@jridgewell/trace-mapping': 0.3.9
+ '@date-fns/tz@1.5.0': {}
+
'@dependents/detective-less@5.0.1':
dependencies:
gonzales-pe: 4.3.0
@@ -22988,7 +23006,7 @@ snapshots:
'@stitches/core@1.2.8': {}
- '@storybook/addon-actions@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))':
+ '@storybook/addon-actions@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))':
dependencies:
'@storybook/global': 5.0.0
'@types/uuid': 9.0.8
@@ -22997,26 +23015,26 @@ snapshots:
storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))
uuid: 9.0.1
- '@storybook/addon-backgrounds@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))':
+ '@storybook/addon-backgrounds@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))':
dependencies:
'@storybook/global': 5.0.0
memoizerific: 1.11.3
storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))
ts-dedent: 2.2.0
- '@storybook/addon-controls@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))':
+ '@storybook/addon-controls@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))':
dependencies:
'@storybook/global': 5.0.0
dequal: 2.0.3
storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))
ts-dedent: 2.2.0
- '@storybook/addon-docs@8.5.8(@types/react@19.2.14)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))':
+ '@storybook/addon-docs@8.5.8(@types/react@19.2.14)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))':
dependencies:
'@mdx-js/react': 3.1.1(@types/react@19.2.14)(react@19.2.6)
- '@storybook/blocks': 8.5.8(react-dom@19.2.4(react@19.2.6))(react@19.2.6)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
- '@storybook/csf-plugin': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
- '@storybook/react-dom-shim': 8.5.8(react-dom@19.2.4(react@19.2.6))(react@19.2.6)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
+ '@storybook/blocks': 8.5.8(react-dom@19.2.4(react@19.2.6))(react@19.2.6)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
+ '@storybook/csf-plugin': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
+ '@storybook/react-dom-shim': 8.5.8(react-dom@19.2.4(react@19.2.6))(react@19.2.6)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
react: 19.2.6
react-dom: 19.2.4(react@19.2.6)
storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))
@@ -23037,23 +23055,23 @@ snapshots:
transitivePeerDependencies:
- '@types/react'
- '@storybook/addon-essentials@8.5.8(@types/react@19.2.14)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))':
- dependencies:
- '@storybook/addon-actions': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
- '@storybook/addon-backgrounds': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
- '@storybook/addon-controls': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
- '@storybook/addon-docs': 8.5.8(@types/react@19.2.14)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
- '@storybook/addon-highlight': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
- '@storybook/addon-measure': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
- '@storybook/addon-outline': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
- '@storybook/addon-toolbars': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
- '@storybook/addon-viewport': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
+ '@storybook/addon-essentials@8.5.8(@types/react@19.2.14)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))':
+ dependencies:
+ '@storybook/addon-actions': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
+ '@storybook/addon-backgrounds': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
+ '@storybook/addon-controls': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
+ '@storybook/addon-docs': 8.5.8(@types/react@19.2.14)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
+ '@storybook/addon-highlight': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
+ '@storybook/addon-measure': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
+ '@storybook/addon-outline': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
+ '@storybook/addon-toolbars': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
+ '@storybook/addon-viewport': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))
ts-dedent: 2.2.0
transitivePeerDependencies:
- '@types/react'
- '@storybook/addon-highlight@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))':
+ '@storybook/addon-highlight@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))':
dependencies:
'@storybook/global': 5.0.0
storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))
@@ -23065,13 +23083,13 @@ snapshots:
optionalDependencies:
react: 19.2.6
- '@storybook/addon-measure@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))':
+ '@storybook/addon-measure@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))':
dependencies:
'@storybook/global': 5.0.0
storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))
tiny-invariant: 1.3.3
- '@storybook/addon-outline@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))':
+ '@storybook/addon-outline@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))':
dependencies:
'@storybook/global': 5.0.0
storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))
@@ -23082,16 +23100,16 @@ snapshots:
storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))
ts-dedent: 2.2.0
- '@storybook/addon-toolbars@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))':
+ '@storybook/addon-toolbars@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))':
dependencies:
storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))
- '@storybook/addon-viewport@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))':
+ '@storybook/addon-viewport@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))':
dependencies:
memoizerific: 1.11.3
storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))
- '@storybook/blocks@8.5.8(react-dom@19.2.4(react@19.2.6))(react@19.2.6)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))':
+ '@storybook/blocks@8.5.8(react-dom@19.2.4(react@19.2.6))(react@19.2.6)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))':
dependencies:
'@storybook/csf': 0.1.12
'@storybook/icons': 1.6.0(react-dom@19.2.4(react@19.2.6))(react@19.2.6)
@@ -23103,7 +23121,7 @@ snapshots:
'@storybook/builder-webpack5@8.5.8(@swc/core@1.15.18)(esbuild@0.27.4)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))(typescript@5.9.3)':
dependencies:
- '@storybook/core-webpack': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
+ '@storybook/core-webpack': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
'@types/semver': 7.7.1
browser-assert: 1.2.1
case-sensitive-paths-webpack-plugin: 2.4.0
@@ -23139,7 +23157,7 @@ snapshots:
'@storybook/builder-webpack5@8.5.8(esbuild@0.27.4)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))(typescript@5.9.3)':
dependencies:
- '@storybook/core-webpack': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
+ '@storybook/core-webpack': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
'@types/semver': 7.7.1
browser-assert: 1.2.1
case-sensitive-paths-webpack-plugin: 2.4.0
@@ -23201,11 +23219,11 @@ snapshots:
- uglify-js
- webpack-cli
- '@storybook/components@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))':
+ '@storybook/components@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))':
dependencies:
storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))
- '@storybook/core-webpack@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))':
+ '@storybook/core-webpack@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))':
dependencies:
storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))
ts-dedent: 2.2.0
@@ -23215,7 +23233,7 @@ snapshots:
storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))
ts-dedent: 2.2.0
- '@storybook/csf-plugin@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))':
+ '@storybook/csf-plugin@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))':
dependencies:
storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))
unplugin: 1.16.1
@@ -23240,7 +23258,7 @@ snapshots:
react: 19.2.6
react-dom: 19.2.4(react@19.2.6)
- '@storybook/manager-api@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))':
+ '@storybook/manager-api@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))':
dependencies:
storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))
@@ -23328,17 +23346,17 @@ snapshots:
- uglify-js
- webpack-cli
- '@storybook/preset-server-webpack@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))':
+ '@storybook/preset-server-webpack@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))':
dependencies:
- '@storybook/core-webpack': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
+ '@storybook/core-webpack': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
'@storybook/global': 5.0.0
- '@storybook/server': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
+ '@storybook/server': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
safe-identifier: 0.4.2
storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))
ts-dedent: 2.2.0
yaml-loader: 0.8.1
- '@storybook/preview-api@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))':
+ '@storybook/preview-api@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))':
dependencies:
storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))
@@ -23356,7 +23374,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@storybook/react-dom-shim@8.5.8(react-dom@19.2.4(react@19.2.6))(react@19.2.6)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))':
+ '@storybook/react-dom-shim@8.5.8(react-dom@19.2.4(react@19.2.6))(react@19.2.6)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))':
dependencies:
react: 19.2.6
react-dom: 19.2.4(react@19.2.6)
@@ -23387,8 +23405,8 @@ snapshots:
'@storybook/server-webpack5@8.5.8(@swc/core@1.15.18)(esbuild@0.27.4)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))(typescript@5.9.3)':
dependencies:
'@storybook/builder-webpack5': 8.5.8(@swc/core@1.15.18)(esbuild@0.27.4)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))(typescript@5.9.3)
- '@storybook/preset-server-webpack': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
- '@storybook/server': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
+ '@storybook/preset-server-webpack': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
+ '@storybook/server': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))
transitivePeerDependencies:
- '@rspack/core'
@@ -23401,8 +23419,8 @@ snapshots:
'@storybook/server-webpack5@8.5.8(esbuild@0.27.4)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))(typescript@5.9.3)':
dependencies:
'@storybook/builder-webpack5': 8.5.8(esbuild@0.27.4)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))(typescript@5.9.3)
- '@storybook/preset-server-webpack': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
- '@storybook/server': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
+ '@storybook/preset-server-webpack': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
+ '@storybook/server': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))
transitivePeerDependencies:
- '@rspack/core'
@@ -23413,14 +23431,14 @@ snapshots:
- webpack-cli
optional: true
- '@storybook/server@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))':
+ '@storybook/server@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))':
dependencies:
- '@storybook/components': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
+ '@storybook/components': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
'@storybook/csf': 0.1.12
'@storybook/global': 5.0.0
- '@storybook/manager-api': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
- '@storybook/preview-api': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
- '@storybook/theming': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))
+ '@storybook/manager-api': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
+ '@storybook/preview-api': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
+ '@storybook/theming': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))
storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))
ts-dedent: 2.2.0
yaml: 2.8.4
@@ -23455,7 +23473,7 @@ snapshots:
- supports-color
- ts-node
- '@storybook/theming@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))':
+ '@storybook/theming@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))':
dependencies:
storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))
@@ -30741,6 +30759,14 @@ snapshots:
countup.js: 2.10.0
react: 19.2.6
+ react-day-picker@10.0.1(@types/react@19.2.14)(react@19.2.6):
+ dependencies:
+ '@date-fns/tz': 1.5.0
+ date-fns: 4.1.0
+ react: 19.2.6
+ optionalDependencies:
+ '@types/react': 19.2.14
+
react-devtools-core@6.1.5(bufferutil@4.1.0)(utf-8-validate@6.0.6):
dependencies:
shell-quote: 1.8.4
diff --git a/services/security-auto-analysis/src/analysis-start-lifecycle.integration.test.ts b/services/security-auto-analysis/src/analysis-start-lifecycle.integration.test.ts
index 6839fc2b59..a82255c998 100644
--- a/services/security-auto-analysis/src/analysis-start-lifecycle.integration.test.ts
+++ b/services/security-auto-analysis/src/analysis-start-lifecycle.integration.test.ts
@@ -1,7 +1,20 @@
import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest';
import { randomUUID } from 'crypto';
import { createDrizzleClient } from '@kilocode/db/client';
-import { kilocode_users, security_analysis_queue, security_findings } from '@kilocode/db/schema';
+import {
+ kilocode_users,
+ security_analysis_queue,
+ security_audit_log,
+ security_findings,
+} from '@kilocode/db/schema';
+import {
+ SecurityAuditLogAction,
+ SecurityFindingAuditSourceContext,
+} from '@kilocode/db/schema-types';
+import {
+ deriveSecurityFindingAuditEventKey,
+ SECURITY_FINDING_AUDIT_SCHEMA_VERSION,
+} from '@kilocode/worker-utils/security-finding-audit';
import { eq, inArray } from 'drizzle-orm';
import {
transitionAnalysisCallbackLifecycle,
@@ -33,6 +46,7 @@ describe('analysis start lifecycle durable transitions', () => {
await client.db
.delete(security_analysis_queue)
.where(inArray(security_analysis_queue.finding_id, ids));
+ await client.db.delete(security_audit_log).where(inArray(security_audit_log.finding_id, ids));
await client.db.delete(security_findings).where(inArray(security_findings.id, ids));
});
@@ -83,6 +97,34 @@ describe('analysis start lifecycle durable transitions', () => {
.from(security_analysis_queue)
.where(eq(security_analysis_queue.finding_id, findingId));
expect(queueRows).toEqual([{ status: 'completed' }]);
+
+ const auditRows = await getFindingAuditRows(findingId);
+ expect(auditRows).toHaveLength(1);
+ expect(auditRows[0]).toMatchObject({
+ action: SecurityAuditLogAction.FindingAnalysisCompleted,
+ finding_id: findingId,
+ resource_type: 'security_finding',
+ resource_id: findingId,
+ event_key: deriveSecurityFindingAuditEventKey([
+ `user:${testUserId}`,
+ findingId,
+ SecurityAuditLogAction.FindingAnalysisCompleted,
+ 'manual-triage-claim',
+ ]),
+ schema_version: SECURITY_FINDING_AUDIT_SCHEMA_VERSION,
+ source_context: SecurityFindingAuditSourceContext.AnalysisWorker,
+ before_state: { analysis_status: 'pending' },
+ after_state: {
+ analysis_status: 'completed',
+ suggested_action: 'manual_review',
+ confidence: 'high',
+ },
+ finding_snapshot: expect.objectContaining({
+ finding_id: findingId,
+ repo_full_name: 'kilo/manual-triage-complete',
+ }),
+ });
+ expect(auditRows[0]?.occurred_at).toEqual(expect.any(String));
});
it('terminalizes completed callbacks with queue and finding state settled together', async () => {
@@ -93,7 +135,7 @@ describe('analysis start lifecycle durable transitions', () => {
jobId: 'callback-completed-job',
queueStatus: 'running',
});
- const analysis = createAnalysis('callback-completed');
+ const analysis = createSandboxAnalysis('callback-completed', 'succeeded');
await expect(
transitionAnalysisCallbackLifecycle(client.db as never, {
@@ -128,6 +170,70 @@ describe('analysis start lifecycle durable transitions', () => {
.from(security_analysis_queue)
.where(eq(security_analysis_queue.finding_id, findingId));
expect(queueRows).toEqual([{ status: 'completed', failureCode: null }]);
+
+ const auditRows = await getFindingAuditRows(findingId);
+ expect(auditRows).toHaveLength(1);
+ expect(auditRows[0]).toMatchObject({
+ action: SecurityAuditLogAction.FindingAnalysisCompleted,
+ finding_id: findingId,
+ resource_id: findingId,
+ event_key: deriveSecurityFindingAuditEventKey([
+ `user:${testUserId}`,
+ findingId,
+ SecurityAuditLogAction.FindingAnalysisCompleted,
+ 'callback-completed-claim',
+ ]),
+ schema_version: SECURITY_FINDING_AUDIT_SCHEMA_VERSION,
+ source_context: SecurityFindingAuditSourceContext.AnalysisWorker,
+ before_state: { analysis_status: 'running' },
+ after_state: {
+ analysis_status: 'completed',
+ structured_extraction_status: 'succeeded',
+ suggested_action: 'dismiss',
+ is_exploitable: false,
+ },
+ finding_snapshot: expect.objectContaining({
+ finding_id: findingId,
+ repo_full_name: 'kilo/callback-completed',
+ status: 'open',
+ }),
+ });
+ expect(auditRows[0]?.occurred_at).toEqual(expect.any(String));
+ });
+
+ it('records structured extraction failures without claiming an exploitability result', async () => {
+ const findingId = await insertFinding('callback-extraction-failed', 'running');
+ await insertQueueClaim({
+ findingId,
+ claimToken: 'callback-extraction-failed-claim',
+ jobId: 'callback-extraction-failed-job',
+ queueStatus: 'running',
+ });
+ const analysis = createSandboxAnalysis('callback-extraction-failed', 'failed');
+
+ await expect(
+ transitionAnalysisCallbackLifecycle(client.db as never, {
+ findingId,
+ attemptToken: 'callback-extraction-failed-claim',
+ outcome: {
+ type: 'completed',
+ analysis,
+ },
+ })
+ ).resolves.toEqual({ status: 'completed' });
+
+ const auditRows = await getFindingAuditRows(findingId);
+ expect(auditRows).toHaveLength(1);
+ expect(auditRows[0]).toMatchObject({
+ action: SecurityAuditLogAction.FindingAnalysisCompleted,
+ after_state: {
+ analysis_status: 'completed',
+ structured_extraction_status: 'failed',
+ suggested_action: 'manual_review',
+ },
+ });
+ expect(auditRows[0]?.after_state).not.toHaveProperty('is_exploitable');
+ expect(auditRows[0]?.after_state).not.toHaveProperty('confidence');
});
it('terminalizes failed callbacks with queue and finding failure state settled together', async () => {
@@ -171,6 +277,28 @@ describe('analysis start lifecycle durable transitions', () => {
expect(queueRows).toEqual([
{ status: 'failed', failureCode: 'UPSTREAM_5XX', lastError: 'upstream 503' },
]);
+
+ const auditRows = await getFindingAuditRows(findingId);
+ expect(auditRows).toHaveLength(1);
+ expect(auditRows[0]).toMatchObject({
+ action: SecurityAuditLogAction.FindingAnalysisFailed,
+ finding_id: findingId,
+ resource_id: findingId,
+ event_key: deriveSecurityFindingAuditEventKey([
+ `user:${testUserId}`,
+ findingId,
+ SecurityAuditLogAction.FindingAnalysisFailed,
+ 'callback-failed-claim',
+ ]),
+ schema_version: SECURITY_FINDING_AUDIT_SCHEMA_VERSION,
+ source_context: SecurityFindingAuditSourceContext.AnalysisWorker,
+ before_state: { analysis_status: 'running' },
+ after_state: { analysis_status: 'failed' },
+ metadata: { failure_code: 'UPSTREAM_5XX' },
+ finding_snapshot: expect.objectContaining({ finding_id: findingId }),
+ });
+ expect(auditRows[0]?.occurred_at).toEqual(expect.any(String));
+ expect(JSON.stringify(auditRows[0])).not.toContain('upstream 503');
});
it('clears superseded callback capacity while settling its queue row', async () => {
@@ -342,6 +470,52 @@ describe('analysis start lifecycle durable transitions', () => {
expect(queueRows).toEqual([{ status: 'running', attemptCount: 1 }]);
});
+ it('records terminal analysis start failures with current audit evidence', async () => {
+ const findingId = await insertFinding('scheduled-terminal-failure', 'running');
+ const queueRowId = await insertQueueClaim({
+ findingId,
+ claimToken: 'scheduled-terminal-failure-claim',
+ jobId: 'scheduled-terminal-failure-job',
+ queueStatus: 'running',
+ });
+
+ await expect(
+ transitionAnalysisStartLifecycle(client.db as never, {
+ claim: {
+ source: 'scheduled',
+ findingId,
+ queueRowId,
+ claimToken: 'scheduled-terminal-failure-claim',
+ },
+ outcome: {
+ type: 'start-failed',
+ errorMessage: 'permanent permission failure',
+ queueStatus: 'failed',
+ failureCode: 'PERMISSION_DENIED_PERMANENT',
+ incrementAttempt: true,
+ nextRetryAt: null,
+ },
+ })
+ ).resolves.toEqual({ transitioned: true });
+
+ const auditRows = await getFindingAuditRows(findingId);
+ expect(auditRows).toHaveLength(1);
+ expect(auditRows[0]).toMatchObject({
+ action: SecurityAuditLogAction.FindingAnalysisFailed,
+ finding_id: findingId,
+ event_key: deriveSecurityFindingAuditEventKey([
+ `user:${testUserId}`,
+ findingId,
+ SecurityAuditLogAction.FindingAnalysisFailed,
+ 'scheduled-terminal-failure-claim',
+ ]),
+ schema_version: SECURITY_FINDING_AUDIT_SCHEMA_VERSION,
+ source_context: SecurityFindingAuditSourceContext.AnalysisWorker,
+ metadata: { failure_code: 'PERMISSION_DENIED_PERMANENT' },
+ });
+ expect(JSON.stringify(auditRows[0])).not.toContain('permanent permission failure');
+ });
+
it('requeues retryable scheduled start failures after running promotion without split state', async () => {
const findingId = await insertFinding('scheduled-retryable-failure', 'running');
const queueRowId = await insertQueueClaim({
@@ -379,8 +553,8 @@ describe('analysis start lifecycle durable transitions', () => {
.where(eq(security_findings.id, findingId));
expect(findingRows).toEqual([
{
- analysisStatus: 'failed',
- analysisError: 'prepareSession timed out',
+ analysisStatus: null,
+ analysisError: null,
},
]);
@@ -403,9 +577,17 @@ describe('analysis start lifecycle durable transitions', () => {
claimToken: null,
},
]);
+ expect(await getFindingAuditRows(findingId)).toEqual([]);
});
});
+function getFindingAuditRows(findingId: string) {
+ return client.db
+ .select()
+ .from(security_audit_log)
+ .where(eq(security_audit_log.finding_id, findingId));
+}
+
async function insertFinding(
suffix: string,
analysisStatus: 'pending' | 'running' = 'pending'
@@ -470,3 +652,36 @@ function createAnalysis(suffix: string): SecurityFindingAnalysis {
correlationId: `correlation-${suffix}`,
};
}
+
+function createSandboxAnalysis(
+ suffix: string,
+ extractionStatus: 'succeeded' | 'failed'
+): SecurityFindingAnalysis {
+ const extractionFailed = extractionStatus === 'failed';
+ return {
+ ...createAnalysis(suffix),
+ triage: {
+ needsSandboxAnalysis: true,
+ needsSandboxReasoning: `Sandbox required for ${suffix}`,
+ suggestedAction: 'analyze_codebase',
+ confidence: 'low',
+ triageAt: '2026-05-19T08:00:00.000Z',
+ },
+ sandboxAnalysis: {
+ isExploitable: extractionFailed ? 'unknown' : false,
+ extractionStatus,
+ exploitabilityReasoning: extractionFailed
+ ? 'Extraction failed. Review raw analysis.'
+ : 'No reachable usage.',
+ usageLocations: [],
+ suggestedFix: extractionFailed ? 'Review raw analysis.' : 'Remove the unused dependency.',
+ suggestedAction: extractionFailed ? 'manual_review' : 'dismiss',
+ summary: extractionFailed
+ ? 'Analysis completed but structured extraction failed.'
+ : 'Dependency is not exploitable.',
+ rawMarkdown: '# Raw analysis',
+ analysisAt: '2026-05-19T08:01:00.000Z',
+ modelUsed: 'analysis/model',
+ },
+ };
+}
diff --git a/services/security-auto-analysis/src/analysis-start-lifecycle.ts b/services/security-auto-analysis/src/analysis-start-lifecycle.ts
index a2538d6359..7818afd097 100644
--- a/services/security-auto-analysis/src/analysis-start-lifecycle.ts
+++ b/services/security-auto-analysis/src/analysis-start-lifecycle.ts
@@ -1,5 +1,20 @@
import type { WorkerDb } from '@kilocode/db/client';
-import { security_analysis_queue, security_findings } from '@kilocode/db/schema';
+import {
+ security_analysis_queue,
+ security_findings,
+ type SecurityFinding,
+} from '@kilocode/db/schema';
+import {
+ SecurityAuditLogAction,
+ SecurityFindingAuditSourceContext,
+} from '@kilocode/db/schema-types';
+import {
+ SECURITY_FINDING_AUDIT_SYSTEM_ACTOR,
+ deriveSecurityFindingAuditEventKey,
+ insertSecurityFindingAuditEvent,
+ type SecurityFindingAuditOwner,
+ type SecurityFindingAuditWriterDb,
+} from '@kilocode/worker-utils/security-finding-audit';
import { and, eq, inArray, isNull, like, not, or, sql } from 'drizzle-orm';
import type { AutoAnalysisFailureCode, SecurityFindingAnalysis } from './types.js';
@@ -62,6 +77,104 @@ class AnalysisStartQueueTransitionRejected extends Error {
}
}
+type TerminalAnalysisAuditOutcome =
+ | { type: 'completed'; analysis: SecurityFindingAnalysis }
+ | { type: 'failed'; failureCode: AutoAnalysisFailureCode };
+
+function toAnalysisAuditOwner(
+ finding: Pick
+): SecurityFindingAuditOwner {
+ if (finding.owned_by_organization_id) {
+ return { type: 'organization', organizationId: finding.owned_by_organization_id };
+ }
+ if (finding.owned_by_user_id) {
+ return { type: 'user', userId: finding.owned_by_user_id };
+ }
+ throw new Error('Security analysis finding has no audit owner');
+}
+
+function analysisAuditOwnerKey(
+ finding: Pick
+): string {
+ if (finding.owned_by_organization_id) {
+ return `organization:${finding.owned_by_organization_id}`;
+ }
+ if (finding.owned_by_user_id) return `user:${finding.owned_by_user_id}`;
+ throw new Error('Security analysis finding has no audit owner');
+}
+
+async function insertTerminalAnalysisAuditEvent(
+ db: SecurityFindingAuditWriterDb,
+ params: {
+ previousAnalysisStatus: string | null;
+ finding: SecurityFinding;
+ attemptToken: string;
+ outcome: TerminalAnalysisAuditOutcome;
+ }
+): Promise {
+ const { finding, outcome } = params;
+ const occurredAt = finding.analysis_completed_at;
+ if (!occurredAt) throw new Error('Terminal Security Finding analysis has no completion time');
+
+ const action =
+ outcome.type === 'completed'
+ ? SecurityAuditLogAction.FindingAnalysisCompleted
+ : SecurityAuditLogAction.FindingAnalysisFailed;
+ const analysis = outcome.type === 'completed' ? outcome.analysis : null;
+ const modelSlug =
+ analysis?.analysisModel ??
+ analysis?.modelUsed ??
+ analysis?.sandboxAnalysis?.modelUsed ??
+ analysis?.triageModel;
+ const sandboxAnalysis = analysis?.sandboxAnalysis;
+ const suggestedAction = sandboxAnalysis?.suggestedAction ?? analysis?.triage?.suggestedAction;
+
+ await insertSecurityFindingAuditEvent(db, {
+ owner: toAnalysisAuditOwner(finding),
+ finding,
+ actor: SECURITY_FINDING_AUDIT_SYSTEM_ACTOR,
+ action,
+ occurredAt,
+ eventKey: deriveSecurityFindingAuditEventKey([
+ analysisAuditOwnerKey(finding),
+ finding.id,
+ action,
+ params.attemptToken,
+ ]),
+ sourceContext: SecurityFindingAuditSourceContext.AnalysisWorker,
+ beforeState: { analysis_status: params.previousAnalysisStatus ?? 'unknown' },
+ afterState:
+ outcome.type === 'completed'
+ ? {
+ analysis_status: 'completed',
+ ...(suggestedAction ? { suggested_action: suggestedAction } : {}),
+ ...(!sandboxAnalysis && analysis?.triage?.confidence
+ ? { confidence: analysis.triage.confidence }
+ : {}),
+ ...(sandboxAnalysis?.extractionStatus
+ ? { structured_extraction_status: sandboxAnalysis.extractionStatus }
+ : {}),
+ ...(sandboxAnalysis?.isExploitable !== undefined &&
+ sandboxAnalysis.extractionStatus !== 'failed'
+ ? { is_exploitable: sandboxAnalysis.isExploitable }
+ : {}),
+ }
+ : { analysis_status: 'failed' },
+ metadata:
+ outcome.type === 'completed'
+ ? {
+ ...(analysis?.correlationId ? { correlation_id: analysis.correlationId } : {}),
+ ...(modelSlug ? { model_slug: modelSlug } : {}),
+ ...(analysis?.triageModel ? { triage_model_slug: analysis.triageModel } : {}),
+ ...(analysis?.analysisModel ? { analysis_model_slug: analysis.analysisModel } : {}),
+ ...(analysis?.triage?.needsSandboxAnalysis !== undefined
+ ? { needs_sandbox_analysis: analysis.triage.needsSandboxAnalysis }
+ : {}),
+ }
+ : { failure_code: outcome.failureCode },
+ });
+}
+
export async function transitionAnalysisCallbackLifecycle(
db: WorkerDb,
params: {
@@ -131,7 +244,14 @@ export async function transitionAnalysisCallbackLifecycle(
return { status: 'superseded' };
}
- const findingRows = await tx
+ const [previousFinding] = await tx
+ .select()
+ .from(security_findings)
+ .where(eq(security_findings.id, params.findingId))
+ .for('update')
+ .limit(1);
+
+ const [updatedFinding] = await tx
.update(security_findings)
.set(
params.outcome.type === 'completed'
@@ -158,9 +278,9 @@ export async function transitionAnalysisCallbackLifecycle(
)
)
)
- .returning({ id: security_findings.id });
+ .returning();
- if (findingRows.length === 0) {
+ if (!updatedFinding) {
await tx
.update(security_findings)
.set({
@@ -201,6 +321,16 @@ export async function transitionAnalysisCallbackLifecycle(
)
);
+ await insertTerminalAnalysisAuditEvent(tx, {
+ previousAnalysisStatus: previousFinding?.analysis_status ?? null,
+ finding: updatedFinding,
+ attemptToken: params.attemptToken,
+ outcome:
+ params.outcome.type === 'completed'
+ ? params.outcome
+ : { type: 'failed', failureCode: params.outcome.failureCode },
+ });
+
return { status: params.outcome.type };
});
}
@@ -214,8 +344,14 @@ export async function transitionAnalysisStartLifecycle(
): Promise<{ transitioned: boolean }> {
try {
return await db.transaction(async tx => {
- const findingRows = await transitionFinding(tx, params.claim, params.outcome);
- if (findingRows.length === 0) {
+ const [previousFinding] = await tx
+ .select()
+ .from(security_findings)
+ .where(eq(security_findings.id, params.claim.findingId))
+ .for('update')
+ .limit(1);
+ const [updatedFinding] = await transitionFinding(tx, params.claim, params.outcome);
+ if (!updatedFinding) {
return { transitioned: false };
}
@@ -261,6 +397,25 @@ export async function transitionAnalysisStartLifecycle(
throw new AnalysisStartQueueTransitionRejected();
}
+ if (params.outcome.type === 'triage-only-completed') {
+ await insertTerminalAnalysisAuditEvent(tx, {
+ previousAnalysisStatus: previousFinding?.analysis_status ?? null,
+ finding: updatedFinding,
+ attemptToken: params.claim.claimToken,
+ outcome: { type: 'completed', analysis: params.outcome.analysis },
+ });
+ } else if (
+ params.outcome.type === 'start-failed' &&
+ params.outcome.queueStatus === 'failed'
+ ) {
+ await insertTerminalAnalysisAuditEvent(tx, {
+ previousAnalysisStatus: previousFinding?.analysis_status ?? null,
+ finding: updatedFinding,
+ attemptToken: params.claim.claimToken,
+ outcome: { type: 'failed', failureCode: params.outcome.failureCode },
+ });
+ }
+
return { transitioned: true };
});
} catch (error) {
@@ -308,20 +463,29 @@ function transitionFinding(
updated_at: sql`now()`.mapWith(String),
})
.where(and(eq(security_findings.id, claim.findingId), ignoredReasonGuard))
- .returning({ id: security_findings.id });
+ .returning();
}
if (outcome.type === 'start-failed') {
return tx
.update(security_findings)
- .set({
- analysis_status: 'failed',
- analysis_error: outcome.errorMessage,
- analysis_completed_at: sql`now()`.mapWith(String),
- updated_at: sql`now()`.mapWith(String),
- })
+ .set(
+ outcome.queueStatus === 'failed'
+ ? {
+ analysis_status: 'failed',
+ analysis_error: outcome.errorMessage,
+ analysis_completed_at: sql`now()`.mapWith(String),
+ updated_at: sql`now()`.mapWith(String),
+ }
+ : {
+ analysis_status: null,
+ analysis_error: null,
+ analysis_completed_at: null,
+ updated_at: sql`now()`.mapWith(String),
+ }
+ )
.where(and(eq(security_findings.id, claim.findingId), ignoredReasonGuard))
- .returning({ id: security_findings.id });
+ .returning();
}
return tx
@@ -336,5 +500,5 @@ function transitionFinding(
updated_at: sql`now()`.mapWith(String),
})
.where(and(eq(security_findings.id, claim.findingId), ignoredReasonGuard))
- .returning({ id: security_findings.id });
+ .returning();
}
diff --git a/services/security-auto-analysis/src/auto-dismiss.test.ts b/services/security-auto-analysis/src/auto-dismiss.test.ts
index d1b6de4fed..d32d682497 100644
--- a/services/security-auto-analysis/src/auto-dismiss.test.ts
+++ b/services/security-auto-analysis/src/auto-dismiss.test.ts
@@ -1,41 +1,175 @@
+import type { SecurityFinding } from '@kilocode/db/schema';
+import {
+ SecurityAuditLogAction,
+ SecurityFindingAuditSourceContext,
+} from '@kilocode/db/schema-types';
+import { SECURITY_FINDING_AUDIT_SCHEMA_VERSION } from '@kilocode/worker-utils/security-finding-audit';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { maybeAutoDismissCompletedAnalysis } from './auto-dismiss.js';
+import type { SecurityFindingAnalysis } from './types.js';
+
+const FINDING_ID = 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb';
+const ORGANIZATION_ID = 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa';
+const INTEGRATION_ID = 'cccccccc-cccc-4ccc-8ccc-cccccccccccc';
+
+function makeFinding(overrides: Partial = {}): SecurityFinding {
+ return {
+ id: FINDING_ID,
+ owned_by_organization_id: ORGANIZATION_ID,
+ owned_by_user_id: null,
+ platform_integration_id: INTEGRATION_ID,
+ repo_full_name: 'kilo/repo',
+ source: 'dependabot',
+ source_id: '42',
+ severity: 'high',
+ ghsa_id: 'GHSA-1234-5678',
+ cve_id: 'CVE-2026-1234',
+ package_name: 'example-package',
+ package_ecosystem: 'npm',
+ vulnerable_version_range: '<2.0.0',
+ patched_version: '2.0.0',
+ manifest_path: 'package.json',
+ title: 'Example vulnerability',
+ description: 'Example description',
+ status: 'open',
+ ignored_reason: null,
+ ignored_by: null,
+ fixed_at: null,
+ sla_due_at: null,
+ dependabot_html_url: 'https://github.com/kilo/repo/security/dependabot/42',
+ cwe_ids: ['CWE-79'],
+ cvss_score: '7.5',
+ dependency_scope: 'runtime',
+ session_id: 'agent-session',
+ cli_session_id: 'kilo-session',
+ analysis_status: 'completed',
+ analysis_started_at: '2026-05-18T09:55:00.000Z',
+ analysis_completed_at: '2026-05-18T10:00:00.000Z',
+ analysis_error: null,
+ analysis: null,
+ raw_data: null,
+ first_detected_at: '2026-05-17T10:00:00.000Z',
+ last_synced_at: '2026-05-18T09:00:00.000Z',
+ created_at: '2026-05-17T10:00:00.000Z',
+ updated_at: '2026-05-18T10:00:00.000Z',
+ ...overrides,
+ };
+}
+
+function sandboxAnalysis(
+ correlationId: string,
+ overrides: Partial> = {}
+): SecurityFindingAnalysis {
+ return {
+ analyzedAt: '2026-05-18T10:00:00.000Z',
+ correlationId,
+ sandboxAnalysis: {
+ isExploitable: false,
+ exploitabilityReasoning:
+ 'The dependency is installed, but the vulnerable template function is never called.',
+ usageLocations: ['package.json:17'],
+ suggestedFix: 'Upgrade',
+ suggestedAction: 'dismiss',
+ summary: 'The vulnerable code path is not reachable.',
+ rawMarkdown: '# Not exploitable',
+ analysisAt: '2026-05-18T10:00:00.000Z',
+ ...overrides,
+ },
+ };
+}
+
+function createDbHarness(
+ options: {
+ finding?: SecurityFinding;
+ config?: Record;
+ installationId?: string;
+ auditError?: Error;
+ } = {}
+) {
+ const state = {
+ finding: options.finding ?? makeFinding(),
+ auditRows: [] as Array>,
+ committedUpdates: [] as Array>,
+ transactionCalls: 0,
+ };
+ let rootSelectCount = 0;
+
+ const db = {
+ select: () => ({
+ from: () => ({
+ where: () => ({
+ limit: async () => {
+ rootSelectCount += 1;
+ return rootSelectCount === 1
+ ? [
+ {
+ config: options.config ?? {
+ auto_dismiss_enabled: true,
+ auto_dismiss_confidence_threshold: 'high',
+ },
+ },
+ ]
+ : [{ installationId: options.installationId ?? 'installation-123' }];
+ },
+ }),
+ }),
+ }),
+ transaction: async (callback: (tx: unknown) => Promise) => {
+ state.transactionCalls += 1;
+ let stagedFinding = state.finding;
+ const stagedAuditRows: Array> = [];
+ const stagedUpdates: Array> = [];
+ const tx = {
+ select: () => ({
+ from: () => ({
+ where: () => ({
+ for: () => ({ limit: async () => [stagedFinding] }),
+ }),
+ }),
+ }),
+ update: () => ({
+ set: (values: Record) => ({
+ where: () => ({
+ returning: async () => {
+ const updatedFinding = { ...stagedFinding, ...values } as SecurityFinding;
+ stagedFinding = updatedFinding;
+ stagedUpdates.push(values);
+ return [updatedFinding];
+ },
+ }),
+ }),
+ }),
+ insert: () => ({
+ values: (values: Record) => ({
+ onConflictDoNothing: () => ({
+ returning: async () => {
+ if (options.auditError) throw options.auditError;
+ stagedAuditRows.push(values);
+ return [{ id: 'audit-row-1' }];
+ },
+ }),
+ }),
+ }),
+ };
+
+ const result = await callback(tx);
+ state.finding = stagedFinding;
+ state.auditRows.push(...stagedAuditRows);
+ state.committedUpdates.push(...stagedUpdates);
+ return result;
+ },
+ };
+
+ return { db, state };
+}
describe('maybeAutoDismissCompletedAnalysis', () => {
afterEach(() => {
vi.unstubAllGlobals();
});
- it('preserves Worker auto-dismiss state, Dependabot writeback, and audit trail', async () => {
- let selectCount = 0;
- const updates: unknown[] = [];
- const auditRows: unknown[] = [];
- const db = {
- select: () => ({
- from: () => ({
- where: () => ({
- limit: async () => {
- selectCount += 1;
- return selectCount === 1
- ? [{ config: { auto_dismiss_enabled: true } }]
- : [{ installationId: 'installation-123' }];
- },
- }),
- }),
- }),
- update: () => ({
- set: (values: unknown) => ({
- where: async () => {
- updates.push(values);
- },
- }),
- }),
- insert: () => ({
- values: async (values: unknown) => {
- auditRows.push(values);
- },
- }),
- };
+ it('commits sandbox dismissal with canonical current audit evidence before writeback', async () => {
+ const { db, state } = createDbHarness();
const fetchSpy = vi.fn().mockResolvedValue(new Response('', { status: 200 }));
vi.stubGlobal('fetch', fetchSpy);
@@ -44,75 +178,55 @@ describe('maybeAutoDismissCompletedAnalysis', () => {
env: {
GIT_TOKEN_SERVICE: { getToken: async () => 'github-token' },
} as unknown as CloudflareEnv,
- findingId: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
- finding: {
- id: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
- owned_by_organization_id: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',
- owned_by_user_id: null,
- source: 'dependabot',
- source_id: '42',
- platform_integration_id: 'cccccccc-cccc-4ccc-8ccc-cccccccccccc',
- repo_full_name: 'kilo/repo',
- } as never,
- analysis: {
- analyzedAt: '2026-05-18T10:00:00.000Z',
- correlationId: 'correlation-123',
- sandboxAnalysis: {
- isExploitable: false,
- exploitabilityReasoning: 'Dependency is not reachable.',
- usageLocations: [],
- suggestedFix: 'Upgrade',
- suggestedAction: 'dismiss',
- summary: 'Not exploitable',
- rawMarkdown: '# Not exploitable',
- analysisAt: '2026-05-18T10:00:00.000Z',
- },
- },
+ findingId: FINDING_ID,
+ finding: makeFinding() as never,
+ analysis: sandboxAnalysis('correlation-123'),
});
- expect(updates[0]).toMatchObject({
+ expect(state.finding).toMatchObject({
status: 'ignored',
ignored_reason: 'not_used',
ignored_by: 'auto-sandbox',
});
- expect(fetchSpy).toHaveBeenCalledTimes(1);
- expect(auditRows[0]).toMatchObject({
- action: 'security.finding.auto_dismissed',
- resource_id: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
- metadata: { correlationId: 'correlation-123', dismissSource: 'sandbox' },
+ expect(state.auditRows).toHaveLength(1);
+ expect(state.auditRows[0]).toMatchObject({
+ action: SecurityAuditLogAction.FindingAutoDismissed,
+ finding_id: FINDING_ID,
+ resource_type: 'security_finding',
+ resource_id: FINDING_ID,
+ occurred_at: expect.any(String),
+ event_key: expect.stringContaining('correlation-123'),
+ schema_version: SECURITY_FINDING_AUDIT_SCHEMA_VERSION,
+ source_context: SecurityFindingAuditSourceContext.AnalysisWorker,
+ before_state: { status: 'open' },
+ after_state: { status: 'ignored', reason_code: 'not_used' },
+ metadata: {
+ reason_code: 'not_used',
+ trigger: 'auto_dismiss_policy',
+ dismiss_source: 'sandbox',
+ correlation_id: 'correlation-123',
+ },
+ finding_snapshot: expect.objectContaining({
+ finding_id: FINDING_ID,
+ status: 'ignored',
+ repo_full_name: 'kilo/repo',
+ }),
});
+ expect(fetchSpy).toHaveBeenCalledWith(
+ expect.stringContaining('/dependabot/alerts/42'),
+ expect.objectContaining({
+ body: JSON.stringify({
+ state: 'dismissed',
+ dismissed_reason: 'not_used',
+ dismissed_comment:
+ '[Kilo Code auto-dismiss] The dependency is installed, but the vulnerable template function is never called.',
+ }),
+ })
+ );
});
- it('keeps automatic dismissal state and audit when upstream writeback fails', async () => {
- let selectCount = 0;
- const updates: unknown[] = [];
- const auditRows: unknown[] = [];
- const db = {
- select: () => ({
- from: () => ({
- where: () => ({
- limit: async () => {
- selectCount += 1;
- return selectCount === 1
- ? [{ config: { auto_dismiss_enabled: true } }]
- : [{ installationId: 'installation-123' }];
- },
- }),
- }),
- }),
- update: () => ({
- set: (values: unknown) => ({
- where: async () => {
- updates.push(values);
- },
- }),
- }),
- insert: () => ({
- values: async (values: unknown) => {
- auditRows.push(values);
- },
- }),
- };
+ it('keeps canonical local dismissal when upstream writeback fails', async () => {
+ const { db, state } = createDbHarness();
const fetchSpy = vi.fn().mockResolvedValue(new Response('', { status: 503 }));
vi.stubGlobal('fetch', fetchSpy);
@@ -121,75 +235,22 @@ describe('maybeAutoDismissCompletedAnalysis', () => {
env: {
GIT_TOKEN_SERVICE: { getToken: async () => 'github-token' },
} as unknown as CloudflareEnv,
- findingId: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
- finding: {
- id: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
- owned_by_organization_id: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',
- owned_by_user_id: null,
- source: 'dependabot',
- source_id: '42',
- platform_integration_id: 'cccccccc-cccc-4ccc-8ccc-cccccccccccc',
- repo_full_name: 'kilo/repo',
- } as never,
- analysis: {
- analyzedAt: '2026-05-18T10:00:00.000Z',
- correlationId: 'correlation-writeback-503',
- sandboxAnalysis: {
- isExploitable: false,
- exploitabilityReasoning: 'Dependency is not reachable.',
- usageLocations: [],
- suggestedFix: 'Upgrade',
- suggestedAction: 'dismiss',
- summary: 'Not exploitable',
- rawMarkdown: '# Not exploitable',
- analysisAt: '2026-05-18T10:00:00.000Z',
- },
- },
+ findingId: FINDING_ID,
+ finding: makeFinding() as never,
+ analysis: sandboxAnalysis('correlation-writeback-503'),
});
- expect(updates[0]).toMatchObject({
- status: 'ignored',
- ignored_reason: 'not_used',
- ignored_by: 'auto-sandbox',
- });
+ expect(state.finding.status).toBe('ignored');
+ expect(state.auditRows).toHaveLength(1);
expect(fetchSpy).toHaveBeenCalledTimes(1);
- expect(auditRows[0]).toMatchObject({
- action: 'security.finding.auto_dismissed',
- resource_id: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
- metadata: { correlationId: 'correlation-writeback-503', dismissSource: 'sandbox' },
- });
});
- it('keeps automatic dismissal durable while skipping partially numeric Dependabot alert IDs', async () => {
- let selectCount = 0;
- const updates: unknown[] = [];
- const auditRows: unknown[] = [];
- const db = {
- select: () => ({
- from: () => ({
- where: () => ({
- limit: async () => {
- selectCount += 1;
- return selectCount === 1
- ? [{ config: { auto_dismiss_enabled: true } }]
- : [{ installationId: 'installation-123' }];
- },
- }),
- }),
- }),
- update: () => ({
- set: (values: unknown) => ({
- where: async () => {
- updates.push(values);
- },
- }),
- }),
- insert: () => ({
- values: async (values: unknown) => {
- auditRows.push(values);
- },
- }),
- };
+ it.each([
+ ['partially numeric alert ID', { source_id: '42junk' }],
+ ['malformed repository name', { repo_full_name: 'kilo/repo/extra' }],
+ ])('keeps local dismissal while skipping %s', async (_label, findingOverrides) => {
+ const finding = makeFinding(findingOverrides);
+ const { db, state } = createDbHarness({ finding });
const fetchSpy = vi.fn().mockResolvedValue(new Response('', { status: 200 }));
vi.stubGlobal('fetch', fetchSpy);
@@ -198,75 +259,19 @@ describe('maybeAutoDismissCompletedAnalysis', () => {
env: {
GIT_TOKEN_SERVICE: { getToken: async () => 'github-token' },
} as unknown as CloudflareEnv,
- findingId: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
- finding: {
- id: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
- owned_by_organization_id: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',
- owned_by_user_id: null,
- source: 'dependabot',
- source_id: '42junk',
- platform_integration_id: 'cccccccc-cccc-4ccc-8ccc-cccccccccccc',
- repo_full_name: 'kilo/repo',
- } as never,
- analysis: {
- analyzedAt: '2026-05-18T10:00:00.000Z',
- correlationId: 'correlation-partial-source',
- sandboxAnalysis: {
- isExploitable: false,
- exploitabilityReasoning: 'Dependency is not reachable.',
- usageLocations: [],
- suggestedFix: 'Upgrade',
- suggestedAction: 'dismiss',
- summary: 'Not exploitable',
- rawMarkdown: '# Not exploitable',
- analysisAt: '2026-05-18T10:00:00.000Z',
- },
- },
+ findingId: FINDING_ID,
+ finding: finding as never,
+ analysis: sandboxAnalysis('correlation-invalid-target'),
});
- expect(updates[0]).toMatchObject({
- status: 'ignored',
- ignored_reason: 'not_used',
- ignored_by: 'auto-sandbox',
- });
+ expect(state.finding.status).toBe('ignored');
+ expect(state.auditRows).toHaveLength(1);
expect(fetchSpy).not.toHaveBeenCalled();
- expect(auditRows[0]).toMatchObject({
- action: 'security.finding.auto_dismissed',
- resource_id: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
- metadata: { correlationId: 'correlation-partial-source', dismissSource: 'sandbox' },
- });
});
- it('keeps automatic dismissal durable while skipping malformed Dependabot repo names', async () => {
- let selectCount = 0;
- const updates: unknown[] = [];
- const auditRows: unknown[] = [];
- const db = {
- select: () => ({
- from: () => ({
- where: () => ({
- limit: async () => {
- selectCount += 1;
- return selectCount === 1
- ? [{ config: { auto_dismiss_enabled: true } }]
- : [{ installationId: 'installation-123' }];
- },
- }),
- }),
- }),
- update: () => ({
- set: (values: unknown) => ({
- where: async () => {
- updates.push(values);
- },
- }),
- }),
- insert: () => ({
- values: async (values: unknown) => {
- auditRows.push(values);
- },
- }),
- };
+ it('does not re-dismiss findings that are already ignored', async () => {
+ const finding = makeFinding({ status: 'ignored' });
+ const { db, state } = createDbHarness({ finding });
const fetchSpy = vi.fn().mockResolvedValue(new Response('', { status: 200 }));
vi.stubGlobal('fetch', fetchSpy);
@@ -275,176 +280,108 @@ describe('maybeAutoDismissCompletedAnalysis', () => {
env: {
GIT_TOKEN_SERVICE: { getToken: async () => 'github-token' },
} as unknown as CloudflareEnv,
- findingId: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
- finding: {
- id: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
- owned_by_organization_id: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',
- owned_by_user_id: null,
- source: 'dependabot',
- source_id: '42',
- platform_integration_id: 'cccccccc-cccc-4ccc-8ccc-cccccccccccc',
- repo_full_name: 'kilo/repo/extra',
- } as never,
- analysis: {
- analyzedAt: '2026-05-18T10:00:00.000Z',
- correlationId: 'correlation-invalid-repo',
- sandboxAnalysis: {
- isExploitable: false,
- exploitabilityReasoning: 'Dependency is not reachable.',
- usageLocations: [],
- suggestedFix: 'Upgrade',
- suggestedAction: 'dismiss',
- summary: 'Not exploitable',
- rawMarkdown: '# Not exploitable',
- analysisAt: '2026-05-18T10:00:00.000Z',
- },
- },
+ findingId: FINDING_ID,
+ finding: finding as never,
+ analysis: sandboxAnalysis('correlation-already-ignored'),
});
- expect(updates[0]).toMatchObject({
- status: 'ignored',
- ignored_reason: 'not_used',
- ignored_by: 'auto-sandbox',
- });
+ expect(state.committedUpdates).toHaveLength(0);
+ expect(state.auditRows).toHaveLength(0);
expect(fetchSpy).not.toHaveBeenCalled();
- expect(auditRows[0]).toMatchObject({
- action: 'security.finding.auto_dismissed',
- resource_id: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
- metadata: { correlationId: 'correlation-invalid-repo', dismissSource: 'sandbox' },
- });
});
- it('does not re-dismiss findings that are already ignored', async () => {
- let selectCount = 0;
- const updates: unknown[] = [];
- const auditRows: unknown[] = [];
- const db = {
- select: () => ({
- from: () => ({
- where: () => ({
- limit: async () => {
- selectCount += 1;
- return selectCount === 1
- ? [{ config: { auto_dismiss_enabled: true } }]
- : [{ installationId: 'installation-123' }];
- },
- }),
- }),
- }),
- update: () => ({
- set: (values: unknown) => ({
- where: async () => {
- updates.push(values);
- },
- }),
- }),
- insert: () => ({
- values: async (values: unknown) => {
- auditRows.push(values);
- },
- }),
- };
+ it('records configured high-confidence triage dismissal as current evidence', async () => {
+ const { db, state } = createDbHarness();
const fetchSpy = vi.fn().mockResolvedValue(new Response('', { status: 200 }));
vi.stubGlobal('fetch', fetchSpy);
+ const analysis: SecurityFindingAnalysis = {
+ analyzedAt: '2026-05-18T10:00:00.000Z',
+ correlationId: 'correlation-456',
+ triage: {
+ needsSandboxAnalysis: false,
+ needsSandboxReasoning: 'No relevant runtime path.',
+ suggestedAction: 'dismiss',
+ confidence: 'high',
+ triageAt: '2026-05-18T09:59:00.000Z',
+ },
+ };
await maybeAutoDismissCompletedAnalysis({
db: db as never,
env: {
GIT_TOKEN_SERVICE: { getToken: async () => 'github-token' },
} as unknown as CloudflareEnv,
- findingId: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
- finding: {
- id: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
- owned_by_organization_id: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',
- owned_by_user_id: null,
- source: 'dependabot',
- source_id: '42',
- status: 'ignored',
- platform_integration_id: 'cccccccc-cccc-4ccc-8ccc-cccccccccccc',
- repo_full_name: 'kilo/repo',
- } as never,
- analysis: {
- analyzedAt: '2026-05-18T10:00:00.000Z',
- correlationId: 'correlation-already-ignored',
- sandboxAnalysis: {
- isExploitable: false,
- exploitabilityReasoning: 'Dependency is not reachable.',
- usageLocations: [],
- suggestedFix: 'Upgrade',
- suggestedAction: 'dismiss',
- summary: 'Not exploitable',
- rawMarkdown: '# Not exploitable',
- analysisAt: '2026-05-18T10:00:00.000Z',
- },
- },
+ findingId: FINDING_ID,
+ finding: makeFinding() as never,
+ analysis,
});
- expect(updates).toHaveLength(0);
- expect(fetchSpy).not.toHaveBeenCalled();
- expect(auditRows).toHaveLength(0);
+ expect(state.finding).toMatchObject({ status: 'ignored', ignored_by: 'auto-triage' });
+ expect(state.auditRows[0]).toMatchObject({
+ source_context: SecurityFindingAuditSourceContext.AnalysisWorker,
+ metadata: {
+ reason_code: 'not_used',
+ trigger: 'auto_dismiss_policy',
+ dismiss_source: 'triage',
+ confidence: 'high',
+ correlation_id: 'correlation-456',
+ },
+ });
+ expect(fetchSpy).toHaveBeenCalledTimes(1);
});
- it('auto-dismisses high-confidence triage decisions at the configured threshold', async () => {
- let selectCount = 0;
- const updates: unknown[] = [];
- const auditRows: unknown[] = [];
- const db = {
- select: () => ({
- from: () => ({
- where: () => ({
- limit: async () => {
- selectCount += 1;
- return selectCount === 1
- ? [
- {
- config: {
- auto_dismiss_enabled: true,
- auto_dismiss_confidence_threshold: 'high',
- },
- },
- ]
- : [{ installationId: 'installation-123' }];
- },
- }),
- }),
- }),
- update: () => ({
- set: (values: unknown) => ({
- where: async () => {
- updates.push(values);
- },
- }),
- }),
- insert: () => ({
- values: async (values: unknown) => {
- auditRows.push(values);
- },
- }),
- };
- const fetchSpy = vi.fn().mockResolvedValue(new Response('', { status: 200 }));
- vi.stubGlobal('fetch', fetchSpy);
+ it.each([
+ ['exploitable', true, 'open_pr'],
+ ['unknown exploitability', 'unknown', 'manual_review'],
+ ['inconsistent not-exploitable result', false, 'manual_review'],
+ ] as const)(
+ 'keeps findings open when authoritative sandbox result is %s',
+ async (_label, isExploitable, suggestedAction) => {
+ const { db, state } = createDbHarness();
+ const fetchSpy = vi.fn().mockResolvedValue(new Response('', { status: 200 }));
+ vi.stubGlobal('fetch', fetchSpy);
+ const analysis = sandboxAnalysis('correlation-authoritative-sandbox', {
+ isExploitable,
+ suggestedAction,
+ });
+ analysis.triage = {
+ needsSandboxAnalysis: false,
+ needsSandboxReasoning: 'Earlier triage recommended dismissal.',
+ suggestedAction: 'dismiss',
+ confidence: 'high',
+ triageAt: '2026-05-18T09:59:00.000Z',
+ };
+
+ await maybeAutoDismissCompletedAnalysis({
+ db: db as never,
+ env: {
+ GIT_TOKEN_SERVICE: { getToken: async () => 'github-token' },
+ } as unknown as CloudflareEnv,
+ findingId: FINDING_ID,
+ finding: makeFinding() as never,
+ analysis,
+ });
+
+ expect(state.transactionCalls).toBe(0);
+ expect(state.finding.status).toBe('open');
+ expect(state.auditRows).toEqual([]);
+ expect(fetchSpy).not.toHaveBeenCalled();
+ }
+ );
+
+ it('keeps triage findings open when triage says sandbox analysis is needed', async () => {
+ const { db, state } = createDbHarness();
await maybeAutoDismissCompletedAnalysis({
db: db as never,
- env: {
- GIT_TOKEN_SERVICE: { getToken: async () => 'github-token' },
- } as unknown as CloudflareEnv,
- findingId: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
- finding: {
- id: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
- owned_by_organization_id: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',
- owned_by_user_id: null,
- source: 'dependabot',
- source_id: '42',
- platform_integration_id: 'cccccccc-cccc-4ccc-8ccc-cccccccccccc',
- repo_full_name: 'kilo/repo',
- } as never,
+ env: {} as CloudflareEnv,
+ findingId: FINDING_ID,
+ finding: makeFinding() as never,
analysis: {
analyzedAt: '2026-05-18T10:00:00.000Z',
- correlationId: 'correlation-456',
triage: {
- needsSandboxAnalysis: false,
- needsSandboxReasoning: 'No relevant runtime path.',
+ needsSandboxAnalysis: true,
+ needsSandboxReasoning: 'Codebase usage must be checked.',
suggestedAction: 'dismiss',
confidence: 'high',
triageAt: '2026-05-18T09:59:00.000Z',
@@ -452,54 +389,24 @@ describe('maybeAutoDismissCompletedAnalysis', () => {
},
});
- expect(updates[0]).toMatchObject({
- status: 'ignored',
- ignored_reason: 'not_used',
- ignored_by: 'auto-triage',
- });
- expect(fetchSpy).toHaveBeenCalledTimes(1);
- expect(auditRows[0]).toMatchObject({
- metadata: {
- correlationId: 'correlation-456',
- dismissSource: 'triage',
- confidence: 'high',
- },
- });
+ expect(state.transactionCalls).toBe(0);
+ expect(state.finding.status).toBe('open');
+ expect(state.auditRows).toEqual([]);
});
- it('keeps low-confidence triage findings open above their confidence threshold', async () => {
- const updates: unknown[] = [];
- const db = {
- select: () => ({
- from: () => ({
- where: () => ({
- limit: async () => [
- {
- config: { auto_dismiss_enabled: true, auto_dismiss_confidence_threshold: 'medium' },
- },
- ],
- }),
- }),
- }),
- update: () => ({
- set: (values: unknown) => ({
- where: async () => {
- updates.push(values);
- },
- }),
- }),
- insert: () => ({ values: async () => undefined }),
- };
+ it('keeps low-confidence triage findings open above configured threshold', async () => {
+ const { db, state } = createDbHarness({
+ config: {
+ auto_dismiss_enabled: true,
+ auto_dismiss_confidence_threshold: 'medium',
+ },
+ });
await maybeAutoDismissCompletedAnalysis({
db: db as never,
env: {} as CloudflareEnv,
- findingId: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
- finding: {
- id: 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb',
- owned_by_organization_id: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',
- owned_by_user_id: null,
- } as never,
+ findingId: FINDING_ID,
+ finding: makeFinding() as never,
analysis: {
analyzedAt: '2026-05-18T10:00:00.000Z',
triage: {
@@ -512,6 +419,63 @@ describe('maybeAutoDismissCompletedAnalysis', () => {
},
});
- expect(updates).toHaveLength(0);
+ expect(state.transactionCalls).toBe(0);
+ expect(state.finding.status).toBe('open');
+ expect(state.auditRows).toEqual([]);
+ });
+
+ it('rolls back local dismissal when canonical audit insertion fails', async () => {
+ const { db, state } = createDbHarness({ auditError: new Error('audit insert failed') });
+ const fetchSpy = vi.fn().mockResolvedValue(new Response('', { status: 200 }));
+ vi.stubGlobal('fetch', fetchSpy);
+
+ await expect(
+ maybeAutoDismissCompletedAnalysis({
+ db: db as never,
+ env: {
+ GIT_TOKEN_SERVICE: { getToken: async () => 'github-token' },
+ } as unknown as CloudflareEnv,
+ findingId: FINDING_ID,
+ finding: makeFinding() as never,
+ analysis: sandboxAnalysis('correlation-rollback'),
+ })
+ ).rejects.toThrow('audit insert failed');
+
+ expect(state.finding.status).toBe('open');
+ expect(state.auditRows).toEqual([]);
+ expect(fetchSpy).not.toHaveBeenCalled();
+ });
+
+ it('fails before dismissing when audit identity is missing from malformed analysis', async () => {
+ const { db, state } = createDbHarness();
+ const fetchSpy = vi.fn().mockResolvedValue(new Response('', { status: 200 }));
+ vi.stubGlobal('fetch', fetchSpy);
+
+ await expect(
+ maybeAutoDismissCompletedAnalysis({
+ db: db as never,
+ env: {
+ GIT_TOKEN_SERVICE: { getToken: async () => 'github-token' },
+ } as unknown as CloudflareEnv,
+ findingId: FINDING_ID,
+ finding: makeFinding() as never,
+ analysis: {
+ sandboxAnalysis: {
+ isExploitable: false,
+ exploitabilityReasoning: 'No reachable vulnerable code path.',
+ usageLocations: [],
+ suggestedFix: 'No fix required.',
+ suggestedAction: 'dismiss',
+ summary: 'Not exploitable.',
+ rawMarkdown: '# Not exploitable',
+ },
+ } as unknown as SecurityFindingAnalysis,
+ })
+ ).rejects.toThrow('Auto-dismiss audit event requires an analysis identity');
+
+ expect(state.transactionCalls).toBe(0);
+ expect(state.finding.status).toBe('open');
+ expect(state.auditRows).toEqual([]);
+ expect(fetchSpy).not.toHaveBeenCalled();
});
});
diff --git a/services/security-auto-analysis/src/auto-dismiss.ts b/services/security-auto-analysis/src/auto-dismiss.ts
index 4de99d0d55..7f4d3e2f1a 100644
--- a/services/security-auto-analysis/src/auto-dismiss.ts
+++ b/services/security-auto-analysis/src/auto-dismiss.ts
@@ -1,8 +1,21 @@
import type { WorkerDb } from '@kilocode/db/client';
-import { platform_integrations, security_audit_log, security_findings } from '@kilocode/db/schema';
-import { SecurityAuditLogAction } from '@kilocode/db/schema-types';
+import {
+ platform_integrations,
+ security_findings,
+ type SecurityFinding,
+} from '@kilocode/db/schema';
+import {
+ SecurityAuditLogAction,
+ SecurityFindingAuditSourceContext,
+} from '@kilocode/db/schema-types';
import { parseDependabotDismissalTarget } from '@kilocode/worker-utils/dependabot-dismissal-target';
-import { eq, sql } from 'drizzle-orm';
+import {
+ SECURITY_FINDING_AUDIT_SYSTEM_ACTOR,
+ deriveSecurityFindingAuditEventKey,
+ insertSecurityFindingAuditEvent,
+ type SecurityFindingAuditOwner,
+} from '@kilocode/worker-utils/security-finding-audit';
+import { and, eq } from 'drizzle-orm';
import { getSecurityAgentConfigForOwner, type SecurityFindingRecord } from './db/queries.js';
import { logger } from './logger.js';
import type { QueueOwner, SecurityFindingAnalysis } from './types.js';
@@ -17,6 +30,22 @@ function findingOwner(finding: SecurityFindingRecord): QueueOwner | null {
return null;
}
+function toAuditOwner(owner: QueueOwner): SecurityFindingAuditOwner {
+ return owner.type === 'org'
+ ? { type: 'organization', organizationId: owner.id }
+ : { type: 'user', userId: owner.id };
+}
+
+function ownerAuditKeyPart(owner: QueueOwner): string {
+ return owner.type === 'org' ? `organization:${owner.id}` : `user:${owner.id}`;
+}
+
+function ownerFindingCondition(owner: QueueOwner) {
+ return owner.type === 'org'
+ ? eq(security_findings.owned_by_organization_id, owner.id)
+ : eq(security_findings.owned_by_user_id, owner.id);
+}
+
function meetsAutoDismissConfidenceThreshold(
threshold: 'high' | 'medium' | 'low',
confidence: 'high' | 'medium' | 'low'
@@ -85,6 +114,80 @@ async function writeBackDependabotDismissal(params: {
}
}
+async function dismissFindingWithAuditEvent(params: {
+ db: WorkerDb;
+ findingId: string;
+ owner: QueueOwner;
+ analysis: SecurityFindingAnalysis;
+ dismissSource: 'sandbox' | 'triage';
+ confidence?: 'high' | 'medium' | 'low';
+}): Promise {
+ const occurredAt = new Date().toISOString();
+ const analysisIdentity =
+ params.analysis.correlationId ??
+ params.analysis.sandboxAnalysis?.analysisAt ??
+ params.analysis.triage?.triageAt ??
+ params.analysis.analyzedAt;
+ if (!analysisIdentity) {
+ throw new Error('Auto-dismiss audit event requires an analysis identity');
+ }
+
+ return params.db.transaction(async tx => {
+ const [finding] = await tx
+ .select()
+ .from(security_findings)
+ .where(and(eq(security_findings.id, params.findingId), ownerFindingCondition(params.owner)))
+ .for('update')
+ .limit(1);
+ if (!finding || finding.status !== 'open') return null;
+
+ const [updatedFinding] = await tx
+ .update(security_findings)
+ .set({
+ status: 'ignored',
+ ignored_reason: 'not_used',
+ ignored_by: `auto-${params.dismissSource}`,
+ updated_at: occurredAt,
+ })
+ .where(
+ and(
+ eq(security_findings.id, params.findingId),
+ ownerFindingCondition(params.owner),
+ eq(security_findings.status, 'open')
+ )
+ )
+ .returning();
+ if (!updatedFinding) return null;
+
+ await insertSecurityFindingAuditEvent(tx, {
+ owner: toAuditOwner(params.owner),
+ finding: updatedFinding,
+ actor: SECURITY_FINDING_AUDIT_SYSTEM_ACTOR,
+ action: SecurityAuditLogAction.FindingAutoDismissed,
+ occurredAt,
+ eventKey: deriveSecurityFindingAuditEventKey([
+ ownerAuditKeyPart(params.owner),
+ updatedFinding.id,
+ SecurityAuditLogAction.FindingAutoDismissed,
+ params.dismissSource,
+ analysisIdentity,
+ ]),
+ sourceContext: SecurityFindingAuditSourceContext.AnalysisWorker,
+ beforeState: { status: finding.status },
+ afterState: { status: 'ignored', reason_code: 'not_used' },
+ metadata: {
+ reason_code: 'not_used',
+ trigger: 'auto_dismiss_policy',
+ dismiss_source: params.dismissSource,
+ ...(params.confidence ? { confidence: params.confidence } : {}),
+ ...(params.analysis.correlationId ? { correlation_id: params.analysis.correlationId } : {}),
+ },
+ });
+
+ return updatedFinding;
+ });
+}
+
export async function maybeAutoDismissCompletedAnalysis(params: {
db: WorkerDb;
env: CloudflareEnv;
@@ -92,55 +195,37 @@ export async function maybeAutoDismissCompletedAnalysis(params: {
finding: SecurityFindingRecord;
analysis: SecurityFindingAnalysis;
}): Promise {
- if (params.finding.status === 'ignored') return;
-
const owner = findingOwner(params.finding);
if (!owner) return;
const config = await getSecurityAgentConfigForOwner(params.db, owner);
if (!config.auto_dismiss_enabled) return;
const sandbox = params.analysis.sandboxAnalysis;
- if (sandbox?.isExploitable === false) {
- await params.db
- .update(security_findings)
- .set({
- status: 'ignored',
- ignored_reason: 'not_used',
- ignored_by: 'auto-sandbox',
- updated_at: sql`now()`.mapWith(String),
- })
- .where(eq(security_findings.id, params.findingId));
+ if (sandbox) {
+ if (sandbox.isExploitable !== false || sandbox.suggestedAction !== 'dismiss') return;
+
+ const dismissedFinding = await dismissFindingWithAuditEvent({
+ db: params.db,
+ findingId: params.findingId,
+ owner,
+ analysis: params.analysis,
+ dismissSource: 'sandbox',
+ });
+ if (!dismissedFinding) return;
await writeBackDependabotDismissal({
db: params.db,
env: params.env,
- finding: params.finding,
+ finding: dismissedFinding,
comment: sandbox.exploitabilityReasoning,
});
-
- await params.db.insert(security_audit_log).values({
- owned_by_organization_id: params.finding.owned_by_organization_id,
- owned_by_user_id: params.finding.owned_by_user_id,
- actor_id: null,
- actor_email: null,
- actor_name: null,
- action: SecurityAuditLogAction.FindingAutoDismissed,
- resource_type: 'security_finding',
- resource_id: params.findingId,
- after_state: { status: 'ignored' },
- metadata: {
- source: 'system',
- trigger: 'auto_dismiss_policy',
- dismissSource: 'sandbox',
- correlationId: params.analysis.correlationId,
- },
- });
return;
}
const triage = params.analysis.triage;
if (
- triage?.suggestedAction !== 'dismiss' ||
+ triage?.needsSandboxAnalysis !== false ||
+ triage.suggestedAction !== 'dismiss' ||
!meetsAutoDismissConfidenceThreshold(
config.auto_dismiss_confidence_threshold,
triage.confidence
@@ -149,39 +234,20 @@ export async function maybeAutoDismissCompletedAnalysis(params: {
return;
}
- await params.db
- .update(security_findings)
- .set({
- status: 'ignored',
- ignored_reason: 'not_used',
- ignored_by: 'auto-triage',
- updated_at: sql`now()`.mapWith(String),
- })
- .where(eq(security_findings.id, params.findingId));
+ const dismissedFinding = await dismissFindingWithAuditEvent({
+ db: params.db,
+ findingId: params.findingId,
+ owner,
+ analysis: params.analysis,
+ dismissSource: 'triage',
+ confidence: triage.confidence,
+ });
+ if (!dismissedFinding) return;
await writeBackDependabotDismissal({
db: params.db,
env: params.env,
- finding: params.finding,
+ finding: dismissedFinding,
comment: triage.needsSandboxReasoning,
});
-
- await params.db.insert(security_audit_log).values({
- owned_by_organization_id: params.finding.owned_by_organization_id,
- owned_by_user_id: params.finding.owned_by_user_id,
- actor_id: null,
- actor_email: null,
- actor_name: null,
- action: SecurityAuditLogAction.FindingAutoDismissed,
- resource_type: 'security_finding',
- resource_id: params.findingId,
- after_state: { status: 'ignored' },
- metadata: {
- source: 'system',
- trigger: 'auto_dismiss_policy',
- dismissSource: 'triage',
- confidence: triage.confidence,
- correlationId: params.analysis.correlationId,
- },
- });
}
diff --git a/services/security-auto-analysis/src/callbacks.test.ts b/services/security-auto-analysis/src/callbacks.test.ts
index 3f4ecd0a32..b76e061a4a 100644
--- a/services/security-auto-analysis/src/callbacks.test.ts
+++ b/services/security-auto-analysis/src/callbacks.test.ts
@@ -130,7 +130,6 @@ describe('finalizeCompletedAnalysisCallback', () => {
it('persists extracted sandbox analysis and terminal queue status for completed callbacks', async () => {
const updates: unknown[] = [];
const executes: unknown[] = [];
- const auditRows: unknown[] = [];
const db = {
select: () => ({
from: () => ({
@@ -171,11 +170,6 @@ describe('finalizeCompletedAnalysisCallback', () => {
executes.push(statement);
return { rows: [] };
},
- insert: () => ({
- values: async (values: unknown) => {
- auditRows.push(values);
- },
- }),
};
const autoDismissCalls: unknown[] = [];
const analyticsCalls: unknown[] = [];
@@ -195,6 +189,7 @@ describe('finalizeCompletedAnalysisCallback', () => {
},
extractSandboxAnalysis: async ({ rawMarkdown }) => ({
isExploitable: false,
+ extractionStatus: 'succeeded',
exploitabilityReasoning: 'No reachable usage',
usageLocations: [],
suggestedFix: 'Upgrade package',
@@ -221,10 +216,10 @@ describe('finalizeCompletedAnalysisCallback', () => {
type: 'completed',
analysis: expect.objectContaining({
rawMarkdown: '# Completed analysis',
+ sandboxAnalysis: expect.objectContaining({ extractionStatus: 'succeeded' }),
}),
}),
});
- expect(auditRows).toHaveLength(1);
expect(autoDismissCalls).toHaveLength(1);
expect(analyticsCalls).toHaveLength(1);
});
diff --git a/services/security-auto-analysis/src/callbacks.ts b/services/security-auto-analysis/src/callbacks.ts
index d52a1f644c..7f120ec62b 100644
--- a/services/security-auto-analysis/src/callbacks.ts
+++ b/services/security-auto-analysis/src/callbacks.ts
@@ -1,6 +1,4 @@
import { getWorkerDb, type WorkerDb } from '@kilocode/db/client';
-import { security_audit_log } from '@kilocode/db/schema';
-import { SecurityAuditLogAction } from '@kilocode/db/schema-types';
import {
CloudAgentCallbackFailureSchema,
type CloudAgentSafeFailure,
@@ -299,24 +297,6 @@ export async function finalizeCompletedAnalysisCallback(params: {
});
if (lifecycleTransition.status === 'superseded') return { status: 'superseded' };
if (lifecycleTransition.status === 'stale-attempt') return { status: 'stale-attempt' };
- await params.db.insert(security_audit_log).values({
- owned_by_organization_id: finding.owned_by_organization_id,
- owned_by_user_id: finding.owned_by_user_id,
- actor_id: null,
- actor_email: null,
- actor_name: null,
- action: SecurityAuditLogAction.FindingAnalysisCompleted,
- resource_type: 'security_finding',
- resource_id: params.findingId,
- metadata: {
- source: 'system',
- model: completedAnalysis.modelUsed,
- triageModel: completedAnalysis.triageModel,
- analysisModel: completedAnalysis.analysisModel,
- correlationId: completedAnalysis.correlationId,
- triggeredByUserId: completedAnalysis.triggeredByUserId,
- },
- });
await params.maybeAutoDismissAnalysis?.({
findingId: params.findingId,
analysis: completedAnalysis,
diff --git a/services/security-auto-analysis/src/db/queries.integration.test.ts b/services/security-auto-analysis/src/db/queries.integration.test.ts
index 9ac7a1ce0c..182edc1734 100644
--- a/services/security-auto-analysis/src/db/queries.integration.test.ts
+++ b/services/security-auto-analysis/src/db/queries.integration.test.ts
@@ -12,8 +12,10 @@ import {
discoverDueOwners,
ensureManualAnalysisQueueRow,
getSecurityFindingById,
+ prepareActiveAnalysisRestart,
reconcileStaleAnalysisQueueRows,
} from './queries.js';
+import { transitionAnalysisCallbackLifecycle } from '../analysis-start-lifecycle.js';
const connectionString =
process.env.POSTGRES_URL ?? 'postgres://postgres:postgres@localhost:5432/postgres';
@@ -50,6 +52,44 @@ describe('security analysis durable database invariants', () => {
await client.pool.end();
});
+ it('loads complete finding evidence for audit snapshots', async () => {
+ const findingId = await insertFinding('audit-snapshot-evidence');
+ await client.db
+ .update(security_findings)
+ .set({
+ cwe_ids: ['CWE-1321'],
+ cvss_score: '9.8',
+ dependabot_html_url:
+ 'https://github.com/kilo/audit-snapshot-evidence/security/dependabot/42',
+ first_detected_at: '2026-03-04T12:00:00.000Z',
+ fixed_at: null,
+ sla_due_at: '2026-03-19T12:00:00.000Z',
+ last_synced_at: '2026-03-06T12:00:00.000Z',
+ analysis_completed_at: '2026-03-05T12:00:00.000Z',
+ })
+ .where(eq(security_findings.id, findingId));
+
+ const finding = await getSecurityFindingById(client.db as never, findingId);
+ expect(finding).toMatchObject({
+ cwe_ids: ['CWE-1321'],
+ cvss_score: '9.8',
+ dependabot_html_url: 'https://github.com/kilo/audit-snapshot-evidence/security/dependabot/42',
+ fixed_at: null,
+ });
+ expect(finding?.first_detected_at && new Date(finding.first_detected_at).toISOString()).toBe(
+ '2026-03-04T12:00:00.000Z'
+ );
+ expect(finding?.sla_due_at && new Date(finding.sla_due_at).toISOString()).toBe(
+ '2026-03-19T12:00:00.000Z'
+ );
+ expect(finding?.last_synced_at && new Date(finding.last_synced_at).toISOString()).toBe(
+ '2026-03-06T12:00:00.000Z'
+ );
+ expect(
+ finding?.analysis_completed_at && new Date(finding.analysis_completed_at).toISOString()
+ ).toBe('2026-03-05T12:00:00.000Z');
+ });
+
it('enforces one manual queue row per finding against Postgres constraints', async () => {
const findingId = await insertFinding('manual-unique');
const finding = await getSecurityFindingById(client.db as never, findingId);
@@ -188,6 +228,189 @@ describe('security analysis durable database invariants', () => {
]);
});
+ it('atomically fences an active run and preserves triage for restart', async () => {
+ const findingId = await insertFinding('active-restart', 'running');
+ const priorAnalysis = {
+ analyzedAt: '2026-05-18T07:55:00.000Z',
+ triage: {
+ needsSandboxAnalysis: true,
+ needsSandboxReasoning: 'Repository inspection required',
+ suggestedAction: 'analyze_codebase' as const,
+ confidence: 'high' as const,
+ triageAt: '2026-05-18T07:55:00.000Z',
+ },
+ };
+ await client.db
+ .update(security_findings)
+ .set({
+ analysis: priorAnalysis,
+ analysis_error: 'prior transient error',
+ analysis_started_at: '2026-05-18T08:00:00.000Z',
+ analysis_completed_at: '2026-05-18T08:05:00.000Z',
+ session_id: 'agent-old-session',
+ cli_session_id: 'ses-old-session',
+ })
+ .where(eq(security_findings.id, findingId));
+ await client.db.insert(security_analysis_queue).values({
+ finding_id: findingId,
+ owned_by_user_id: testUserId,
+ queue_status: 'running',
+ severity_rank: 1,
+ queued_at: '2026-05-18T08:00:00.000Z',
+ claimed_at: '2026-05-18T08:00:00.000Z',
+ claimed_by_job_id: 'old-job',
+ claim_token: 'old-attempt-token',
+ attempt_count: 3,
+ reopen_requeue_count: 2,
+ next_retry_at: '2026-05-18T09:00:00.000Z',
+ failure_code: 'NETWORK_TIMEOUT',
+ last_error_redacted: 'prior failure',
+ });
+
+ const commandId = randomUUID();
+ const claimToken = `manual-restart:${commandId}`;
+ await expect(
+ prepareActiveAnalysisRestart(client.db as never, {
+ findingId,
+ owner: { type: 'user', id: testUserId },
+ commandId,
+ })
+ ).resolves.toEqual({
+ status: 'ready',
+ claimToken,
+ oldCloudAgentSessionId: 'agent-old-session',
+ });
+
+ const queueRows = await client.db
+ .select({
+ status: security_analysis_queue.queue_status,
+ claimToken: security_analysis_queue.claim_token,
+ claimedByJobId: security_analysis_queue.claimed_by_job_id,
+ attemptCount: security_analysis_queue.attempt_count,
+ reopenRequeueCount: security_analysis_queue.reopen_requeue_count,
+ nextRetryAt: security_analysis_queue.next_retry_at,
+ failureCode: security_analysis_queue.failure_code,
+ lastErrorRedacted: security_analysis_queue.last_error_redacted,
+ })
+ .from(security_analysis_queue)
+ .where(eq(security_analysis_queue.finding_id, findingId));
+ expect(queueRows).toEqual([
+ {
+ status: 'pending',
+ claimToken,
+ claimedByJobId: claimToken,
+ attemptCount: 0,
+ reopenRequeueCount: 0,
+ nextRetryAt: null,
+ failureCode: null,
+ lastErrorRedacted: null,
+ },
+ ]);
+
+ const findingRows = await client.db
+ .select({
+ analysisStatus: security_findings.analysis_status,
+ analysis: security_findings.analysis,
+ analysisError: security_findings.analysis_error,
+ analysisStartedAt: security_findings.analysis_started_at,
+ analysisCompletedAt: security_findings.analysis_completed_at,
+ sessionId: security_findings.session_id,
+ cliSessionId: security_findings.cli_session_id,
+ })
+ .from(security_findings)
+ .where(eq(security_findings.id, findingId));
+ expect(findingRows).toEqual([
+ {
+ analysisStatus: null,
+ analysis: priorAnalysis,
+ analysisError: null,
+ analysisStartedAt: null,
+ analysisCompletedAt: null,
+ sessionId: null,
+ cliSessionId: null,
+ },
+ ]);
+
+ await expect(
+ transitionAnalysisCallbackLifecycle(client.db as never, {
+ findingId,
+ attemptToken: 'old-attempt-token',
+ outcome: {
+ type: 'failed',
+ errorMessage: 'late old callback',
+ failureCode: 'STATE_GUARD_REJECTED',
+ },
+ })
+ ).resolves.toEqual({ status: 'stale-attempt' });
+
+ await expect(
+ prepareActiveAnalysisRestart(client.db as never, {
+ findingId,
+ owner: { type: 'user', id: testUserId },
+ commandId,
+ })
+ ).resolves.toEqual({
+ status: 'ready',
+ claimToken,
+ oldCloudAgentSessionId: null,
+ });
+ });
+
+ it('no-ops when callback terminal state wins before active restart takeover', async () => {
+ const findingId = await insertFinding('active-restart-callback-won', 'completed');
+ await client.db.insert(security_analysis_queue).values({
+ finding_id: findingId,
+ owned_by_user_id: testUserId,
+ queue_status: 'completed',
+ severity_rank: 1,
+ queued_at: '2026-05-18T08:00:00.000Z',
+ claimed_at: '2026-05-18T08:00:00.000Z',
+ claimed_by_job_id: 'completed-job',
+ claim_token: 'completed-attempt-token',
+ });
+
+ await expect(
+ prepareActiveAnalysisRestart(client.db as never, {
+ findingId,
+ owner: { type: 'user', id: testUserId },
+ commandId: randomUUID(),
+ })
+ ).resolves.toEqual({ status: 'no-op' });
+
+ const queueRows = await client.db
+ .select({
+ status: security_analysis_queue.queue_status,
+ claimToken: security_analysis_queue.claim_token,
+ })
+ .from(security_analysis_queue)
+ .where(eq(security_analysis_queue.finding_id, findingId));
+ expect(queueRows).toEqual([{ status: 'completed', claimToken: 'completed-attempt-token' }]);
+ });
+
+ it('treats same-command running redelivery as already launched', async () => {
+ const findingId = await insertFinding('active-restart-redelivery', 'running');
+ const commandId = randomUUID();
+ const claimToken = `manual-restart:${commandId}`;
+ await client.db.insert(security_analysis_queue).values({
+ finding_id: findingId,
+ owned_by_user_id: testUserId,
+ queue_status: 'running',
+ severity_rank: 1,
+ queued_at: '2026-05-18T08:00:00.000Z',
+ claimed_at: '2026-05-18T08:00:00.000Z',
+ claimed_by_job_id: claimToken,
+ claim_token: claimToken,
+ });
+
+ await expect(
+ prepareActiveAnalysisRestart(client.db as never, {
+ findingId,
+ owner: { type: 'user', id: testUserId },
+ commandId,
+ })
+ ).resolves.toEqual({ status: 'launch-started' });
+ });
+
it('requeues stale pending rows and terminalizes stale running rows in real SQL', async () => {
const pendingFindingId = await insertFinding('stale-pending');
const runningFindingId = await insertFinding('stale-running', 'running');
diff --git a/services/security-auto-analysis/src/db/queries.ts b/services/security-auto-analysis/src/db/queries.ts
index 66c055ba3e..990b9f624e 100644
--- a/services/security-auto-analysis/src/db/queries.ts
+++ b/services/security-auto-analysis/src/db/queries.ts
@@ -27,9 +27,15 @@ export type ClaimedQueueRow = {
export type ActorUser = {
id: string;
+ email?: string;
+ name?: string;
api_token_pepper: string | null;
};
+export type AuthoritativeActorUser = ActorUser & {
+ is_admin: boolean;
+};
+
type ClaimRowsForOwnerResult = {
rows: ClaimedQueueRow[];
config: SecurityAgentConfig;
@@ -421,6 +427,110 @@ export async function ensureManualAnalysisQueueRow(
return rows.length > 0;
}
+export type ActiveAnalysisRestartPreparation =
+ | {
+ status: 'ready';
+ claimToken: string;
+ oldCloudAgentSessionId: string | null;
+ }
+ | { status: 'launch-started' | 'no-op' };
+
+export async function prepareActiveAnalysisRestart(
+ db: WorkerDb,
+ params: { findingId: string; owner: QueueOwner; commandId: string }
+): Promise