Ship Paseo-equivalent provider snapshot, AgentComposerBar, ACP dispatch rewrite with streaming/persist, permission prompts, and agent commands. Follow-up: pane-scoped chat resolution, CoderMessageList tool timeline, WS user-delta replace, and inference orphan tool_call stripping. Archive openspec v2-2; update CHANGELOG and CURRENT. Co-authored-by: Cursor <cursoragent@cursor.com>
155 lines
4.3 KiB
TypeScript
155 lines
4.3 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import type { SessionConfigOption } from '@agentclientprotocol/sdk';
|
|
import {
|
|
deriveModesFromACP,
|
|
deriveModelDefinitionsFromACP,
|
|
findThoughtLevelConfigId,
|
|
} from '../acp-derive.js';
|
|
|
|
describe('deriveModesFromACP', () => {
|
|
it('prefers modeState.availableModes when present', () => {
|
|
const { modes, currentModeId } = deriveModesFromACP(
|
|
[{ id: 'fallback', label: 'Fallback' }],
|
|
{
|
|
currentModeId: 'plan',
|
|
availableModes: [
|
|
{ id: 'plan', name: 'Plan', description: 'Read-only planning' },
|
|
{ id: 'code', name: 'Code' },
|
|
],
|
|
},
|
|
);
|
|
|
|
expect(modes).toEqual([
|
|
{ id: 'plan', label: 'Plan', description: 'Read-only planning' },
|
|
{ id: 'code', label: 'Code', description: undefined },
|
|
]);
|
|
expect(currentModeId).toBe('plan');
|
|
});
|
|
|
|
it('falls back to configOptions mode select', () => {
|
|
const configOptions: SessionConfigOption[] = [
|
|
{
|
|
type: 'select',
|
|
id: 'mode',
|
|
category: 'mode',
|
|
currentValue: 'auto',
|
|
options: [
|
|
{ value: 'auto', name: 'Auto' },
|
|
{ value: 'manual', name: 'Manual', description: 'Ask first' },
|
|
],
|
|
},
|
|
];
|
|
|
|
const { modes, currentModeId } = deriveModesFromACP([], null, configOptions);
|
|
|
|
expect(modes).toEqual([
|
|
{ id: 'auto', label: 'Auto', description: undefined },
|
|
{ id: 'manual', label: 'Manual', description: 'Ask first' },
|
|
]);
|
|
expect(currentModeId).toBe('auto');
|
|
});
|
|
|
|
it('uses static fallback when no ACP mode data', () => {
|
|
const fallback = [{ id: 'default', label: 'Default' }];
|
|
const { modes, currentModeId } = deriveModesFromACP(fallback, null, null);
|
|
|
|
expect(modes).toEqual(fallback);
|
|
expect(currentModeId).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('deriveModelDefinitionsFromACP', () => {
|
|
it('maps availableModels with thought_level options', () => {
|
|
const configOptions: SessionConfigOption[] = [
|
|
{
|
|
type: 'select',
|
|
id: 'thought',
|
|
category: 'thought_level',
|
|
currentValue: 'medium',
|
|
options: [
|
|
{ value: 'low', name: 'Low' },
|
|
{ value: 'medium', name: 'Medium' },
|
|
],
|
|
},
|
|
];
|
|
|
|
const models = deriveModelDefinitionsFromACP(
|
|
{
|
|
currentModelId: 'gpt-4',
|
|
availableModels: [
|
|
{ modelId: 'gpt-4', name: 'GPT-4' },
|
|
{ modelId: 'gpt-4-mini', name: 'Mini', description: 'Cheaper' },
|
|
],
|
|
},
|
|
configOptions,
|
|
);
|
|
|
|
expect(models).toEqual([
|
|
{
|
|
id: 'gpt-4',
|
|
label: 'GPT-4',
|
|
description: undefined,
|
|
isDefault: true,
|
|
thinkingOptions: [
|
|
{ id: 'low', label: 'Low', isDefault: false },
|
|
{ id: 'medium', label: 'Medium', isDefault: true },
|
|
],
|
|
defaultThinkingOptionId: 'medium',
|
|
},
|
|
{
|
|
id: 'gpt-4-mini',
|
|
label: 'Mini',
|
|
description: 'Cheaper',
|
|
isDefault: false,
|
|
thinkingOptions: [
|
|
{ id: 'low', label: 'Low', isDefault: false },
|
|
{ id: 'medium', label: 'Medium', isDefault: true },
|
|
],
|
|
defaultThinkingOptionId: 'medium',
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('falls back to model select config when no availableModels', () => {
|
|
const configOptions: SessionConfigOption[] = [
|
|
{
|
|
type: 'select',
|
|
id: 'model',
|
|
category: 'model',
|
|
currentValue: 'sonnet',
|
|
options: [
|
|
{ value: 'sonnet', name: 'Sonnet' },
|
|
{ value: 'opus', name: 'Opus' },
|
|
],
|
|
},
|
|
];
|
|
|
|
const models = deriveModelDefinitionsFromACP(null, configOptions);
|
|
|
|
expect(models).toEqual([
|
|
{ id: 'sonnet', label: 'Sonnet', isDefault: true, defaultThinkingOptionId: undefined },
|
|
{ id: 'opus', label: 'Opus', isDefault: false, defaultThinkingOptionId: undefined },
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe('findThoughtLevelConfigId', () => {
|
|
it('returns thought_level select id', () => {
|
|
const configOptions: SessionConfigOption[] = [
|
|
{
|
|
type: 'select',
|
|
id: 'effort',
|
|
category: 'thought_level',
|
|
currentValue: 'high',
|
|
options: [{ value: 'high', name: 'High' }],
|
|
},
|
|
];
|
|
|
|
expect(findThoughtLevelConfigId(configOptions)).toBe('effort');
|
|
});
|
|
|
|
it('returns null when missing', () => {
|
|
expect(findThoughtLevelConfigId(null)).toBeNull();
|
|
});
|
|
});
|