forge-delegate 1.0.7 → 1.0.9

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
@@ -27,7 +27,7 @@ so you're picking from actual available models rather than guessing.
27
27
  **npx (recommended — works everywhere, no bash needed):**
28
28
 
29
29
  ```bash
30
- npx -y forge-delegate setup --model "opencode/mimo-v2.5-free" --targets "claude,codex,opencode"
30
+ npx -y forge-delegate setup --model "opencode/deepseek-v4.1-flash-free" --targets "claude,codex,opencode"
31
31
  ```
32
32
 
33
33
  That's it — npx pulls the latest package and `setup` registers the MCP server with the agents
@@ -56,8 +56,11 @@ Then restart your agent and confirm the server is connected: `/mcp` in Claude Co
56
56
 
57
57
  ### What you need
58
58
 
59
- - **Node.js ≥ 18** and **opencode** installed (the installer can fetch opencode for you).
60
- - **Free `opencode/*` models** (e.g. `opencode/mimo-v2.5-free`) need no auth — zero setup.
59
+ - **Node.js ≥ 18** and **opencode v2 or newer** installed (the installer can fetch opencode for you).
60
+ v2 removed the CLI flags older versions relied on for working directories and variants, and an
61
+ unknown flag makes `opencode run` print help and exit successfully — so a pre-v2 binary fails
62
+ silently. The server checks at startup and warns instead.
63
+ - **Free `opencode/*` models** (e.g. `opencode/deepseek-v4.1-flash-free`) need no auth — zero setup.
61
64
  - **Any other provider** (deepseek, openai, google, ollama, …) must be logged into **your**
62
65
  opencode first — that's a step *you* do in your own opencode, not something forge-delegate
63
66
  handles: `opencode auth login <provider>`. forge-delegate reuses your opencode's credentials
@@ -80,10 +83,10 @@ Use the `npx` route — no bash needed (works in cmd/PowerShell). Drop `codex` f
80
83
  opencode ships free no-auth models under `opencode/`:
81
84
 
82
85
  ```bash
83
- npx -y forge-delegate setup --model "opencode/mimo-v2.5-free" --targets "claude,codex"
86
+ npx -y forge-delegate setup --model "opencode/deepseek-v4.1-flash-free" --targets "claude,codex"
84
87
  ```
85
88
 
86
- Other free options: `opencode/hy3-free`, `opencode/nemotron-3.5-lightning-free`. Free tiers are
89
+ Other free options: `opencode/mimo-v2.6-flash-free`, `opencode/nemotron-3.5-lightning-free`. Free tiers are
87
90
  rate-limited; switch anytime with `forge-delegate config set --model <m>`.
88
91
 
89
92
  ## Tools
