pub struct Runner {
pub command: Vec<String>,
pub mcp: Option<McpWiring>,
}Expand description
How to invoke a particular headless agent CLI.
Fields§
§command: Vec<String>Command and arguments.
{prompt} substitutes the path to the agent’s composed instructions, which the Tower
writes into the run’s Hangar before spawning. It is not the instructions themselves.
That distinction is the whole design. A prompt is passed on stdin, never on the command line: Windows caps a command line at 32,767 characters, and real agent prompts go well past it — a sibling project’s review agent composes to roughly 98 KB, three times over, and its ordinary developer agent to 34 KB. Inlining the prompt would work in every test written against a small fixture and fail on the first agent worth running.
So most runners need no placeholder at all. It exists for CLIs that accept a file of instructions as a flag; those that do not get the instructions prepended to stdin.
mcp: Option<McpWiring>How this runner is told where Layover’s MCP server is.
Implementations§
Source§impl Runner
impl Runner
Sourcepub const PROMPT_PATH: &'static str = "{prompt}"
pub const PROMPT_PATH: &'static str = "{prompt}"
The placeholder substituted with the path to the composed instructions.
Sourcepub const MODEL: &'static str = "{model}"
pub const MODEL: &'static str = "{model}"
The placeholder substituted with the agent’s model.
Every supported CLI spells its model flag differently — --model, -m, a config key — so
the spelling stays in the runner command, which is already the one place that knows how to
invoke a given CLI. The alternative, a model_flag field, would put half of an invocation
in one place and half in another.
Sourcepub const MCP: &'static str = "{mcp}"
pub const MCP: &'static str = "{mcp}"
The placeholder substituted with this runner’s McpWiring::flag and the path to the
generated MCP configuration — two arguments, not one.
Optional. A command that does not contain it gets the pair appended at the end, which is
what claude and copilot want. It exists for commands that end in a positional argument
— codex exec … - reads the prompt from stdin and must stay last — where appending would
put a flag after the thing it has to precede.
Sourcepub fn takes_prompt_path(&self) -> bool
pub fn takes_prompt_path(&self) -> bool
Returns true when this runner wants the instructions as a file it is handed.
When false, the Tower prepends them to the stdin payload instead.
Sourcepub fn takes_model(&self) -> bool
pub fn takes_model(&self) -> bool
Returns true when this runner can carry an agent’s model.
An agent that declares a model whose runner cannot carry it is a silent no-op: the run happens, on whichever model the CLI defaults to, and nothing says the declaration was ignored. Validation warns about it rather than letting it pass.
Sourcepub fn invocation(
&self,
prompt_path: Option<&str>,
model: Option<&str>,
) -> Vec<String>
pub fn invocation( &self, prompt_path: Option<&str>, model: Option<&str>, ) -> Vec<String>
Builds the command line for one run.
Substitution is textual and deliberately so: a placeholder sits inside an argument like
--model={model} as readily as it stands alone, and the operator writes whichever their
CLI expects.
An argument that is only a {model} placeholder disappears when no model is set, rather
than becoming an empty argument — an empty string in argv is not nothing, and several
CLIs treat it as a positional.
Sourcepub fn invocation_with_mcp(
&self,
prompt_path: Option<&str>,
model: Option<&str>,
mcp_config: Option<&str>,
) -> Vec<String>
pub fn invocation_with_mcp( &self, prompt_path: Option<&str>, model: Option<&str>, mcp_config: Option<&str>, ) -> Vec<String>
The command to run, with every placeholder resolved.
mcp_config is the path to the file McpWiring::format describes. When the command
names Self::MCP the flag and path replace it there; otherwise they are appended, which
is the right answer for every CLI that does not end in a positional argument.