@askalf/dario 6.12.2 → 6.12.4

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.
@@ -532,6 +532,19 @@ export declare function dedupeToolsByName<T extends {
532
532
  name?: unknown;
533
533
  }>(tools: T[]): T[];
534
534
  export declare function isGenuineCCClient(clientBody: Record<string, unknown>): boolean;
535
+ /**
536
+ * The client's tool_choice, when it can be honoured against the tools going out: `auto`, `any`
537
+ * and `none` always; a forced `{type:'tool', name}` with the name translated the way the tool
538
+ * itself was (remap mode: the client name -> its CC tool or fallback slot; preserve mode: the
539
+ * map is empty and the name is the client's), and only when that name is in `outgoingTools`.
540
+ * Anything else, including a malformed value, yields null and the request goes out without one.
541
+ * Exported for unit testing.
542
+ */
543
+ export declare function passthroughToolChoice(choice: unknown, outgoingTools: Array<{
544
+ name?: unknown;
545
+ }> | undefined, toolMap?: ReadonlyMap<string, {
546
+ ccTool: string;
547
+ }>): Record<string, unknown> | null;
535
548
  export declare function buildCCRequest(clientBody: Record<string, unknown>, billingTag: string, cacheControl: CacheControl, identity: {
536
549
  deviceId: string;
537
550
  accountUuid: string;
@@ -1596,6 +1596,26 @@ export function isGenuineCCClient(clientBody) {
1596
1596
  const text = second.text;
1597
1597
  return CC_ORIGIN_SYSTEM_OPENERS.some((opener) => text.startsWith(opener)) || text.includes('Claude Agent SDK');
1598
1598
  }
1599
+ /**
1600
+ * The client's tool_choice, when it can be honoured against the tools going out: `auto`, `any`
1601
+ * and `none` always; a forced `{type:'tool', name}` with the name translated the way the tool
1602
+ * itself was (remap mode: the client name -> its CC tool or fallback slot; preserve mode: the
1603
+ * map is empty and the name is the client's), and only when that name is in `outgoingTools`.
1604
+ * Anything else, including a malformed value, yields null and the request goes out without one.
1605
+ * Exported for unit testing.
1606
+ */
1607
+ export function passthroughToolChoice(choice, outgoingTools, toolMap = new Map()) {
1608
+ if (!choice || typeof choice !== 'object' || Array.isArray(choice))
1609
+ return null;
1610
+ const c = choice;
1611
+ if (c.type === 'auto' || c.type === 'any' || c.type === 'none')
1612
+ return c;
1613
+ if (c.type === 'tool' && typeof c.name === 'string') {
1614
+ const name = toolMap.get(c.name)?.ccTool ?? c.name;
1615
+ return (outgoingTools ?? []).some((t) => t.name === name) ? { ...c, name } : null;
1616
+ }
1617
+ return null;
1618
+ }
1599
1619
  export function buildCCRequest(clientBody, billingTag, cacheControl, identity, opts = {}) {
1600
1620
  const model = clientBody.model || 'claude-sonnet-5';
1601
1621
  const isHaiku = model.toLowerCase().includes('haiku');
@@ -2132,6 +2152,12 @@ export function buildCCRequest(clientBody, billingTag, cacheControl, identity, o
2132
2152
  ccRequest.tools = CC_TOOL_DEFINITIONS;
2133
2153
  ccRequest.tool_choice = { type: 'none' };
2134
2154
  }
2155
+ // Preserve a valid client tool_choice after tool-name translation.
2156
+ if (clientTools && clientTools.length > 0) {
2157
+ const passed = passthroughToolChoice(clientBody.tool_choice, ccRequest.tools, activeToolMap);
2158
+ if (passed)
2159
+ ccRequest.tool_choice = passed;
2160
+ }
2135
2161
  // Metadata
2136
2162
  ccRequest.metadata = {
2137
2163
  user_id: JSON.stringify({
package/dist/proxy.d.ts CHANGED
@@ -87,8 +87,12 @@ export declare function parseProviderPrefix(model: string): {
87
87
  * the fable request carries it, the opus request does not). Subscription
88
88
  * traffic on fable WITHOUT it is soft-refused upstream — every request
89
89
  * returns 200 with `stop_reason: "refusal"` and empty content, while the
90
- * same request on opus/sonnet answers normally. Mirror CC exactly: append
91
- * for the fable family, leave every other family's set untouched.
90
+ * same request on opus/sonnet answers normally. CC 2.1.220 extended it to
91
+ * opus-5; CC 2.1.282 stopped sending it on EVERY family (wire-drift live
92
+ * capture 2026-09-25: opus-5 and fable-5 now equal the opus-4-8 base), so
93
+ * betaForModel adds it for no family. Kept exported: drift-report.mjs still
94
+ * treats it as model-conditional, so a capture that carries it again reads
95
+ * as a transform change, not base drift.
92
96
  * Exported for tests.
93
97
  */
94
98
  export declare const FABLE_FALLBACK_CREDIT_BETA = "fallback-credit-2026-06-01";
@@ -105,9 +109,10 @@ export declare const MID_CONVERSATION_TOOL_CHANGES_BETA = "mid-conversation-tool
105
109
  * it, ORDER included:
106
110
  *
107
111
  * opus-4-8 = base (unchanged)
108
- * opus-5 = base + fallback-credit-2026-06-01 inserted BEFORE afk-mode
109
- * (live capture CC 2.1.220, 2026-07-25 — identical transform to
110
- * fable's; both are the models whose classifiers can refuse)
112
+ * opus-5 = base (CC 2.1.282 dropped
113
+ * fallback-credit-2026-06-01, which 2.1.220 to 2.1.281 inserted
114
+ * before afk-mode; wire-drift live capture 2026-09-25)
115
+ * fable-5 = base (same drop as opus-5)
111
116
  * sonnet-5 = base (== opus — wire-drift
112
117
  * live capture, CC 2.1.204: mid-conversation-system included)
113
118
  * CC 2.1.265 does NOT send mid-conversation-tool-changes here.
@@ -116,7 +121,6 @@ export declare const MID_CONVERSATION_TOOL_CHANGES_BETA = "mid-conversation-tool
116
121
  * haiku-4-5 = base − {mid-conversation-system, mid-conversation-tool-changes,
117
122
  * effort, afk-mode}, and claude-code-20250219 MOVED to
118
123
  * position 5 (before advisor-tool)
119
- * fable-5 = base + fallback-credit-2026-06-01 inserted BEFORE afk-mode
120
124
  *
121
125
  * mid-conversation-tool-changes-2026-07-01 entered `base` with the 2026-09-08
122
126
  * rebake (CC 2.1.265). The live wire-drift capture on that rebake shows it on
package/dist/proxy.js CHANGED
@@ -428,8 +428,12 @@ function filterBillableBetas(betas) {
428
428
  * the fable request carries it, the opus request does not). Subscription
429
429
  * traffic on fable WITHOUT it is soft-refused upstream — every request
430
430
  * returns 200 with `stop_reason: "refusal"` and empty content, while the
431
- * same request on opus/sonnet answers normally. Mirror CC exactly: append
432
- * for the fable family, leave every other family's set untouched.
431
+ * same request on opus/sonnet answers normally. CC 2.1.220 extended it to
432
+ * opus-5; CC 2.1.282 stopped sending it on EVERY family (wire-drift live
433
+ * capture 2026-09-25: opus-5 and fable-5 now equal the opus-4-8 base), so
434
+ * betaForModel adds it for no family. Kept exported: drift-report.mjs still
435
+ * treats it as model-conditional, so a capture that carries it again reads
436
+ * as a transform change, not base drift.
433
437
  * Exported for tests.
434
438
  */
435
439
  export const FABLE_FALLBACK_CREDIT_BETA = 'fallback-credit-2026-06-01';
@@ -441,18 +445,11 @@ export const ADVISOR_TOOL_BETA = 'advisor-tool-2026-03-01';
441
445
  export const CLAUDE_CODE_BETA = 'claude-code-20250219';
442
446
  export const MID_CONVERSATION_TOOL_CHANGES_BETA = 'mid-conversation-tool-changes-2026-07-01';
443
447
  /**
444
- * Insert `flag` immediately before the first `anchor`, deduped. If `flag` is
448
+ * Insert `flag` immediately AFTER the first `anchor`, deduped. If `flag` is
445
449
  * already present the list is returned unchanged; if `anchor` is absent `flag`
446
450
  * is appended at the tail. Used to place model-conditional betas at the exact
447
451
  * position real CC emits them, rather than always appending.
448
452
  */
449
- function insertBetaBefore(flags, flag, anchor) {
450
- if (flags.includes(flag))
451
- return flags;
452
- const i = flags.indexOf(anchor);
453
- return i < 0 ? [...flags, flag] : [...flags.slice(0, i), flag, ...flags.slice(i)];
454
- }
455
- /** As insertBetaBefore, but places `flag` immediately AFTER the first `anchor`. */
456
453
  function insertBetaAfter(flags, flag, anchor) {
457
454
  if (flags.includes(flag))
458
455
  return flags;
@@ -477,9 +474,10 @@ function moveBetaBefore(flags, flag, anchor) {
477
474
  * it, ORDER included:
478
475
  *
479
476
  * opus-4-8 = base (unchanged)
480
- * opus-5 = base + fallback-credit-2026-06-01 inserted BEFORE afk-mode
481
- * (live capture CC 2.1.220, 2026-07-25 — identical transform to
482
- * fable's; both are the models whose classifiers can refuse)
477
+ * opus-5 = base (CC 2.1.282 dropped
478
+ * fallback-credit-2026-06-01, which 2.1.220 to 2.1.281 inserted
479
+ * before afk-mode; wire-drift live capture 2026-09-25)
480
+ * fable-5 = base (same drop as opus-5)
483
481
  * sonnet-5 = base (== opus — wire-drift
484
482
  * live capture, CC 2.1.204: mid-conversation-system included)
485
483
  * CC 2.1.265 does NOT send mid-conversation-tool-changes here.
@@ -488,7 +486,6 @@ function moveBetaBefore(flags, flag, anchor) {
488
486
  * haiku-4-5 = base − {mid-conversation-system, mid-conversation-tool-changes,
489
487
  * effort, afk-mode}, and claude-code-20250219 MOVED to
490
488
  * position 5 (before advisor-tool)
491
- * fable-5 = base + fallback-credit-2026-06-01 inserted BEFORE afk-mode
492
489
  *
493
490
  * mid-conversation-tool-changes-2026-07-01 entered `base` with the 2026-09-08
494
491
  * rebake (CC 2.1.265). The live wire-drift capture on that rebake shows it on
@@ -538,17 +535,9 @@ export function betaForModel(base, model, skipContext1m = false) {
538
535
  flags = flags.filter((f) => f !== MID_CONVERSATION_SYSTEM_BETA);
539
536
  }
540
537
  }
541
- else if (m.includes('fable') || /opus-5(?!\d)/.test(m)) {
542
- // fallback-credit is NOT fable-only any more. Live capture on CC 2.1.220
543
- // (2026-07-25) shows claude-opus-5 carrying it in the same slot — before
544
- // afk-mode — while opus-4-8 and sonnet-5 do not. That tracks the models
545
- // with refusal classifiers: Opus 5 and Fable 5 can decline a request and
546
- // route to a fallback, so CC opts both into the credit beta. Matched on
547
- // `opus-5` specifically rather than `opus-5+`: a future opus-6 probably
548
- // inherits this, but that's a guess and the wire-drift runner will say so.
549
- flags = insertBetaBefore(flags, FABLE_FALLBACK_CREDIT_BETA, AFK_MODE_BETA);
550
- }
551
- // opus-4-x + unknown families keep the base set unchanged.
538
+ // opus, fable + unknown families keep the base set unchanged. fable-5 and
539
+ // opus-5 carried fallback-credit-2026-06-01 from CC 2.1.220 to 2.1.281; CC
540
+ // 2.1.282 sends neither with it (wire-drift capture 2026-09-25).
552
541
  if (/\[1m\]$/.test(m) && !skipContext1m) {
553
542
  flags = insertBetaAfter(flags, CONTEXT_1M_BETA, CLAUDE_CODE_BETA);
554
543
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askalf/dario",
3
- "version": "6.12.2",
3
+ "version": "6.12.4",
4
4
  "description": "Use your Claude and ChatGPT subscriptions in Cursor, Cline, Aider, Claude Code and the Agent SDK — at subscription pricing, not per-token API bills. One local Anthropic + OpenAI-compatible endpoint: either plan answers either wire shape, with automatic failover when one hits its limit.",
5
5
  "type": "module",
6
6
  "bin": {