@@ -152,7 +155,7 @@ Every delegation tool also accepts per-call `model`, `directory`, `agent`, `auto
152
155
 
153
156
  ## Models
154
157
 
155
- - **opencode provider** — free no-auth models (`opencode/mimo-v2.5-free`, ...) plus
158
+ - **opencode provider** — free no-auth models (`opencode/deepseek-v4.1-flash-free`, ...) plus
156
159
  anything you have logged in via `opencode auth login <provider>`.
157
160
  - **Direct API from opencode logins** — `provider/model` for OpenAI-compatible providers.
158
161
  - **Custom providers** — OpenAI-compatible providers defined in `opencode.json`
package/cli.mjs CHANGED
@@ -5,7 +5,7 @@ import { join, dirname } from "node:path";
5
5
  import { homedir } from "node:os";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { execSync } from "node:child_process";
8
- import { loadConfig, saveConfig, configPath } from "./lib.mjs";
8
+ import { loadConfig, saveConfig, configPath, detectOpencode, loadOpencodeAuth } from "./lib.mjs";
9
9
 
10
10
  const here = dirname(fileURLToPath(import.meta.url));
11
11
 
@@ -121,8 +121,20 @@ async function setup() {
121
121
  console.log("\n=== forge-delegate setup ===\n");
122
122
  if (interactive) console.log("Press Enter to accept the suggested [default], or type a value.\n");
123
123
 
124
+ const oc = await detectOpencode();
125
+ if (!oc.found) {
126
+ console.log(" ✗ opencode not found on PATH — install it from https://opencode.ai, then re-run setup.\n");
127
+ } else if (!oc.supported) {
128
+ console.log(
129
+ ` ✗ opencode ${oc.version?.raw ?? "(unknown version)"} is too old — forge-delegate requires opencode 2 or newer.\n` +
130
+ " Run: opencode upgrade\n"
131
+ );
132
+ } else {
133
+ console.log(` ✓ opencode ${oc.version.raw}\n`);
134
+ }
135
+
124
136
  if (!model) {
125
- const def = current.defaultModel ?? "opencode/mimo-v2.5-free";
137
+ const def = current.defaultModel ?? "opencode/deepseek-v4.1-flash-free";
126
138
  model = interactive ? ((await ask(`Default model [${def}]: `)) ?? "").trim() || def : def;
127
139
  }
128
140
  if (!targets) targets = "claude,codex,opencode";
@@ -158,8 +170,7 @@ async function setup() {
158
170
  // 3. Check opencode logins
159
171
  let authCount = 0;
160
172
  try {
161
- const authPath = join(homedir(), ".local/share/opencode/auth.json");
162
- authCount = Object.keys(JSON.parse(readFileSync(authPath, "utf8"))).length;
173
+ authCount = Object.keys(await loadOpencodeAuth()).length;
163
174
  } catch {}
164
175
  if (authCount > 0) {
165
176
  console.log(`Found ${authCount} opencode provider login(s) — ready to use.`);
@@ -199,12 +210,14 @@ function configCmd(argv) {
199
210
  }
200
211
 
201
212
  async function status() {
213
+ const oc = await detectOpencode();
214
+ console.log(
215
+ `\nopencode: ${oc.found ? (oc.version?.raw ?? "unknown version") : "not found on PATH"}` +
216
+ `${oc.found && !oc.supported ? " (too old — requires v2 or newer)" : ""}`
217
+ );
202
218
  let providers = [];
203
219
  try {
204
- const authPath = join(homedir(), ".local/share/opencode/auth.json");
205
- const { listAuthDirectProviders } = await import("./lib.mjs");
206
- const auth = JSON.parse(readFileSync(authPath, "utf8"));
207
- providers = Object.keys(auth);
220
+ providers = Object.keys(await loadOpencodeAuth());
208
221
  } catch {}
209
222
  console.log(`\nopencode logins: ${providers.length ? providers.join(", ") : "none (run 'opencode auth login <provider>')"}\n`);
210
223
  try {
package/docs/index.html CHANGED
@@ -96,7 +96,7 @@
96
96
 
97
97
  <div class="field picker">
98
98
  <label for="model">Default model (provider/model)</label>
99
- <input type="text" id="model" placeholder="type to search, e.g. opencode/mimo-v2.5-free" autocomplete="off">
99
+ <input type="text" id="model" placeholder="type to search, e.g. opencode/deepseek-v4.1-flash-free" autocomplete="off">
100
100
  <div class="filters">
101
101
  <select id="provider" title="Provider">
102
102
  <option value="">all providers</option>
@@ -330,7 +330,7 @@
330
330
  var cmd = command();
331
331
  var modelLine = model
332
332
  ? 'Use "' + model + '" as the default model unless I say otherwise.'
333
- : 'Use a cheap/free default model (e.g. opencode/mimo-v2.5-free) unless I say otherwise.';
333
+ : 'Use a cheap/free default model (e.g. opencode/deepseek-v4.1-flash-free) unless I say otherwise.';
334
334
  return [
335
335
  "Install and register the forge-delegate MCP server for me. " + modelLine +
336
336
  " Register it with " + targets + ", and use \"" + scope + "\" scope.",
package/lib.mjs CHANGED
@@ -1,18 +1,82 @@
1
- import { spawn } from "node:child_process";
1
+ import { execFile, spawn } from "node:child_process";
2
2
  import { appendFileSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import { homedir } from "node:os";
5
5
 
6
6
  export const OPENCODE_TIMEOUT = Number(process.env.FORGE_OPENCODE_TIMEOUT ?? 900_000);
7
+
8
+ // forge-delegate targets opencode v2+. v2 removed the `--dir` and `--variant`
9
+ // flags from `run` — and treats an unknown flag as a help request, exiting 0 —
10
+ // so an older binary fails silently. Detecting the version is the only way to
11
+ // tell the user instead of returning empty results.
12
+ export function parseOpencodeVersion(text) {
13
+ const m = String(text ?? "").match(/(\d+)\.(\d+)\.(\d+)/);
14
+ if (!m) return null;
15
+ return { major: +m[1], minor: +m[2], patch: +m[3], raw: m[0] };
16
+ }
17
+
18
+ export function detectOpencode() {
19
+ return new Promise((resolve) => {
20
+ execFile("opencode", ["--version"], { timeout: 10_000 }, (err, stdout, stderr) => {
21
+ if (err) return resolve({ found: false, version: null, supported: false });
22
+ const version = parseOpencodeVersion(`${stdout}\n${stderr}`);
23
+ resolve({ found: true, version, supported: Boolean(version) && version.major >= 2 });
24
+ });
25
+ });
26
+ }
27
+
28
+ // The startup warning on stderr is easy for an MCP client to swallow, and a v1
29
+ // binary does not fail loudly: `run` reads the v2 flags as a help request and
30
+ // exits 0. Check once per process, then refuse to spawn instead of returning
31
+ // "(no output)" and letting it look like the model had nothing to say.
32
+ let opencodeGate;
33
+ function requireOpencodeV2() {
34
+ opencodeGate ??= detectOpencode();
35
+ return opencodeGate.then((oc) => {
36
+ if (!oc.found)
37
+ throw new Error("opencode not found on PATH — install it from https://opencode.ai");
38
+ if (!oc.supported)
39
+ throw new Error(
40
+ `opencode ${oc.version?.raw ?? "(unknown version)"} is too old — forge-delegate requires opencode 2 or newer ` +
41
+ `(v2 removed the CLI flags it uses). Run: opencode upgrade`
42
+ );
43
+ return oc;
44
+ });
45
+ }
46
+
7
47
  const CONFIG_DIR = join(homedir(), ".forge-delegate");
8
48
  const AUTH_PATH = join(homedir(), ".local/share/opencode/auth.json");
9
49
  const OPENCODE_CONFIG_PATH = join(homedir(), ".config/opencode/opencode.json");
10
50
  const CATALOG_CACHE = join(CONFIG_DIR, "models-dev.json");
11
51
  const CATALOG_TTL = 24 * 3600 * 1000;
12
- const VARIANTS_CACHE = join(CONFIG_DIR, "variants.json");
13
52
  const OLLAMA_BASE = process.env.OLLAMA_URL ?? "http://localhost:11434";
14
53
 
15
- function loadOpencodeAuth() {
54
+ function execFileText(cmd, args, opts = {}) {
55
+ return new Promise((resolve, reject) => {
56
+ execFile(cmd, args, { timeout: 15_000, maxBuffer: 4 * 1024 * 1024, ...opts }, (err, stdout) => {
57
+ if (err) reject(err);
58
+ else resolve(stdout);
59
+ });
60
+ });
61
+ }
62
+
63
+ // opencode v2 moved credentials into its own store; auth.json is frozen at
64
+ // whatever v1 last wrote there. Reading the file on v2 therefore hands back
65
+ // stale keys, which surface as 401s that look like the user's fault. Ask v2
66
+ // for its credentials instead, and fall back to the file on v1 (where the
67
+ // `auth export` command does not exist).
68
+ export async function loadOpencodeAuth() {
69
+ try {
70
+ const entries = JSON.parse(await execFileText("opencode", ["auth", "export"]));
71
+ if (Array.isArray(entries)) {
72
+ const auth = {};
73
+ for (const e of entries) {
74
+ if (e?.integrationID && e?.value?.type === "key" && e.value.key)
75
+ auth[e.integrationID] = { type: "api", key: e.value.key };
76
+ }
77
+ return auth;
78
+ }
79
+ } catch {}
16
80
  try {
17
81
  return JSON.parse(readFileSync(AUTH_PATH, "utf8"));
18
82
  } catch {
@@ -91,83 +155,43 @@ export function resolveConfig(overrides = {}) {
91
155
  };
92
156
  }
93
157
 
94
- export function opencodeArgs(model, prompt, { files, sessionId, variant, directory, agent, autoApprove } = {}) {
158
+ // opencode v2 selects the run directory via the child's cwd (there is no `--dir`)
159
+ // and the reasoning variant inline as `provider/model#variant` (there is no
160
+ // `--variant`). Passing the old flags makes `run` print its help and exit 0 —
161
+ // a silent no-op, so do not reintroduce them.
162
+ export function opencodeArgs(model, prompt, { files, sessionId, variant, agent, autoApprove } = {}) {
95
163
  const args = ["run", "--format", "json"];
96
164
  if (autoApprove) args.push("--auto");
97
165
  if (sessionId) args.push("-s", sessionId);
98
- if (directory) args.push("--dir", directory);
99
166
  if (agent) args.push("--agent", agent);
100
- args.push("-m", model);
101
- if (variant) args.push("--variant", variant);
167
+ const ref = variant && !model.includes("#") ? `${model}#${variant}` : model;
168
+ args.push("-m", ref);
102
169
  for (const f of files ?? []) args.push("-f", f);
103
170
  args.push(prompt);
104
171
  return args;
105
172
  }
106
173
 
174
+ // Variant (reasoning effort) names come from the model catalog's
175
+ // `reasoning_options`. opencode v2 no longer exposes them over the server API.
107
176
  export async function pickVariant(model) {
108
- const [provider, ...rest] = model.split("/");
109
- const id = rest.join("/");
110
- if (!provider || !id || model.startsWith("ollama:")) return undefined;
111
- let variants = {};
177
+ if (model.includes("#") || model.startsWith("ollama:")) return undefined;
178
+ const slash = model.indexOf("/");
179
+ if (slash <= 0) return undefined;
180
+ const provider = model.slice(0, slash);
181
+ const id = model.slice(slash + 1);
182
+ let catalog;
112
183
  try {
113
- variants = await loadVariantMap();
184
+ catalog = await loadCatalog();
114
185
  } catch {
115
186
  return undefined;
116
187
  }
117
- const list = variants[`${provider}/${id}`];
188
+ const options = catalog?.[provider]?.models?.[id]?.reasoning_options;
189
+ const effort = Array.isArray(options) ? options.find((o) => o?.type === "effort") : null;
190
+ const list = effort?.values;
118
191
  if (!Array.isArray(list) || list.length === 0) return undefined;
119
192
  return list.length === 1 ? list[0] : list[list.length - 2];
120
193
  }
