Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions agents/codelayer/src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@ function resolveAnthropicThinking(model: LanguageModel, effort?: string): Record
effort: resolvedEffort,
}
}
if (modelId.includes('opus') && (modelId.includes('5-5') || modelId.includes('5.5'))) {
return {
thinking: { type: 'adaptive', display: 'summarized' },
effort: resolvedEffort,
}
}
if (modelId.includes('opus') && (modelId.includes('4-8') || modelId.includes('4.8'))) {
return {
thinking: { type: 'adaptive', display: 'summarized' },
Expand Down Expand Up @@ -200,8 +206,13 @@ export function buildProviderOptions(
model: LanguageModel,
overrides: CodelayerProviderOptionOverrides = {},
): CodelayerProviderOptions {
const modelId = ((model as { modelId?: string }).modelId ?? '').toLowerCase()
const requiresAdaptiveThinking =
modelId.includes('opus') && (modelId.includes('5-5') || modelId.includes('5.5'))
const anthropicThinking =
overrides.anthropic?.thinking === 'off'
requiresAdaptiveThinking
? resolveAnthropicThinking(model, overrides.anthropic?.effort)
: overrides.anthropic?.thinking === 'off'
? {}
: overrides.anthropic?.thinking === 'adaptive'
? {
Expand Down Expand Up @@ -292,6 +303,7 @@ const EFFORT_RANK: Record<string, number> = {
medium: 1,
high: 2,
xhigh: 3,
max: 4,
}

/**
Expand All @@ -302,7 +314,7 @@ const EFFORT_RANK: Record<string, number> = {
* - codex / firepass / copilot → `reasoningEffort = level`
* - anthropic `4-5`/`4.5` (extended thinking, no adaptive support) →
* `{ thinking: 'enabled', budgetTokens: LOW_ANTHROPIC_BUDGET }`
* - anthropic adaptive (`4.6`+/`4.7`/`4.8`, and any other model) →
* - anthropic adaptive (`4.6`+/`4.7`/`4.8`/`5.5`, and any other model) →
* `effort = level`
*
* Guards (respect an explicitly-throttled parent — sub-agents never think
Expand Down
4 changes: 3 additions & 1 deletion agents/codelayer/src/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ function assertThinkingValue(args: { provider: ProviderType; modelId: string; th
}
}

if (args.provider === 'codex' && (modelId.includes('gpt-6-astra') || modelId.includes('gpt-5.6'))) {
if (args.provider === 'codex' && (modelId.includes('gpt-6-') || modelId.includes('gpt-5.6'))) {
// These models also advertise ultra, but it assumes Codex CLI spawn-agent tools we do not expose yet.
supported(['low', 'medium', 'high', 'xhigh', 'max'])
return
Expand All @@ -89,6 +89,8 @@ function assertThinkingValue(args: { provider: ProviderType; modelId: string; th

if (modelId.includes('fable-5')) {
supported(['low', 'medium', 'high', 'xhigh', 'max'])
} else if (modelId.includes('opus') && (modelId.includes('5-5') || modelId.includes('5.5'))) {
supported(['low', 'medium', 'high', 'xhigh', 'max'])
} else if (modelId.includes('opus') && (modelId.includes('4-8') || modelId.includes('4.8'))) {
supported(['low', 'medium', 'high', 'xhigh', 'max'])
} else if (modelId.includes('opus') && (modelId.includes('4-7') || modelId.includes('4.7'))) {
Expand Down
119 changes: 119 additions & 0 deletions agents/codelayer/test/agent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,18 @@ describe('createCodelayerAgent', () => {
expect(buildProviderOptions(model, overrides).openai.reasoningEffort).toBe('max')
})

test.each(['gpt-6-sol', 'gpt-6-luna'])('%s accepts every exposed CLI effort', (modelId) => {
const model = createMockModel(modelId)

for (const thinking of ['low', 'medium', 'high', 'xhigh', 'max']) {
const overrides = applyCliThinkingOverride({ provider: 'codex', modelId, thinking, overrides: {} })
expect(buildProviderOptions(model, overrides).openai.reasoningEffort).toBe(thinking)
}
expect(() =>
applyCliThinkingOverride({ provider: 'codex', modelId, thinking: 'extreme', overrides: {} }),
).toThrow('Unsupported --thinking value "extreme"')
})

test('applies explicit CLI thinking for firepass kimi models', () => {
const model = createMockModel(DEFAULT_MODELS.firepass)
const overrides = applyCliThinkingOverride({
Expand Down Expand Up @@ -580,6 +592,43 @@ describe('createCodelayerAgent', () => {
})
})

test('uses summarized adaptive thinking and all exposed efforts for opus 5.5', () => {
const modelId = 'claude-opus-5-5'
const model = createMockModel(modelId)

expect(buildProviderOptions(model).anthropic).toEqual({
thinking: { type: 'adaptive', display: 'summarized' },
effort: 'medium',
cacheControl: { type: 'ephemeral' },
})
for (const thinking of ['low', 'medium', 'high', 'xhigh', 'max']) {
const overrides = applyCliThinkingOverride({ provider: 'anthropic', modelId, thinking, overrides: {} })
expect(buildProviderOptions(model, overrides).anthropic).toMatchObject({
thinking: { type: 'adaptive', display: 'summarized' },
effort: thinking,
})
}
expect(() =>
applyCliThinkingOverride({ provider: 'anthropic', modelId, thinking: 'extreme', overrides: {} }),
).toThrow('Unsupported --thinking value "extreme"')
})

test('does not disable or downgrade opus 5.5 adaptive thinking through overrides', () => {
const model = createMockModel('claude-opus-5-5')

for (const thinking of ['off', 'enabled'] as const) {
expect(
buildProviderOptions(model, {
anthropic: { thinking, budgetTokens: 10_000, effort: 'high' },
}).anthropic,
).toEqual({
thinking: { type: 'adaptive', display: 'summarized' },
effort: 'high',
cacheControl: { type: 'ephemeral' },
})
}
})

test('uses summarized adaptive thinking with explicit xhigh CLI effort for opus 4.8', () => {
const model = createMockModel('claude-opus-4-8')
const overrides = applyCliThinkingOverride({
Expand Down Expand Up @@ -1132,6 +1181,47 @@ describe('subagentThinkingOverrides', () => {
}
})

test.each(['gpt-6-sol', 'gpt-6-luna'])('%s preserves the root, child, research, and outline matrix', async (modelId) => {
const rootModel = createMockModel(modelId, 'codex')
const researchModel = createMockModel('gpt-5.6-terra', 'codex')
const agent = await createCodelayerAgent({
model: rootModel,
researchModel,
cwd: '/tmp',
context7ApiKey: 'context7-test-key',
providerOptionOverrides: { codex: { reasoningEffort: 'max', fastMode: true } },
})
const rootConfig = getAgentConfig(agent)
const rootOptions = (rootConfig.providerOptions as unknown as (ctx: { runId: string }) => Record<string, any>)({
runId: 'root',
})
const researchNames = new Set([
'rpi:codebase-locator',
'rpi:codebase-analyzer',
'rpi:codebase-pattern-finder',
'web-search-researcher',
])

expect(rootConfig.model).toBe(rootModel)
expect(rootOptions.openai).toMatchObject({ reasoningEffort: 'max', fastMode: true })
for (const subagent of getSubagents(rootConfig.tools?.agent)) {
const config = getAgentConfig(subagent.agent)
const options = (config.providerOptions as unknown as (ctx: { runId: string }) => Record<string, any>)({
runId: subagent.name,
})
if (researchNames.has(subagent.name)) {
expect(config.model, subagent.name).toBe(researchModel)
expect(options.openai.reasoningEffort, subagent.name).toBe('xhigh')
} else {
expect(config.model, subagent.name).toBe(rootModel)
expect(options.openai.reasoningEffort, subagent.name).toBe(
subagent.name === 'rpi:outline-implementer-agent' ? 'max' : 'low',
)
}
expect(options.openai.fastMode, subagent.name).toBe(true)
}
})

test('lets the outline implementer use parent anthropic effort while other sub-agents stay throttled', async () => {
const agent = await createCodelayerAgent({
model: createMockModel('claude-opus-4-8'),
Expand All @@ -1157,6 +1247,35 @@ describe('subagentThinkingOverrides', () => {
expect(generalProviderOptions({ runId: 'general' }).anthropic.effort).toBe('low')
expect(outlineProviderOptions({ runId: 'outline' }).anthropic.effort).toBe('max')
})

test('keeps opus 5.5 adaptive across root and child roles while reducing ordinary children', async () => {
const model = createMockModel('claude-opus-5-5', 'anthropic')
const agent = await createCodelayerAgent({
model,
cwd: '/tmp',
providerOptionOverrides: { anthropic: { effort: 'max' } },
})
const rootConfig = getAgentConfig(agent)
const rootOptions = (rootConfig.providerOptions as unknown as (ctx: { runId: string }) => Record<string, any>)({
runId: 'root',
})

expect(rootOptions.anthropic).toMatchObject({
thinking: { type: 'adaptive', display: 'summarized' },
effort: 'max',
})
for (const subagent of getSubagents(rootConfig.tools?.agent)) {
const config = getAgentConfig(subagent.agent)
const options = (config.providerOptions as unknown as (ctx: { runId: string }) => Record<string, any>)({
runId: subagent.name,
})
expect(config.model, subagent.name).toBe(model)
expect(options.anthropic.thinking, subagent.name).toEqual({ type: 'adaptive', display: 'summarized' })
expect(options.anthropic.effort, subagent.name).toBe(
subagent.name === 'rpi:outline-implementer-agent' ? 'max' : 'low',
)
}
})
})

describe('--subagent-thinking CLI knob', () => {
Expand Down
Loading
Loading