turn: keep the reply a raced interjection used to erase
A finished text response arriving with a queued interjection was pushed nowhere: not streamed, not added to messages, and overwritten in final_content on the next round. The surface showed nothing and she answered the interjection blind to what she had just said. It now streams, enters history, and is carried into the single commit. compact: summary dropped the system anchor. Index 0 is excluded from to_summarize because it must not be compressed, then was left out of keep_indices too, so the primary lost persona and covenant on her first compaction. Every other strategy seeds vec![0]; culled_count already counted as though this one did.
This commit is contained in:
parent
dfc89d09ea
commit
eee26fcf57
2 changed files with 43 additions and 1 deletions
|
|
@ -159,8 +159,18 @@ impl CompactionStrategy for SummaryStrategy {
|
|||
)
|
||||
.await?;
|
||||
|
||||
// Index 0 is the system anchor — persona, covenant, human context. It
|
||||
// is deliberately excluded from `to_summarize` above because it must
|
||||
// not be compressed, and it was then dropped from `keep_indices` too,
|
||||
// so the primary lost who she was on her first compaction. Every other
|
||||
// strategy seeds this with `vec![0]`; `culled_count` below already
|
||||
// counted as though it did.
|
||||
let mut keep_indices: Vec<usize> = vec![0];
|
||||
keep_indices.extend(cutoff..messages.len());
|
||||
keep_indices.dedup();
|
||||
|
||||
Ok(CompactionPlan {
|
||||
keep_indices: (cutoff..messages.len()).collect(),
|
||||
keep_indices,
|
||||
summary_text: Some(summary),
|
||||
culled_count: cutoff.saturating_sub(1),
|
||||
token_savings: cutoff * 100,
|
||||
|
|
|
|||
|
|
@ -284,6 +284,10 @@ pub(crate) async fn run_turn(
|
|||
let mut messages = initial_messages;
|
||||
let mut tool_round = 0u32;
|
||||
let mut final_content: String = String::new();
|
||||
// Replies the agent finished before a raced interjection reopened the
|
||||
// turn. The turn commits one assistant message, so without carrying these
|
||||
// the session record keeps only the last round's text.
|
||||
let mut carried_replies: Vec<String> = Vec::new();
|
||||
let mut interrupted = false;
|
||||
// The subconscious's `halt` tool stopped the loop. Different shape from
|
||||
// an Esc-driven interrupt: no `*[raised hand]*` marker, no fake
|
||||
|
|
@ -486,6 +490,24 @@ pub(crate) async fn run_turn(
|
|||
.unwrap_or_default();
|
||||
|
||||
if !interjected.is_empty() {
|
||||
// The reply is finished; only the *turn* continues. It used to
|
||||
// be dropped on the floor here — never streamed, never added to
|
||||
// `messages`, and overwritten in `final_content` next round —
|
||||
// so the surface showed nothing and she answered the
|
||||
// interjection blind to what she had just said.
|
||||
if !response.content.is_empty() {
|
||||
let chars: Vec<char> = response.content.chars().collect();
|
||||
for chunk in chars.chunks(10) {
|
||||
let s: String = chunk.iter().collect();
|
||||
if tx.send(Ok(BackendEvent::Token(s.clone()))).await.is_err() {
|
||||
break;
|
||||
}
|
||||
dispatcher.emit_segment(&s);
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
|
||||
}
|
||||
messages.push(BifrostMessage::text("assistant", response.content.clone()));
|
||||
carried_replies.push(response.content.clone());
|
||||
}
|
||||
for text in &interjected {
|
||||
let stamp = chrono::Local::now().format("%H:%M");
|
||||
let note = format!("[interjected at {} — {}]", stamp, text.trim());
|
||||
|
|
@ -817,6 +839,16 @@ pub(crate) async fn run_turn(
|
|||
dispatcher.emit_primary_complete();
|
||||
}
|
||||
|
||||
if !carried_replies.is_empty() {
|
||||
carried_replies.push(std::mem::take(&mut final_content));
|
||||
final_content = carried_replies
|
||||
.iter()
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(String::as_str)
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n\n");
|
||||
}
|
||||
|
||||
// The subconscious halt is felt in the primary's body, not stamped in
|
||||
// session storage. We do not write a `*[subconscious HALT]*` marker —
|
||||
// that pollution was the resume-corruption bug. The session keeps
|
||||
|
|
|
|||
Loading…
Reference in a new issue