Compare commits

...

3 Commits

Author SHA1 Message Date
argenis de la rosa
a56330d6f2 fix(tools): implement per-task cancellation for background delegate tasks
cancel_task previously only updated the JSON result file but did not
actually cancel the running tokio task. The CancellationToken was shared
across all background tasks, so there was no way to cancel an individual
task without cancelling all of them.

This fix adds a per-task token registry (task_tokens) that maps each
task_id to its dedicated CancellationToken. When cancel_task is called,
it cancels only the specific task's token, which the spawned task
monitors via tokio::select!. Tokens are cleaned up automatically when
tasks complete.
2026-03-21 20:00:43 -04:00
Giulio V
297b29a3a0 fix(config): fix pre-existing test compilation errors in schema.rs
- Remove #[cfg(unix)] gate on `use tempfile::TempDir` import since
  TempDir is used unconditionally in bootstrap file tests
- Add explicit type annotations on tokio::fs::* calls to resolve
  type inference failures (create_dir_all, write, read_to_string)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 18:10:54 +01:00
Giulio V
dae6989314 feat(tools): add background and parallel execution to delegate tool
Add three new execution modes to the delegate tool:

- background: when true, spawns the sub-agent in a background tokio task
  and returns a task_id immediately. Results are persisted to
  workspace/delegate_results/{task_id}.json.

- parallel: accepts an array of agent names, runs them all concurrently
  with the same prompt, and returns all results when complete.

- action parameter with check_result/list_results/cancel_task support
  for managing background task lifecycle.

Cascade control: background sub-agents use child CancellationToken
derived from the parent, enabling cancel_all_background_tasks() to
abort all running background agents when the parent session ends.

Existing synchronous delegation flow is fully preserved (opt-in only).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 15:20:13 +01:00
3 changed files with 1011 additions and 12 deletions

View File

@ -9275,7 +9275,6 @@ mod tests {
use std::os::unix::fs::PermissionsExt;
use std::path::PathBuf;
use std::sync::{Arc, Mutex as StdMutex};
#[cfg(unix)]
use tempfile::TempDir;
use tokio::sync::{Mutex, MutexGuard};
use tokio::test;
@ -13671,12 +13670,12 @@ require_otp_to_resume = true
async fn ensure_bootstrap_files_creates_missing_files() {
let tmp = TempDir::new().unwrap();
let ws = tmp.path().join("workspace");
tokio::fs::create_dir_all(&ws).await.unwrap();
let _: () = tokio::fs::create_dir_all(&ws).await.unwrap();
ensure_bootstrap_files(&ws).await.unwrap();
let soul = tokio::fs::read_to_string(ws.join("SOUL.md")).await.unwrap();
let identity = tokio::fs::read_to_string(ws.join("IDENTITY.md"))
let soul: String = tokio::fs::read_to_string(ws.join("SOUL.md")).await.unwrap();
let identity: String = tokio::fs::read_to_string(ws.join("IDENTITY.md"))
.await
.unwrap();
assert!(soul.contains("SOUL.md"));
@ -13687,21 +13686,21 @@ require_otp_to_resume = true
async fn ensure_bootstrap_files_does_not_overwrite_existing() {
let tmp = TempDir::new().unwrap();
let ws = tmp.path().join("workspace");
tokio::fs::create_dir_all(&ws).await.unwrap();
let _: () = tokio::fs::create_dir_all(&ws).await.unwrap();
let custom = "# My custom SOUL";
tokio::fs::write(ws.join("SOUL.md"), custom).await.unwrap();
let _: () = tokio::fs::write(ws.join("SOUL.md"), custom).await.unwrap();
ensure_bootstrap_files(&ws).await.unwrap();
let soul = tokio::fs::read_to_string(ws.join("SOUL.md")).await.unwrap();
let soul: String = tokio::fs::read_to_string(ws.join("SOUL.md")).await.unwrap();
assert_eq!(
soul, custom,
"ensure_bootstrap_files must not overwrite existing files"
);
// IDENTITY.md should still be created since it was missing
let identity = tokio::fs::read_to_string(ws.join("IDENTITY.md"))
let identity: String = tokio::fs::read_to_string(ws.join("IDENTITY.md"))
.await
.unwrap();
assert!(identity.contains("IDENTITY.md"));

File diff suppressed because it is too large Load Diff

View File

@ -106,6 +106,9 @@ pub use cron_runs::CronRunsTool;
pub use cron_update::CronUpdateTool;
pub use data_management::DataManagementTool;
pub use delegate::DelegateTool;
// Re-exported for downstream consumers of background delegation results.
#[allow(unused_imports)]
pub use delegate::{BackgroundDelegateResult, BackgroundTaskStatus};
pub use file_edit::FileEditTool;
pub use file_read::FileReadTool;
pub use file_write::FileWriteTool;