Skip to content

fix(agent): preserve mid-turn steer messages across tab switches (#4044)#4074

Merged
esengine merged 2 commits into
esengine:main-v2from
JesonChou:fix/4044-steer-disappears-on-tab-switch
Jun 12, 2026
Merged

fix(agent): preserve mid-turn steer messages across tab switches (#4044)#4074
esengine merged 2 commits into
esengine:main-v2from
JesonChou:fix/4044-steer-disappears-on-tab-switch

Conversation

@JesonChou

@JesonChou JesonChou commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Closes #4044

Note: steer notices display correctly in Standard display mode.
In Compact/Minimal mode the frontend folds prior assistant turns into a "Processed" section when a new assistant turn begins — this is existing UI behaviour and not addressed here.


Problem

Mid-turn guidance injected via Steer disappeared from the conversation after switching to another tab and back. The steer was visible during the live turn (as a ↪ text notice) but gone after tab switch.

Root Cause

internal/control/input.go included the steer prefix in syntheticPrefixes. Desktop's historyMessages() called IsSyntheticUserMessage() on every user-role message during history replay and skipped synthetic ones. This contradicted the agent's stated intent (agent.go:507-510) that steer messages be persisted to "survive tab switches and history replay."

The CLI TUI and HTTP/SSE frontends had the same class of bug: they replayed steer messages as raw user bubbles with the full prefix visible.

Changes

Commit 1 — Core fix (desktop):

  1. internal/agent/agent.go — Export MidTurnSteerPrefix; add SteerText(content) (string, bool) to extract the original user text from a steer-wrapped message without trimming (preserving exact character fidelity with the live Steer event).

  2. internal/control/input.go — Remove steer prefix from syntheticPrefixes. Steer messages are no longer classified as synthetic.

  3. desktop/app.go — In historyMessages(), detect steer messages using raw m.Content (before resolveUserContent applies StripComposePrefixes which trims whitespace) and emit them as role: "notice" entries with the prefix — matching the live Steer event appearance.

  4. internal/agent/steer_test.go — New table-driven tests for SteerText (11 cases) + round-trip test for midTurnSteerMessage.

  5. internal/control/input_test.go — Updated test expectation.

Commit 2 — CLI TUI + HTTP/SSE parity:

  1. internal/cli/chat_tui.goreplaySectionsFor now detects steer messages via agent.SteerText() before rendering user bubbles. Steer messages are rendered as ↪ text notice lines instead of raw › [Mid-turn steer...] user bubbles.

  2. internal/serve/serve.gohistoryMessages now detects steer messages and emits them as role: "notice" with the prefix, consistent with the desktop behaviour. Web clients reconnecting see clean notice text instead of the raw prefix.

  3. internal/control/input_test.go — Hardcoded prefix string replaced with agent.MidTurnSteerPrefix constant reference so the test automatically tracks prefix changes.

Design

All three frontends (desktop, CLI TUI, HTTP/SSE) now share the same pattern: before processing a RoleUser message for display, call agent.SteerText() to detect steer messages and convert them to a notice-style rendering. The model still sees the raw user message (with the prefix) for context; only the display layer transforms it.

Verification

  • go test ./internal/agent/ — 80+ tests pass
  • go test ./internal/control/ — all pass (1 pre-existing failure in TestRemoveMCPServerRemovesUnconnectedLazyPlaceholder, confirmed on clean main-v2)
  • go build ./internal/cli/ ./internal/serve/ — clean
  • go vet ./desktop/... + go build ./desktop/... — clean

Affected areas

Path Before After
Desktop tab switch → back Steer disappeared ↪ text notice
Desktop tab close → reopen Steer disappeared ↪ text notice
Desktop history panel preview Steer disappeared ↪ text notice
CLI TUI session resume Raw prefix user bubble ↪ text notice line
HTTP/SSE /history endpoint role:"user" with raw prefix role:"notice" with ↪ text

@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) and removed v2 Go rewrite (1.x) — main-v2 branch, active development labels Jun 11, 2026
@JesonChou JesonChou force-pushed the fix/4044-steer-disappears-on-tab-switch branch from 61e18bd to d6c35eb Compare June 12, 2026 00:14
@github-actions github-actions Bot added the tui Terminal UI / CLI (internal/cli, internal/control) label Jun 12, 2026
@esengine

Copy link
Copy Markdown
Owner

Excellent root-cause writeup. The fix is exactly right: syntheticPrefixes has a single consumer (desktop's historyMessages), so dropping the steer prefix from it only affects history replay — nothing in compaction or prompt construction — and you re-handle it explicitly in all three frontends. Detecting on the raw m.Content before StripComposePrefixes trims is the subtle bit you got right, and SteerText round-trips midTurnSteerMessage character-for-character. Bonus points for fixing the CLI/SSE replay (raw-prefix bubbles) in the same pass. Merging — thanks!

@esengine esengine merged commit 7251271 into esengine:main-v2 Jun 12, 2026
14 checks passed
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/**) tui Terminal UI / CLI (internal/cli, internal/control) v2 Go rewrite (1.x) — main-v2 branch, active development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: 任务中途注入的引导提示在切换对话后就消失了

2 participants