Skip to content

Commit 3d26096

Browse files
ndemiancclaude
andcommitted
feat(ai): default provider mode to gateway so signed-in users auto-use their plan
`levelcode.ai.providerMode` defaulted to `byok`, and the gateway only engages when the mode is *explicitly* `gateway`. So a user could sign in, have an active LevelCode plan, and still route AI through their own key — their paid subscription silently unused because they never flipped the toggle. Flip the default to `gateway`. This is safe because resolveGateway() already falls back to BYOK whenever there's no token: signed-out users transparently stay on BYOK, and signed-in users route through their metered plan with nothing to toggle. An explicit `byok` choice is still honored (the mode stays a first-class, user-selectable escape hatch). - package.json: default byok → gateway; enum/markdown descriptions updated to say gateway is the default and auto-falls back to BYOK when signed out. - extension.js: matching fallback in providerMode() (byok → gateway) for the unregistered-schema edge. - test/providerMode.test.js: guards the default ('gateway'), that both modes stay selectable, and that the default is safe signed out (no token → BYOK) — so the revenue path can't silently regress to byok. All extension tests pass (node test/*.test.js). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 1f44586 commit 3d26096

3 files changed

Lines changed: 49 additions & 5 deletions

File tree

extensions/levelcode-ai/extension.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ function providerErrorMessage(req) {
157157
/** The configured provider routing mode ('byok' default | 'gateway'). NOTE: the key is
158158
* `levelcode.ai.providerMode`, NOT `provider.mode` — the latter collides with the scalar
159159
* `levelcode.ai.provider` (VS Code navigates into the string → always undefined). */
160-
function providerMode() { return aiConfig().get('providerMode', 'byok'); }
160+
function providerMode() { return aiConfig().get('providerMode', 'gateway'); }
161161

162162
// ── LevelCode Cloud gateway model entitlement (mirrors backend LevelCode.gateway_model) ──────────
163163
// The free plan runs the cheap open-weights engine; a paid plan runs the flagship. The BACKEND

extensions/levelcode-ai/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -357,11 +357,11 @@
357357
"gateway"
358358
],
359359
"enumDescriptions": [
360-
"Bring your own key — AI requests go direct from your machine to your provider; your keys never touch the LevelCode Cloud.",
361-
"LevelCode Cloud gateway — when signed in, AI runs through your metered LevelCode plan (no provider key needed); requests are proxied by the account server."
360+
"Bring your own key — AI requests always go direct from your machine to your provider with your own key, even when you're signed in; your keys never touch LevelCode Cloud.",
361+
"LevelCode Cloud gateway (default) — when you're signed in, AI runs through your metered LevelCode plan (no provider key needed) and automatically falls back to your own key (BYOK) when you're signed out."
362362
],
363-
"default": "byok",
364-
"markdownDescription": "How AI requests are routed. **byok** (default): direct to your provider with your own key. **gateway**: when you're signed in to LevelCode Cloud, requests run through your metered LevelCode plan via `levelcode.cloud.apiUrl` (the Rails backend) — no provider key required. Gateway falls back to BYOK when you're signed out."
363+
"default": "gateway",
364+
"markdownDescription": "How AI requests are routed. **gateway** (default): when you're signed in to LevelCode Cloud, requests run through your metered LevelCode plan via `levelcode.cloud.apiUrl` (the Rails backend) — no provider key required — and **automatically fall back to BYOK when you're signed out**, so simply signing in puts you on your plan with nothing to toggle. **byok**: always go direct to your provider with your own key, even when signed in. (The gateway is only ever used while signed in, so this default is safe when signed out.)"
365365
},
366366
"levelcode.ai.commandTimeout": {
367367
"type": "number",
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Guards the "signed-in users auto-use the paid gateway" behavior — run: node test/providerMode.test.js
3+
*
4+
* levelcode.ai.providerMode MUST default to 'gateway'. Because resolveGateway() falls back to BYOK
5+
* whenever there's no token (signed out — see gateway.test.js), a 'gateway' default means signing in
6+
* is all it takes to route through the metered plan, while signed-out users transparently stay on
7+
* BYOK. If this regresses to 'byok', paid subscriptions silently go unused (users would have to find
8+
* and flip the toggle by hand), so this test fails loudly to protect that revenue path.
9+
*--------------------------------------------------------------------------------------------*/
10+
// @ts-check
11+
'use strict';
12+
13+
const assert = require('assert');
14+
const { resolveGateway } = require('../providers/gateway');
15+
const pkg = require('../package.json');
16+
17+
let n = 0;
18+
function test(name, fn) { fn(); n++; console.log(' ok - ' + name); }
19+
20+
/** The contributed setting node, tolerating configuration being a single object or an array. */
21+
function providerModeSetting() {
22+
const c = pkg.contributes.configuration;
23+
const props = Array.isArray(c) ? Object.assign({}, ...c.map(x => x.properties || {})) : (c && c.properties) || {};
24+
return props['levelcode.ai.providerMode'];
25+
}
26+
27+
test('providerMode defaults to gateway (signed-in users auto-use their plan)', () => {
28+
const s = providerModeSetting();
29+
assert.ok(s, 'levelcode.ai.providerMode must be a contributed setting');
30+
assert.strictEqual(s.default, 'gateway', "default must be 'gateway' — signing in should route through the plan with no manual toggle");
31+
});
32+
33+
test('both modes stay selectable so a user can still opt back into BYOK', () => {
34+
const s = providerModeSetting();
35+
assert.deepStrictEqual([...(s.enum || [])].sort(), ['byok', 'gateway']);
36+
});
37+
38+
test('the gateway default is safe when signed out: no token → falls back to BYOK', () => {
39+
// This is what makes 'gateway' a safe DEFAULT rather than a footgun: with the default mode but no
40+
// session, the router does not try to use the gateway — it hands back to the BYOK path.
41+
assert.deepStrictEqual(resolveGateway({ mode: 'gateway', endpoint: 'https://levelcode.ai', token: '' }), { use: false });
42+
});
43+
44+
console.log('\nproviderMode: ' + n + ' tests passed.');

0 commit comments

Comments
 (0)