feat(opencode): add optional model param to Task tool and show provider in message headers#31694
feat(opencode): add optional model param to Task tool and show provider in message headers#31694shoootyou wants to merge 11 commits into
Conversation
Allow a Task subagent call to specify the model it runs on via an optional "providerID/modelID" string param. The model is parsed with Provider.parseModel and validated with Provider.getModel; an explicit model overrides both the agent's configured model and parent inheritance, forcing variant to undefined. Omitting the param preserves the existing inherit-from-agent/parent behavior. Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
Display the provider alongside the model as "provider/model" in both assistant footer and user header of the shared message component (used by app web and desktop). Extracts a pure formatModelLabel helper with per-field catalog fallback to raw providerID/modelID and a no-model guard that never renders a stray slash. Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
Add Model.providerModel formatter returning "provider/model" with raw providerID/modelID fallback on catalog miss, leaving Model.name intact. Switch the session transcript header and the session view model label to the new formatter. Existing transcript tests updated to the new format. Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
Address audit findings on the Task tool model override: - treat the param as present when defined (model: "" now errors instead of silently inheriting the parent model) - reject empty and prototype-polluting segments (__proto__, constructor, prototype) before any provider lookup, and catch defects so a crafted value never leaks a raw TypeError - validate the model before creating the child subagent session, so an invalid model fails fast without spawning an orphan session Adds tests for empty-string, prototype keys, multi-slash modelID, and override-beats-agent-own-model. Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
When the provider resolves but the model is unknown, render the provider display name with the raw model id (provider/model), matching the shared UI helper instead of falling back to raw IDs for both. Full miss still yields raw providerID/modelID. Adds direct Model.name tests for the retained helper. Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
Add tests for the partial-miss branch (provider known, model unknown) and both branches of the no-model guard, and replace the brittle double-cast fixture with a fully-typed one. Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
Replace the fixed denylist with a prototype-membership check
(segment in {}) so inherited Object.prototype member names such as
toString and valueOf cannot pass as a modelID against a valid provider
and spawn an orphan subagent session. Adds regression tests and
strengthens the empty-string assertion.
Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
Note that the invoking agent can override the subagent model per call via the Task tool's optional model parameter (provider/model form), and that an invalid value fails with an Invalid task model error. Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
Split the single generic Task model error into format vs availability: - malformed format (no slash, empty segment, prototype-chain key) keeps a "provider/model format" explanation - a well-formed but unknown provider reports "provider ... is not configured" - a known provider with an unknown model reports "model ... is not available for provider ..." Availability errors surface getModel suggestions (Did you mean: ...), discriminated by the typed ModelNotFoundError tag and Provider.list() membership rather than message text. Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
Add cases for the empty/omitted suggestions branch (no dangling Did you mean), pin the comma-join separator and order, cover the single-suggestion no-comma path, guard the suggestion-presence loops against vacuum, and assert the empty-string format echo. Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
Distinguish the malformed-format Invalid task model error from the well-formed-but-unavailable Model unavailable error (with suggestions). Co-authored-by: yui-soul <yui-soul@users.noreply.github.com>
|
Hey! Your PR title Please update it to start with one of:
Where See CONTRIBUTING.md for details. |
|
The following comment was made by an LLM, it may be inaccurate: Based on my search, I found one potentially related PR: Potential Duplicate/Related PRPR #29447: feat(opencode): add task model override This PR appears to address the same feature—adding a model override capability to the Task tool. Since PR #31694 (the current PR) implements dynamic model parameters for Task tools with both feature and provider display improvements, PR #29447 may be an earlier or alternative approach to the same problem. You should verify whether #29447 was closed/superseded or if there's overlapping work between the two PRs. |
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
Issue for this PR
Closes #17595
Related #9584
Related #24812
Type of change
What does this PR do?
Two related changes:
1. Optional
modelparam on the Task toolAdds an optional
modelstring (provider/modelformat) to the Task tool input. When provided, the spawned subagent runs on that model instead of its configured or inherited one. The param is validated before the child session is created so invalid values fail fast without orphaning a session. Error messages distinguish malformed format from unavailable models, and surfacegetModel's fuzzy suggestions as aDid you mean:clause.Note: PR #29447 implements the same Task model param with an additional
model_overridepermission gate (default deny). This PR intentionally omits that gate and leaves the policy decision to maintainers. The implementations are otherwise complementary — this one adds full test coverage (36 passing tests), differentiated error messages, and prototype-key security hardening.2.
provider/modelin message headersShows
provider/model(e.g.anthropic/Claude Sonnet 4) instead of just the model name in assistant and user message headers — acrosspackages/ui(app + desktop) and the TUI (session view + transcript export).providerIDwas already persisted onAssistantMessage; this is a presentational change only.How did you verify your code works?
bun test test/tool/task.test.tsinpackages/opencode— 36/36 pass (model override, inheritance, error cases, prototype-key rejection, no orphan sessions)bun test src/components/message-part-model.test.tsinpackages/ui— 6/6 passbun test test/util/provider-model-label.test.ts test/util/model.test.ts test/util/transcript.test.tsinpackages/tui— 29/29 passbun typecheckclean inpackages/opencode,packages/ui,packages/tuiopencode serveinstance: model override routes subagents correctly,provider/modellabel visible in the web UIScreenshots / recordings
UI change (provider label in message headers) — screenshot available on request.
Checklist