@khalilgharbaoui/opencode-claude-code-plugin 0.11.1 → 0.12.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
@@ -70,17 +70,17 @@ The plugin auto-registers the following. They appear in the model picker without
70
70
 
71
71
  | ID | Display name | Context | Output | Reasoning variants | Price × |
72
72
  |---|---|---|---|---|---|
73
- | `claude-haiku-4-5` | Claude Haiku 4.5 | 200k | 8,192 | – | 1× |
74
- | `claude-sonnet-4-5` | Claude Sonnet 4.5 | 1M | 16,384 | low/medium/high/xhigh/max | 3× |
75
- | `claude-sonnet-4-6` | Claude Sonnet 4.6 | 1M | 16,384 | low/medium/high/xhigh/max | 3× |
73
+ | `claude-haiku-4-5` | Claude Haiku 4.5 | 200k | 64,000 | – | 1× |
74
+ | `claude-sonnet-4-5` | Claude Sonnet 4.5 | 200k | 64,000 | low/medium/high/xhigh/max | 3× |
75
+ | `claude-sonnet-4-6` | Claude Sonnet 4.6 | 1M | 128,000 | low/medium/high/xhigh/max | 3× |
76
76
  | `claude-sonnet-5` | Claude Sonnet 5 | 1M | 128,000 | low/medium/high/xhigh/max | 2×* |
77
- | `claude-opus-4-5` | Claude Opus 4.5 | 1M | 16,384 | low/medium/high/xhigh/max | 5× |
78
- | `claude-opus-4-6` | Claude Opus 4.6 | 1M | 16,384 | low/medium/high/xhigh/max | 5× |
79
- | `claude-opus-4-7` | Claude Opus 4.7 | 1M | 16,384 | low/medium/high/xhigh/max | 5× |
80
- | `claude-opus-4-8` | Claude Opus 4.8 | 1M | 16,384 | low/medium/high/xhigh/max | 5× |
77
+ | `claude-opus-4-5` | Claude Opus 4.5 | 200k | 64,000 | low/medium/high/xhigh/max | 5× |
78
+ | `claude-opus-4-6` | Claude Opus 4.6 | 1M | 128,000 | low/medium/high/xhigh/max | 5× |
79
+ | `claude-opus-4-7` | Claude Opus 4.7 | 1M | 128,000 | low/medium/high/xhigh/max | 5× |
80
+ | `claude-opus-4-8` | Claude Opus 4.8 | 1M | 128,000 | low/medium/high/xhigh/max | 5× |
81
81
  | `claude-opus-5` | Claude Opus 5 | 1M | 128,000 | low/medium/high/xhigh/max | 5× |
82
- | `claude-fable-5` | Claude Fable 5 | 1M | 16,384 | low/medium/high/xhigh/max | 10× |
83
- | `claude-mythos-5` | Claude Mythos 5 | 1M | 16,384 | low/medium/high/xhigh/max | 10× |
82
+ | `claude-fable-5` | Claude Fable 5 | 1M | 128,000 | low/medium/high/xhigh/max | 10× |
83
+ | `claude-mythos-5` | Claude Mythos 5 | 1M | 128,000 | low/medium/high/xhigh/max | 10× |
84
84
 
