@khalilgharbaoui/opencode-claude-code-plugin 0.59.0 → 0.60.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khalilgharbaoui/opencode-claude-code-plugin",
3
- "version": "0.59.0",
3
+ "version": "0.60.1",
4
4
  "description": "Claude Code CLI provider plugin for opencode",
5
5
  "homepage": "https://opencode-claude-code-plugin.dev/",
6
6
  "funding": {
@@ -119,7 +119,7 @@ Defaults below describe normal headless opencode use when the key is absent.
119
119
  | `baseCliPath` | string | unset/derived | The `cliPath` before the per-account wrapper substitution, so a failover can build another account's wrapper on the same binary. Supplied by the config hook. Do not hand-wire it. |
120
120
  | `accountInProcess` | boolean | unset/derived | Set by the config hook on Windows, where there is no per-account wrapper script: the spawn exports `CLAUDE_CONFIG_DIR` itself and strips the `@<account>` marker off `--model`. Do not hand-wire it. |
121
121
  | `subagentDispatch` | `"ask"` / `"off"` | `"off"` | **Opt-in: only an explicit `"ask"` raises the form**, so unset and `"off"` are identical and a dispatch is handed to opencode exactly as it is today. With `"ask"`, a proxied `task` or `task_batch` call is held at the drain and the turn ends on opencode's native `question` form asking how the subagents should run; the real `task` calls are released, with the answer applied, on the next step of the SAME opencode turn. Always exactly one question first (`Same as last time` when this conversation has one, `Default`, four curated model/effort combos, `Customise…`), so the common answer is one click. `Customise…` opens a second form with one row per agent type plus one account row when more than one account is offered. That account row also offers `Per type…` when the dispatch has more than one agent type, and a type row with more than one task offers `Per task…`; either one opens a third form, and asking for both gives one third form carrying the per-type account rows followed by the per-task rows. Every row takes a typed answer too: a model id, an effort level, an `@account`, or any combination (`claude-opus-5-5 max @worker`); anything unrecognised keeps that row's default with a NOTICE. **The account is choosable at three widths and the narrowest wins**: the dispatch (`Account` row), the agent type (`Per type…` or `@name` on the type's row) and one task (`@name` on its row in the third form). Every account answer, typed ones included, is checked against the same same-group candidate list. The answer applies to each child's own `claude` spawn (`--model`, `CLAUDE_CODE_EFFORT_LEVEL`, and the account's wrapper and `CLAUDE_CONFIG_DIR`) and beats `forceModel`, `defaultSubagentModel` and the inherited effort, because it is about this dispatch. A child is matched to its task by the dispatching session, the subagent type and the task prompt, verified against `parentID`. A dismissed or unanswered form dispatches with the defaults and writes one `▌ **subagent dispatch:**` note; the dispatch is never lost. Never on compaction turns, never inside a child session (a subagent follows the choice its own parent made for it), and only where opencode's registry has the `question` entry. `Same as last time` is per opencode session and per agent type and is kept in `$XDG_STATE_HOME/opencode-claude-code-plugin/subagent-dispatch.json` (`0600`, capped at 128 sessions and 32 agent types each, pruned at 30 days, merged rather than overwritten between opencode processes, deleted with the session), so it survives an opencode restart; only the model id, effort level and account name are stored. A remembered model this install no longer has, or an account no longer configured or no longer in the group, is dropped from the row and named in one note. Both transports, both opencode majors. |
122
- | `subagentDispatchCrossGroup` | boolean | `false` | Let `subagentDispatch`'s account row offer accounts outside the dispatching account's own `accountGroups` group. **Off by default and never implied by anything else**: a subagent is handed the task text the main agent writes, which can quote the conversation, and it reads the repository, so offering another group is offering that group the conversation by another route. Does nothing when `accountGroups` is unset, where every account is already one group, or when `subagentDispatch` is off. |
122
+ | `subagentDispatchCrossGroup` | boolean | `false` | Let `subagentDispatch`'s account row offer accounts outside the dispatching account's own `accountGroups` group, and on opencode 2 let a `task` call's own `model` name one. **Off by default and never implied by anything else**: a subagent is handed the task text the main agent writes, which can quote the conversation, and it reads the repository, so offering another group is offering that group the conversation by another route. Does nothing when `accountGroups` is unset, where every account is already one group, or when `subagentDispatch` is off. |
123
123
  | `defaultSubagentModel` | string | unset | Seed-config default for discovered `mode: subagent` agents without a full `provider/model` pin; `forceModel` takes precedence. Keeps the caller's account. Unknown ids warn and keep the inherited model. Not independently read per expanded account. |
124
124
  | `defaultSubagentCacheTtl` | string | unset | Prompt cache TTL (`5m` / `1h`) for discovered `mode: subagent` agents that declare no `cacheTtl`; the agent's own value takes precedence. Unset leaves the CLI's default (1 hour on a subscription). Unknown values warn and change nothing. Headless and interactive spawns, never compaction. |
125
125
  | `defaultSubagentMaxTurns` | number | unset | `--max-turns` for discovered `mode: subagent` agents that declare no `maxTurns`; the agent's own value wins. Counted by the CLI per user message, so safe on a reused process. CLI 2.1.280+; headless only (print-only flag, the interactive transport WARNs once). Part of the session key. |
@@ -297,7 +297,7 @@ Same package, same config. 2.x's native key is `plugins` (plural), but it still
297
297
  - Check the major first with `opencode --version`. `plugin` is read by both majors (recorded in `docs/agents-history.md` #g39); `plugins` is read by 2.x only.
298
298
  - Provider settings, lowest precedence first: `provider.claude-code.options` (1.x's spelling, still read on 2.x), `provider.claude-code.settings`, `providers.claude-code.settings` (2.x's own), and the plugin entry's own `options`, which wins over all three. Any option of this plugin can sit in any of them; the plugin entry is the usual home for `accounts`: `{"package": "@khalilgharbaoui/opencode-claude-code-plugin", "options": {"accounts": ["work"]}}`.
299
299
  - A local checkout is loaded by pointing `plugins` at its **`dist`** directory, never the repository root: 2.x resolves a configured plugin path as `<dir>/server` or `<dir>/index`.
300
- - Known 2.x differences: there is no todo panel (2.x has no `todowrite` tool, so the sidebar's progress bar is always the time estimate), `bridgeMcpOauthTokens` finds nothing (2.x's plugin API can activate a stored credential but not read one), and Claude's `EnterPlanMode`, `ExitPlanMode` and `NotebookEdit` calls get no tool row (2.x rejects an unknown tool name). Everything else behaves as on 1.x, including `/btw` answered inside the running turn, a permission prompt not counting against a proxied call's deadline, a stopped turn releasing its parked proxied calls, and warnings shown as toasts: the plugin reads busy and idle off opencode 2's event stream (`session.execution.*`) and sends warnings over its plugin RPC channel (measured on 2.0.22 and 2.0.31). Do not set `hostApi`; the 2.x entrypoint sets it, and forcing it on 1.x breaks every proxied tool call.
300
+ - Known 2.x differences: a `task` call can carry its own `model` (2.x's `subagent` tool takes one; 1.x's `task` does not), there is no todo panel (2.x has no `todowrite` tool, so the sidebar's progress bar is always the time estimate), `bridgeMcpOauthTokens` finds nothing (2.x's plugin API can activate a stored credential but not read one), and Claude's `EnterPlanMode`, `ExitPlanMode` and `NotebookEdit` calls get no tool row (2.x rejects an unknown tool name). Everything else behaves as on 1.x, including `/btw` answered inside the running turn, a permission prompt not counting against a proxied call's deadline, a stopped turn releasing its parked proxied calls, and warnings shown as toasts: the plugin reads busy and idle off opencode 2's event stream (`session.execution.*`) and sends warnings over its plugin RPC channel (measured on 2.0.22 and 2.0.31). Do not set `hostApi`; the 2.x entrypoint sets it, and forcing it on 1.x breaks every proxied tool call.
301
301
  - Every form the plugin raises is answered through 2.x's own `Form` surface rather than a question tool, which changes nothing the operator does. The account-switch form and the plan-mode approval were measured live on 2.0.22, and the `subagentDispatch` form (including `Customise…`, `Per type…` accounts and `Same as last time` across a restart) on 2026-10-08. The by-hand account carry and the `accountGroups` guard were measured live there the same day: both behave exactly as on 1.x.
302
302
 
303
303
  #### V2 MCP and Code Mode
@@ -651,8 +651,10 @@ Or once for every discovered subagent without a full provider/model pin:
651
651
  } } } }
