hive-agent: guarantee a wake after a self-requested /compact
This commit is contained in:
parent
fa658567db
commit
2c7872841a
8 changed files with 227 additions and 48 deletions
|
|
@ -374,15 +374,18 @@ pub async fn drive_turn(
|
|||
archive_session(bus);
|
||||
return Err(TurnError::PromptTooLong);
|
||||
}
|
||||
// Operator `/compact` (`POST /api/compact`) deferred to the turn boundary:
|
||||
// run it now that the turn is done, so it works mid-turn rather than only
|
||||
// when the agent is idle. Only on a healthy turn — no point spawning a
|
||||
// compaction after a rate-limited / auth-failed / crashed one.
|
||||
// `is_ok()` first: `take_compact()` clears the flag, so it must only fire
|
||||
// when the compaction will actually run. On an unhealthy turn
|
||||
// (rate-limited / auth-failed / failed) the flag is left set for the next
|
||||
// turn or the idle `run_pending_compact` to service — not silently eaten.
|
||||
if outcome.is_ok() && bus.take_compact() {
|
||||
// Operator `/compact` (`POST /api/compact`) or an agent's own `compact`
|
||||
// MCP tool call, deferred to the turn boundary: run it now that the turn
|
||||
// is done, so it works mid-turn rather than only when the agent is idle.
|
||||
// Only on a healthy turn — no point spawning a compaction after a
|
||||
// rate-limited / auth-failed / crashed one. `is_ok()` first: `take_compact()`
|
||||
// clears the flag, so it must only fire when the compaction will actually
|
||||
// run. On an unhealthy turn (rate-limited / auth-failed / failed) the flag
|
||||
// is left set for the next turn or the idle `run_pending_compact` to
|
||||
// service — not silently eaten.
|
||||
if outcome.is_ok()
|
||||
&& let Some(request) = bus.take_compact()
|
||||
{
|
||||
bus.emit(LiveEvent::Note {
|
||||
text: "operator: /compact — running at turn end".into(),
|
||||
});
|
||||
|
|
@ -390,6 +393,14 @@ pub async fn drive_turn(
|
|||
// does; the serve loop resets to `Idle` once this turn returns.
|
||||
bus.set_state(crate::events::TurnState::Compacting);
|
||||
let _ = session.compact(&config, &sink).await;
|
||||
// If the compact call asked to be woken (the agent's own `compact`
|
||||
// tool with a `wake_prompt`), stash it — the serve loop reads it
|
||||
// back after this turn returns and drives a synthetic follow-up
|
||||
// turn, so a self-requested compact provably doesn't strand the
|
||||
// agent idle waiting for the next external event.
|
||||
if let Some(prompt) = request.wake_prompt {
|
||||
bus.set_post_compact_wake(prompt);
|
||||
}
|
||||
return Ok(true);
|
||||
}
|
||||
outcome
|
||||
|
|
@ -500,11 +511,13 @@ pub fn emit_turn_end(bus: &Bus, outcome: &TurnOutcome) {
|
|||
/// so a queued `/compact` runs even when no turn is driving. (The in-flight
|
||||
/// case is handled at the end of [`drive_turn`].) Resume-only via
|
||||
/// [`InfiniteSession::compact`]: a missing session is a harmless no-op. Returns
|
||||
/// `true` if a compaction ran.
|
||||
/// `true` if a compaction ran; the serve loop follows up with
|
||||
/// `Bus::take_post_compact_wake` to see whether a synthetic follow-up turn
|
||||
/// should run (set below when the compact request carried a `wake_prompt`).
|
||||
pub async fn run_pending_compact(files: &TurnFiles, bus: &Bus, session: &AgentSession) -> bool {
|
||||
if !bus.take_compact() {
|
||||
let Some(request) = bus.take_compact() else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
bus.emit(LiveEvent::Note {
|
||||
text: "operator: /compact — running on idle session".into(),
|
||||
});
|
||||
|
|
@ -520,6 +533,9 @@ pub async fn run_pending_compact(files: &TurnFiles, bus: &Bus, session: &AgentSe
|
|||
}),
|
||||
}
|
||||
bus.set_state(crate::events::TurnState::Idle);
|
||||
if let Some(prompt) = request.wake_prompt {
|
||||
bus.set_post_compact_wake(prompt);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue