Skip to content

fix(usage): include connected hosts in slash command limits - #13127

Open
chrisdeeming wants to merge 23 commits into
pingdotgg:mainfrom
chrisdeeming:fix/usage-limits-connected-hosts
Open

chrisdeeming wants to merge 23 commits into
pingdotgg:mainfrom
chrisdeeming:fix/usage-limits-connected-hosts

Conversation

@chrisdeeming

@chrisdeeming chrisdeeming commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

What Changed

/usage-limits now selects accounts from all connected environments using the same pooled account selection as Usage → Limits, filtered to the chat's provider. This applies to web, desktop, and mobile, including command-menu availability when only another host reports limits.

Accounts are deduplicated, the freshest quota wins, unsupported API-key rows are omitted, and reset-credit actions retain the environment that owns the native account or hub.

Why

A Claude thread on one host could show only “no subscription limits” while a CLIProxyAPI hub on another connected host appeared correctly in Limits. The slash panel read only the thread's environment and used separate selection rules. Those rules also preferred stale native quota over fresher hub readings.

Reusing the pooled selector fixes these discrepancies without introducing another quota probe or agent turn. This follows up on #9875.

UI Changes

The existing composer panel and quota components are retained; the accounts and quota values they receive now match the shared Limits selection. Fresh before/after screenshots and integrated browser/mobile verification are still pending, so this PR is a draft.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes (no animation changes)

Validation:

  • 37 shared selection and mobile command-menu tests passed, including remote-host Claude hubs, freshest snapshots, deduplication, and redemption routing.
  • 7 focused server subscription tests passed.
  • Web, mobile, and shared typechecks passed.
  • Targeted lint passed with warnings; git diff --check passed.

Implemented with GPT-6 in the Codex harness via T3 Code.

Summary by CodeRabbit

  • New Features

    • /usage-limits appears when supported and available within threads.
    • Usage limits include eligible accounts and hubs across connected environments for the selected model provider.
    • Limits use the freshest available quota information and route credit resets to the correct environment.
  • Bug Fixes

    • Improved consistency with Usage → Limits.
    • Offline environments and unsupported providers no longer show unavailable usage-limit options.
  • Documentation

    • Updated usage-limits documentation for cross-environment account selection and current snapshots.

Sending /usage-limits in a thread opens the current model's limits above
the composer, built from the same snapshot as Usage > Limits. The command
resolves in the client without starting a turn, so nothing is written to
the thread; the panel closes on dismiss or the next send.

Web renders it as a composer notice so it stacks under warnings and uses
the standard dismiss. Mobile docks an opaque card in the approval slot.
The server only advertises the command for providers present in Limits.

Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
Review follow-ups for /usage-limits: drop the snapshot when the thread or
model changes, keep it hidden rather than dropped while the provider list
is unavailable, clear it only when a message actually leaves, clear it
when a later request has nothing to show, let submissions carrying
attachments or contexts send as prompts, and report a usage-limit source
that failed to read even though it has no accounts left to match.

Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
Seed the provider status stream with the current providers so a usage
limit source refresh reaches clients before any provider change, with a
test. Tie the /usage-limits snapshot to the turn and clear it once an
approval or question is answered, since the agent then spends quota.

Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
The /usage-limits panel now reads provider data at render, so a redeemed
reset credit or refreshed probe shows through instead of a frozen
snapshot. Mobile New Task no longer offers the command and refuses to
send it, since only the thread composer can answer it.

Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
Choosing /usage-limits from the composer menu opens the panel at once
instead of leaving the command in the draft to be sent. Typing it out and
sending still works as a fallback.

Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
Compare provider status pairings against the snapshot the client holds
instead of dropping the first one, so a refresh between snapshot and
subscription still goes out. A usage-limit source that failed to read
now counts for every driver, so its error reaches the panel. Only a
successful approval or answer clears the panel, and New Task lets a
prompt with attachments through.

Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
Native accounts in the panel show their instance id when they share a
driver and have no display name. A response that resolves after
navigating away clears only the originating thread's panel. The catalog
republish comparator now lives in shared, counts a failed source as
covering every driver, and is tested.

Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
A report with no accounts, such as a failed hub and no native windows,
now renders its own heading row with the close control.

Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
A send can outlast a navigation while attachments upload, so it clears
only the originating thread's limits panel instead of whatever is open.

Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
Only a delivered approval or answer resumes the agent, so a failed reply
leaves the snapshot in place.

Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
Source-backed rows showed only the hub kind, so two accounts from one
hub were indistinguishable. They now carry the hub and account id.

Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
Ownership was a prefix match on a composite key; thread ids may contain
the delimiter, so the panel now stores its thread key and compares it.

Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
A send that fails before the turn starts spends no quota, so it leaves
the panel in place. Both the composer and plan follow-up paths clear
after a successful start.

Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
The command name is only T3's where Limits has data for the selected
provider; elsewhere a provider's own command of that name is sent
through untouched, on the menu, on send, and in New Task. The panel key
now includes any pending approval or question, so an answer from any
client closes it, which also retires the local response wrappers. The
web key stays hidden rather than dropped while a server thread is not
yet loaded.

Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
Clients that handle the command locally say so on the config
subscription; the server injects it into provider catalogs only for
them. An older client, which would send the injected command to the
provider as a prompt, never sees it. The one-shot config fetch stays
without it.

Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
The menu handler and the send intercept now require an available panel
key, so a reconnect or loading thread leaves the text in the composer.

Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
Picking /usage-limits with attachments or contexts aboard inserts the
command text instead, so it sends as a prompt, matching the typed path.

Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
Limits bars and labels now show what is left in each window, matching
Codex and T3 Chat. The even-spending mark moves to the time left so the
fill sits on the line when spending is on pace, and mobile warns as the
remainder drops below 30% and 10%. Applies to Usage > Limits and the
/usage-limits panel on web and mobile.

Model: Claude Fable 5.1. Harness: Claude Code in T3 Code.
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Sep 22, 2026
@chrisdeeming
chrisdeeming marked this pull request as ready for review September 22, 2026 20:57
@macroscopeapp

macroscopeapp Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

All clear

Posted via Macroscope — Effect Service Conventions

const offersUsageLimits = useMemo(
() =>
flow.selectedProviderStatus !== null &&
hasPooledProviderUsageLimits(flow.selectedProviderStatus.driver, limitPresentations),

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.

🟠 High threads/NewTaskDraftScreen.tsx:435

When the selected environment is disconnected, a stale quota presentation from that host can make offersUsageLimits true, so submitting a bare /usage-limits in New Task is intercepted by the local alert at line 1216 instead of being forwarded to the selected provider. limitPresentations contains disconnected catalog entries and this selector does not filter on presentation.connection.phase; restrict the pooled-limit check to presentations for the selected, currently connected environment (or otherwise exclude disconnected entries).

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/mobile/src/features/threads/NewTaskDraftScreen.tsx around line 435:

When the selected environment is disconnected, a stale quota presentation from that host can make `offersUsageLimits` true, so submitting a bare `/usage-limits` in New Task is intercepted by the local alert at line 1216 instead of being forwarded to the selected provider. `limitPresentations` contains disconnected catalog entries and this selector does not filter on `presentation.connection.phase`; restrict the pooled-limit check to presentations for the selected, currently connected environment (or otherwise exclude disconnected entries).

@macroscopeapp

macroscopeapp Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This change broadens usage-limit handling across connected environments and changes existing report, command-interception, and reset-credit routing behavior across web and mobile. The cross-cutting runtime impact and an unresolved high-severity finding require human review.

Not approved because:

  • 1 blocking correctness issue found at or above your repo's Minimum Blocking Severity

Adjust the Minimum Blocking Severity for this repo — including turning it Off — in Settings. You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: pingdotgg/t3code/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 0afbe6f9-491c-45a1-b15c-8b5f86c7d6d4

📥 Commits

Reviewing files that changed from the base of the PR and between bbabdb8 and 17d3b17.

📒 Files selected for processing (3)
  • apps/mobile/src/widgets/subscriptionUsageSnapshot.test.ts
  • packages/shared/src/usageLimits.test.ts
  • packages/shared/src/usageLimits.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review.


📝 Walkthrough

Walkthrough

Usage-limit collection now uses pooled environment presentations across web and mobile. Reports include connected-environment accounts and redemption environments. Composer commands are conditionally injected, and reset-credit actions use each account’s environment when available.

Changes

Pooled usage limits

Layer / File(s) Summary
Shared usage-limit contracts and collection
packages/contracts/src/providerUsageLimits.ts, packages/shared/src/usageLimits.ts
Usage-limit reports can store a reset environment. Collection now accepts pooled presentations and provider drivers. New helpers provide availability checks and conditional command injection.
Pooled collection and command validation
packages/shared/src/usageLimits.test.ts
Tests cover pooled accounts, freshest snapshots, redemption routing, deduplication, connection phases, failure notices, and slash-command behavior.
Web pooled usage-limit integration
apps/web/src/components/ChatView.tsx, apps/web/src/components/chat/ChatComposer.tsx, apps/web/src/components/chat/ComposerUsageLimits.tsx, docs/user/usage.md
Web reports and availability checks read pooled presentations. Composer commands use the shared injection helper. Reset credits use the account reset environment. Documentation describes connected-environment results.
Mobile pooled usage-limit integration
apps/mobile/src/features/threads/*, apps/mobile/src/widgets/subscriptionUsageSnapshot.test.ts
Mobile reports and availability checks read pooled presentations. Threaded composers conditionally expose /usage-limits. Reset credits use the account reset environment. Tests cover threaded commands and connected presentation fixtures.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant Composer
  participant environmentPresentations
  participant usageLimits
  participant ResetCredits
  Composer->>environmentPresentations: read presentationsAtom
  Composer->>usageLimits: check driver and limitPresentations
  usageLimits-->>Composer: return availability or UsageLimitsReport
  Composer->>ResetCredits: pass account resetCreditEnvironmentId when present
Loading

Suggested reviewers: juliusmarminge

Merge Risk: 🔵 Low · up to 17d3b

The main pooled flow is consistent, but affected users may see an unavailable command or lose a provider command in mobile New Task; merge is otherwise low risk with bounded follow-up.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 15 functions across 13 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: including connected hosts in the usage-limits slash command.
Description check ✅ Passed The description explains what changed, why it changed, UI impact, validation results, and remaining verification work. The UI evidence is incomplete because screenshots and integrated browser/mobile v…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

Comment @coderabbitai help to get the list of available commands.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant