Skip to content

refactor(cloud-agent-next): gate billing by model classification#4465

Open
eshurakov wants to merge 3 commits into
mainfrom
single-crab
Open

refactor(cloud-agent-next): gate billing by model classification#4465
eshurakov wants to merge 3 commits into
mainfrom
single-crab

Conversation

@eshurakov

@eshurakov eshurakov commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Re-architects how cloud-agent-next gates sessions on balance, replacing the
"blanket $1 minimum for all mutations + an opt-out x-skip-balance-check
header" model with per-model billing classification.

  • New admission round-trip. The worker balance middleware now asks a new
    POST /api/profile/cloud-agent-admission endpoint, which classifies the
    requested model as free, byok, or balance-required (source of truth:
    classifyCloudAgentModelBilling in apps/web) and — only for
    balance-required — returns balance + depletion in the same call.
    free/byok sessions are admitted regardless of balance;
    balance-required sessions require a positive balance (previously a $1
    minimum).
  • New worker preflight. model-billing-preflight.ts extracts the model +
    owner from each procedure body (prepareSession, start,
    initiateFromKilocodeSessionV2, sendMessageV2, send, kilo.prompt_async),
    validates org membership / session access, and calls admission. The middleware
    delegates to it and maps tRPC error codes to HTTP statuses.
    balance-validation.ts shrinks to procedure-name extraction + the mutation
    allowlist.
  • Removes the x-skip-balance-check escape hatch. Dropped from the client
    (createAppBuilderCloudAgentNextClient / createCloudAgentNextClientForModel
    removed; createCloudAgentNextClient takes no options), App Builder, the
    Discord/Slack bot session spawn, the code-review orchestrator
    (skipBalanceCheck removed from CodeReview/CodeReviewRequest/payload), and
    webhook-agent-ingest. These flows are now admitted normally: free/BYOK models
    pass; platform-billed models still require balance.
  • Frontend eligibility refined. buildCloudAgentNextEligibility now exposes
    accessLevel (full/limited), isLowBalance, and isEligible.
    NewSessionPanel distinguishes zero-credit (free/BYOK models only, "Add
    Credits" banner) from low-funds (all models available, "Credits are below $1"
    notice); filtering + notice logic extracted to new-session-balance.ts.
  • assertOrganizationMembership moved from session-start.ts to
    session-access.ts for reuse by the preflight. Error copy changes from "$1
    minimum required" to "a positive credit balance is required".

Net effect: users with a $0 balance can now start free or BYOK-billed Cloud
Agent sessions; platform-billed models require a positive balance instead of a
$1 floor.

Verification

  • No manual verification was performed in this session; the local web/worker
    stack was not exercised end-to-end.

Visual Changes

Before After
Zero-balance users saw an "insufficient balance" block banner and could not start any Cloud Agent session (the paid-model $1 minimum applied to all) Zero-balance users see an info banner ("Free and BYOK models remain available") and can start free/BYOK-model sessions; only paid models are filtered out
Low-balance users saw a generic "Free Models Available" banner Low-balance users see "Credits are below $1 — all models remain available while your balance is positive"

Reviewer Notes

  • Behavior change: the balance gate moved from "$1 minimum" to "positive balance
    required" for platform-billed models, broadening who can run paid models to
    anyone with > $0 — confirm this is the intended product policy.
  • The worker now makes a synchronous HTTP call to apps/web on every billable
    mutation (/api/profile/cloud-agent-admission); on failure it fails closed
    (503, retryable) so platform-billed models are never served for free during an
    outage — verify that retry behavior is acceptable for callers.
  • prepareSession is newly added to BALANCE_REQUIRED_MUTATIONS; it previously
    could be bypassed via the skip header for App Builder/code reviews and now runs
    the admission preflight like everything else.
  • New test files cover cloud-agent-admission, model-billing-preflight,
    classify-model-billing, new-session-balance, and the admission route.

Replace the blanket $1 minimum + x-skip-balance-check escape hatch with
per-model billing classification. The worker balance middleware now asks a
new POST /api/profile/cloud-agent-admission endpoint, which classifies the
model as free/byok/balance-required and returns balance only when needed.

