mirror of
https://github.com/stablyai/orca.git
synced 2026-09-30 08:03:12 +00:00
* fix(native-chat): remember structured chat model and effort picks Structured Claude and Codex sessions already read the saved launch options at create, but nothing ever wrote them back. The only writer of `nativeChatSessionOptions` was the PTY picker, and the composer swaps in the structured surface for structured panes, so a structured pick went nowhere: it was forgotten when the session ended and every new session started at the CLI default. Persist a settled pick from both the desktop and mobile structured surfaces. Model and effort are stored as a pair, because a launch resolves a stored effort only under a stored model — so an effort-only pick adopts the model it was chosen against, otherwise the remembered effort never reaches a launch at all. Two things the persist path deliberately avoids: it writes what the provider committed rather than what was requested, since Codex reconciles an effort the newly selected model cannot run; and it never writes the provider readback, which is the CLI's own default and would pin a `-m` the user never chose. * fix(native-chat): persist session option picks atomically --------- Co-authored-by: Merge Sim <sim@local>
79 lines
2.6 KiB
TypeScript
79 lines
2.6 KiB
TypeScript
import { describe, expect, it, vi } from 'vitest'
|
|
import type { OrcaRuntimeService } from '../../orca-runtime'
|
|
import type { RpcRequest } from '../core'
|
|
import { RpcDispatcher } from '../dispatcher'
|
|
import { CLIENT_UI_METHODS } from './client-ui'
|
|
|
|
const request = (params: unknown): RpcRequest => ({
|
|
id: 'req-1',
|
|
authToken: 'tok',
|
|
method: 'settings.mutateNativeChatSessionOptions',
|
|
params
|
|
})
|
|
|
|
describe('native-chat settings RPC', () => {
|
|
it('routes option deltas to the runtime-owned atomic update', async () => {
|
|
const updateClientNativeChatSessionOptions = vi.fn()
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
updateClientNativeChatSessionOptions
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
|
|
const mutation = {
|
|
type: 'apply-picks' as const,
|
|
agent: 'codex' as const,
|
|
picks: [
|
|
{ modelId: 'gpt-fast', optionId: 'model' as const, value: 'gpt-fast' },
|
|
{ modelId: 'gpt-fast', optionId: 'effort' as const, value: 'low' }
|
|
]
|
|
}
|
|
|
|
const response = await dispatcher.dispatch(request(mutation))
|
|
|
|
expect(updateClientNativeChatSessionOptions).toHaveBeenCalledExactlyOnceWith(mutation)
|
|
expect(response).toMatchObject({ ok: true, result: { ok: true } })
|
|
})
|
|
|
|
it('rejects malformed option deltas', async () => {
|
|
const updateClientNativeChatSessionOptions = vi.fn()
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
updateClientNativeChatSessionOptions
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
|
|
|
|
for (const mutation of [
|
|
{ type: 'apply-picks', agent: 'codex', picks: [] },
|
|
{
|
|
type: 'apply-picks',
|
|
agent: 'opencode',
|
|
picks: [{ modelId: 'model', optionId: 'model', value: 'model' }]
|
|
},
|
|
{
|
|
type: 'apply-picks',
|
|
agent: 'codex',
|
|
picks: [{ modelId: 'model', optionId: 'arbitrary', value: 'value' }]
|
|
},
|
|
{
|
|
type: 'apply-picks',
|
|
agent: 'codex',
|
|
picks: [{ modelId: 'model', optionId: 'effort', value: true }]
|
|
},
|
|
{
|
|
type: 'apply-picks',
|
|
agent: 'cursor',
|
|
picks: [{ modelId: 'model', optionId: 'fastMode', value: 'true' }]
|
|
},
|
|
{
|
|
type: 'clear-model-if-missing',
|
|
agent: 'grok',
|
|
availableModelIds: []
|
|
}
|
|
]) {
|
|
const response = await dispatcher.dispatch(request(mutation))
|
|
expect(response).toMatchObject({ ok: false, error: { code: 'invalid_argument' } })
|
|
}
|
|
expect(updateClientNativeChatSessionOptions).not.toHaveBeenCalled()
|
|
})
|
|
})
|