saros-proxy 0.7.4 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +116 -64
  3. package/dist/cli/autostart.d.ts +0 -0
  4. package/dist/cli/autostart.js +0 -0
  5. package/dist/cli/autostart.js.map +0 -0
  6. package/dist/cli/daemon.d.ts +0 -0
  7. package/dist/cli/daemon.js +4 -0
  8. package/dist/cli/daemon.js.map +1 -1
  9. package/dist/cli/dsh-config.d.ts +32 -0
  10. package/dist/cli/dsh-config.js +122 -0
  11. package/dist/cli/dsh-config.js.map +1 -0
  12. package/dist/cli/harness-models.d.ts +26 -2
  13. package/dist/cli/harness-models.js +192 -2
  14. package/dist/cli/harness-models.js.map +1 -1
  15. package/dist/cli/harness-sync.d.ts +3 -6
  16. package/dist/cli/harness-sync.js +14 -7
  17. package/dist/cli/harness-sync.js.map +1 -1
  18. package/dist/cli/help.d.ts +0 -0
  19. package/dist/cli/help.js +2 -2
  20. package/dist/cli/help.js.map +0 -0
  21. package/dist/cli/omp-config.d.ts +0 -0
  22. package/dist/cli/omp-config.js +11 -1
  23. package/dist/cli/omp-config.js.map +1 -1
  24. package/dist/cli/opencode-config.d.ts +0 -0
  25. package/dist/cli/opencode-config.js +0 -0
  26. package/dist/cli/opencode-config.js.map +0 -0
  27. package/dist/cli/overview.d.ts +0 -0
  28. package/dist/cli/overview.js +2 -1
  29. package/dist/cli/overview.js.map +1 -1
  30. package/dist/cli/pi-config.d.ts +0 -0
  31. package/dist/cli/pi-config.js +11 -1
  32. package/dist/cli/pi-config.js.map +1 -1
  33. package/dist/cli/setup.d.ts +6 -10
  34. package/dist/cli/setup.js +100 -232
  35. package/dist/cli/setup.js.map +1 -1
  36. package/dist/cli/ui.d.ts +1 -2
  37. package/dist/cli/ui.js +0 -16
  38. package/dist/cli/ui.js.map +1 -1
  39. package/dist/cli/update-check.d.ts +0 -0
  40. package/dist/cli/update-check.js +0 -0
  41. package/dist/cli/update-check.js.map +0 -0
  42. package/dist/cli/usage.d.ts +12 -0
  43. package/dist/cli/usage.js +182 -0
  44. package/dist/cli/usage.js.map +1 -0
  45. package/dist/config.d.ts +13 -17
  46. package/dist/config.js +29 -78
  47. package/dist/config.js.map +1 -1
  48. package/dist/constants.d.ts +9 -5
  49. package/dist/constants.js +14 -5
  50. package/dist/constants.js.map +1 -1
  51. package/dist/index.d.ts +0 -0
  52. package/dist/index.js +9 -25
  53. package/dist/index.js.map +1 -1
  54. package/dist/key-encryption.d.ts +0 -0
  55. package/dist/key-encryption.js +0 -0
  56. package/dist/key-encryption.js.map +0 -0
  57. package/dist/logger.d.ts +0 -0
  58. package/dist/logger.js +0 -0
  59. package/dist/logger.js.map +0 -0
  60. package/dist/model-probe.d.ts +0 -0
  61. package/dist/model-probe.js +0 -0
  62. package/dist/model-probe.js.map +0 -0
  63. package/dist/model-stats.d.ts +20 -0
  64. package/dist/model-stats.js +135 -0
  65. package/dist/model-stats.js.map +1 -0
  66. package/dist/models-fetcher.d.ts +0 -0
  67. package/dist/models-fetcher.js +0 -0
  68. package/dist/models-fetcher.js.map +0 -0
  69. package/dist/models-sync.d.ts +0 -0
  70. package/dist/models-sync.js +0 -0
  71. package/dist/models-sync.js.map +0 -0
  72. package/dist/probe-cache.d.ts +0 -0
  73. package/dist/probe-cache.js +0 -0
  74. package/dist/probe-cache.js.map +0 -0
  75. package/dist/providers/commandcode.d.ts +17 -0
  76. package/dist/providers/commandcode.js +237 -0
  77. package/dist/providers/commandcode.js.map +1 -0
  78. package/dist/providers/index.d.ts +43 -0
  79. package/dist/providers/index.js +75 -0
  80. package/dist/providers/index.js.map +1 -0
  81. package/dist/providers/infer.d.ts +23 -0
  82. package/dist/providers/infer.js +13 -0
  83. package/dist/providers/infer.js.map +1 -0
  84. package/dist/providers/opencode-go.d.ts +4 -0
  85. package/dist/providers/opencode-go.js +190 -0
  86. package/dist/providers/opencode-go.js.map +1 -0
  87. package/dist/providers/types.d.ts +135 -0
  88. package/dist/providers/types.js +10 -0
  89. package/dist/providers/types.js.map +1 -0
  90. package/dist/proxy-logic.d.ts +61 -22
  91. package/dist/proxy-logic.js +163 -106
  92. package/dist/proxy-logic.js.map +1 -1
  93. package/dist/proxy.d.ts +43 -2
  94. package/dist/proxy.js +327 -62
  95. package/dist/proxy.js.map +1 -1
  96. package/dist/token-usage.d.ts +53 -0
  97. package/dist/token-usage.js +198 -0
  98. package/dist/token-usage.js.map +1 -0
  99. package/dist/usage-client.d.ts +62 -0
  100. package/dist/usage-client.js +155 -0
  101. package/dist/usage-client.js.map +1 -0
  102. package/dist/validation.d.ts +5 -5
  103. package/dist/validation.js +7 -9
  104. package/dist/validation.js.map +1 -1
  105. package/package.json +11 -7
  106. package/dist/dashboard-parser.d.ts +0 -47
  107. package/dist/dashboard-parser.js +0 -88
  108. package/dist/dashboard-parser.js.map +0 -1
  109. package/dist/firefox-cookies.d.ts +0 -42
  110. package/dist/firefox-cookies.js +0 -170
  111. package/dist/firefox-cookies.js.map +0 -1
  112. package/dist/scraper.d.ts +0 -70
  113. package/dist/scraper.js +0 -239
  114. package/dist/scraper.js.map +0 -1
