mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-28 11:32:22 +00:00
fix(state): exclude delegate/branch/tool children from resume walk + reconcile salvaged fixes
Follow-up to the salvage of #45035 + #48682. The two PRs touched different functions (resolve_resume_session_id vs get_compression_tip) but #45035's descendant walk followed ANY parent_session_id child, so a delegate/subagent child could hijack the resume target. Apply the same _branched_from / _delegate_from / source!='tool' exclusion the rest of hermes_state.py uses, so the resume walk only follows genuine compression continuations. Also updates the unrealistic delegation test fixture to carry the real _delegate_from marker, and updates 3 list_sessions_rich test mocks for the order_by_last_active kwarg #48682 added. AUTHOR_MAP: map PINKIIILQWQ + ailang323 salvage authors.
This commit is contained in:
parent
6d9ca04574
commit
6dfb8326f5
4 changed files with 24 additions and 5 deletions
|
|
@ -3198,11 +3198,19 @@ class SessionDB:
|
|||
if row is not None:
|
||||
best = current
|
||||
|
||||
# Walk to the most-recently-started child.
|
||||
# Walk to the most-recently-started child — but skip explicit
|
||||
# branch (`_branched_from`), delegate/subagent (`_delegate_from`),
|
||||
# and tool children. They also carry a ``parent_session_id`` yet
|
||||
# are NOT compression continuations; following them would hijack
|
||||
# the resume target to an unrelated session (e.g. a subagent
|
||||
# run). This mirrors the child-exclusion in ``get_compression_tip``.
|
||||
try:
|
||||
child_row = self._conn.execute(
|
||||
"SELECT id FROM sessions "
|
||||
"WHERE parent_session_id = ? "
|
||||
" AND json_extract(COALESCE(model_config, '{}'), '$._branched_from') IS NULL "
|
||||
" AND json_extract(COALESCE(model_config, '{}'), '$._delegate_from') IS NULL "
|
||||
" AND COALESCE(source, '') != 'tool' "
|
||||
"ORDER BY started_at DESC, id DESC LIMIT 1",
|
||||
(current,),
|
||||
).fetchone()
|
||||
|
|
|
|||
|
|
@ -81,6 +81,9 @@ AUTHOR_MAP = {
|
|||
"joaomarcosdias444@gmail.com": "JoaoMarcos44",
|
||||
"286497132+srojk34@users.noreply.github.com": "srojk34",
|
||||
"srojk34@users.noreply.github.com": "srojk34", # legacy prefix-less noreply (PR #50098 salvage; #38763)
|
||||
"pinkiilqwq@users.noreply.github.com": "PINKIIILQWQ", # PR #45035 salvage (resume-to-tip; #38763)
|
||||
"pink@PinkdeMacBook-Air.local": "PINKIIILQWQ", # PR #45035 local git identity (resume-to-tip; #38763)
|
||||
"ailang323@163.com": "ailang323", # PR #48682 salvage (compression-tip predicate; #38763)
|
||||
"59806492+sitkarev@users.noreply.github.com": "sitkarev",
|
||||
"zheng@omegasys.eu": "omegazheng",
|
||||
"220877172+james47kjv@users.noreply.github.com": "james47kjv",
|
||||
|
|
|
|||
|
|
@ -116,7 +116,15 @@ def test_compression_tip_not_confused_with_delegation_child(db):
|
|||
base = int(time.time()) - 10_000
|
||||
db.create_session("conv", source="cli")
|
||||
db.append_message("conv", role="user", content="parent turn")
|
||||
db.create_session("subagent", source="cli", parent_session_id="conv")
|
||||
# Real delegate/subagent sessions carry the `_delegate_from` marker
|
||||
# (set in delegate_tool.py) — that marker, not timing, is what
|
||||
# distinguishes them from a compression continuation.
|
||||
db.create_session(
|
||||
"subagent",
|
||||
source="subagent",
|
||||
parent_session_id="conv",
|
||||
model_config={"_delegate_from": "conv"},
|
||||
)
|
||||
db.append_message("subagent", role="assistant", content="delegated work")
|
||||
conn = db._conn
|
||||
assert conn is not None
|
||||
|
|
|
|||
|
|
@ -6100,7 +6100,7 @@ def test_session_most_recent_returns_first_non_denied(monkeypatch):
|
|||
"""Drops `tool` rows like session.list does, returns the first hit."""
|
||||
|
||||
class _DB:
|
||||
def list_sessions_rich(self, *, source=None, limit=200):
|
||||
def list_sessions_rich(self, *, source=None, limit=200, order_by_last_active=False):
|
||||
return [
|
||||
{"id": "tool-1", "source": "tool", "title": "noise", "started_at": 100},
|
||||
{"id": "tui-1", "source": "tui", "title": "real", "started_at": 99},
|
||||
|
|
@ -6119,7 +6119,7 @@ def test_session_most_recent_returns_first_non_denied(monkeypatch):
|
|||
|
||||
def test_session_most_recent_returns_null_when_only_tool_rows(monkeypatch):
|
||||
class _DB:
|
||||
def list_sessions_rich(self, *, source=None, limit=200):
|
||||
def list_sessions_rich(self, *, source=None, limit=200, order_by_last_active=False):
|
||||
return [{"id": "tool-1", "source": "tool", "started_at": 1}]
|
||||
|
||||
monkeypatch.setattr(server, "_get_db", lambda: _DB())
|
||||
|
|
@ -6137,7 +6137,7 @@ def test_session_most_recent_folds_db_exception_into_null_result(monkeypatch):
|
|||
'no answer' (Copilot review on #17130)."""
|
||||
|
||||
class _BrokenDB:
|
||||
def list_sessions_rich(self, *, source=None, limit=200):
|
||||
def list_sessions_rich(self, *, source=None, limit=200, order_by_last_active=False):
|
||||
raise RuntimeError("db locked")
|
||||
|
||||
monkeypatch.setattr(server, "_get_db", lambda: _BrokenDB())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue