Skip to content

model picker + sandbox fixes#210

Open
bmdavis419 wants to merge 11 commits intomainfrom
davis/sandbox-fixes
Open

model picker + sandbox fixes#210
bmdavis419 wants to merge 11 commits intomainfrom
davis/sandbox-fixes

Conversation

@bmdavis419
Copy link
Collaborator

@bmdavis419 bmdavis419 commented Mar 5, 2026

sandbox size fixes

sandboxes are not burning cash

Greptile Summary

This PR introduces a per-project model picker (MiniMax M2.5, Claude Haiku 4.5, GPT-5.4) and a significant sandbox cost-control overhaul: the three separate Autumn meters (tokens-in, tokens-out, sandbox-hours) are collapsed into a single ai_budget USD-micros meter with per-model pricing, sandbox auto-stop is enforced at 2 idle minutes, and disk-full errors now surface a clear user-facing message and reset flow. It also adds snapshot migration, GitHub App installation token auth, and per-message stats.

Key changes:

  • New ai_budget billing meter replaces tokens_in / tokens_out / sandbox_hours across usage.ts, billing/, and all UI components
  • New resetProAiBudgetBalances internal action resets pro subscribers' monthly budget — but it is not scheduled in crons.ts, so budgets will never automatically reset
  • Dead code in isOriginAllowed (http.ts): the second if (allowedOrigins.size === 0) block is unreachable after the first local-dev check
  • Schema gains snapshotName + errorKind on instances, stats on messages, and a new githubInstallations table

Confidence Score: 2/5

  • Not safe to merge without fixing: pro users' AI budgets will never reset, breaking the pro tier entirely. Also contains unreachable dead code.
  • One critical functional bug discovered: resetProAiBudgetBalances is implemented but never scheduled in crons.ts, preventing pro users' monthly budget reset. This is a breaking issue for the pro plan. A secondary issue: unreachable dead-code block in http.ts should be removed for code clarity. Both issues must be fixed before merging.
  • apps/web/src/convex/http.ts (dead code cleanup) and apps/web/src/convex/usage.ts + crons.ts (missing cron registration)

