Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ Community needs voiced in HN agent-UI discussions map directly to EMRG's design:
pkill -f "emrg.server"; rm -f ~/.emrg/emrgd.token; python -m emrg
```

Python: `uv run pytest tests/ -v` (1180) — import check: `uv run python -c "from emrg.client.app import run_client"`
Python: `uv run pytest tests/ -v` (1183) — import check: `uv run python -c "from emrg.client.app import run_client"`
GUI: `cd emrg/gui && npm test` (95: 45 daemon_client + 20 conn-manager + 8 integration + 6 build-config + 7 gui-state + 3 preload-api + 4 boot-contract + 2 theme-guard) — syntax: `node --check main.js preload.js daemon_client.js`
Renderer: `cd emrg/gui/renderer && npm run typecheck && npm test` (480: 5 snapshot-store + 9 utils + 3 ErrorBoundary + 2 App smoke + 11 commands + 4 copywriting + 11 i18n + 11 markdown + 17 transcript + 10 TranscriptView + 15 history + 22 composer + 34 Composer + 6 LinkDialog + 12 sidebar + 17 Sidebar + 9 fileTree + 9 FileTree + 16 resultPanel + 8 ResultPanel + 27 workspaceView + 10 WorkspaceView + 10 dialog + 6 Dialog + 9 ConfirmDialog + 9 RenameDialog + 10 dialogLists + 3 HelpDialog + 9 MemoryDialog + 6 SkillsDialog + 9 openSession + 6 WelcomeDialog + 8 OpenSessionDialog + 7 NewSessionDialog + 7 rewind + 8 RewindDialog + 7 GithubDeviceDialog + 15 daemonBridge + 7 DaemonBridgeProvider + 26 Shell + 15 DialogHost + 20 SettingsPanel + 6 TaskFormDialog + 5 RantDialog + 4 vendorMarkdown) + `npm run build` → `renderer/dist/`
CI: `uv run pytest` (ubuntu + **windows-2025 matrix** — Windows pytest 回归在 PR CI 即失败,v0.2.29 教训 #725) + GUI tests + **actionlint workflow lint** (`rhysd/actionlint@v1.7.12` gate, #444 — workflow 解析错误在 PR CI 即失败,如 `if:` secrets 上下文)
Expand Down
16 changes: 15 additions & 1 deletion emrg/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,12 @@ def append_message(self, record: dict) -> None:
with open(self._daily_history_path(), "a", encoding="utf-8") as f:
f.write(line)

self._message_count += 1
# message_count counts message-type records only — tool_result / summary
# records are persisted but do not inflate the user-facing message count
# (rant 2026-08-31T14:18:14: count previously grew for every record,
# including tool_results, and compact never decremented it).
if record.get("type", "message") == "message":
self._message_count += 1
self._updated_at = datetime.now().isoformat()
self._save_meta()

Expand Down Expand Up @@ -450,6 +455,15 @@ def compact(self, summary: str, keep_recent: int = 5) -> int:
new_history = [summary_record] + recent
self._write_history(new_history)

# Recompute message_count from the surviving records — compact replaces
# the compacted messages with one summary, so the count must shrink with
# them (rant 2026-08-31T14:18:14: previously the count was never
# decremented, inflating the TUI/GUI "msgs" display). Same semantics as
# the rewind handler (daemon.py): count message-type records only.
self._message_count = sum(
1 for r in new_history if r.get("type", "message") == "message"
)

self._compact_count += 1
self._last_compact_at = datetime.now().isoformat()
self._updated_at = self._last_compact_at
Expand Down
45 changes: 45 additions & 0 deletions tests/test_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,16 @@ def test_append_message_preserves_existing_timestamp(self, tmp_path):
records = session._read_history()
assert records[0]["timestamp"] == ts

def test_append_tool_result_does_not_increment_count(self, tmp_path):
"""tool_result records persist but do not inflate message_count."""
session = Session.create(tmp_path)
session.append_message({"type": "message", "role": "user", "content": "hi"})
session.append_message({"type": "tool_result", "tool_call_id": "c1", "content": "ok"})
session.append_message({"type": "message", "role": "assistant", "content": "done"})

assert session.message_count == 2 # user + assistant, tool_result excluded
assert len(session._read_history()) == 3 # all three persisted

def test_append_llm_writes_to_llm_file(self, tmp_path):
"""append_llm() writes records to llm.jsonl."""
session = Session.create(tmp_path)
Expand Down Expand Up @@ -484,6 +494,41 @@ def test_compact_updates_meta(self, tmp_path):
assert meta["compact_count"] == 1
assert meta["last_compact_at"] is not None

def test_compact_recomputes_message_count(self, tmp_path):
"""compact() decrements message_count to match surviving message records.

Regression for rant 2026-08-31T14:18:14 — the count previously only grew
(append_message incremented for every record incl. tool_results) and
compact never decremented it, inflating the TUI/GUI "msgs" display.
"""
session = Session.create(tmp_path)
for i in range(8):
session.append_message({"type": "message", "role": "user", "content": f"msg {i}"})
session.append_message({"type": "tool_result", "tool_call_id": f"c{i}", "content": "ok"})
# 8 user messages + 8 tool_results → count is message-only
assert session.message_count == 8

session.compact("summary of first 5", keep_recent=3)
records = session._read_history()
# new_history = [summary] + 3 recent records ([c6, msg7, c7] — keep_recent
# slices raw records, not message-only records)
assert records[0]["type"] == "summary"
surviving_messages = sum(1 for r in records if r.get("type", "message") == "message")
assert session.message_count == surviving_messages
assert session.message_count == 1 # only the last user message survives
assert len(records) == 4 # summary + 3 recent

def test_compact_count_persists_after_reload(self, tmp_path):
"""compact() recomputed message_count survives a reload from meta."""
session = Session.create(tmp_path)
for i in range(6):
session.append_message({"type": "message", "role": "user", "content": f"msg {i}"})
session.append_message({"type": "tool_result", "tool_call_id": f"c{i}", "content": "ok"})

session.compact("summary", keep_recent=2)
loaded = Session.load(session.session_id, tmp_path)
assert loaded.message_count == 1 # only the last user message survives

def test_compact_keeps_exact_keep_recent(self, tmp_path):
"""compact() with keep_recent=1 replaces all but the last message."""
session = Session.create(tmp_path)
Expand Down
Loading