Skip to content
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
e39aa23
feat: add change_gating_enabled column and enrollment API to repo sel…
isiddharthsingh Jun 12, 2026
e4892ae
feat: per-run tool denylist plumbed through agent state, background c…
isiddharthsingh Jun 12, 2026
6d6e224
feat: change-gating service layer (GitHub PR adapter, diff utils, ver…
isiddharthsingh Jun 12, 2026
9b84cd5
feat: PR change-gating webhook filters and investigate_pr Celery task
isiddharthsingh Jun 12, 2026
8fff13d
test: change-gating unit tests (adapter, diff utils, verdict, webhook…
isiddharthsingh Jun 12, 2026
5df452b
feat: Incident Prevention toggle on connected repos
isiddharthsingh Jun 12, 2026
7b3d7a4
chore: add CHANGE_GATING_DRY_RUN env var across compose files and .en…
isiddharthsingh Jun 12, 2026
94a0cdd
feat: transient 'Aurora is reviewing' progress comment on PRs
isiddharthsingh Jun 12, 2026
a465743
feat: incremental CodeRabbit-style inline review comments (post net-n…
isiddharthsingh Jun 12, 2026
c1a02fe
feat: incremental-diff PR reviews (review only new commits, status-ga…
isiddharthsingh Jun 12, 2026
9d2fc90
fix: address CodeRabbit review (dedupe-key release on skip, dup-log g…
isiddharthsingh Jun 12, 2026
c53f8ad
ci: install PyJWT in pre-checks so change_gating adapter/verdict test…
isiddharthsingh Jun 12, 2026
9919b9c
fix: address PR #506 review (quality-gate: secret-scan/ReDoS/reliabil…
isiddharthsingh Jun 12, 2026
c84212b
refactor: clear remaining sonar nits on PR #506 (logging.exception, d…
isiddharthsingh Jun 12, 2026
0a8125f
feat: scope change-gating review to infra/deploy/CI-CD risk with per-…
isiddharthsingh Jun 15, 2026
656b8b6
feat: split github oauth login vs token-honoring and auto-import inst…
isiddharthsingh Jun 15, 2026
4749f84
fix: use real github logo and drop oauth installation skeleton
isiddharthsingh Jun 15, 2026
61234c4
fix: make import_installation_repos body celery-agnostic so CI (stubb…
isiddharthsingh Jun 15, 2026
4bff8ea
refactor: extract per-file diff block helper (cognitive complexity) a…
isiddharthsingh Jun 15, 2026
ae37165
docs: add GitHub App setup guide to Docusaurus and inline permissions…
isiddharthsingh Jun 16, 2026
35ab3b0
fix: close adapter session in finally, guard pagination type, and esc…
isiddharthsingh Jun 17, 2026
fa1203b
refactor: remove tool denylist, add verdict-source logging, reduce ti…
isiddharthsingh Jun 17, 2026
c15fc73
feat: rewrite review prompt to leverage live infrastructure context a…
isiddharthsingh Jun 17, 2026
168501a
refactor: replace tool_denylist with is_pr_review context flag and re…
isiddharthsingh Jun 17, 2026
e2c0bd5
Update github-provider-integration.tsx
damianloch Jun 17, 2026
731d8ae
feat: add NEXT_PUBLIC_ENABLE_INCIDENT_PREVENTION feature flag replaci…
isiddharthsingh Jun 17, 2026
9691823
fix: remove unused os import from change_gating.py
isiddharthsingh Jun 17, 2026
21dd4ac
feat: add verdict decision test to reduce review sensitivity and fals…
isiddharthsingh Jun 17, 2026
95b5eb0
fix: skip NeMo input guardrail for PR change-gating reviews (content-…
isiddharthsingh Jun 18, 2026
ea76cbf
docs: rework GitHub App setup per review; add missing GitHub vars to …
isiddharthsingh Jun 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,8 @@ GITHUB_APP_WEBHOOK_URL=
GITHUB_APP_SETUP_URL=
# Fallback used when the value is not stored in Vault.
GITHUB_APP_WEBHOOK_SECRET=
# Enable/disable Incident Prevention (PR change-gating reviews). Set to false to turn off.
NEXT_PUBLIC_ENABLE_INCIDENT_PREVENTION=true

# GitHub OAuth (only required when GITHUB_AUTH_MODE=oauth or =hybrid).
# Create at https://github.com/settings/developers > New OAuth App.
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/linters.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
python-version: "3.12"

- name: Install test dependencies
run: pip install pytest==9.0.3 Flask==3.1.3 psycopg2-binary==2.9.12 python-dotenv==1.2.2 pyyaml==6.0.1 pydantic==2.13.3 langchain-core==1.2.31
run: pip install pytest==9.0.3 Flask==3.1.3 psycopg2-binary==2.9.12 python-dotenv==1.2.2 pyyaml==6.0.1 pydantic==2.13.3 langchain-core==1.2.31 PyJWT==2.13.0

- name: Run all tests
working-directory: server
Expand Down
6 changes: 3 additions & 3 deletions client/src/components/connectors/ConnectorRegistry.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Github, Server } from "lucide-react";
import { Server } from "lucide-react";
import { isOvhEnabled, isSharePointEnabled, isJiraEnabled, isSpinnakerEnabled, isNotionEnabled, isCloudBeesEnabled } from "@/lib/feature-flags";
import type { ConnectorConfig } from "./types";

Expand Down Expand Up @@ -259,8 +259,8 @@ class ConnectorRegistry {
id: "github",
name: "GitHub",
description: "Integrate with GitHub to manage repositories, track issues, and automate workflows. Connect your GitHub account to enable seamless code collaboration.",
icon: Github,
iconColor: "text-gray-800 dark:text-gray-300",
iconPath: "/github-mark.svg",
iconClassName: "dark:invert",
iconBgColor: "bg-gray-200 dark:bg-gray-800",
category: "Development",
useCustomConnection: true,
Expand Down
100 changes: 72 additions & 28 deletions client/src/components/github-provider-integration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Switch } from "@/components/ui/switch";
import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert";
import {
AlertDialog,
Expand Down Expand Up @@ -76,13 +77,17 @@ export interface ConnectedRepo {
metadata_status: string;
repo_data: Repository | null;
created_at: string | null;
// PR change gating (incident prevention) — only settable on App-linked repos.
change_gating_enabled?: boolean;
installation_id?: number | null;
}

export interface GitHubAuthConfig {
mode: 'app' | 'oauth' | 'hybrid';
app_enabled: boolean;
oauth_enabled: boolean;
oauth_configured: boolean;
incident_prevention_enabled: boolean;
}

export class GitHubIntegrationService {
Expand All @@ -97,7 +102,7 @@ export class GitHubIntegrationService {
if (!response.ok) {
// Default to App-only on error so a misconfigured proxy never
// surfaces an OAuth CTA the deployment hasn't enabled.
return { mode: 'app', app_enabled: true, oauth_enabled: false, oauth_configured: false };
return { mode: 'app', app_enabled: true, oauth_enabled: false, oauth_configured: false, incident_prevention_enabled: true };
}
return response.json();
}
Expand Down Expand Up @@ -188,6 +193,18 @@ export class GitHubIntegrationService {
if (!response.ok) throw new Error('Failed to update metadata');
}

static async setChangeGating(repoFullName: string, enabled: boolean): Promise<void> {
const response = await fetch(`/api/proxy/github/repo-selections/${encodeURIComponent(repoFullName)}/change-gating`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ enabled }),
});
if (!response.ok) {
const data = await response.json().catch(() => null);
throw new Error(data?.error || 'Failed to update incident prevention setting');
}
}

static async generateRepoMetadata(repoFullName: string): Promise<void> {
const response = await fetch('/api/proxy/github/repo-metadata/generate', {
method: 'POST',
Expand Down Expand Up @@ -230,6 +247,7 @@ export default function GitHubProviderIntegration() {
const [savedRepos, setSavedRepos] = useState<ConnectedRepo[]>([]);
const [savedReposLoaded, setSavedReposLoaded] = useState(false);
const [editingMetadata, setEditingMetadata] = useState<Record<string, string>>({});
const [gatingUpdating, setGatingUpdating] = useState<Set<string>>(new Set());
const pollingRef = useRef<ReturnType<typeof setInterval> | null>(null);
const popupCleanupsRef = useRef<Array<() => void>>([]);

Expand All @@ -243,7 +261,6 @@ export default function GitHubProviderIntegration() {

// GitHub App installations linked to this user
const [installations, setInstallations] = useState<GitHubInstallation[]>([]);
const [isLoadingInstallations, setIsLoadingInstallations] = useState(false);
const [installationFilter, setInstallationFilter] = useState<string>('all');

// App installations that exist on GitHub but aren't linked to this Aurora
Expand All @@ -259,6 +276,7 @@ export default function GitHubProviderIntegration() {
app_enabled: true,
oauth_enabled: false,
oauth_configured: false,
incident_prevention_enabled: true,
});

useEffect(() => {
Expand All @@ -278,15 +296,10 @@ export default function GitHubProviderIntegration() {
}, []);

const fetchInstallations = useCallback(async () => {
setIsLoadingInstallations(true);
try {
const data = await GitHubAppService.listInstallations();
const linked = data.installations || [];
setInstallations(linked);
// If the user has no linked installs but the App exists on GitHub
// for some account (left over from a previous install or another
// session), surface those for explicit claim. Cleared once any
// install is linked.
if (linked.length === 0 && authConfig.app_enabled) {
try {
const discovered = await GitHubAppService.discoverInstallations();
Expand All @@ -298,7 +311,6 @@ export default function GitHubProviderIntegration() {
setDiscoveredInstallations([]);
}
} catch { setInstallations([]); }
finally { setIsLoadingInstallations(false); }
}, [authConfig.app_enabled]);

const startMetadataPolling = useCallback((repos: ConnectedRepo[]) => {
Expand Down Expand Up @@ -526,6 +538,39 @@ export default function GitHubProviderIntegration() {
}
};

const handleChangeGatingToggle = async (repoFullName: string, enabled: boolean) => {
setGatingUpdating(prev => new Set(prev).add(repoFullName));
setSavedRepos(prev => prev.map(r =>
r.repo_full_name === repoFullName ? { ...r, change_gating_enabled: enabled } : r
));
try {
await GitHubIntegrationService.setChangeGating(repoFullName, enabled);
// Re-assert the confirmed value: a loadSavedRepos poll snapshotted
// before the PUT committed can land after the optimistic update and
// clobber it with the stale flag.
setSavedRepos(prev => prev.map(r =>
r.repo_full_name === repoFullName ? { ...r, change_gating_enabled: enabled } : r
));
globalThis.dispatchEvent(new CustomEvent('providerStateChanged'));
} catch (error: unknown) {
const err = error as Error;
setSavedRepos(prev => prev.map(r =>
r.repo_full_name === repoFullName ? { ...r, change_gating_enabled: !enabled } : r
));
toast({
title: "Error",
description: err.message || "Failed to update incident prevention setting",
variant: "destructive",
});
} finally {
setGatingUpdating(prev => {
const next = new Set(prev);
next.delete(repoFullName);
return next;
});
}
};

const handleRegenerate = async (repoFullName: string) => {
try {
await GitHubIntegrationService.generateRepoMetadata(repoFullName);
Expand Down Expand Up @@ -947,26 +992,10 @@ export default function GitHubProviderIntegration() {
{/* Expanded content */}
{expanded && githubStatus.isAuthenticated && (
<div className="ml-6 border-l-2 border-muted pl-6 mt-2 space-y-3">
{/* GitHub App installations (rendered only when at least one exists or first load is in flight) */}
{(installations.length > 0 || (isLoadingInstallations && installations.length === 0)) && (
{installations.length > 0 && (
<div className="space-y-2" data-testid="installations-section">
<p className="text-sm font-medium text-muted-foreground">Connected GitHub Installations</p>
{isLoadingInstallations && installations.length === 0 ? (
<div className="space-y-2" data-testid="installations-skeleton">
{[0, 1].map(i => (
<div key={i} className="p-3 rounded-md border border-border animate-pulse">
<div className="flex items-center gap-3">
<div className="h-10 w-10 rounded-full bg-muted flex-shrink-0" />
<div className="flex-1 space-y-2">
<div className="h-3 w-32 bg-muted rounded" />
<div className="h-3 w-48 bg-muted rounded" />
</div>
</div>
</div>
))}
</div>
) : (
installations.map(installation => (
{installations.map(installation => (
<div
key={installation.installation_id}
className="p-3 rounded-md border border-border"
Expand Down Expand Up @@ -1022,8 +1051,7 @@ export default function GitHubProviderIntegration() {
</div>
</div>
</div>
))
)}
))}
</div>
)}

Expand Down Expand Up @@ -1135,6 +1163,22 @@ export default function GitHubProviderIntegration() {
{isReady && !isEditing && repo.metadata_summary && (
<p className="text-xs text-muted-foreground">{repo.metadata_summary.replace(/\*\*/g, '')}</p>
)}
{repo.installation_id != null && authConfig.incident_prevention_enabled && (
<div
className="flex items-center justify-between gap-2 pt-1"
title="Incident Prevention — Aurora reviews PRs for incident risk"
>
<span className="text-xs text-muted-foreground">Incident Prevention</span>
<Switch
checked={!!repo.change_gating_enabled}
disabled={gatingUpdating.has(repo.repo_full_name)}
onCheckedChange={(checked) => handleChangeGatingToggle(repo.repo_full_name, checked)}
className="scale-75 origin-right"
aria-label={`Incident Prevention for ${repo.repo_full_name}`}
data-testid={`repo-change-gating-${repo.repo_full_name}`}
/>
</div>
)}
</div>
);
})}
Expand Down
12 changes: 11 additions & 1 deletion deploy/helm/aurora/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,17 @@ secrets:
GOOGLE_CHAT_CLIENT_SECRET: "" # OAuth client secret (for setup flow)
GOOGLE_CHAT_SERVICE_ACCOUNT_KEY: "" # JSON key for the Chat app service account (for messaging)

# --- Optional: GitHub OAuth ---
# --- GitHub App (default, recommended) ---
GITHUB_AUTH_MODE: "app" # app (default) | oauth | hybrid
NEXT_PUBLIC_ENABLE_INCIDENT_PREVENTION: "true" # false = disable PR change-gating entirely
GITHUB_APP_ID: "" # Numeric App ID from App settings
GITHUB_APP_CLIENT_ID: "" # Starts with Iv23l...
NEXT_PUBLIC_GITHUB_APP_SLUG: "" # URL slug (e.g. aurora-acme-prod)
GITHUB_APP_WEBHOOK_URL: "" # https://<your-host>/github/webhook
GITHUB_APP_SETUP_URL: "" # https://<your-host>/github
GITHUB_APP_WEBHOOK_SECRET: "" # openssl rand -hex 32 (also stored in secrets backend)

# --- GitHub OAuth (only when GITHUB_AUTH_MODE=oauth or hybrid) ---
GH_OAUTH_CLIENT_ID: "" # Get from: https://github.com/settings/developers
GH_OAUTH_CLIENT_SECRET: ""

Expand Down
2 changes: 2 additions & 0 deletions docker-compose.airtight.yml
Comment thread
isiddharthsingh marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ x-common-env: &common-env
NEXT_PUBLIC_ENABLE_SHAREPOINT: ${NEXT_PUBLIC_ENABLE_SHAREPOINT}
NEXT_PUBLIC_ENABLE_NOTION: ${NEXT_PUBLIC_ENABLE_NOTION}
NEXT_PUBLIC_ENABLE_SPINNAKER: ${NEXT_PUBLIC_ENABLE_SPINNAKER}
# Enable/disable Incident Prevention (PR change-gating). Set to false to turn off entirely.
NEXT_PUBLIC_ENABLE_INCIDENT_PREVENTION: ${NEXT_PUBLIC_ENABLE_INCIDENT_PREVENTION:-true}
# Slack OAuth (needed by celery_worker for formatting responses)
SLACK_CLIENT_ID: ${SLACK_CLIENT_ID}
SLACK_CLIENT_SECRET: ${SLACK_CLIENT_SECRET}
Expand Down
2 changes: 2 additions & 0 deletions docker-compose.prod-local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ x-common-env: &common-env
GITHUB_APP_SETUP_URL: ${GITHUB_APP_SETUP_URL}
GH_OAUTH_CLIENT_ID: ${GH_OAUTH_CLIENT_ID}
GH_OAUTH_CLIENT_SECRET: ${GH_OAUTH_CLIENT_SECRET}
# Enable/disable Incident Prevention (PR change-gating). Set to false to turn off entirely.
NEXT_PUBLIC_ENABLE_INCIDENT_PREVENTION: ${NEXT_PUBLIC_ENABLE_INCIDENT_PREVENTION:-true}

# AI Safety Guardrails
GUARDRAILS_ENABLED: ${GUARDRAILS_ENABLED:-true}
Expand Down
2 changes: 2 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ x-common-env: &common-env
GITHUB_APP_SETUP_URL: ${GITHUB_APP_SETUP_URL}
GH_OAUTH_CLIENT_ID: ${GH_OAUTH_CLIENT_ID}
GH_OAUTH_CLIENT_SECRET: ${GH_OAUTH_CLIENT_SECRET}
# Enable/disable Incident Prevention (PR change-gating). Set to false to turn off entirely.
NEXT_PUBLIC_ENABLE_INCIDENT_PREVENTION: ${NEXT_PUBLIC_ENABLE_INCIDENT_PREVENTION:-true}

# AI Safety Guardrails
GUARDRAILS_ENABLED: ${GUARDRAILS_ENABLED:-true}
Expand Down
1 change: 1 addition & 0 deletions server/celery_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
'utils.aws.credential_refresh',
'routes.aws.cloudwatch_tasks',
'tasks.github_webhook_tasks',
'tasks.change_gating',
'routes.github.github_repo_metadata',
'utils.repo_metadata',
'services.actions.scheduler',
Expand Down
4 changes: 2 additions & 2 deletions server/chat/backend/agent/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,8 +355,8 @@ async def agentic_tool_flow(
tools = get_cloud_tools()
if tool_subset is not None:
tools = tool_subset


prompt_text = ''
if state.messages and hasattr(state.messages[-1], 'content'):
# Handle both string and multimodal content
Expand Down
15 changes: 9 additions & 6 deletions server/chat/backend/agent/tools/cloud_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -1028,11 +1028,12 @@ def get_cloud_tools():
rca_flag = getattr(state_context, 'trigger_rca_requested', False) if state_context else False
is_background = getattr(state_context, 'is_background', False) if state_context else False
is_postmortem_action = getattr(state_context, 'is_postmortem_action', False) if state_context else False
is_pr_review = getattr(state_context, 'is_pr_review', False) if state_context else False
is_rca_context = _is_background_rca(state_context, is_background)
if tool_capture is None:
cache_key = f"{user_id}:nocapture:{mode_suffix}:background={is_background}:rca={rca_flag}:postmortem={is_postmortem_action}:is_rca_ctx={is_rca_context}"
cache_key = f"{user_id}:nocapture:{mode_suffix}:background={is_background}:rca={rca_flag}:postmortem={is_postmortem_action}:is_rca_ctx={is_rca_context}:pr_review={is_pr_review}"
else:
cache_key = f"{user_id}:capture:{id(tool_capture)}:{mode_suffix}:background={is_background}:rca={rca_flag}:postmortem={is_postmortem_action}:is_rca_ctx={is_rca_context}"
cache_key = f"{user_id}:capture:{id(tool_capture)}:{mode_suffix}:background={is_background}:rca={rca_flag}:postmortem={is_postmortem_action}:is_rca_ctx={is_rca_context}:pr_review={is_pr_review}"

current_time = time.time()
if (
Expand Down Expand Up @@ -1315,7 +1316,8 @@ def cloud_exec_wrapper(provider: str, command: str, output_file: Optional[str] =
]

# Cloud provider tools (only if at least one provider is connected)
if get_connected_providers(user_id):
# PR review is read-only: exclude exec/write tools.
if get_connected_providers(user_id) and not is_pr_review:
tool_functions.append((run_iac_tool, "iac_tool"))
tool_functions.append((cloud_exec_wrapper, "cloud_exec"))

Expand All @@ -1340,7 +1342,8 @@ def _safe_connected(check_fn, connector_name: str) -> bool:
return False

if _safe_connected(is_github_connected, "GitHub"):
tool_functions.append((github_commit, "github_commit"))
if not is_pr_review:
tool_functions.append((github_commit, "github_commit"))
tool_functions.append((get_connected_repos, "get_connected_repos"))
tool_functions.append((github_rca, "github_rca"))
# github_fix saves suggestions for user review in the incident card UI.
Expand All @@ -1351,11 +1354,11 @@ def _safe_connected(check_fn, connector_name: str) -> bool:
tool_functions.append((github_fix, "github_fix"))
logging.info(f"Added GitHub tools for user {user_id} (github_fix={'included' if is_rca_context else 'excluded (not RCA)'})")

if _safe_connected(is_tailscale_connected, "Tailscale"):
if _safe_connected(is_tailscale_connected, "Tailscale") and not is_pr_review:
tool_functions.append((tailscale_ssh, "tailscale_ssh"))
logging.info(f"Added Tailscale SSH tool for user {user_id}")

if _safe_connected(is_kubectl_onprem_connected, "kubectl_onprem"):
if _safe_connected(is_kubectl_onprem_connected, "kubectl_onprem") and not is_pr_review:
tool_functions.append((get_connected_clusters, "get_connected_clusters"))
tool_functions.append((on_prem_kubectl, "on_prem_kubectl"))
logging.info(f"Added on-prem kubectl tools for user {user_id}")
Expand Down
4 changes: 2 additions & 2 deletions server/chat/backend/agent/tools/github_repos_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
NoGitHubAuthError,
get_any_auth_for_user,
)
from utils.auth.github_auth_mode import is_oauth_enabled
from utils.auth.github_auth_mode import is_oauth_token_honored
from utils.auth.token_management import get_token_data

logger = logging.getLogger(__name__)
Expand All @@ -24,7 +24,7 @@ class GetConnectedReposArgs(BaseModel):


def _user_has_oauth(user_id: str) -> bool:
if not is_oauth_enabled():
if not is_oauth_token_honored():
return False
creds = get_token_data(user_id, "github")
return bool(creds and creds.get("access_token"))
Expand Down
2 changes: 2 additions & 0 deletions server/chat/backend/agent/utils/state.py
Comment thread
damianloch marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from pydantic import BaseModel, ConfigDict



class State(BaseModel):
messages: List[AnyMessage] = []
question: str
Expand All @@ -26,6 +27,7 @@ class State(BaseModel):
is_postmortem_action: bool = (
False # True only when the session is the dedicated "Generate Postmortem" action
)
is_pr_review: bool = False # True for PR change-gating risk reviews
rca_context: Optional[Dict[str, Any]] = (
None # RCA-specific context (source, providers) - used by prompt_builder
)
Expand Down
Loading
Loading