mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 17:17:38 +00:00
* fix(omp): discover and switch native-chat models Report the running OMP provider/model and discover available choices on the execution host for desktop and mobile. Register an extension command to switch through the OMP API because its TUI does not accept /model args. Advertise that command in status so older hosts remain read-only. Addresses the OMP portion of #17603; Pi chat enablement remains separate. Model reporting begins on lifecycle activity; no startup status is invented. Co-authored-by: SudoAI-DEV <220139811+SudoAI-DEV@users.noreply.github.com> * refactor(omp): check generated model metadata types * test(omp): verify model picker command and reported selection * test(omp): add repeatable real model-switch runtime proof * test(omp): require model capability delivery in runtime smoke * fix(mobile): decode OMP model discovery through RPC operations * fix(omp): preserve exact reported model selectors * fix(omp): preserve generated extension syntax after rebase * fix(omp): merge generated harness UI context types * test(omp): model switching keeps one session manager * test(omp): include transcript path in model status proof * test(omp): avoid renderer error-type union * test(omp): keep renderer test type explicit --------- Co-authored-by: SudoAI-DEV <220139811+SudoAI-DEV@users.noreply.github.com>
84 lines
3.8 KiB
TypeScript
84 lines
3.8 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import {
|
|
CLAUDE_SESSION_OPTION_CATALOG,
|
|
CODEX_SESSION_OPTION_CATALOG
|
|
} from './agent-session-option-catalog-claude-codex'
|
|
import {
|
|
applyNativeChatReportedSessionOptions,
|
|
createNativeChatSessionOptionRecord,
|
|
matchNativeChatCatalogModelId,
|
|
type NativeChatSessionOptionRecord
|
|
} from './native-chat-session-option-state'
|
|
|
|
function claudeRecord(): NativeChatSessionOptionRecord {
|
|
return createNativeChatSessionOptionRecord('claude')
|
|
}
|
|
|
|
describe('applyNativeChatReportedSessionOptions', () => {
|
|
it('reports become authority and reset stale per-model values on a model change', () => {
|
|
const record = claudeRecord()
|
|
record.model = { value: 'sonnet', source: 'dispatched' }
|
|
record.valuesByModel.opus = { effort: { value: 'high', source: 'dispatched' } }
|
|
expect(applyNativeChatReportedSessionOptions(record, { model: 'opus' })).toBe(true)
|
|
expect(record.model).toEqual({ value: 'opus', source: 'reported' })
|
|
// A model change invalidates previously tracked values for the destination.
|
|
expect(record.valuesByModel.opus).toEqual({})
|
|
})
|
|
|
|
it('promotes a dispatched guess the report confirms', () => {
|
|
const record = claudeRecord()
|
|
record.model = { value: 'sonnet', source: 'dispatched' }
|
|
expect(applyNativeChatReportedSessionOptions(record, { model: 'sonnet' })).toBe(true)
|
|
expect(record.model).toEqual({ value: 'sonnet', source: 'reported' })
|
|
})
|
|
|
|
it('keeps locally tracked options when the reported model is unchanged', () => {
|
|
const record = claudeRecord()
|
|
record.model = { value: 'sonnet', source: 'reported' }
|
|
record.valuesByModel.sonnet = { effort: { value: 'high', source: 'dispatched' } }
|
|
expect(applyNativeChatReportedSessionOptions(record, { model: 'sonnet' })).toBe(false)
|
|
expect(record.valuesByModel.sonnet?.effort).toEqual({ value: 'high', source: 'dispatched' })
|
|
})
|
|
|
|
it('is a no-op when the report matches tracked state', () => {
|
|
const record = claudeRecord()
|
|
record.model = { value: 'sonnet', source: 'reported' }
|
|
expect(applyNativeChatReportedSessionOptions(record, { model: 'sonnet' })).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('matchNativeChatCatalogModelId', () => {
|
|
it('prefers the longest contained id and keeps catalog order for ties', () => {
|
|
const catalog = {
|
|
...CLAUDE_SESSION_OPTION_CATALOG,
|
|
models: ['a', 'abc', 'xyz'].map((id) => ({ id, label: id, options: [] }))
|
|
}
|
|
expect(matchNativeChatCatalogModelId(catalog, 'provider-xyz-abc')).toBe('abc')
|
|
expect(catalog.models.map((model) => model.id)).toEqual(['a', 'abc', 'xyz'])
|
|
})
|
|
|
|
it('keeps the reported selector as-is for a catalog that seeds no models', () => {
|
|
// Why: OMP's hook reports `provider/id`, the exact form `/model` accepts back,
|
|
// and an empty seed has nothing that could outrank it.
|
|
const unseeded = { ...CLAUDE_SESSION_OPTION_CATALOG, models: [] }
|
|
expect(matchNativeChatCatalogModelId(unseeded, ' deepseek/deepseek-v4-pro ')).toBe(
|
|
'deepseek/deepseek-v4-pro'
|
|
)
|
|
expect(matchNativeChatCatalogModelId(unseeded, ' ')).toBeNull()
|
|
})
|
|
|
|
it('matches exact ids, labels, and provider-id containment', () => {
|
|
expect(matchNativeChatCatalogModelId(CLAUDE_SESSION_OPTION_CATALOG, 'sonnet')).toBe('sonnet')
|
|
expect(matchNativeChatCatalogModelId(CLAUDE_SESSION_OPTION_CATALOG, 'Sonnet 5')).toBe('sonnet')
|
|
expect(matchNativeChatCatalogModelId(CLAUDE_SESSION_OPTION_CATALOG, 'claude-sonnet-5')).toBe(
|
|
'sonnet'
|
|
)
|
|
expect(matchNativeChatCatalogModelId(CODEX_SESSION_OPTION_CATALOG, 'gpt-5.5')).toBe('gpt-5.5')
|
|
})
|
|
|
|
it('returns null for unrecognized reports', () => {
|
|
expect(matchNativeChatCatalogModelId(CLAUDE_SESSION_OPTION_CATALOG, 'mystery-model')).toBeNull()
|
|
expect(matchNativeChatCatalogModelId(CLAUDE_SESSION_OPTION_CATALOG, '')).toBeNull()
|
|
})
|
|
})
|