@askalf/dario 4.8.45 → 4.8.47

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.
package/dist/analytics.js CHANGED
@@ -40,6 +40,9 @@ export function billingBucketFromClaim(claim) {
40
40
  // Anthropic pricing (per 1M tokens, USD). Not authoritative — used for
41
41
  // rough burn-rate display in the /analytics summary.
42
42
  const PRICING = {
43
+ // Fable 5 official per-token pricing wasn't published at integration time
44
+ // (2026-06-09) — assumed at the current flagship (opus-4-8) rate. Display-only.
45
+ 'claude-fable-5': { input: 5, output: 25, cacheRead: 0.5, cacheCreate: 6.25 },
43
46
  'claude-opus-4-8': { input: 5, output: 25, cacheRead: 0.5, cacheCreate: 6.25 },
44
47
  'claude-opus-4-7': { input: 5, output: 25, cacheRead: 0.5, cacheCreate: 6.25 },
45
48
  'claude-opus-4-6': { input: 15, output: 75, cacheRead: 1.5, cacheCreate: 18.75 },
@@ -47,7 +50,11 @@ const PRICING = {
47
50
  'claude-haiku-4-5': { input: 0.8, output: 4, cacheRead: 0.08, cacheCreate: 1 },
48
51
  };
49
52
  function estimateCost(record) {
50
- const p = PRICING[record.model] ?? PRICING['claude-sonnet-4-6'];
53
+ // Strip a trailing context tag (`claude-fable-5[1m]`, `claude-opus-4-7[1m]`)
54
+ // before the lookup — the [1m] ids billed at the wrong family's rate before
55
+ // (fell through to the sonnet fallback).
56
+ const baseModel = record.model.replace(/\[[^\]]*\]$/, '');
57
+ const p = PRICING[baseModel] ?? PRICING['claude-sonnet-4-6'];
51
58
  return ((record.inputTokens * p.input) +
52
59
  (record.outputTokens * p.output) +
53
60
  (record.cacheReadTokens * p.cacheRead) +
@@ -291,14 +291,20 @@ 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.
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
298
304
  *
299
305
  * Exported for tests.
300
306
  */
301
- export declare function resolveEffort(flag: EffortValue | undefined, clientBody: Record<string, unknown>): string;
307
+ export declare function resolveEffort(flag: EffortValue | undefined, clientBody: Record<string, unknown>, model?: string): string;
302
308
  /**
303
309
  * Returns true if the given model accepts `thinking: { type: "adaptive" }`.
304
310
  *
@@ -308,6 +314,9 @@ export declare function resolveEffort(flag: EffortValue | undefined, clientBody:
308
314
  * claude-opus-4-7 ✓ accepts adaptive
309
315
  * claude-opus-4-6 ✓ accepts adaptive
310
316
  * claude-sonnet-4-6 ✓ accepts adaptive
317
+ * claude-fable-5 ✓ accepts adaptive (2026-06-09 — Fable 5 is CC's new
318
+ * flagship and real CC sends `thinking:{type:"adaptive"}`
319
+ * on it, incl. the `claude-fable-5[1m]` long-context id)
311
320
  * claude-opus-4-5 ✗ "adaptive thinking is not supported on this model"
312
321
  * claude-sonnet-4-5 ✗ same
313
322
  * claude-haiku-4-5 ✗ same (already gated separately by isHaiku)
@@ -985,22 +985,29 @@ 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.
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
992
998
  *
993
999
  * Exported for tests.
994
1000
  */
995
- export function resolveEffort(flag, clientBody) {
1001
+ export function resolveEffort(flag, clientBody, model) {
1002
+ const familyDefault = (model ?? '').toLowerCase().includes('fable') ? 'high' : 'max';
996
1003
  if (flag === undefined)
997
- return 'max';
1004
+ return familyDefault;
998
1005
  if (flag === 'client') {
999
1006
  const clientOC = clientBody.output_config;
1000
1007
  const clientEffort = clientOC?.effort;
1001
1008
  if (typeof clientEffort === 'string' && clientEffort.length > 0)
1002
1009
  return normalizeEffortForWire(clientEffort);
1003
- return 'max';
1010
+ return familyDefault;
1004
1011
  }
1005
1012
  return normalizeEffortForWire(flag);
1006
1013
  }
@@ -1013,6 +1020,9 @@ export function resolveEffort(flag, clientBody) {
1013
1020
  * claude-opus-4-7 ✓ accepts adaptive
1014
1021
  * claude-opus-4-6 ✓ accepts adaptive
1015
1022
  * claude-sonnet-4-6 ✓ accepts adaptive
1023
+ * claude-fable-5 ✓ accepts adaptive (2026-06-09 — Fable 5 is CC's new
1024
+ * flagship and real CC sends `thinking:{type:"adaptive"}`
1025
+ * on it, incl. the `claude-fable-5[1m]` long-context id)
1016
1026
  * claude-opus-4-5 ✗ "adaptive thinking is not supported on this model"
1017
1027
  * claude-sonnet-4-5 ✗ same
1018
1028
  * claude-haiku-4-5 ✗ same (already gated separately by isHaiku)
@@ -1029,26 +1039,30 @@ export function resolveEffort(flag, clientBody) {
1029
1039
  */
1030
1040
  export function supportsAdaptiveThinking(modelId) {
1031
1041
  const m = modelId.toLowerCase();
1032
- // Opus/Sonnet, major-minor form: opus-4-6+, sonnet-4-6+, opus-5-X, etc.
1042
+ // Opus/Sonnet/Fable, major-minor form: opus-4-6+, sonnet-4-6+, opus-5-X,
1043
+ // fable-5-X, etc. (Fable launched at 5 — there is no fable-4 line — so the
1044
+ // shared "4-6+" threshold is correct for it by construction.)
1033
1045
  //
1034
1046
  // Digit groups are bounded to {1,2} so the dated-suffix pre-4.x line
1035
1047
  // (`claude-3-5-sonnet-20241022`, `claude-3-7-sonnet-20250219`) doesn't
1036
1048
  // accidentally match the date as `sonnet-2024-1022` and parse year as
1037
1049
  // major. Realistic Anthropic version numbers are 1-2 digits.
1038
- const mm = m.match(/(?:opus|sonnet)-(\d{1,2})-(\d{1,2})\b/);
1050
+ const mm = m.match(/(?:opus|sonnet|fable)-(\d{1,2})-(\d{1,2})\b/);
1039
1051
  if (mm) {
1040
1052
  const major = Number(mm[1]);
1041
1053
  const minor = Number(mm[2]);
1042
1054
  if (major > 4)
1043
- return true; // any opus-5+ / sonnet-5+
1055
+ return true; // any opus-5+ / sonnet-5+ / fable-5+
1044
1056
  if (major === 4 && minor >= 6)
1045
1057
  return true; // 4-6, 4-7, …
1046
1058
  return false; // 4-5 and older
1047
1059
  }
1048
- // Major-only form (e.g. `opus-5`, `opus-10`). The negative lookahead
1049
- // prevents matching the `5` in `opus-5-X` (handled above), and the
1050
- // {1,2} bound prevents matching long dated suffixes.
1051
- const majorOnly = m.match(/(?:opus|sonnet)-(\d{1,2})(?!\d|-)/);
1060
+ // Major-only form (e.g. `opus-5`, `fable-5`, `opus-10`). The negative
1061
+ // lookahead prevents matching the `5` in `opus-5-X` (handled above), and
1062
+ // the {1,2} bound prevents matching long dated suffixes. A trailing
1063
+ // context tag (`claude-fable-5[1m]`) is fine: `[` is neither digit nor
1064
+ // hyphen, so the lookahead passes.
1065
+ const majorOnly = m.match(/(?:opus|sonnet|fable)-(\d{1,2})(?!\d|-)/);
1052
1066
  if (majorOnly && Number(majorOnly[1]) >= 5)
1053
1067
  return true;
1054
1068
  return false;
@@ -1470,7 +1484,7 @@ export function buildCCRequest(clientBody, billingTag, cacheControl, identity, o
1470
1484
  }
1471
1485
  }
1472
1486
  if (!skip || !skip.has('output_config')) {
1473
- ccRequest.output_config = { effort: resolveEffort(opts.effort, clientBody) };
1487
+ ccRequest.output_config = { effort: resolveEffort(opts.effort, clientBody, model) };
1474
1488
  }
1475
1489
  }
1476
1490
  ccRequest.stream = stream;
package/dist/cli.js CHANGED
@@ -1159,8 +1159,9 @@ async function help() {
1159
1159
 
1160
1160
  Proxy options:
1161
1161
  --model=MODEL Force a model for all requests
1162
- Shortcuts: opus, sonnet, haiku
1163
- Full IDs: claude-opus-4-6, claude-sonnet-4-6
1162
+ Shortcuts: fable, fable1m, opus, sonnet, haiku
1163
+ Full IDs: claude-fable-5, claude-opus-4-8,
1164
+ claude-sonnet-4-6 (append [1m] for 1M context)
1164
1165
  Provider prefix: openai:gpt-4o, groq:llama-3.3-70b,
1165
1166
  claude:opus, local:qwen-coder (forces backend)
1166
1167
  Default: passthrough (client decides)
@@ -1450,7 +1451,7 @@ async function help() {
1450
1451
  curl http://localhost:3456/v1/messages \\
1451
1452
  -H "Content-Type: application/json" \\
1452
1453
  -H "anthropic-version: 2023-06-01" \\
1453
- -d '{"model":"claude-opus-4-6","max_tokens":1024,"messages":[{"role":"user","content":"Hello"}]}'
1454
+ -d '{"model":"claude-fable-5","max_tokens":1024,"messages":[{"role":"user","content":"Hello"}]}'
1454
1455
 
1455
1456
  Your subscription handles the billing. No API key needed.
1456
1457
  Tokens auto-refresh in the background — set it and forget it.
package/dist/doctor.js CHANGED
@@ -479,6 +479,7 @@ export async function runChecks(opts = {}) {
479
479
  { family: 'haiku', model: 'claude-haiku-4-5' },
480
480
  { family: 'sonnet', model: 'claude-sonnet-4-6' },
481
481
  { family: 'opus', model: 'claude-opus-4-8' },
482
+ { family: 'fable', model: 'claude-fable-5' },
482
483
  ];
483
484
  const probe = async (model) => {
484
485
  const res = await fetch(probeEndpoint, {
package/dist/pool.d.ts CHANGED
@@ -78,15 +78,18 @@ export interface PoolStatus {
78
78
  /** Parse an Anthropic response's rate-limit headers into a snapshot. */
79
79
  export declare function parseRateLimits(headers: Headers): RateLimitSnapshot;
80
80
  /**
81
- * Extract the model family (`opus` / `sonnet` / `haiku`) from a request's
82
- * model id. Used to look up the per-model 7d bucket in
81
+ * Extract the model family (`opus` / `sonnet` / `haiku` / `fable`) from a
82
+ * request's model id. Used to look up the per-model 7d bucket in
83
83
  * `RateLimitSnapshot.perModel7d` during routing decisions. Returns null
84
84
  * for non-Claude models or model ids that don't carry a recognizable
85
85
  * family token (those requests just use the unified buckets).
86
86
  *
87
87
  * Generous on input shape: matches `claude-opus-4-7`, `opus`, `claude-3-7-sonnet-…`,
88
- * `claude-haiku-4-5`, anything containing the family token. Lowercase-normalized
89
- * so it pairs cleanly with `parseRateLimits`'s lowercase family keys.
88
+ * `claude-haiku-4-5`, `claude-fable-5[1m]`, anything containing the family token.
89
+ * Lowercase-normalized so it pairs cleanly with `parseRateLimits`'s lowercase
90
+ * family keys (the header parser is generic on `7d_<family>`, so a `7d_fable`
91
+ * bucket is captured automatically the moment Anthropic starts emitting it —
92
+ * this function is what lets routing USE it).
90
93
  */
91
94
  export declare function modelFamily(modelId: string | null | undefined): string | null;
92
95
  /**
package/dist/pool.js CHANGED
@@ -94,15 +94,18 @@ export function parseRateLimits(headers) {
94
94
  };
95
95
  }
96
96
  /**
97
- * Extract the model family (`opus` / `sonnet` / `haiku`) from a request's
98
- * model id. Used to look up the per-model 7d bucket in
97
+ * Extract the model family (`opus` / `sonnet` / `haiku` / `fable`) from a
98
+ * request's model id. Used to look up the per-model 7d bucket in
99
99
  * `RateLimitSnapshot.perModel7d` during routing decisions. Returns null
100
100
  * for non-Claude models or model ids that don't carry a recognizable
101
101
  * family token (those requests just use the unified buckets).
102
102
  *
103
103
  * Generous on input shape: matches `claude-opus-4-7`, `opus`, `claude-3-7-sonnet-…`,
104
- * `claude-haiku-4-5`, anything containing the family token. Lowercase-normalized
105
- * so it pairs cleanly with `parseRateLimits`'s lowercase family keys.
104
+ * `claude-haiku-4-5`, `claude-fable-5[1m]`, anything containing the family token.
105
+ * Lowercase-normalized so it pairs cleanly with `parseRateLimits`'s lowercase
106
+ * family keys (the header parser is generic on `7d_<family>`, so a `7d_fable`
107
+ * bucket is captured automatically the moment Anthropic starts emitting it —
108
+ * this function is what lets routing USE it).
106
109
  */
107
110
  export function modelFamily(modelId) {
108
111
  if (!modelId)
@@ -114,6 +117,8 @@ export function modelFamily(modelId) {
114
117
  return 'sonnet';
115
118
  if (m.includes('haiku'))
116
119
  return 'haiku';
120
+ if (m.includes('fable'))
121
+ return 'fable';
117
122
  return null;
118
123
  }
119
124
  /**
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
@@ -128,6 +128,8 @@ function loadClaudeIdentity() {
128
128
  }
129
129
  // Model shortcuts — users can pass short names
130
130
  const MODEL_ALIASES = {
131
+ 'fable': 'claude-fable-5',
132
+ 'fable1m': 'claude-fable-5[1m]',
131
133
  'opus': 'claude-opus-4-8',
132
134
  'opus47': 'claude-opus-4-7',
133
135
  'opus46': 'claude-opus-4-6',
@@ -188,6 +190,24 @@ const BILLABLE_BETA_PREFIXES = [
188
190
  function filterBillableBetas(betas) {
189
191
  return betas.split(',').map(b => b.trim()).filter(b => b.length > 0 && !BILLABLE_BETA_PREFIXES.some(p => b.startsWith(p))).join(',');
190
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
+ }
191
211
  // Orchestration tags injected by agents (Aider, Cursor, OpenCode, etc.)
192
212
  // that confuse Claude when passed through. Strip before forwarding.
193
213
  export const ORCHESTRATION_TAG_NAMES = [
@@ -363,7 +383,7 @@ function translateStreamChunk(line) {
363
383
  catch { }
364
384
  return null;
365
385
  }
366
- const OPENAI_MODELS_LIST = { object: 'list', data: ['claude-opus-4-8', 'claude-opus-4-7', 'claude-opus-4-6', 'claude-sonnet-4-6', 'claude-haiku-4-5'].map(id => ({ id, object: 'model', created: 1700000000, owned_by: 'anthropic' })) };
386
+ const OPENAI_MODELS_LIST = { object: 'list', data: ['claude-fable-5', 'claude-fable-5[1m]', 'claude-opus-4-8', 'claude-opus-4-7', 'claude-opus-4-6', 'claude-sonnet-4-6', 'claude-haiku-4-5'].map(id => ({ id, object: 'model', created: 1700000000, owned_by: 'anthropic' })) };
367
387
  /**
368
388
  * Append a JSON-ND line to the proxy log file. No-op when stream is
369
389
  * null (logFile not configured). Errors are swallowed — log writes
@@ -1560,7 +1580,9 @@ export async function startProxy(opts = {}) {
1560
1580
  // skip context-1m entirely (dario#36).
1561
1581
  const acctKey = poolAccount?.alias ?? ACCOUNT_KEY_SINGLE;
1562
1582
  const skipContext1m = context1mUnavailable.has(acctKey);
1563
- 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);
1564
1586
  if (clientBeta) {
1565
1587
  const baseSet = new Set(beta.split(','));
1566
1588
  const filtered = filterBillableBetas(clientBeta)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askalf/dario",
3
- "version": "4.8.45",
3
+ "version": "4.8.47",
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": {