import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { Effect } from "effect"; import { Type } from "typebox"; import { formatModelAmbiguous, formatModelNotFound, type ModelSummary, resolveModel, } from "./core.ts"; export default function switchModelExtension(pi: ExtensionAPI) { pi.registerTool({ name: "switch_model", label: "Switch Model", description: 'Switch the active model mid-conversation, e.g. when the user says "implement this ' + 'with 5.6 sol" or "switch to fable". Free-text query — not required to be an exact ' + "id. The switch is permanent: stays on the new model until the user or you call this " + "again. Conversation history and working tree are unchanged.", promptGuidelines: [ "Call switch_model when the user names a different model to use, then continue the task on the new model in the same reply flow — no approval loop needed.", "If switch_model fails, it lists the actual available models; retry once with an exact provider/id from that list.", ], parameters: Type.Object({ query: Type.String({ description: 'e.g. "5.6 sol", "fable", "sonnet"' }), }), async execute(_toolCallId, params, _signal, _onUpdate, ctx) { const available: ModelSummary[] = ctx.modelRegistry .getAvailable() .map((model) => ({ provider: model.provider, id: model.id, name: model.name })); const resolved = await Effect.runPromise( resolveModel(params.query, available).pipe( Effect.mapError((error) => error._tag === "ModelNotFoundError" ? new Error(formatModelNotFound(error)) : new Error(formatModelAmbiguous(error)), ), ), ); const model = ctx.modelRegistry.find(resolved.provider, resolved.id); if (!model) { throw new Error(`Resolved model disappeared from registry: ${resolved.provider}/${resolved.id}`); } if (!(await pi.setModel(model))) { throw new Error(`No credentials available for ${resolved.provider}/${resolved.id}`); } return { content: [ { type: "text" as const, text: `Switched to ${resolved.provider}/${resolved.id}. Staying on this model until asked to switch again.`, }, ], details: { provider: resolved.provider, id: resolved.id }, }; }, }); }