chat: state-driven sync from agent entries

InteractScreen holds agent ref, syncs conversation display from
agent.entries() on each tick via blocking_lock(). Tracks generation
counter and entry count to detect compactions and new entries.

Agent gets a generation counter, incremented on compaction and
non-last-entry mutations (age_out_images).

sync_from_agent() is the single path for pane updates. UiMessage
handle_ui_message still exists but will be removed once sync
handles all entry types (streaming, tool calls, DMN).

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
Kent Overstreet 2026-04-05 19:17:13 -04:00
parent 6f000bd0f6
commit 350c447ebc
3 changed files with 57 additions and 2 deletions

View file

@ -95,6 +95,8 @@ pub struct Agent {
pub prompt_file: String,
/// Stable session ID for memory-search dedup across turns.
pub session_id: String,
/// Incremented on compaction — UI uses this to detect resets.
pub generation: u64,
/// Agent orchestration state (surface-observe, journal, reflect).
/// TODO: move to Session — it's session-level, not agent-level.
pub agent_cycles: crate::subconscious::subconscious::AgentCycleState,
@ -153,6 +155,7 @@ impl Agent {
app_config,
prompt_file,
session_id,
generation: 0,
agent_cycles,
active_tools,
};
@ -914,6 +917,7 @@ impl Agent {
msg.content = Some(MessageContent::Text(replacement));
}
}
self.generation += 1;
}
/// Strip ephemeral tool calls from the conversation history.
@ -959,6 +963,7 @@ impl Agent {
dbglog!("[compact] budget: {}", budget.status_string());
self.load_startup_journal();
self.generation += 1;
self.last_prompt_tokens = 0;
self.publish_context_state();
}