SIGN IN SIGN UP

Proj/rework llm billing (#653)

* feat: [ENG-2512] surface ByteRover credits and add billing team picker

Adds the billing transport layer (BillingEvents + team:list) backed by an
HttpBillingService against BRV_BILLING_BASE_URL and a file-backed pinned-team
store. The header trigger drops

* feat: [ENG-2512] refactor billing chain: discriminated DTO, brv-provider.json, non-interactive UX

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: [ENG-2512] split type and value imports in agent-process

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat: [ENG-2512] handle billing:resolve in tier-2 query test mocks

* feat: [ENG-2512] address PR bot review — slug for top-up URL, filter pinned team from suggestions, preserve unknown config keys

* feat: [ENG-2512] route InsufficientCreditsError through reportError

* feat: [ENG-2512] rework billing-team resolution and fix project-switch race

- Remove daemon-side workspace fallback from `resolveBillingTeamId`; it's now a pure prediction helper (pin / single-paid / undefined).
- Add `projectPath` to `billing:getPinnedTeam`, `billing:setPinnedTeam`, and `auth:getState` request bodies so the daemon resolves the project explicitly per request instead of via per-client state.
- Add `computeTeamPreselection` so the team picker can visually pre-select a row without writing the pin to disk until the user clicks Confirm.
- `useBillingDisplay`: fall through to auto-pick when the pin is stale; ignore pin entirely when the user has no paid teams (free monthly always wins).
- Header: drop the workspace fallback; show the team that's actually being billed.
- `team-select-step`: disable Confirm when the current selection isn't in the team list (stale workspace id or removed team).
- `BrvApiClient.request`: pre-check `socket.connected` and fast-fail on disconnect so requests don't silently wait the full 5s timeout during project-switch socket teardown.

* feat: [ENG-2512] address PR bot review — validate projectPath, drop workspace fallback in provider list, harden tests

- billing-handler: reject empty `projectPath` for `billing:getPinnedTeam` and `billing:setPinnedTeam` so a stray empty string from the client can't end up writing the pin to the daemon's CWD.
- provider-select-step: drop the `?? teamId` workspace fallback to match the header (was the only remaining surface still showing the old behavior).
- set-pinned-team: read `projectPath` at mutate-call time via `useTransportStore.getState()` so a project switch mid-dialog can't write the pin to the previous project.
- Tests: add behavioral coverage for `BrvApiClient.request` (not-connected pre-check, disconnect-before-ack, normal ack) and for empty-projectPath rejection in both billing handlers.

* feat: [ENG-2512] address second PR bot pass — gate empty-projectPath queries, assert listener cleanup

- get-pinned-team / get-auth-state: gate react-query on `projectPath !== ''` so we don't fire a doomed request on initial mount (cached error envelope is now skipped entirely).
- api-client tests: assert `socket.off('disconnect', ...)` runs on both the ack-success and timeout paths so a future regression that drops the cleanup is caught.

* feat: [ENG-2512] AND-merge enabled gate so callers can't bypass the projectPath check

Reverse the spread order in `useGetAuthState` (was queryConfig last) so the projectPath gate was being silently overridden by `auth-initializer.tsx`'s `enabled: apiClient !== null`. Switch both hooks to an explicit AND-merge form (`base.enabled !== false && (queryConfig?.enabled ?? true)`) so neither the gate nor a caller's enable check can clobber the other.

---------

Co-authored-by: wzlng <llmaniac@icloud.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: wzlng <74853570+wzlng@users.noreply.github.com>
N
Nguyen Cong Nhat Thien committed
1c54787fefa25fe4c8f6aa26008367aeb336f8b7
Parent: a8594cb
Committed by GitHub <noreply@github.com> on 5/14/2026, 7:44:38 AM