85
85
  `claude-mythos-5` is Mythos-class like Fable 5 but without safety classifiers, and is **limited availability via [Project Glasswing](https://anthropic.com/glasswing)**. It's registered unconditionally; if your Claude account lacks access, `claude --model claude-mythos-5` just errors. Use `claude-fable-5` (generally available) otherwise.
86
86
 
@@ -271,6 +271,7 @@ By default, the plugin proxies `Bash`, `Edit`, `Write`, `WebFetch`, and `Task`.
271
271
  | `"Write"` | `Write` | `mcp__opencode_proxy__write` |
272
272
  | `"WebFetch"` | `WebFetch` | `mcp__opencode_proxy__webfetch` |
273
273
  | `"Task"` | `Agent` | `mcp__opencode_proxy__task` |
274
+ | `"Question"` | `AskUserQuestion` | `mcp__opencode_proxy__question` |
274
275
 
275
276
  ### OpenCode-native subagents
276
277
 
@@ -281,7 +282,21 @@ By default, the plugin proxies `Bash`, `Edit`, `Write`, `WebFetch`, and `Task`.
281
282
  - **Nested tasks:** current opencode defaults `subagent_depth` to `1`, so a first-level child cannot launch another child. Increase top-level `subagent_depth` to permit deeper nesting, and explicitly grant `permission.task` on every subagent that should delegate; opencode otherwise adds a task deny to spawned subagent sessions.
282
283
  - **Background:** `background: true` returns after starting the child and lets opencode notify the parent when it finishes. Current opencode requires `OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true` in the environment of the opencode process. Foreground is the default.
283
284
 
284
- Only those five values are actually proxied; anything else you put in `proxyTools` is ignored. Proxying `Edit` also disables `MultiEdit` — opencode has no batched-edit equivalent, so Claude is forced to fan out into single `Edit` calls that each flow through the permission UI.
285
+ **Steering models to it.** Headless Claude Code CLIs expose no `Agent`/`Task`
286
+ dispatch tool of their own (verified on 2.1.211), while they *do* expose
287
+ `TaskCreate` — a todo tool. So "use a subagent" requests get mis-resolved:
288
+ a todo appears, nothing runs, and the model may still narrate a successful
289
+ dispatch. Two spawn-time countermeasures prevent that. The plugin injects
290
+ opencode's live agent-type list into the `task` proxy description (so the model
291
+ picks a real `subagent_type` instead of guessing a Claude Code name like
292
+ `general-purpose`, and doesn't grep configs to check a subagent exists), and
293
+ appends a system-prompt note naming
294
+ `mcp__opencode_proxy__task` as the only dispatch path — with the ToolSearch
295
+ recovery step for harnesses that defer MCP tool schemas. Both apply per Claude
296
+ process at spawn, and provider options are read once at opencode startup, so
297
+ `proxyTools` changes need a full opencode restart.
298
+
299
+ Only those six values are actually proxied; anything else you put in `proxyTools` is ignored. Proxying `Edit` also disables `MultiEdit` — opencode has no batched-edit equivalent, so Claude is forced to fan out into single `Edit` calls that each flow through the permission UI. The `"Question"` proxy is version-gated on opencode's built-in `question` tool: on builds that lack the registry entry the def is silently dropped (a forwarded call would otherwise render as `⚙ invalid`), so add it only on opencode versions that ship the `question` tool.
285
300
 
286
301
  Without `"Task"` in `proxyTools`, Claude's built-in `Agent` tool stays enabled and Claude orchestrates subagents internally with no opencode child-session visibility. To opt out of all proxying, including Task, use an explicit empty list:
287
302
 
@@ -289,6 +304,48 @@ Without `"Task"` in `proxyTools`, Claude's built-in `Agent` tool stays enabled a
289
304
  "options": { "proxyTools": [] }
290
305
  ```
291
306
 
307
+ ### Subagent todos
308
+
309
+ When Claude works through a multi-step task it emits `TaskCreate` / `TaskUpdate` calls. The plugin translates those into opencode's full-list `todowrite` so the todo panel populates. Inside a **subagent** that translation is blocked unless you say otherwise: opencode's task tool injects `todowrite: false` into the tools dict for any subagent without an explicit rule, so the plugin's synthetic emissions surface as `⚙ invalid todowrite` rows instead of todos. The built-in `general` subagent denies it by default.
310
+
311
+ Grant it per subagent definition in `opencode.json`:
312
+
313
+ ```json
314
+ {
315
+ "agent": {
316
+ "multistep": {
317
+ "description": "Multi-step worker whose progress should be visible as todos",
318
+ "mode": "subagent",
319
+ "model": "claude-code-default/claude-opus-5",
320
+ "permission": {
321
+ "todowrite": "allow",
322
+ "todoread": "allow",
323
+ "task": "deny"
324
+ }
325
+ }
326
+ }
327
+ }
328
+ ```
329
+
330
+ Notes on that example:
331
+
332
+ - `todowrite: "allow"` is the load-bearing line. Without it you get `⚙ invalid` rows, not a broken run.
333
+ - `todoread` is worth allowing too so the subagent can re-read its own list across turns.
334
+ - `task: "deny"` is explicit rather than implied. Leave it denied unless this subagent should itself delegate, in which case set `"allow"` and raise the top-level `subagent_depth` (opencode defaults it to `1`, so a child cannot spawn a grandchild).
335
+ - Provider and agent config are read at startup, so restart opencode fully after editing.
336
+
337
+ The todos render in the **subagent's own session view**, not the parent's panel. Navigate to it in the TUI with `session.child.next` (and back with `session.parent`); run `opencode --print-logs` or check the keybindings if those actions are unbound in your setup.
338
+
339
+ To confirm the data actually landed rather than trusting the UI:
340
+
341
+ ```bash
342
+ sqlite3 ~/.local/share/opencode/opencode.db \
343
+ "select id, parent_id from session order by rowid desc limit 5;"
344
+ # then, with the child session id:
345
+ sqlite3 ~/.local/share/opencode/opencode.db \
346
+ "select tool, state from part where session_id='<child-id>' and tool='todowrite';"
347
+ ```
348
+
292
349
  ### What you get with proxying on
293
350
 
294
351
  - opencode's **permission prompts** for every Bash/Edit/Write/WebFetch call (the default `claude --dangerously-skip-permissions` is NOT applied to proxied tools).
@@ -397,7 +454,29 @@ Set `permissionMode: "plan"` to forward `--permission-mode plan` to Claude. The
397
454
 
398
455
  ## AskUserQuestion
399
456
 
400
- opencode has no native structured ask-question executor to proxy through (unlike `Bash`/`Task`), so the plugin handles `AskUserQuestion` specially:
457
+ opencode ships a built-in `question` tool (`packages/opencode/src/tool/question.ts`) that renders a real TUI form with options and a custom-answer field — near-identical to Claude Code's `AskUserQuestion` (`multiSelect` `multiple`). The plugin can route `AskUserQuestion` through it so the prompt becomes an actual form instead of plain text. Two modes:
458
+
459
+ ### With `"Question"` in `proxyTools` (currently blocked upstream — leave it off)
460
+
461
+ > **Known upstream breakage (opencode 1.15.x through at least 1.18.5).** opencode's `question` TUI form does not render, so the tool blocks until you interrupt the turn. This is not specific to this plugin: native providers hit it identically, and a `--pure` headless server drives the same question end to end successfully (`question.asked` → `GET /question` → `POST /question/{id}/reply` → tool completes), which isolates the fault to the TUI. Tracked upstream as [anomalyco/opencode#36604](https://github.com/anomalyco/opencode/issues/36604) with fix [PR #36603](https://github.com/anomalyco/opencode/pull/36603) (unmerged). Until that lands, enabling `"Question"` trades the working fallback below for a hang. The instructions here describe the intended behavior for when it is fixed.
462
+
463
+ Add `"Question"` to `proxyTools` and grant `permission.question: allow` to the calling agent. Claude's built-in `AskUserQuestion` is disabled via `--disallowedTools`, and the plugin exposes `mcp__opencode_proxy__question` in its place. The model calls the proxy, opencode renders the form, and the operator's answers come back as arrays of selected labels. On builds that lack the `question` registry entry the def is silently dropped at spawn (version gate), and the deny/markdown fallback below applies instead.
464
+
465
+ `proxyTools` replaces the default list rather than adding to it, so repeat the defaults you still want:
466
+
467
+ ```json
468
+ "options": {
469
+ "proxyTools": ["Bash", "Edit", "Write", "WebFetch", "Task", "Question"]
470
+ }
471
+ ```
472
+
473
+ To turn it back off, drop `"Question"` from the list. It is **not** in the default list, so no configuration means the deny/markdown fallback below stays in force.
474
+
475
+ The same spawn-time caveat as `"Task"` applies: provider options are read once at opencode startup, so restart opencode fully after adding it. Question calls get a 30-minute proxy deadline (raise it with `proxyToolTimeoutMs` if you expect to be AFK longer; an expired call comes back as an error, not an answer).
476
+
477
+ ### Without the proxy (default fallback)
478
+
479
+ When `"Question"` is not in `proxyTools` (or the opencode version lacks the `question` tool), the plugin handles `AskUserQuestion` as follows:
401
480
 
402
481
  1. **It renders the full question.** The tool's payload — every question, header, option label, and option description — is emitted as readable markdown into the assistant stream so the user actually sees the choices (same approach as `ExitPlanMode`).
403
482
  2. **It is never auto-allowed at the CLI gate.** Allowing it would let the headless Claude CLI resolve its own question (no TTY → fabricated/empty answer) and proceed on a guess. `controlRequestBehaviorForTool` hard-denies `AskUserQuestion` and returns a message telling the model to **stop and wait for the operator's answer** — end the turn, call no further tools, and never self-answer. (Before v0.7.0 this message also offered an "if the run is non-interactive, proceed with a reasonable guess" fallback. The model could not reliably tell interactive opencode from a headless run and routinely took it, so questions appeared to be skipped — [issue #8](https://github.com/khalilgharbaoui/opencode-claude-code-plugin/issues/8). For genuinely unattended runs, use the `controlRequestToolBehaviors` override below instead.)
@@ -469,7 +548,7 @@ The plugin respects the standard Claude Code thinking env vars. If you set them
469
548
 
470
549
  - **Empty text blocks are dropped.** Claude sometimes opens a `content_block_start` for text but never sends a delta. The plugin no longer emits the empty block (which was triggering Anthropic 400s like `cache_control cannot be set for empty text blocks`).
471
550
  - **Smart incomplete-turn continuation.** By default, the plugin keeps the current opencode stream open and feeds Claude CLI a small internal continuation message when Claude emits a `result` after reasoning/tool activity without a useful visible answer. It still stops normally on final-looking answers, questions, blockers, errors, aborts, or internal safety-budget exhaustion. Disable with `"autoContinueIncompleteTurns": false`.
472
- - **`AskUserQuestion`** from the CLI is converted into plain text content rather than forwarded as a tool call.
551
+ - **`AskUserQuestion`** from the CLI is converted into plain text content rather than forwarded as a tool call — unless `"Question"` is in `proxyTools`, in which case it is routed through opencode's native `question` tool (see [AskUserQuestion](#askuserquestion)).
473
552
  - **Wire-inactivity watchdog.** Once the CLI has produced any content, the stream closes gracefully if stdout goes silent for 60 seconds without a `result` message arriving. Resets on every line received, so long mid-turn pauses (Sonnet between text-end and the next tool_use, for example) are tolerated. On a user-initiated abort, the watchdog shortens to 5 seconds.
474
553
  - **Per-iteration usage.** When the CLI internally retries with tools, the plugin only counts the last iteration's usage so opencode's context accounting stays accurate.
475
554
  - **Lazy `cwd`.** The working directory is re-resolved at every request, so opencode's project-aware behavior works without restarting the plugin.
@@ -602,8 +681,8 @@ Workaround for autonomous compression: trigger it manually with `/dcp compress`
602
681
  - No streaming of tool inputs as they're being constructed (Anthropic's `input_json_delta`); the plugin emits them once complete.
603
682
  - Raw chain-of-thought is not available. Claude 4 family models ship summarized thinking only. See [Extended thinking](#extended-thinking) for the full picture.
604
683
  - Recommended Claude Code CLI: **2.1.142+**. Older CLIs work for everything else but skip the `--thinking-display` flag, so Claude Opus 4.7 turns may render empty Thinking rows. If something breaks after a Claude Code update, the CLI version is the first thing to check.
605
- - **Foreground Task calls have a 30-minute proxy timeout.** The same timeout is written into Claude's generated HTTP MCP configuration so long-running opencode subagents are not cut off by Claude's 60-second default. For independent longer work, use `background: true` after enabling opencode's experimental background-subagent flag.
606
- - **Subagent todos require explicit permission.** opencode's task tool gates `todowrite` per subagent: without a `permission: { todowrite: "allow" }` rule on the subagent definition, opencode injects `todowrite: false` into the tools dict and the plugin's synthetic `todowrite` emissions surface as `⚙ invalid todowrite` rows. The built-in `general` subagent denies `todowrite` by default; use a custom subagent for parallel work that needs todo visibility. Subagent todos render inline in the **subagent's** session view (navigate with the TUI's `session.child.next` / `session.parent` commands), not in the parent session's panel.
684
+ - **Foreground Task calls have a 60-minute proxy deadline** (configurable via [`proxyToolTimeoutMs`](#per-tool-proxy-timeouts)). A ceiling covering the longest configured deadline is written into Claude's generated HTTP MCP configuration so long-running opencode subagents are not cut off by Claude's 60-second default. For independent longer work, use `background: true` after enabling opencode's experimental background-subagent flag.
685
+ - **Subagent todos require explicit permission.** See [Subagent todos](#subagent-todos) for the rule and a working config.
607
686
 
608
687
  ---
609
688
 
package/dist/index.d.ts CHANGED
@@ -258,16 +258,22 @@ interface ClaudeCodeProviderSettings {
258
258
  * opencode's tool executor (with its native permission UI) and returns
259
259
  * the result.
260
260
  *
261
- * Supported: `bash`, `write`, `edit`, `webfetch`, `task`. Leave empty or unset to disable proxying.
262
- *
263
- * `task` proxies Claude CLI's `Agent` (subagent dispatch) tool through
264
- * opencode's `task` tool, so subagent calls run under opencode's
265
- * configured subagent set (build/general/custom) with opencode's
266
- * permission and lifecycle handling, instead of Claude CLI's
267
- * internal-only general-purpose / Explore / Plan options. The calling
268
- * agent must have `permission.task: allow` for the target subagent
269
- * (see opencode's agent docs).
270
- */
261
+ * Supported: `bash`, `write`, `edit`, `webfetch`, `task`, `question`. Leave empty or unset to disable proxying.
262
+ *
263
+ * `task` proxies Claude CLI's `Agent` (subagent dispatch) tool through
264
+ * opencode's `task` tool, so subagent calls run under opencode's
265
+ * configured subagent set (build/general/custom) with opencode's
266
+ * permission and lifecycle handling, instead of Claude CLI's
267
+ * internal-only general-purpose / Explore / Plan options. The calling
268
+ * agent must have `permission.task: allow` for the target subagent
269
+ * (see opencode's agent docs).
270
+ *
271
+ * `question` proxies Claude CLI's `AskUserQuestion` through opencode's
272
+ * native `question` tool (TUI form with options + custom answer). The
273
+ * calling agent must have `permission.question: allow`. Version-gated:
274
+ * silently dropped on opencode builds that lack the `question` registry
275
+ * entry, in which case the deny/markdown fallback applies.
276
+ */
271
277
  proxyTools?: string[];
272
278
  /**
273
279
  * Per-tool proxy call timeouts in milliseconds, keyed by the proxy tool
@@ -505,6 +511,23 @@ declare class ClaudeCodeLanguageModel implements LanguageModelV3 {
505
511
  * client is unavailable, or no MCP servers are configured.
506
512
  */
507
513
  private resolvedProxyMcpTools;
514
+ /**
515
+ * Live tool info derived from a single `client.tool.list()` fetch:
516
+ *
517
+ * - `taskDescription`: opencode's `task` tool description exactly as the
518
+ * registry renders it for native models, including the "Available
519
+ * agent types" list. Overlaid onto the static `task` proxy def so
520
+ * Claude sees the same subagent catalog native models see, instead
521
+ * of hunting through config files.
522
+ * - `questionDescription` / `hasQuestion`: opencode's `question` tool
523
+ * description and whether the registry has the entry at all. Older
524
+ * builds lack it, in which case a `mcp__opencode_proxy__question`
525
+ * call resolves to `⚙ invalid`; the version gate drops the def.
526
+ *
527
+ * Returns undefined/false when the SDK client is unavailable (direct
528
+ * AI-SDK use, tests) so the static defs stand.
529
+ */
530
+ private fetchLiveToolInfo;
508
531
  /**
509
532
  * Create a proxy MCP server for a single active Claude process/session.
510
533
  * The process lifecycle owns the server lifecycle via session-manager.
package/dist/index.js CHANGED
@@ -2071,8 +2071,10 @@ var SERVER_NAME = "opencode_proxy";
2071
2071
  var PROXY_TOOL_PREFIX = `mcp__${SERVER_NAME}__`;
2072
2072
  var PROXY_DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
2073
2073
  var PROXY_PER_TOOL_DEFAULT_TIMEOUT_MS = {
2074
- task: 60 * 60 * 1e3
2074
+ task: 60 * 60 * 1e3,
2075
2075
  // 60 min
2076
+ question: 30 * 60 * 1e3
2077
+ // 30 min
2076
2078
  };
2077
2079
  var MAX_PROXY_TIMEOUT_MS = 2 ** 31 - 1;
2078
2080
  function resolveProxyCallTimeoutMs(toolName, input, overrides) {
@@ -2117,6 +2119,51 @@ function buildProxyTimeoutError(toolName, ms) {
2117
2119
  }
2118
2120
  return new Error(base);
2119
2121
  }
2122
+ var TASK_PROXY_NOTE = "This is the ONLY tool that dispatches opencode subagents (including user @-mentions). Claude Code's built-in TaskCreate/TaskUpdate manage a local todo list and cannot dispatch subagents. Do not search config files to verify a subagent type exists \u2014 invalid types fail fast with a clear error. Foreground calls block until the subagent finishes; set `background` to request opencode's background execution mode. Task calls get a 60-minute proxy deadline by default (configurable via proxyToolTimeoutMs).";
2123
+ var AGENT_TYPES_HEADING = "Available agent types";
2124
+ var AGENT_BLURB_LIMIT = 140;
2125
+ var QUESTION_PROXY_NOTE = "This routes structured questions through opencode's native `question` tool, which renders a TUI form with the options you provide and blocks until the operator answers. Claude Code's built-in AskUserQuestion is disabled in this environment; this proxy is the ONLY way to ask the operator for a decision or clarification. Answers come back as arrays of selected labels (set `multiple: true` to allow more than one). If the operator dismisses the form the call returns an error \u2014 treat that as 'no answer' and stop, do not guess. Question calls get a 30-minute proxy deadline by default (configurable via proxyToolTimeoutMs); for long-AFK scenarios prefer fewer, high-signal questions.";
2126
+ function extractAgentTypeList(liveDescription) {
2127
+ const live = liveDescription?.trim();
2128
+ if (!live) return void 0;
2129
+ const start = live.indexOf(AGENT_TYPES_HEADING);
2130
+ if (start === -1) return void 0;
2131
+ const entries = [];
2132
+ for (const raw of live.slice(start).split("\n")) {
2133
+ const match = /^-\s*([^:]+):\s*(.+)$/.exec(raw.trim());
2134
+ if (!match) continue;
2135
+ const name = match[1].trim();
2136
+ const blurb = match[2].trim();
2137
+ entries.push(
2138
+ `- ${name}: ${blurb.length > AGENT_BLURB_LIMIT ? `${blurb.slice(0, AGENT_BLURB_LIMIT).trimEnd()}\u2026` : blurb}`
2139
+ );
2140
+ }
2141
+ if (entries.length === 0) return void 0;
2142
+ return `Valid subagent_type values, from opencode's live registry \u2014 anything else fails:
2143
+ ${entries.join("\n")}`;
2144
+ }
2145
+ function overlayTaskProxyDescription(tools, liveDescription) {
2146
+ const agentTypes = extractAgentTypeList(liveDescription);
2147
+ if (!agentTypes) return tools;
2148
+ return tools.map(
2149
+ (t) => t.name === "task" ? { ...t, description: `${agentTypes}
2150
+
2151
+ ${t.description}` } : t
2152
+ );
2153
+ }
2154
+ function overlayQuestionProxyDescription(tools, liveDescription) {
2155
+ const live = liveDescription?.trim();
2156
+ if (!live) return tools;
2157
+ return tools.map(
2158
+ (t) => t.name === "question" ? { ...t, description: `${live}
2159
+
2160
+ ${QUESTION_PROXY_NOTE}` } : t
2161
+ );
2162
+ }
2163
+ function filterQuestionProxyByOpencodeSupport(tools, opencodeHasQuestion) {
2164
+ if (opencodeHasQuestion) return tools;
2165
+ return tools.filter((t) => t.name !== "question");
2166
+ }
2120
2167
  var DEFAULT_PROXY_TOOLS = [
2121
2168
  {
2122
2169
  name: "bash",
@@ -2209,7 +2256,7 @@ var DEFAULT_PROXY_TOOLS = [
2209
2256
  },
2210
2257
  {
2211
2258
  name: "task",
2212
- description: "Launch an opencode subagent to handle a complex multi-step task autonomously. Routed through opencode's task tool so subagent orchestration, permission, and lifecycle are handled by opencode. Use `subagent_type` to pick which configured subagent runs (e.g. `build`, `general`, `explore`, or any custom subagent declared in opencode.json). Foreground calls block until the subagent finishes; set `background` to request opencode's background execution mode. Task calls get a 60-minute proxy deadline by default (configurable via proxyToolTimeoutMs).",
2259
+ description: "Launch an opencode subagent to handle a complex multi-step task autonomously. Routed through opencode's task tool so subagent orchestration, permission, and lifecycle are handled by opencode. Use `subagent_type` to pick which configured subagent runs (e.g. `build`, `general`, `explore`, or any custom subagent declared in opencode.json). " + TASK_PROXY_NOTE,
2213
2260
  inputSchema: {
2214
2261
  type: "object",
2215
2262
  properties: {
@@ -2240,6 +2287,56 @@ var DEFAULT_PROXY_TOOLS = [
2240
2287
  },
2241
2288
  required: ["description", "prompt", "subagent_type"]
2242
2289
  }
2290
+ },
2291
+ {
2292
+ name: "question",
2293
+ description: "Ask the operator structured questions with options and receive their answers back. Routed through opencode's native `question` tool so the prompt renders as a real TUI form (with options and a custom-answer field) instead of a plain text turn. Use this when you need a decision, clarification, or preference from the operator mid-task. " + QUESTION_PROXY_NOTE,
2294
+ inputSchema: {
2295
+ type: "object",
2296
+ properties: {
2297
+ questions: {
2298
+ type: "array",
2299
+ description: "Questions to ask.",
2300
+ items: {
2301
+ type: "object",
2302
+ properties: {
2303
+ question: {
2304
+ type: "string",
2305
+ description: "Complete question."
2306
+ },
2307
+ header: {
2308
+ type: "string",
2309
+ description: "Very short label (max 30 chars)."
2310
+ },
2311
+ options: {
2312
+ type: "array",
2313
+ description: "Available choices.",
2314
+ items: {
2315
+ type: "object",
2316
+ properties: {
2317
+ label: {
2318
+ type: "string",
2319
+ description: "Display text (1-5 words, concise)."
2320
+ },
2321
+ description: {
2322
+ type: "string",
2323
+ description: "Explanation of choice."
2324
+ }
2325
+ },
2326
+ required: ["label", "description"]
2327
+ }
2328
+ },
2329
+ multiple: {
2330
+ type: "boolean",
2331
+ description: "Allow selecting multiple choices. Defaults to false."
2332
+ }
2333
+ },
2334
+ required: ["question", "header", "options"]
2335
+ }
2336
+ }
2337
+ },
2338
+ required: ["questions"]
2339
+ }
2243
2340
  }
2244
2341
  ];
2245
2342
  async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS, timeoutOverrides) {
@@ -2357,23 +2454,14 @@ async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS, timeoutOverride
2357
2454
  if (timer) clearTimeout(timer);
2358
2455
  pending.delete(callId);
2359
2456
  });
2360
- if (result.kind === "error") {
2361
- writeJson(res, {
2362
- jsonrpc: "2.0",
2363
- id: requestId,
2364
- result: {
2365
- content: [{ type: "text", text: result.message }],
2366
- isError: true
2367
- }
2368
- });
2369
- return;
2370
- }
2457
+ const text = result.kind === "error" ? result.message : result.text;
2458
+ const isError = result.kind === "error" || result.isError === true;
2371
2459
  writeJson(res, {
2372
2460
  jsonrpc: "2.0",
2373
2461
  id: requestId,
2374
2462
  result: {
2375
- content: [{ type: "text", text: result.text }],
2376
- isError: result.isError === true
2463
+ content: [{ type: "text", text }],
2464
+ isError
2377
2465
  }
2378
2466
  });
2379
2467
  return;
@@ -2501,7 +2589,13 @@ function disallowedToolFlags(tools) {
2501
2589
  glob: ["Glob"],
2502
2590
  grep: ["Grep"],
2503
2591
  webfetch: ["WebFetch"],
2504
- task: ["Agent"]
2592
+ task: ["Agent"],
2593
+ // `question` disables Claude Code's built-in `AskUserQuestion` so the
2594
+ // structured-questions path flows through opencode's native `question`
2595
+ // tool instead — same UI/permission/audit benefits as the other
2596
+ // proxies. Without this, the model can call both and the two paths
2597
+ // diverge (opencode's form vs the headless deny-and-render fallback).
2598
+ question: ["AskUserQuestion"]
2505
2599
  };
2506
2600
  const out = [];
2507
2601
  const seen = /* @__PURE__ */ new Set();
@@ -2884,6 +2978,22 @@ than pausing for user confirmation between subtasks. End the turn only
2884
2978
  when the task is done, you need clarification on intent, or you hit a real
2885
2979
  blocker. The user can interrupt or abort at any time; turn endings should
2886
2980
  mark meaningful checkpoints, not every completed substep.`;
2981
+ var SUBAGENT_DISPATCH_HINT = `## opencode subagents
2982
+
2983
+ Subagent dispatch in this environment goes through exactly one tool: \`mcp__opencode_proxy__task\`.
2984
+
2985
+ - When the user mentions \`@<agent>\` or an instruction says "call the task tool with subagent: <name>", call \`mcp__opencode_proxy__task\` with \`subagent_type: "<name>"\`.
2986
+ - If that tool is not in your visible tool list it is deferred \u2014 load it with ToolSearch (\`select:mcp__opencode_proxy__task\`), then call it.
2987
+ - Claude Code's built-in TaskCreate/TaskUpdate/TaskList manage a local todo list. They cannot dispatch subagents; creating a task there runs nothing. Never report a subagent as dispatched unless \`mcp__opencode_proxy__task\` returned its result.
2988
+ - Do not verify a subagent's existence by searching config files \u2014 the tool's description lists the available agent types, and invalid types fail fast with a clear error.`;
2989
+ var QUESTION_PROXY_HINT = `## Asking the operator questions
2990
+
2991
+ Structured questions in this environment go through exactly one tool: \`mcp__opencode_proxy__question\`.
2992
+
2993
+ - When you need to ask the operator a question with options, call \`mcp__opencode_proxy__question\` with a \`questions\` array (each item has \`question\`, \`header\`, \`options\` of \`{label, description}\`, and optional \`multiple\`).
2994
+ - If that tool is not in your visible tool list it is deferred \u2014 load it with ToolSearch (\`select:mcp__opencode_proxy__question\`), then call it by its FULL name.
2995
+ - Do NOT call bare \`question\` \u2014 that is not a tool. Always use the full \`mcp__opencode_proxy__question\` name when invoking it.
2996
+ - Claude Code's built-in \`AskUserQuestion\` is disabled in this environment; the proxy is the only way to ask structured questions.`;
2887
2997
  var CLAUDE_CLI_CONTEXT_NOTE = `## Runtime environment: Claude Code CLI
2888
2998
 
2889
2999
  You are running via the Claude Code CLI (not a direct API call). This affects context management:
@@ -3058,6 +3168,35 @@ var ClaudeCodeLanguageModel = class {
3058
3168
  }
3059
3169
  return out.length > 0 ? out : null;
3060
3170
  }
3171
+ /**
3172
+ * Live tool info derived from a single `client.tool.list()` fetch:
3173
+ *
3174
+ * - `taskDescription`: opencode's `task` tool description exactly as the
3175
+ * registry renders it for native models, including the "Available
3176
+ * agent types" list. Overlaid onto the static `task` proxy def so
3177
+ * Claude sees the same subagent catalog native models see, instead
3178
+ * of hunting through config files.
3179
+ * - `questionDescription` / `hasQuestion`: opencode's `question` tool
3180
+ * description and whether the registry has the entry at all. Older
3181
+ * builds lack it, in which case a `mcp__opencode_proxy__question`
3182
+ * call resolves to `⚙ invalid`; the version gate drops the def.
3183
+ *
3184
+ * Returns undefined/false when the SDK client is unavailable (direct
3185
+ * AI-SDK use, tests) so the static defs stand.
3186
+ */
3187
+ async fetchLiveToolInfo() {
3188
+ const items = await fetchOpencodeToolList(
3189
+ this.config.provider,
3190
+ this.modelId,
3191
+ this.config.cwd
3192
+ );
3193
+ const question = items?.find((item) => item.id === "question");
3194
+ return {
3195
+ taskDescription: items?.find((item) => item.id === "task")?.description,
3196
+ questionDescription: question?.description,
3197
+ hasQuestion: !!question
3198
+ };
3199
+ }
3061
3200
  /**
3062
3201
  * Create a proxy MCP server for a single active Claude process/session.
3063
3202
  * The process lifecycle owns the server lifecycle via session-manager.
@@ -3929,11 +4068,53 @@ ${plan}
3929
4068
  discovery.allEnabledServerNames
3930
4069
  );
3931
4070
  const excludeServers = proxyMcpTools ? new Set(discovery.allEnabledServerNames) : void 0;
3932
- const combinedProxyTools = resolvedProxy || proxyMcpTools ? [...resolvedProxy ?? [], ...proxyMcpTools ?? []] : null;
4071
+ const taskProxyEnabled = resolvedProxy?.some((t) => t.name === "task") ?? false;
4072
+ const questionProxyEnabled = resolvedProxy?.some((t) => t.name === "question") ?? false;
4073
+ const liveToolInfo = taskProxyEnabled || questionProxyEnabled ? await self.fetchLiveToolInfo() : {
4074
+ taskDescription: void 0,
4075
+ questionDescription: void 0,
4076
+ hasQuestion: false
4077
+ };
4078
+ let enrichedProxy = resolvedProxy;
4079
+ if (enrichedProxy && taskProxyEnabled) {
4080
+ enrichedProxy = overlayTaskProxyDescription(
4081
+ enrichedProxy,
4082
+ liveToolInfo.taskDescription
4083
+ );
4084
+ log.info("task proxy description overlay", {
4085
+ applied: Boolean(liveToolInfo.taskDescription),
4086
+ liveDescriptionLength: liveToolInfo.taskDescription?.length ?? 0,
4087
+ listsAgentTypes: Boolean(
4088
+ liveToolInfo.taskDescription?.includes(
4089
+ "Available agent types"
4090
+ )
4091
+ )
4092
+ });
4093
+ }
4094
+ if (enrichedProxy && questionProxyEnabled) {
4095
+ enrichedProxy = overlayQuestionProxyDescription(
4096
+ enrichedProxy,
4097
+ liveToolInfo.hasQuestion ? liveToolInfo.questionDescription : void 0
4098
+ );
4099
+ enrichedProxy = filterQuestionProxyByOpencodeSupport(
4100
+ enrichedProxy,
4101
+ liveToolInfo.hasQuestion
4102
+ );
4103
+ log.info("question proxy version gate", {
4104
+ opencodeHasQuestion: liveToolInfo.hasQuestion,
4105
+ kept: liveToolInfo.hasQuestion
4106
+ });
4107
+ }
4108
+ const combinedList = [
4109
+ ...enrichedProxy ?? [],
4110
+ ...proxyMcpTools ?? []
4111
+ ];
4112
+ const combinedProxyTools = combinedList.length > 0 ? combinedList : null;
3933
4113
  if (!proxyServer && combinedProxyTools) {
3934
4114
  proxyServer = await self.ensureProxyServer(combinedProxyTools, sk);
3935
4115
  }
3936
- const proxyDisallowed = resolvedProxy ? disallowedToolFlags(resolvedProxy) : [];
4116
+ const questionProxyActive = enrichedProxy?.some((t) => t.name === "question") ?? false;
4117
+ const proxyDisallowed = enrichedProxy ? disallowedToolFlags(enrichedProxy) : [];
3937
4118
  const extraDisallowed = [];
3938
4119
  if (self.config.webSearch === "disabled") extraDisallowed.push("WebSearch");
3939
4120
  const allDisallowed = [...proxyDisallowed, ...extraDisallowed];
@@ -3946,7 +4127,11 @@ ${plan}
3946
4127
  const systemPromptFile = activeProcess ? void 0 : buildAppendedSystemPrompt(
3947
4128
  cwd,
3948
4129
  self.config.multiStepContinuation !== false,
3949
- extractSystemMessages(options.prompt)
4130
+ [
4131
+ ...extractSystemMessages(options.prompt),
4132
+ ...taskProxyEnabled ? [SUBAGENT_DISPATCH_HINT] : [],
4133
+ ...questionProxyActive ? [QUESTION_PROXY_HINT] : []
4134
+ ]
3950
4135
  );
3951
4136
  cliArgs = buildCliArgs({
3952
4137
  sessionKey: sk,
@@ -5162,21 +5347,21 @@ var defaultModels = {
5162
5347
  family: "haiku",
5163
5348
  reasoning: false,
5164
5349
  context: 2e5,
5165
- output: 8192,
5350
+ output: 64e3,
5166
5351
  cost: haikuCost,
5167
5352
  multiplier: 1,
5168
- releaseDate: "2024-10-22"
5353
+ releaseDate: "2025-10-01"
5169
5354
  }),
5170
5355
  "claude-sonnet-4-5": defineModel({
5171
5356
  id: "claude-sonnet-4-5",
5172
5357
  name: "Claude Sonnet 4.5",
5173
5358
  family: "sonnet",
5174
5359
  reasoning: true,
5175
- context: 1e6,
5176
- output: 16384,
5360
+ context: 2e5,
5361
+ output: 64e3,
5177
5362
  cost: sonnetCost,
5178
5363
  multiplier: 3,
5179
- releaseDate: "2025-04-14"
5364
+ releaseDate: "2025-09-29"
5180
5365
  }),
5181
5366
  "claude-sonnet-4-6": defineModel({
5182
5367
  id: "claude-sonnet-4-6",
@@ -5184,7 +5369,7 @@ var defaultModels = {
5184
5369
  family: "sonnet",
5185
5370
  reasoning: true,
5186
5371
  context: 1e6,
5187
- output: 16384,
5372
+ output: 128e3,
5188
5373
  cost: sonnetCost,
5189
5374
  multiplier: 3,
5190
5375
  releaseDate: "2025-06-19"
@@ -5205,11 +5390,11 @@ var defaultModels = {
5205
5390
  name: "Claude Opus 4.5",
5206
5391
  family: "opus",
5207
5392
  reasoning: true,
5208
- context: 1e6,
5209
- output: 16384,
5393
+ context: 2e5,
5394
+ output: 64e3,
5210
5395
  cost: opusCost,
5211
5396
  multiplier: 5,
5212
- releaseDate: "2025-04-14"
5397
+ releaseDate: "2025-11-01"
5213
5398
  }),
5214
5399
  "claude-opus-4-6": defineModel({
5215
5400
  id: "claude-opus-4-6",
@@ -5217,7 +5402,7 @@ var defaultModels = {
5217
5402
  family: "opus",
5218
5403
  reasoning: true,
5219
5404
  context: 1e6,
5220
- output: 16384,
5405
+ output: 128e3,
5221
5406
  cost: opusCost,
5222
5407
  multiplier: 5,
5223
5408
  releaseDate: "2025-06-19"
@@ -5228,7 +5413,7 @@ var defaultModels = {
5228
5413
  family: "opus",
5229
5414
  reasoning: true,
5230
5415
  context: 1e6,
5231
- output: 16384,
5416
+ output: 128e3,
5232
5417
  cost: opusCost,
5233
5418
  multiplier: 5,
5234
5419
  releaseDate: "2025-07-16"
@@ -5239,7 +5424,7 @@ var defaultModels = {
5239
5424
  family: "opus",
5240
5425
  reasoning: true,
5241
5426
  context: 1e6,
5242
- output: 16384,
5427
+ output: 128e3,
5243
5428
  cost: opusCost,
5244
5429
  multiplier: 5,
5245
5430
  releaseDate: "2026-05-28"
@@ -5261,7 +5446,7 @@ var defaultModels = {
5261
5446
  family: "fable",
5262
5447
  reasoning: true,
5263
5448
  context: 1e6,
5264
- output: 16384,
5449
+ output: 128e3,
5265
5450
  cost: fableCost,
5266
5451
  multiplier: 10,
5267
5452
  releaseDate: "2026-06-09"
@@ -5276,7 +5461,7 @@ var defaultModels = {
5276
5461
  family: "mythos",
5277
5462
  reasoning: true,
5278
5463
  context: 1e6,
5279
- output: 16384,
5464
+ output: 128e3,
5280
5465
  cost: fableCost,
5281
5466
  multiplier: 10,
5282
5467
  releaseDate: "2026-06-09"