harness: flip into needs_login on 401 mid-turn (closes #419)

This commit is contained in:
damocles 2026-05-25 21:32:01 +02:00 committed by Mara
commit 799804e3d1
6 changed files with 164 additions and 22 deletions

View file

@ -117,8 +117,14 @@ pub async fn build_all(coord: &Coordinator) -> Vec<ContainerView> {
};
let deployed_full = locked.get(&format!("agent-{logical}")).map(std::string::String::as_str);
let needs_update = crate::auto_update::agent_config_pending(&logical, deployed_full);
let needs_login =
!is_manager && !claude_has_session(&Coordinator::agent_claude_dir(&logical));
// needs_login fires when EITHER the claude session dir is
// missing (boot-time / fresh container) OR the harness wrote
// the auth-failed sentinel because a turn hit 401 (#419). The
// manager has its own session lifecycle and never participates
// in needs_login.
let needs_login = !is_manager
&& (!claude_has_session(&Coordinator::agent_claude_dir(&logical))
|| auth_failed_sentinel(&logical));
let deployed_sha = deployed_full.map(|s| s[..s.len().min(12)].to_owned());
// Recipient name the broker uses for this agent — sub-agents
// are addressed by logical name, the manager by the
@ -199,6 +205,17 @@ fn is_rate_limited(name: &str) -> bool {
.exists()
}
/// True when the harness wrote `{state_dir}/hyperhive-needs-login`
/// after a 401 mid-turn. Lets the dashboard surface `needs_login` for
/// agents whose `/root/.claude/` dir still exists (so
/// `claude_has_session` returns true) but whose OAuth credentials
/// inside it have actually expired (#419).
fn auth_failed_sentinel(name: &str) -> bool {
Coordinator::agent_notes_dir(name)
.join("hyperhive-needs-login")
.exists()
}
/// Read the agent's free-text status and the Unix timestamp when it was last set
/// (derived from the file's mtime). Returns `(None, None)` when the file is absent
/// or empty. `pub` so `agent_server` and `manager_server` can populate `AgentMeta`.