@askalf/dario 5.4.15 → 5.4.17

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.
@@ -0,0 +1,269 @@
1
+ # Agent compatibility
2
+
3
+ Dario's built-in `TOOL_MAP` carries **66 schema-verified entries** covering the tool schemas of every major coding agent. On the Claude backend, tool calls translate to CC's native `Bash / Read / Write / Edit / Glob / Grep / WebSearch / WebFetch` on the outbound path (so the request stays on the subscription wire shape) and rebuild to your agent's exact expected shape on the inbound path (so your validator is happy). No flag required.
4
+
5
+ For a one-page status table of every tool dario supports — working / inferred / untested — see [`compat-matrix.md`](./compat-matrix.md). This page covers per-tool setup; the matrix covers "does it work?" at a glance.
6
+
7
+ | Agent | Covered tool names (subset) |
8
+ |---|---|
9
+ | Claude Code / Claude Agent SDK | default — CC / SDK tools (same schema as of CC v2.1.114 / `@anthropic-ai/claude-agent-sdk@0.2.x`) |
10
+ | Cline / Roo Code / Kilo Code | `execute_command`, `write_to_file`, `replace_in_file`, `apply_diff`, `list_files`, `search_files`, `read_file` |
11
+ | Cursor | `run_terminal_cmd`, `edit_file`, `search_replace`, `codebase_search`, `grep_search`, `file_search`, `list_dir`, `read_file` (`target_file`) |
12
+ | Windsurf | `run_command`, `view_file`, `write_to_file`, `replace_file_content`, `find_by_name`, `grep_search`, `list_dir`, `search_web`, `read_url_content` |
13
+ | Continue.dev | `builtin_run_terminal_command`, `builtin_read_file`, `builtin_create_new_file`, `builtin_edit_existing_file`, `builtin_file_glob_search`, `builtin_grep_search`, `builtin_ls` |
14
+ | GitHub Copilot | `run_in_terminal`, `insert_edit_into_file`, `semantic_search`, `codebase_search`, `list_dir`, `fetch_webpage` |
15
+ | OpenHands | `execute_bash`, `str_replace_editor` |
16
+ | OpenClaw | `exec`, `process`, `web_search`, `web_fetch`, `browser`, `message` |
17
+ | hands ([askalf/hands](https://github.com/askalf/hands)) | Anthropic beta computer-use tools (`computer`, `bash`, `str_replace_based_edit_tool`) — auto-preserved via system-prompt identity match (v3.33.0) |
18
+ | Hermes Agent (Nous Research) | `terminal`, `process`, `read_file`, `write_file`, `patch`, `search_files`, `web_search`, `web_extract`, `todo` mapped directly. Hermes-specific tools (`browser_*`, `vision_analyze`, `image_generate`, `skill_*`, `memory`, `session_search`, `cronjob`, `send_message`, `ha_*`, `mixture_of_agents`, `delegate_task`, `execute_code`, `text_to_speech`) have no CC equivalent and auto-preserve through the identity detector. Also consider `--max-tokens=client` so Hermes's 64k/128k per-model caps survive dario's outbound pin. |
19
+
20
+ Text-tool clients (Cline / Kilo Code / Roo Code and forks) are auto-detected via system-prompt identity markers and automatically flipped into preserve-tools mode, because mixing CC's `tools` array with their XML protocol makes the model emit `<function_calls><invoke>` that their parsers can't read. The same identity path also catches `hands` (askalf's computer-use agent) — its tool names overlap with `TOOL_MAP` but its schemas diverge, so identity match → preserve-tools is the only correct routing. If you run dario specifically for wire-level fidelity and would rather pick `--preserve-tools` yourself, `--no-auto-detect` (v3.20.1, aka `--no-auto-preserve`) disables the heuristic — explicit operator choice then wins.
21
+
22
+ Beyond the identity path, dario falls back to a **structural** check: when a request carries 3+ tools and ≥80% of them aren't in `TOOL_MAP`, that's a custom client whose tool surface has effectively no overlap with CC's, and round-robin remap onto CC fallback slots silently corrupts the calls. The structural fallback flips those requests to preserve-tools too, with `client: 'unknown-non-cc'` in the request log. This catches in-house agents and OpenClaw derivatives that we haven't added an explicit pattern for, without needing per-client maintenance. `--no-auto-detect` disables both paths.
23
+
24
+ Platform-scoped CC natives (`PowerShell`, `Glob`, `Grep` — win32-only in CC's own tool list) map by the **client's** declaration, not the proxy host's platform (v4.8.136): a win32 CC client through a Linux-hosted dario gets their canonical definitions instead of having them dropped from the advertised array.
25
+
26
+ MCP tools take a third path: anything named `mcp__<server>__<tool>` is neither remapped nor flipped to preserve — it forwards **verbatim** and its calls flow back untouched (v4.8.135). That's what real CC does with session-attached MCP servers; their schemas are operator-supplied and have no canonical template entry, so passthrough *is* the CC wire shape. Before this, a CC session with an MCP server attached had its `mcp__*` surface dropped from the advertised array and its history references round-robined onto fallback slots (`tool substitution: 28/52 client tools not in TOOL_MAP` in the live log), and two or three attached servers were enough to trip the 80% structural threshold on their own. MCP names no longer count toward that threshold.
27
+
28
+ If your agent's tool names aren't pre-mapped and its tools carry fields CC's schema doesn't have, there are two escape hatches: **`--preserve-tools`** (forward your schema verbatim, lose the CC wire shape) or **`--hybrid-tools`** (keep the CC wire shape, fill request-context fields from headers). See [Custom tool schemas](#custom-tool-schemas).
29
+
30
+ The OpenAI-compat backend forwards tool definitions byte-for-byte and doesn't need any of this.
31
+
32
+ ## Per-tool setup
33
+
34
+ ### Cursor
35
+
36
+ > **⚠️ Architectural mismatch (read this before configuring)**
37
+ >
38
+ > Cursor's BYOK is **backend-mediated**, not client-side. When you set "Override OpenAI Base URL" in Cursor, the Electron app sends that URL up to Cursor's own backend (`api2.cursor.sh`), and **Cursor's servers** make the outbound LLM call — not your machine. Their backend has an SSRF (Server-Side Request Forgery) guard that rejects RFC1918 + loopback addresses by design, so `http://localhost:3456` is structurally unreachable. The error surfaces as either `Provider returned error: Access to private networks is forbidden` (older form) or `{"error":{"type":"client","reason":"ssrf_blocked","message":"connection to private IP is blocked"}}` (current form).
39
+ >
40
+ > Confirmed by Cursor staff in their own words across multiple forum threads — Colin, Feb 9 2026: *"we have SSRF (Server-Side Request Forgery) protection that blocks connections to private/internal IP ranges"* ([thread](https://forum.cursor.com/t/cannot-connect-to-self-hosted-llm)); Dean Rie, Jan 20 2026: *"BYOK API keys work through Cursor's backend. All requests go through our servers"* ([thread](https://forum.cursor.com/t/use-on-prem-model/149334)). No fix ETA. dario#190 + every other local-proxy project (e.g. [mergd/ccproxy](https://github.com/mergd/ccproxy)) hits the same wall.
41
+ >
42
+ > **The simple path:** if you want a frictionless setup, use Claude Code, Continue.dev, OpenHands, Aider, Cline, or Zed instead (sections below). Those clients make the outbound call from your machine, so `http://localhost:3456` Just Works — no tunnel required.
43
+ >
44
+ > **The Cursor path:** expose dario behind a public HTTPS tunnel (cloudflared, ngrok, etc.) so Cursor's backend can reach it. Walkthrough below.
45
+
46
+ #### 1. Expose dario via a public HTTPS tunnel
47
+
48
+ In two terminals:
49
+
50
+ ```bash
51
+ # Terminal 1 — dario as usual
52
+ dario proxy --verbose
53
+
54
+ # Terminal 2 — cloudflared quick tunnel (free, no signup)
55
+ cloudflared tunnel --url http://localhost:3456
56
+ # → prints something like https://random-words-here.trycloudflare.com
57
+ ```
58
+
59
+ Copy the `https://...trycloudflare.com` URL — you'll paste it into Cursor next.
60
+
61
+ > **🔐 The tunnel URL is a credential.** `.trycloudflare.com` URLs are unauthenticated by default — anyone who learns the URL can spend your Claude subscription against it. Random subdomains keep casual exposure low-risk, but **don't paste it publicly, and kill the tunnel when you're done.** For anything beyond a quick test:
62
+ > - ngrok with `--basic-auth` or a reserved domain + auth, or
63
+ > - Named cloudflared tunnels behind [Cloudflare Access](https://developers.cloudflare.com/cloudflare-one/applications/configure-apps/self-hosted-public-app/) policies, or
64
+ > - dario behind your own VPS reverse proxy with TLS + auth.
65
+
66
+ #### 2. Configure Cursor
67
+
68
+ **Cmd/Ctrl + ,** → **Models**. Under the **OpenAI API Key** section:
69
+
70
+ - Check **Override OpenAI Base URL**: `https://random-words-here.trycloudflare.com/v1` *(your tunnel URL + `/v1`; the checkbox must be enabled, not just the field populated)*
71
+ - API key: `dario`
72
+ - *(Recent Cursor versions removed the explicit "Verify" button — the green toggle on its own is sufficient.)*
73
+
74
+ #### 3. Add models — use `anthropic:` (not `claude:`) to keep tool-format intact
75
+
76
+ Two name gotchas to dodge in this step. Both are about Cursor's behavior, not dario's.
77
+
78
+ **Gotcha A — built-in name collision.** Cursor recognizes any model name it ships natively (e.g., `claude-opus-4-7`, `claude-sonnet-4-6`, `claude-haiku-4-5`, `gpt-5`, `gpt-4o`). Add one of those raw and Cursor pops a *"this model is already available as Opus 4.7"* toast and silently routes it through **its own** Anthropic gateway — billing your Cursor API credits, never reaching the override URL. The Override OpenAI Base URL only takes effect for model names Cursor does **not** recognize as built-ins.
79
+
80
+ **Gotcha B — Anthropic-format-switcher (matters for Agent mode).** Cursor inspects the model-name string and switches its outbound tool-call format based on substring match. From Cursor staff Dean Rie: *"When Cursor sees a model name like `claude-*`, it switches to a Claude-specific tool-calling format, which isn't compatible with OpenAI-compatible API endpoints"* ([forum thread](https://forum.cursor.com/t/using-byok-in-agent-mode-with-claude-opus-4-5-not-apply-to-file/148018)). So `claude:claude-opus-4-7` (or any name containing `claude-`) makes Cursor send Anthropic-shape tool blocks to the OpenAI-compat `/v1/chat/completions` endpoint — dario's OpenAI-compat handler can't parse those, the model receives a confused tool surface, and you get text-form tool calls in the response instead of structured edits. dario#190 is the canonical case.
81
+
82
+ Use the [provider prefix](./usage.md#provider-prefix) form that dodges **both** gotchas:
83
+
84
+ - **Claude** — `anthropic:opus`, `anthropic:sonnet`, `anthropic:haiku` (or full IDs: `anthropic:claude-opus-5` / `anthropic:claude-sonnet-5` / `anthropic:claude-haiku-4-5`). The `anthropic:` prefix routes through dario's Claude backend identically to `claude:`, but the visible model name doesn't contain the `claude-` substring, so Cursor ships OpenAI-shape `tool_calls` and dario's translator handles them cleanly.
85
+ - **OpenAI** *(if you've run `dario backend add openai --key=sk-...`)* — `openai:gpt-4o`, `openai:gpt-5`, `openai:o1`, etc. The `openai:` prefix dodges Cursor's `gpt-*` collision the same way.
86
+ - **Other OpenAI-compat backends** *(Groq, OpenRouter, local LiteLLM, Ollama, etc.)* — `groq:llama-3.3-70b`, `openrouter:moonshotai/kimi-k2`, `local:qwen-coder-32b`, etc.
87
+
88
+ dario v3.36+ resolves `anthropic:fable`/`opus`/`sonnet`/`haiku` (and `fable1m`/`opus1m`/`sonnet1m`) shortcuts to canonical Anthropic model IDs at request time. Older dario versions (≤ v3.35) need the full canonical form: `anthropic:claude-opus-4-7` etc.
89
+
90
+ > **Older docs / muscle memory note:** earlier versions of this guide recommended the `claude:` prefix. That works fine on tool-less Chat (Gotcha B doesn't fire when no tools are sent) but breaks Agent mode. Prefer `anthropic:` going forward — it's drop-in compatible with every dario version that supports `claude:`.
91
+
92
+ Select one of the registered models in Cursor's model picker.
93
+
94
+ #### 4. Use **Agent mode**, not Chat — Chat doesn't pass tools to BYOK models
95
+
96
+ Cursor's surfaces handle tools differently:
97
+
98
+ | Surface | Shortcut | Tools forwarded to BYOK? |
99
+ |---|---|---|
100
+ | Chat (right-pane chat tab) | — | No — chat-only, no `tools` array sent |
101
+ | Agent / Composer (Cmd-I) | **Cmd-I** (Mac) / **Ctrl-I** (Windows/Linux) | **Yes** — full `tools` array sent in OpenAI function-calling format |
102
+ | Tab Apply (autocomplete) | — | First-party model, BYOK ignored |
103
+ | Cmd-K (inline) | Cmd-K / Ctrl-K | Variable; uses its own model selection |
104
+
105
+ If you point dario at the **Chat** surface, the request body has no `tools` array, but dario still replays Claude Code's full system prompt (which tells the model "you have Bash/Read/Write/Edit/Grep/Glob…") — the model improvises by narrating tool calls in plain text. Same root cause as the *"system instructs me to default to no comments…"* leak: the model is decision-narrating because the wire shape it expected (full agent harness) doesn't match what arrived (plain chat with no tools).
106
+
107
+ **For agent-style work, open Cmd-I / Ctrl-I (Agent / Composer pane), not the Chat tab.** Pick one of the `anthropic:*` models in the picker and send your request. dario's logs should show the request/response cycle for each tool call, with `tool_use` blocks translated to OpenAI `tool_calls` on the way back to Cursor.
108
+
109
+ #### 5. Verify
110
+
111
+ With `dario proxy --verbose` running, send a test message in Cursor's **Agent** pane. You should see:
112
+
113
+ - A `provider prefix: anthropic:opus → claude backend with model claude-opus-5` line in dario's logs
114
+ - One or more `POST /v1/chat/completions` lines per turn (one per tool round-trip)
115
+ - An incremented request count in `dario doctor --usage`
116
+
117
+ If dario's logs stay silent and `Usage 5h (all)` stays at `0.0%`, the request never reached the tunnel. Three likely causes:
118
+
119
+ - **`Access to private networks is forbidden` / `ssrf_blocked` error in Cursor** — you pasted the `localhost:3456` URL, not the tunnel URL. Check step 2.
120
+ - **Cursor's name-collision toast fired** — the model name you added matches a Cursor built-in (Gotcha A). Use the `anthropic:` form (step 3).
121
+ - **You're testing in Chat, not Agent** — open Cmd-I / Ctrl-I and test there (step 4).
122
+
123
+ If logs show traffic but the model emits text-form tool calls (`Tool: Read\n{"file_path":...}`) instead of structured calls, you're hitting Gotcha B — your model name still contains `claude-`. Switch to `anthropic:opus` etc. (step 3).
124
+
125
+ **Why no "Override Anthropic Base URL"?** Cursor doesn't have one. There's a [year-old open feature request](https://forum.cursor.com/t/missing-anthropic-base-url-override-in-cursor-byok/158805) and no plans to ship it. Routing Claude through dario is only possible via the OpenAI-compat path with a prefixed model name as above.
126
+
127
+ ### Continue.dev
128
+
129
+ In `~/.continue/config.yaml` (or the Continue settings UI, which edits the same file):
130
+
131
+ ```yaml
132
+ models:
133
+ - name: Claude Sonnet (dario)
134
+ provider: anthropic
135
+ model: claude-sonnet-5
136
+ apiBase: http://localhost:3456
137
+ apiKey: dario
138
+ - name: Claude Opus (dario)
139
+ provider: anthropic
140
+ model: claude-opus-5
141
+ apiBase: http://localhost:3456
142
+ apiKey: dario
143
+ ```
144
+
145
+ `provider: anthropic` + `apiBase: http://localhost:3456` points Continue's Anthropic SDK path at dario instead of `api.anthropic.com`. dario runs the full Claude Code wire replay on the outbound path.
146
+
147
+ ### Aider
148
+
149
+ ```bash
150
+ export ANTHROPIC_BASE_URL=http://localhost:3456
151
+ export ANTHROPIC_API_KEY=dario
152
+ aider --model sonnet
153
+ ```
154
+
155
+ Aider's Anthropic path honors `ANTHROPIC_BASE_URL` directly. `--model opus`, `--model haiku`, or any explicit `claude-*` model name works.
156
+
157
+ ### Cline / Roo Code / Kilo Code
158
+
159
+ Cline and its forks use a UI-based "API Provider" dropdown. Pick **Anthropic** as the provider and fill in:
160
+
161
+ - **API Key**: `dario`
162
+ - **Anthropic Base URL**: `http://localhost:3456`
163
+ - **Model**: `claude-sonnet-5` / `claude-opus-5` / `claude-haiku-4-5`
164
+
165
+ Cline's tool-invocation protocol is XML-based (`<execute_command>`, `<write_to_file>`, etc.), not Anthropic's tool-use format. Dario auto-detects Cline-family clients via system-prompt identity markers and flips into preserve-tools mode automatically — Cline's own tool schema passes through, your commands route back to Cline's parser. No flag required. Override: `--no-auto-detect` if you'd rather force the CC wire shape and deal with the parser mismatch yourself.
166
+
167
+ ### Zed
168
+
169
+ Zed's Anthropic provider config (`~/.config/zed/settings.json` or Cmd/Ctrl+,):
170
+
171
+ ```json
172
+ {
173
+ "language_models": {
174
+ "anthropic": {
175
+ "api_url": "http://localhost:3456",
176
+ "version": "2023-06-01"
177
+ }
178
+ }
179
+ }
180
+ ```
181
+
182
+ Set the `ANTHROPIC_API_KEY` env var to `dario` before launching Zed. Model picker then shows Claude models routed through your subscription.
183
+
184
+ ### OpenHands
185
+
186
+ ```bash
187
+ export LLM_BASE_URL=http://localhost:3456
188
+ export LLM_API_KEY=dario
189
+ export LLM_MODEL=anthropic/claude-sonnet-4-6
190
+ openhands --task "task description"
191
+ ```
192
+
193
+ Prefix the model with `anthropic/` so LiteLLM (OpenHands' inner routing layer) knows to hit the Anthropic path, which dario is now fronting.
194
+
195
+ For a full end-to-end walkthrough — install, battletested model picks, subscription-billing verification, retries, multi-account pool, and the gotchas that bite first-time users — see [`openhands-walkthrough.md`](./openhands-walkthrough.md).
196
+
197
+ ### OpenClaw
198
+
199
+ ```bash
200
+ export ANTHROPIC_BASE_URL=http://localhost:3456
201
+ export ANTHROPIC_API_KEY=dario
202
+ openclaw "task description"
203
+ ```
204
+
205
+ OpenClaw uses the standard `ANTHROPIC_BASE_URL` and `ANTHROPIC_API_KEY` env vars. Dario's structural-fallback tool detection auto-translates OpenClaw's `exec` / `process` / `web_search` / `web_fetch` / `browser` / `message` tools to CC's canonical set — no flag required.
206
+
207
+ **Heads up:** OpenClaw 2026.2.17+ reads `~/.openclaw/agents/main/agent/auth-profiles.json` before checking env vars, so a stale Anthropic key in that file silently overrides `ANTHROPIC_API_KEY=dario`. If you see 401s, see the [auth-profiles entry in faq.md](./faq.md#openclaw-returns-401-after-i-set-dario_api_key-or-upgrade-past-v3306). Dario's default template-replay mode also strips the `openclaw.inbound_meta.v1` classifier-trigger string from your local git context at the proxy boundary, so subscription billing is preserved on OpenClaw-namespaced projects without you doing anything.
208
+
209
+ For a full end-to-end walkthrough — auth-profiles handling, classifier-filter protection, subscription-billing verification, multi-account pool, and the gotchas that bite first-time users — see [`openclaw-walkthrough.md`](./openclaw-walkthrough.md).
210
+
211
+ ### hands
212
+
213
+ [hands](https://github.com/askalf/hands) is a sister project to dario — a local computer-use agent that drives your OS through its native shell instead of a screenshot loop. Two modes: Claude Login (uses the `claude` CLI directly, no dario required) and SDK mode (audit-logged, supports `--dry-run`, routes through dario for $0 per task).
214
+
215
+ ```bash
216
+ # SDK mode — env vars route the Anthropic SDK through dario
217
+ export ANTHROPIC_BASE_URL=http://localhost:3456
218
+ export ANTHROPIC_API_KEY=dario
219
+
220
+ dario proxy --verbose &
221
+ hands auth # pick "API Key", paste: dario
222
+ hands run "open notepad and type hello world"
223
+ ```
224
+
225
+ Dario v3.33.0+ auto-detects hands via system-prompt identity match and **preserves the Anthropic computer-use beta tools** (`computer`, `bash`, `str_replace_based_edit_tool`) instead of remapping them. No flag required — the `anthropic-beta: computer-use-*` header survives the proxy and the wire shape stays subscription-eligible.
226
+
227
+ For the full end-to-end walkthrough — both auth modes, audit log, dry-run patterns, voice mode, multi-account pool, and the gotchas that bite first-time users — see [`hands-walkthrough.md`](./hands-walkthrough.md).
228
+
229
+ ### Everything else
230
+
231
+ If your tool isn't listed, check whether it reads `OPENAI_BASE_URL` / `ANTHROPIC_BASE_URL` from the environment. Most do. For tools that don't, look in their settings for "Base URL" / "API URL" / "Endpoint" / "OpenAI-compatible endpoint" — all of those map to dario's `http://localhost:3456` (Anthropic-protocol) or `http://localhost:3456/v1` (OpenAI-protocol). If the tool only accepts `https://`, you'll need a loopback TLS shim (out of scope here — open an issue if you need one for a specific tool).
232
+
233
+ ## Custom tool schemas
234
+
235
+ By default, on the Claude backend, dario replaces your client's tool definitions with the real Claude Code tools (`Bash`, `Read`, `Write`, `Edit`, `Grep`, `Glob`, `WebSearch`, `WebFetch`) and translates parameters back and forth. That's what keeps the request on the CC wire shape, which is what keeps the session on subscription billing instead of per-token API pricing. For the agents listed in the table above, the translation is pre-mapped and runs automatically — nothing to configure.
236
+
237
+ The trade-off shows up when you're running something that *isn't* in the pre-mapped list and whose tools carry fields CC's schema doesn't have — a `sessionId`, a custom request id, a channel-bound context token, a `confidence` score the model is supposed to emit. Those fields don't survive the round trip.
238
+
239
+ Symptom: your tool calls come back looking stripped-down, or your runtime complains about a required field being absent *only when routed through dario's Claude backend*.
240
+
241
+ Fix: run dario with `--preserve-tools`. That skips the CC tool remap entirely, passes your client's tool definitions through to the model unchanged, and lets the model populate every field your schema expects.
242
+
243
+ ```bash
244
+ dario proxy --preserve-tools
245
+ ```
246
+
247
+ The cost: requests no longer look like CC on the wire, so the subscription-billing wire shape is gone. On a subscription plan, that means the request may be counted against your API usage rather than your subscription quota. Hybrid tool mode below is the compromise that keeps both.
248
+
249
+ The OpenAI-compat backend is unaffected — it forwards tool definitions byte-for-byte and doesn't need this flag.
250
+
251
+ ## Hybrid tool mode
252
+
253
+ For the very common case where the "missing" fields on your client's tool are **request context** — `sessionId`, `requestId`, `channelId`, `userId`, `timestamp` — dario can remap to CC tools *and* inject those values on the reverse path. The CC wire shape stays intact, the model still sees only CC's tools (so subscription billing still routes), and your validator still sees the fields it requires because dario fills them from request headers on the way back.
254
+
255
+ ```bash
256
+ dario proxy --hybrid-tools
257
+ ```
258
+
259
+ **How it works.** On each request, dario builds a `RequestContext` from headers (`x-session-id`, `x-request-id`, `x-channel-id`, `x-user-id`) plus its own generated ids and the current timestamp. After `translateBack` produces the client-shaped tool call on the response path, any field declared on the client's tool schema whose name matches a known context field (`sessionId`/`session_id`, `requestId`/`request_id`, `channelId`/`channel_id`, `userId`/`user_id`, `timestamp`/`created_at`/`createdAt`) and isn't already populated gets filled from the context. Fields the model genuinely populated are never overwritten.
260
+
261
+ **When to use which flag:**
262
+
263
+ | Your situation | Flag | Why |
264
+ |---|---|---|
265
+ | Your agent is listed in the table at the top | *(neither)* | Pre-mapped in `TOOL_MAP`; the default path already handles it. |
266
+ | Your custom fields are request context (session/request/channel/user ids, timestamps) | `--hybrid-tools` | Keeps the CC wire shape *and* your validator is satisfied. |
267
+ | Your custom fields need the model's reasoning (e.g. `confidence`, `reasoning_trace`, `tool_selection_rationale`) | `--preserve-tools` | The model has to see the real schema to populate these. Accept the CC-wire-shape loss. |
268
+ | Your client's tools are already a subset of CC's `Bash/Read/Write/Edit/Grep/Glob/WebSearch/WebFetch` | *(neither)* | Default mode works as-is. |
269
+ | You're on a text-tool client (Cline / Kilo Code / Roo Code) and want to override the auto-detect | `--no-auto-detect` (plus `--preserve-tools` or not, your call) | Operator choice outranks the heuristic. |
@@ -0,0 +1,51 @@
1
+ # Compatibility matrix
2
+
3
+ One-page status table per tool. The setup details for each row live in [`agent-compat.md`](./agent-compat.md) and the per-tool walkthroughs (`hands-walkthrough.md`, `openhands-walkthrough.md`, `openclaw-walkthrough.md`); this page is just "does it work?" with an honest cell per tool.
4
+
5
+ Status legend:
6
+
7
+ - **✅ Working** — code path exercised, walkthrough or per-tool docs exist, no known dario-side gaps.
8
+ - **⚪ Inferred** — uses a generic protocol path (Anthropic SDK or OpenAI-compat passthrough) that dario handles correctly, but no tool-specific test exists. Should work; report if not.
9
+ - **🟡 Untested** — listed in the README's "every tool that honors those env vars" sentence, but no walkthrough, no per-tool docs, and no smoke test.
10
+
11
+ | Tool | Protocol | Routes via | Status | Setup |
12
+ |---|---|---|---|---|
13
+ | **Claude Code** | Anthropic Messages | Claude backend | ✅ Working | Default — what dario was built around. `dario login`, `dario proxy`, no per-tool config. |
14
+ | **Cursor** (BYOK Custom OpenAI) | Anthropic Messages | Claude backend | ✅ Working | Cloudflared tunnel + `anthropic:` model prefix + Agent mode. Long form: [`agent-compat.md#cursor`](./agent-compat.md#cursor). |
15
+ | **Continue.dev** | Anthropic / OpenAI | Either | ✅ Working | [`agent-compat.md#continuedev`](./agent-compat.md#continuedev) |
16
+ | **Aider** | Anthropic / OpenAI | Either | ✅ Working | [`agent-compat.md#aider`](./agent-compat.md#aider) |
17
+ | **Cline / Roo Code / Kilo Code** | Anthropic (text-tool) | Claude backend | ✅ Working | Auto-flips into preserve-tools mode via system-prompt identity markers. [`agent-compat.md#cline--roo-code--kilo-code`](./agent-compat.md#cline--roo-code--kilo-code) |
18
+ | **Zed** | Anthropic | Claude backend | ✅ Working | [`agent-compat.md#zed`](./agent-compat.md#zed) |
19
+ | **OpenHands** | Anthropic | Claude backend | ✅ Working | Full walkthrough: [`openhands-walkthrough.md`](./openhands-walkthrough.md) |
20
+ | **OpenClaw** | Anthropic | Claude backend | ✅ Working | Full walkthrough: [`openclaw-walkthrough.md`](./openclaw-walkthrough.md). Identity-detected for preserve-tools. |
21
+ | **hands** | Anthropic | Claude backend | ✅ Working | Full walkthrough: [`hands-walkthrough.md`](./hands-walkthrough.md). Identity-detected. |
22
+ | **CC sub-agents** | Anthropic | Claude backend | ✅ Working | `dario subagent install` registers a CC sub-agent that exposes `dario doctor` and other read-only diagnostics inside any CC session. [`sub-agent.md`](./sub-agent.md) |
23
+ | **Claude Agent SDK** | Anthropic | Claude backend | ✅ Working | `baseURL: 'http://localhost:3456'` on the `Anthropic` client. SDK examples in [`usage.md`](./usage.md). |
24
+ | **MCP clients (any)** | MCP / JSON-RPC | dario as MCP server | ✅ Working | `dario mcp` exposes dario as a read-only MCP server. [`mcp-server.md`](./mcp-server.md) |
25
+ | **Codex CLI** | OpenAI | OpenAI-compat backend | ⚪ Inferred | `dario backend add openai --key=...` then point Codex CLI at `OPENAI_BASE_URL=http://localhost:3456/v1`. The OpenAI backend is a byte-for-byte passthrough (verified by `test/openai-backend-passthrough.mjs`); no Codex-specific code path exists or is needed. |
26
+ | **Hermes** | Anthropic | Claude backend | ⚪ Inferred | Identity-detected by name in CC's identity markers; routes through the standard preserve-tools path. No dedicated walkthrough yet. |
27
+ | **Windsurf** | Anthropic | Claude backend | 🟡 Untested | Listed in README — uses Anthropic-shape requests, should pass through dario's Claude backend. No dedicated walkthrough or smoke test. Open an issue with `dario doctor` output if it doesn't work. |
28
+ | **Claude Desktop** | Anthropic | Claude backend | 🟡 Untested | Generic Anthropic SDK consumer; should work via the same path Claude Code uses. No dedicated walkthrough. |
29
+ | **GitHub Copilot** | Proprietary | n/a | 🟡 Not applicable | Copilot's BYOK paths are surface-specific (Copilot Chat in VS Code, GitHub.com, etc.) and don't expose a generic OpenAI/Anthropic base-URL override. Listed in README for completeness; no dario integration is currently possible without a vendor-side change. |
30
+
31
+ ## What "Inferred" means in practice
32
+
33
+ For ⚪ Inferred entries, the underlying protocol path is identical to a tested one — the generic OpenAI-compat passthrough (`forwardToOpenAI` in [`src/openai-backend.ts`](../src/openai-backend.ts), exercised by `test/openai-backend-passthrough.mjs`) or the standard Anthropic backend path. There's no tool-specific code to break; the dario-side risk is "did the upstream provider rotate something we cared about", which `cc-drift-watch.yml` catches on the Claude side and which OpenAI-compat clients self-detect by speaking the protocol they speak.
34
+
35
+ If an Inferred entry doesn't work for you, the failure is almost always upstream (provider rate-limit shape, model deprecation, custom header the tool sends that we don't forward). Open an issue with `dario doctor` output and we'll either fix it (if it's dario) or document the workaround (if it's the tool).
36
+
37
+ ## What's missing from this page
38
+
39
+ - A "tested at version vX.Y.Z" column — the matrix is moment-in-time, not historical.
40
+ - Performance characteristics (latency, throughput) — see the per-tool walkthroughs.
41
+ - Per-tool feature matrices (does Cursor's BYOK pass `tools`? does Cline's text-tool mode survive `--system-prompt=partial`? etc) — those live in the long-form docs.
42
+
43
+ ## Adding a tool to this matrix
44
+
45
+ Honest framing for new entries:
46
+
47
+ - **✅ Working** requires either (a) a checked-in walkthrough doc, or (b) a smoke test that exercises the tool's request shape end-to-end.
48
+ - **⚪ Inferred** is the right cell for "uses a generic protocol path I can point to in code, but I haven't run the actual tool through dario."
49
+ - **🟡 Untested** is the right cell for "mentioned in README, but I have no evidence either way."
50
+
51
+ Don't promote ⚪ → ✅ without a walkthrough or test landing. Don't promote 🟡 → ⚪ without at least pointing at the code path you're claiming covers it.
@@ -0,0 +1,295 @@
1
+ # dario + hands — battletested setup
2
+
3
+ End-to-end walkthrough for running [hands](https://github.com/askalf/hands) — a local computer-use agent that drives your OS through its native shell — through dario so the model spend bills against your Claude Pro / Max subscription instead of per-token overage on the computer-use beta. Covers install → mode selection → first run → verification → the gotchas that bite first-time users.
4
+
5
+ This is the **first-party** walkthrough. hands is one of dario's sister projects under [askalf](https://github.com/askalf), so unlike the OpenHands / OpenClaw guides where dario is *integrating* with someone else's tool, this is the canonical end-to-end stack we run ourselves. Most of the integration work has already been done on both ends: dario v3.33.0 auto-detects hands via system-prompt identity match and preserves the computer-use beta tools (`computer`, `bash`, `str_replace_based_edit_tool`) without you needing any flag.
6
+
7
+ ## Why hands + dario
8
+
9
+ Hosted "AI controls your computer" products charge $20–50/mo on top of any LLM costs. The math is unfavorable on at least four axes:
10
+
11
+ | Axis | Hosted product | hands + dario |
12
+ |---|---|---|
13
+ | **Per-task cost** | Bundled into the $20–50/mo tier | **$0** — bills against the Claude Max plan you already pay for |
14
+ | **Where your screenshots go** | Vendor's servers | Your machine. The only outbound is to your chosen LLM endpoint |
15
+ | **What drives your OS** | A screenshot loop simulating clicks | Your actual shell — PowerShell on Windows, `open` + AppleScript on macOS, `xdotool` / `ydotool` on Linux. Faster, cheaper, more reliable |
16
+ | **Audit trail** | Vendor's logs (good luck exporting) | `~/.hands/audit.jsonl` — every tool call, locally, line-delimited JSON. `--dry-run` to plan without acting |
17
+
18
+ The walkthrough below puts that stack together in 5 minutes.
19
+
20
+ ## Two modes — pick the right one
21
+
22
+ hands ships with two authentication paths. Same agent loop, same tools — the difference is **where** the model runs and **what it costs.**
23
+
24
+ | Mode | What it uses | Per-task cost via dario | Audit log | Best for |
25
+ |---|---|---|---|---|
26
+ | **Claude Login** *(default)* | The `claude` CLI as a child process | $0 (the CLI already uses your subscription) | None — `claude` runs the tools internally | Daily use, lowest setup |
27
+ | **SDK mode** | Anthropic SDK directly | **$0** when routed through dario | ✅ `~/.hands/audit.jsonl` | Programmatic access, dry-run planning, security review |
28
+
29
+ If you already pay for Claude Max and want zero friction, **Claude Login mode** is fine — dario isn't strictly required because the `claude` binary handles subscription billing on its own. dario becomes useful when you want SDK mode's audit log, `--dry-run` planning, or to run hands programmatically from your own scripts — those don't work on Claude Login.
30
+
31
+ This walkthrough covers both. SDK + dario gets the spotlight because that's where dario actually adds value.
32
+
33
+ ## Prerequisites
34
+
35
+ | Thing | Version | Why |
36
+ |---|---|---|
37
+ | **Node.js** | 20+ | hands and dario both target Node 20 minimum |
38
+ | **hands** | latest from npm — `npm i -g @askalf/hands` | The agent itself |
39
+ | **dario** | v3.33.0+ (latest preferred — `npm i -g @askalf/dario@latest`) | v3.33.0 added the system-prompt identity match that auto-preserves hands' computer-use tools |
40
+ | **A Claude OAuth login** | run `dario login` once | A Pro / Max subscription on a Claude account |
41
+ | **`claude` CLI** | latest | Required for Claude Login mode; `hands init` will install for you if missing |
42
+ | **Bun** (recommended) | 1.1+ | dario auto-relaunches under Bun for TLS-fingerprint fidelity. Skip if you're fine with a runtime banner; install via [bun.sh](https://bun.sh) for the full subscription wire shape. |
43
+
44
+ Verify dario before starting:
45
+
46
+ ```bash
47
+ dario doctor # all green = ready
48
+ dario status # OAuth healthy, expires in N hours
49
+ ```
50
+
51
+ ## Install + init
52
+
53
+ One npm install, one interactive command:
54
+
55
+ ```bash
56
+ npm install -g @askalf/hands
57
+ hands init
58
+ ```
59
+
60
+ `hands init` walks every choice a new user has to make — auth mode, optional voice (whisper.cpp), `claude` CLI install if missing, dario routing tip. It's safe to re-run; pick a different mode any time.
61
+
62
+ ## Mode 1 — Claude Login (default, simplest)
63
+
64
+ This is the path `hands init` recommends. Pick "Claude Login" when prompted. Done.
65
+
66
+ ```bash
67
+ hands run "open notepad and type hello world"
68
+ ```
69
+
70
+ What happens under the hood:
71
+
72
+ 1. hands spawns the `claude` CLI as a child process
73
+ 2. `claude` uses your Claude Code subscription (the same OAuth login you have for CC)
74
+ 3. The agent loop runs inside `claude`, dispatching computer-use tools via hands' shell wrappers
75
+ 4. You see the result in your terminal
76
+
77
+ dario isn't on the path here because `claude` handles subscription billing directly. That's by design — Claude Login mode is the "I want it to just work" path.
78
+
79
+ If you're using Claude Login mode, **you can stop reading this walkthrough now** — you're done. The rest of this guide covers SDK mode.
80
+
81
+ ## Mode 2 — SDK + dario (audit-logged, programmatic, dry-run)
82
+
83
+ Pick this mode when you want one of:
84
+
85
+ - **`--dry-run`** — see exactly what the agent would do before letting it act
86
+ - **`~/.hands/audit.jsonl`** — every tool call timestamped, with args, durations, outcomes. Useful for security review or post-incident forensics.
87
+ - **Programmatic agent runs** from your own Node scripts (importing hands as a library)
88
+ - **A specific Claude account** different from the one your `claude` CLI is logged into (via `dario accounts add`)
89
+
90
+ Setup is two env vars and one running dario instance:
91
+
92
+ ```bash
93
+ # In whatever shell starts hands:
94
+ export ANTHROPIC_BASE_URL=http://localhost:3456
95
+ export ANTHROPIC_API_KEY=dario # or your DARIO_API_KEY if set
96
+ ```
97
+
98
+ Add those to your shell profile (`~/.bashrc`, `~/.zshrc`, fish config, PowerShell `$PROFILE`) so they're set for every session.
99
+
100
+ Then in one terminal:
101
+
102
+ ```bash
103
+ dario proxy --verbose
104
+ ```
105
+
106
+ In another:
107
+
108
+ ```bash
109
+ hands auth # pick "API Key" — when prompted for the key, paste: dario
110
+ hands run "open notepad and type hello world"
111
+ ```
112
+
113
+ That's it. The Anthropic SDK reads the env vars by default, so no hands-side config is needed beyond `hands auth` once.
114
+
115
+ ### What dario does for hands automatically
116
+
117
+ You don't need any flag. Dario v3.33.0+ recognizes hands via a system-prompt identity match and:
118
+
119
+ - **Preserves** the Anthropic computer-use beta tools (`computer`, `bash`, `str_replace_based_edit_tool`) instead of remapping them to CC's canonical set. The computer-use beta tools have schema fields CC's tools don't carry; trying to translate them would corrupt the calls.
120
+ - **Strips** orchestration tags from the prompt to keep the wire shape on the subscription path.
121
+ - **Forwards** the `anthropic-beta: computer-use-*` header so the upstream model knows to enable the beta.
122
+ - Everything else (template replay, OAuth swap, sticky session) runs identically to a Claude Code request.
123
+
124
+ You'll see this in `dario proxy --verbose` as a log line like:
125
+
126
+ ```
127
+ [dario] #1 POST /v1/messages (model: claude-sonnet-4-6, client: hands, preserve_tools: true, beta: computer-use-2025-01-24) → 200 (1842 ms)
128
+ ```
129
+
130
+ ## Voice (optional)
131
+
132
+ If you opted into voice during `hands init`, you'll have whisper.cpp installed locally. Then:
133
+
134
+ ```bash
135
+ hands run "open chrome and go to amazon.com" --voice
136
+ ```
137
+
138
+ Press Enter to start recording, Enter again to stop. Whisper transcribes locally (no audio leaves your machine), and the transcribed task feeds into the agent loop the same as a typed prompt.
139
+
140
+ ## Verifying subscription billing
141
+
142
+ Two checks, one at the dario layer and one at Anthropic's:
143
+
144
+ ### Check 1: dario doctor --usage
145
+
146
+ ```bash
147
+ dario doctor --usage
148
+ ```
149
+
150
+ You should see your 5-hour bucket showing non-zero usage with `claim=five_hour (subscription)`:
151
+
152
+ ```
153
+ [ OK ] Usage 5h (all) 14.2% used • status=allowed • claim=five_hour (subscription)
154
+ ```
155
+
156
+ If `claim=five_hour (subscription)` shows up, you're billing against the Claude Max plan, not API. Done.
157
+
158
+ If `claim=api` shows up, something flipped you to per-token billing — usually because you started hands in Claude Login mode (where `claude` doesn't go through dario at all) but then ran SDK mode without setting the env vars. `hands doctor` reports the effective base URL hands sees; cross-check it.
159
+
160
+ ### Check 2: hands' audit log
161
+
162
+ ```bash
163
+ tail -f ~/.hands/audit.jsonl
164
+ ```
165
+
166
+ Every tool call is one JSON-ND record. If you ran `hands run "open notepad"` and the audit log is silent, you're on Claude Login mode (which bypasses hands' tool dispatcher). Switch with `hands auth` to API Key mode if you want the audit trail.
167
+
168
+ ### Check 3: Anthropic dashboard
169
+
170
+ Log into [console.anthropic.com](https://console.anthropic.com) → Usage. **Your API spend should be flat** (no new charges since you started hands). If it's climbing, the env vars didn't take effect — restart your shell and re-export.
171
+
172
+ ## Battletested patterns
173
+
174
+ After running hands+dario in production for months, here are the patterns we lean on:
175
+
176
+ ### Plan first, act second
177
+
178
+ Always run a `--dry-run` before letting hands actually touch anything irreversible:
179
+
180
+ ```bash
181
+ hands run --dry-run "delete every file in ~/Downloads older than 30 days"
182
+ ```
183
+
184
+ `--dry-run` forces SDK mode (which is the only mode where hands sees individual tool calls before they execute), prints the planned action sequence, and exits without doing anything. If the plan looks right, run it without `--dry-run`.
185
+
186
+ ### Pin the model for cost-sensitive runs
187
+
188
+ Long autonomous loops on Sonnet are usually right. For exploratory single-shot tasks where you just want a quick answer, Haiku is dramatically cheaper *in API mode* — but on subscription via dario, the bucket is the same. Pin Sonnet for everything unless you have a specific reason; the model-choice tradeoff is real on direct API but neutralized through dario.
189
+
190
+ ### Multi-account pool for parallel runs
191
+
192
+ If you run two or more hands sessions in parallel — say, one task on the desktop and another headless on a server — you'll exhaust a single Claude account's 5-hour bucket. Add a second account to dario's pool and pool mode load-balances:
193
+
194
+ ```bash
195
+ dario login # log in to a second Claude account
196
+ dario accounts add work
197
+ ```
198
+
199
+ See [`docs/multi-account-pool.md`](./multi-account-pool.md). Session stickiness ensures multi-turn hands conversations stay on one account.
200
+
201
+ ### Audit-log review before deploying agents into shared environments
202
+
203
+ Before letting hands SDK-mode loose on a shared machine (CI agent, family computer, etc.), run a representative task with `--dry-run` and read `~/.hands/audit.jsonl` end-to-end. The audit log is exactly the visibility you'd want before signing off on agentic access to a shared OS — and it's local, not vendor-side.
204
+
205
+ ## Common gotchas
206
+
207
+ ### `claude` CLI not found, hands won't start in Claude Login mode
208
+
209
+ ```bash
210
+ hands init # offers to install claude CLI for you, then re-runs hands setup
211
+ ```
212
+
213
+ Or install Claude Code yourself per [Anthropic's docs](https://docs.anthropic.com/en/docs/claude-code).
214
+
215
+ ### `Connection refused` to localhost:3456 in SDK mode
216
+
217
+ dario isn't running:
218
+
219
+ ```bash
220
+ curl -s http://localhost:3456/health
221
+ # expected: {"status":"ok",...}
222
+ ```
223
+
224
+ If that fails, start dario (`dario proxy --verbose`) before invoking hands.
225
+
226
+ ### Claim flips to api in SDK mode
227
+
228
+ Three causes, in order of likelihood:
229
+
230
+ 1. **You're not actually on dario.** Run `hands doctor` — if `Effective base URL` doesn't show `localhost:3456`, the env vars didn't take effect. Restart your shell.
231
+ 2. **Your dario template is stale.** Run `dario doctor` and check the template-age line. If it's >48 hours old, the captured CC system prompt may not match what Anthropic's classifier currently expects. `dario doctor --bun-bootstrap` to force a fresh capture.
232
+ 3. **You're running hands SDK mode against a different account than the one paying for Max.** Run `dario status` and confirm the OAuth account is the subscription account.
233
+
234
+ ### "computer use" beta header dropped
235
+
236
+ If dario isn't preserving the `anthropic-beta: computer-use-*` header, you're probably on a dario version older than v3.33.0. Upgrade — the system-prompt identity match and beta-preserve behavior both landed in that release.
237
+
238
+ ```bash
239
+ npm install -g @askalf/dario@latest
240
+ ```
241
+
242
+ ### Voice mode says "whisper.cpp not found"
243
+
244
+ ```bash
245
+ hands init # offers to download whisper.cpp for you
246
+ ```
247
+
248
+ Or install it yourself: clone [ggerganov/whisper.cpp](https://github.com/ggerganov/whisper.cpp), `make`, and put the binary on your `PATH`.
249
+
250
+ ### Hands hangs on a screenshot in SDK mode
251
+
252
+ Computer-use beta requests with multiple screenshots can be slow on first response. Bump retry config in your shell:
253
+
254
+ ```bash
255
+ export ANTHROPIC_REQUEST_TIMEOUT_MS=120000
256
+ ```
257
+
258
+ dario's outbound timeout is 5 min by default, so this is purely about hands' own client-side timeout.
259
+
260
+ ## What this guide doesn't cover
261
+
262
+ - **hands as a library** (importing into your own Node scripts). The dario integration works the same way — env vars route the underlying SDK to `localhost:3456`. See hands' README for the programmatic API surface.
263
+ - **Custom agents extending hands' core.** Subclassing the agent loop is supported but out of scope here. The dario integration is at the LLM layer; agent code doesn't need to change.
264
+
265
+ ## Quick reference card
266
+
267
+ ```bash
268
+ # One-time setup
269
+ npm install -g @askalf/dario @askalf/hands
270
+ dario login
271
+ hands init
272
+
273
+ # Per-session — Claude Login mode (default, no dario needed)
274
+ hands run "your task here"
275
+
276
+ # Per-session — SDK + dario mode (audit-logged, programmatic, --dry-run)
277
+ export ANTHROPIC_BASE_URL=http://localhost:3456
278
+ export ANTHROPIC_API_KEY=dario
279
+ dario proxy --verbose &
280
+ hands run "your task here"
281
+
282
+ # Verify subscription billing
283
+ dario doctor --usage # claim=five_hour (subscription) ✓
284
+ hands doctor # effective base URL shows localhost:3456
285
+ tail -f ~/.hands/audit.jsonl # tool calls flowing in real time (SDK mode only)
286
+ ```
287
+
288
+ ## Related guides
289
+
290
+ - [`openhands-walkthrough.md`](./openhands-walkthrough.md) — sister walkthrough for the OpenHands software-engineer agent
291
+ - [`openclaw-walkthrough.md`](./openclaw-walkthrough.md) — sister walkthrough for OpenClaw
292
+ - [`agent-compat.md`](./agent-compat.md) — short setup snippets for every other agent dario supports
293
+ - [`multi-account-pool.md`](./multi-account-pool.md) — adding 2+ Claude accounts to extend rate limits for parallel hands runs
294
+ - [`commands.md`](./commands.md) — full dario CLI reference
295
+ - [hands repo](https://github.com/askalf/hands) — full hands documentation, security model, and architecture deep-dive