652
652
  ```
653
653
 
654
- Rules, in order: `forceModel` wins; else `mode: subagent` with `defaultSubagentModel`
655
- set; else untouched. An agent with `model: <provider>/<id>` is left exactly as written,
654
+ Rules, in order: on opencode 2 a model named for that dispatch in the conversation
655
+ (the `task` call's `model`, recipe "One subagent on a model the user names") wins over
656
+ everything here; else `forceModel` wins; else
657
+ `mode: subagent` with `defaultSubagentModel` set; else untouched. An agent with `model: <provider>/<id>` is left exactly as written,
656
658
  account and all (`model: claude-code-work/claude-opus-5@work` pins the account too).
657
659
  Undeclared built-ins are not discovered; a user definition with a built-in name can
658
660
  enter the registry and is subject to these rules. This is not a built-in-name denylist.
@@ -691,6 +693,67 @@ the opencode schema requires it. Markdown fallback reads top-level scalar fields
691
693
  | `maxBudgetUsd` | Positive dollars, passed as `--max-budget-usd` (per process life, reset on resume). Discovered `mode: subagent` agents only; on a main agent it is refused with a WARN. Headless only. |
692
694
  | `fallbackModels` | Ordered registered bare model ids to try when this agent's model is refused. Both YAML spellings (`[a, b]` or a `- ` block). Replaces the provider-level `fallbackModels` rather than extending it. Keeps the caller's account; an entry carrying `@account` has it stripped. Unknown ids warn and are skipped. |
693
695
 
696
+ ### One subagent on a model the user names (opencode 2)
697
+
698
+ On opencode 2 the user can choose a subagent's model in the message itself, with no
699
+ configuration: the proxied `task` (and each `task_batch.tasks` item) takes an optional
700
+ `model`, forwarded to opencode's own `subagent` tool, which resolves it by exact id
701
+ against the models opencode has. opencode 1's `task` has no such field, so the plugin
702
+ leaves it out there; on 1.x point the user at `forceModel`, `defaultSubagentModel` or
703
+ `subagentDispatch: "ask"` instead. (PR #123, h #g254, h #g255.)
704
+
705
+ opencode's form is `providerID/modelID`, plus `#low`, `#medium`, `#high`, `#xhigh` or
706
+ `#max` for a reasoning variant (every model has them except `claude-haiku-4-5`):
707
+
708
+ | Install | Reference |
709
+ |---|---|
710
+ | No `accounts` | `claude-code/claude-sonnet-5-5` |
711
+ | `accounts`, the default account | `claude-code-default/claude-sonnet-5-5` |
712
+ | `accounts`, another account | `claude-code-<account>/claude-sonnet-5-5@<account>` |
713
+
714
+ A bare model id (`claude-sonnet-5-5`, `claude-sonnet-5-5#high`) is completed to the
715
+ reference this conversation's own provider serves (the first column's provider and
716
+ marker) before opencode sees it, so the subagent stays on the conversation's account.
717
+ Only an id the plugin registers is completed, and only with no `@` marker or the
718
+ provider's own; a full reference, another account's marker and an unknown id reach
719
+ opencode as written. Use the full reference only for another account or provider.
720
+
721
+ Tell the user to name the model by id: "Run the reviewer subagent on
722
+ `claude-sonnet-5-5#high`." Two ids in one request ("review it on `claude-opus-5-5` and
723
+ on `claude-sonnet-5-5`, in parallel") become one `task_batch` with a model per task. A
724
+ model named in words is a guess, and it depends on the model doing the dispatching.
725
+ Measured on opencode 2.0.31 with "a general subagent on Sonnet 5.5" and "on
726
+ claude-sonnet-5-5": Opus 5.5 passed `claude-code/claude-sonnet-5-5` first time; Haiku
727
+ 4.5 passed a bare `claude-sonnet-5-5` first both times, and before bare ids were
728
+ completed went on to `anthropic/claude-sonnet-5-5` and `claude-sonnet-5.5`, each refused
729
+ by opencode (`Invalid model "…"`, `Model "…" is not available`) with no subagent
730
+ started. With the completion both of Haiku's requests ran on the first call. A guessed
731
+ provider that IS in the user's opencode config would be accepted, though: an
732
+ `anthropic` provider serving that id would run the subagent there, outside this plugin
733
+ and billed to that provider. That is the reason to have the user name the model.
734
+
735
+ What the plugin adds on top of forwarding:
736
+
737
+ - The named model beats the agent's `forceModel` and `defaultSubagentModel`, and a
738
+ named `#variant` beats its `reasoningEffort`, which is opencode's own order. With no
739
+ variant the agent's effort still applies. A child resumed by `task_id` with a model
740
+ runs on it.
741
+ - A task that names a model is left out of the `subagentDispatch` form, and a dispatch
742
+ whose every task names one raises no form.
743
+ - A model on a plugin account outside the dispatching account's `accountGroups` group
744
+ is refused before any task of the call starts; `subagentDispatchCrossGroup: true`
745
+ lifts that. A model on any other provider is opencode's routing and is not checked.
746
+ - On 1.x a `model` set anyway is refused before dispatch, for the whole batch.
747
+
748
+ Claude is told to set `model` only when the user asked for one. If it ever sets one
749
+ unasked, that subagent runs on it, under the same rules.
750
+
751
+ Verify: the subagent's sidebar row shows the model it really spawned with, its tool row
752
+ shows the `model` opencode received (none there means opencode picked the model as
753
+ usual), and `plugin.log` has `bare task model completed to this conversation's own
754
+ provider` for a completion and `explicit task model claimed by its child session`. Every spawn logs
755
+ `task model gate` with `supported`, which is false on a 1.x host.
756
+
694
757
  ### Ask before each subagent dispatch