@@ -12,21 +12,45 @@
12
12
  interface ApiKey {
13
13
  label: string;
14
14
  key: string;
15
+ /** Provider id ('opencode-go' | 'commandcode' | future extensions). */
16
+ provider: string;
15
17
  enabled: boolean;
16
18
  consecutiveFailures: number;
17
19
  lastUsed: number | null;
18
20
  disabledAt: number | null;
21
+ /** Worst-window used percent (0-100) reported by the upstream usage API. */
22
+ usagePercent: number | null;
23
+ /**
24
+ * False when the upstream rejected this key for lack of plan
25
+ * (403 EntitlementError / upgrade_required): the account cannot use the
26
+ * provider's models at all, so the key leaves rotation until the usage
27
+ * refresh sees it work again. Independent of the circuit breaker — this is
28
+ * not a transient fault.
29
+ */
30
+ entitled: boolean;
31
+ /** Smooth weighted round-robin state (nginx-style). */
32
+ currentWeight: number;
19
33
  }
20
34
  /** Read-only projection returned to callers — cannot mutate internal state. */
21
35
  export interface KeySnapshot {
22
36
  label: string;
23
37
  key: string;
38
+ /** Provider this key belongs to (set at state creation from config). */
39
+ provider: string;
24
40
  }
25
41
  /** Tracks a single HTTP request's key usage through its lifecycle. */
26
42
  interface RequestContext {
27
43
  requestId: string;
28
44
  triedKeys: string[];
29
45
  currentKey: KeySnapshot | null;
46
+ /**
47
+ * Provider filter fixed at selection time (model affinity / @suffix pin).
48
+ * Failover re-applies it so a pinned commandcode request never fails over
49
+ * to an opencode-go key that cannot serve the (already rewritten) model id.
50
+ */
51
+ providerFilter?: (provider: string) => boolean;
52
+ /** True when the model is provider-pinned (@suffix) — tier-3 cross-provider fallback disabled. */
53
+ pinned?: boolean;
30
54
  }
