Skip to content

Enable Opus 5 support, native thinking fixes, proper mapping for effort#117

Open
guidan-nick wants to merge 4 commits into
tickernelz:masterfrom
guidan-nick:feat/reasoning-flags-and-effort-ladder
Open

Enable Opus 5 support, native thinking fixes, proper mapping for effort#117
guidan-nick wants to merge 4 commits into
tickernelz:masterfrom
guidan-nick:feat/reasoning-flags-and-effort-ladder

Conversation

@guidan-nick

@guidan-nick guidan-nick commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Thinking never rendered in OpenCode on Kiro models, and xhigh effort could not be reached. Three independent causes.

Fixes

  • Reasoning was discarded. Kiro streams reasoning as reasoningContentEvent, which transformSdkStream did not handle, so every event was dropped. Now forwarded as reasoning_content deltas. Measured on opus-5: 17 events in / 0 deltas out before, 20 in / 19 out after. The one-event gap is the trailing signature/redactedContent event, skipped deliberately since it has no renderable text.
  • OpenCode was never told the models can reason. It drops reasoning deltas unless the model declares reasoning: true and interleaved.field. The plugin emitted reasoning_content but declared neither. Both are now set on every -thinking entry, matching how OpenCode's own registry declares non-standard reasoning fields.
  • xhigh was unreachable. budgetToEffort had no branch returning it, so with auto_effort_mapping on by default it was dead configuration — adding models to XHIGH_CAPABLE_MODELS had no effect. Confirmed on the wire after the fix: {"output_config":{"effort":"xhigh"}}, HTTP 200.

The old mechanism was prompt injection plus scraping <thinking> tags out of the answer. Opus 5 never emits those tags, so thinking was invisible there regardless of config. The scraper stays as a fallback for models that do inline reasoning.

Also

  • Model list moves to plugin/model-registry.ts; thinking entries derive from effort capability so variants and budget bands cannot drift apart. xhigh only offered on opus-4.7/4.8/5 and sonnet-5.
  • Adds missing claude-opus-5, claude-opus-5-thinking, claude-opus-4-7-thinking entries.
  • GPT-5.6 tiers deliberately not advertised — they use reasoning.effort/reasoning.mode, not output_config.effort, so they need their own request path.
  • logSdkRequest records the resolved additionalModelRequestFields; effort was previously invisible in logs and unconfirmable without patching the plugin.
  • README install example no longer tells users to hand-write the model registry, which is what left thinking models without reasoning flags to begin with.

Breaking

Budget bands rescaled to Kiro's real range (1024–128000) instead of OpenCode's 32768 convention, so xhigh and max are reachable from a budget:

budget effort
<= 16384 low
<= 32768 medium
<= 65536 high
<= 98304 xhigh
> 98304 max

A budget of 32768 now maps to medium rather than max. Anyone relying on the old bands should set an explicit effort in kiro.json.

Testing

173 tests pass, tsc --noEmit and Prettier clean. New coverage for registry flags, xhigh gating, variant/band agreement, and the reasoning stream including the live event shape. Verified end to end against a live account with provider.kiro.models removed, so the plugin's registry supplied everything.

Every behavioural claim here is wire-verified. Nothing speculative: an earlier revision of this branch added a send_thinking_config option to send Kiro's thinking block explicitly, and it was dropped because the API's acceptance of those fields was never tested and reasoning works without them.

Thinking blocks never rendered in OpenCode, and xhigh effort was
unreachable. Two independent causes.

OpenCode drops reasoning deltas unless the model declares both
`reasoning: true` and `interleaved.field`. The plugin emits reasoning as
`reasoning_content` (streaming/openai-converter.ts) but never declared
either field, so every reasoning chunk was discarded before rendering.
Every model in OpenCode's own registry that carries reasoning in a
non-standard field declares both; this brings the plugin in line.

budgetToEffort had no branch returning xhigh, so the level could only be
reached by setting `effort` explicitly in kiro.json. With
auto_effort_mapping on by default, xhigh was dead configuration and
adding models to XHIGH_CAPABLE_MODELS had no effect on the auto path.

Move the model list into plugin/model-registry.ts and derive the thinking
entries from effort capability, so a model's advertised variants and the
budget mapping cannot drift apart. Adds the missing claude-opus-5,
claude-opus-5-thinking and claude-opus-4-7-thinking entries.

Kiro's GPT-5.6 tiers stay unadvertised: they configure reasoning through
`reasoning.effort` / `reasoning.mode` rather than `output_config.effort`,
so they need their own request path.

BREAKING: budget bands are rescaled to Kiro's real thinking range
(1024-128000) instead of OpenCode's conventional 32768 cap, so xhigh and
max are reachable from a budget. A budget of 32768 now maps to medium
rather than max. Users depending on the old bands should set an explicit
`effort` in kiro.json.

Verified: effort=xhigh confirmed on the wire against opus-5 (HTTP 200).
The install example told users to hand-write the whole model registry,
which is what left thinking models without reasoning flags in the first
place. The plugin advertises every model itself, so the example is now
just the plugin entry.

Document that reasoning + interleaved.field are both required, and warn
that overriding provider.kiro.models replaces the plugin registry
wholesale, so anyone doing that has to carry the flags themselves.
CodeWhisperer sends reasoning on thinking models as reasoningContentEvent,
a member of the ChatResponseStream union. transformSdkStream handled
assistantResponseEvent, toolUseEvent, metadataEvent and contextUsageEvent
and fell through on everything else, so every reasoning event was dropped.

Measured against opus-5 before this change: 17 reasoning events received,
0 reasoning deltas emitted. After: 20 events in, 19 reasoning_content
chunks out; 36 in, 35 out. The one-event gap per response is the trailing
event carrying signature/redactedContent rather than text, which is
skipped deliberately — redactedContent is encrypted by the provider and
has no renderable form.

Until now the only thinking that ever surfaced came from injecting
<thinking_mode> into the system prompt and scraping <thinking> tags out of
the assistant text. Opus 5 does not emit those tags at all, so on that
model thinking was invisible regardless of configuration. The scraper
stays as a fallback for models that do inline reasoning in their text;
when native reasoning arrives, the scraper is bypassed and its block
closed so literal tag mentions in the answer are not re-parsed.

logSdkRequest now records the resolved additionalModelRequestFields, since
effort was previously invisible in the logs and could not be confirmed
without patching the plugin.
@guidan-nick
guidan-nick force-pushed the feat/reasoning-flags-and-effort-ladder branch from 142b54f to 1cd6f08 Compare July 25, 2026 11:10
@guidan-nick guidan-nick changed the title Stream native reasoning and reach every effort level Enable Opus 5 support, native thinking fixes, proper mapping for effort Jul 25, 2026
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