695
758
 
696
759
  Opt-in; off by default. Turns a fan-out into one question instead of a static agent file.
@@ -727,6 +790,8 @@ What happens, in order:
727
790
  Rules worth stating to a user:
728
791
 
729
792
  - A dispatch answer beats `forceModel`, `defaultSubagentModel` and the inherited effort.
793
+ - A task whose `task` call names its own `model` (opencode 2) is not asked about, and a
794
+ dispatch whose every task names one raises no form: the user already chose.
730
795
  - Any row takes a typed answer: a registered model id, an effort level, an `@account`,
731
796
  or any combination (`claude-opus-5-5 max @worker`). Unrecognised text keeps that row's
732
797
  default with a NOTICE.
@@ -862,7 +927,7 @@ Names below become `mcp__opencode_proxy__<name>`; input config is case-insensiti
862
927
  | `edit` | `"Edit"`, default; replaces CLI Edit. |
863
928
  | `write` | `"Write"`, default; replaces CLI Write. |
864
929
  | `webfetch` | `"WebFetch"`, default; replaces CLI WebFetch. |
865
- | `task` | `"Task"`, default; disables CLI Agent and dispatches opencode subagents under its permissions. No proxy deadline by default; a positive `proxyToolTimeoutMs` entry adds one. Takes `background: true` only on a host that runs background subagents (see below). |
930
+ | `task` | `"Task"`, default; disables CLI Agent and dispatches opencode subagents under its permissions. No proxy deadline by default; a positive `proxyToolTimeoutMs` entry adds one. Takes `background: true` only on a host that runs background subagents (see below), and `model` only on opencode 2 (recipe "One subagent on a model the user names"). |
866
931
  | `task_batch` | Included with Task; one MCP call fans out two or more independent task inputs concurrently. Separate task calls were measured serial (2026-09-06, two 8-second calls: the second MCP request left the CLI 7 ms after the first resolved). The input must be a `tasks` array of at least two items, each with `description`, `prompt` and `subagent_type`; anything else is refused before the calls are queued. |
