Skip to content

feat(new_task): add optional variant parameter for per-subtask reasoning#9172

Open
Githubguy132010 wants to merge 18 commits intoKilo-Org:mainfrom
Githubguy132010:feat/new-task-variant
Open

feat(new_task): add optional variant parameter for per-subtask reasoning#9172
Githubguy132010 wants to merge 18 commits intoKilo-Org:mainfrom
Githubguy132010:feat/new-task-variant

Conversation

@Githubguy132010
Copy link
Copy Markdown
Contributor

Context

Closes #9145.

Currently the new_task tool has no way to override a subagent's reasoning level / variant for a specific subtask. The only workaround is to pre-define a second subagent with a different agent.variant — coarse, and doesn't scale.

This PR adds one optional variant parameter to new_task so a primary agent can pick the reasoning effort per call (low, medium, high, xhigh, max on Claude Opus 4.7; none, minimal, low, medium, high, xhigh on OpenAI reasoning models; whatever the configured model exposes otherwise).

Implementation

Additive-optional across four files:

  • packages/opencode/src/tool/task.ts — added variant: z.string().optional() to the tool schema and a validation block in TaskTool.execute that looks up the target model via Provider.getModel(...) and fails the Effect with a helpful error listing available variants if the passed value isn't recognised. ModelNotFoundError is caught via Effect.tryPromise and surfaced as a clean tool-level error. The msg + model resolution + validation block runs before Session.create, so an invalid variant never creates an orphan child session. The validated params.variant is forwarded to SessionPrompt.prompt, where the pre-existing PromptInput.variant → createUserMessage precedence handles the rest.

  • packages/opencode/src/session/message-v2.ts — added variant: z.string().optional() to SubtaskPart so programmatic subtask callers (message-level SubtaskParts, not just the LLM tool call) can also specify a variant. Strictly additive — existing persisted sessions deserialize unchanged.

  • packages/opencode/src/session/prompt.ts — in handleSubtask, threaded task.variant into both the persisted tool-part state.input and the taskArgs object passed to taskTool.execute. Both paths converge at a single validation site in TaskTool.execute.

  • .changeset/new-task-variant.md — minor bump for @kilocode/cli.

All new blocks under packages/opencode/ carry // kilocode_change markers.

End-to-end flow: primary agent calls new_task({ variant: "high" })TaskTool.execute resolves the subagent's target model → Provider.getModel returns the model with its variants map → validation fails fast if the variant isn't a key → SessionPrompt.prompt({ variant })createUserMessage writes variant onto the child's user message → llm.ts merges model.variants[variant] into provider options for the API call.

When variant is omitted, behavior is identical to before — no Provider.getModel round-trip, no variant injected anywhere.

How to Test

Start a primary-agent session on a reasoning-capable model (e.g. Claude Opus 4.7), then:

Happy path (expected: subtask runs at the specified reasoning level)

Have the main agent invoke:

Call new_task with subagent_type: "code-explorer", description: "test", prompt: "pick any file and describe it", variant: "max".

Inspect the child session's first user message — model.variant should be "max". If provider debug logging is on, the merged provider options should include the adaptive-reasoning config from ProviderTransform.variants.

Negative path (expected: tool-level error, no child session created)

Call with variant: "turbo". The tool result should be an error containing "turbo", "Available variants:", and the real variant names for the target model. Running against a non-reasoning model with any variant should error with "does not support variants".

Regression (expected: unchanged)

Omit variant entirely. Behavior should be identical to today — the subagent runs at its configured agent.variant if any, else default reasoning.

Automated coverage

  • packages/opencode/test/tool/task.test.ts — 6 new tests: valid forward, invalid variant rejected with list, no-variants-on-model branch, omitted-variant regression guard, task_id resume + variant.
  • packages/opencode/test/session/prompt-effect.test.ts — 1 new end-to-end test driving prompt.loop on a SubtaskPart with variant set, asserting it reaches the persisted tool-part input.

All 11 tests in task.test.ts and all non-skipped tests in prompt-effect.test.ts pass on this branch. Typecheck is clean.

Get in Touch

n/a

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Githubguy132010 and others added 13 commits April 19, 2026 14:52
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Forward the optional `variant` field from a SubtaskPart into both the
persisted tool-call `state.input` record and the `taskArgs` object passed
to `taskTool.execute`, so the variant is available downstream in the
subtask session.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a new live test "subtask variant is threaded into tool-part state.input"
that creates a subtask with variant="high", drives the loop until the
tool-part is observable, and asserts state.input.variant === "high".

Also extends the addSubtask helper with an optional `variant` parameter
and adds a variantProviderCfg helper with high/low variants on test-model.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment thread packages/opencode/src/tool/task.ts
@kilo-code-bot
Copy link
Copy Markdown
Contributor

kilo-code-bot Bot commented Apr 19, 2026

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (6 files)
  • .changeset/new-task-variant.md
  • packages/opencode/src/session/message-v2.ts
  • packages/opencode/src/session/prompt.ts
  • packages/opencode/src/tool/task.ts
  • packages/opencode/test/session/prompt-effect.test.ts
  • packages/opencode/test/tool/task.test.ts

Reviewed by gpt-5.4-20260305 · 2,766,052 tokens

Githubguy132010 and others added 5 commits April 19, 2026 15:25
- Remove duplicate `const msg`/`const model` block that was left behind
  when the early-validation block was added (TS2451 redeclaration error)
- Remove duplicate variant validation block (same code existed in both
  early and late positions in TaskTool.execute)
- Rewrite variant tests to use existing `stubOps` + `promptOps` pattern
  instead of monkey-patching SessionPrompt namespace (which fails because
  SessionPrompt is imported as a type-only import and its properties are
  read-only consts)
- Add `// kilocode_change` annotation to the modified import line in
  task.test.ts to satisfy the annotation CI check
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.

[FEATURE]: Allow new_task tool to specify a reasoning level

1 participant