@askalf/dario 4.7.1 → 4.8.0

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
@@ -513,7 +513,6 @@ Ordered by relevance to a dario reader — projects that route through dario fir
513
513
  | [hands](https://github.com/askalf/hands) | Cross-platform computer-use agent — your LLM on your mouse, keyboard, and screen. Windows + macOS + Linux. Routes through dario or any Anthropic-compat. |
514
514
  | [deepdive](https://github.com/askalf/deepdive) | Local research agent. One command, cited answer. Plan → search → headless fetch → extract → synthesize. Every LLM call through your own router. |
515
515
  | [claude-sync](https://github.com/askalf/claude-sync) | Sync Claude Code sessions across machines. Pack a CC session into a portable `.ccsync` file, ship it via Dropbox / iCloud / USB, unpack on the other side. |
516
- | [claude-bridge](https://github.com/askalf/claude-bridge) | Bridge Claude Code sessions to Discord. Stay in sync with your CC instances from your phone. |
517
516
  | [browser-bridge](https://github.com/askalf/browser-bridge) | Stealth headless Chromium in a container, CDP on 9222. Connect from Playwright, Puppeteer, MCP browser tools, any agent that wants a remote browser. |
518
517
  | [install-kit](https://github.com/askalf/install-kit) | curl-pipe-bash template for self-hosted Docker apps — banner, prereq probes, `.env` scaffolding with crypto-rand secrets, healthcheck wait loop. |
519
518
  | [pgflex](https://github.com/askalf/pgflex) | One Postgres API, two modes — real PostgreSQL for production, PGlite (in-process WASM) for standalone / dev. Same SQL, drop the server when you don't need it. |
@@ -317,6 +317,7 @@ export declare function buildCCRequest(clientBody: Record<string, unknown>, bill
317
317
  effort?: EffortValue;
318
318
  maxTokens?: number | 'client';
319
319
  systemPrompt?: string;
320
+ skipFields?: ReadonlySet<string>;
320
321
  }): {
321
322
  body: Record<string, unknown>;
322
323
  toolMap: Map<string, ToolMapping>;
@@ -1278,24 +1278,45 @@ export function buildCCRequest(clientBody, billingTag, cacheControl, identity, o
1278
1278
  ccRequest.max_tokens = resolveMaxTokens(opts.maxTokens, clientBody);
1279
1279
  // Model-specific fields — order: thinking, context_management, output_config
1280
1280
  //
1281
- // `thinking: {type:"adaptive"}` is a 4.6-generation feature; older
1282
- // Opus/Sonnet 4-5 models 400 it (`"adaptive thinking is not supported
1283
- // on this model"`). `context_management.edits[clear_thinking_*]` is
1284
- // tied to thinking — sending it without an enabled thinking field
1285
- // 400s too (`"clear_thinking_* strategy requires thinking to be
1286
- // enabled or adaptive"`). So both are gated on the same model check,
1287
- // and either both ship or neither does.
1281
+ // Layered guard:
1282
+ //
1283
+ // 1. Haiku skips all three by construction (existing behavior).
1284
+ //
1285
+ // 2. `thinking: {type:"adaptive"}` is a 4.6-generation feature; older
1286
+ // Opus/Sonnet 4-5 models 400 it (`"adaptive thinking is not supported
1287
+ // on this model"`). `context_management.edits[clear_thinking_*]` is
1288
+ // tied to thinking — sending it without an enabled thinking field
1289
+ // 400s too (`"clear_thinking_* strategy requires thinking to be
1290
+ // enabled or adaptive"`). Both are gated on `supportsAdaptiveThinking`;
1291
+ // either both ship or neither does.
1292
+ //
1293
+ // 3. Each remaining injection is also opt-out via `opts.skipFields`.
1294
+ // Non-CC clients (e.g. apps calling dario via the Anthropic SDK)
1295
+ // sometimes hit model endpoints that still 400 on these fields with
1296
+ // "Extra inputs are not permitted" even when supportsAdaptiveThinking
1297
+ // is true. Operators set `--skip-fields=context_management,…` (or
1298
+ // DARIO_SKIP_FIELDS=…) to suppress the offending field while keeping
1299
+ // all other CC fingerprinting (headers, beta flags, metadata) intact
1300
+ // — Max billing pool routing is unchanged.
1288
1301
  //
1289
1302
  // `output_config.effort` is independent of thinking and ships for all
1290
- // non-Haiku models. Default `'high'` matches CC 2.1.116's wire value;
1291
- // `--effort` flag overrides; `'client'` passes through whatever the
1292
- // client sent (or falls back to `'high'` if absent). See dario#87.
1303
+ // non-Haiku models that aren't opted out via skipFields. Default `'high'`
1304
+ // matches CC 2.1.116's wire value; `--effort` flag overrides; `'client'`
1305
+ // passes through whatever the client sent (or falls back to `'high'` if
1306
+ // absent). See dario#87.
1293
1307
  if (!isHaiku) {
1308
+ const skip = opts.skipFields;
1294
1309
  if (supportsAdaptiveThinking(model)) {
1295
- ccRequest.thinking = { type: 'adaptive' };
1296
- ccRequest.context_management = { edits: [{ type: 'clear_thinking_20251015', keep: 'all' }] };
1310
+ if (!skip || !skip.has('thinking')) {
1311
+ ccRequest.thinking = { type: 'adaptive' };
1312
+ }
1313
+ if (!skip || !skip.has('context_management')) {
1314
+ ccRequest.context_management = { edits: [{ type: 'clear_thinking_20251015', keep: 'all' }] };
1315
+ }
1316
+ }
1317
+ if (!skip || !skip.has('output_config')) {
1318
+ ccRequest.output_config = { effort: resolveEffort(opts.effort, clientBody) };
1297
1319
  }
1298
- ccRequest.output_config = { effort: resolveEffort(opts.effort, clientBody) };
1299
1320
  }
1300
1321
  ccRequest.stream = stream;
1301
1322
  // Replay the captured top-level key order. The hardcoded build order above
package/dist/cli.d.ts CHANGED
@@ -41,6 +41,13 @@ export declare function resolveSystemPromptFlag(args: string[], envVar: string |
41
41
  * - empty entries / whitespace-only entries / duplicates are dropped.
42
42
  */
43
43
  export declare function parsePassthroughBetasFlag(args: string[], envVar: string | undefined): string[];
44
+ /**
45
+ * Parse `--skip-fields=<csv>` (or DARIO_SKIP_FIELDS env) into a deduped
46
+ * trimmed list of CC body field names. Unrecognized values are passed
47
+ * through; proxy.ts validates against the allowed set at startup and
48
+ * warns on each typo. Same edge-case handling as parsePassthroughBetasFlag.
49
+ */
50
+ export declare function parseSkipFieldsFlag(args: string[], envVar: string | undefined): string[];
44
51
  /**
45
52
  * Parse `--max-tokens=<N|client>` + `DARIO_MAX_TOKENS` env (dario#88).
46
53
  * Numeric values pin; `client` (case-insensitive) = passthrough client's
package/dist/cli.js CHANGED
@@ -513,6 +513,11 @@ async function proxy() {
513
513
  ?? overageNotifyFromEnv
514
514
  ?? fileCfg.overageGuard?.notifyOs
515
515
  ?? true;
516
+ // --skip-fields=name1,name2 — CC body fields to omit from outbound
517
+ // requests. Allowed values: thinking, context_management, output_config.
518
+ // Falls back to DARIO_SKIP_FIELDS env var. See ProxyOptions.skipFields
519
+ // for rationale.
520
+ const skipFields = parseSkipFieldsFlag(args, process.env['DARIO_SKIP_FIELDS']);
516
521
  // Non-loopback bind without DARIO_API_KEY turns dario into an open
517
522
  // OAuth-subscription relay for anyone on the reachable network. Refuse
518
523
  // to start rather than rely on the operator to read the startup banner.
@@ -532,7 +537,7 @@ async function proxy() {
532
537
  console.error(`[dario] Override (not recommended): pass --unsafe-no-auth if you have out-of-band network controls and accept the risk.`);
533
538
  process.exit(1);
534
539
  }
535
- await startProxy({ port, host, verbose, verboseBodies, model, passthrough, preserveTools, hybridTools, mergeTools, noAutoDetect, strictTls, pacingMinMs, pacingJitterMs, thinkTimeBaseMs, thinkTimePerTokenMs, thinkTimeJitterMs, thinkTimeMaxMs, sessionStartMinMs, sessionStartJitterMs, stealth, drainOnClose, sessionIdleRotateMs, sessionRotateJitterMs, sessionMaxAgeMs, sessionPerClient, preserveOrchestrationTags, noLiveCapture, strictTemplate, maxConcurrent, maxQueued, queueTimeoutMs, effort, maxTokens, logFile, passthroughBetas, systemPrompt, overageGuardEnabled, overageGuardBehavior, overageGuardCooldownMs, overageGuardNotifyOs });
540
+ await startProxy({ port, host, verbose, verboseBodies, model, passthrough, preserveTools, hybridTools, mergeTools, noAutoDetect, strictTls, pacingMinMs, pacingJitterMs, thinkTimeBaseMs, thinkTimePerTokenMs, thinkTimeJitterMs, thinkTimeMaxMs, sessionStartMinMs, sessionStartJitterMs, stealth, drainOnClose, sessionIdleRotateMs, sessionRotateJitterMs, sessionMaxAgeMs, sessionPerClient, preserveOrchestrationTags, noLiveCapture, strictTemplate, maxConcurrent, maxQueued, queueTimeoutMs, effort, maxTokens, logFile, passthroughBetas, skipFields, systemPrompt, overageGuardEnabled, overageGuardBehavior, overageGuardCooldownMs, overageGuardNotifyOs });
536
541
  }
537
542
  /**
538
543
  * Parse `--system-prompt=<verbatim|partial|aggressive|filepath>` (or the
@@ -604,6 +609,28 @@ export function parsePassthroughBetasFlag(args, envVar) {
604
609
  }
605
610
  return out;
606
611
  }
612
+ /**
613
+ * Parse `--skip-fields=<csv>` (or DARIO_SKIP_FIELDS env) into a deduped
614
+ * trimmed list of CC body field names. Unrecognized values are passed
615
+ * through; proxy.ts validates against the allowed set at startup and
616
+ * warns on each typo. Same edge-case handling as parsePassthroughBetasFlag.
617
+ */
618
+ export function parseSkipFieldsFlag(args, envVar) {
619
+ const eqArg = args.find((a) => a.startsWith('--skip-fields='));
620
+ const raw = eqArg !== undefined ? eqArg.slice('--skip-fields='.length) : envVar;
621
+ if (!raw)
622
+ return [];
623
+ const seen = new Set();
624
+ const out = [];
625
+ for (const piece of raw.split(',')) {
626
+ const trimmed = piece.trim();
627
+ if (trimmed.length > 0 && !seen.has(trimmed)) {
628
+ seen.add(trimmed);
629
+ out.push(trimmed);
630
+ }
631
+ }
632
+ return out;
633
+ }
607
634
  /**
608
635
  * Parse `--log-file=<path>` or `--log-file <path>`. Returns the path
609
636
  * string when present, undefined otherwise. An empty path (e.g.
@@ -1346,6 +1373,17 @@ async function help() {
1346
1373
  on your account but isn't in the captured
1347
1374
  template. Env: DARIO_PASSTHROUGH_BETAS.
1348
1375
 
1376
+ --skip-fields=CSV CC body fields to NOT inject into outbound
1377
+ requests. Allowed: thinking,
1378
+ context_management, output_config. Headers
1379
+ and metadata stay intact (Max billing
1380
+ unchanged). Use when an upstream model 400s
1381
+ "Extra inputs are not permitted" on one of
1382
+ these fields — typically a non-CC SDK
1383
+ client routed through dario to a model
1384
+ that rejects the field despite the beta
1385
+ header. Env: DARIO_SKIP_FIELDS.
1386
+
1349
1387
  --upstream-proxy=URL / --via=URL
1350
1388
  Route all of dario's outbound fetch
1351
1389
  calls (api.anthropic.com, OpenAI-compat
package/dist/proxy.d.ts CHANGED
@@ -143,6 +143,18 @@ interface ProxyOptions {
143
143
  * pinned flags has been rejected and is therefore being dropped.
144
144
  */
145
145
  passthroughBetas?: string[];
146
+ /**
147
+ * CC body fields to NOT inject into outbound requests. Allowed values:
148
+ * `thinking`, `context_management`, `output_config`. Sourced from
149
+ * `--skip-fields=name1,name2` or `DARIO_SKIP_FIELDS`. Used when an
150
+ * upstream model 400s on a CC-shaped body field with "Extra inputs are
151
+ * not permitted" (observed 2026-05-18 with a non-CC SDK client routed
152
+ * through dario to claude-sonnet-4-6 — `context_management` rejected
153
+ * despite the beta header). Skipping a field leaves all other CC
154
+ * fingerprinting intact (headers, beta flags, metadata, OAuth identity),
155
+ * so Max billing pool routing is unchanged.
156
+ */
157
+ skipFields?: string[];
146
158
  /**
147
159
  * System-prompt mode for the Claude backend. Empirically validated as
148
160
  * unfingerprinted by the billing classifier in docs/research/system-prompt-classifier-study.md.
package/dist/proxy.js CHANGED
@@ -495,6 +495,27 @@ export async function startProxy(opts = {}) {
495
495
  if (passthroughBetas.size > 0) {
496
496
  console.log(` Beta passthrough: ${[...passthroughBetas].sort().join(', ')} (always forwarded; per-account rejection cache still applies)`);
497
497
  }
498
+ // CC body fields to suppress. Allowed values: thinking, context_management,
499
+ // output_config. Anything else is silently ignored after a warn (so a typo
500
+ // doesn't quietly disable nothing). See ProxyOptions.skipFields.
501
+ const ALLOWED_SKIP_FIELDS = new Set(['thinking', 'context_management', 'output_config']);
502
+ const skipFields = new Set();
503
+ for (const raw of [
504
+ ...(opts.skipFields ?? []),
505
+ ...((process.env.DARIO_SKIP_FIELDS ?? '').split(',')),
506
+ ]) {
507
+ const trimmed = raw.trim();
508
+ if (trimmed.length === 0)
509
+ continue;
510
+ if (!ALLOWED_SKIP_FIELDS.has(trimmed)) {
511
+ console.warn(`[dario] WARNING: --skip-fields value ${JSON.stringify(trimmed)} is not recognized; ignoring. Allowed: ${[...ALLOWED_SKIP_FIELDS].join(', ')}.`);
512
+ continue;
513
+ }
514
+ skipFields.add(trimmed);
515
+ }
516
+ if (skipFields.size > 0) {
517
+ console.log(` Skip CC body fields: ${[...skipFields].sort().join(', ')} (omitted from outbound non-haiku requests; headers and metadata unchanged)`);
518
+ }
498
519
  // Tool-routing mode mutex. preserve / hybrid / merge each shape the
499
520
  // outbound `tools` array differently; combining two would mean two
500
521
  // different bodies. Refuse to start with a clear error rather than
@@ -1369,6 +1390,7 @@ export async function startProxy(opts = {}) {
1369
1390
  effort: opts.effort,
1370
1391
  maxTokens: opts.maxTokens,
1371
1392
  systemPrompt: opts.systemPrompt,
1393
+ skipFields,
1372
1394
  });
1373
1395
  detectedClientForLog = detectedClient;
1374
1396
  preserveToolsEffective = Boolean(opts.preserveTools)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askalf/dario",
3
- "version": "4.7.1",
3
+ "version": "4.8.0",
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": {