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:
parent
6f000bd0f6
commit
350c447ebc
3 changed files with 57 additions and 2 deletions
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,10 +31,15 @@ pub(crate) struct InteractScreen {
|
|||
pub(crate) turn_started: Option<std::time::Instant>,
|
||||
pub(crate) call_started: Option<std::time::Instant>,
|
||||
pub(crate) call_timeout_secs: u64,
|
||||
// State sync with agent
|
||||
last_generation: u64,
|
||||
last_entry_count: usize,
|
||||
/// Reference to agent for state sync
|
||||
agent: std::sync::Arc<tokio::sync::Mutex<crate::agent::Agent>>,
|
||||
}
|
||||
|
||||
impl InteractScreen {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(agent: std::sync::Arc<tokio::sync::Mutex<crate::agent::Agent>>) -> Self {
|
||||
Self {
|
||||
autonomous: PaneState::new(true),
|
||||
conversation: PaneState::new(true),
|
||||
|
|
@ -48,9 +53,51 @@ impl InteractScreen {
|
|||
turn_started: None,
|
||||
call_started: None,
|
||||
call_timeout_secs: 60,
|
||||
last_generation: 0,
|
||||
last_entry_count: 0,
|
||||
agent,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sync conversation display from agent entries.
|
||||
fn sync_from_agent(&mut self) {
|
||||
let agent = self.agent.blocking_lock();
|
||||
let gen = agent.generation;
|
||||
let count = agent.entries().len();
|
||||
|
||||
if gen != self.last_generation {
|
||||
// Generation changed — full re-render
|
||||
self.conversation = PaneState::new(true);
|
||||
self.autonomous = PaneState::new(true);
|
||||
self.tools = PaneState::new(false);
|
||||
self.last_entry_count = 0;
|
||||
}
|
||||
|
||||
// Render new entries
|
||||
if count > self.last_entry_count {
|
||||
for entry in &agent.entries()[self.last_entry_count..] {
|
||||
let msg = entry.message();
|
||||
let text = msg.content_text();
|
||||
match msg.role {
|
||||
crate::agent::api::types::Role::User => {
|
||||
self.conversation.push_line_with_marker(
|
||||
text.to_string(), Color::Cyan, Marker::User,
|
||||
);
|
||||
}
|
||||
crate::agent::api::types::Role::Assistant => {
|
||||
self.conversation.push_line_with_marker(
|
||||
text.to_string(), Color::Reset, Marker::Assistant,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.last_generation = gen;
|
||||
self.last_entry_count = count;
|
||||
}
|
||||
|
||||
/// Process a UiMessage — update pane state.
|
||||
pub fn handle_ui_message(&mut self, msg: &UiMessage, app: &mut App) {
|
||||
match msg {
|
||||
|
|
@ -399,6 +446,9 @@ impl ScreenView for InteractScreen {
|
|||
}
|
||||
}
|
||||
|
||||
// Sync state from agent
|
||||
self.sync_from_agent();
|
||||
|
||||
// Draw
|
||||
self.draw_main(frame, area, app);
|
||||
None
|
||||
|
|
|
|||
|
|
@ -382,7 +382,7 @@ pub async fn run(
|
|||
let notify_rx = crate::thalamus::channels::subscribe_all();
|
||||
|
||||
// InteractScreen held separately for UiMessage routing
|
||||
let mut interact = crate::user::chat::InteractScreen::new();
|
||||
let mut interact = crate::user::chat::InteractScreen::new(mind.agent.clone());
|
||||
// Overlay screens: F2=conscious, F3=subconscious, F4=unconscious, F5=thalamus
|
||||
let mut screens: Vec<Box<dyn tui::ScreenView>> = vec![
|
||||
Box::new(crate::user::context::ConsciousScreen::new()),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue