/** * Teleported /ultrareview execution. Creates a CCR session with the current repo, * sends the review prompt as the initial message, and registers a * RemoteAgentTask so the polling loop pipes results back into the local * session via task-notification. Mirrors the /ultraplan → CCR flow. * * TODO(#22051): pass useBundleMode once landed so local-only / uncommitted * repo state is captured. The GitHub-clone path (current) only works for * pushed branches on repos with the Claude GitHub app installed. */ import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages.js' import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../services/analytics/growthbook.js' import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent, } from '../../services/analytics/index.js' import { fetchUltrareviewQuota } from '../../services/api/ultrareviewQuota.js' import { fetchUtilization } from '../../services/api/usage.js' import type { ToolUseContext } from '../../Tool.js' import { checkRemoteAgentEligibility, formatPreconditionError, getRemoteTaskSessionUrl, registerRemoteAgentTask, } from '../../tasks/RemoteAgentTask/RemoteAgentTask.js' import { isEnterpriseSubscriber, isTeamSubscriber } from '../../utils/auth.js' import { detectCurrentRepositoryWithHost } from '../../utils/detectRepository.js' import { execFileNoThrow } from '../../utils/execFileNoThrow.js' import { getDefaultBranch, gitExe } from '../../utils/git.js' import { teleportToRemote } from '../../utils/teleport.js' // One-time session flag: once the user confirms overage billing via the // dialog, all subsequent /ultrareview invocations in this session proceed // without re-prompting. let sessionOverageConfirmed = false export function confirmOverage(): void { sessionOverageConfirmed = true } export type OverageGate = | { kind: 'proceed'; billingNote: string } | { kind: 'not-enabled' } | { kind: 'low-balance'; available: number } | { kind: 'needs-confirm' } /** * Determine whether the user can launch an ultrareview and under what * billing terms. Fetches quota and utilization in parallel. */ export async function checkOverageGate(): Promise { // Team and Enterprise plans include ultrareview — no free-review quota // or Extra Usage dialog. The quota endpoint is scoped to consumer plans // (pro/max); hitting it on team/ent would surface a confusing dialog. if (isTeamSubscriber() || isEnterpriseSubscriber()) { return { kind: 'proceed', billingNote: '' } } const [quota, utilization] = await Promise.all([ fetchUltrareviewQuota(), fetchUtilization().catch(() => null), ]) // No quota info (non-subscriber or endpoint down) — let it through, // server-side billing will handle it. if (!quota) { return { kind: 'proceed', billingNote: '' } } if (quota.reviews_remaining > 0) { return { kind: 'proceed', billingNote: ` This is free ultrareview ${quota.reviews_used + 1} of ${quota.reviews_limit}.`, } } // Utilization fetch failed (transient network error, timeout, etc.) — // let it through, same rationale as the quota fallback above. if (!utilization) { return { kind: 'proceed', billingNote: '' } } // Free reviews exhausted — check Extra Usage setup. const extraUsage = utilization.extra_usage if (!extraUsage?.is_enabled) { logEvent('tengu_review_overage_not_enabled', {}) return { kind: 'not-enabled' } } // Check available balance (null monthly_limit = unlimited). const monthlyLimit = extraUsage.monthly_limit const usedCredits = extraUsage.used_credits ?? 0 const available = monthlyLimit === null || monthlyLimit === undefined ? Infinity : monthlyLimit - usedCredits if (available < 10) { logEvent('tengu_review_overage_low_balance', { available }) return { kind: 'low-balance', available } } if (!sessionOverageConfirmed) { logEvent('tengu_review_overage_dialog_shown', {}) return { kind: 'needs-confirm' } } return { kind: 'proceed', billingNote: ' This review bills as Extra Usage.', } } /** * Launch a teleported review session. Returns ContentBlockParam[] describing * the launch outcome for injection into the local conversation (model is then * queried with this content, so it can narrate the launch to the user). * * Returns ContentBlockParam[] with user-facing error messages on recoverable * failures (missing merge-base, empty diff, bundle too large), or null on * other failures so the caller falls through to the local-review prompt. * Reason is captured in analytics. * * Caller must run checkOverageGate() BEFORE calling this function * (ultrareviewCommand.tsx handles the dialog). */ export async function launchRemoteReview( args: string, context: ToolUseContext, billingNote?: string, ): Promise { const eligibility = await checkRemoteAgentEligibility() // Synthetic DEFAULT_CODE_REVIEW_ENVIRONMENT_ID works without per-org CCR // setup, so no_remote_environment isn't a blocker. Server-side quota // consume at session creation routes billing: first N zero-rate, then // anthropic:cccr org-service-key (overage-only). if (!eligibility.eligible) { const blockers = eligibility.errors.filter( e => e.type !== 'no_remote_environment', ) if (blockers.length > 0) { logEvent('tengu_review_remote_precondition_failed', { precondition_errors: blockers .map(e => e.type) .join( ',', ) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, }) const reasons = blockers.map(formatPreconditionError).join('\n') return [ { type: 'text', text: `Ultrareview cannot launch:\n${reasons}`, }, ] } } const resolvedBillingNote = billingNote ?? '' const prNumber = args.trim() const isPrNumber = /^\d+$/.test(prNumber) // Synthetic code_review env. Go taggedid.FromUUID(TagEnvironment, // UUID{...,0x02}) encodes with version prefix '01' — NOT Python's // legacy tagged_id() format. Verified in prod. const CODE_REVIEW_ENV_ID = 'env_011111111111111111111113' // Lite-review bypasses bughunter.go entirely, so it doesn't see the // webhook's bug_hunter_config (different GB project). These env vars are // the only tuning surface — without them, run_hunt.sh's bash defaults // apply (60min, 120s agent timeout), and 120s kills verifiers mid-run // which causes infinite respawn. // // total_wallclock must stay below RemoteAgentTask's 30min poll timeout // with headroom for finalization (~3min synthesis). Per-field guards // match autoDream.ts — GB cache can return stale wrong-type values. const raw = getFeatureValue_CACHED_MAY_BE_STALE | null>('tengu_review_bughunter_config', null) const posInt = (v: unknown, fallback: number, max?: number): number => { if (typeof v !== 'number' || !Number.isFinite(v)) return fallback const n = Math.floor(v) if (n <= 0) return fallback return max !== undefined && n > max ? fallback : n } // Upper bounds: 27min on wallclock leaves ~3min for finalization under // RemoteAgentTask's 30min poll timeout. If GB is set above that, the // hang we're fixing comes back — fall to the safe default instead. const commonEnvVars = { BUGHUNTER_DRY_RUN: '1', BUGHUNTER_FLEET_SIZE: String(posInt(raw?.fleet_size, 5, 20)), BUGHUNTER_MAX_DURATION: String(posInt(raw?.max_duration_minutes, 10, 25)), BUGHUNTER_AGENT_TIMEOUT: String( posInt(raw?.agent_timeout_seconds, 600, 1800), ), BUGHUNTER_TOTAL_WALLCLOCK: String( posInt(raw?.total_wallclock_minutes, 22, 27), ), ...(process.env.BUGHUNTER_DEV_BUNDLE_B64 && { BUGHUNTER_DEV_BUNDLE_B64: process.env.BUGHUNTER_DEV_BUNDLE_B64, }), } let session let command let target if (isPrNumber) { // PR mode: refs/pull/N/head via github.com. Orchestrator --pr N. const repo = await detectCurrentRepositoryWithHost() if (!repo || repo.host !== 'github.com') { logEvent('tengu_review_remote_precondition_failed', {}) return null } session = await teleportToRemote({ initialMessage: null, description: `ultrareview: ${repo.owner}/${repo.name}#${prNumber}`, signal: context.abortController.signal, branchName: `refs/pull/${prNumber}/head`, environmentId: CODE_REVIEW_ENV_ID, environmentVariables: { BUGHUNTER_PR_NUMBER: prNumber, BUGHUNTER_REPOSITORY: `${repo.owner}/${repo.name}`, ...commonEnvVars, }, }) command = `/ultrareview ${prNumber}` target = `${repo.owner}/${repo.name}#${prNumber}` } else { // Branch mode: bundle the working tree, orchestrator diffs against // the fork point. No PR, no existing comments, no dedup. const baseBranch = (await getDefaultBranch()) || 'main' // Env-manager's `git remote remove origin` after bundle-clone // deletes refs/remotes/origin/* — the base branch name won't resolve // in the container. Pass the merge-base SHA instead: it's reachable // from HEAD's history so `git diff ` works without a named ref. const { stdout: mbOut, code: mbCode } = await execFileNoThrow( gitExe(), ['merge-base', baseBranch, 'HEAD'], { preserveOutputOnError: false }, ) const mergeBaseSha = mbOut.trim() if (mbCode !== 0 || !mergeBaseSha) { logEvent('tengu_review_remote_precondition_failed', {}) return [ { type: 'text', text: `Could not find merge-base with ${baseBranch}. Make sure you're in a git repo with a ${baseBranch} branch.`, }, ] } // Bail early on empty diffs instead of launching a container that // will just echo "no changes". const { stdout: diffStat, code: diffCode } = await execFileNoThrow( gitExe(), ['diff', '--shortstat', mergeBaseSha], { preserveOutputOnError: false }, ) if (diffCode === 0 && !diffStat.trim()) { logEvent('tengu_review_remote_precondition_failed', {}) return [ { type: 'text', text: `No changes against the ${baseBranch} fork point. Make some commits or stage files first.`, }, ] } session = await teleportToRemote({ initialMessage: null, description: `ultrareview: ${baseBranch}`, signal: context.abortController.signal, useBundle: true, environmentId: CODE_REVIEW_ENV_ID, environmentVariables: { BUGHUNTER_BASE_BRANCH: mergeBaseSha, ...commonEnvVars, }, }) if (!session) { logEvent('tengu_review_remote_teleport_failed', {}) return [ { type: 'text', text: 'Repo is too large. Push a PR and use `/ultrareview ` instead.', }, ] } command = '/ultrareview' target = baseBranch } if (!session) { logEvent('tengu_review_remote_teleport_failed', {}) return null } registerRemoteAgentTask({ remoteTaskType: 'ultrareview', session, command, context, isRemoteReview: true, }) logEvent('tengu_review_remote_launched', {}) const sessionUrl = getRemoteTaskSessionUrl(session.id) // Concise — the tool-output block is visible to the user, so the model // shouldn't echo the same info. Just enough for Claude to acknowledge the // launch without restating the target/URL (both already printed above). return [ { type: 'text', text: `Ultrareview launched for ${target} (~10–20 min, runs in the cloud). Track: ${sessionUrl}${resolvedBillingNote} Findings arrive via task-notification. Briefly acknowledge the launch to the user without repeating the target or URL — both are already visible in the tool output above.`, }, ] }