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>
129 lines
3.8 KiB
TypeScript
129 lines
3.8 KiB
TypeScript
/**
|
|
* ACP model/mode derivation — adapted from Paseo acp-agent.ts.
|
|
*/
|
|
import type {
|
|
SessionConfigOption,
|
|
SessionModelState,
|
|
SessionModeState,
|
|
} from '@agentclientprotocol/sdk';
|
|
import type { ProviderMode, ProviderModel, ThinkingOption } from './provider-types.js';
|
|
|
|
type SelectConfigOption = Extract<SessionConfigOption, { type: 'select' }>;
|
|
|
|
interface SelectConfigChoice {
|
|
value: string;
|
|
name: string;
|
|
description?: string | null;
|
|
group?: string;
|
|
}
|
|
|
|
function findSelectConfigOption({
|
|
configOptions,
|
|
category,
|
|
id,
|
|
}: {
|
|
configOptions: SessionConfigOption[] | null | undefined;
|
|
category: string;
|
|
id?: string;
|
|
}): SelectConfigOption | null {
|
|
const option = configOptions?.find(
|
|
(entry): entry is SelectConfigOption =>
|
|
entry.type === 'select' && entry.category === category && (!id || entry.id === id),
|
|
);
|
|
return option ?? null;
|
|
}
|
|
|
|
function flattenSelectOptions(options: SelectConfigOption['options']): SelectConfigChoice[] {
|
|
const flattened: SelectConfigChoice[] = [];
|
|
for (const option of options) {
|
|
if ('value' in option) {
|
|
flattened.push(option);
|
|
continue;
|
|
}
|
|
for (const groupOption of option.options) {
|
|
flattened.push({ ...groupOption, group: option.group });
|
|
}
|
|
}
|
|
return flattened;
|
|
}
|
|
|
|
function deriveSelectorOptions(
|
|
configOptions: SessionConfigOption[] | null | undefined,
|
|
category: string,
|
|
): ThinkingOption[] {
|
|
const option = findSelectConfigOption({ configOptions, category });
|
|
if (!option) return [];
|
|
|
|
return flattenSelectOptions(option.options).map((value) => ({
|
|
id: value.value,
|
|
label: value.name,
|
|
isDefault: value.value === option.currentValue,
|
|
}));
|
|
}
|
|
|
|
export function deriveModesFromACP(
|
|
fallbackModes: ProviderMode[],
|
|
modeState?: SessionModeState | null,
|
|
configOptions?: SessionConfigOption[] | null,
|
|
): { modes: ProviderMode[]; currentModeId: string | null } {
|
|
if (modeState?.availableModes?.length) {
|
|
return {
|
|
modes: modeState.availableModes.map((mode) => ({
|
|
id: mode.id,
|
|
label: mode.name,
|
|
description: mode.description ?? undefined,
|
|
})),
|
|
currentModeId: modeState.currentModeId ?? null,
|
|
};
|
|
}
|
|
|
|
const modeOption = findSelectConfigOption({ configOptions, category: 'mode' });
|
|
if (modeOption) {
|
|
const flatOptions = flattenSelectOptions(modeOption.options);
|
|
return {
|
|
modes: flatOptions.map((option) => ({
|
|
id: option.value,
|
|
label: option.name,
|
|
description: option.description ?? undefined,
|
|
})),
|
|
currentModeId: modeOption.currentValue,
|
|
};
|
|
}
|
|
|
|
return { modes: fallbackModes, currentModeId: null };
|
|
}
|
|
|
|
export function deriveModelDefinitionsFromACP(
|
|
models: SessionModelState | null | undefined,
|
|
configOptions?: SessionConfigOption[] | null,
|
|
): ProviderModel[] {
|
|
const thinkingOptions = deriveSelectorOptions(configOptions, 'thought_level');
|
|
const defaultThinkingOptionId = thinkingOptions.find((o) => o.isDefault)?.id;
|
|
|
|
if (models?.availableModels?.length) {
|
|
return models.availableModels.map((model) => ({
|
|
id: model.modelId,
|
|
label: model.name,
|
|
description: model.description ?? undefined,
|
|
isDefault: model.modelId === models.currentModelId,
|
|
thinkingOptions: thinkingOptions.length > 0 ? thinkingOptions : undefined,
|
|
defaultThinkingOptionId: defaultThinkingOptionId ?? undefined,
|
|
}));
|
|
}
|
|
|
|
const modelOptions = deriveSelectorOptions(configOptions, 'model');
|
|
return modelOptions.map((option) => ({
|
|
id: option.id,
|
|
label: option.label,
|
|
isDefault: option.isDefault,
|
|
thinkingOptions: thinkingOptions.length > 0 ? thinkingOptions : undefined,
|
|
defaultThinkingOptionId: defaultThinkingOptionId ?? undefined,
|
|
}));
|
|
}
|
|
|
|
export function findThoughtLevelConfigId(
|
|
configOptions: SessionConfigOption[] | null | undefined,
|
|
): string | null {
|
|
return findSelectConfigOption({ configOptions, category: 'thought_level' })?.id ?? null;
|
|
}
|