mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 16:02:32 +00:00
refactor(native-chat): name the structured launch option seed
The create-intent resolver narrowed saved options to model/effort with an inline key literal, inside a file carrying @ts-nocheck — so neither the key list nor the string narrowing had a typechecked or testable home, and the repo already expresses this concept as a named shared shape. Move it to resolveStructuredLaunchSeedOptions beside the persisted settings it reads, where it is typechecked and unit-tested, and document why the seed is exactly model and effort: they are the only ids the picker persists that both providers also accept as strings. No behavior change. Adds coverage for a non-string persisted effort, which settings.json can hold and the durable record must not carry.
This commit is contained in:
@@ -13,7 +13,7 @@ import { getLocalProjectWorktreeGitOptions } from '../project-runtime-git-option
|
|||||||
import type { AgentSessionAttachParams } from '../native-chat/agent-session-wire/structured-agent-session-attach'
|
import type { AgentSessionAttachParams } from '../native-chat/agent-session-wire/structured-agent-session-attach'
|
||||||
import { getSystemCodexHomePath } from '../codex/codex-home-paths'
|
import { getSystemCodexHomePath } from '../codex/codex-home-paths'
|
||||||
import { resolveTuiAgentLaunchEnv } from '../../shared/tui-agent-launch-defaults'
|
import { resolveTuiAgentLaunchEnv } from '../../shared/tui-agent-launch-defaults'
|
||||||
import { resolveNativeChatSessionOptionDefaults } from '../../shared/native-chat-session-option-defaults'
|
import { resolveStructuredLaunchSeedOptions } from '../../shared/native-chat-session-option-defaults'
|
||||||
import { hasPersistedStructuredAgentSessionStore as hasPersistedStructuredAgentSessionStoreOnDisk } from './structured-agent-session-runtime'
|
import { hasPersistedStructuredAgentSessionStore as hasPersistedStructuredAgentSessionStoreOnDisk } from './structured-agent-session-runtime'
|
||||||
import { getProfileUserDataPath } from '../orca-profiles/profile-storage-paths'
|
import { getProfileUserDataPath } from '../orca-profiles/profile-storage-paths'
|
||||||
import { homedir } from 'node:os'
|
import { homedir } from 'node:os'
|
||||||
@@ -162,17 +162,10 @@ export class OrcaRuntimeWithResolveRecoveredStructuredTuiTranscript extends Orca
|
|||||||
}
|
}
|
||||||
const settings = this.requireStore().getSettings()
|
const settings = this.requireStore().getSettings()
|
||||||
const launchEnv = resolveTuiAgentLaunchEnv(input.agent, settings.agentDefaultEnv)
|
const launchEnv = resolveTuiAgentLaunchEnv(input.agent, settings.agentDefaultEnv)
|
||||||
const defaults = resolveNativeChatSessionOptionDefaults(
|
const options = resolveStructuredLaunchSeedOptions(
|
||||||
settings.nativeChatSessionOptions,
|
settings.nativeChatSessionOptions,
|
||||||
input.agent
|
input.agent
|
||||||
)
|
)
|
||||||
const options = defaults
|
|
||||||
? Object.fromEntries(
|
|
||||||
['model', 'effort'].flatMap((key) =>
|
|
||||||
typeof defaults[key] === 'string' ? [[key, defaults[key]]] : []
|
|
||||||
)
|
|
||||||
)
|
|
||||||
: undefined
|
|
||||||
const location = await this.resolveStructuredAgentSessionLocation(input.worktree)
|
const location = await this.resolveStructuredAgentSessionLocation(input.worktree)
|
||||||
const workspacePath = (await this.resolveRuntimeFileTarget(input.worktree)).worktree.path
|
const workspacePath = (await this.resolveRuntimeFileTarget(input.worktree)).worktree.path
|
||||||
return {
|
return {
|
||||||
@@ -189,7 +182,7 @@ export class OrcaRuntimeWithResolveRecoveredStructuredTuiTranscript extends Orca
|
|||||||
variable: input.agent === 'claude' ? 'CLAUDE_CONFIG_DIR' : 'CODEX_HOME',
|
variable: input.agent === 'claude' ? 'CLAUDE_CONFIG_DIR' : 'CODEX_HOME',
|
||||||
path: await resolveAccountHomePath({ workspacePath, launchEnv, location })
|
path: await resolveAccountHomePath({ workspacePath, launchEnv, location })
|
||||||
},
|
},
|
||||||
...(options && Object.keys(options).length > 0 ? { options } : {}),
|
...(options ? { options } : {}),
|
||||||
runtimeKind: 'native'
|
runtimeKind: 'native'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest'
|
|||||||
import {
|
import {
|
||||||
clearNativeChatSessionOptionModel,
|
clearNativeChatSessionOptionModel,
|
||||||
resolveNativeChatSessionOptionDefaults,
|
resolveNativeChatSessionOptionDefaults,
|
||||||
|
resolveStructuredLaunchSeedOptions,
|
||||||
updateNativeChatSessionOptionDefaults
|
updateNativeChatSessionOptionDefaults
|
||||||
} from './native-chat-session-option-defaults'
|
} from './native-chat-session-option-defaults'
|
||||||
import type { PersistedNativeChatSessionOptions } from './native-chat-session-options'
|
import type { PersistedNativeChatSessionOptions } from './native-chat-session-options'
|
||||||
@@ -74,3 +75,56 @@ describe('resolveNativeChatSessionOptionDefaults', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('resolveStructuredLaunchSeedOptions', () => {
|
||||||
|
const persistedCodex = (
|
||||||
|
valuesByModel: Record<string, Record<string, string | boolean>>
|
||||||
|
): PersistedNativeChatSessionOptions =>
|
||||||
|
({ codex: { model: 'gpt-5.6-sol', valuesByModel } }) as PersistedNativeChatSessionOptions
|
||||||
|
|
||||||
|
it('seeds the saved model and effort a structured create must apply', () => {
|
||||||
|
expect(
|
||||||
|
resolveStructuredLaunchSeedOptions(
|
||||||
|
persistedCodex({ 'gpt-5.6-sol': { effort: 'medium' } }),
|
||||||
|
'codex'
|
||||||
|
)
|
||||||
|
).toEqual({ model: 'gpt-5.6-sol', effort: 'medium' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('drops ids the providers only accept mid-session', () => {
|
||||||
|
// `fastMode` is a boolean and `personality` is settable only mid-session;
|
||||||
|
// neither belongs in the reservation's Record<string, string>.
|
||||||
|
expect(
|
||||||
|
resolveStructuredLaunchSeedOptions(
|
||||||
|
persistedCodex({
|
||||||
|
'gpt-5.6-sol': { effort: 'high', fastMode: true, personality: 'concise' }
|
||||||
|
}),
|
||||||
|
'codex'
|
||||||
|
)
|
||||||
|
).toEqual({ model: 'gpt-5.6-sol', effort: 'high' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('drops a seeded id whose persisted value is not a usable string', () => {
|
||||||
|
// settings.json is user-writable, so a non-string `effort` must not reach a
|
||||||
|
// record typed Record<string, string> and be emitted as a turn option.
|
||||||
|
expect(
|
||||||
|
resolveStructuredLaunchSeedOptions(
|
||||||
|
persistedCodex({ 'gpt-5.6-sol': { effort: true } }),
|
||||||
|
'codex'
|
||||||
|
)
|
||||||
|
).toEqual({ model: 'gpt-5.6-sol' })
|
||||||
|
expect(
|
||||||
|
resolveStructuredLaunchSeedOptions(
|
||||||
|
persistedCodex({ 'gpt-5.6-sol': { effort: ' ' } }),
|
||||||
|
'codex'
|
||||||
|
)
|
||||||
|
).toEqual({ model: 'gpt-5.6-sol' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('seeds nothing until a model is picked, so the CLI default survives', () => {
|
||||||
|
expect(resolveStructuredLaunchSeedOptions(undefined, 'codex')).toBeUndefined()
|
||||||
|
expect(
|
||||||
|
resolveStructuredLaunchSeedOptions({ codex: { valuesByModel: {} } }, 'codex')
|
||||||
|
).toBeUndefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
@@ -28,6 +28,33 @@ export function resolveNativeChatSessionOptionDefaults(
|
|||||||
return values
|
return values
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Why only these two: they are the only ids the picker persists into
|
||||||
|
* `nativeChatSessionOptions` that both structured providers also accept as
|
||||||
|
* strings. Claude's `fastMode` is a boolean the durable `Record<string, string>`
|
||||||
|
* record cannot carry, and the providers' remaining keys are settable only
|
||||||
|
* mid-session, never seeded at launch. */
|
||||||
|
const STRUCTURED_LAUNCH_SEED_OPTION_IDS = ['model', 'effort'] as const
|
||||||
|
|
||||||
|
/** The saved selection a structured create seeds into its reservation, narrowed
|
||||||
|
* to the wire-safe string subset the durable record and both providers accept. */
|
||||||
|
export function resolveStructuredLaunchSeedOptions(
|
||||||
|
persisted: PersistedNativeChatSessionOptions | null | undefined,
|
||||||
|
agent: AgentType
|
||||||
|
): Record<string, string> | undefined {
|
||||||
|
const defaults = resolveNativeChatSessionOptionDefaults(persisted, agent)
|
||||||
|
if (!defaults) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
const seeded: Record<string, string> = {}
|
||||||
|
for (const id of STRUCTURED_LAUNCH_SEED_OPTION_IDS) {
|
||||||
|
const value = defaults[id]
|
||||||
|
if (typeof value === 'string' && value.trim()) {
|
||||||
|
seeded[id] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Object.keys(seeded).length > 0 ? seeded : undefined
|
||||||
|
}
|
||||||
|
|
||||||
/** Why: an authoritative probe proved this id gone, and a stale `model` is emitted
|
/** Why: an authoritative probe proved this id gone, and a stale `model` is emitted
|
||||||
* verbatim as a launch flag — grok exits fatally on an unknown one. Dropping only
|
* verbatim as a launch flag — grok exits fatally on an unknown one. Dropping only
|
||||||
* `model` keeps the per-model option values for a later reselect. */
|
* `model` keeps the per-model option values for a later reselect. */
|
||||||
|
|||||||
Reference in New Issue
Block a user