@askalf/dario 3.9.5 → 3.9.6

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.
@@ -44,6 +44,14 @@ export interface ToolMapping {
44
44
  * into fields CC's schema doesn't carry. Unset in default mode.
45
45
  */
46
46
  clientFields?: string[];
47
+ /**
48
+ * Reverse-lookup priority for resolving collisions when multiple client
49
+ * tools map to the same CC tool. Higher wins. Default 10. Set lower for
50
+ * niche / lossy translations (e.g. OpenClaw's `process` action-discriminator
51
+ * tool loses most of its schema when flattened to Bash, so bash/exec
52
+ * should win the Bash reverse slot when both are declared — dario#37).
53
+ */
54
+ reverseScore?: number;
47
55
  }
48
56
  /**
49
57
  * Request context extracted once per incoming request. Source for
@@ -141,10 +141,17 @@ const TOOL_MAP = {
141
141
  // so the model upstream can't actually drive it. Kept mapped for fingerprint
142
142
  // continuity but the reverse translation is inherently lossy — clients with a
143
143
  // process-style tool should use --preserve-tools instead of --hybrid-tools.
144
+ //
145
+ // reverseScore: 1 makes sure that when a client declares BOTH `process` AND
146
+ // `exec`/`bash` (OpenClaw does — both are exported from bash-tools.ts), the
147
+ // reverse lookup picks the bash-family mapping for CC's Bash tool slot
148
+ // instead of routing CC tool calls through process's action-based shape
149
+ // and breaking every Bash call with "Unknown action" (dario#37).
144
150
  process: {
145
151
  ccTool: 'Bash',
146
152
  translateArgs: (a) => ({ command: a.action || a.cmd || '' }),
147
153
  translateBack: (a) => ({ action: a.command ?? '' }),
154
+ reverseScore: 1,
148
155
  },
149
156
  read: {
150
157
  ccTool: 'Read',
@@ -499,11 +506,22 @@ export function buildCCRequest(clientBody, billingTag, cache1h, identity, opts =
499
506
  }
500
507
  /**
501
508
  * Build the CC-name → {clientName, mapping} reverse lookup used by both
502
- * the non-streaming and streaming reverse-mappers. Two-pass construction
503
- * preserves the original identity-protection rule: when a client sent a
504
- * tool with the literal CC name (e.g. `WebSearch`), that pairing claims
505
- * the CC slot first so a later unmapped-tool fallback that also lands
506
- * on `WebSearch` can't overwrite it.
509
+ * the non-streaming and streaming reverse-mappers.
510
+ *
511
+ * Two-pass construction preserves the original identity-protection rule:
512
+ * when a client sent a tool with the literal CC name (e.g. `WebSearch`),
513
+ * that pairing claims the CC slot first so a later unmapped-tool fallback
514
+ * that also lands on `WebSearch` can't overwrite it.
515
+ *
516
+ * Within the non-identity pass, collisions are broken by `reverseScore`
517
+ * (higher wins, default 10). This matters when a client declares two
518
+ * tools that both map to the same CC tool — OpenClaw declares both
519
+ * `exec` (bash-like, score 10) and `process` (action-discriminator,
520
+ * score 1) and both map to Bash. Pre-fix, insertion-order last-wins
521
+ * routed Bash tool calls through `process`, which interpreted the
522
+ * command string as an action and returned "Unknown action" for
523
+ * every call. `process` now has reverseScore: 1 so bash/exec wins
524
+ * (dario#37).
507
525
  */
508
526
  function buildReverseLookup(toolMap) {
509
527
  const reverseMap = new Map();
@@ -514,12 +532,17 @@ function buildReverseLookup(toolMap) {
514
532
  reverseMap.set(mapping.ccTool, { clientName, mapping });
515
533
  }
516
534
  }
535
+ // Score-based collision resolution in the non-identity pass.
536
+ const scoreOf = (m) => m.reverseScore ?? 10;
517
537
  for (const [clientName, mapping] of toolMap) {
518
538
  if (clientName.toLowerCase() === mapping.ccTool.toLowerCase())
519
539
  continue;
520
540
  if (identityClaimed.has(mapping.ccTool))
521
541
  continue;
522
- reverseMap.set(mapping.ccTool, { clientName, mapping });
542
+ const existing = reverseMap.get(mapping.ccTool);
543
+ if (!existing || scoreOf(mapping) > scoreOf(existing.mapping)) {
544
+ reverseMap.set(mapping.ccTool, { clientName, mapping });
545
+ }
523
546
  }
524
547
  return reverseMap;
525
548
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askalf/dario",
3
- "version": "3.9.5",
3
+ "version": "3.9.6",
4
4
  "description": "A local LLM router. One endpoint, every provider — Claude subscriptions, OpenAI, OpenRouter, Groq, local LiteLLM, any OpenAI-compat endpoint — your tools don't need to change.",
5
5
  "type": "module",
6
6
  "bin": {