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}
59
60/// Serde default for [`SubAgentCallInput::continue_session`]: omitted means
61/// RESUME (the token-efficient default), so the flag is opt-out only.
62fn default_true() -> bool {
63 true
64}
65
66/// Output from calling a sub-agent over ACP.
67#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct SubAgentCallOutput {
69 /// The sub-agent's report: its FINAL message — the text written after
70 /// its last tool call (see `subagent::closure::TurnClosure`), not the
71 /// concatenation of every message of the turn.
72 pub output: String,
73 /// Why the prompt turn ended: a snake_case ACP stop reason (e.g.
74 /// `end_turn`, `cancelled` — the spec-mandated answer to a
75 /// `session/cancel`), or the client-side terminal marker `error` when
76 /// the harness failed mid-turn. There is no timeout: the turn runs
77 /// until the agent ends it or the user stops it.
78 pub stop_reason: String,
79}