refactor(agent): build the InfiniteSession once in the serve loop, thread it
This commit is contained in:
parent
f6977a961a
commit
c9de2eeb97
2 changed files with 32 additions and 12 deletions
|
|
@ -503,6 +503,9 @@ async fn serve_loop<S: Surface>(
|
|||
) -> Result<()> {
|
||||
tracing::info!(socket = %socket.display(), "harness serve");
|
||||
S::requeue_inflight(socket).await;
|
||||
// The durable claude session, built once and reused for every turn +
|
||||
// idle compaction below (it's effectively stateless).
|
||||
let session = turn::make_session(&bus);
|
||||
// Set when a turn calls `request_next_turn` and no real work is
|
||||
// pending — the next iteration drives this synthetic message
|
||||
// in-process instead of long-polling the broker. Never
|
||||
|
|
@ -519,7 +522,7 @@ async fn serve_loop<S: Surface>(
|
|||
// (the in-flight case is handled at the end of drive_turn).
|
||||
let compacted = {
|
||||
let _guard = turn_lock.lock().await;
|
||||
turn::run_pending_compact(files, &bus).await
|
||||
turn::run_pending_compact(files, &bus, &session).await
|
||||
};
|
||||
if !compacted {
|
||||
tokio::time::sleep(interval).await;
|
||||
|
|
@ -545,6 +548,7 @@ async fn serve_loop<S: Surface>(
|
|||
stats.as_ref(),
|
||||
files,
|
||||
&turn_lock,
|
||||
&session,
|
||||
graceful_stop_message(),
|
||||
)
|
||||
.await;
|
||||
|
|
@ -553,7 +557,8 @@ async fn serve_loop<S: Surface>(
|
|||
}
|
||||
},
|
||||
};
|
||||
let ctrl = handle_turn::<S>(socket, &bus, stats.as_ref(), files, &turn_lock, next).await;
|
||||
let ctrl =
|
||||
handle_turn::<S>(socket, &bus, stats.as_ref(), files, &turn_lock, &session, next).await;
|
||||
if ctrl.auth_failed {
|
||||
*login_state.lock().unwrap() = LoginState::NeedsLogin;
|
||||
login::wait_for_login(
|
||||
|
|
@ -582,6 +587,7 @@ async fn handle_turn<S: Surface>(
|
|||
stats: Option<&TurnStats>,
|
||||
files: &turn::TurnFiles,
|
||||
turn_lock: &TurnLock,
|
||||
session: &turn::AgentSession,
|
||||
first: hive_sh4re::DeliveredMessage,
|
||||
) -> TurnControl {
|
||||
let from = first.from;
|
||||
|
|
@ -603,7 +609,7 @@ async fn handle_turn<S: Surface>(
|
|||
let prompt = serve_common::format_wake_prompt(msg_id, &from, &body, unread, redelivered);
|
||||
let outcome = {
|
||||
let _guard = turn_lock.lock().await;
|
||||
turn::drive_turn(&prompt, files, bus).await
|
||||
turn::drive_turn(&prompt, files, bus, session).await
|
||||
};
|
||||
turn::emit_turn_end(bus, &outcome);
|
||||
bus.set_state(TurnState::Idle);
|
||||
|
|
|
|||
|
|
@ -230,11 +230,20 @@ fn compact_percent() -> u8 {
|
|||
u8::try_from(pct.min(100)).unwrap_or(DEFAULT_COMPACT_PERCENT)
|
||||
}
|
||||
|
||||
/// Build the agent's durable session: constant title + on-disk store + a
|
||||
/// The agent's durable session type: the constant-title [`InfiniteSession`]
|
||||
/// with hyperhive's percent-of-window compaction policy. Built once by the
|
||||
/// serve loop (see [`make_session`]) and threaded through the turns, rather
|
||||
/// than rebuilt each time — it's effectively stateless, so one instance serves
|
||||
/// the whole run.
|
||||
pub type AgentSession = InfiniteSession<PercentPolicy>;
|
||||
|
||||
/// Construct the agent's durable session: constant title + on-disk store + a
|
||||
/// percent-of-window compaction policy that checkpoints (`CHECKPOINT_PROMPT`)
|
||||
/// before compacting. `default_window` feeds the policy the effective window
|
||||
/// for turns where the model didn't itself report one.
|
||||
fn infinite_session(bus: &Bus) -> InfiniteSession<PercentPolicy> {
|
||||
/// before compacting. Called once at serve-loop start. `percent` comes from a
|
||||
/// boot-time env var and `default_window` is only a fallback for turns where
|
||||
/// the model didn't report a window, so a single build at startup is fine.
|
||||
#[must_use]
|
||||
pub fn make_session(bus: &Bus) -> AgentSession {
|
||||
InfiniteSession::new(
|
||||
session_title(),
|
||||
session_store(),
|
||||
|
|
@ -260,8 +269,14 @@ fn infinite_session(bus: &Bus) -> InfiniteSession<PercentPolicy> {
|
|||
/// the whole turn is retried a single time before bubbling `AuthFailed` to
|
||||
/// the serve loop (which parks for re-login).
|
||||
///
|
||||
/// Called once per turn by the `hive` serve loop.
|
||||
pub async fn drive_turn(prompt: &str, files: &TurnFiles, bus: &Bus) -> TurnOutcome {
|
||||
/// Called once per turn by the `hive` serve loop, which owns the shared
|
||||
/// `session` ([`make_session`]) and threads it in.
|
||||
pub async fn drive_turn(
|
||||
prompt: &str,
|
||||
files: &TurnFiles,
|
||||
bus: &Bus,
|
||||
session: &AgentSession,
|
||||
) -> TurnOutcome {
|
||||
if bus.take_session_reset() {
|
||||
// Operator-requested (deferred from `POST /api/new-session`).
|
||||
bus.emit(LiveEvent::Note {
|
||||
|
|
@ -274,7 +289,6 @@ pub async fn drive_turn(prompt: &str, files: &TurnFiles, bus: &Bus) -> TurnOutco
|
|||
}
|
||||
let config = claude_config(bus, files);
|
||||
let sink = BusSink::new(bus);
|
||||
let session = infinite_session(bus);
|
||||
let mut result = session.run(&config, prompt, &sink).await;
|
||||
if matches!(result, Err(hive_claude::Error::AuthFailed)) {
|
||||
bus.emit(LiveEvent::Note {
|
||||
|
|
@ -398,7 +412,7 @@ pub fn emit_turn_end(bus: &Bus, outcome: &TurnOutcome) {
|
|||
/// 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.
|
||||
pub async fn run_pending_compact(files: &TurnFiles, bus: &Bus) -> bool {
|
||||
pub async fn run_pending_compact(files: &TurnFiles, bus: &Bus, session: &AgentSession) -> bool {
|
||||
if !bus.take_compact() {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -408,7 +422,7 @@ pub async fn run_pending_compact(files: &TurnFiles, bus: &Bus) -> bool {
|
|||
bus.set_state(crate::events::TurnState::Compacting);
|
||||
let config = claude_config(bus, files);
|
||||
let sink = BusSink::new(bus);
|
||||
match infinite_session(bus).compact(&config, &sink).await {
|
||||
match session.compact(&config, &sink).await {
|
||||
Ok(()) => bus.emit(LiveEvent::Note {
|
||||
text: "/compact done".into(),
|
||||
}),
|
||||
|
|
|
|||
Loading…
Reference in a new issue