revert(gateway): keep profile truncation routing out of scope
This commit is contained in:
@@ -5291,73 +5291,6 @@ def test_prompt_submit_truncates_by_row_id(monkeypatch):
|
||||
server._sessions.pop("row-id-trunc-sid", None)
|
||||
|
||||
|
||||
def test_prompt_submit_row_id_truncates_profile_owned_history(monkeypatch, tmp_path):
|
||||
"""A remote-profile edit must write through the same DB used to resolve it."""
|
||||
from hermes_state import SessionDB
|
||||
|
||||
profile_home = tmp_path / "remote-profile"
|
||||
profile_home.mkdir()
|
||||
session_key = "profile-row-id-session"
|
||||
db = SessionDB(db_path=profile_home / "state.db")
|
||||
try:
|
||||
db.create_session(session_key, source="desktop")
|
||||
db.append_messages_batch(
|
||||
session_key,
|
||||
[
|
||||
{"role": "user", "content": "first"},
|
||||
{"role": "assistant", "content": "reply 1"},
|
||||
{"role": "user", "content": "second"},
|
||||
{"role": "assistant", "content": "reply 2"},
|
||||
],
|
||||
)
|
||||
history = db.get_messages_as_conversation(
|
||||
session_key, include_row_ids=True
|
||||
)
|
||||
target_row_id = history[2]["_row_id"]
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
sess = _session(history=list(history), session_key=session_key)
|
||||
sess["profile_home"] = str(profile_home)
|
||||
server._sessions["profile-row-id-sid"] = sess
|
||||
monkeypatch.setattr(
|
||||
server,
|
||||
"_get_db",
|
||||
lambda: pytest.fail("must not write through the launch-profile DB"),
|
||||
)
|
||||
monkeypatch.setattr(server, "_start_agent_build", lambda *a, **k: None)
|
||||
|
||||
try:
|
||||
resp = server.handle_request(
|
||||
{
|
||||
"id": "1",
|
||||
"method": "prompt.submit",
|
||||
"params": {
|
||||
"session_id": "profile-row-id-sid",
|
||||
"text": "edited second",
|
||||
"truncate_before_row_id": target_row_id,
|
||||
"confirm_truncate": True,
|
||||
},
|
||||
}
|
||||
)
|
||||
assert resp.get("error") is None
|
||||
assert [message["content"] for message in sess["history"]] == [
|
||||
"first",
|
||||
"reply 1",
|
||||
]
|
||||
verify_db = SessionDB(db_path=profile_home / "state.db")
|
||||
try:
|
||||
persisted = verify_db.get_messages_as_conversation(session_key)
|
||||
finally:
|
||||
verify_db.close()
|
||||
assert [message["content"] for message in persisted] == [
|
||||
"first",
|
||||
"reply 1",
|
||||
]
|
||||
finally:
|
||||
server._sessions.pop("profile-row-id-sid", None)
|
||||
|
||||
|
||||
def test_prompt_submit_truncates_by_string_row_id(monkeypatch):
|
||||
"""#82959: String row IDs in history match correctly against integer truncate_before_row_id."""
|
||||
replaced = []
|
||||
|
||||
@@ -575,49 +575,48 @@ def _(rid, params: dict) -> dict:
|
||||
# new exchange is appended on top of the "undone" turns — durable
|
||||
# zombie history on resume, and the edit/regenerate never sticks.
|
||||
# Fail closed: refuse the turn and leave memory/DB unchanged.
|
||||
with _session_db(session) as db:
|
||||
if db is not None:
|
||||
try:
|
||||
# active_only=True: replace only the live (active=1) rows.
|
||||
# In-place compaction (#38763) keeps the pre-compaction
|
||||
# transcript as active=0/compacted=1 rows under this same
|
||||
# session key; a bare replace_messages() would DELETE that
|
||||
# durable archive on every edit/regenerate — the same bug
|
||||
# class #80216 fixed for /retry. On an uncompacted session
|
||||
# all rows are active=1, so this is behaviorally identical
|
||||
# to the full replace.
|
||||
# archive_dropped: a rewind overwrites turns the user may
|
||||
# not have meant to drop, and this write is the last step
|
||||
# before they are gone — three reported incidents ended
|
||||
# here with nothing to restore from (#70516, #80763,
|
||||
# #82756). Soft-archiving keeps them on disk (active=0) and
|
||||
# in the FTS index, so a mis-aimed cut is recoverable
|
||||
# instead of terminal. The live transcript is unchanged.
|
||||
# Fall back to session id when session_key is NULL — CLI-origin
|
||||
# sessions created before the session_key default fix have no
|
||||
# key, and replace_messages(None) triggers an FK violation.
|
||||
truncation_key = session.get("session_key") or sid
|
||||
db.replace_messages(
|
||||
truncation_key,
|
||||
truncated,
|
||||
active_only=True,
|
||||
archive_dropped=True,
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.error(
|
||||
"prompt.submit: replace_messages failed for session %s "
|
||||
"(ordinal=%d); refusing turn so memory and DB stay "
|
||||
"aligned: %s",
|
||||
sid,
|
||||
ordinal,
|
||||
exc,
|
||||
exc_info=True,
|
||||
)
|
||||
return _err(
|
||||
rid,
|
||||
5008,
|
||||
f"failed to persist history truncation: {exc}",
|
||||
)
|
||||
if (db := _get_db()) is not None:
|
||||
try:
|
||||
# active_only=True: replace only the live (active=1) rows.
|
||||
# In-place compaction (#38763) keeps the pre-compaction
|
||||
# transcript as active=0/compacted=1 rows under this same
|
||||
# session key; a bare replace_messages() would DELETE that
|
||||
# durable archive on every edit/regenerate — the same bug
|
||||
# class #80216 fixed for /retry. On an uncompacted session
|
||||
# all rows are active=1, so this is behaviorally identical
|
||||
# to the full replace.
|
||||
# archive_dropped: a rewind overwrites turns the user may
|
||||
# not have meant to drop, and this write is the last step
|
||||
# before they are gone — three reported incidents ended
|
||||
# here with nothing to restore from (#70516, #80763,
|
||||
# #82756). Soft-archiving keeps them on disk (active=0) and
|
||||
# in the FTS index, so a mis-aimed cut is recoverable
|
||||
# instead of terminal. The live transcript is unchanged.
|
||||
# Fall back to session id when session_key is NULL — CLI-origin
|
||||
# sessions created before the session_key default fix have no
|
||||
# key, and replace_messages(None) triggers an FK violation.
|
||||
truncation_key = session.get("session_key") or sid
|
||||
db.replace_messages(
|
||||
truncation_key,
|
||||
truncated,
|
||||
active_only=True,
|
||||
archive_dropped=True,
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.error(
|
||||
"prompt.submit: replace_messages failed for session %s "
|
||||
"(ordinal=%d); refusing turn so memory and DB stay "
|
||||
"aligned: %s",
|
||||
sid,
|
||||
ordinal,
|
||||
exc,
|
||||
exc_info=True,
|
||||
)
|
||||
return _err(
|
||||
rid,
|
||||
5008,
|
||||
f"failed to persist history truncation: {exc}",
|
||||
)
|
||||
session["history"] = truncated
|
||||
session["history_version"] = int(session.get("history_version", 0)) + 1
|
||||
if db is not None:
|
||||
|
||||
Reference in New Issue
Block a user