agentkit-task-manager 0.5.1

Task manager abstractions and implementations for tool execution scheduling.
Documentation

agentkit-task-manager

Task scheduling abstractions for tool execution in agentkit.

A TaskManager decides whether each tool call runs inline (foreground), in the background, or starts foreground and detaches after a timeout. The agent loop talks to a single TaskManagerHandle and receives TaskEvents as tasks make progress, complete, or get cancelled.

What it provides

  • TaskManager trait + two implementations:
    • SimpleTaskManager -- executes every tool call inline, no spawning
    • AsyncTaskManager -- spawns tokio tasks and supports background work, cancellation, and detachment
  • TaskRoutingPolicy for deciding Foreground vs Background vs ForegroundThenDetachAfter(Duration) per request
  • TaskManagerHandle for cancellation, listing running/completed tasks, and draining results out-of-band
  • TaskEvent stream covering Started, Detached, Completed, Cancelled, Failed, and ContinueRequested

Quick start

SimpleTaskManager is the smallest useful implementation. It runs each tool call to completion before returning a TaskResolution:

use std::sync::Arc;

use agentkit_core::{MetadataMap, SessionId, ToolCallId, TurnId};
use agentkit_task_manager::{
    SimpleTaskManager, TaskLaunchRequest, TaskManager, TaskStartContext,
};
use agentkit_tools_core::{
    BasicToolExecutor, OwnedToolContext, PermissionChecker, PermissionDecision,
    PermissionRequest, ToolExecutor, ToolName, ToolRegistry, ToolRequest,
};
use serde_json::json;

struct AllowAll;
impl PermissionChecker for AllowAll {
    fn evaluate(&self, _: &dyn PermissionRequest) -> PermissionDecision {
        PermissionDecision::Allow
    }
}

# async fn run() -> Result<(), Box<dyn std::error::Error>> {
let manager = SimpleTaskManager::new();
let executor: Arc<dyn ToolExecutor> =
    Arc::new(BasicToolExecutor::from_registry(ToolRegistry::new()));

let request = ToolRequest {
    call_id: ToolCallId::new("call-1"),
    tool_name: ToolName::new("my_tool"),
    input: json!({}),
    session_id: SessionId::new("session-1"),
    turn_id: TurnId::new("turn-1"),
    metadata: MetadataMap::new(),
};

let ctx = TaskStartContext {
    executor,
    tool_context: OwnedToolContext {
        session_id: request.session_id.clone(),
        turn_id: request.turn_id.clone(),
        metadata: MetadataMap::new(),
        permissions: Arc::new(AllowAll),
        resources: Arc::new(()),
        cancellation: None,
    },
};

let _outcome = manager
    .start_task(TaskLaunchRequest::plain(None, request), ctx)
    .await?;
# Ok(())
# }

Routing tasks to the background

AsyncTaskManager accepts a TaskRoutingPolicy that decides per-request whether work runs in the foreground (blocking the turn) or in the background (letting the turn continue while the task runs):

use std::time::Duration;

use agentkit_task_manager::{AsyncTaskManager, RoutingDecision};
use agentkit_tools_core::ToolRequest;

let manager = AsyncTaskManager::new().routing(|request: &ToolRequest| {
    match request.tool_name.0.as_str() {
        "long_running_search" => RoutingDecision::Background,
        "interactive_step"     => RoutingDecision::ForegroundThenDetachAfter(
            Duration::from_secs(5),
        ),
        _                      => RoutingDecision::Foreground,
    }
});
# let _ = manager;

The handle returned by manager.handle() is what callers use to observe events (next_event), cancel by id (cancel), tweak per-task delivery (set_delivery_mode, set_continue_policy), and drain background results delivered out-of-band (drain_ready_items).