Comments Outside Diff (13)

  1. apps/web/src/convex/instances/actions.ts, line 147-151 (link)

    Project model field is never used

    generateBtcaConfig always writes DEFAULT_MODEL / DEFAULT_PROVIDER to the btca config file, even when a project has a custom model stored in the DB (set via the sync MCP action which calls updateProjectModelInternal). This means the model picker has no effect on what model btca actually uses, because the config file is overwritten with the hardcoded values every time the sandbox wakes.

    To wire it up, getResourceConfigs (or a sibling helper) would need to also return the project's model field, and that value would then be passed into generateBtcaConfig in place of DEFAULT_MODEL.

  2. apps/web/src/convex/instances/actions.ts, line 525-531 (link)

    opencodeVersion never populated

    getInstalledVersions only reads btca --version and the InstalledVersions type only has btcaVersion. The schema, store, and UI all now reference opencodeVersion / latestOpencodeVersion, but there is no code path that sets these fields — neither here during provisioning/waking, nor in checkVersions which only fetches the latest btca package.

    As a result, opencodeVersion will always be null in the store and the UI will always display "Unknown" for the opencode version row added in InstanceCard.svelte.

  3. apps/web/autumn.config.ts, line 490-495 (link)

    included_usage: 5 should be 5_000_000 micros

    The ai_budget feature tracks usage in micros (1 USD = 1_000_000 micros), but the Autumn product is configured with included_usage: 5. This means pro users get only 5 micros per month (~$0.000005), which is far below the intended $5/month budget defined in PRO_AI_BUDGET_MICROS = 5_000_000.

    Because ensureUsageAvailable compares proUsage.aiBudget.balance >= requiredBudgetMicros and a trivial 100-token request already requires hundreds of micros, this will cause every pro request to fail the usage check the moment the balance (only 5) is depleted after the very first interaction.

  4. apps/web/src/convex/http.ts, line 1140-1142 (link)

    Dead code — second allowedOrigins.size === 0 check is unreachable

    The first if (allowedOrigins.size === 0) block above this line always returns, so this second identical check can never be reached. The original fallback return false for an empty allowed-origins set is now silently dropped, but the logic is already covered above.

    Simply remove this block.

  5. apps/web/autumn.config.ts, line 591-594 (link)

    included_usage should be 5_000_000, not 5

    The billing code (aiBudget.ts) tracks usage in micros (PRO_AI_BUDGET_MICROS = 5_000_000), and trackUsage is called with chargedBudgetMicros. The Autumn feature balance is also compared against micros in usage.ts:

    const ok = proUsage.aiBudget.balance >= requiredBudgetMicros;

    With included_usage: 5, a Pro user would exhaust their entire monthly budget after roughly 5 micros of spend — effectively after the very first message. The PLAN.md also explicitly specifies included_usage: 5_000_000.

  6. apps/web/autumn.config.ts, line 56-60 (link)

    ai_budget included_usage is wrong — should be 5_000_000, not 5

    The code tracks AI budget in micros. The budget calculation functions in aiBudget.ts return values in micros (totalAiBudgetMicros, getPreflightAiBudgetMicros), and the usage check at line 842 of usage.ts compares proUsage.aiBudget.balance >= requiredBudgetMicros.

    With included_usage: 5, a pro user's balance will only be 5 micros. A typical short message to claude-haiku-4-5 with ~100 input + 100 output tokens already costs ~600 micros. This will cause every preflight check to fail and lock out all pro users after their first message.

    PLAN.md (Phase 2) and aiBudget.ts (PRO_AI_BUDGET_MICROS = 5 * 1_000_000 = 5_000_000) confirm the intended value.

  7. apps/web/src/convex/http.ts, line 241-243 (link)

    Dead code: duplicate allowedOrigins.size === 0 check

    The second if (allowedOrigins.size === 0) block (lines 241–243) is unreachable. If allowedOrigins.size were 0, execution would have already returned inside the first block (lines 232–239). The return false here is never hit; execution always falls through to return allowedOrigins.has(origin).

  8. apps/web/src/convex/http.ts, line 241-243 (link)

    Dead code: unreachable second allowedOrigins.size === 0 check

    The first if (allowedOrigins.size === 0) block at lines 232–239 always returns (either true/false from the localhost check, or false from the catch). The second identical check at this location is unreachable and should be removed to avoid confusion.

    if (allowedOrigins.size === 0) {
        return false;  // Unreachable
    }
  9. apps/web/src/convex/usage.ts, line 546 (link)

    Silent fallback to success URL when checkout_url is missing

    The code silently redirects to the checkout success page if Autumn's attach call returns no checkout_url:

    return Result.ok(payload.data?.checkout_url ?? `${args.baseUrl}${checkoutSuccessPath}`);

    If Autumn returns success but no checkout_url (e.g., due to an API contract change or unexpected response shape), users will silently skip the checkout flow without paying. This differs from createBillingPortalSessionUrl (line 571–578), which properly validates and returns an error when the URL is missing.

    Consider at minimum logging a warning, or returning an error if checkout_url is absent — especially since billing is at stake.

  10. apps/web/src/convex/instances/actions.ts, line 1254 (link)

    updateInstanceInternal calls uploadBtcaConfig(sandbox, resources) without passing a model, so it always defaults to claude-haiku-4-5. This overwrites the user's selected model in the config. If the sandbox is running, it immediately restarts with the wrong model (line 1282).

    Compare to wakeInstanceInternal (line 1125), which correctly retrieves the project's model via getSandboxModelConfig(ctx, instanceId, projectId) before uploading. The fix requires either:

    1. Adding projectId to updateInstanceInternal's signature and passing it to uploadBtcaConfig, or
    2. Querying the active project for this instance and using its model
  11. apps/web/src/convex/http.ts, line 287-289 (link)

    The second if (allowedOrigins.size === 0) check (lines 287-289) is unreachable. The first check at line 278 already handles all branches where allowedOrigins.size === 0 and returns in all cases. This block can be safely removed.

  12. apps/web/src/convex/http.ts, line 288-290 (link)

    The second if (allowedOrigins.size === 0) check is unreachable dead code. The first check at line 279 always returns (either localDevHosts.has(...) or false in the catch block), so execution never reaches this second check. Remove this dead block and keep only return allowedOrigins.has(origin);

  13. apps/web/src/convex/usage.ts, line 969-1024 (link)

    resetProAiBudgetBalances is defined as an internal action but is never scheduled as a cron job. Pro users' AI budgets will never reset, effectively breaking the pro tier. Add this entry to apps/web/src/convex/crons.ts:

    crons.cron(
      'reset-pro-ai-budget',
      '0 0 1 * *', // first day of each month at midnight
      internal.usage.resetProAiBudgetBalances,
      {}
    );

Last reviewed commit: b8cb2eb

@vercel
Copy link

vercel bot commented Mar 5, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
better-context Ready Ready Preview, Comment Mar 8, 2026 10:23pm

Request Review

@bmdavis419 bmdavis419 changed the title sandbox size fixes model picker + sandbox fixes Mar 5, 2026
Copy link
Collaborator Author

bmdavis419 commented Mar 5, 2026

This stack of pull requests is managed by Graphite. Learn more about stacking.

@bmdavis419 bmdavis419 marked this pull request as ready for review March 5, 2026 22:37
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