Skip to content

feat(task): allow selecting a model per task invocation (1.18.23 lineage) - #4

Merged
leoncheng57 merged 2 commits into
fix/subagent-effective-deny-inheritancefrom
feat/task-model-selection-1823
Aug 26, 2026
Merged

feat(task): allow selecting a model per task invocation (1.18.23 lineage)#4
leoncheng57 merged 2 commits into
fix/subagent-effective-deny-inheritancefrom
feat/task-model-selection-1823

Conversation

@leoncheng57

@leoncheng57 leoncheng57 commented Aug 26, 2026

Copy link
Copy Markdown
Owner

Issue for this PR

Closes leoncheng57/custom-dca-opencode#90. Upstream tracking issue: anomalyco#6651.

Fork-internal PR: this targets fix/subagent-effective-deny-inheritance, the branch our local patched binary is built from. It is not a contribution to anomalyco/opencode — see the note at the bottom.

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Adds an optional model parameter to the task tool so one delegation can run on a different model than the subagent's configured default.

Resolution order becomes: explicit model > subagent's configured model > invoking assistant's model. Omitting the parameter takes the same branch as before, so existing behaviour is untouched.

The model is resolved once, before the foreground/background split, so a background BackgroundJob child runs on the same model a foreground one would, and both report it in tool metadata. Metadata still carries the normalized { providerID, modelID } object, so downstream consumers that read task provenance need no change.

Validation is early: provider.getModel is called before the child session is created, so an unknown model fails with ProviderModelNotFoundError and leaves no orphaned child. This is the same call the prompt path makes later, so it changes when the failure happens, not whether.

On a resume via task_id, an explicit model applies to that invocation only and is not persisted as a session default.

How did you verify your code works?

  • bun test test/tool/task.test.ts test/tool/parameters.test.ts — 91 pass, 0 fail
  • bun run typecheck (tsgo) — exit 0
  • Full packages/opencode suite — 3329 pass, 1 fail; the failure is chunkTimeout ... SSE body stalls, which reproduces identically on the pristine base
  • prettier clean; oxlint 0 added warnings
  • Mutation check: reverting the precedence expression fails 6 of the new tests, including the background case
  • Built the binary from the merged tip and diffed the live tool schema against the previous binary: model appears in the task tool's properties and is absent from required; the old binary has no model key at all

Background coverage needed one correction: the completion handler injects a hand-back prompt into the parent, so a naive last-prompt capture asserts the wrong message. The test now asserts on the prompt addressed to the child.

Screenshots / recordings

Not a UI change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Note on upstream

This is deliberately not proposed to anomalyco/opencode. That repository already has an active implementation of the same feature in anomalyco#34947, which additionally gates the parameter behind a model_override permission defaulting to deny. Earlier raw-parameter attempts (anomalyco#26535, anomalyco#29447) were closed as superseded by it. This fork carries the change only so the locally pinned binary has it.

Adds an optional `model` parameter ("provider/model") to the native task
tool, so a delegating agent can route one subtask to a different model
without changing the subagent's configuration.

Precedence is explicit per-invocation model > subagent's configured model
> invoking assistant's model. Omitting the parameter is behaviourally
identical to before.

The model is resolved once, before the foreground/background branch, so a
BackgroundJob child runs the same model a foreground one would and the
`{ providerID, modelID }` tool metadata stays correct on every return path.

Validation is early: an explicit model goes through the same
Provider.getModel the prompt path calls later, but before the child
session is created, so an unavailable model fails without leaving an
orphaned child. Omission skips that call entirely and leaves the existing
fallback chain's late validation untouched.

The parent's variant is only inherited when the child actually runs the
parent's model, so a variant cannot leak onto a different model.

Provider.node is already a tool-registry dependency, so acquiring
Provider.Service adds no new wiring; the test layer is updated to match.
Wire shape: the new parameter is optional, string-typed, and rejects a
non-string; the JSON Schema snapshot records what the calling model sees.

Behaviour, against a config-declared provider so model lookups resolve
deterministically without network access or loading an SDK:

- explicit model beats the invoking assistant's model
- explicit model beats the subagent's configured model
- omission preserves both existing fallbacks, including that the parent's
  variant is still inherited only when no model overrides it
- an explicit model on a resumed task_id applies to that invocation and
  creates no second child, and does not persist onto the child session
- running and result metadata both report the selected pair
- a model id containing extra slashes parses as provider + rest
- a background launch runs the child on the selected model, asserted on
  the prompt actually addressed to the child rather than on the hand-back
  prompt injected into the parent

Validation is pinned to what the code really does rather than to the
aspiration: an unknown or malformed model fails with Provider's own
ProviderModelNotFoundError before any child session exists, before the
child is prompted, and before any running metadata is emitted.
@leoncheng57
leoncheng57 marked this pull request as ready for review August 26, 2026 17:19
@leoncheng57
leoncheng57 merged commit d677b7e into fix/subagent-effective-deny-inheritance Aug 26, 2026
4 of 9 checks passed
@github-actions

Copy link
Copy Markdown

Thanks for updating your PR! It now meets our contributing guidelines. 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant