mirror of
https://github.com/stablyai/orca.git
synced 2026-09-29 08:03:20 +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>
101 lines
3.7 KiB
TypeScript
101 lines
3.7 KiB
TypeScript
import type { AgentType } from './agent-status-types'
|
|
|
|
export type SessionOptionValue = string | boolean
|
|
|
|
export type SessionOptionSelectChoice = {
|
|
value: string
|
|
label: string
|
|
description?: string
|
|
}
|
|
|
|
/** `default` is the catalog's own value shown before anything is observed —
|
|
* truthful to display, but never evidence about a running agent. `dispatched`
|
|
* is sent-but-unread: the pill shows it, and a later report that disagrees is
|
|
* what corrects it. Both transports emit it, so it alone names neither — see
|
|
* `transport` on the descriptor. */
|
|
export type SessionOptionValueSource = 'applied' | 'dispatched' | 'reported' | 'default' | 'unknown'
|
|
|
|
/** How a live value reaches the agent. `catalog` types the catalog's command into
|
|
* the agent's terminal and can only learn the outcome by parsing the screen back;
|
|
* `agent-session` writes over the structured protocol, which reports every turn. */
|
|
export type NativeChatLiveOptionTransport = 'catalog' | 'agent-session'
|
|
|
|
/** Closed set of reasons an option is not settable in the current mode. A key
|
|
* (not free English) so the producer and the localized label stay in sync —
|
|
* an exhaustive switch turns any drift into a type error instead of leaking
|
|
* untranslated text. */
|
|
export type SessionOptionDisabledReason =
|
|
| 'available-after-session-start'
|
|
| 'set-when-session-starts'
|
|
|
|
export type SessionOptionDescriptor = {
|
|
id: string
|
|
label: string
|
|
description?: string
|
|
category?: 'model' | 'thought_level' | 'model_config' | 'mode'
|
|
kind:
|
|
| {
|
|
type: 'select'
|
|
currentValue?: string
|
|
choices: SessionOptionSelectChoice[]
|
|
}
|
|
| {
|
|
type: 'boolean'
|
|
currentValue?: boolean
|
|
}
|
|
valueSource: SessionOptionValueSource
|
|
/** Required so a new producer cannot inherit the wrong lane's rendering by
|
|
* omission — `dispatched` is emitted identically by both and cannot discriminate. */
|
|
transport: NativeChatLiveOptionTransport
|
|
settable: boolean
|
|
disabledReason?: SessionOptionDisabledReason
|
|
/** Why: picker-only and toggle-only PTY commands cannot be represented as
|
|
* a truthful radio/checkbox state, so the producer exposes an action row. */
|
|
action?: { type: 'agent-picker' | 'toggle-command' }
|
|
}
|
|
|
|
/** A value we typed at the agent and have never read back. Only the terminal
|
|
* transport can be in this state: the structured lane's own per-turn report is
|
|
* what moves a value off `dispatched`, and until it lands nothing else has. */
|
|
export function sessionOptionDispatchUnconfirmed(
|
|
descriptor: Pick<SessionOptionDescriptor, 'valueSource' | 'transport'>
|
|
): boolean {
|
|
return descriptor.valueSource === 'dispatched' && descriptor.transport === 'catalog'
|
|
}
|
|
|
|
export type SessionOptionSetResult = {
|
|
snapshot: SessionOptionDescriptor[]
|
|
}
|
|
|
|
export type PersistedNativeChatSessionOptions = Partial<
|
|
Record<
|
|
string,
|
|
{
|
|
model?: string
|
|
valuesByModel?: Record<string, Record<string, SessionOptionValue>>
|
|
}
|
|
>
|
|
>
|
|
|
|
export type NativeChatSessionOptionSettingsMutation =
|
|
| {
|
|
type: 'apply-picks'
|
|
agent: AgentType
|
|
picks: readonly {
|
|
modelId: string
|
|
optionId: string
|
|
value: SessionOptionValue
|
|
adoptModelAsLaunchDefault?: boolean
|
|
}[]
|
|
}
|
|
| { type: 'clear-model-if-missing'; agent: AgentType; availableModelIds: readonly string[] }
|
|
|
|
export type SessionOptionsSurface = {
|
|
getSnapshot(): SessionOptionDescriptor[]
|
|
/** Apply an absolute target; known flip-only options use their tracked baseline. */
|
|
setOption(id: string, value: SessionOptionValue): Promise<SessionOptionSetResult>
|
|
/** Invoke the value-less action exposed by the current descriptor. */
|
|
invokeAction(id: string): Promise<SessionOptionSetResult>
|
|
subscribe(listener: (snapshot: SessionOptionDescriptor[]) => void): () => void
|
|
}
|