867
932
  | `task_status` | Included with Task, and only on a host that runs background subagents. Reads a background subagent's state by `task_id` and collects its result. A recovery path for a completion notification that never arrived, not a progress poll; a result is handed over once. Answered in-process (opencode has no such tool) and refuses any session that is not this conversation's subagent. Not nameable in `proxyTools`. |
868
933
  | `task_cancel` | Included with Task, same host gate as `task_status`. Aborts a background subagent's child session; a cancelled subagent sends no completion notification. |
@@ -1193,6 +1258,8 @@ Useful log lines to search for (redact payloads): `spawning new claude process`,
1193
1258
  `bridged opencode skills into claude`, `interrupt sent for aborted turn`, `btw:`,
1194
1259
  `rendering opencode-side tool result as text`, `proxy-mcp tool call received`,
1195
1260
  `evicting idle claude process`, `evicting LRU claude process`, `background subagent gate`,
1261
+ `task model gate`, `bare task model completed`, `explicit task model claimed by its child session`,
1262
+ `task model refused`,
1196
1263
  `proxy call still waiting`, `fast mode` warnings.
1197
1264
 
1198
1265
  Version requirements. The first four are flag gates in `src/cli-version.ts`; the last two
@@ -1383,6 +1450,10 @@ Prefer the doctor: it needs no logging change and no restart.
1383
1450
  | `Subagent failed (task_id …): Tool execution aborted` while the child finished fine | Bug fixed in 0.15.1 | Upgrade |
