mirror of
https://github.com/stablyai/orca.git
synced 2026-09-29 16:02:50 +00:00
fix(claude): report effort from get_settings, which is the only frame that has it
The composer's Effort pill rendered blank in every structured session. This is
not a missing source: the publication reads `effortLevel` off the `system/init`
frame, and that frame has never carried an effort of any kind, while the correct
value is already fetched at acquisition and thrown away on the auth diagnostic.
Verified two ways -- a live get_settings probe against Claude Code 2.1.258, and
the shipped binary's own init frame construction, which lists `model` and no
effort. So `reportedOptions.effort` was always empty, the options reader dropped
the key, and the pill had no value. Model survived only because
`currentModelId()` has a fallback chain.
The get_settings call acquisition already makes reports the session's current
effort as `effective.effortLevel`; pass that into the publication instead.
Selecting an effort already worked, so this is the arrival value only.
The legacy PTY path is unaffected and must not be "fixed" to match: it reads its
effort by parsing the startup banner (`CLAUDE_MODEL_EFFORT` in
src/renderer/src/components/native-chat/claude-terminal-session-options.ts),
which is why it shows a value where the structured path does not.
Also removes the fixture that hid this: the fake init frame invented
`effortLevel: 'high'`, a field the CLI does not send, which is why every gate
stayed green over a value that is always empty in production. The fixture's
get_settings now returns the real {applied, effective, sources} shape instead of
a bare `{env: {}}`, so the two adapter tests that asserted an effort keep
asserting it through the path production actually uses.
The reader returns null rather than defaulting: an effort nothing measured would
repeat the fixture's mistake, and a blank pill is the honest degradation if the
provider ever renames the key.
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { readClaudeSettingsEffort } from './claude-structured-session-options'
|
||||
import type { ClaudeStructuredSessionEvent } from './claude-structured-session-adapter'
|
||||
import { acquired, fakeClaude } from './claude-structured-session-test-support'
|
||||
|
||||
/** Verbatim from Claude Code 2.1.258's get_settings response. */
|
||||
const REAL_SETTINGS = {
|
||||
applied: { model: 'claude-opus-5[1m]', effort: 'high', advisor: null, ultracode: false },
|
||||
effective: { model: 'claude-opus-5[1m]', effortLevel: 'high', env: {} },
|
||||
sources: {}
|
||||
}
|
||||
|
||||
describe('Claude effort reporting', () => {
|
||||
it('reads the effort get_settings reports', () => {
|
||||
expect(readClaudeSettingsEffort(REAL_SETTINGS)).toBe('high')
|
||||
})
|
||||
|
||||
it.each([
|
||||
[
|
||||
'the provider stops reporting it',
|
||||
{ applied: { effort: 'high' }, effective: {}, sources: {} }
|
||||
],
|
||||
['the payload carries no effective block', { applied: { effort: 'high' } }],
|
||||
['the request failed outright', null]
|
||||
])('reports no effort when %s', (_case, settings) => {
|
||||
// Never defaulted: an effort nothing measured would be worse than a blank
|
||||
// pill, and this is the assertion that goes red if the key is renamed.
|
||||
expect(readClaudeSettingsEffort(settings)).toBeNull()
|
||||
})
|
||||
|
||||
it('publishes the effort from get_settings, which system/init never carries', async () => {
|
||||
const claude = fakeClaude({ settings: REAL_SETTINGS })
|
||||
const adapter = await acquired(claude)
|
||||
|
||||
await expect(adapter.readOptions({ sessionId: 'session-1', fence: 7 })).resolves.toMatchObject({
|
||||
current: { effort: 'high' }
|
||||
})
|
||||
})
|
||||
|
||||
it('leaves the effort unreported when the session never learns one', async () => {
|
||||
const claude = fakeClaude({ settings: { applied: {}, effective: {}, sources: {} } })
|
||||
const adapter = await acquired(claude)
|
||||
|
||||
const options = await adapter.readOptions({ sessionId: 'session-1', fence: 7 })
|
||||
expect(options.current.effort).toBeUndefined()
|
||||
expect(options.current.model).toBeTruthy()
|
||||
})
|
||||
|
||||
it('keeps the init fixture free of an effort the real frame never sends', async () => {
|
||||
const events: ClaudeStructuredSessionEvent[] = []
|
||||
await acquired(fakeClaude(), {}, events)
|
||||
const init = events.flatMap((event) =>
|
||||
event.type === 'message' && event.message.subtype === 'init' ? [event.message] : []
|
||||
)
|
||||
|
||||
expect(init).toHaveLength(1)
|
||||
expect(init[0]).toHaveProperty('model')
|
||||
// The regression that hid this defect: a fixture inventing `effortLevel`
|
||||
// kept every gate green over a value that is always empty in production.
|
||||
expect(Object.keys(init[0])).not.toContain('effortLevel')
|
||||
})
|
||||
})
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
} from './claude-structured-options'
|
||||
import { ClaudePromptRegistry } from './claude-structured-prompt-replies'
|
||||
import { createClaudeSessionJournalTranslator } from './claude-structured-journal-translation'
|
||||
import { readClaudeSettingsEffort } from './claude-structured-session-options'
|
||||
import { createClaudeSessionPublication } from './claude-structured-session-publication'
|
||||
import {
|
||||
cancelClaudeAcquisitionAttempt,
|
||||
@@ -243,6 +244,7 @@ export async function acquireClaudeSession({
|
||||
claudeConfigDir: launch.claudeConfigDir,
|
||||
leafUuid: observedLeafUuid,
|
||||
fence: input.fence,
|
||||
effort: readClaudeSettingsEffort(settings),
|
||||
resumed: launch.resumed,
|
||||
prompts,
|
||||
translator,
|
||||
|
||||
@@ -19,6 +19,16 @@ function text(value: unknown): string | null {
|
||||
return typeof value === 'string' && value.trim() ? value : null
|
||||
}
|
||||
|
||||
/**
|
||||
* The session's current effort, which only `get_settings` reports: the
|
||||
* `system/init` frame carries `model` but has never carried an effort of any
|
||||
* kind. Null when the provider stops reporting it, so the pill goes empty
|
||||
* rather than showing an effort nothing measured.
|
||||
*/
|
||||
export function readClaudeSettingsEffort(settings: unknown): string | null {
|
||||
return text(record(record(settings)?.effective)?.effortLevel)
|
||||
}
|
||||
|
||||
function effortLabel(value: string): string {
|
||||
return value === 'xhigh' ? 'Extra high' : `${value.charAt(0).toUpperCase()}${value.slice(1)}`
|
||||
}
|
||||
|
||||
@@ -21,9 +21,11 @@ export function createClaudeSessionPublication(input: {
|
||||
observedAt: number
|
||||
options?: ReadonlyMap<string, string>
|
||||
capabilities: readonly string[]
|
||||
/** Read from `get_settings`; `system/init` never reports an effort. */
|
||||
effort: string | null
|
||||
}): { acquisition: AgentSessionAcquisition; session: ClaudeSession } {
|
||||
const model = readClaudeFrameString(input.init.message, 'model')
|
||||
const effort = readClaudeFrameString(input.init.message, 'effortLevel')
|
||||
const effort = input.effort
|
||||
return {
|
||||
acquisition: {
|
||||
process: input.process,
|
||||
|
||||
@@ -50,7 +50,6 @@ export function fakeClaude(
|
||||
initSessionId?: string
|
||||
initUuid?: string
|
||||
initModel?: string
|
||||
initEffort?: string
|
||||
initProof?: 'init' | 'session-start' | 'none'
|
||||
initAccount?: unknown
|
||||
exitBeforeInit?: string
|
||||
@@ -97,13 +96,15 @@ export function fakeClaude(
|
||||
uuid: options.initUuid ?? 'init-uuid'
|
||||
})
|
||||
} else if (options.initProof !== 'none') {
|
||||
// Keys mirror the real system/init frame, which carries `model` but no
|
||||
// effort of any kind: the current effort only comes back from
|
||||
// get_settings. Never add a field the CLI does not send.
|
||||
handlers.onMessage?.({
|
||||
type: 'system',
|
||||
subtype: 'init',
|
||||
session_id: options.initSessionId ?? PROVIDER_SESSION_ID,
|
||||
uuid: options.initUuid ?? 'init-uuid',
|
||||
model: options.initModel ?? 'claude-sonnet-5',
|
||||
effortLevel: options.initEffort ?? 'high',
|
||||
apiKeySource: 'none',
|
||||
...(options.capabilities ? { capabilities: options.capabilities } : {})
|
||||
})
|
||||
@@ -115,7 +116,15 @@ export function fakeClaude(
|
||||
},
|
||||
getSettings: async () => {
|
||||
connection.calls.push({ subtype: 'get_settings' })
|
||||
return options.settings ?? { env: {} }
|
||||
// Shape measured from Claude Code 2.1.258: {applied, effective, sources},
|
||||
// and the only place the session's current effort is reported.
|
||||
return (
|
||||
options.settings ?? {
|
||||
applied: { model: 'claude-sonnet-5', effort: 'high', advisor: null, ultracode: false },
|
||||
effective: { model: 'claude-sonnet-5', effortLevel: 'high', env: {} },
|
||||
sources: {}
|
||||
}
|
||||
)
|
||||
},
|
||||
supportedModels: async () => {
|
||||
connection.calls.push({ subtype: 'list_models' })
|
||||
|
||||
Reference in New Issue
Block a user