31
55
  export interface ProxyState {
32
56
  keys: ApiKey[];
@@ -34,51 +58,63 @@ export interface ProxyState {
34
58
  circuitBreakerThreshold: number;
35
59
  circuitBreakerCooldownMs: number;
36
60
  activeRequests: Map<string, RequestContext>;
37
- }
38
- /**
39
- * Usage data for a single account, scraped from the OpenCode-Go workspace.
40
- * All values are percentages (0-100). null means no data for that window.
41
- */
42
- export interface UsageInfo {
43
- rolling: number | null;
44
- weekly: number | null;
45
- monthly: number | null;
46
- }
47
- /**
48
- * Optional parameters for usage-gated key selection.
49
- */
50
- export interface KeySelectionOptions {
51
- usageMap?: Map<string, UsageInfo>;
52
- usageThreshold?: number;
61
+ /**
62
+ * Optional model→provider affinity resolver: (providerId, modelId) →
63
+ * 'yes' | 'no' | 'maybe'. Wired from the provider registry at app setup;
64
+ * absence disables affinity routing (single-provider behavior).
65
+ */
66
+ affinityResolver?: (provider: string, modelId: string) => 'yes' | 'no' | 'maybe';
53
67
  }
54
68
  /**
55
69
  * Error classification for HTTP responses.
56
- * - KeyFault: the API key should be penalised (invalid, revoked, rate-limited, server fault)
70
+ * - KeyFault: the API key should be penalised (invalid, revoked, quota exhausted)
57
71
  * - RequestFault: the request itself is bad — no key penalisation
58
72
  * - ServerFault: transient server issue — key penalised (normal circuit-breaker)
73
+ * - NetworkFault: timeout / connection error — no key penalisation. The
74
+ * upstream base URL is shared by every key, so network-level failures are
75
+ * not a per-key fault; disabling keys for them caused total-blackout
76
+ * lockups ("all keys unavailable") when the upstream hung. Failover still
77
+ * tries the next key for this request.
59
78
  */
60
- export type ErrorType = 'KeyFault' | 'RequestFault' | 'ServerFault';
79
+ export type ErrorType = 'KeyFault' | 'RequestFault' | 'ServerFault' | 'NetworkFault';
61
80
  export declare function createProxyState(keys: Array<{
62
81
  label: string;
63
82
  key: string;
83
+ provider?: string;
64
84
  }>, options?: {
65
85
  circuitBreakerThreshold?: number;
66
86
  circuitBreakerCooldownMs?: number;
67
87
  }): ProxyState;
88
+ /**
89
+ * Update per-key usage data (worst-window used percent, 0-100).
90
+ * Keys absent from the map keep their previous value.
91
+ */
92
+ export declare function updateKeyUsage(state: ProxyState, usage: Map<string, number>): void;
93
+ /**
94
+ * Mark a key as (not) entitled to use its provider. Unentitled keys are
95
+ * skipped by selection without touching the circuit breaker; the usage
96
+ * refresh flips the flag back as soon as the account can serve requests
97
+ * again.
98
+ */
99
+ export declare function setKeyEntitlement(state: ProxyState, keyLabel: string, entitled: boolean): void;
68
100
  /**
69
101
  * Obtain a key for a new (or in-flight) request.
70
102
  * Creates a RequestContext if one does not already exist for this requestId.
71
103
  * Skips keys that have already been tried for this request AND keys booked
72
104
  * by any other active request.
105
+ *
106
+ * When a modelId is supplied and some providers claim it (modelAffinity
107
+ * 'yes'), keys of other providers are only used after the preferred pool is
108
+ * exhausted (including its booked-share tier).
73
109
  */
74
- export declare function selectKeyForRequest(state: ProxyState, requestId: string, options?: KeySelectionOptions): KeySnapshot | null;
110
+ export declare function selectKeyForRequest(state: ProxyState, requestId: string, modelId?: string): KeySnapshot | null;
75
111
  /**
76
112
  * Failover: record the current key of this request as failed and pick the next
77
113
  * available key. The caller should also call markKeyFailed() with the
78
114
  * appropriate ErrorType; this function only manages the request-context
79
115
  * tracking (adds the current key to triedKeys).
80
116
  */
81
- export declare function failoverRequest(state: ProxyState, requestId: string, options?: KeySelectionOptions): KeySnapshot | null;
117
+ export declare function failoverRequest(state: ProxyState, requestId: string): KeySnapshot | null;
82
118
  /**
83
119
  * Complete a request: if successful, mark the current key as succeeded;
84
120
  * always clean up the request context.
@@ -87,7 +123,8 @@ export declare function completeRequest(state: ProxyState, requestId: string, su
87
123
  /**
88
124
  * Mark a key as failed according to the error type (C2).
89
125
  * - KeyFault / ServerFault: penalise the key (KeyFault disables immediately)
90
- * - RequestFault: no penalisation (bad request, not bad key)
126
+ * - RequestFault / NetworkFault: no penalisation (bad request, or network
127
+ * trouble that is not the key's fault)
91
128
  */
92
129
  export declare function markKeyFailed(state: ProxyState, keyLabel: string, errorType: ErrorType): void;
93
130
  /**
@@ -101,7 +138,9 @@ export declare function markKeySucceeded(state: ProxyState, keyLabel: string): v
101
138
  * 401 → KeyFault (invalid/revoked key — immediate disable)
102
139
  * 429 → ServerFault (transient rate-limit — incremental circuit breaker)
103
140
  * 500 / 502 / 503 → KeyFault if body mentions quota/balance, else ServerFault
104
- * 400 / 404 / 422 → RequestFault (bad request — don't penalise the key)
141
+ * 400 / 403 / 404 / 422 → RequestFault (bad request or permission denial —
142
+ * don't penalise the key; 403 MODEL_NOT_IN_PLAN is a plan
143
+ * restriction, found live on commandcode)
105
144
  * everything else → ServerFault
106
145
  */
107
146
  export declare function classifyHttpError(status: number, _body?: string): ErrorType;
@@ -18,10 +18,14 @@ export function createProxyState(keys, options) {
18
18
  keys: keys.map((k) => ({
19
19
  label: k.label,
20
20
  key: k.key,
21
+ provider: k.provider ?? 'opencode-go',
21
22
  enabled: true,
22
23
  consecutiveFailures: 0,
23
24
  lastUsed: null,
24
25
  disabledAt: null,
26
+ usagePercent: null,
27
+ entitled: true,
28
+ currentWeight: 0,
25
29
  })),
26
30
  currentIndex: 0,
27
31
  circuitBreakerThreshold: options?.circuitBreakerThreshold ?? 3,
@@ -37,7 +41,7 @@ export function createProxyState(keys, options) {
37
41
  * (Keeps mutation paths through markKeyFailed / markKeySucceeded.)
38
42
  */
39
43
  function toSnapshot(key) {
40
- return { label: key.label, key: key.key };
44
+ return { label: key.label, key: key.key, provider: key.provider };
41
45
  }
42
46
  /**
43
47
  * Internal: check whether a key is available (enabled & under threshold).
@@ -45,6 +49,11 @@ function toSnapshot(key) {
45
49
  * (C1 — fixes dead-code cooldown).
46
50
  */
47
51
  function isKeyAvailable(state, key) {
52
+ // Entitlement is definitive, not transient: a key whose account has no
53
+ // plan for this provider must not be retried until the usage refresh
54
+ // clears the flag.
55
+ if (!key.entitled)
56
+ return false;
48
57
  if (key.enabled && key.consecutiveFailures < state.circuitBreakerThreshold) {
49
58
  return true;
50
59
  }
@@ -60,86 +69,6 @@ function isKeyAvailable(state, key) {
60
69
  }
61
70
  return false;
62
71
  }
63
- /**
64
- * Check if a key is available based on usage data.
65
- * A key is unavailable if ANY of its usage windows >= threshold.
66
- *
67
- * @param usage - The usage data for this key's account (null = no data = available)
68
- * @param threshold - The usage threshold (1-100)
69
- * @returns true if the key is available (usage below threshold or no data)
70
- */
71
- function isKeyAvailableByUsage(usage, threshold) {
72
- if (usage === null)
73
- return true; // no data = don't block
74
- return ((usage.rolling === null || usage.rolling < threshold) &&
75
- (usage.weekly === null || usage.weekly < threshold) &&
76
- (usage.monthly === null || usage.monthly < threshold));
77
- }
78
- /**
79
- * Internal: when all circuit-breaker-available keys are over the usage
80
- * threshold, fall back to the key with the lowest maximum usage across
81
- * all windows. If a key has no usage data, it is preferred immediately.
82
- */
83
- function findFallbackKey(state, usageMap) {
84
- let best = null;
85
- let bestMaxUsage = Infinity;
86
- for (const key of state.keys) {
87
- if (!isKeyAvailable(state, key))
88
- continue;
89
- const usage = usageMap.get(key.label);
90
- if (!usage) {
91
- // No usage data = best possible – take it immediately
92
- key.lastUsed = Date.now();
93
- return toSnapshot(key);
94
- }
95
- const maxUsage = Math.max(usage.rolling ?? 0, usage.weekly ?? 0, usage.monthly ?? 0);
96
- if (maxUsage < bestMaxUsage) {
97
- bestMaxUsage = maxUsage;
98
- best = key;
99
- }
100
- }
101
- if (best) {
102
- best.lastUsed = Date.now();
103
- return toSnapshot(best);
104
- }
105
- return null;
106
- }
107
- /**
108
- * Internal: apply usage-gating to an exclusion set, then attempt to find
109
- * the next available key. Falls back to findFallbackKey if all
110
- * circuit-breaker-available keys are over the usage threshold.
111
- *
112
- * Shared by selectKeyForRequest and failoverRequest — extracts the ~90%
113
- * identical usage-gating logic that was previously copy-pasted (PR-5).
114
- *
115
- * @param excludeLabels Mutable set; usage-over-threshold labels are added.
116
- * @returns A key snapshot, or null if no key is available.
117
- */
118
- function selectKeyWithUsageFallback(state, excludeLabels, options) {
119
- // Exclude keys whose account usage exceeds the threshold
120
- if (options?.usageMap && options?.usageThreshold !== undefined) {
121
- const threshold = options.usageThreshold;
122
- for (const key of state.keys) {
123
- const usage = options.usageMap.get(key.label) ?? null;
124
- if (!isKeyAvailableByUsage(usage, threshold)) {
125
- excludeLabels.add(key.label);
126
- }
127
- }
128
- }
129
- let snapshot = findNextKey(state, excludeLabels);
130
- // Fallback: all circuit-breaker-available keys are over threshold
131
- if (!snapshot && options?.usageMap) {
132
- const fallback = findFallbackKey(state, options.usageMap);
133
- if (fallback) {
134
- const idx = state.keys.findIndex((k) => k.label === fallback.label);
135
- if (idx !== -1) {
136
- state.currentIndex = (idx + 1) % state.keys.length;
137
- }
138
- snapshot = fallback;
139
- }
140
- }
141
- return snapshot;
142
- }
143
72
  /**
144
73
  * Internal: return the set of key labels that are currently *in use* by
145
74
  * active requests (C4 double-booking avoidance).
@@ -163,36 +92,139 @@ function buildBookedLabels(state) {
163
92
  * Internal: find the next available key, skipping:
164
93
  * - disabled / under-cooldown keys
165
94
  * - labels in `excludeLabels`
166
- * Advances the round-robin index and returns a read-only snapshot.
95
+ *
96
+ * Selection strategy (smooth weighted round-robin, nginx-style):
97
+ * - When usage data is known for the candidates, keys with more remaining
98
+ * quota are proportionally preferred. E.g. key X at 30% used and key Y at
99
+ * 70% used receive ~70%/30% of requests — exact proportions over a cycle,
100
+ * not random sampling.
101
+ * - Without usage data (or when every candidate is exhausted), falls back to
102
+ * plain round-robin ordering.
167
103
  */
168
- function findNextKey(state, excludeLabels) {
104
+ function findNextKey(state, excludeLabels, providerFilter) {
169
105
  const n = state.keys.length;
170
106
  if (n === 0)
171
107
  return null;
108
+ const candidates = [];
172
109
  for (let i = 0; i < n; i++) {
173
- const idx = (state.currentIndex + i) % n;
174
- const key = state.keys[idx];
110
+ const key = state.keys[(state.currentIndex + i) % n];
175
111
  if (excludeLabels.has(key.label))
176
112
  continue;
113
+ if (providerFilter && !providerFilter(key.provider))
114
+ continue;
177
115
  if (!isKeyAvailable(state, key))
178
116
  continue;
179
- // Advance past the selected key
180
- state.currentIndex = (idx + 1) % n;
181
- key.lastUsed = Date.now();
182
- return toSnapshot(key);
117
+ candidates.push(key);
118
+ }
119
+ if (candidates.length === 0)
120
+ return null;
121
+ const picked = pickWeighted(state, candidates);
122
+ state.currentIndex = (state.keys.indexOf(picked) + 1) % n;
123
+ picked.lastUsed = Date.now();
124
+ return toSnapshot(picked);
125
+ }
126
+ /**
127
+ * Internal: smooth weighted round-robin over the candidates.
128
+ * Falls back to the first candidate (round-robin order) when usage data is
129
+ * missing for any candidate or when no candidate has remaining capacity.
130
+ */
131
+ function pickWeighted(state, candidates) {
132
+ const known = candidates.filter((k) => k.usagePercent !== null);
133
+ if (known.length === 0)
134
+ return candidates[0];
135
+ let weights = candidates.map((k) => Math.max(0, 100 - (k.usagePercent ?? 0)));
136
+ // Keys missing usage data get the median of the known weights — neutral,
137
+ // so one flaky key no longer disables weighting for the whole pool.
138
+ const sorted = known.map((k) => 100 - (k.usagePercent ?? 0)).sort((a, b) => a - b);
139
+ const median = sorted[Math.floor(sorted.length / 2)] ?? 0;
140
+ weights = weights.map((w, i) => (candidates[i].usagePercent === null ? median : w));
141
+ const totalWeight = weights.reduce((sum, w) => sum + w, 0);
142
+ if (totalWeight === 0)
143
+ return candidates[0];
144
+ let best = candidates[0];
145
+ let bestWeight = -Infinity;
146
+ for (let i = 0; i < candidates.length; i++) {
147
+ candidates[i].currentWeight += weights[i];
148
+ if (candidates[i].currentWeight > bestWeight) {
149
+ bestWeight = candidates[i].currentWeight;
150
+ best = candidates[i];
151
+ }
152
+ }
153
+ best.currentWeight -= totalWeight;
154
+ return best;
155
+ }
156
+ /**
157
+ * Update per-key usage data (worst-window used percent, 0-100).
158
+ * Keys absent from the map keep their previous value.
159
+ */
160
+ export function updateKeyUsage(state, usage) {
161
+ for (const key of state.keys) {
162
+ const percent = usage.get(key.label);
163
+ if (typeof percent === 'number')
164
+ key.usagePercent = percent;
183
165
  }
184
- return null;
166
+ }
167
+ /**
168
+ * Mark a key as (not) entitled to use its provider. Unentitled keys are
169
+ * skipped by selection without touching the circuit breaker; the usage
170
+ * refresh flips the flag back as soon as the account can serve requests
171
+ * again.
172
+ */
173
+ export function setKeyEntitlement(state, keyLabel, entitled) {
174
+ const key = state.keys.find((k) => k.label === keyLabel);
175
+ if (key)
176
+ key.entitled = entitled;
185
177
  }
186
178
  // ---------------------------------------------------------------------------
187
179
  // Request-Scoped Key Tracking (C4)
188
180
  // ---------------------------------------------------------------------------
181
+ /** Parse a trailing '@provider' suffix from a model id ('x@cc' → 'cc'). */
182
+ function parseModelSuffix(modelId) {
183
+ if (!modelId)
184
+ return null;
185
+ const at = modelId.lastIndexOf('@');
186
+ if (at <= 0 || at === modelId.length - 1)
187
+ return null;
188
+ return modelId.slice(at + 1);
189
+ }
190
+ /**
191
+ * Resolve the provider-preference predicate for a request.
192
+ *
193
+ * Rule: exclude providers whose verdict is definitively 'no' for the model
194
+ * (e.g. claude-* on opencode-go). Everything else — 'yes' AND 'maybe' —
195
+ * serves the request, so genuinely shared models rotate across ALL their
196
+ * keys regardless of which catalog loaded first. When no provider says 'no'
197
+ * the filter is undefined (legacy any-key behavior).
198
+ */
199
+ function makeProviderFilter(state, modelId) {
200
+ if (!modelId)
201
+ return undefined;
202
+ const resolver = state.affinityResolver;
203
+ if (!resolver)
204
+ return undefined;
205
+ const excluded = new Set();
206
+ for (const key of state.keys) {
207
+ if (excluded.has(key.provider))
208
+ continue;
209
+ if (resolver(key.provider, modelId) === 'no') {
210
+ excluded.add(key.provider);
211
+ }
212
+ }
213
+ if (excluded.size === 0)
214
+ return undefined;
215
+ return (provider) => !excluded.has(provider);
216
+ }
189
217
  /**
190
218
  * Obtain a key for a new (or in-flight) request.
191
219
  * Creates a RequestContext if one does not already exist for this requestId.
192
220
  * Skips keys that have already been tried for this request AND keys booked
193
221
  * by any other active request.
222
+ *
223
+ * When a modelId is supplied and some providers claim it (modelAffinity
224
+ * 'yes'), keys of other providers are only used after the preferred pool is
225
+ * exhausted (including its booked-share tier).
194
226
  */
195
- export function selectKeyForRequest(state, requestId, options) {
227
+ export function selectKeyForRequest(state, requestId, modelId) {
196
228
  // If context already exists, return the already-assigned current key
197
229
  const existing = state.activeRequests.get(requestId);
198
230
  if (existing) {
@@ -201,18 +233,27 @@ export function selectKeyForRequest(state, requestId, options) {
201
233
  // Create new context and pick a fresh key
202
234
  const ctx = { requestId, triedKeys: [], currentKey: null };
203
235
  state.activeRequests.set(requestId, ctx);
236
+ const providerFilter = makeProviderFilter(state, modelId);
237
+ ctx.providerFilter = providerFilter;
238
+ ctx.pinned = Boolean(parseModelSuffix(modelId));
204
239
  // Tier 1: prefer a key not booked by another active request (spread load)
205
240
  const excludeLabels = new Set(ctx.triedKeys);
206
241
  const booked = buildBookedLabels(state);
207
242
  for (const label of booked) {
208
243
  excludeLabels.add(label);
209
244
  }
210
- let snapshot = selectKeyWithUsageFallback(state, excludeLabels, options);
211
- // Tier 2: all healthy keys are booked — share one via round-robin.
212
- // Better to share a key than return 503 when concurrent requests
213
- // outnumber the key pool.
245
+ let snapshot = findNextKey(state, excludeLabels, providerFilter);
246
+ // Tier 2: all healthy preferred keys are booked — share one via
247
+ // round-robin. Better to share a key than return 503 when concurrent
248
+ // requests outnumber the key pool.
214
249
  if (!snapshot) {
215
- snapshot = selectKeyWithUsageFallback(state, new Set(ctx.triedKeys), options);
250
+ snapshot = findNextKey(state, new Set(ctx.triedKeys), providerFilter);
251
+ }
252
+ // Tier 3: preferred providers exhausted → fall back to any provider
253
+ // (pinned requests exempt: a @provider suffix or messages-only model
254
+ // cannot be served by other providers at all).
255
+ if (!snapshot && providerFilter && !ctx.pinned) {
256
+ snapshot = findNextKey(state, new Set(ctx.triedKeys));
216
257
  }
217
258
  ctx.currentKey = snapshot;
218
259
  return snapshot;
@@ -223,7 +264,7 @@ export function selectKeyForRequest(state, requestId, options) {
223
264
  * appropriate ErrorType; this function only manages the request-context
224
265
  * tracking (adds the current key to triedKeys).
225
266
  */
226
- export function failoverRequest(state, requestId, options) {
267
+ export function failoverRequest(state, requestId) {
227
268
  const ctx = state.activeRequests.get(requestId);
228
269
  if (!ctx)
229
270
  return null;
@@ -231,18 +272,25 @@ export function failoverRequest(state, requestId, options) {
231
272
  if (ctx.currentKey) {
232
273
  ctx.triedKeys.push(ctx.currentKey.label);
233
274
  }
234
- // Tier 1: prefer a key not tried AND not booked by another request
275
+ // The provider preference was fixed when the request context was created;
276
+ // failover re-applies it so cross-provider failover can't pick a key that
277
+ // cannot serve the (possibly rewritten) model id.
278
+ const filter = ctx.providerFilter;
235
279
  const excludeLabels = new Set(ctx.triedKeys);
236
280
  const booked = buildBookedLabels(state);
237
281
  for (const label of booked) {
238
282
  excludeLabels.add(label);
239
283
  }
240
- let snapshot = selectKeyWithUsageFallback(state, excludeLabels, options);
241
- // Tier 2: all remaining healthy keys are booked — share one.
284
+ let snapshot = findNextKey(state, excludeLabels, filter);
285
+ // Tier 2: all remaining healthy preferred keys are booked — share one.
242
286
  // Still respects per-request triedKeys (don't re-use a key this
243
287
  // request already failed on).
244
288
  if (!snapshot) {
245
- snapshot = selectKeyWithUsageFallback(state, new Set(ctx.triedKeys), options);
289
+ snapshot = findNextKey(state, new Set(ctx.triedKeys), filter);
290
+ }
291
+ // Tier 3: exhausted → any provider, unless the model is provider-pinned.
292
+ if (!snapshot && !ctx.pinned) {
293
+ snapshot = findNextKey(state, new Set(ctx.triedKeys));
246
294
  }
247
295
  ctx.currentKey = snapshot;
248
296
  return snapshot;
@@ -266,10 +314,11 @@ export function completeRequest(state, requestId, success) {
266
314
  /**
267
315
  * Mark a key as failed according to the error type (C2).
268
316
  * - KeyFault / ServerFault: penalise the key (KeyFault disables immediately)
269
- * - RequestFault: no penalisation (bad request, not bad key)
317
+ * - RequestFault / NetworkFault: no penalisation (bad request, or network
318
+ * trouble that is not the key's fault)
270
319
  */
271
320
  export function markKeyFailed(state, keyLabel, errorType) {
272
- if (errorType === 'RequestFault')
321
+ if (errorType === 'RequestFault' || errorType === 'NetworkFault')
273
322
  return;
274
323
  const key = state.keys.find((k) => k.label === keyLabel);
275
324
  if (!key)
@@ -313,15 +362,23 @@ export function markKeySucceeded(state, keyLabel) {
313
362
  * 401 → KeyFault (invalid/revoked key — immediate disable)
314
363
  * 429 → ServerFault (transient rate-limit — incremental circuit breaker)
315
364
  * 500 / 502 / 503 → KeyFault if body mentions quota/balance, else ServerFault
316
- * 400 / 404 / 422 → RequestFault (bad request — don't penalise the key)
365
+ * 400 / 403 / 404 / 422 → RequestFault (bad request or permission denial —
366
+ * don't penalise the key; 403 MODEL_NOT_IN_PLAN is a plan
367
+ * restriction, found live on commandcode)
317
368
  * everything else → ServerFault
318
369
  */
319
370
  export function classifyHttpError(status, _body) {
320
- if (status === 401)
371
+ if (status === 401) {
372
+ // opencode-go returns 401 ModelError for UNKNOWN MODELS (verified live:
373
+ // a Model gpt-5.5 is not supported, with a valid key). That is a request
374
+ // fault — penalising it would disable every key as the pool rotates.
375
+ if (_body && /ModelError|not supported/i.test(_body))
376
+ return 'RequestFault';
321
377
  return 'KeyFault';
378
+ }
322
379
  if (status === 429)
323
380
  return 'ServerFault';
324
- if (status === 400 || status === 404 || status === 422)
381
+ if (status === 400 || status === 403 || status === 404 || status === 422)
325
382
  return 'RequestFault';
326
383
  if (status === 500 || status === 502 || status === 503) {
327
384
  // Server errors: if the body hints at a quota/balance issue treat as KeyFault
@@ -1 +1 @@
1
- {"version":3,"file":"proxy-logic.js","sourceRoot":"","sources":["../src/proxy-logic.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"proxy-logic.js","sourceRoot":"","sources":["../src/proxy-logic.ts"],"names":[],"mappings":"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"}
package/dist/proxy.d.ts CHANGED
@@ -6,6 +6,44 @@
6
6
  */
7
7
  import { Hono } from 'hono';
8
8
  import type { ProxyConfig } from './config.js';
9
+ import type { KeyProvider } from './providers/index.js';
10
+ /**
11
+ * Resolve the upstream URL for one attempt: per-provider base URL + path
12
+ * remapping. Clients always speak canonical saros routes (/zen/go/v1/<rest>,
13
+ * the opencode-go shape); requests routed to a provider with a different
14
+ * chat surface get the prefix remapped (e.g. /provider/v1 for commandcode).
15
+ */
16
+ export declare function resolveUpstreamUrl(config: ProxyConfig, provider: KeyProvider, path: string): string;
17
+ /**
18
+ * Extract a provider-suffix routing hint from a model id
19
+ * (e.g. 'claude-…@commandcode' → 'commandcode'). Returns null when absent.
20
+ */
21
+ export declare function parseProviderSuffix(modelId: string | undefined): string | null;
22
+ /** Strip the @provider routing suffix from a model id for upstream forwarding. */
23
+ export declare function stripProviderSuffix(modelId: string): string;
24
+ /** Outbound body plus the session id saros must add when the client sent none. */
25
+ export interface OutboundRequest {
26
+ bodyText: string;
27
+ /** Value for `provider.sessionHeader`; absent when the client supplied one. */
28
+ sessionId?: string;
29
+ }
30
+ /**
31
+ * Prepare the outbound body for one attempt in a single JSON pass:
32
+ * - rewrite the model id for the serving provider (see targetModelId);
33
+ * - ask for streamed token usage (`stream_options.include_usage`) on chat
34
+ * completions, unless the client set the option itself — an explicit
35
+ * `false` is respected, otherwise the upstream may withhold the final
36
+ * usage chunk and per-key token accounting goes blind;
37
+ * - derive a conversation session id when the provider needs one and the
38
+ * client didn't send it (see conversationSessionId).
39
+ *
40
+ * The original text is returned untouched when nothing changes (or the body
41
+ * isn't a JSON object), so most requests pay no re-serialization.
42
+ */
43
+ export declare function prepareOutboundRequest(bodyText: string, modelId: string | undefined, provider: KeyProvider, options: {
44
+ wantsStreamUsage: boolean;
45
+ clientSessionId?: string;
46
+ }): OutboundRequest;
9
47
  declare module 'hono' {
10
48
  interface ContextVariableMap {
11
49
  remote: {
@@ -18,8 +56,11 @@ declare module 'hono' {
18
56
  */
19
57
  export declare function buildUpstreamUrl(base: string, path: string): string;
20
58
  /**
21
- * Clean headers for upstream forwarding: remove hop-by-hop headers
22
- * and any pre-existing Authorization.
59
+ * Clean headers for upstream forwarding: remove hop-by-hop headers,
60
+ * pre-existing Authorization, and content-length (the forwarded body may be
61
+ * rewritten — model-id aliasing / suffix strip — so a stale content-length
62
+ * makes the upstream wait for bytes that never arrive; undici recomputes it
63
+ * from the real body).
23
64
  */
24
65
  export declare function buildUpstreamHeaders(incoming: Headers, bearerToken: string): Headers;
25
66
  /**