free/byok sessions are admitted regardless of balance; balance-required
sessions require a positive balance (previously a $1 floor). Drops the
skip-balance-check header across the client, App Builder, Discord/Slack bot,
code-review orchestrator, and webhook-agent-ingest. Refines frontend
eligibility to distinguish zero-credit from low-funds states.

const client = createAppBuilderCloudAgentNextClient(authToken);
const client = createCloudAgentNextClient(authToken);
const { cloudAgentSessionId } = await client.prepareSession({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: InsufficientCreditsError from prepareSession is downgraded to a generic 500

App Builder no longer bypasses the balance gate (the x-skip-balance-check escape hatch and createAppBuilderCloudAgentNextClient were removed in this PR), so client.prepareSession(...) can now throw InsufficientCreditsError for balance-required models. The enclosing catch block (a few lines below) does code: error instanceof TRPCError ? error.code : 'INTERNAL_SERVER_ERROR'. InsufficientCreditsError is a plain Error, not a TRPCError, so this always downgrades it to INTERNAL_SERVER_ERROR instead of the intended PAYMENT_REQUIRED/402. Compare with cloud-agent-next-router.ts and security-agent/services/analysis-service.ts, which both call rethrowAsPaymentRequired/explicitly check InsufficientCreditsError to preserve 402 semantics. App Builder's own frontend (components/app-builder/project-manager/logging.ts) checks for code === 'PAYMENT_REQUIRED' || httpStatus === 402 to show a friendly "add credits" message, so zero-balance users hitting a platform-billed model here will instead see a generic internal-error UI.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.


const client = createAppBuilderCloudAgentNextClient(authToken);
const client = createCloudAgentNextClient(authToken);
const result = await client.sendMessage({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Same InsufficientCreditsError handling gap for sendMessage

client.sendMessage(...) can now also throw InsufficientCreditsError since App Builder no longer bypasses the balance gate for balance-required models. sendToExistingCloudAgentNextSession has no try/catch at all, so the error propagates raw up through the caller (e.g. sendMessage tRPC procedure) as an unstructured error instead of the intended PAYMENT_REQUIRED/402 that other Cloud Agent Next call sites (cloud-agent-next-router.ts, security-agent/services/analysis-service.ts) produce via rethrowAsPaymentRequired.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

'Balance bypass is not supported for public Kilo prompt mutations'
);
}
const balance = await (params.deps?.validatePromptBalance ?? defaultValidatePromptBalance)({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: validatePromptBalance call is unguarded against the new throwing admission preflight

defaultValidatePromptBalance was rewritten to call preflightCloudAgentModelBilling, which — unlike the previous fetchOrgIdForSession/validateBalanceOnly pair — can throw a TRPCError (e.g. SERVICE_UNAVAILABLE on admission/DB failure, NOT_FOUND/FORBIDDEN on session-access issues, BAD_REQUEST on a missing model). This call site is not wrapped in try/catch, and nothing further up the chain (admitBasicPrompthandlePromptAsyncMutationhandleKiloFacadeRequestUserKiloFacade.fetch()) catches it either — only the later preflightAndAdmitPromptMessage call a few lines below is wrapped in try/catch { return promptPreflightError(error); }. Any admission failure for kilo.prompt_async (including an ordinary session-not-found or a transient admission-service outage) becomes an unhandled exception instead of the intended structured facadeError(...) response, breaking the documented "fail closed (503, retryable)" contract for this path.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@kilo-code-bot

kilo-code-bot Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: 3 Issues Found | Recommendation: Address before merge

Executive Summary

The previously flagged ClientModule namespace type error is fixed; the remaining InsufficientCreditsError/admission-failure handling gaps in App Builder and the Kilo facade are unresolved.

Overview

Severity Count
CRITICAL 0
WARNING 3
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
apps/web/src/lib/app-builder/app-builder-service.ts 439 InsufficientCreditsError from prepareSession downgraded to generic 500 instead of PAYMENT_REQUIRED
apps/web/src/lib/app-builder/app-builder-service.ts 361 Same gap for sendMessage; no try/catch converts InsufficientCreditsError to 402
services/cloud-agent-next/src/kilo-facade/user-kilo-facade.ts 772 validatePromptBalance call unguarded; preflightCloudAgentModelBilling can throw TRPCError uncaught, breaking the fail-closed contract
Files Reviewed (1 file)
  • apps/web/src/lib/cloud-agent-next/cloud-agent-client.test.ts - 0 issues (previously flagged ClientModule type error fixed)

Fix these issues in Kilo Cloud

Previous Review Summaries (2 snapshots, latest commit 42870ba)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 42870ba)

Status: 4 Issues Found | Recommendation: Address before merge

Executive Summary

A new commit's cleanup of a typeof import(...) type annotation in cloud-agent-client.test.ts introduces a bare-namespace type (ClientModule) that will not compile, and the previously-flagged InsufficientCreditsError/admission-failure handling gaps in App Builder and the Kilo facade remain unresolved.

Overview

Severity Count
CRITICAL 1
WARNING 3
SUGGESTION 0
Issue Details (click to expand)

CRITICAL

File Line Issue
apps/web/src/lib/cloud-agent-next/cloud-agent-client.test.ts 18 ClientModule namespace used bare as a type instead of typeof ClientModule; will not compile (TS2709)

WARNING

File Line Issue
apps/web/src/lib/app-builder/app-builder-service.ts 439 InsufficientCreditsError from prepareSession downgraded to generic 500 instead of PAYMENT_REQUIRED
apps/web/src/lib/app-builder/app-builder-service.ts 361 Same gap for sendMessage; no try/catch converts InsufficientCreditsError to 402
services/cloud-agent-next/src/kilo-facade/user-kilo-facade.ts 772 validatePromptBalance call unguarded; new preflightCloudAgentModelBilling can throw TRPCError uncaught, breaking the fail-closed contract
Files Reviewed (41 files)
  • apps/web/src/app/api/profile/cloud-agent-admission/route.ts - 0 issues
  • apps/web/src/app/api/profile/cloud-agent-admission/route.test.ts - 0 issues
  • apps/web/src/components/cloud-agent-next/NewSessionPanel.tsx - 0 issues
  • apps/web/src/components/cloud-agent-next/new-session-balance.ts - 0 issues
  • apps/web/src/components/cloud-agent-next/new-session-balance.test.ts - 0 issues
  • apps/web/src/lib/app-builder/app-builder-service.ts - 2 issues
  • apps/web/src/lib/bot/tools/spawn-cloud-agent-session.ts - 0 issues
  • apps/web/src/lib/bot/tools/spawn-cloud-agent-session.test.ts - 0 issues
  • apps/web/src/lib/cloud-agent-next/balance-check-eligibility.ts (deleted) - 0 issues
  • apps/web/src/lib/cloud-agent-next/balance-check-eligibility.test.ts (deleted) - 0 issues
  • apps/web/src/lib/cloud-agent-next/classify-model-billing.ts - 0 issues
  • apps/web/src/lib/cloud-agent-next/classify-model-billing.test.ts - 0 issues
  • apps/web/src/lib/cloud-agent-next/cloud-agent-client.ts - 0 issues
  • apps/web/src/lib/cloud-agent-next/cloud-agent-client.test.ts - 1 issue
  • apps/web/src/lib/code-reviews/dispatch/dispatch-pending-reviews.ts - 0 issues
  • apps/web/src/lib/code-reviews/dispatch/dispatch-pending-reviews.test.ts - 0 issues
  • apps/web/src/lib/code-reviews/triggers/prepare-review-payload.ts - 0 issues
  • apps/web/src/lib/discord-bot.ts - 0 issues
  • apps/web/src/routers/cloud-agent-next-eligibility.ts - 0 issues
  • apps/web/src/routers/cloud-agent-next-router.ts - 0 issues
  • apps/web/src/routers/cloud-agent-next-router.test.ts - 0 issues
  • apps/web/src/routers/organizations/organization-cloud-agent-next-router.ts - 0 issues
  • apps/web/src/routers/organizations/organization-cloud-agent-next-router.test.ts - 0 issues
  • services/cloud-agent-next/AGENTS.md - 0 issues
  • services/cloud-agent-next/src/balance-validation.ts - 0 issues
  • services/cloud-agent-next/src/balance-validation.test.ts - 0 issues
  • services/cloud-agent-next/src/cloud-agent-admission.ts - 0 issues
  • services/cloud-agent-next/src/cloud-agent-admission.test.ts - 0 issues
  • services/cloud-agent-next/src/kilo-facade/user-kilo-facade.ts - 1 issue
  • services/cloud-agent-next/src/kilo-facade/user-kilo-facade.test.ts - 0 issues
  • services/cloud-agent-next/src/middleware/balance.ts - 0 issues
  • services/cloud-agent-next/src/middleware/balance.test.ts - 0 issues
  • services/cloud-agent-next/src/model-billing-preflight.ts - 0 issues
  • services/cloud-agent-next/src/model-billing-preflight.test.ts - 0 issues
  • services/cloud-agent-next/src/router/handlers/session-start.ts - 0 issues
  • services/cloud-agent-next/src/session-access.ts - 0 issues
  • services/cloud-agent-next/src/session-prepare.test.ts - 0 issues
  • services/cloud-agent-next/src/session/model-preflight.ts - 0 issues
  • services/cloud-agent-next/test/e2e/client.ts - 0 issues
  • services/code-review-infra/src/code-review-orchestrator.ts - 0 issues
  • services/code-review-infra/src/index.ts - 0 issues
  • services/code-review-infra/src/types.ts - 0 issues
  • services/webhook-agent-ingest/src/queue-consumer.ts - 0 issues
  • services/webhook-agent-ingest/src/queue-consumer.test.ts - 0 issues

Fix these issues in Kilo Cloud

Previous review (commit 8e277aa)

Status: 3 Issues Found | Recommendation: Address before merge

Executive Summary

Removing the x-skip-balance-check bypass makes previously-unreachable InsufficientCreditsError/admission-failure paths reachable in App Builder (app-builder-service.ts) and the Kilo facade (user-kilo-facade.ts), but neither was updated to convert those into the structured payment-required/fail-closed responses used elsewhere in the codebase.

Overview

Severity Count
CRITICAL 0
WARNING 3
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
apps/web/src/lib/app-builder/app-builder-service.ts 439 InsufficientCreditsError from prepareSession downgraded to generic 500 instead of PAYMENT_REQUIRED
apps/web/src/lib/app-builder/app-builder-service.ts 361 Same gap for sendMessage; no try/catch converts InsufficientCreditsError to 402
services/cloud-agent-next/src/kilo-facade/user-kilo-facade.ts 772 validatePromptBalance call unguarded; new preflightCloudAgentModelBilling can throw TRPCError uncaught, breaking the fail-closed contract
Files Reviewed (41 files)
  • apps/web/src/app/api/profile/cloud-agent-admission/route.ts - 0 issues
  • apps/web/src/app/api/profile/cloud-agent-admission/route.test.ts - 0 issues
  • apps/web/src/components/cloud-agent-next/NewSessionPanel.tsx - 0 issues
  • apps/web/src/components/cloud-agent-next/new-session-balance.ts - 0 issues
  • apps/web/src/components/cloud-agent-next/new-session-balance.test.ts - 0 issues
  • apps/web/src/lib/app-builder/app-builder-service.ts - 2 issues
  • apps/web/src/lib/bot/tools/spawn-cloud-agent-session.ts - 0 issues
  • apps/web/src/lib/bot/tools/spawn-cloud-agent-session.test.ts - 0 issues
  • apps/web/src/lib/cloud-agent-next/balance-check-eligibility.ts (deleted) - 0 issues
  • apps/web/src/lib/cloud-agent-next/balance-check-eligibility.test.ts (deleted) - 0 issues
  • apps/web/src/lib/cloud-agent-next/classify-model-billing.ts - 0 issues
  • apps/web/src/lib/cloud-agent-next/classify-model-billing.test.ts - 0 issues
  • apps/web/src/lib/cloud-agent-next/cloud-agent-client.ts - 0 issues
  • apps/web/src/lib/cloud-agent-next/cloud-agent-client.test.ts - 0 issues
  • apps/web/src/lib/code-reviews/dispatch/dispatch-pending-reviews.ts - 0 issues
  • apps/web/src/lib/code-reviews/dispatch/dispatch-pending-reviews.test.ts - 0 issues
  • apps/web/src/lib/code-reviews/triggers/prepare-review-payload.ts - 0 issues
  • apps/web/src/lib/discord-bot.ts - 0 issues
  • apps/web/src/routers/cloud-agent-next-eligibility.ts - 0 issues
  • apps/web/src/routers/cloud-agent-next-router.ts - 0 issues
  • apps/web/src/routers/cloud-agent-next-router.test.ts - 0 issues
  • apps/web/src/routers/organizations/organization-cloud-agent-next-router.ts - 0 issues
  • apps/web/src/routers/organizations/organization-cloud-agent-next-router.test.ts - 0 issues
  • services/cloud-agent-next/AGENTS.md - 0 issues
  • services/cloud-agent-next/src/balance-validation.ts - 0 issues
  • services/cloud-agent-next/src/balance-validation.test.ts - 0 issues
  • services/cloud-agent-next/src/cloud-agent-admission.ts - 0 issues
  • services/cloud-agent-next/src/cloud-agent-admission.test.ts - 0 issues
  • services/cloud-agent-next/src/kilo-facade/user-kilo-facade.ts - 1 issue
  • services/cloud-agent-next/src/kilo-facade/user-kilo-facade.test.ts - 0 issues
  • services/cloud-agent-next/src/middleware/balance.ts - 0 issues
  • services/cloud-agent-next/src/middleware/balance.test.ts - 0 issues
  • services/cloud-agent-next/src/model-billing-preflight.ts - 0 issues
  • services/cloud-agent-next/src/model-billing-preflight.test.ts - 0 issues
  • services/cloud-agent-next/src/router/handlers/session-start.ts - 0 issues
  • services/cloud-agent-next/src/session-access.ts - 0 issues
  • services/cloud-agent-next/src/session-prepare.test.ts - 0 issues
  • services/cloud-agent-next/src/session/model-preflight.ts - 0 issues
  • services/cloud-agent-next/test/e2e/client.ts - 0 issues
  • services/code-review-infra/src/code-review-orchestrator.ts - 0 issues
  • services/code-review-infra/src/index.ts - 0 issues
  • services/code-review-infra/src/types.ts - 0 issues
  • services/webhook-agent-ingest/src/queue-consumer.ts - 0 issues
  • services/webhook-agent-ingest/src/queue-consumer.test.ts - 0 issues

Fix these issues in Kilo Cloud


Reviewed by claude-sonnet-5-20260630 · Input: 28 · Output: 7.2K · Cached: 637.2K

Review guidance: REVIEW.md from base branch main

oxlint forbids `import()` type annotations. Replace the
`typeof import('./cloud-agent-client')` annotation in the client test
with a type-only namespace import so the CI lint check passes.
createAppBuilderCloudAgentNextClient: mockCreateAppBuilderCloudAgentNextClient,
createCloudAgentNextClientForModel,
} = clientModule;
let clientModule: ClientModule;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL: Namespace used bare as a type — will not compile

import type * as ClientModule from './cloud-agent-client' binds ClientModule as a namespace of the module's exports; a namespace import cannot be used directly as a type annotation (TS2709 "Cannot use namespace 'ClientModule' as a type"). This replaces the previously-working typeof import('./cloud-agent-client'), which correctly referred to the module's type. Every other import type * as X usage in this codebase (e.g. apps/web/src/lib/contributor-champions/service.test.ts, apps/web/src/lib/kilo-pass/apple-store-sdk.test.ts) always wraps the namespace in typeof X when using it as a type; this file omits it, so let clientModule: ClientModule; should be let clientModule: typeof ClientModule;.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Using the type-only namespace directly as a type annotation
(`let clientModule: ClientModule`) passes tsc but fails tsgo with
TS2709 "Cannot use namespace 'ClientModule' as a type". Mirror the
repo's existing pattern (auth.test.ts) and use `typeof ClientModule`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant