From 5795b3be4e2aa5a840ce810925e3e88e3370f4f0 Mon Sep 17 00:00:00 2001 From: Junass1 Date: Tue, 5 May 2026 08:34:27 -0700 Subject: [PATCH] fix(acp): use SessionDB.replace_messages for atomic history rewrite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ACP's save_session() did a non-atomic clear_messages() + append_message() loop. If any message hit an exception mid-loop (bad tool_call shape, etc.), the DELETE had already committed and the persisted conversation was lost. SessionDB.replace_messages() wraps DELETE + bulk INSERT in a single BEGIN IMMEDIATE transaction that rolls back on any exception, so a bad message can no longer clobber previously-persisted history. Salvages @Awsh1's PR #13675 — uses the existing replace_messages() helper (which covers more message fields than the PR's own copy) instead of adding a duplicate. --- acp_adapter/session.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/acp_adapter/session.py b/acp_adapter/session.py index d6dace66b4..61d06432a7 100644 --- a/acp_adapter/session.py +++ b/acp_adapter/session.py @@ -466,17 +466,10 @@ class SessionManager: except Exception: logger.debug("Failed to update ACP session metadata", exc_info=True) - # Replace stored messages with current history. - db.clear_messages(state.session_id) - for msg in state.history: - db.append_message( - session_id=state.session_id, - role=msg.get("role", "user"), - content=msg.get("content"), - tool_name=msg.get("tool_name") or msg.get("name"), - tool_calls=msg.get("tool_calls"), - tool_call_id=msg.get("tool_call_id"), - ) + # Replace stored messages with current history atomically so a + # mid-rewrite failure rolls back and the previously persisted + # conversation is preserved (salvaged from #13675). + db.replace_messages(state.session_id, state.history) except Exception: logger.warning("Failed to persist ACP session %s", state.session_id, exc_info=True)