Skip to content

fix(agent): persist canonical todo state across tab switches and restarts#4159

Open
JesonChou wants to merge 1 commit into
esengine:main-v2from
JesonChou:fix/canonical-todo-persist
Open

fix(agent): persist canonical todo state across tab switches and restarts#4159
JesonChou wants to merge 1 commit into
esengine:main-v2from
JesonChou:fix/canonical-todo-persist

Conversation

@JesonChou

Copy link
Copy Markdown
Contributor

Builds on #4132 · Closes #4105

Description

Relationship to #4132

#4132 fixed the host-advance ID collision (static "host-advance" → unique host-advance-{seq}-{idx}), ensuring multiple complete_step calls within a single turn correctly update the TodoPanel. This PR addresses the remaining half of #4105: the panel still reverted after tab switch or restart because the synthetic events were never persisted to the session file.

Problem

Host-advance synthetic todo_write events (emitTodoState) update the frontend TodoPanel in real time but are emitted to the event sink, not written as provider.Message to the session file. When the session is reloaded (restart, tab switch, reconnect) the frontend rebuilds its transcript from the session file and only sees the last explicit model todo_write, missing every intermediate host-advance.

Reproduction (after #4132):

1. Model todo_write: [A:in_progress, B:pending, C:pending]
2. complete_step A → panel shows 1/3 ✓
3. complete_step B → panel shows 2/3 ✓
4. Restart or switch tabs and back → panel reverts to 1/3 ✗

Fix

Two complementary mechanisms:

  1. HistoryWithCanonicalTodos — appends a synthetic assistant message carrying the canonical todo state at the end of the history response. Called from HistoryForTab (desktop) and GET /history (HTTP).

  2. EmitCanonicalTodoState — emits the canonical state as a live event through the sink, so the frontend receives it even when tab state is served from the in-memory cache without a full history reload.

Preserved (unchanged)

Files changed

File Lines Change
internal/agent/agent.go +23 Add CanonicalTodoState getter and EmitCanonicalTodoState method
internal/control/controller.go +17 Add HistoryWithCanonicalTodos and EmitCanonicalTodoState
desktop/app.go +3 / −1 Call emit + inject in HistoryForTab
internal/serve/serve.go +3 / −1 Call HistoryWithCanonicalTodos in GET /history

@github-actions github-actions Bot added v2 Go rewrite (1.x) — main-v2 branch, active development desktop Wails desktop app (desktop/**) agent Core agent loop (internal/agent, internal/control) labels Jun 12, 2026
…arts

Builds on esengine#4132 (unique host-advance IDs) by fixing the remaining half of
esengine#4105: host-advance events were emitted in real time but never persisted, so
the TodoPanel reverted on tab switch or restart.

Add HistoryWithCanonicalTodos that injects the canonical task list into the
history response, and EmitCanonicalTodoState that pushes it via the live
stream, covering both history-load and cached-tab-switch paths.

Closes esengine#4105.

Regression:
  internal/agent         PASS
  internal/agent/testutil PASS
  internal/control       PASS (1 pre-existing MCP env failure)
  internal/serve         PASS (1 pre-existing HTML lang failure)
  internal/evidence      PASS
  Full ./...             PASS (9 pre-existing env failures unchanged)
@JesonChou JesonChou force-pushed the fix/canonical-todo-persist branch from 320bad1 to 8a14c67 Compare June 12, 2026 08:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent Core agent loop (internal/agent, internal/control) desktop Wails desktop app (desktop/**) v2 Go rewrite (1.x) — main-v2 branch, active development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: 代办任务更新不同步

1 participant