Skip to main content

steer_tools/tools/
dispatch_agent.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3use thiserror::Error;
4
5use crate::ToolSpec;
6use crate::error::{ToolExecutionError, WorkspaceOpError};
7use crate::result::AgentResult;
8
9pub const DISPATCH_AGENT_TOOL_NAME: &str = "dispatch_agent";
10
11pub struct DispatchAgentToolSpec;
12
13impl ToolSpec for DispatchAgentToolSpec {
14    type Params = DispatchAgentParams;
15    type Result = AgentResult;
16    type Error = DispatchAgentError;
17
18    const NAME: &'static str = DISPATCH_AGENT_TOOL_NAME;
19    const DISPLAY_NAME: &'static str = "Dispatch Agent";
20
21    fn execution_error(error: Self::Error) -> ToolExecutionError {
22        ToolExecutionError::DispatchAgent(error)
23    }
24}
25
26#[derive(Debug, Deserialize, Serialize, JsonSchema, PartialEq)]
27#[serde(tag = "location", rename_all = "snake_case")]
28pub enum WorkspaceTarget {
29    /// Run the sub-agent in the caller's current workspace.
30    Current,
31    /// Create a fresh workspace (jj workspace or git worktree) and run there.
32    /// The resulting path may differ from the caller's current directory.
33    New { name: String },
34}
35
36#[derive(Debug, Deserialize, Serialize, JsonSchema, PartialEq)]
37#[serde(tag = "session", rename_all = "snake_case")]
38pub enum DispatchAgentTarget {
39    /// Start a new child session.
40    New {
41        workspace: WorkspaceTarget,
42        #[serde(default)]
43        agent: Option<String>,
44    },
45    /// Continue an existing child session by id.
46    Resume { session_id: String },
47}
48
49#[derive(Debug, Deserialize, Serialize, JsonSchema, PartialEq)]
50pub struct DispatchAgentParams {
51    /// Instructions for the sub-agent.
52    /// Include relevant context you already gathered (paths, findings,
53    /// constraints, and acceptance criteria) so the sub-agent does not need to
54    /// re-gather it.
55    /// Do not prepend synthetic path headers like `Repo: ...` or `CWD: ...`.
56    /// The sub-agent receives its working-directory context automatically.
57    pub prompt: String,
58    /// Session/workspace target for the sub-agent call.
59    pub target: DispatchAgentTarget,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Error)]
63#[serde(tag = "code", content = "details", rename_all = "snake_case")]
64pub enum DispatchAgentError {
65    #[error("{0}")]
66    Workspace(WorkspaceOpError),
67
68    #[error("workspace unavailable: {message}")]
69    WorkspaceUnavailable { message: String },
70
71    #[error("sub-agent failed: {message}")]
72    SpawnFailed { message: String },
73
74    #[error("failed to load session {session_id}: {message}")]
75    SessionLoadFailed { session_id: String, message: String },
76
77    #[error("session {session_id} is missing a SessionCreated event")]
78    MissingSessionCreatedEvent { session_id: String },
79
80    #[error("session {session_id} is not a child of current session {parent_session_id}")]
81    InvalidParentSession {
82        session_id: String,
83        parent_session_id: String,
84    },
85
86    #[error("failed to open workspace for session {session_id}: {message}")]
87    WorkspaceOpenFailed { session_id: String, message: String },
88}