fix(bedrock): honor reasoning_effort for Anthropic-on-Bedrock models (#2887)

John Jansen and JJ Bot created

Bedrock routes through Fantasy's Anthropic implementation with a
different display name, so provider options must still be filed under
anthropic.Name for the language model to pick them up. Previously the
bedrock provider type was missing from getProviderOptions entirely, so
reasoning_effort and think were silently dropped.

Co-authored-by: JJ Bot <john.jansen+bot@me.com>

Change summary

internal/agent/coordinator.go      |  2 
internal/agent/coordinator_test.go | 35 ++++++++++++++++++++++++++++++++
2 files changed, 36 insertions(+), 1 deletion(-)

Detailed changes

internal/agent/coordinator.go 🔗

@@ -286,7 +286,7 @@ func getProviderOptions(model Model, providerCfg config.ProviderConfig) fantasy.
 				options[openai.Name] = parsed
 			}
 		}
-	case anthropic.Name:
+	case anthropic.Name, bedrock.Name:
 		var (
 			_, hasEffort = mergedOptions["effort"]
 			_, hasThink  = mergedOptions["thinking"]

internal/agent/coordinator_test.go 🔗

@@ -7,6 +7,8 @@ import (
 
 	"charm.land/catwalk/pkg/catwalk"
 	"charm.land/fantasy"
+	"charm.land/fantasy/providers/anthropic"
+	"charm.land/fantasy/providers/bedrock"
 	"github.com/charmbracelet/crush/internal/config"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -383,3 +385,36 @@ func TestUpdateParentSessionCost(t *testing.T) {
 		assert.InDelta(t, 0.0, updated.Cost, 1e-9)
 	})
 }
+
+func TestGetProviderOptionsReasoningEffort(t *testing.T) {
+	// Bedrock is Fantasy's Anthropic under a different provider name; options
+	// must land under anthropic.Name so the Anthropic language model picks them up.
+	tests := []struct {
+		name         string
+		providerType catwalk.Type
+	}{
+		{"anthropic honors reasoning_effort", catwalk.Type(anthropic.Name)},
+		{"bedrock honors reasoning_effort", catwalk.Type(bedrock.Name)},
+	}
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+			model := Model{
+				CatwalkCfg: catwalk.Model{ID: "claude-opus-4-7"},
+				ModelCfg: config.SelectedModel{
+					Provider:        "test",
+					ReasoningEffort: "max",
+				},
+			}
+			providerCfg := config.ProviderConfig{ID: "test", Type: tc.providerType}
+
+			opts := getProviderOptions(model, providerCfg)
+
+			raw, ok := opts[anthropic.Name]
+			require.True(t, ok, "options should be keyed under anthropic.Name for type %q", tc.providerType)
+			parsed, ok := raw.(*anthropic.ProviderOptions)
+			require.True(t, ok)
+			require.NotNil(t, parsed.Effort)
+			assert.Equal(t, anthropic.Effort("max"), *parsed.Effort)
+		})
+	}
+}