121
194
 
122
- // Variant names per provider/model, discovered from `GET /config/providers`
123
- // on a short-lived local opencode server. Cached on disk for 24h.
124
- async function loadVariantMap() {
125
- try {
126
- const cached = JSON.parse(readFileSync(VARIANTS_CACHE, "utf8"));
127
- if (Date.now() - cached.fetchedAt < CATALOG_TTL && cached.data) return cached.data;
128
- } catch {}
129
- const child = spawn("opencode", ["serve", "--port", "0"], { stdio: ["ignore", "pipe", "pipe"] });
130
- try {
131
- const baseUrl = await new Promise((resolvePromise, rejectPromise) => {
132
- let out = "";
133
- const timer = setTimeout(() => rejectPromise(new Error("opencode serve timed out")), 20_000);
134
- child.stdout.on("data", (d) => {
135
- out += d;
136
- const m = out.match(/listening on (https?:\/\/\S+)/);
137
- if (m) {
138
- clearTimeout(timer);
139
- resolvePromise(m[1]);
140
- }
141
- });
142
- child.on("error", (e) => {
143
- clearTimeout(timer);
144
- rejectPromise(e);
145
- });
146
- child.on("exit", () => {
147
- clearTimeout(timer);
148
- rejectPromise(new Error("opencode serve exited before listening"));
149
- });
150
- });
151
- const res = await fetch(`${baseUrl.replace(/\/+$/, "")}/config/providers`, { signal: AbortSignal.timeout(15_000) });
152
- if (!res.ok) throw new Error(`config/providers ${res.status}`);
153
- const body = await res.json();
154
- const providers = body.providers ?? body;
155
- const map = {};
156
- for (const p of Array.isArray(providers) ? providers : Object.values(providers)) {
157
- for (const [mid, m] of Object.entries(p.models ?? {})) {
158
- if (m?.variants) map[`${p.id}/${mid}`] = Object.keys(m.variants);
159
- }
160
- }
161
- try {
162
- mkdirSync(CONFIG_DIR, { recursive: true });
163
- writeFileSync(VARIANTS_CACHE, JSON.stringify({ fetchedAt: Date.now(), data: map }));
164
- } catch {}
165
- return map;
166
- } finally {
167
- child.kill("SIGKILL");
168
- }
169
- }
170
-
171
195
  export function parseEvents(stdout) {
172
196
  let text = "";
173
197
  let sessionId = null;
@@ -186,13 +210,19 @@ export function parseEvents(stdout) {
186
210
  }
187
211
 
188
212
  export async function opencodeRun(model, prompt, opts = {}) {
213
+ await requireOpencodeV2();
189
214
  const started = Date.now();
190
215
  const variant = opts.variant ?? (await pickVariant(model));
191
216
  const timeoutMs = opts.timeoutMs ?? OPENCODE_TIMEOUT;
192
217
  const cwd = opts.directory ?? process.cwd();
218
+ // opencode v2 picks the project from the PWD environment variable, not from the
219
+ // child's cwd — so a delegation with `directory` set would otherwise run against
220
+ // the server's directory while looking like it honoured the option.
221
+ const env = opts.directory ? { ...process.env, PWD: opts.directory } : process.env;
193
222
  const stdout = await new Promise((resolvePromise, rejectPromise) => {
194
223
  const child = spawn("opencode", opencodeArgs(model, prompt, { ...opts, variant }), {
195
224
  cwd,
225
+ env,
196
226
  stdio: ["ignore", "pipe", "pipe"],
197
227
  });
198
228
  let out = "";
@@ -232,7 +262,7 @@ export async function callDirect(baseUrl, model, messages, apiKey, extraBody = {
232
262
  }
233
263
 
234
264
  export async function listAuthDirectProviders() {
235
- const auth = loadOpencodeAuth();
265
+ const auth = await loadOpencodeAuth();
236
266
  let catalog = {};
237
267
  try {
238
268
  catalog = await loadCatalog();
@@ -302,9 +332,18 @@ export async function runAuthDirect(model, prompt) {
302
332
  if (slashIdx <= 0) throw new Error(`expected provider/model format, got '${model}'`);
303
333
  const prov = model.slice(0, slashIdx);
304
334
  const id = model.slice(slashIdx + 1);
305
- const auth = loadOpencodeAuth();
306
- if (!auth[prov]?.key)
335
+ const auth = await loadOpencodeAuth();
336
+ if (!auth[prov]?.key) {
337
+ // opencode's own free tier rejects direct HTTP ("can only be used from within
338
+ // OpenCode"), so these models are reachable only through the CLI.
339
+ if (prov === "opencode" || prov === "opencode-go")
340
+ throw new Error(
341
+ `'${model}' is hosted by opencode, whose free tier only works from inside opencode — ` +
342
+ `ask_model cannot call it directly. Use the delegate tool instead, or prefix the model ` +
343
+ `with 'opencode:' to route it through the CLI.`
344
+ );
307
345
  throw new Error(`no opencode login for '${prov}' — run: opencode auth login ${prov}`);
346
+ }
308
347
  const catalog = await loadCatalog();
309
348
  const info = catalog[prov];
310
349
  if (!info?.api || info.npm !== "@ai-sdk/openai-compatible")
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "forge-delegate",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "MCP server that relays coding work to other AI models (DeepSeek, Gemini, OpenAI, Ollama) so Claude Code saves tokens",
5
5
  "type": "module",
6
6
  "bin": {
7
- "forge-delegate": "./cli.mjs"
7
+ "forge-delegate": "cli.mjs"
8
8
  },
9
9
  "scripts": {
10
10
  "setup": "node cli.mjs setup",
@@ -20,7 +20,16 @@
20
20
  "docs/",
21
21
  "README.md"
22
22
  ],
23
- "keywords": ["mcp", "claude", "claude-code", "ai", "deepseek", "gemini", "ollama", "delegation"],
23
+ "keywords": [
24
+ "mcp",
25
+ "claude",
26
+ "claude-code",
27
+ "ai",
28
+ "deepseek",
29
+ "gemini",
30
+ "ollama",
31
+ "delegation"
32
+ ],
24
33
  "license": "MIT",
25
34
  "engines": {
26
35
  "node": ">=18"
package/server.mjs CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  opencodeArgs,
15
15
  parseEvents,
16
16
  pickVariant,
17
+ detectOpencode,
17
18
  listAuthDirectProviders,
18
19
  listLocalProviders,
19
20
  loadConfig,
@@ -54,6 +55,24 @@ if (SKILL_STATUS && SKILL_STATUS.state !== "ok") {
54
55
  );
55
56
  }
56
57
 
58
+ // Same channel rule. Non-blocking so a slow or missing `opencode` can never delay
59
+ // the MCP handshake; if the binary is absent or pre-v2 the user hears about it
60
+ // instead of getting silent empty delegations later.
61
+ detectOpencode()
62
+ .then((oc) => {
63
+ if (!oc.found) {
64
+ process.stderr.write(
65
+ "forge-delegate: opencode not found on PATH — delegations will fail. Install: https://opencode.ai\n"
66
+ );
67
+ } else if (!oc.supported) {
68
+ process.stderr.write(
69
+ `forge-delegate: opencode ${oc.version?.raw ?? "(unknown version)"} is too old — requires opencode 2 or newer ` +
70
+ `(v2 removed the CLI flags forge-delegate uses). Run: opencode upgrade\n`
71
+ );
72
+ }
73
+ })
74
+ .catch(() => {});
75
+
57
76
  const INSTRUCTIONS = `forge-delegate hands coding work to cheap external models running as full agents. They read the project and write the code in their own loop, so the work never passes through your context. Spend your context on judgment; delegate the typing.
58
77
 
59
78
  DELEGATE BY DEFAULT — do not hand-write these yourself:
@@ -111,8 +130,11 @@ async function startBackgroundJob(model, task, opts = {}) {
111
130
  const logPath = join(RUNS_DIR, `${id}.log`);
112
131
  const out = openSync(logPath, "w");
113
132
  const cwd = opts.directory ?? process.cwd();
133
+ // opencode v2 resolves the project from PWD, not the child's cwd (see lib.mjs).
134
+ const env = opts.directory ? { ...process.env, PWD: opts.directory } : process.env;
114
135
  const child = spawn("opencode", opencodeArgs(model, task, { ...opts, variant }), {
115
136
  cwd,
137
+ env,
116
138
  detached: true,
117
139
  stdio: ["ignore", out, out],
118
140
  });
@@ -147,7 +169,7 @@ server.tool(
147
169
  if (local.ollama.length) lines.push(` ollama: ${local.ollama.slice(0, 12).join(", ")}`);
148
170
  else lines.push(" ollama: not running");
149
171
  for (const [name, p] of Object.entries(local.config))
150
- lines.push(` ${name} (opencode.json): ${p.models.join(", ") || "(models defined ad hoc)"}`);
172
+ lines.push(` ${name} (opencode.json): ${Object.keys(p.models).join(", ") || "(models defined ad hoc)"}`);
151
173
 
152
174
  lines.push("\n== direct via opencode logins (ask_model, provider/model) ==");
153
175
  try {
@@ -192,7 +214,7 @@ server.tool(
192
214
  "delegate",
193
215
  "USE THIS INSTEAD OF WRITING THE CODE YOURSELF when the task is: tests for code that already exists; a mechanical refactor (rename, signature change, import rewrite, API migration); fixing lint/type/build errors; boilerplate or scaffolding (CRUD endpoints, config, schemas, adapters, fixtures); a port between languages or frameworks; the same edit repeated across 3 or more files; reading a large file/directory to answer one narrow question; or the survey half of an audit (delegate the gathering, keep the verdict — demand a file path behind every claim). Hands the task to another AI model running as a full agent via local opencode: it reads your project, explores, and writes concrete code in its own tool loop — none of which enters your context. Keep architecture, unknown-root-cause debugging, and security-sensitive code for yourself, and always review what comes back. Runs INLINE by default (result returned when done; Claude Code auto-backgrounds calls past 2 minutes). Pass background:true to run detached and get a job id instantly, then poll check_delegation — use that for parallel fan-out or hosts that block on long calls. Pass continue_session (returned by a previous delegation) to keep the same agent session instead of re-exploring. Never paste file contents into task: name the paths and let the agent read them. model/directory/agent/autoApprove default from config — omit them.",
194
216
  {
195
- model: z.string().optional().describe("provider/model format, e.g. opencode/mimo-v2.5-free (defaults to config defaultModel)"),
217
+ model: z.string().optional().describe("provider/model format, e.g. opencode/deepseek-v4.1-flash-free (defaults to config defaultModel)"),
196
218
  task: z.string(),
197
219
  files: z.array(z.string()).optional().describe("specific files to attach (inline mode)"),
198
220
  background: z.boolean().optional().describe("run detached and return a job id (default false)"),