@askalf/dario 3.9.4 → 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.
- package/dist/cc-template.d.ts +8 -0
- package/dist/cc-template.js +68 -16
- package/package.json +1 -1
package/dist/cc-template.d.ts
CHANGED
|
@@ -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
|
package/dist/cc-template.js
CHANGED
|
@@ -100,40 +100,58 @@ function injectContextFields(input, clientFields, ctx) {
|
|
|
100
100
|
}
|
|
101
101
|
const TOOL_MAP = {
|
|
102
102
|
// Direct maps
|
|
103
|
+
// Note on translateBack field names: the vast majority of client bash-like
|
|
104
|
+
// tools use `command` (the Anthropic convention), not `cmd`. OpenClaw's
|
|
105
|
+
// `exec` tool takes `{command, workdir, env, ...}` (dario#36 triage).
|
|
106
|
+
// Hybrid mode overrides these with the actual client schema via clientFields,
|
|
107
|
+
// but default mode relies on these output names being the common case.
|
|
103
108
|
bash: {
|
|
104
109
|
ccTool: 'Bash',
|
|
105
110
|
translateArgs: (a) => ({ command: a.cmd || a.command || a.c || '' }),
|
|
106
|
-
translateBack: (a) => ({
|
|
111
|
+
translateBack: (a) => ({ command: a.command ?? '' }),
|
|
107
112
|
},
|
|
108
113
|
exec: {
|
|
109
114
|
ccTool: 'Bash',
|
|
110
115
|
translateArgs: (a) => ({ command: a.cmd || a.command || a.c || '' }),
|
|
111
|
-
translateBack: (a) => ({
|
|
116
|
+
translateBack: (a) => ({ command: a.command ?? '' }),
|
|
112
117
|
},
|
|
113
118
|
shell: {
|
|
114
119
|
ccTool: 'Bash',
|
|
115
120
|
translateArgs: (a) => ({ command: a.cmd || a.command || a.c || '' }),
|
|
116
|
-
translateBack: (a) => ({
|
|
121
|
+
translateBack: (a) => ({ command: a.command ?? '' }),
|
|
117
122
|
},
|
|
118
123
|
run: {
|
|
119
124
|
ccTool: 'Bash',
|
|
120
125
|
translateArgs: (a) => ({ command: a.cmd || a.command || '' }),
|
|
121
|
-
translateBack: (a) => ({
|
|
126
|
+
translateBack: (a) => ({ command: a.command ?? '' }),
|
|
122
127
|
},
|
|
123
128
|
command: {
|
|
124
129
|
ccTool: 'Bash',
|
|
125
130
|
translateArgs: (a) => ({ command: a.cmd || a.command || '' }),
|
|
126
|
-
translateBack: (a) => ({
|
|
131
|
+
translateBack: (a) => ({ command: a.command ?? '' }),
|
|
127
132
|
},
|
|
128
133
|
terminal: {
|
|
129
134
|
ccTool: 'Bash',
|
|
130
135
|
translateArgs: (a) => ({ command: a.cmd || a.command || '' }),
|
|
131
|
-
translateBack: (a) => ({
|
|
136
|
+
translateBack: (a) => ({ command: a.command ?? '' }),
|
|
132
137
|
},
|
|
138
|
+
// `process` is OpenClaw's session-manager tool — it's an action-discriminator
|
|
139
|
+
// shape {action: "list"|"poll"|"log"|..., sessionId?, ...}. Flattening it onto
|
|
140
|
+
// Bash.command loses all sibling fields (data, keys, hex, literal, text, ...),
|
|
141
|
+
// so the model upstream can't actually drive it. Kept mapped for fingerprint
|
|
142
|
+
// continuity but the reverse translation is inherently lossy — clients with a
|
|
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).
|
|
133
150
|
process: {
|
|
134
151
|
ccTool: 'Bash',
|
|
135
152
|
translateArgs: (a) => ({ command: a.action || a.cmd || '' }),
|
|
136
153
|
translateBack: (a) => ({ action: a.command ?? '' }),
|
|
154
|
+
reverseScore: 1,
|
|
137
155
|
},
|
|
138
156
|
read: {
|
|
139
157
|
ccTool: 'Read',
|
|
@@ -307,16 +325,34 @@ export function buildCCRequest(clientBody, billingTag, cache1h, identity, opts =
|
|
|
307
325
|
claimedCC.add(mapping.ccTool);
|
|
308
326
|
}
|
|
309
327
|
}
|
|
328
|
+
// Unmapped-tool handling differs by mode:
|
|
329
|
+
//
|
|
330
|
+
// - Default mode: round-robin to CC fallback tools. The model sees the CC
|
|
331
|
+
// tool set, any tool call is "something", and we best-effort relay it
|
|
332
|
+
// back to the client tool name. Broken-by-design for clients with rich
|
|
333
|
+
// discriminator tools (OpenClaw lobster/memory_get, dario#36), but
|
|
334
|
+
// preserves the old behavior for simple clients that don't have many
|
|
335
|
+
// unmapped tools.
|
|
336
|
+
//
|
|
337
|
+
// - Hybrid mode: DROP unmapped tools entirely. We can't forward them to
|
|
338
|
+
// the upstream (adding to CC_TOOL_DEFINITIONS breaks the fingerprint),
|
|
339
|
+
// and round-robin mapping produces nonsense shapes on the reverse path
|
|
340
|
+
// (lobster.translateBack(Glob.input) → {pattern: "..."} when lobster
|
|
341
|
+
// wants {action: "run"}). Better to let the model not see those tools
|
|
342
|
+
// than to pretend they exist and corrupt every call. Users needing
|
|
343
|
+
// every client tool to actually work must use --preserve-tools.
|
|
310
344
|
const CC_FALLBACK_TOOLS = ['Bash', 'Read', 'Grep', 'Glob', 'WebSearch', 'WebFetch'];
|
|
311
345
|
for (const tool of clientTools) {
|
|
312
346
|
const name = (tool.name || '').toLowerCase();
|
|
313
347
|
if (TOOL_MAP[name])
|
|
314
348
|
continue;
|
|
315
349
|
unmappedTools.push(tool.name);
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
//
|
|
319
|
-
//
|
|
350
|
+
if (opts.hybridTools)
|
|
351
|
+
continue; // dropped — see comment above
|
|
352
|
+
// Default mode: round-robin distribution. Exclude CC tools the client
|
|
353
|
+
// already uses so we never create a two-client-names-to-one-CC-tool
|
|
354
|
+
// collision. If every fallback is claimed (rare: client already uses 6+
|
|
355
|
+
// CC tools), fall back to the full pool and accept the ambiguity.
|
|
320
356
|
const pool = CC_FALLBACK_TOOLS.filter(t => !claimedCC.has(t));
|
|
321
357
|
const fallbackPool = pool.length > 0 ? pool : CC_FALLBACK_TOOLS;
|
|
322
358
|
const fallbackTool = fallbackPool[(unmappedTools.length - 1) % fallbackPool.length];
|
|
@@ -470,11 +506,22 @@ export function buildCCRequest(clientBody, billingTag, cache1h, identity, opts =
|
|
|
470
506
|
}
|
|
471
507
|
/**
|
|
472
508
|
* Build the CC-name → {clientName, mapping} reverse lookup used by both
|
|
473
|
-
* the non-streaming and streaming reverse-mappers.
|
|
474
|
-
*
|
|
475
|
-
*
|
|
476
|
-
*
|
|
477
|
-
*
|
|
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).
|
|
478
525
|
*/
|
|
479
526
|
function buildReverseLookup(toolMap) {
|
|
480
527
|
const reverseMap = new Map();
|
|
@@ -485,12 +532,17 @@ function buildReverseLookup(toolMap) {
|
|
|
485
532
|
reverseMap.set(mapping.ccTool, { clientName, mapping });
|
|
486
533
|
}
|
|
487
534
|
}
|
|
535
|
+
// Score-based collision resolution in the non-identity pass.
|
|
536
|
+
const scoreOf = (m) => m.reverseScore ?? 10;
|
|
488
537
|
for (const [clientName, mapping] of toolMap) {
|
|
489
538
|
if (clientName.toLowerCase() === mapping.ccTool.toLowerCase())
|
|
490
539
|
continue;
|
|
491
540
|
if (identityClaimed.has(mapping.ccTool))
|
|
492
541
|
continue;
|
|
493
|
-
reverseMap.
|
|
542
|
+
const existing = reverseMap.get(mapping.ccTool);
|
|
543
|
+
if (!existing || scoreOf(mapping) > scoreOf(existing.mapping)) {
|
|
544
|
+
reverseMap.set(mapping.ccTool, { clientName, mapping });
|
|
545
|
+
}
|
|
494
546
|
}
|
|
495
547
|
return reverseMap;
|
|
496
548
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askalf/dario",
|
|
3
|
-
"version": "3.9.
|
|
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": {
|