singularity-forge/patches/@mariozechner+pi-coding-agent+0.57.1.patch
Lex Christopherson 1872e8db78 fix: prevent auto-mode model switches from persisting as global default (#30) and harden resume resilience (#16)
Patch SDK setModel() to accept { persist: false } so per-unit model
switching in auto-mode no longer overwrites the user's global default.
Add state rebuild + doctor on resume, guard logging for silent dispatch
failures, and active-state check before prompt injection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 17:39:46 -06:00

108 lines
5.6 KiB
Diff

diff --git a/node_modules/@mariozechner/pi-coding-agent/dist/core/agent-session.js b/node_modules/@mariozechner/pi-coding-agent/dist/core/agent-session.js
index 90622c2..cff094b 100644
--- a/node_modules/@mariozechner/pi-coding-agent/dist/core/agent-session.js
+++ b/node_modules/@mariozechner/pi-coding-agent/dist/core/agent-session.js
@@ -1007,7 +1007,7 @@ export class AgentSession {
* Validates API key, saves to session and settings.
* @throws Error if no API key available for the model
*/
- async setModel(model) {
+ async setModel(model, options) {
const apiKey = await this._modelRegistry.getApiKey(model);
if (!apiKey) {
throw new Error(`No API key for ${model.provider}/${model.id}`);
@@ -1016,7 +1016,9 @@ export class AgentSession {
const thinkingLevel = this._getThinkingLevelForModelSwitch();
this.agent.setModel(model);
this.sessionManager.appendModelChange(model.provider, model.id);
- this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
+ if (options?.persist !== false) {
+ this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
+ }
// Re-clamp thinking level for new model's capabilities
this.setThinkingLevel(thinkingLevel);
await this._emitModelSelect(model, previousModel, "set");
@@ -1067,7 +1069,9 @@ export class AgentSession {
// Apply model
this.agent.setModel(next.model);
this.sessionManager.appendModelChange(next.model.provider, next.model.id);
- this.settingsManager.setDefaultModelAndProvider(next.model.provider, next.model.id);
+ if (options?.persist !== false) {
+ this.settingsManager.setDefaultModelAndProvider(next.model.provider, next.model.id);
+ }
// Apply thinking level.
// - Explicit scoped model thinking level overrides current session level
// - Undefined scoped model thinking level inherits the current session preference
@@ -1094,7 +1098,9 @@ export class AgentSession {
const thinkingLevel = this._getThinkingLevelForModelSwitch();
this.agent.setModel(nextModel);
this.sessionManager.appendModelChange(nextModel.provider, nextModel.id);
- this.settingsManager.setDefaultModelAndProvider(nextModel.provider, nextModel.id);
+ if (options?.persist !== false) {
+ this.settingsManager.setDefaultModelAndProvider(nextModel.provider, nextModel.id);
+ }
// Re-clamp thinking level for new model's capabilities
this.setThinkingLevel(thinkingLevel);
await this._emitModelSelect(nextModel, currentModel, "cycle");
@@ -1659,11 +1665,11 @@ export class AgentSession {
setActiveTools: (toolNames) => this.setActiveToolsByName(toolNames),
refreshTools: () => this._refreshToolRegistry(),
getCommands,
- setModel: async (model) => {
+ setModel: async (model, options) => {
const key = await this.modelRegistry.getApiKey(model);
if (!key)
return false;
- await this.setModel(model);
+ await this.setModel(model, options);
return true;
},
getThinkingLevel: () => this.thinkingLevel,
diff --git a/node_modules/@mariozechner/pi-coding-agent/dist/core/tools/bash.js b/node_modules/@mariozechner/pi-coding-agent/dist/core/tools/bash.js
index 27fe820..68f277f 100644
--- a/node_modules/@mariozechner/pi-coding-agent/dist/core/tools/bash.js
+++ b/node_modules/@mariozechner/pi-coding-agent/dist/core/tools/bash.js
@@ -1,11 +1,35 @@
import { randomBytes } from "node:crypto";
import { createWriteStream, existsSync } from "node:fs";
+import { createRequire } from "node:module";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { Type } from "@sinclair/typebox";
import { spawn } from "child_process";
import { getShellConfig, getShellEnv, killProcessTree } from "../../utils/shell.js";
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, truncateTail } from "./truncate.js";
+// Cached Win32 FFI handles for restoring VT input after child processes
+let _vtHandles = null;
+function restoreWindowsVTInput() {
+ if (process.platform !== "win32") return;
+ try {
+ if (!_vtHandles) {
+ const cjsRequire = createRequire(import.meta.url);
+ const koffi = cjsRequire("koffi");
+ const k32 = koffi.load("kernel32.dll");
+ const GetStdHandle = k32.func("void* __stdcall GetStdHandle(int)");
+ const GetConsoleMode = k32.func("bool __stdcall GetConsoleMode(void*, _Out_ uint32_t*)");
+ const SetConsoleMode = k32.func("bool __stdcall SetConsoleMode(void*, uint32_t)");
+ const handle = GetStdHandle(-10);
+ _vtHandles = { GetConsoleMode, SetConsoleMode, handle };
+ }
+ const ENABLE_VIRTUAL_TERMINAL_INPUT = 0x0200;
+ const mode = new Uint32Array(1);
+ _vtHandles.GetConsoleMode(_vtHandles.handle, mode);
+ if (!(mode[0] & ENABLE_VIRTUAL_TERMINAL_INPUT)) {
+ _vtHandles.SetConsoleMode(_vtHandles.handle, mode[0] | ENABLE_VIRTUAL_TERMINAL_INPUT);
+ }
+ } catch { }
+}
/**
* Generate a unique temp file path for bash output
*/
@@ -76,6 +100,7 @@ const defaultBashOperations = {
}
// Handle process exit
child.on("close", (code) => {
+ restoreWindowsVTInput();
if (timeoutHandle)
clearTimeout(timeoutHandle);
if (signal)