@askalf/dario 4.8.46 → 4.8.48

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.
@@ -291,14 +291,31 @@ export declare function parseEffortSuffix(model: string): {
291
291
  * accepted by all and still routes to the subscription pool
292
292
  * (verified: representative-claim=five_hour on Opus + Sonnet).
293
293
  * Set --effort=xhigh / DARIO_EFFORT=xhigh for Opus's extra tier.)
294
+ * EXCEPT fable: real CC's print-mode default for fable is
295
+ * 'high' (live capture 2026-06-09, CC v2.1.170 — the same
296
+ * capture got 'xhigh' on opus and 'high' on fable), so the
297
+ * unset-flag default mirrors that per-family. An explicit
298
+ * flag still pins (subject to the fable clamp below).
294
299
  * 'low' / 'medium' / 'high' / 'xhigh' / 'max' → pin to that value
295
300
  * 'ultracode' → 'xhigh' (CC's ultracode mode; xhigh on the wire)
296
301
  * 'client' → extract from `clientBody.output_config.effort` (normalized
297
- * for the wire); fall back to 'max' if absent/non-string
302
+ * for the wire); fall back to the per-family default if
303
+ * absent/non-string
304
+ *
305
+ * FABLE CLAMP (2026-06-09, live replay bisect on the deployed proxy):
306
+ * fable-5 SOFT-REFUSES `max` and `xhigh` — 200 with stop_reason "refusal"
307
+ * and empty content on every prompt, not a 400 — while `high` answers
308
+ * normally (byte-identical request bodies, only output_config.effort
309
+ * mutated). Empirical matrix on claude-fable-5:
310
+ * high → answers xhigh → refusal max → refusal
311
+ * So on the fable family any resolved 'max'/'xhigh' (from --effort /
312
+ * DARIO_EFFORT pins, ultracode, or client passthrough) is clamped to
313
+ * 'high', the strongest level fable accepts. Operators who pin
314
+ * --effort=max for Opus's sake keep that on every other family.
298
315
  *
299
316
  * Exported for tests.
300
317
  */
301
- export declare function resolveEffort(flag: EffortValue | undefined, clientBody: Record<string, unknown>): string;
318
+ export declare function resolveEffort(flag: EffortValue | undefined, clientBody: Record<string, unknown>, model?: string): string;
302
319
  /**
303
320
  * Returns true if the given model accepts `thinking: { type: "adaptive" }`.
304
321
  *
@@ -985,24 +985,44 @@ function normalizeEffortForWire(effort) {
985
985
  * accepted by all and still routes to the subscription pool
986
986
  * (verified: representative-claim=five_hour on Opus + Sonnet).
987
987
  * Set --effort=xhigh / DARIO_EFFORT=xhigh for Opus's extra tier.)
988
+ * EXCEPT fable: real CC's print-mode default for fable is
989
+ * 'high' (live capture 2026-06-09, CC v2.1.170 — the same
990
+ * capture got 'xhigh' on opus and 'high' on fable), so the
991
+ * unset-flag default mirrors that per-family. An explicit
992
+ * flag still pins (subject to the fable clamp below).
988
993
  * 'low' / 'medium' / 'high' / 'xhigh' / 'max' → pin to that value
989
994
  * 'ultracode' → 'xhigh' (CC's ultracode mode; xhigh on the wire)
990
995
  * 'client' → extract from `clientBody.output_config.effort` (normalized
991
- * for the wire); fall back to 'max' if absent/non-string
996
+ * for the wire); fall back to the per-family default if
997
+ * absent/non-string
998
+ *
999
+ * FABLE CLAMP (2026-06-09, live replay bisect on the deployed proxy):
1000
+ * fable-5 SOFT-REFUSES `max` and `xhigh` — 200 with stop_reason "refusal"
1001
+ * and empty content on every prompt, not a 400 — while `high` answers
1002
+ * normally (byte-identical request bodies, only output_config.effort
1003
+ * mutated). Empirical matrix on claude-fable-5:
1004
+ * high → answers xhigh → refusal max → refusal
1005
+ * So on the fable family any resolved 'max'/'xhigh' (from --effort /
1006
+ * DARIO_EFFORT pins, ultracode, or client passthrough) is clamped to
1007
+ * 'high', the strongest level fable accepts. Operators who pin
1008
+ * --effort=max for Opus's sake keep that on every other family.
992
1009
  *
993
1010
  * Exported for tests.
994
1011
  */
995
- export function resolveEffort(flag, clientBody) {
1012
+ export function resolveEffort(flag, clientBody, model) {
1013
+ const isFable = (model ?? '').toLowerCase().includes('fable');
1014
+ const familyDefault = isFable ? 'high' : 'max';
1015
+ const clamp = (effort) => isFable && (effort === 'max' || effort === 'xhigh') ? 'high' : effort;
996
1016
  if (flag === undefined)
997
- return 'max';
1017
+ return familyDefault;
998
1018
  if (flag === 'client') {
999
1019
  const clientOC = clientBody.output_config;
1000
1020
  const clientEffort = clientOC?.effort;
1001
1021
  if (typeof clientEffort === 'string' && clientEffort.length > 0)
1002
- return normalizeEffortForWire(clientEffort);
1003
- return 'max';
1022
+ return clamp(normalizeEffortForWire(clientEffort));
1023
+ return familyDefault;
1004
1024
  }
1005
- return normalizeEffortForWire(flag);
1025
+ return clamp(normalizeEffortForWire(flag));
1006
1026
  }
1007
1027
  /**
1008
1028
  * Returns true if the given model accepts `thinking: { type: "adaptive" }`.
@@ -1477,7 +1497,7 @@ export function buildCCRequest(clientBody, billingTag, cacheControl, identity, o
1477
1497
  }
1478
1498
  }
1479
1499
  if (!skip || !skip.has('output_config')) {
1480
- ccRequest.output_config = { effort: resolveEffort(opts.effort, clientBody) };
1500
+ ccRequest.output_config = { effort: resolveEffort(opts.effort, clientBody, model) };
1481
1501
  }
1482
1502
  }
1483
1503
  ccRequest.stream = stream;
package/dist/proxy.d.ts CHANGED
@@ -17,6 +17,18 @@ export declare function parseProviderPrefix(model: string): {
17
17
  provider: 'openai' | 'claude';
18
18
  model: string;
19
19
  } | null;
20
+ /**
21
+ * Fable-5 (2026-06) — real CC appends `fallback-credit-2026-06-01` to the
22
+ * beta set on fable requests ONLY (live captures 2026-06-09, CC v2.1.170:
23
+ * the fable request carries it, the opus request does not). Subscription
24
+ * traffic on fable WITHOUT it is soft-refused upstream — every request
25
+ * returns 200 with `stop_reason: "refusal"` and empty content, while the
26
+ * same request on opus/sonnet answers normally. Mirror CC exactly: append
27
+ * for the fable family, leave every other family's set untouched.
28
+ * Exported for tests.
29
+ */
30
+ export declare const FABLE_FALLBACK_CREDIT_BETA = "fallback-credit-2026-06-01";
31
+ export declare function betaForModel(base: string, model: string | null | undefined): string;
20
32
  export declare const ORCHESTRATION_TAG_NAMES: string[];
21
33
  /**
22
34
  * Build the regex list that actually strips orchestration tags.
package/dist/proxy.js CHANGED
@@ -190,6 +190,24 @@ const BILLABLE_BETA_PREFIXES = [
190
190
  function filterBillableBetas(betas) {
191
191
  return betas.split(',').map(b => b.trim()).filter(b => b.length > 0 && !BILLABLE_BETA_PREFIXES.some(p => b.startsWith(p))).join(',');
192
192
  }
193
+ /**
194
+ * Fable-5 (2026-06) — real CC appends `fallback-credit-2026-06-01` to the
195
+ * beta set on fable requests ONLY (live captures 2026-06-09, CC v2.1.170:
196
+ * the fable request carries it, the opus request does not). Subscription
197
+ * traffic on fable WITHOUT it is soft-refused upstream — every request
198
+ * returns 200 with `stop_reason: "refusal"` and empty content, while the
199
+ * same request on opus/sonnet answers normally. Mirror CC exactly: append
200
+ * for the fable family, leave every other family's set untouched.
201
+ * Exported for tests.
202
+ */
203
+ export const FABLE_FALLBACK_CREDIT_BETA = 'fallback-credit-2026-06-01';
204
+ export function betaForModel(base, model) {
205
+ if (!model || !model.toLowerCase().includes('fable'))
206
+ return base;
207
+ if (base.split(',').includes(FABLE_FALLBACK_CREDIT_BETA))
208
+ return base;
209
+ return base ? `${base},${FABLE_FALLBACK_CREDIT_BETA}` : FABLE_FALLBACK_CREDIT_BETA;
210
+ }
193
211
  // Orchestration tags injected by agents (Aider, Cursor, OpenCode, etc.)
194
212
  // that confuse Claude when passed through. Strip before forwarding.
195
213
  export const ORCHESTRATION_TAG_NAMES = [
@@ -1562,7 +1580,9 @@ export async function startProxy(opts = {}) {
1562
1580
  // skip context-1m entirely (dario#36).
1563
1581
  const acctKey = poolAccount?.alias ?? ACCOUNT_KEY_SINGLE;
1564
1582
  const skipContext1m = context1mUnavailable.has(acctKey);
1565
- beta = skipContext1m ? betaWithoutContext1m : betaBase;
1583
+ // Fable requires its fallback-credit beta or upstream soft-refuses
1584
+ // every request (see betaForModel) — model-conditional, like real CC.
1585
+ beta = betaForModel(skipContext1m ? betaWithoutContext1m : betaBase, requestModel);
1566
1586
  if (clientBeta) {
1567
1587
  const baseSet = new Set(beta.split(','));
1568
1588
  const filtered = filterBillableBetas(clientBeta)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askalf/dario",
3
- "version": "4.8.46",
3
+ "version": "4.8.48",
4
4
  "description": "Use your Claude Pro/Max subscription in any tool — Cursor, Cline, Aider, the Agent SDK, your scripts — at subscription pricing, not per-token API bills. One local Anthropic + OpenAI-compatible endpoint.",
5
5
  "type": "module",
6
6
  "bin": {