@askalf/dario 4.8.70 → 4.8.72

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/README.md CHANGED
@@ -16,7 +16,9 @@
16
16
 
17
17
  ---
18
18
 
19
- > **NewClaude Fable 5 fully supported.** `claude-fable-5`, its `[1m]` long-context variant, and the `fable` / `fable1m` shortcuts route on your subscription like any other model. dario tracked CC's new flagship the day it shipped, including the wire quirks Anthropic never documented: the `fallback-credit` beta fable *requires* (without it subscription fable traffic is silently soft-refused), its refusal of high effort levels, and the **per-model beta tailoring** matched to CC v2.1.170. `npm install -g @askalf/dario@latest`
19
+ > 🗓️ **2026-06-15Anthropic splits Claude billing.** Agent-SDK and `claude -p` (headless) traffic stops counting against your subscription pool and moves to a small separate monthly credit ($20 / $100 / $200 by plan), then metered per-token API rates. Most proxies forward your requests in exactly the shape that gets reclassified into that bucket. dario rewrites every request into interactive Claude Code wire-shape before it leaves your machine, so your traffic stays in the subscription pool you already pay for — same install, no config change for the cliff. **[What changes, and how to verify it on your own machine →](#the-deadline-2026-06-15)**
20
+
21
+ > ⛔ **Claude Fable 5 / Mythos 5 — temporarily suspended for all Anthropic customers.** On 2026-06-12 a US-government legal directive disabled Fable 5 and Mythos 5 for **every** Anthropic plan and tier — `api.anthropic.com` now returns `not_found` for them, and no account or proxy can route around it ([details](https://www.anthropic.com/news/fable-mythos-access)). As of **v4.8.71**, dario filters both families out of `/v1/models` and rejects any spelling (`fable`, `fable1m`, `claude-fable-5`, `claude-fable-5[1m]`, `claude:fable`) up front with a clean `404` pointing at `claude-opus-4-8` / `claude-sonnet-4-6` — instead of forwarding into a confusing upstream error. Reversible without a code change once access is restored: set `DARIO_SUSPENDED_MODELS=` (empty) on the instance. `npm install -g @askalf/dario@latest`
20
22
  >
21
23
  > ⚠️ Still on a version **before 4.8.39**? Upgrade now — those could silently corrupt code/structured content routed through the proxy (the identifier scrub stripped tokens like the JS `continue` keyword). **[Details →](https://github.com/askalf/dario/issues/457)**
22
24
 
@@ -73,10 +75,9 @@ Type `dario` with no args (in another terminal) to open a full-screen control pa
73
75
  │ Tokens out: 38,200 Subscription %: 98% │
74
76
  │ │
75
77
  │ Per-model: │
76
- fable-5 ██████████░░░░░░░░░░░ 50% (124 req) │
77
- opus-4-8 ██████░░░░░░░░░░░░░░░ 30% ( 74 req) │
78
- sonnet-4-6 ███░░░░░░░░░░░░░░░░░░ 14% ( 34 req) │
79
- │ haiku-4-5 █░░░░░░░░░░░░░░░░░░░░ 6% ( 15 req) │
78
+ opus-4-8 ████████████░░░░░░░░ 60% (148 req) │
79
+ sonnet-4-6 █████░░░░░░░░░░░░░░░ 26% ( 64 req) │
80
+ haiku-4-5 ███░░░░░░░░░░░░░░░░░ 14% ( 35 req) │
80
81
  │ │
81
82
  │ Rate-limit: │
82
83
  │ 5h ████░░░░░░░░░░░░░░░░░░░░░░░░ 18% │
@@ -86,18 +87,6 @@ Type `dario` with no args (in another terminal) to open a full-screen control pa
86
87
 
87
88
  ---
88
89
 
89
- ## The money
90
-
91
- | Setup | Monthly cost — heavy user |
92
- |---|---|
93
- | Cursor + Anthropic API direct | **$80–$300** |
94
- | Multi-tool heavy use (Cursor + Aider + Cline + Continue), per-token | **$200–$600+** |
95
- | **Any of the above + dario** | **$20–$200 flat** — your existing Pro/Max plan, nothing extra |
96
-
97
- Switching providers is a model-name change, not a reconfigure. Add a backend once and the same `localhost:3456` speaks OpenAI, Groq, OpenRouter, or a local Ollama too.
98
-
99
- ---
100
-
101
90
  ## The deadline: 2026-06-15
102
91
 
103
92
  On **2026-06-15**, Anthropic splits Claude billing in two. Agentic traffic — Agent SDK, `claude -p` headless — stops counting against your subscription pool and gets a separate small monthly credit. [Announced 2026-05-13](https://support.claude.com/en/articles/15036540-use-the-claude-agent-sdk-with-your-claude-plan) via Claude's Help Center and a [@ClaudeDevs X post](https://x.com/ClaudeDevs/status/2054610152817619388) — no anthropic.com blog post, no email to most subscribers, no mention in CC release notes.
@@ -123,6 +112,18 @@ Same install, same `localhost:3456`, no config change for the cliff. Verify on y
123
112
 
124
113
  ---
125
114
 
115
+ ## The money
116
+
117
+ | Setup | Monthly cost — heavy user |
118
+ |---|---|
119
+ | Cursor + Anthropic API direct | **$80–$300** |
120
+ | Multi-tool heavy use (Cursor + Aider + Cline + Continue), per-token | **$200–$600+** |
121
+ | **Any of the above + dario** | **$20–$200 flat** — your existing Pro/Max plan, nothing extra |
122
+
123
+ Switching providers is a model-name change, not a reconfigure. Add a backend once and the same `localhost:3456` speaks OpenAI, Groq, OpenRouter, or a local Ollama too.
124
+
125
+ ---
126
+
126
127
  ## The principle dario operates on
127
128
 
128
129
  Two layers, separated:
@@ -166,6 +167,7 @@ dario doesn't *guess* Claude Code's request shape — it captures it live from y
166
167
  | `TodoWrite`/`TodoRead` replaced by the `Task*` family, no migration note | Clients hardcoding `todo_*` send unrecognized tools | [v3.38.6](https://github.com/askalf/dario/pull/274) — 13 min later |
167
168
  | **Claude Fable 5** ships as CC's flagship (v2.1.170) with undocumented wire quirks: a required `fallback-credit-2026-06-01` beta, soft-refusal of `max`/`xhigh` effort, and a `[1m]`-only `context-1m` beta | Subscription fable traffic 200-refused (empty body) without the flag; wrong effort silently returns nothing | [v4.8.46–52](https://github.com/askalf/dario/pulls?q=fable) — 2026-06-09 |
168
169
  | CC tailors the `anthropic-beta` set **per model** — opus 9, sonnet 8 (no `mid-conversation-system`), haiku 6 (also no `effort`) | Proxies sending one beta set diverge from CC for non-opus models | [v4.8.53](https://github.com/askalf/dario/pull/478) — 2026-06-09 |
170
+ | **Fable 5 / Mythos 5 globally suspended** by a US-government directive (2026-06-12, every customer + tier) — `api.anthropic.com` now `not_found`s them | dario kept advertising `claude-fable-5[1m]` in `/v1/models` and forwarded fable traffic into a confusing upstream 404 | [v4.8.71](https://github.com/askalf/dario/pull/518) — 2026-06-13 (filtered out + clean local 404; reversible via `DARIO_SUSPENDED_MODELS`) |
169
171
 
170
172
  And it gets subtler: v4.2.1 (2026-05-17) shipped receipts for **same CC binary, different wire output 24 hours apart** — Anthropic ships changes through CC's *remote configuration*, not just npm releases. So dario runs **three classes of drift detection**, all auto-detecting and auto-PR'ing:
171
173
 
@@ -184,7 +186,7 @@ You point every tool at one URL. dario reads each request, decides which backend
184
186
 
185
187
  | Client speaks | Model | Routes to | What happens |
186
188
  |---|---|---|---|
187
- | Anthropic Messages | `claude-*` / `fable` / `opus` / `sonnet` / `haiku` | Claude backend | OAuth swap + CC template replay → `api.anthropic.com` |
189
+ | Anthropic Messages | `claude-*` / `opus` / `sonnet` / `haiku` | Claude backend | OAuth swap + CC template replay → `api.anthropic.com` |
188
190
  | Anthropic Messages | `gpt-*`, `llama-*`, … | OpenAI-compat backend | Anthropic→OpenAI translation, forwarded |
189
191
  | OpenAI Chat | `gpt-*` / `o1-*` / `o3-*` | OpenAI-compat backend | Auth swap, body forwarded byte-for-byte |
190
192
  | OpenAI Chat | `claude-*` | Claude backend | OpenAI→Anthropic translation, then Claude path |
@@ -192,7 +194,7 @@ You point every tool at one URL. dario reads each request, decides which backend
192
194
 
193
195
  The tool doesn't know. The backend doesn't know. dario is the seam.
194
196
 
195
- **The full Claude lineup, autodetected.** Claude Fable 5 (CC's flagship), Opus 4.8, Sonnet 4.6, and Haiku 4.5 — plus `[1m]` long-context variants, generated by one rule for every family — by full id (`claude-fable-5`, `claude-opus-4-8`) or shortcut (`fable` / `opus` / `sonnet` / `haiku`, append `1m` for the long-context form). `GET /v1/models` asks Anthropic's live catalog (TTL-cached, baked fallback when offline), and the family shortcuts track it — a new model shows up and resolves the day it lands, no dario release needed; the model-specific wire shape (effort level, beta set, thinking config) is applied automatically.
197
+ **The full Claude lineup, autodetected.** Opus 4.8, Sonnet 4.6, and Haiku 4.5 — plus `[1m]` long-context variants, generated by one rule for every family — by full id (`claude-opus-4-8`) or shortcut (`opus` / `sonnet` / `haiku`, append `1m` for the long-context form). `GET /v1/models` asks Anthropic's live catalog (TTL-cached, baked fallback when offline), and the family shortcuts track it — a new model shows up and resolves the day it lands, no dario release needed; the model-specific wire shape (effort level, beta set, thinking config) is applied automatically. **Globally-suspended families are filtered out** of both the live catalog and the baked fallback, so `/v1/models` never advertises a model that 404s upstream and suspended ids are rejected locally with an actionable error — currently Claude Fable 5 and Mythos 5 (US-government directive, 2026-06-12); governed by `DARIO_SUSPENDED_MODELS` (default `fable`), set it empty to re-enable when access is restored.
196
198
 
197
199
  ---
198
200
 
@@ -299,6 +301,9 @@ No. `five_hour` and `seven_day` are both subscription billing — different acco
299
301
  **Will the 2026-06-15 split break my setup? / What if Anthropic ships another silent change?**
300
302
  No, and it's caught automatically — see [The deadline](#the-deadline-2026-06-15) and [How it stays working](#how-it-works-and-how-it-stays-working). dario rewrites every request to interactive-CC shape before it reaches `api.anthropic.com`, and the three-class drift watcher picks up new changes (npm-release hourly, remote-config every 30 min, classifier-rule daily). v3.38.5 + v3.38.6 — 13 minutes apart, same day as v2.1.142's silent drops — are the prior art.
301
303
 
304
+ **I'm getting `404 not_found` for `claude-fable-5` / `fable`. Did dario break?**
305
+ No — Claude Fable 5 and Mythos 5 were disabled for **all** Anthropic customers by a US-government legal directive on 2026-06-12 (every plan and tier; [details](https://www.anthropic.com/news/fable-mythos-access)). `api.anthropic.com` returns `not_found` for them, so no subscription or proxy can route them. Since v4.8.71 dario drops both families from `/v1/models` and rejects them locally with a clear 404 pointing at `claude-opus-4-8` / `claude-sonnet-4-6`, instead of forwarding into a confusing upstream error. When access is restored, set `DARIO_SUSPENDED_MODELS=` (empty) on the instance to re-enable — no upgrade needed.
306
+
302
307
  Full FAQ: [`docs/faq.md`](./docs/faq.md)
303
308
 
304
309
  ---
@@ -39,6 +39,10 @@ export declare const CC_TOOL_DEFINITIONS: {
39
39
  description: string;
40
40
  input_schema: Record<string, unknown>;
41
41
  }[];
42
+ /** Lowercased CC tool name → canonical name, for identity-matching a CC client's
43
+ * own tools (so newer built-ins not yet in TOOL_MAP map to themselves rather
44
+ * than being treated as foreign and round-robined). Tracks the live bundle. */
45
+ export declare const CC_NATIVE_LOWER: Map<string, string>;
42
46
  /** CC's static system prompt (~25KB). */
43
47
  export declare const CC_SYSTEM_PROMPT: string;
44
48
  /** CC's agent identity string. */
@@ -45,6 +45,10 @@ export function filterToolsForPlatform(tools, platform) {
45
45
  }
46
46
  /** CC's exact tool definitions for the current platform — filtered from the bundled union. */
47
47
  export const CC_TOOL_DEFINITIONS = filterToolsForPlatform(TEMPLATE.tools, process.platform);
48
+ /** Lowercased CC tool name → canonical name, for identity-matching a CC client's
49
+ * own tools (so newer built-ins not yet in TOOL_MAP map to themselves rather
50
+ * than being treated as foreign and round-robined). Tracks the live bundle. */
51
+ export const CC_NATIVE_LOWER = new Map(CC_TOOL_DEFINITIONS.map((t) => [String(t.name).toLowerCase(), String(t.name)]));
48
52
  /** CC's static system prompt (~25KB). */
49
53
  export const CC_SYSTEM_PROMPT = TEMPLATE.system_prompt;
50
54
  /** CC's agent identity string. */
@@ -1230,7 +1234,19 @@ export function buildCCRequest(clientBody, billingTag, cacheControl, identity, o
1230
1234
  const claimedCC = new Set();
1231
1235
  for (const tool of clientTools) {
1232
1236
  const name = (tool.name || '').toLowerCase();
1233
- const mapping = TOOL_MAP[name];
1237
+ // A CC client's OWN tools map to THEMSELVES (identity). TOOL_MAP only
1238
+ // carries the legacy core surface + cross-client aliases, so it lags CC's
1239
+ // newer built-ins (Agent, AskUserQuestion, Cron*, Task*, NotebookEdit,
1240
+ // Enter/ExitPlanMode, Workflow, …). Without this, those land "unmapped",
1241
+ // get round-robined onto Read/Bash/etc., and the collision corrupts the
1242
+ // calls of the tools that DID map (the dock saw Read's `file_path` arrive
1243
+ // as `path`/`filePath`). Identity-matching against the live CC tool set
1244
+ // (CC_NATIVE_LOWER, refreshed by every capture-and-bake) fixes it for
1245
+ // every current-and-future CC client and keeps the canonical CC fingerprint.
1246
+ const mapping = TOOL_MAP[name]
1247
+ ?? (CC_NATIVE_LOWER.get(name)
1248
+ ? { ccTool: CC_NATIVE_LOWER.get(name), translateArgs: (a) => a, translateBack: (a) => a }
1249
+ : undefined);
1234
1250
  if (mapping) {
1235
1251
  // In hybrid mode, clone the shared mapping and attach the
1236
1252
  // client-declared top-level field names from input_schema.
@@ -1266,8 +1282,8 @@ export function buildCCRequest(clientBody, billingTag, cacheControl, identity, o
1266
1282
  const CC_FALLBACK_TOOLS = ['Bash', 'Read', 'Grep', 'Glob', 'WebSearch', 'WebFetch'];
1267
1283
  for (const tool of clientTools) {
1268
1284
  const name = (tool.name || '').toLowerCase();
1269
- if (TOOL_MAP[name])
1270
- continue;
1285
+ if (TOOL_MAP[name] || CC_NATIVE_LOWER.has(name))
1286
+ continue; // mapped, or a CC-native tool (identity in pass 1)
1271
1287
  unmappedTools.push(tool.name);
1272
1288
  if (opts.hybridTools)
1273
1289
  continue; // dropped — see comment above
@@ -32,6 +32,12 @@
32
32
  * — the same ordering normalizeUpstreamIds() produces for live data.
33
33
  */
34
34
  export declare const BAKED_BASE_MODELS: readonly string[];
35
+ /** The set of suspended families, resolved from env at call time. */
36
+ export declare function suspendedFamilies(): Set<string>;
37
+ /** True when `model` (any spelling) belongs to a suspended family. */
38
+ export declare function isSuspendedModel(model: string): boolean;
39
+ /** Drop suspended families from an advertised base list. */
40
+ export declare function dropSuspendedModels(bases: readonly string[]): string[];
35
41
  /**
36
42
  * THE long-context rule — applied identically to every family. A base id
37
43
  * takes a `[1m]` variant unless it's the haiku family (CC's picker never
@@ -43,6 +43,45 @@ export const BAKED_BASE_MODELS = [
43
43
  'claude-sonnet-4-6',
44
44
  'claude-haiku-4-5',
45
45
  ];
46
+ /**
47
+ * Globally-suspended model families — dario neither advertises nor proxies
48
+ * them, even when upstream still lists the id, because api.anthropic.com
49
+ * returns `not_found` for every request to them.
50
+ *
51
+ * Claude Fable 5 AND Mythos 5 were disabled for ALL Anthropic customers by a
52
+ * US-government legal directive on 2026-06-12 (all plans/tiers; other models
53
+ * unaffected) — https://www.anthropic.com/news/fable-mythos-access. dario's
54
+ * baked fallback still carries `claude-fable-5`, and upstream may still list
55
+ * it, so without this filter dario keeps advertising a model that 404s.
56
+ *
57
+ * TEMP — reversible without a code change: `DARIO_SUSPENDED_MODELS` overrides
58
+ * the default (comma-separated families or model ids; each is normalized to
59
+ * its family). Set `DARIO_SUSPENDED_MODELS=` (empty) to re-enable everything
60
+ * once access is restored. Matching is by FAMILY, so every spelling is caught:
61
+ * `fable`, `fable1m`, `claude-fable-5`, `claude-fable-5[1m]`, `claude:fable`.
62
+ */
63
+ const DEFAULT_SUSPENDED_MODELS = 'fable';
64
+ /** The set of suspended families, resolved from env at call time. */
65
+ export function suspendedFamilies() {
66
+ const raw = process.env.DARIO_SUSPENDED_MODELS ?? DEFAULT_SUSPENDED_MODELS;
67
+ return new Set(raw
68
+ .split(',')
69
+ .map((s) => s.trim())
70
+ .filter((s) => s.length > 0)
71
+ .map((s) => modelFamily(s) ?? s.toLowerCase()));
72
+ }
73
+ /** True when `model` (any spelling) belongs to a suspended family. */
74
+ export function isSuspendedModel(model) {
75
+ const fams = suspendedFamilies();
76
+ if (fams.size === 0)
77
+ return false;
78
+ const fam = modelFamily(model);
79
+ return fam !== null && fams.has(fam);
80
+ }
81
+ /** Drop suspended families from an advertised base list. */
82
+ export function dropSuspendedModels(bases) {
83
+ return bases.filter((b) => !isSuspendedModel(b));
84
+ }
46
85
  /**
47
86
  * THE long-context rule — applied identically to every family. A base id
48
87
  * takes a `[1m]` variant unless it's the haiku family (CC's picker never
@@ -201,7 +240,7 @@ async function fetchUpstreamBases(deps) {
201
240
  async function refresh(deps) {
202
241
  const now = deps.now ?? Date.now;
203
242
  lastAttempt = now();
204
- const bases = await fetchUpstreamBases(deps);
243
+ const bases = dropSuspendedModels(await fetchUpstreamBases(deps));
205
244
  cache = { bases, source: 'upstream', fetchedAt: now() };
206
245
  deps.log?.(`[dario] model catalog: autodetected ${bases.length} base models upstream`);
207
246
  }
@@ -242,7 +281,7 @@ export async function getModelCatalog(deps = {}) {
242
281
  }
243
282
  }
244
283
  if (cache === null)
245
- cache = { bases: [...BAKED_BASE_MODELS], source: 'baked', fetchedAt: 0 };
284
+ cache = { bases: dropSuspendedModels(BAKED_BASE_MODELS), source: 'baked', fetchedAt: 0 };
246
285
  return cache;
247
286
  }
248
287
  /**
@@ -251,7 +290,7 @@ export async function getModelCatalog(deps = {}) {
251
290
  * Never blocks the hot path on the network.
252
291
  */
253
292
  export function getCachedBases() {
254
- return cache?.bases ?? BAKED_BASE_MODELS;
293
+ return cache?.bases ?? dropSuspendedModels(BAKED_BASE_MODELS);
255
294
  }
256
295
  /** Fire-and-forget warmup so the first client /v1/models call is served warm. */
257
296
  export function prewarmModelCatalog(deps = {}) {
package/dist/proxy.js CHANGED
@@ -17,7 +17,7 @@ import { loadAllAccounts, loadAccount, refreshAccountToken, resyncLoginFromCrede
17
17
  import { getOpenAIBackend, isOpenAIModel, forwardToOpenAI } from './openai-backend.js';
18
18
  import { RequestQueue, QueueFullError, QueueTimeoutError, DEFAULT_MAX_CONCURRENT, DEFAULT_MAX_QUEUED, DEFAULT_QUEUE_TIMEOUT_MS } from './request-queue.js';
19
19
  import { redactSecrets } from './redact.js';
20
- import { BAKED_BASE_MODELS, withLongContextVariants, buildOpenAIModelsList, getModelCatalog, getCachedBases, resolveAliasAgainst, prewarmModelCatalog } from './model-catalog.js';
20
+ import { BAKED_BASE_MODELS, withLongContextVariants, buildOpenAIModelsList, getModelCatalog, getCachedBases, resolveAliasAgainst, prewarmModelCatalog, isSuspendedModel } from './model-catalog.js';
21
21
  const ANTHROPIC_API = 'https://api.anthropic.com';
22
22
  const DEFAULT_PORT = 3456;
23
23
  const MAX_BODY_BYTES = 10 * 1024 * 1024; // 10 MB — generous for large prompts, prevents abuse
@@ -1587,6 +1587,26 @@ export async function startProxy(opts = {}) {
1587
1587
  const result = isOpenAI ? openaiToAnthropic(parsed, modelOverride) : (modelOverride ? { ...parsed, model: modelOverride } : parsed);
1588
1588
  const r = result;
1589
1589
  requestModel = (r.model || '').toLowerCase();
1590
+ // Suspended-model guard. Fable 5 / Mythos 5 are disabled for ALL
1591
+ // Anthropic customers (US-gov directive 2026-06-12); forwarding any
1592
+ // spelling of them 404s upstream with a confusing not_found. Reject
1593
+ // up front with an actionable error instead — runs before the
1594
+ // template build / account lease / forwarding, so nothing to unwind.
1595
+ // TEMP + reversible: governed by DARIO_SUSPENDED_MODELS (default
1596
+ // 'fable'); set it empty to re-enable when access is restored.
1597
+ if (requestModel && isSuspendedModel(requestModel)) {
1598
+ if (verbose)
1599
+ console.log(`[dario] suspended model rejected: ${requestModel} (${urlPath})`);
1600
+ res.writeHead(404, JSON_HEADERS);
1601
+ res.end(JSON.stringify({
1602
+ type: 'error',
1603
+ error: {
1604
+ type: 'not_found_error',
1605
+ message: `Model "${requestModel}" is suspended in this dario instance. Claude Fable 5 and Mythos 5 are disabled for all Anthropic customers (US government directive 2026-06-12 — https://www.anthropic.com/news/fable-mythos-access). Use claude-opus-4-8 or claude-sonnet-4-6 instead. To re-enable when access is restored, set DARIO_SUSPENDED_MODELS= (empty) on the dario instance.`,
1606
+ },
1607
+ }));
1608
+ return;
1609
+ }
1590
1610
  // In passthrough mode, skip all Claude-specific injection — OAuth swap only.
1591
1611
  // count_tokens also forwards thin (see resolveProxyTarget) — the endpoint
1592
1612
  // counts the CLIENT's own prompt, so template injection would distort it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askalf/dario",
3
- "version": "4.8.70",
3
+ "version": "4.8.72",
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": {