From b1bba172d77920356a5223473f7897fcd9af460b Mon Sep 17 00:00:00 2001 From: dschlueter Date: Mon, 15 Jun 2026 06:56:50 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20Modell=20nach=20Coding-Kommando=20auf=20?= =?UTF-8?q?vorheriges=20Modell=20zur=C3=BCcksetzen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit selectRole() sichert jetzt ctx.model vor dem ersten Wechsel auf coding_model. deselectRole() stellt es danach wieder her — alle 10 Kommando-Exits (/coder, /judge, /fix, /shipit, /patch, /quick_check, /plan, /continue, /optimize finally, runUpdateDoku) rufen deselectRole(pi) statt currentRole = "idle" auf. Damit bleibt das OpenRouter-Modell des Nutzers aktiv für freien Chat (Wetter, Recherchen etc.), während Coding-Kommandos weiterhin explizit coding_model via selectRole() setzen. Co-Authored-By: Claude Sonnet 4.6 --- pi-coding-extension.ts | 43 ++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/pi-coding-extension.ts b/pi-coding-extension.ts index 14ea727..34cd4dd 100644 --- a/pi-coding-extension.ts +++ b/pi-coding-extension.ts @@ -460,15 +460,34 @@ async function ensureSingleModel( // Setzt die aktive Rolle (steuert den per before_agent_start injizierten System-Prompt) // und stellt das Single-Modell sicher. Ersetzt das frühere switchModel(): da beide Rollen // dasselbe Modell nutzen, ist der einzige echte Effekt das Setzen von currentRole. +// Speichert ctx.model vor dem ersten Wechsel damit deselectRole() es wiederherstellen kann. async function selectRole( pi: ExtensionAPI, ctx: ExtensionCommandContext, role: "coder" | "judge" ): Promise { + if (previousModel === null && ctx.model) { + previousModel = ctx.model; + } currentRole = role; return ensureSingleModel(pi, ctx); } +// Kehrt selectRole() um: setzt currentRole auf "idle" und stellt das vor dem letzten +// selectRole()-Aufruf aktive Modell wieder her (damit Free-Chat wieder das OpenRouter-Modell nutzt). +async function deselectRole(pi: ExtensionAPI): Promise { + currentRole = "idle"; + if (previousModel !== null) { + try { + await pi.setModel(previousModel); + } catch { + // Modell-Restore fehlgeschlagen — currentRole ist trotzdem "idle" + } + previousModel = null; + currentModelKey = ""; + } +} + // Sendet eine Nachricht und wartet bis der Agent fertig ist. // Retry-Schleife fängt "Agent is already processing" ab — tritt auf wenn // waitForIdle() zu früh zurückkehrt (Race Condition im pi-Agent). @@ -762,7 +781,7 @@ async function runUpdateDoku(pi: ExtensionAPI, ctx: ExtensionCommandContext): Pr ctx.ui.setStatus("update_doku", "✓ Dokumentation abgeschlossen"); ctx.ui.notify("Dokumentations-Phase abgeschlossen. Commit angelegt.", "info"); - currentRole = "idle"; + await deselectRole(pi); } // ── Versions-Verwaltung (SemVer + Git-Tags) ────────────────────────────────── @@ -922,6 +941,7 @@ function finalNotify( let cancelRequested = false; let currentModelKey = ""; // Cache für ensureSingleModel() — verhindert redundante setModel()-Aufrufe +let previousModel: any = null; // Modell vor dem letzten selectRole()-Aufruf — wird in deselectRole() wiederhergestellt // Aktive Rolle für die Single-Server-Architektur: steuert, welcher Persona-System-Prompt // im before_agent_start-Hook injiziert wird. Wird ausschließlich über selectRole() gesetzt. // "idle" = kein aktiver Coding-Workflow → Hook injiziert keine Persona (freier Chat). @@ -1082,7 +1102,7 @@ export default function (pi: ExtensionAPI) { await selectRole(pi, ctx, "coder"); currentActivity = "Coder implementiert…"; await sendAndWait(pi, ctx, coderKickoff(task)); - currentRole = "idle"; + await deselectRole(pi); } }); @@ -1096,7 +1116,7 @@ export default function (pi: ExtensionAPI) { await selectRole(pi, ctx, "judge"); currentActivity = "Judge reviewt…"; await sendAndWait(pi, ctx, judgePrompt(args || "")); - currentRole = "idle"; + await deselectRole(pi); } }); @@ -1110,7 +1130,7 @@ export default function (pi: ExtensionAPI) { await selectRole(pi, ctx, "coder"); currentActivity = "Coder fixt Judge-Kritik…"; await sendAndWait(pi, ctx, fixPrompt(args || "")); - currentRole = "idle"; + await deselectRole(pi); } }); @@ -1133,7 +1153,7 @@ export default function (pi: ExtensionAPI) { } else if (shipVerdict === "NO-SHIP") { ctx.ui.notify("NO-SHIP — noch Blocker offen. Bitte /fix aufrufen.", "error"); } - currentRole = "idle"; + await deselectRole(pi); } }); @@ -1394,9 +1414,8 @@ export default function (pi: ExtensionAPI) { stopPhaseTimer(); // Sicherstellen dass keine Zustandsvariable in späteren /optimize-Aufruf leckt cancelRequested = false; - currentModelKey = ""; - // Zurück auf idle: freier Chat nach dem Lauf soll keine Coding-Persona erhalten. - currentRole = "idle"; + // Zurück auf idle + Modell wiederherstellen: freier Chat nach dem Lauf soll keine Coding-Persona erhalten. + await deselectRole(pi); interactivePauseActive = false; interactiveContinueRequested = false; interactivePauseTask = ""; @@ -1421,7 +1440,7 @@ export default function (pi: ExtensionAPI) { await selectRole(pi, ctx, "coder"); currentActivity = "Coder patcht…"; await sendAndWait(pi, ctx, patchPrompt(change)); - currentRole = "idle"; + await deselectRole(pi); } }); @@ -1435,7 +1454,7 @@ export default function (pi: ExtensionAPI) { await selectRole(pi, ctx, "judge"); currentActivity = "Judge: Schnellcheck…"; await sendAndWait(pi, ctx, quickCheckPrompt(args || "")); - currentRole = "idle"; + await deselectRole(pi); } }); @@ -1508,7 +1527,7 @@ export default function (pi: ExtensionAPI) { await sendAndWait(pi, ctx, planPrompt(task)); ctx.ui.setStatus("plan", ""); finalNotify(ctx, "📋 Plan", "Analyse abgeschlossen — PLAN.md + Chat"); - currentRole = "idle"; + await deselectRole(pi); } }); @@ -1596,7 +1615,7 @@ export default function (pi: ExtensionAPI) { "5. Fahre direkt mit dem nächsten sinnvollen Schritt fort — kein langer Bericht, einfach weitermachen.", ].join("\n")); ctx.ui.setStatus("continue", ""); - currentRole = "idle"; + await deselectRole(pi); } });