merge dev/friel/watchdog-interval-reset into collab stack

This commit is contained in:
Friel
2026-03-30 08:52:37 +00:00
4 changed files with 75 additions and 1 deletions
+11
View File
@@ -686,6 +686,7 @@ impl AgentControl {
)
.await;
if result.is_ok() {
self.watchdogs.note_owner_input(agent_id).await;
self.state
.update_last_task_message(agent_id, last_task_message);
}
@@ -1264,6 +1265,16 @@ impl AgentControl {
self.watchdogs.force_due_for_tests(target_thread_id).await;
}
#[cfg(test)]
pub(crate) async fn watchdog_owner_idle_since_is_none_for_tests(
&self,
target_thread_id: ThreadId,
) -> Option<bool> {
self.watchdogs
.owner_idle_since_is_none_for_tests(target_thread_id)
.await
}
#[cfg(test)]
pub(crate) async fn set_watchdog_active_helper_for_tests(
&self,
+42
View File
@@ -417,6 +417,48 @@ async fn send_input_submits_user_message() {
assert_eq!(captured, Some(expected));
}
#[tokio::test]
async fn send_input_resets_watchdog_owner_idle_state() {
let harness = AgentControlHarness::new().await;
let (owner_thread_id, _thread) = harness.start_thread().await;
let target_thread_id = ThreadId::new();
harness
.control
.register_watchdog(WatchdogRegistration {
owner_thread_id,
target_thread_id,
child_depth: 0,
interval_s: 30,
prompt: String::new(),
config: harness.config.clone(),
})
.await
.expect("watchdog registration should succeed");
assert_eq!(
harness
.control
.watchdog_owner_idle_since_is_none_for_tests(target_thread_id)
.await,
Some(false)
);
harness
.control
.send_input(owner_thread_id, text_input("ping"))
.await
.expect("send_input should succeed");
assert_eq!(
harness
.control
.watchdog_owner_idle_since_is_none_for_tests(target_thread_id)
.await,
Some(true)
);
}
#[test]
fn build_agent_inbox_items_emits_function_call_and_output() {
let sender_thread_id = ThreadId::new();
+21
View File
@@ -448,6 +448,16 @@ impl WatchdogManager {
true
}
pub(crate) async fn note_owner_input(&self, owner_thread_id: ThreadId) {
let mut registrations = self.registrations.lock().await;
for entry in registrations.values_mut() {
if entry.registration.owner_thread_id == owner_thread_id {
entry.owner_idle_since = None;
entry.owner_was_running = true;
}
}
}
#[cfg(test)]
pub(crate) async fn force_due_for_tests(&self, target_thread_id: ThreadId) {
let mut registrations = self.registrations.lock().await;
@@ -456,6 +466,17 @@ impl WatchdogManager {
}
}
#[cfg(test)]
pub(crate) async fn owner_idle_since_is_none_for_tests(
&self,
target_thread_id: ThreadId,
) -> Option<bool> {
let registrations = self.registrations.lock().await;
registrations
.get(&target_thread_id)
.map(|entry| entry.owner_idle_since.is_none())
}
async fn update_after_spawn(
&self,
target_thread_id: ThreadId,
+1 -1
View File
@@ -145,7 +145,7 @@ pub(crate) const PROJECT_DOC_MAX_BYTES: usize = 32 * 1024; // 32 KiB
pub(crate) const DEFAULT_AGENT_MAX_THREADS: Option<usize> = Some(6);
pub(crate) const DEFAULT_AGENT_MAX_DEPTH: i32 = 1;
pub(crate) const DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS: Option<u64> = None;
pub(crate) const DEFAULT_WATCHDOG_INTERVAL_S: i64 = 60;
pub(crate) const DEFAULT_WATCHDOG_INTERVAL_S: i64 = 10;
pub const CONFIG_TOML_FILE: &str = "config.toml";
const OPENAI_BASE_URL_ENV_VAR: &str = "OPENAI_BASE_URL";