remove hive and agent apis nothing calls: build-log routes, /api/compact, host.sock list, mcp.sock wake
This commit is contained in:
parent
cbfd7b2174
commit
36c7781245
5 changed files with 115 additions and 801 deletions
|
|
@ -251,8 +251,7 @@ pub enum TurnState {
|
|||
Idle,
|
||||
/// `claude --print` is running for a turn.
|
||||
Thinking,
|
||||
/// Operator-triggered `/compact` is running on the persistent
|
||||
/// session.
|
||||
/// A `/compact` is running on the persistent session.
|
||||
Compacting,
|
||||
}
|
||||
|
||||
|
|
@ -316,9 +315,8 @@ pub struct Bus {
|
|||
/// boundary keeps compaction from racing a live claude process mid-turn.
|
||||
/// `Some(request)` when a compact is pending; `request.wake_prompt` is
|
||||
/// what to drive as a synthetic follow-up turn once the compaction
|
||||
/// actually completes (`None` = pending but no follow-up wake wanted,
|
||||
/// e.g. the operator dashboard's `/compact` button). `None` = no compact
|
||||
/// pending. Wrapped in [`CompactRequest`] rather than
|
||||
/// actually completes (`None` = pending but no follow-up wake wanted). `None` = no
|
||||
/// compact pending. Wrapped in [`CompactRequest`] rather than
|
||||
/// `Option<Option<String>>` (clippy pedantic's `option_option` lint,
|
||||
/// and the named field reads clearer at call sites than a bare nested
|
||||
/// `Option`) so "pending" and "what to wake with" can never desync.
|
||||
|
|
@ -491,8 +489,7 @@ impl Bus {
|
|||
/// serviced just overwrites `wake_prompt` with the latest ask. `Some
|
||||
/// (wake_prompt)` schedules a synthetic follow-up turn (driven with
|
||||
/// `wake_prompt` as its body) once the compaction actually completes;
|
||||
/// `None` requests a plain compact with no follow-up wake (the operator
|
||||
/// dashboard's `/compact` button).
|
||||
/// `None` requests a plain compact with no follow-up wake.
|
||||
pub fn request_compact(&self, wake_prompt: Option<String>) {
|
||||
*self.compact_pending.lock().unwrap() = Some(CompactRequest { wake_prompt });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,8 @@
|
|||
//! reminder op then returns `Response::Err`); a reminder *firing* is a
|
||||
//! separate path (`reminder_timer`), not driven through this socket.
|
||||
//! `Request::Compact` is the odd one out — it doesn't touch any store, just
|
||||
//! the harness's [`Bus`] (gate-checked context usage, then the same
|
||||
//! deferred `compact_pending` flag the operator dashboard's `/compact`
|
||||
//! button sets). No hive-c0re round-trip, no broker long-poll, no marker
|
||||
//! the harness's [`Bus`] (gate-checked context usage, then the deferred
|
||||
//! `compact_pending` flag). No hive-c0re round-trip, no broker long-poll, no marker
|
||||
//! files.
|
||||
//!
|
||||
//! One request/response line per connection, matching the producers'
|
||||
|
|
@ -39,8 +38,7 @@ use crate::todos::{Todo, Todos};
|
|||
/// Context-usage floor for `Request::Compact`: below this fraction of the
|
||||
/// effective context window, an agent-initiated compact is refused (nothing
|
||||
/// meaningful to reclaim yet, and it'd just burn a compaction pass on a
|
||||
/// near-empty session). Same number a human operator would eyeball off the
|
||||
/// dashboard's `ctx·Nk` badge before hitting the `/compact` button.
|
||||
/// near-empty session).
|
||||
const COMPACT_MIN_USAGE_FRACTION: f64 = 0.66;
|
||||
|
||||
/// Pause after a failed `accept`, so a failure that persists (fd exhaustion)
|
||||
|
|
@ -434,13 +432,10 @@ fn mark_todos_done(store: &Todos, ids: &[i64]) -> Response {
|
|||
}
|
||||
}
|
||||
|
||||
/// `Request::Compact` handler: gate on context usage, then queue the same
|
||||
/// deferred `compact_pending` flag the operator's `/compact` button sets.
|
||||
/// Mirrors `hive-agent::web_ui::actions::post_compact` but reachable from
|
||||
/// the agent's own MCP tool instead of the dashboard, and refuses below
|
||||
/// [`COMPACT_MIN_USAGE_FRACTION`] instead of always honouring the request —
|
||||
/// an agent can call this speculatively, a human clicking the dashboard
|
||||
/// button already made the judgment call. `wake_prompt`, when set, is
|
||||
/// `Request::Compact` handler: gate on context usage, then queue the
|
||||
/// deferred `compact_pending` flag. Refuses below
|
||||
/// [`COMPACT_MIN_USAGE_FRACTION`] because an agent can call this
|
||||
/// speculatively. `wake_prompt`, when set, is
|
||||
/// forwarded to `Bus::request_compact` so the turn loop drives a synthetic
|
||||
/// follow-up turn once the compaction actually finishes.
|
||||
fn compact(bus: &Bus, wake_prompt: Option<String>) -> Response {
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ and end the turn.";
|
|||
/// passed here — they live at `/etc/claude-code/managed-settings.json`
|
||||
/// and claude auto-discovers them.
|
||||
/// Materialised at harness startup; shared between the turn loop and the
|
||||
/// operator-driven `/compact` path so both invocations look identical to
|
||||
/// `/compact` path so both invocations look identical to
|
||||
/// claude (same MCP surface, same allowed tools, same role prompt — only the
|
||||
/// stdin payload differs). The MCP config is rewritten before each
|
||||
/// invocation ([`TurnFiles::refresh_mcp_config`]), since which servers it
|
||||
|
|
@ -161,7 +161,7 @@ pub async fn write_system_prompt(socket: &Path, label: &str) -> Result<PathBuf>
|
|||
/// One claude turn's outcome: `Ok(compacted)` on success, or a [`TurnError`]
|
||||
/// the serve loop must act on. The `compacted` bool is `true` when a
|
||||
/// compaction ran this turn (reactively on overflow, or proactively per the
|
||||
/// policy — or a successful operator `/compact` at turn end); it's recorded as
|
||||
/// policy — or a successful agent-requested `/compact` at turn end); it's recorded as
|
||||
/// `result_kind = "compacted"` in turn stats so the stats page can distinguish
|
||||
/// those turns. Both `Ok(true)` and `Ok(false)` are ack'd; the error cases
|
||||
/// each map to a distinct serve-loop action (see [`emit_turn_end`] and the
|
||||
|
|
@ -452,8 +452,7 @@ pub async fn drive_turn(
|
|||
archive_session(bus, session);
|
||||
return Err(TurnError::PromptTooLong);
|
||||
}
|
||||
// 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
|
||||
// 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()`
|
||||
|
|
@ -485,7 +484,7 @@ pub async fn drive_turn(
|
|||
}
|
||||
|
||||
/// Emit the `/compact done` / `/compact failed: …` note for a finished
|
||||
/// operator or agent `/compact` and return whether it succeeded. Shared by the
|
||||
/// agent-requested `/compact` and return whether it succeeded. Shared by the
|
||||
/// turn-end path in [`drive_turn`] and the idle path in [`run_pending_compact`].
|
||||
fn report_compact<E: std::fmt::Display>(bus: &Bus, result: Result<(), E>) -> bool {
|
||||
match result {
|
||||
|
|
@ -620,7 +619,7 @@ pub fn emit_turn_end(bus: &Bus, outcome: &TurnOutcome) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Service a pending operator `/compact` (`Bus::request_compact`) while the
|
||||
/// Service a pending `/compact` (`Bus::request_compact`) while the
|
||||
/// agent is idle — the serve loop calls this when a `recv` returns no message,
|
||||
/// 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
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//! Operator action POST handlers (cancel, compact, reset, todos mark-done).
|
||||
//! Operator action POST handlers (cancel, reset, todos mark-done).
|
||||
|
||||
use axum::{
|
||||
Form,
|
||||
|
|
@ -16,22 +16,6 @@ pub(super) async fn post_cancel_turn(State(state): State<AppState>) -> Response
|
|||
(axum::http::StatusCode::OK, "ok").into_response()
|
||||
}
|
||||
|
||||
/// Operator-initiated `/compact`. Deferred: sets the `compact_pending` flag
|
||||
/// that `turn::drive_turn` consumes at the end of the current/next turn, so it
|
||||
/// works while a turn is in flight (a mid-turn compaction would race the live
|
||||
/// claude process) rather than only when the agent is idle. Returns 200
|
||||
/// immediately; the compaction stream lands in the live panel when it runs.
|
||||
pub(super) async fn post_compact(State(state): State<AppState>) -> Response {
|
||||
// No wake prompt: the operator is watching the dashboard, not waiting on
|
||||
// an inbox message — `request_compact`'s wake-prompt arg exists for the
|
||||
// agent's own `compact` MCP tool (`todo_server.rs::compact`).
|
||||
state.bus.request_compact(None);
|
||||
state.bus.emit(crate::events::LiveEvent::Note {
|
||||
text: "operator: /compact queued — runs at the end of the current turn".into(),
|
||||
});
|
||||
(axum::http::StatusCode::OK, "ok").into_response()
|
||||
}
|
||||
|
||||
/// Request a session reset. The current session is archived (its backing
|
||||
/// `<uuid>.jsonl` renamed out of claude's resolution glob) at the next turn
|
||||
/// boundary, so the following turn's `--resume` misses and self-heals into a
|
||||
|
|
|
|||
|
|
@ -76,7 +76,6 @@ fn routes() -> Router<AppState> {
|
|||
.route("/events/stream", get(stream::events_stream))
|
||||
.route("/events/history", get(stream::events_history))
|
||||
.route("/api/cancel", post(actions::post_cancel_turn))
|
||||
.route("/api/compact", post(actions::post_compact))
|
||||
.route("/api/new-session", post(actions::post_new_session))
|
||||
.route("/api/logout", post(auth::post_logout))
|
||||
.route("/api/todos", get(stats::api_todos))
|
||||
|
|
@ -532,7 +531,7 @@ mod tests {
|
|||
/// either.
|
||||
#[test]
|
||||
fn neither_model_nor_effort_has_a_route() {
|
||||
let (model, effort, compact) = crate::paths::with_harness_dir(|| {
|
||||
let (model, effort, existing) = crate::paths::with_harness_dir(|| {
|
||||
let state = AppState {
|
||||
label: "test".to_owned(),
|
||||
login: Arc::new(Mutex::new(LoginState::Online)),
|
||||
|
|
@ -550,17 +549,17 @@ mod tests {
|
|||
let effort = app.clone().oneshot(form("/api/effort", "effort=high"));
|
||||
// A route the router does serve, so the 404s above are not
|
||||
// the router refusing everything.
|
||||
let compact = app.oneshot(form("/api/compact", ""));
|
||||
let existing = app.oneshot(form("/api/new-session", ""));
|
||||
(
|
||||
model.await.unwrap(),
|
||||
effort.await.unwrap(),
|
||||
compact.await.unwrap(),
|
||||
existing.await.unwrap(),
|
||||
)
|
||||
})
|
||||
});
|
||||
assert_eq!(model.status(), StatusCode::NOT_FOUND);
|
||||
assert_eq!(effort.status(), StatusCode::NOT_FOUND);
|
||||
assert_eq!(compact.status(), StatusCode::OK);
|
||||
assert_eq!(existing.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
/// Poll `path` until it holds a complete, newline-terminated pid.
|
||||
|
|
|
|||
Loading…
Reference in a new issue