Skip to content

Commit bdded05

Browse files
lidge-junwtfsayo
andcommitted
fix(quota): reject negative Devin used credits
Co-authored-by: Sayo <[email protected]>
1 parent 043f0b8 commit bdded05

4 files changed

Lines changed: 12 additions & 3 deletions

File tree

‎docs-site/src/content/docs/guides/providers.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ ocx logout <provider>
205205
| `google-antigravity` | `google` | `https://daily-cloudcode-pa.googleapis.com` | Google OAuth over the Cloud Code Assist wire. Live discovery uses CCA's authenticated `v1internal:fetchAvailableModels` endpoint and publishes the agent models available to the signed-in account; the maintained catalog remains the fallback. |
206206
| `cursor` | `cursor` | `https://api2.cursor.sh` | Experimental PKCE login, live HTTP/2 transport with an opt-in HTTP/1.1 compatibility path, and account-filtered model discovery. |
207207
| `orcarouter-oauth` | `openai-chat` | `https://api.orcarouter.ai/v1` | Browser consent and key exchange use `https://www.orcarouter.ai` with S256 PKCE. The returned user-owned `sk-orca-…` API key is stored in the existing credential store and reused until revoked. |
208-
| `devin` | `devin` | `https://server.codeium.com` | Experimental unofficial Cognition/Devin bridge. Login first imports the credential the installed Devin CLI already holds (`devin auth login` writes a `devin-session-token` to its own `credentials.toml`); when none is present it opens Auth0 browser sign-in and exchanges the pasted token via Cognition's `RegisterUser` for a long-lived API key. `ocx login devin-cli` remains as a deprecated alias. Models are discovered per account with `GetCascadeModelConfigs`. Account quota comes from `GetUserStatus` under one eight-second request and body deadline: dated daily and weekly windows, plus the monthly prompt and flex credit pool for a credit-billed plan or an unknown strategy with both reset dates absent when a balance field is present. A timed-out probe keeps the last good quota. Not shown in the dashboard preset by default. Chat and usage reporting are verified against a live account across three models. |
208+
| `devin` | `devin` | `https://server.codeium.com` | Experimental unofficial Cognition/Devin bridge. Login first imports the credential the installed Devin CLI already holds (`devin auth login` writes a `devin-session-token` to its own `credentials.toml`); when none is present it opens Auth0 browser sign-in and exchanges the pasted token via Cognition's `RegisterUser` for a long-lived API key. `ocx login devin-cli` remains as a deprecated alias. Models are discovered per account with `GetCascadeModelConfigs`. Account quota comes from `GetUserStatus` under one eight-second request and body deadline: dated daily and weekly windows, plus the monthly prompt and flex credit pool for a credit-billed plan or an unknown strategy with both reset dates absent when a balance field is present. Negative used credits omit the monthly window; valid zero available credits mark it exhausted. A timed-out probe keeps the last good quota. Not shown in the dashboard preset by default. Chat and usage reporting are verified against a live account across three models. |
209209
| `github-copilot` | `openai-chat` | `https://api.githubcopilot.com` | Experimental. GitHub device flow + `copilot_internal` exchange (VS Code OAuth client). Requires an active Copilot subscription; not an official third-party API. |
210210

211211
Google Antigravity account and provider quota probes use fixed Google accounting endpoints, including the models fallback. They support transparent Fake-IP DNS for those destinations while retaining TLS verification, redirect rejection and private-address checks. A custom provider base URL changes model requests, not quota destinations; `NO_PROXY` continues to select the direct-route policy.

‎src/providers/quota/devin.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,9 @@ export function devinQuotaFromStatus(status: DevinUserStatus, now = Date.now()):
221221
const creditBilled = strategy === BILLING_STRATEGY_CREDITS
222222
|| (strategy === 0 && status.dailyResetMs === undefined && status.weeklyResetMs === undefined);
223223
const available = status.availablePromptCredits + status.availableFlexCredits;
224-
const credits = !creditBilled || !status.promptCreditBalancePresent || status.availablePromptCredits < 0 || status.availableFlexCredits < 0
224+
const credits = !creditBilled || !status.promptCreditBalancePresent
225+
|| status.availablePromptCredits < 0 || status.availableFlexCredits < 0
226+
|| status.usedPromptCredits < 0 || status.usedFlexCredits < 0
225227
? undefined
226228
// A credit-billed plan with nothing used and nothing left has no balance to serve from;
227229
// leaving it unmeasured would rank it as untested headroom.

‎structure/providers-and-adapters.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ rolled-over window; both would read as exhausted. Prompt plus flex credits form
8585
measured against the server balance, published only for a credit-billed plan (or an unknown
8686
strategy with both reset fields absent) when at least one of the four prompt/flex balance fields
8787
is present (proto3 omits zeros, so an exhausted pool arrives as a used count alone); a negative
88-
balance is the unlimited sentinel, and zero available reads as exhausted. Expired dated windows
88+
available balance is the unlimited sentinel; a negative used balance is malformed and omits the
89+
monthly window even when zero is available. Valid zero available reads as exhausted.
90+
Expired dated windows
8991
do not cause the credit fallback. Only a 401 rejects the credential and clears last-good; a 403
9092
may scope this one RPC away from a key that still serves
9193
chat. Other HTTP failures and malformed protobufs, including a wrong

‎tests/providers/devin-quota.test.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,11 @@ describe("Devin GetUserStatus decode and mapping", () => {
147147
expect(devinQuotaFromStatus(decodeDevinUserStatus(creditPlan({ 6: 0, 8: 0 }))!, 1).monthlyPercent).toBe(100);
148148
});
149149

150+
test.each([6, 7])("a negative used credit field %i cannot mark a zero-available pool exhausted", usedField => {
151+
const status = decodeDevinUserStatus(creditPlan({ [usedField]: -1, 8: 0, 4: 0 }))!;
152+
expect(devinQuotaFromStatus(status, 1)).toEqual({ updatedAt: 1 });
153+
});
154+
150155
test("missing credit balance fields do not publish an exhausted monthly window", () => {
151156
expect(devinQuotaFromStatus(decodeDevinUserStatus(creditPlan({}))!, 1).monthlyPercent).toBeUndefined();
152157
});

0 commit comments

Comments
 (0)