Skip to main content

cosh_tools/subagent/
types.rs

1//! Types for the `subagent.call` tool.
2
3use serde::{Deserialize, Serialize};
4
5/// Input for calling a sub-agent.
6///
7/// The same visible tool dispatches to TWO implementations: an external
8/// ACP agent harness when `agent` is provided, or the internal sub-agent
9/// (a nested harness) when `agent` is omitted or empty.
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct SubAgentCallInput {
12    /// The agent harness to call (e.g. "gemini").
13    /// Must be one of the supported agents listed in the tool description.
14    ///
15    /// Optional: if omitted (or empty), an internal agent runs the task
16    /// instead — a fresh nested harness with an empty context, in
17    /// auto-approve mode, that persists nothing and returns only its final
18    /// report.
19    pub agent: Option<String>,
20    /// The message to send to the sub-agent.
21    ///
22    /// Optional: if omitted (or empty), the last message sent to a sub-agent
23    /// in this session is reused automatically. If no sub-agent has been
24    /// called yet, an error is returned telling the caller to provide one.
25    pub input: Option<String>,
26    /// Whether this sub-agent task is a CODE REVIEW.
27    ///
28    /// When `true`, the harness appends a contract to the prompt: the
29    /// sub-agent's FINAL REPORT must start with an HTML comment header
30    /// declaring the review outcome — `<!-- severity: green -->`,
31    /// `<!-- severity: yellow -->` (minor issues / bad practice at most) or
32    /// `<!-- severity: red -->` (something critical was found). The header
33    /// is consumed by the client: it is never rendered, it only tints the
34    /// sub-agent box (green/yellow/red). Without the flag (or without a
35    /// header in the report) the box keeps its neutral per-agent color.
36    ///
37    /// Optional; defaults to `false`.
38    #[serde(default)]
39    pub code_review: bool,
40    /// Resume the agent's most recent session, keeping its context (default).
41    ///
42    /// `true` (or omitted): the call resumes the sub-agent's most recent
43    /// ACP session for this agent (tracked per agent name in the calling
44    /// session), keeping its conversation context — the token-efficient
45    /// default for review/iteration loops. `false`: start a brand-new
46    /// session with clean context, for tasks unrelated to the previous
47    /// one (the `--continue`/`-c` CLI convention, inverted to a positive
48    /// flag).
49    ///
50    /// Graceful degradation: a harness without session-resume support, or
51    /// one whose stored session no longer exists (e.g. it restarted and
52    /// lost state), silently falls back to a fresh `session/new`.
53    ///
54    /// Optional; defaults to `true`. Ignored when `agent` is omitted (the
55    /// internal agent never persists state across calls).
56    #[serde(default = "default_true")]
57    pub continue_session: bool,
58    /// Run the sub-agent in the BACKGROUND (default `false`).
59    ///
60    /// `true`: the harness spawns the sub-agent without blocking, returns a
61    /// `task_id` immediately, and the final report is delivered later — as
62    /// an automated completion notification in a subsequent turn. Query
63    /// progress at any time with the `subagent_status` tool (by `task_id`,
64    /// or omit its argument to list all background tasks).
65    ///
66    /// Omitted or `false`: the call blocks until the sub-agent finishes and
67    /// returns its report directly (the default synchronous behavior).
68    #[serde(default)]
69    pub run_in_background: bool,
70}
71
72/// Serde default for [`SubAgentCallInput::continue_session`]: omitted means
73/// RESUME (the token-efficient default), so the flag is opt-out only.
74fn default_true() -> bool {
75    true
76}
77
78/// Output from calling a sub-agent over ACP.
79#[derive(Debug, Clone, Serialize, Deserialize)]
80pub struct SubAgentCallOutput {
81    /// The sub-agent's report: its FINAL message — the text written after
82    /// its last tool call (see `subagent::closure::TurnClosure`), not the
83    /// concatenation of every message of the turn.
84    pub output: String,
85    /// Why the prompt turn ended: a snake_case ACP stop reason (e.g.
86    /// `end_turn`, `cancelled` — the spec-mandated answer to a
87    /// `session/cancel`), or the client-side terminal marker `error` when
88    /// the harness failed mid-turn. There is no timeout: the turn runs
89    /// until the agent ends it or the user stops it.
90    pub stop_reason: String,
91}