Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
169ed4a1dThis file is pinned to the selected release. Download the original to open it in your design tool.
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { mkdtempSync, rmSync, writeFileSync } from 'fs'
import { tmpdir } from 'os'
import { join } from 'path'
import type { RegisteredSession } from './registry.js'
/**
* Command Code agents were DISPLAY-ONLY between 2026-07-30 and CLI 1.6.0: switching had been built and
* reverted because the /model dialog ignored typed input, so it could only be arrow-driven, and a 49-row
* picker stalled the device's the device UI task into a watchdog reset. 1.6.0 added `--list-models` and gave
* `/model` and `/effort` arguments, so the picker is back — behind a version gate, and with the chip
* still required to name what the agent is actually running: model AND reasoning level.
*/
let commandcodeHome = ''
async function loadRuntimeProfile() {
vi.resetModules()
process.env.COMMANDCODE_HOME = commandcodeHome
return import('./runtimeProfile.js')
}
const session = (): RegisteredSession => ({
schemaVersion: 2,
active: true,
sessionId: 'session:1',
engine: 'commandcode',
launcherId: 'h1',
agentId: 'h1',
boundAt: 0,
transcriptPath: '/tmp/session.jsonl',
projectDir: 'tmp',
cwd: '/tmp',
tmuxPane: '%1',
runtimes: [{ backend: 'tmux', paneId: '%1' }],
primaryRuntimeKey: 'tmux\u0000%1',
source: null,
title: null,
model: null,
cliVersion: null,
processIdentity: null,
registeredAt: 1,
touchedAt: 1,
lastHookAt: 1,
lastTranscriptAt: 1,
})
/** An assistant line as Command Code writes it: the model is top level, as a full gateway id. */
const assistantLine = (model: string): string =>
JSON.stringify({ type: 'message', model, message: { role: 'assistant', content: [{ type: 'text', text: 'hi' }] } })
/** Its global config — the ONLY place the reasoning level is recorded, keyed by full id. */
const writeConfig = (config: unknown): void =>
writeFileSync(join(commandcodeHome, 'config.json'), JSON.stringify(config))
describe('a Command Code agent', () => {
beforeEach(() => {
commandcodeHome = mkdtempSync(join(tmpdir(), 'adapter-commandcode-'))
})
afterEach(() => {
rmSync(commandcodeHome, { recursive: true, force: true })
})
it('shows the running model and its configured reasoning level', async () => {
const { parseRuntimeProfile, RuntimeProfileManager } = await loadRuntimeProfile()
writeConfig({
model: 'deepseek/deepseek-v4-flash',
reasoningEffort: { 'deepseek/deepseek-v4-flash': 'high' },
})
const manager = new RuntimeProfileManager()
const target = session()
manager.ingest(target, assistantLine('deepseek/deepseek-v4-flash'))
await manager.ingestConfig(target, true)
const profile = parseRuntimeProfile(manager.selectedModel(target))
// Short name, not "deepseek/deepseek-v4-flash": the device labels by splitting on -/_ and never "/",
// so a vendor prefix reads as "Deepseek/deepseek V4 Flash" on the round screen.
expect(profile?.model).toBe('deepseek-v4-flash')
expect(profile?.effort).toBe('high')
})
it('keeps the level after the next assistant line', async () => {
// Regression: the transcript path used to stamp effort='auto' on every line, wiping the configured
// level a second after it was read, so the chip flipped back to Auto on the next reply.
const { parseRuntimeProfile, RuntimeProfileManager } = await loadRuntimeProfile()
writeConfig({ model: 'x/y', reasoningEffort: { 'moonshotai/kimi-k3': 'medium' } })
const manager = new RuntimeProfileManager()
const target = session()
manager.ingest(target, assistantLine('moonshotai/kimi-k3'))
await manager.ingestConfig(target, true)
manager.ingest(target, assistantLine('moonshotai/kimi-k3'))
expect(parseRuntimeProfile(manager.selectedModel(target))?.effort).toBe('medium')
})
it('falls back to Auto when the model has no level set', async () => {
const { parseRuntimeProfile, RuntimeProfileManager } = await loadRuntimeProfile()
writeConfig({ model: 'deepseek/deepseek-v4-flash', reasoningEffort: { 'qwen/qwen3.7-plus': 'low' } })
const manager = new RuntimeProfileManager()
const target = session()
manager.ingest(target, assistantLine('deepseek/deepseek-v4-flash'))
await manager.ingestConfig(target, true)
expect(parseRuntimeProfile(manager.selectedModel(target))?.effort).toBe('auto')
})
it('names the config model before the agent has answered once', async () => {
const { parseRuntimeProfile, RuntimeProfileManager } = await loadRuntimeProfile()
writeConfig({ model: 'moonshotai/kimi-k3', reasoningEffort: {} })
const manager = new RuntimeProfileManager()
const target = session()
await manager.ingestConfig(target, true)
// A fresh agent has no transcript line to read; the config's model is what it will run.
expect(parseRuntimeProfile(manager.selectedModel(target))?.model).toBe('kimi-k3')
})
it('re-reads the level when the model changes', async () => {
// The level is stored PER MODEL. Switching to a model with no level of its own kept showing the
// previous model's — the chip read "Kimi K3 · High" when Kimi K3 has no level set at all.
const { parseRuntimeProfile, RuntimeProfileManager } = await loadRuntimeProfile()
writeConfig({ model: 'deepseek/deepseek-v4-flash', reasoningEffort: { 'deepseek/deepseek-v4-flash': 'high' } })
const manager = new RuntimeProfileManager()
const target = session()
manager.ingest(target, assistantLine('deepseek/deepseek-v4-flash'))
await manager.ingestConfig(target, true)
expect(parseRuntimeProfile(manager.selectedModel(target))?.effort).toBe('high')
manager.ingest(target, assistantLine('moonshotai/kimi-k3'))
await new Promise((resolve) => setTimeout(resolve, 20)) // the re-read ingest() fires is async
const profile = parseRuntimeProfile(manager.selectedModel(target))
expect(profile?.model).toBe('kimi-k3')
expect(profile?.effort).toBe('auto')
})
it('offers nothing to pick when the catalogue cannot be read', async () => {
// Deliberately hide the CLI: a catalogue that cannot be read must leave the device with nothing to
// open rather than a half-list — and this test must not depend on what is installed on the computer.
const realPath = process.env.PATH
process.env.PATH = commandcodeHome
try {
const { RuntimeProfileManager } = await loadRuntimeProfile()
const manager = new RuntimeProfileManager()
const target = session()
manager.ingest(target, assistantLine('moonshotai/kimi-k3'))
await expect(manager.modelsForSession(target)).resolves.toEqual([])
} finally {
process.env.PATH = realPath
}
})
it('is never switchable, whatever the CLI version says', async () => {
// Switching is Claude and Codex only (owner, 2026-07-31). 1.6.0 does take `/model` as a plain command,
// so the driver would work — the gate is policy, not capability, which is why version is irrelevant.
const { supportsNativeRuntimeControl } = await loadRuntimeProfile()
expect(supportsNativeRuntimeControl({ ...session(), cliVersion: '1.6.0' })).toBe(false)
expect(supportsNativeRuntimeControl({ ...session(), cliVersion: '1.5.0' })).toBe(false)
expect(supportsNativeRuntimeControl({ ...session(), cliVersion: null })).toBe(false)
})
it('keeps the transcript schema version out of the CLI version', async () => {
const { RuntimeProfileManager } = await loadRuntimeProfile()
const manager = new RuntimeProfileManager()
const target = session()
// Command Code stamps `"version": 3` on its session header — a SCHEMA number. Reading it as a CLI
// version would invent a "3.0.0" build that no gate should ever act on.
manager.ingest(target, JSON.stringify({ type: 'session', version: 3, id: 'x', cwd: '/tmp' }))
expect(target.cliVersion).toBeNull()
})
})