@askalf/dario 4.8.133 → 4.8.135
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 +1 -1
- package/dist/cc-template.d.ts +11 -0
- package/dist/cc-template.js +45 -10
- package/dist/proxy.d.ts +2 -3
- package/dist/proxy.js +25 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -224,7 +224,7 @@ Tune via `~/.dario/config.json` → `overageGuard`, or CLI flags: `--overage-beh
|
|
|
224
224
|
- **Multi-account pool.** Drop Claude accounts in `~/.dario/accounts/` and pool mode auto-activates — one is enough to serve, so `dario accounts add` alone bootstraps a proxy with no `dario login` step. With more, every request routes to the account with the most headroom, multi-turn sessions pin to one account so the prompt cache survives, in-flight 429s fail over to a peer before your client sees an error. → [`docs/multi-account-pool.md`](./docs/multi-account-pool.md)
|
|
225
225
|
- **Headless admin API (`DARIO_ADMIN=1`).** Provision and manage pool accounts entirely over HTTP — start with zero accounts, `POST /admin/login/start`, paste the code back, and the account is routable the moment the `200` lands (live pool hot-reload, no restart). Token-gated even on loopback, audit-logged, rate-limited; `GET /admin/accounts` reports live per-account headroom. Built for Docker / k8s / Raspberry-Pi deployments where a console is the awkward part. → [`docs/admin-api.md`](./docs/admin-api.md)
|
|
226
226
|
- **Behavioral stealth (`--stealth`).** Static wire fidelity covers *what* the request looks like; `--stealth` adds *when* it arrives — response-length-correlated think time and 1.2–4.2s session-start latency, the inter-arrival pattern real interactive sessions have and agent loops don't. → [`docs/wire-fidelity.md`](./docs/wire-fidelity.md)
|
|
227
|
-
- **Runs any non-Claude-Code agent.** A 64-entry schema-verified `TOOL_MAP` pre-maps Cline, Roo, Kilo, Cursor, Windsurf, Continue, Copilot, OpenHands, OpenClaw, Hermes, [hands](https://github.com/askalf/hands) tool names to CC's native set. No flag, no validator errors. → [`docs/integrations/agent-compat.md`](./docs/integrations/agent-compat.md)
|
|
227
|
+
- **Runs any non-Claude-Code agent.** A 64-entry schema-verified `TOOL_MAP` pre-maps Cline, Roo, Kilo, Cursor, Windsurf, Continue, Copilot, OpenHands, OpenClaw, Hermes, [hands](https://github.com/askalf/hands) tool names to CC's native set. No flag, no validator errors. MCP tools (`mcp__server__tool`) forward verbatim — the shape real CC uses for session-attached servers (v4.8.135). → [`docs/integrations/agent-compat.md`](./docs/integrations/agent-compat.md)
|
|
228
228
|
- **Recover output capability.** `dario proxy --system-prompt=partial` strips CC's tone/verbosity/no-comments constraints for 1.2–2.8× more output on open-ended work — empirically without flipping billing (the classifier doesn't read that slot). [Discussion #183](https://github.com/askalf/dario/discussions/183) has the per-prompt receipts. → [`docs/system-prompt.md`](./docs/system-prompt.md)
|
|
229
229
|
- **Honor client thinking (`--honor-client-thinking`).** By default dario rebuilds the outbound request with CC's interactive thinking shape regardless of what the client sent. Pass this flag (or `DARIO_HONOR_CLIENT_THINKING=1`) to pass a non-CC client's own `thinking` block through unchanged. Off by default; the rebuild-to-CC path is what keeps the subscription pool routing.
|
|
230
230
|
- **Preserve structured output (`--preserve-output-format`).** By default dario rebuilds `output_config` from CC's template (effort only), dropping a client's `output_config.format` JSON schema, so structured-output clients get unconstrained prose their strict parser rejects. Pass this flag (or `DARIO_PRESERVE_OUTPUT_FORMAT=1`) to carry the client's schema through unchanged — SDK clients like the Vercel AI SDK's `generateObject` then get schema-constrained output. Off by default; empirically without flipping billing (verified `five_hour` on `claude-sonnet-4-6`).
|
package/dist/cc-template.d.ts
CHANGED
|
@@ -63,6 +63,17 @@ export declare const CC_TOOL_DEFINITIONS: {
|
|
|
63
63
|
* PascalCase, the {path}-style clients send lowercase/snake, so a non-CC `read`
|
|
64
64
|
* still routes through TOOL_MAP. Tracks the live bundle (refreshed each bake). */
|
|
65
65
|
export declare const CC_NATIVE_NAMES: Set<string>;
|
|
66
|
+
/** MCP tools attached to a CC session are namespaced `mcp__<server>__<tool>`.
|
|
67
|
+
* Real CC advertises them VERBATIM after its built-ins — the schemas are
|
|
68
|
+
* operator-supplied, so there is no canonical template entry to substitute.
|
|
69
|
+
* Passing them through unchanged IS CC's wire shape; remapping them was the
|
|
70
|
+
* divergence. Before v4.8.135 they fell through to the unmapped-tool
|
|
71
|
+
* round-robin: dropped from the advertised array (the model never saw them)
|
|
72
|
+
* while history tool_use blocks were renamed onto Bash/Read/… with junk args
|
|
73
|
+
* — seen live as "tool substitution: 28/52 client tools not in TOOL_MAP" on
|
|
74
|
+
* a CC session with an MCP server attached. Like CC_NATIVE_NAMES, these
|
|
75
|
+
* identity-map and skip TOOL_MAP entirely. */
|
|
76
|
+
export declare function isMcpToolName(name: unknown): boolean;
|
|
66
77
|
/** CC's static system prompt (~25KB). The shared base — baked from a non-Fable
|
|
67
78
|
* model (Opus). CC ships some models a larger, model-specific prompt; see
|
|
68
79
|
* CC_SYSTEM_PROMPT_FABLE / systemPromptForModel. */
|
package/dist/cc-template.js
CHANGED
|
@@ -73,6 +73,19 @@ export const CC_TOOL_DEFINITIONS = filterToolsForPlatform(TEMPLATE.tools, proces
|
|
|
73
73
|
* PascalCase, the {path}-style clients send lowercase/snake, so a non-CC `read`
|
|
74
74
|
* still routes through TOOL_MAP. Tracks the live bundle (refreshed each bake). */
|
|
75
75
|
export const CC_NATIVE_NAMES = new Set(CC_TOOL_DEFINITIONS.map((t) => String(t.name)));
|
|
76
|
+
/** MCP tools attached to a CC session are namespaced `mcp__<server>__<tool>`.
|
|
77
|
+
* Real CC advertises them VERBATIM after its built-ins — the schemas are
|
|
78
|
+
* operator-supplied, so there is no canonical template entry to substitute.
|
|
79
|
+
* Passing them through unchanged IS CC's wire shape; remapping them was the
|
|
80
|
+
* divergence. Before v4.8.135 they fell through to the unmapped-tool
|
|
81
|
+
* round-robin: dropped from the advertised array (the model never saw them)
|
|
82
|
+
* while history tool_use blocks were renamed onto Bash/Read/… with junk args
|
|
83
|
+
* — seen live as "tool substitution: 28/52 client tools not in TOOL_MAP" on
|
|
84
|
+
* a CC session with an MCP server attached. Like CC_NATIVE_NAMES, these
|
|
85
|
+
* identity-map and skip TOOL_MAP entirely. */
|
|
86
|
+
export function isMcpToolName(name) {
|
|
87
|
+
return typeof name === 'string' && name.startsWith('mcp__');
|
|
88
|
+
}
|
|
76
89
|
/** CC's static system prompt (~25KB). The shared base — baked from a non-Fable
|
|
77
90
|
* model (Opus). CC ships some models a larger, model-specific prompt; see
|
|
78
91
|
* CC_SYSTEM_PROMPT_FABLE / systemPromptForModel. */
|
|
@@ -526,14 +539,19 @@ export function detectNonCCByTools(clientTools) {
|
|
|
526
539
|
const rawName = tool.name || '';
|
|
527
540
|
// A tool is "foreign" only if dario can neither map it (TOOL_MAP, by
|
|
528
541
|
// lowercased cross-client alias) nor recognize it as CC's own (CC_NATIVE_NAMES,
|
|
529
|
-
// by exact PascalCase)
|
|
542
|
+
// by exact PascalCase) nor pass it through as an MCP tool (mcp__* — identity,
|
|
543
|
+
// like CC natives). CC-native tools identity-map to themselves in the remap
|
|
530
544
|
// path (see buildCCRequest), so counting them here inflates the ratio and
|
|
531
545
|
// mis-flags a modern, agentic-heavy CC client (Agent, Skill, Workflow, Task*,
|
|
532
546
|
// … — in the live bundle but absent from TOOL_MAP's alias table) as
|
|
533
547
|
// 'unknown-non-cc', flipping it to preserve and discarding the CC tool
|
|
534
|
-
// fingerprint dario exists to present.
|
|
535
|
-
//
|
|
536
|
-
|
|
548
|
+
// fingerprint dario exists to present. Same trap for MCP tools: two or three
|
|
549
|
+
// attached servers push a real CC session past the 80% line on their own
|
|
550
|
+
// (28/52 seen live). Mirror the routing's membership test so detection and
|
|
551
|
+
// routing agree. An ALL-mcp surface now scores ratio 0 and stays in default
|
|
552
|
+
// mode — safe, because the advertise path sends an mcp-only declaration
|
|
553
|
+
// verbatim rather than falling back to the full CC template.
|
|
554
|
+
if (!TOOL_MAP[rawName.toLowerCase()] && !CC_NATIVE_NAMES.has(rawName) && !isMcpToolName(rawName))
|
|
537
555
|
unmapped++;
|
|
538
556
|
}
|
|
539
557
|
const ratio = unmapped / clientTools.length;
|
|
@@ -1336,10 +1354,13 @@ export function buildCCRequest(clientBody, billingTag, cacheControl, identity, o
|
|
|
1336
1354
|
// 2. CC's newer built-ins (Agent, AskUserQuestion, Cron*, Task*, Workflow,
|
|
1337
1355
|
// NotebookEdit, Enter/ExitPlanMode, …) aren't in TOOL_MAP at all, so
|
|
1338
1356
|
// they were round-robined onto Read/Bash/etc. and collided.
|
|
1357
|
+
// 3. MCP tools (mcp__<server>__<tool>) carry operator-supplied schemas
|
|
1358
|
+
// that never have a TOOL_MAP or template entry. Real CC forwards them
|
|
1359
|
+
// verbatim, so they identity-map too and are advertised as-is below.
|
|
1339
1360
|
// Exact case is the discriminator (CC sends PascalCase; {path}-style clients
|
|
1340
1361
|
// send lowercase/snake) so a genuine non-CC `read` still routes via TOOL_MAP.
|
|
1341
1362
|
// Tracks the live bundle, so future CC tools are covered after the next bake.
|
|
1342
|
-
const mapping = CC_NATIVE_NAMES.has(tool.name)
|
|
1363
|
+
const mapping = CC_NATIVE_NAMES.has(tool.name) || isMcpToolName(tool.name)
|
|
1343
1364
|
? { ccTool: tool.name, translateArgs: (a) => a, translateBack: (a) => a }
|
|
1344
1365
|
: TOOL_MAP[name];
|
|
1345
1366
|
if (mapping) {
|
|
@@ -1377,8 +1398,8 @@ export function buildCCRequest(clientBody, billingTag, cacheControl, identity, o
|
|
|
1377
1398
|
const CC_FALLBACK_TOOLS = ['Bash', 'Read', 'Grep', 'Glob', 'WebSearch', 'WebFetch'];
|
|
1378
1399
|
for (const tool of clientTools) {
|
|
1379
1400
|
const name = (tool.name || '').toLowerCase();
|
|
1380
|
-
if (CC_NATIVE_NAMES.has(tool.name) || TOOL_MAP[name])
|
|
1381
|
-
continue; // CC-native (identity in pass 1) or mapped
|
|
1401
|
+
if (CC_NATIVE_NAMES.has(tool.name) || isMcpToolName(tool.name) || TOOL_MAP[name])
|
|
1402
|
+
continue; // CC-native / MCP (identity in pass 1) or mapped
|
|
1382
1403
|
unmappedTools.push(tool.name);
|
|
1383
1404
|
if (opts.hybridTools)
|
|
1384
1405
|
continue; // dropped — see comment above
|
|
@@ -1555,13 +1576,27 @@ export function buildCCRequest(clientBody, billingTag, cacheControl, identity, o
|
|
|
1555
1576
|
// dario-routed CC session). A real CC client with a reduced tool set sends
|
|
1556
1577
|
// exactly this reduced array (every --disallowedTools / MCP delta does
|
|
1557
1578
|
// this), so filtering tracks CC's wire shape rather than diverging from it.
|
|
1558
|
-
//
|
|
1559
|
-
//
|
|
1579
|
+
//
|
|
1580
|
+
// MCP tools (mcp__<server>__<tool>) are appended VERBATIM after the
|
|
1581
|
+
// canonical natives — that mirrors real CC, which sends operator-supplied
|
|
1582
|
+
// MCP schemas after its built-ins. There is no template entry to
|
|
1583
|
+
// substitute, and omitting them here (pre-v4.8.135) meant the model never
|
|
1584
|
+
// saw the session's MCP surface at all while history references were
|
|
1585
|
+
// round-robined onto fallback slots.
|
|
1586
|
+
//
|
|
1587
|
+
// If the client declared neither a CC-native nor an MCP tool it isn't
|
|
1588
|
+
// really CC; keep the full template as the safer fingerprint default in
|
|
1589
|
+
// that case. An mcp-only declaration goes out verbatim instead — falling
|
|
1590
|
+
// back to the full template there would advertise natives the client
|
|
1591
|
+
// can't execute (the AskUserQuestion failure mode above).
|
|
1560
1592
|
const clientToolNames = new Set(clientTools
|
|
1561
1593
|
.map((t) => t.name?.toLowerCase())
|
|
1562
1594
|
.filter((n) => Boolean(n)));
|
|
1563
1595
|
const availableCC = CC_TOOL_DEFINITIONS.filter((t) => clientToolNames.has(t.name.toLowerCase()));
|
|
1564
|
-
|
|
1596
|
+
const mcpTools = clientTools.filter((t) => isMcpToolName(t.name));
|
|
1597
|
+
ccRequest.tools = availableCC.length > 0 || mcpTools.length > 0
|
|
1598
|
+
? [...availableCC, ...mcpTools]
|
|
1599
|
+
: CC_TOOL_DEFINITIONS;
|
|
1565
1600
|
}
|
|
1566
1601
|
}
|
|
1567
1602
|
else if (effectiveMergeTools) {
|
package/dist/proxy.d.ts
CHANGED
|
@@ -64,9 +64,8 @@ export declare const CLAUDE_CODE_BETA = "claude-code-20250219";
|
|
|
64
64
|
* it, ORDER included:
|
|
65
65
|
*
|
|
66
66
|
* opus-4-8 = base (unchanged)
|
|
67
|
-
* sonnet-5 = base
|
|
68
|
-
*
|
|
69
|
-
* gone: 2.1.199 sonnet == opus)
|
|
67
|
+
* sonnet-5 = base − {mid-conversation-system} (CC 2.1.201 dropped it
|
|
68
|
+
* from sonnet; 2.1.199 sonnet == opus and still kept it — #667)
|
|
70
69
|
* haiku-4-5 = base − {mid-conversation-system, effort, afk-mode}, and
|
|
71
70
|
* claude-code-20250219 MOVED to position 5 (before advisor-tool)
|
|
72
71
|
* fable-5 = base + fallback-credit-2026-06-01 inserted BEFORE afk-mode
|
package/dist/proxy.js
CHANGED
|
@@ -9,7 +9,7 @@ import { arch, platform } from 'node:process';
|
|
|
9
9
|
import { getAccessToken, getStatus } from './oauth.js';
|
|
10
10
|
import { buildHealthResponse, derivePoolStatus, shouldDiscloseHealthInternals } from './health-response.js';
|
|
11
11
|
import { darioVersion } from './version.js';
|
|
12
|
-
import { buildCCRequest, applyCcPromptCaching, parseEffortSuffix, reverseMapResponse, createStreamingReverseMapper, orderHeadersForOutbound, CC_TEMPLATE } from './cc-template.js';
|
|
12
|
+
import { buildCCRequest, applyCcPromptCaching, parseEffortSuffix, reverseMapResponse, createStreamingReverseMapper, orderHeadersForOutbound, isMcpToolName, CC_TEMPLATE } from './cc-template.js';
|
|
13
13
|
import { stampCch, hasCchSeed } from './cch.js';
|
|
14
14
|
import { describeTemplate, detectDrift, checkCCCompat } from './live-fingerprint.js';
|
|
15
15
|
import { AccountPool, computeStickyKey, parseRateLimits, modelFamily, isInAuthCooldown, authCooldownMs, reconcilePoolAccounts } from './pool.js';
|
|
@@ -323,9 +323,8 @@ function moveBetaBefore(flags, flag, anchor) {
|
|
|
323
323
|
* it, ORDER included:
|
|
324
324
|
*
|
|
325
325
|
* opus-4-8 = base (unchanged)
|
|
326
|
-
* sonnet-5 = base
|
|
327
|
-
*
|
|
328
|
-
* gone: 2.1.199 sonnet == opus)
|
|
326
|
+
* sonnet-5 = base − {mid-conversation-system} (CC 2.1.201 dropped it
|
|
327
|
+
* from sonnet; 2.1.199 sonnet == opus and still kept it — #667)
|
|
329
328
|
* haiku-4-5 = base − {mid-conversation-system, effort, afk-mode}, and
|
|
330
329
|
* claude-code-20250219 MOVED to position 5 (before advisor-tool)
|
|
331
330
|
* fable-5 = base + fallback-credit-2026-06-01 inserted BEFORE afk-mode
|
|
@@ -354,10 +353,15 @@ export function betaForModel(base, model, skipContext1m = false) {
|
|
|
354
353
|
flags = flags.filter((f) => !drop.has(f));
|
|
355
354
|
flags = moveBetaBefore(flags, CLAUDE_CODE_BETA, ADVISOR_TOOL_BETA);
|
|
356
355
|
}
|
|
356
|
+
else if (m.includes('sonnet')) {
|
|
357
|
+
// CC 2.1.201 dropped mid-conversation-system from sonnet's beta set (2.1.199
|
|
358
|
+
// sonnet == opus and still carried it — live capture #667). opus + fable keep it.
|
|
359
|
+
flags = flags.filter((f) => f !== MID_CONVERSATION_SYSTEM_BETA);
|
|
360
|
+
}
|
|
357
361
|
else if (m.includes('fable')) {
|
|
358
362
|
flags = insertBetaBefore(flags, FABLE_FALLBACK_CREDIT_BETA, AFK_MODE_BETA);
|
|
359
363
|
}
|
|
360
|
-
// opus +
|
|
364
|
+
// opus + unknown families keep the base set unchanged.
|
|
361
365
|
if (/\[1m\]$/.test(m) && !skipContext1m) {
|
|
362
366
|
flags = insertBetaAfter(flags, CONTEXT_1M_BETA, CLAUDE_CODE_BETA);
|
|
363
367
|
}
|
|
@@ -888,6 +892,8 @@ export async function startProxy(opts = {}) {
|
|
|
888
892
|
// tool-substitution warn line. Same de-dup contract as
|
|
889
893
|
// detectedClientsLogged so mixed-traffic proxies don't spam.
|
|
890
894
|
const toolSubLogged = new Set();
|
|
895
|
+
// Same de-dup contract for the verbose-only MCP-passthrough note.
|
|
896
|
+
const mcpPassthroughLogged = new Set();
|
|
891
897
|
// Body-dump mode: set via --verbose=2 / -vv or DARIO_LOG_BODIES=1.
|
|
892
898
|
// When on, every request emits a redacted JSON body to stderr so
|
|
893
899
|
// operators can see exactly what dario forwards upstream. Default
|
|
@@ -2142,6 +2148,20 @@ export async function startProxy(opts = {}) {
|
|
|
2142
2148
|
const more = unmappedTools.length > 5 ? `, +${unmappedTools.length - 5} more` : '';
|
|
2143
2149
|
console.log(`[dario] tool substitution: ${unmappedTools.length}/${totalTools} client tool${unmappedTools.length === 1 ? '' : 's'} not in TOOL_MAP — remapped onto CC fallback slots (${sample}${more}). Pass --preserve-tools to forward your schemas verbatim instead.`);
|
|
2144
2150
|
}
|
|
2151
|
+
// MCP tools (mcp__<server>__<tool>) forward verbatim in default
|
|
2152
|
+
// mode — real CC advertises session-attached MCP schemas as-is,
|
|
2153
|
+
// so this is passthrough, not substitution. Verbose-only note so
|
|
2154
|
+
// operators can confirm their MCP surface survived; same de-dupe
|
|
2155
|
+
// as the substitution warn.
|
|
2156
|
+
if (verbose && !preserveToolsEffective && !mcpPassthroughLogged.has(subKey)) {
|
|
2157
|
+
const mcpCount = Array.isArray(r.tools)
|
|
2158
|
+
? r.tools.filter((t) => isMcpToolName(t?.name)).length
|
|
2159
|
+
: 0;
|
|
2160
|
+
if (mcpCount > 0) {
|
|
2161
|
+
mcpPassthroughLogged.add(subKey);
|
|
2162
|
+
console.log(`[dario] ${mcpCount} MCP tool${mcpCount === 1 ? '' : 's'} (mcp__*) forwarded verbatim alongside CC's native set`);
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2145
2165
|
// Store tool map for response reverse-mapping
|
|
2146
2166
|
ccToolMap = toolMap;
|
|
2147
2167
|
// Replace request body entirely with CC template
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askalf/dario",
|
|
3
|
-
"version": "4.8.
|
|
3
|
+
"version": "4.8.135",
|
|
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": {
|