hive-runtime: respawn the ACP agent when its provider key changed after a failed turn
The provider key was resolved once, at spawn. A provider rejecting it shows only as an empty end_turn or an idle timeout, neither of which drops the agent, so a key rotated in the store never reached a running opencode until the process died for another reason. After a turn fails with EmptyEndTurn or IdleTimeout, the runtime now resolves the key once more. When that yields a key that differs from the one the agent was spawned with, the agent is dropped (SIGINT to its process group, as every other respawn) and the next turn spawns it with the new key and reloads the recorded session. No key read (inherited env value, no store, a failed or timed-out read, nothing stored) keeps the running agent. The failed turn's error is returned unchanged. Part of #5017 (slice A).
This commit is contained in:
parent
16e7a41bbd
commit
fa56931a99
3 changed files with 157 additions and 2 deletions
|
|
@ -4,8 +4,18 @@
|
|||
//!
|
||||
//! A value the process already inherited (`backendEnvironmentFile`) wins and
|
||||
//! the store is not asked. Without either, the agent is spawned without it.
|
||||
//!
|
||||
//! The key is read when the agent is spawned, and again after a turn fails
|
||||
//! with [`AcpError::EmptyEndTurn`](super::AcpError::EmptyEndTurn) or
|
||||
//! [`AcpError::IdleTimeout`](super::AcpError::IdleTimeout), which is how a
|
||||
//! provider rejecting the key shows. A stored key that differs from the one
|
||||
//! the agent was spawned with gets the agent respawned with it. Anything else
|
||||
//! (the same key, an inherited value, no store, a failed or empty read) keeps
|
||||
//! the running agent.
|
||||
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use swarm_secret_client::{
|
||||
|
|
@ -31,6 +41,15 @@ pub(super) struct KeyVar {
|
|||
pub(super) value: String,
|
||||
}
|
||||
|
||||
/// Resolves the variable it is given the name of, as [`resolve`] does.
|
||||
pub(super) type Resolve =
|
||||
Arc<dyn Fn(String) -> Pin<Box<dyn Future<Output = Option<KeyVar>> + Send>> + Send + Sync>;
|
||||
|
||||
/// A [`Resolve`] calling [`resolve`].
|
||||
pub(super) fn resolver() -> Resolve {
|
||||
Arc::new(|name| Box::pin(async move { resolve(&name).await }))
|
||||
}
|
||||
|
||||
/// Where [`resolve_with`] reads the key from. A trait so the precedence can
|
||||
/// be exercised without a store.
|
||||
trait KeySource {
|
||||
|
|
|
|||
Loading…
Reference in a new issue