From 8ca2ae2c2c48d4735066d0367694405ea8ea18c5 Mon Sep 17 00:00:00 2001 From: abose Date: Sat, 25 Apr 2026 22:43:38 +0530 Subject: [PATCH 1/3] fix(ai-chat): forward replace_all from Edit tool to buffer applier The PreToolUse hook intercepting Claude's Edit tool was dropping the replace_all flag, so buffer/plan edits only ever replaced the first occurrence even when Claude requested replace_all=true. --- src-node/claude-code-agent.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src-node/claude-code-agent.js b/src-node/claude-code-agent.js index 40eb387683..a6a3222add 100644 --- a/src-node/claude-code-agent.js +++ b/src-node/claude-code-agent.js @@ -522,7 +522,13 @@ async function _runQuery(requestId, prompt, projectPath, model, signal, locale, content = fs.readFileSync(input.tool_input.file_path, "utf8"); } if (input.tool_input.old_string && input.tool_input.new_string) { - content = content.replace(input.tool_input.old_string, input.tool_input.new_string); + if (input.tool_input.replace_all === true) { + content = content.split(input.tool_input.old_string) + .join(input.tool_input.new_string); + } else { + content = content.replace(input.tool_input.old_string, + input.tool_input.new_string); + } } const dir = path.dirname(input.tool_input.file_path); if (!fs.existsSync(dir)) { @@ -550,7 +556,8 @@ async function _runQuery(requestId, prompt, projectPath, model, signal, locale, const edit = { file: input.tool_input.file_path, oldText: input.tool_input.old_string, - newText: input.tool_input.new_string + newText: input.tool_input.new_string, + replaceAll: input.tool_input.replace_all === true }; editCount++; let editResult; From e03365fbcd0feba4a2dd61e27f5a66defaf5df79 Mon Sep 17 00:00:00 2001 From: abose Date: Sat, 25 Apr 2026 23:04:41 +0530 Subject: [PATCH 2/3] fix(ai-chat): preserve SDK session across cancel and error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cancelQuery, the abort catch in the query loop, and the error catch all nulled currentSessionId, so the next prompt started a fresh SDK session and Claude saw none of the prior turn — looking like amnesia after a Stop. Aborts only leave an interrupt marker in the session log, and most errors are transient (network, rate limit), so keep the session ID and let the next prompt resume; a truly broken session will surface its own fresh error. --- src-node/claude-code-agent.js | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src-node/claude-code-agent.js b/src-node/claude-code-agent.js index a6a3222add..82a11d3f9f 100644 --- a/src-node/claude-code-agent.js +++ b/src-node/claude-code-agent.js @@ -270,8 +270,8 @@ exports.cancelQuery = async function () { if (currentAbortController) { currentAbortController.abort(); currentAbortController = null; - // Clear session so next query starts fresh instead of resuming a killed session - currentSessionId = null; + // Keep currentSessionId so the next prompt resumes the same SDK session. + // Aborts leave an interrupt marker in the session log, not a corrupted state. // Clear any pending question or plan _questionResolve = null; _planResolve = null; @@ -1256,13 +1256,11 @@ async function _runQuery(requestId, prompt, projectPath, model, signal, locale, return; } _log("Cancelled"); - // Send sessionId so browser side can save partial history for later resume - const cancelledSessionId = currentSessionId; - // Clear session so next query starts fresh - currentSessionId = null; + // Keep currentSessionId so the next prompt can resume the same SDK + // session — the abort just leaves an interrupt marker in the log. nodeConnector.triggerPeer("aiComplete", { requestId: requestId, - sessionId: cancelledSessionId + sessionId: currentSessionId }); return; } @@ -1288,8 +1286,9 @@ async function _runQuery(requestId, prompt, projectPath, model, signal, locale, } } - // Clear session after error to prevent cascading failures from resuming a broken session - currentSessionId = null; + // Keep currentSessionId so the user can retry — errors are often + // transient (network, rate limit), and if the session really is broken + // the next attempt will surface a fresh error of its own. nodeConnector.triggerPeer("aiError", { requestId: requestId, @@ -1299,7 +1298,7 @@ async function _runQuery(requestId, prompt, projectPath, model, signal, locale, // Always send aiComplete after aiError so the UI exits streaming state nodeConnector.triggerPeer("aiComplete", { requestId: requestId, - sessionId: null + sessionId: currentSessionId }); } } From c818570577b0fbfa24eb37d0547deb4d851a7657 Mon Sep 17 00:00:00 2001 From: abose Date: Sat, 25 Apr 2026 23:14:13 +0530 Subject: [PATCH 3/3] build: update pro deps --- tracking-repos.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracking-repos.json b/tracking-repos.json index 44d0dd5a11..b883c1d8bc 100644 --- a/tracking-repos.json +++ b/tracking-repos.json @@ -1,5 +1,5 @@ { "phoenixPro": { - "commitID": "871b8fdaf238c0bedd9dc1e24cbc75ee67987453" + "commitID": "4749d2e7bbdbe93f5f4c270212b30b02061c0d7b" } }