1384
1451
  | A `subtask: true` command's subagent output is "lost" | Bug fixed in 0.15.4 | Upgrade |
1385
1452
  | Two subagents run one after another | The CLI serialises MCP calls | Plugin 0.17.0+; the model must use `mcp__opencode_proxy__task_batch` |
1453
+ | On opencode 2, a subagent the user asked for on a particular model ran on another | Claude passed no `model` (the subagent's tool row shows none), most often because the model was named in words and the dispatching model did not turn it into a reference; or the plugin is older than 0.60.0, whose `task` tool had no `model` | Have the user name the model by id (`claude-sonnet-5-5`, completed to this conversation's own account) or by full reference for another account; see "One subagent on a model the user names". If the tool row does carry a `model` and the sidebar row shows another model, that is a bug: ask for `/claude-code-doctor bundle` |
1454
+ | `Invalid model "…"` or `Model "…" is not available` on a subagent row, on opencode 2 | Claude passed a reference opencode does not have, usually a guess at a model named in words | Nothing ran: opencode refused it before starting anything, and Claude normally tries again. A bare id the plugin registers is completed instead of refused, so on a current plugin this is a misspelt id, another account's marker without its provider, or a guessed provider; an older plugin refused every bare id. Naming the model by id or full reference avoids the guessing |
1455
+ | A dispatch refused with `… which is outside this conversation's account group, so no tasks were started` | The `model` sits on a plugin account in another `accountGroups` group | Working as designed. Name a model on an account in this conversation's group, or set `subagentDispatchCrossGroup: true` only if the user wants groups crossed |
1456
+ | On opencode 1, Claude says its task tool cannot take a model, or a dispatch fails with "This opencode's task tool takes no per-call `model`" | opencode 1's `task` has no `model` field | Not available on 1.x. Use an agent's `forceModel`, `defaultSubagentModel`, or `subagentDispatch: "ask"` |
1386
1457
  | Esc does not stop Claude; aborted turns keep running | Plugin older than 0.16.0 | Upgrade |
1387
1458
  | Esc pressed in the first moment of a turn does nothing, and the turn runs and bills anyway | A turn prepares before it asks the CLI for anything (spawn directory, `claude --version`, opencode's MCP status and tool registry), and a stop that landed in that window used to be dropped. Fixed: the turn ends there, spawning nothing and writing nothing, and the reply is empty | Upgrade and fully relaunch every opencode window. Grep `plugin.log` for `abort while the turn was still being prepared` |
1388
1459
  | Under `opencode serve` or the web UI every project spawns Claude in the server's launch dir | Plugin older than 0.16.0 | Upgrade, or pin `cwd` |