Agents - Dark-Alex-17/coyote GitHub Wiki
Agents in Coyote follow the same style as OpenAI's GPTs. They consist of 3 parts:
- Role - Tell the LLM how to behave
- RAG - Pre-built knowledge bases specifically for the agent
- Function Calling (#2) - Extends the functionality of the LLM through custom functions it can call
Looking for declarative, multi-step workflows? See Graph Agents: a YAML-driven workflow engine where each step (LLM call, script, user prompt, child-agent spawn) is its own typed node. Useful when an agent's behavior follows a fixed shape rather than a single open-ended LLM loop.

Agent configuration files are stored in the agents subdirectory of your Coyote configuration directory. The location of
this directory varies between systems so you can use the following command to locate yours:
coyote --info | grep 'agents_dir' | awk '{print $2}'If you're looking for more example agents, refer to the built-in agents.
Running an agent inside a Docker Sandbox? If the agent needs external binaries (
pandoc,usql, etc.) or specific network domains, ship a per-agentsbx-mixin.yamlalongside itsconfig.yaml. See Sandbox Support at the bottom of this page.
Agent configurations often have the following directory structure:
<coyote-config-dir>/agents
└── my-agent
├── config.yaml
├── tools.sh
or
├── tools.py
or
├── tools.ts
This means that agent configurations often are only two files: the agent configuration file (config.yaml), and the
tool definitions (agents/my-agent/tools.sh, tools.py, or tools.ts).
To see a full example configuration file, refer to the example agent config file.
The best way to understand how an agent is built is to go step by step in the following manner:
Agent configurations have the following settings available to customize each agent:
# Model Configuration
model: openai:gpt-4o # Specify the LLM to use
temperature: null # Set default temperature parameter, range (0, 1)
top_p: null # Set default top-p parameter, with a range of (0, 1) or (0, 2), depending on the model
reasoning_effort: null # Set the reasoning effort level for models that support it (e.g. low, medium, high)
# Agent Metadata Configuration
agent_session: null # Set a session to use when starting the agent. (e.g. temp, default); defaults to globally set agent_session
compression_model: null # Model to use when compressing this agent's session (overrides the global compression_model); falls back to the current model with a printed warning if it fails
# Agent Configuration
name: <agent-name> # Name of the agent, used in the UI and logs
description: <description> # Description of the agent, used in the UI
version: 1 # Version of the agent
# Function Calling Configuration
mcp_servers: # Optional list of MCP servers that the agent utilizes
- github # Corresponds to the name of an MCP server in the `<coyote-config-dir>/mcp.json` file
mcp_tools: # Optional per-server tool whitelists for the agent's MCP servers; a map of
github: # server name -> tool-name patterns (`*`/`?` globs, YAML list values only).
- get_* # See the MCP Servers wiki page, "Restricting MCP Tools (Tool Whitelists)"
- list_*
global_tools: # Optional list of additional global tools to enable for the agent; i.e. not tools specific to the agent
- web_search
- fs
- python
# Todo System & Auto-Continuation (see "Todo System & Auto-Continuation" section below)
auto_continue: false # Enable automatic continuation when incomplete todos remain
max_auto_continues: 10 # Maximum continuation attempts before stopping
inject_todo_instructions: true # Inject todo tool instructions into system prompt
continuation_prompt: null # Custom prompt for continuations (optional)
# Sub-Agent Spawning (see "Sub-Agent Spawning System" section below)
can_spawn_agents: false # Enable spawning child agents
max_concurrent_agents: 4 # Max simultaneous child agents
max_agent_depth: 3 # Max nesting depth (prevents runaway)
inject_spawn_instructions: true # Inject spawning instructions into system prompt
summarization_model: null # Model for summarizing sub-agent output (e.g. 'openai:gpt-4o-mini')
summarization_threshold: 4000 # Char count above which sub-agent output is summarized
escalation_timeout: 0 # 0 = wait indefinitely for escalated user input (default); N = give up after N secondsAs mentioned previously: Agents utilize function calling to extend a model's capabilities. However, agents operate in isolated environment, so in order for an agent to use a tool or MCP server that you have defined globally, you must explicitly state which tools and/or MCP servers the agent uses. Otherwise, it is assumed that the agent doesn't use any tools outside its own custom defined tools.
And if you don't define a agents/my-agent/tools.sh, agents/my-agent/tools.py, or agents/my-agent/tools.ts, then the agent is really just a
role.
You'll notice there are no settings for agent-specific tooling. This is because they are handled separately and automatically. See the Building Tools for Agents section below for more information.
To see a full example configuration file, refer to the example agent config file.
At their heart, agents function similarly to roles in that they tell the model how to behave. Agent configuration files have the following settings for the instruction definitions:
dynamic_instructions: # Whether to use dynamically generated instructions for the agent; if false, static instructions are used. False by default.
instructions: # Static instructions for the LLM; These are ignored if dynamic instructions are used
variables: # An array of optional variables that the agent expects and usesBy default, Coyote agents use statically defined instructions. Think of them as being identical to the instructions for a role, because they virtually are.
Example:
instructions: |
You are an AI agent designed to demonstrate agentic capabilitiesJust like roles, agents support variable interpolation at runtime. There's two types of variables that can be
interpolated into the instructions at runtime: special variables (like roles have), and user-defined variables. Just
like roles, variables are interpolated into your instructions anywhere Coyote sees the {{variable}} syntax.
The following special variables are provided by Coyote at runtime and can be injected into your agent's instructions:
| Name | Description | Example |
|---|---|---|
__os__ |
Operating system name | linux |
__os_family__ |
Operating system family | unix |
__arch__ |
System architecture | x86_64 |
__shell__ |
The current user's default shell | bash |
__locale__ |
The current user's preferred language and region settings | en-US |
__now__ |
Current timestamp in ISO 8601 format | 2025-11-07T10:15:44.268Z |
__cwd__ |
The current working directory | /tmp |
__tools__ |
A list of the enabled tools (global + mcp servers + agent-specific) |
Agents also support user-defined variables that can be interpolated into the instructions, and are made available to any agent-specific tools you define (see Building Tools for Agents for more details on how to create agent-specific tooling).
The variables setting in an agent's config has the following fields:
| Field | Required | Description |
|---|---|---|
name |
* | The name of the variable |
description |
* | The description of the field |
default |
A default value for the field. If left undefined, the user will be prompted for a value at runtime |
Variable values can be supplied three ways (highest precedence first):
-
At spawn time: A parent agent passes them via
agent__spawn's /agent__task_create'svariablesparameter (see Passing Agent Variables at Spawn). Spawned child agents also inherit the parent's CLI-set variables by name as a fallback. -
On the CLI:
coyote -a my-agent --agent-variable <name> <value> "...". -
Declared
default: Used when nothing else supplies a value. A required variable (no default) with no supplied value triggers an interactive prompt at startup, except for spawned child agents, which never prompt (see below).
These variables can be referenced in both the agent's instructions, and in the tool definitions via LLM_AGENT_VAR_<name>.
Example:
instructions: |
You are an agent who answers questions about a user's system.
<tools>
{{__tools__}}
</tools>
<system>
os: {{__os__}}
os_family: {{__os_family__}}
arch: {{__arch__}}
shell: {{__shell__}}
locale: {{__locale__}}
now: {{__now__}}
cwd: {{__cwd__}}
</system>
<user>
username: {{username}}
</user>
variables:
- name: username # Accessible from the tool definitions via the `LLM_AGENT_VAR_USERNAME` environment variable
description: Your user nameSometimes you may find it useful to dynamically generate instructions on startup. Whether that be via a call to Coyote
itself to generate them, or by some other means. Coyote supports this type of behavior using a special function defined
in your agents/my-agent/tools.py, agents/my-agent/tools.sh, or agents/my-agent/tools.ts.
Example: Instructions for a JSON-reader agent that specializes on each JSON input it receives
agents/json-reader/tools.py:
import json
from pathlib import Path
from genson import SchemaBuilder
def _instructions():
"""Generates instructions for the agent dynamically"""
value = input("Enter a JSON file path OR paste raw JSON: ").strip()
if not value:
raise SystemExit("A file path or JSON string is required.")
p = Path(value)
if p.exists() and p.is_file():
json_file_path = str(p.resolve())
json_text = p.read_text(encoding="utf-8")
else:
try:
json.loads(value)
except json.JSONDecodeError as e:
raise SystemExit(f"Input is neither a file nor valid JSON.\n{e}")
json_file_path = "<provided-inline-json>"
json_text = value
try:
data = json.loads(json_text)
except json.JSONDecodeError as e:
raise SystemExit(f"Provided content is not valid JSON.\n{e}")
builder = SchemaBuilder()
builder.add_object(data)
json_schema = builder.to_schema()
return f"""
You are an AI agent that can view and filter JSON data with jq.
## Context
json_file_path: {json_file_path}
json_schema: {json.dumps(json_schema, indent=2)}
"""or
agents/json-reader/tools.sh:
#!/usr/bin/env bash
set -e
# @meta require-tools jq,genson
# @env LLM_OUTPUT=/dev/stdout The output path
# @cmd Generates instructions for the agent dynamically
_instructions() {
read -r -p "Enter a JSON file path OR paste raw JSON: " value
if [[ -z "${value}" ]]; then
echo "A file path or JSON string is required" >&2
exit 1
fi
json_file_path=""
inline_temp=""
cleanup() {
[[ -n "${inline_temp:-}" && -f "${inline_temp}" ]] && rm -f "${inline_temp}"
}
trap cleanup EXIT
if [[ -f "${value}" ]]; then
json_file_path="$(realpath "${value}")"
if ! jq empty "${json_file_path}" >/dev/null 2>&1; then
echo "Error: File does not contain valid JSON: ${json_file_path}" >&2
exit 1
fi
else
inline_temp="$(mktemp)"
printf "%s" "${value}" > "${inline_temp}"
if ! jq empty "${inline_temp}" >/dev/null 2>&1; then
echo "Error: Input is neither a file nor valid JSON." >&2
exit 1
fi
json_file_path="<provided-inline-json>"
fi
source_file="${json_file_path}"
if [[ "${json_file_path}" == "<provided-inline-json>" ]]; then
source_file="${inline_temp}"
fi
json_schema="$(genson < "${source_file}" | jq -c '.')"
cat <<EOF >> "$LLM_OUTPUT"
You are an AI agent that can view and filter JSON data with jq.
## Context
json_file_path: ${json_file_path}
json_schema: ${json_schema}
EOF
}For more information on how to create custom tools for your agent and the structure of the agent/my-agent/tools.sh,
agent/my-agent/tools.py, or agent/my-agent/tools.ts files, refer to the Building Tools for Agents section below.
All the same variable interpolations supported by static instructions is also supported by dynamic instructions. For more information on what variables are available and how to use them, refer to the Special Variables and User-Defined Variables sections above.
Each agent you create also has a dedicated knowledge base that adds additional context to your queries and helps the LLM
answer queries effectively. The documents to load into RAG are defined in the documents array of your agent
configuration file:
documents:
- https://www.ohdsi.org/data-standardization/
- https://github.com/OHDSI/Vocabulary-v5.0/wiki/**
- OMOPCDM_ddl.sql # Relative path to agent (i.e. file lives at '<coyote-config-dir>/agents/my-agent/OMOPCDM_ddl.sql')These documents use the same syntax as those you'd define when constructing RAG normally. To see all the available types of documents that Coyote supports and how to use custom document loaders, refer to the RAG documentation.
Anytime your agent starts up, it will automatically be using the RAG you've defined here.
The first time an agent starts with documents attached, Coyote offers to build the knowledge base and asks which
storage driver to store it with. yaml (the default, and what Coyote has always done) or
duckdb, which keeps the vectors and text on disk so a large corpus doesn't have to be read back into memory on every
start. The choice is fixed once the knowledge base is built: changing it means removing the agent's built knowledge
base and letting it rebuild, which re-embeds every document.
Note
On Linux, duckdb requires a gnu build. Musl builds are statically linked and cannot load DuckDB's
extensions, so they skip the prompt and use yaml (details).
When function calling is enabled, an agent with documents also gets the rag__query
built-in tool, which lets the LLM run additional retrieval on-demand during a turn (in addition to the up-front
injection). Graph agents drive RAG through explicit rag nodes instead and do not receive this tool.
Building tools for agents is virtually identical to building custom tools, with one slight difference: instead of
defining a single function that gets executed at runtime (e.g. main for bash tools and run for Python tools), agent
tools define a number of subcommands.
You can only utilize one of: a bash-based <coyote-config-dir>/agents/my-agent/tools.sh, a Python-based
<coyote-config-dir>/agents/my-agent/tools.py, or a TypeScript-based <coyote-config-dir>/agents/my-agent/tools.ts.
However, if it's easier to achieve a task in one language vs the other,
you're free to define other scripts in your agent's configuration directory and reference them from the main
tools file. Any scripts not named tools.{py,sh,ts} will not be picked up by Coyote's compiler, meaning they
can be used like any other set of scripts.
It's important to keep in mind the following:
- Do not give agents the same name as an executable. Coyote compiles the tools for each agent into a binary that it temporarily places on your path during execution. If you have a binary with the same name as your agent, then your shell may execute the existing binary instead of your agent's tools
-
LLM_ROOT_DIRpoints to the agent's configuration directory. This is where agents differ slightly from normal tools: TheLLM_ROOT_DIRenvironment variable does not point to thefunctions/toolsdirectory like it does in global tools. Instead, it points to the agent's configuration directory, making it easier to source scripts and other miscellaneous files
When Coyote loads an agent, it will also search the agent's configuration directory for a .env file. If found, all
environment variables defined in the file will be made available to the agent's tools.
Python-based tools are defined exactly the same as they are for custom tool definitions. The only difference is that
instead of a single run function, you define as many as you like with whatever arguments you like.
Example:
agents/my-agent/tools.py
import urllib.request
def get_ip_info():
"""
Get your IP information
"""
with urllib.request.urlopen("https://httpbin.org/ip") as response:
data = response.read()
return data.decode('utf-8')
def get_ip_address_from_aws():
"""
Find your public IP address using AWS
"""
with urllib.request.urlopen("https://checkip.amazonaws.com") as response:
data = response.read()
return data.decode('utf-8')Coyote automatically compiles these as separate functions for the LLM to call. No extra work is needed. Just make sure you follow all the same steps to define each function as you would when creating custom Python tools.
For more information on how to build tools in Python, refer to the custom Python tools documentation
Bash-based agent tools are virtually identical to custom bash tools, with only one difference. Instead of defining a
single entrypoint via the main function, you actually define as many subcommands as you like.
Example:
agents/my-agent/tools.sh
#!/usr/bin/env bash
# @env LLM_OUTPUT=/dev/stdout The output path
# @describe Discover network information about your computer and its place in the internet
# Use the `@cmd` annotation to define subcommands for your script.
# @cmd Get your IP information
get_ip_info() {
curl -fsSL https://httpbin.org/ip >> "$LLM_OUTPUT"
}
# @cmd Find your public IP address using AWS
get_ip_address_from_aws() {
curl -fsSL https://checkip.amazonaws.com >> "$LLM_OUTPUT"
}To compile the script so it's executable and testable:
$ coyote --build-toolsThen you can execute your script (assuming your current working directory is agents/my-agent):
$ ./tools.sh get_ip_info
$ ./tools.sh get_ip_address_from_awsAll other special annotations (@env, @arg, @option @flags) apply to subcommands as well, so be sure to follow
the same syntax ad formatting as is used to create custom bash tools globally.
For more information on how to write, build and test tools in bash, refer to the custom bash tools documentation.
TypeScript-based agent tools work exactly the same as TypeScript global tools. Instead of a single run function,
you define as many exported functions as you like. Non-exported functions are private helpers and are invisible to the
LLM.
Example:
agents/my-agent/tools.ts
/**
* Get your IP information
*/
export async function get_ip_info(): Promise<string> {
const resp = await fetch("https://httpbin.org/ip");
return await resp.text();
}
/**
* Find your public IP address using AWS
*/
export async function get_ip_address_from_aws(): Promise<string> {
const resp = await fetch("https://checkip.amazonaws.com");
return await resp.text();
}
// Non-exported helper — invisible to the LLM
function formatResponse(data: string): string {
return data.trim();
}Coyote automatically compiles each exported function as a separate tool for the LLM to call. Just make sure you follow the same JSDoc and parameter conventions as you would when creating custom TypeScript tools.
TypeScript agent tools also support dynamic instructions via an exported _instructions() function:
import { readFileSync } from "fs";
/**
* Generates instructions for the agent dynamically
*/
export function _instructions(): string {
const schema = readFileSync("schema.json", "utf-8");
return `You are an AI agent that works with the following schema:\n${schema}`;
}For more information on how to build tools in TypeScript, refer to the custom TypeScript tools documentation.
It's often helpful to also have some conversation starters so users know what kinds of things the agent is capable of
doing. These are available in the REPL via the .starter command and are selectable.
They are defined using the conversation_starters setting in your agent's configuration file:
Example:
agents/my-agent/config.yaml:
conversation_starters:
- What is my username?
- What is my current shell?
- What is my ip?
- How much disk space is left on my PC??
- How to create an agent?
Coyote includes a built-in task tracking system designed to improve the reliability of LLM interactions, especially when using smaller language models. The Todo System helps models:
- Break complex tasks into manageable steps
- Track progress through multi-step workflows
- Automatically continue work until all tasks are complete
# agents/my-agent/config.yaml
auto_continue: true # Enable auto-continuation
max_auto_continues: 10 # Max continuation attempts
inject_todo_instructions: true # Include the default todo instructions into prompt
continuation_prompt: null # Custom prompt used when auto-continuing if inject_todo_instructions is 'false'-
When
inject_todo_instructionsis enabled, agents receive instructions on using five built-in tools:-
todo__init: Initialize a todo list with a goal -
todo__add: Add a task to the list -
todo__done: Mark a task complete -
todo__list: View current todo state -
todo__clear: Clear the entire todo list and reset the goal
These instructions are a reasonable default that detail how to use Coyote's To-Do System. If you wish, you can disable the injection of the default instructions and specify your own instructions for how to use the To-Do System into your main
instructionsfor the agent. -
-
When
auto_continueis enabled and the model stops with incomplete tasks, Coyote automatically sends a continuation prompt with the current todo state, nudging the model to continue working. -
This continues until all tasks are done or
max_auto_continuesis reached.
- Multistep tasks where the model might lose track
- Smaller models that need more structure
- Workflows requiring guaranteed completion of all steps
For complete documentation including all configuration options, tool details, and best practices, see the Todo System Guide.
Coyote agents can spawn and manage child agents that run in parallel as background tasks inside the same process. This enables orchestrator-style agents that delegate specialized work to other agents, similar to how tools like Claude Code or OpenCode handle complex multi-step tasks.
For a working example of an orchestrator agent that uses sub-agent spawning, see the built-in sisyphus agent. For an example of the teammate messaging pattern with parallel sub-agents, see the domain-reviewer agent.
Jobs vs. sub-agents: sub-agents are for work that needs reasoning: they run a full LLM loop. To background a single long-running tool call (a build, a test suite, a slow MCP invocation), use the Background Jobs system (
job__*tools) instead. The two compose one way only: agents may start jobs, but a job can never start an agent or another job. Completion notifications (system_notificationsentries on the next tool result) cover both systems.
| Setting | Type | Default | Description |
|---|---|---|---|
can_spawn_agents |
boolean | false |
Enable this agent to spawn child agents |
spawnable_agents |
list of strings | unset | Whitelist of agent names this agent may spawn. Omit for unrestricted access. |
max_concurrent_agents |
integer | 4 |
Maximum number of child agents that can run simultaneously |
max_agent_depth |
integer | 3 |
Maximum nesting depth for sub-agents (prevents runaway spawning chains) |
inject_spawn_instructions |
boolean | true |
Inject the default spawning instructions into the agent's system prompt |
summarization_model |
string | current model | Model to use for summarizing long sub-agent output (e.g. openai:gpt-4o-mini) |
summarization_threshold |
integer | 4000 |
Character count above which sub-agent output is summarized before returning |
escalation_timeout |
integer | 0 |
Seconds a sub-agent waits for an escalated user interaction response; 0 = wait indefinitely (default) |
Example configuration:
# agents/my-orchestrator/config.yaml
can_spawn_agents: true
spawnable_agents:
- explore
- coder
- oracle
max_concurrent_agents: 6
max_agent_depth: 2
inject_spawn_instructions: true
summarization_model: openai:gpt-4o-mini
summarization_threshold: 3000
escalation_timeout: 600By default (i.e. when spawnable_agents is omitted), any agent installed under
<coyote-config-dir>/agents/ is spawnable.
To restrict, add a list of agent names. Only agents whose directory name
appears in the list will be spawnable. Anything else the agent tries to
spawn via agent__spawn is rejected with an actionable error, and hidden
from agent__list_available output.
can_spawn_agents: true
spawnable_agents:
- explore
- coder
- oracleUse this to:
-
Scope an orchestrator to a focused set of specialists. An orchestrator
that only needs
explore/coder/oraclewon't accidentally spawn unrelated personal agents in your config directory. - Enforce team boundaries. In a shared/CI setup, restrict what a given agent is allowed to invoke.
-
Prevent recursion cycles. Combined with
max_agent_depth, an explicit whitelist gives you fine-grained control over the spawn graph.
Notes:
- Match is exact and case-sensitive. Use directory names as they appear on disk.
-
spawnable_agents: []means literally nothing spawnable (rare but valid). - Graph agents (with a
graph.yaml) always ignorespawnable_agents; they statically declare their spawn targets viaagentnodes in the graph itself. - Override at runtime via env var:
<AGENT_NAME>_SPAWNABLE_AGENTSas a JSON array (e.g.SISYPHUS_SPAWNABLE_AGENTS='["explore","coder"]').
When can_spawn_agents is enabled, the agent receives tools for spawning and managing child agents:
| Tool | Description |
|---|---|
agent__spawn |
Spawn a child agent in the background, optionally passing its declared variables. Returns an agent ID immediately. |
agent__check |
Non-blocking check: is the agent done? Returns PENDING or the result. |
agent__collect |
Blocking wait: wait for an agent to finish, return its output. |
agent__list_available |
List all agent types installed and available to spawn (name + description + declared variables). Discovery counterpart to agent__spawn. |
agent__list_running |
List all subagents you have spawned, with their status. |
agent__cancel |
Cancel a running agent by ID. |
The core pattern is Spawn -> Continue -> Collect:
# 1. Spawn agents in parallel (returns IDs immediately)
agent__spawn --agent explore --prompt "Find auth middleware patterns in src/"
agent__spawn --agent explore --prompt "Find error handling patterns in src/"
# 2. Continue your own work while they run
# 3. Check if done (non-blocking)
agent__check --id agent_explore_a1b2c3d4
# 4. Collect results when ready (blocking)
agent__collect --id agent_explore_a1b2c3d4
agent__collect --id agent_explore_e5f6g7h8
Any agent defined in your <coyote-config-dir>/agents/ directory can be spawned as a child. Child agents:
- Run in a fully isolated environment (separate session, config, and tools)
- Have their output suppressed from the terminal (no spinner, no tool call logging)
- Return their accumulated output to the parent when collected
agent__spawn accepts an optional variables parameter (an object of string values). This is the spawn-time
equivalent of the CLI's --agent-variable:
agent__spawn --agent pr-reviewer --variables {"pr": "1234"} --prompt "Review this PR."
Discovering an agent's variables. agent__list_available advertises each agent's declared
variables, so a parent can learn the spawn contract up front:
{
"name": "pr-reviewer",
"description": "Reviews pull requests",
"variables": [
{ "name": "pr", "description": "The PR URL or number", "required": true }
]
}required is true when the variable declares no default; the variables key is omitted for
agents that declare none.
Precedence. For each declared variable, the value is resolved in this order:
- The spawn call's
variablesentry - A same-named variable inherited from the parent (e.g. set on the parent via
--agent-variable) - The variable's declared
default
Validation and error behavior. Unknown variable names, non-string values, or passing
variables to an agent that declares none all return a self-correcting error listing the target
agent's declared variables. Spawned children never trigger the interactive variable prompt: if a
required variable ends up with no value, the spawn fails with an error naming each missing
variable so the parent can re-spawn with the right variables.
Notes:
- Values reach the child's instructions (
{{name}}interpolation), its tools (LLM_AGENT_VAR_<NAME>), and, for graph agents, the initial graph state, so{{name}}resolves insidellmnode prompts. - If the target agent is configured with a persistent
agent_sessionand an existing session is resumed, the session's stored variables win and spawn-providedvariablesare ignored. -
agent__task_createaccepts the samevariablesparameter for auto-dispatched tasks (see Active Task Dispatch).
For complex workflows where tasks have ordering requirements, the spawning system includes a dependency-aware task queue:
| Tool | Description |
|---|---|
agent__task_create |
Create a task with optional dependencies, auto-dispatch agent, and agent variables. |
agent__task_list |
List all tasks with their status, dependencies, and assignments. |
agent__task_complete |
Mark a task done. Returns newly unblocked tasks and auto-dispatches agents. |
agent__task_fail |
Mark a task as failed. Dependents remain blocked. |
# Create tasks with dependency ordering
agent__task_create --subject "Explore existing patterns"
agent__task_create --subject "Implement feature" --blocked_by ["task_1"]
agent__task_create --subject "Write tests" --blocked_by ["task_2"]
# Mark tasks complete to unblock dependents
agent__task_complete --task_id task_1
Tasks can optionally specify an agent to auto-spawn when the task becomes runnable:
agent__task_create \
--subject "Implement the auth module" \
--blocked_by ["task_1"] \
--agent coder \
--variables {"project_dir": "/path/to/repo"} \
--prompt "Implement auth module based on patterns found in task_1"
When task_1 completes and the dependent task becomes unblocked, an agent is automatically spawned with the
specified prompt, and with the task's stored variables passed through to the spawn, exactly as if they
had been given to agent__spawn directly (same precedence and validation). agent__task_list echoes each
task's variables alongside its prompt. No manual intervention needed. This enables fully automated
multi-step pipelines.
When a child agent produces long output, it can be automatically summarized before returning to the parent. This keeps parent context windows manageable.
- If the output exceeds
summarization_thresholdcharacters (default: 4000), it is sent through an LLM summarization pass - The
summarization_modelsetting lets you use a cheaper/faster model for summarization (e.g.gpt-4o-mini) - If
summarization_modelis not set, the parent's current model is used - The summarization preserves all actionable information: code snippets, file paths, error messages, and concrete recommendations
All agents (including children) automatically receive tools for direct sibling-to-sibling messaging:
| Tool | Description |
|---|---|
agent__send_message |
Send a text message to another agent's inbox by ID. |
agent__check_inbox |
Drain all pending messages from your inbox. |
This enables coordination patterns where child agents share cross-cutting findings:
# Agent A discovers something relevant to Agent B
agent__send_message --id agent_reviewer_b1c2d3e4 --message "Found a security issue in auth.rs line 42"
# Agent B checks inbox before finalizing
agent__check_inbox
Messages are routed through the parent's supervisor. A parent can message its children, and children can message their siblings. For a working example of the teammate pattern, see the built-in domain-reviewer agent, which spawns file-specific reviewers that share cross-cutting findings with each other.
Sub-agents run by graph agent nodes can also form per-fan-out teams: set teammates: true on the
agent node and concurrent siblings (map branches or same-super-step flagged nodes) get pre-provisioned inboxes, a roster
block in their prompts, and a peers section in agent__list_running. See
Teammate messaging in fan-outs.
The spawning system includes built-in safeguards to prevent runaway agent chains:
-
max_concurrent_agents: Caps how many agents can run at once (default: 4). Spawn attempts beyond this limit return an error asking the agent to wait or cancel existing agents. -
max_agent_depth: Caps nesting depth (default: 3). A child agent spawning its own child increments the depth counter. Attempts beyond the limit are rejected. -
can_spawn_agents: Only agents with this flag set totruecan spawn children. By default, spawning is disabled. This means child agents cannot spawn their own children unless you explicitly create them withcan_spawn_agents: truein their config.
Coyote includes built-in tools for agents (and the REPL) to interactively prompt the user for input. These tools are always available. No configuration needed. They are automatically injected into every agent and into REPL mode when function calling is enabled.
| Tool | Description | Returns |
|---|---|---|
user__select |
Present a list of named options and ask the user to pick exactly one. Use this — not confirm — whenever there are 2+ named options to choose between. |
The selected option string |
user__confirm |
Ask a genuinely binary yes/no question with no other choices. Do NOT use for "A or B?" situations; use select instead. |
"yes" or "no"
|
user__input |
Collect free-form text from the user when no predefined options exist. | The text entered by the user |
user__checkbox |
Ask the user to pick one or more options from a list (multi-select). Use when multiple answers are valid simultaneously. | Array of selected option strings |
Parameters:
-
user__select:--question "..." --options ["Option A", "Option B", "Option C"] -
user__confirm:--question "..." -
user__input:--question "..." -
user__checkbox:--question "..." --options ["Option A", "Option B", "Option C"]
At the top level (depth 0), these tools render interactive terminal prompts directly using arrow-key navigation, checkboxes, and text input fields.
When a child agent (depth > 0) calls a user__* tool, it cannot prompt the terminal directly. Instead,
the request is automatically escalated to the root agent:
- The child agent calls
user__select(...)and blocks, waiting for a reply - The root agent sees a
pending_escalationsnotification in its next tool results - The root agent either answers from context or prompts the user itself, then calls
agent__reply_escalationto unblock the child - The child receives the reply and continues
The escalation wait is configurable via escalation_timeout in the agent's config.yaml (default: 0 =
wait indefinitely, so autonomous runs never lose an approval while the user is away). When a nonzero
timeout is configured and expires, the child receives a fallback message asking it to use its best
judgment.
| Tool | Description |
|---|---|
agent__reply_escalation |
Reply to a pending child escalation, unblocking the waiting child agent. |
This tool is automatically available to any agent with can_spawn_agents: true.
Skills are modular knowledge or capability packs that the model loads and unloads mid-conversation
via skill__list and skill__load. Where a role defines who the assistant is, a skill layers a
methodology (code-review), a toolkit (git-master), or a one-shot helper onto whatever role/agent/session is
active. Multiple skills can be loaded at once; their instructions stack and their granted tools/MCP servers union.
Agents can override the global skill policy in their own config.yaml:
# agents/my-agent/config.yaml
skills_enabled: true # Master switch for this agent (false wins at any level; unset inherits global)
enabled_skills: # Allowlist for this agent (most-specific-wins: agent > role > global)
- code-review
- git-master
inject_skill_instructions: true # Inject the skill-discovery hint into this agent's system prompt
skill_instructions: null # Override the built-in hint text (null uses the default)-
skills_enabled(default unset): Settingfalsedisables skills for this agent.skill__list,skill__load, andskill__unloadare not exposed when this agent runs. If unset, the agent inherits the global setting. -
enabled_skills(default unset): When set, restricts which skills this agent can load. Skills not in this list are filtered out ofskill__list, andskill__loadrejects them with"Skill 'X' is not enabled in this context". If unset, the agent inherits the role or global allowlist. Accepts a YAML list or a comma-separated string. -
inject_skill_instructions(defaulttrue): Controls whether Coyote appends a short hint to the system prompt telling the model to callskill__listearly in a task. Suppressed automatically when the effectiveenabled_skillsset is empty. -
skill_instructions(default unset): Replaces the built-in hint text with your own wording.
Skills require function calling (function_calling_support: true); the skill subsystem is silently disabled
otherwise. For the full skills model, including the frontmatter format, validation rules, built-in skills, and
the graph-agent variant, see the Skills Guide.
For working examples of agents that use skills heavily, see the bundled
file-reviewer and domain-reviewer agents.
Coyote automatically appends usage instructions to your agent's system prompt for each enabled built-in system. These instructions are injected into both static and dynamic instructions after your own instructions, ensuring agents always know how to use their available tools.
| System | Injected When | Toggle |
|---|---|---|
| Todo tools |
auto_continue: true AND inject_todo_instructions: true
|
inject_todo_instructions |
| Spawning tools |
can_spawn_agents: true AND inject_spawn_instructions: true
|
inject_spawn_instructions |
| Teammate messaging | Always (all agents) | None (always injected) |
| User interaction | Always (all agents) | None (always injected) |
| Skill discovery hint |
skills_enabled: true AND inject_skill_instructions: true AND effective enabled_skills is non-empty |
inject_skill_instructions |
If you prefer to write your own instructions for a system, set the corresponding inject_* flag to false
and include your custom instructions in the agent's instructions field. The built-in tools will still be
available; only the auto-injected prompt text is suppressed.
Coyote comes packaged with some useful built-in agents:
-
adversary: A graph-based adversarial plan-conformance reviewer. Wherecode-reviewerasks "is this code good?",adversaryasks "is this the code the plan asked for? All of it, and only it?". The graph extracts the plan's acceptance criteria into a list and fans out one fresh-context verification branch per criterion, so partial coverage ("checked 8 of 12 and waved the rest through") is structurally impossible. Each verdict passes a deterministic contract gate:METrequires evidence including the test proving the behavior (no test means at bestPARTIAL), malformed output is rejected once then recorded asPARTIAL(unproven). Then, a holistic ADDITIVE-only pass hunts what per-criterion checks can't see: absence ("the dog that didn't bark"), scope drift, interface substitution, silently-overridden recorded decisions, and gamed tests. A script computes the blockingADVERSARIAL_REVIEW: CONFORMS/DIVERGESverdict: CONFORMS requires every criterion MET with zero holistic complaints, and a missing plan fails closed. The post-implementation counterpart tooracle's pre-implementationplan-review. Designed to be delegated to bysisyphusalongsidecode-reviewer(they cover different failure modes: quality vs. conformance). -
architecture-reviewer: An on-demand architecture improvement scout (via thecodebase-designskill). Scans a codebase for deepening opportunities (e.g. shallow modules, leaked seams, missing locality, etc.), weighted by git-history hot spots, presents candidates as a visual HTML (or markdown) report, then refines the chosen candidate into an implementation-ready interface proposal via design-it-twice. Proposes only, never implements, and is NOT a completion gate: invoke it when you want the codebase made deeper, more testable, and easier to navigate. -
architect: A design-doc orchestrator for large-scale implementation jobs. Consumes a high-level design doc, decomposes it into a quality-gated plan (gatekeeperself-containedness gate +oracleplan-review) and ~1-engineer-day task files (via thedesign-sessionandtask-trackingskills, tracked on disk in aplans/directory), spawns onesisyphusper task on a single run branch with a per-taskreview-gauntletdone-gate (forcedadversaryconformance lane, plusprobewhen the task changes consumer-facing surface), and finishes with ONE draft PR, CI checks watched to green, plus follow-up task files for every manual action the code can't do for itself. Ends with awhetstonecontinuous-improvement pass when the run addressed review feedback from outside its own lanes. Anescalation_policyvariable governs decision points:autonomousanswers them (its own and its Sisyphus children's escalations) from standing rulings in<config_dir>/share/standing-rulings.mdor a clearly recommended option, amends plan text before any conformance re-run, and batches every self-made call into aDecisions taken autonomouslydigest in the final report and PR body: secrets, money movement, and destructive actions always escalate to you. Supports opt-in worktree-based parallel task execution. Plans declare a quality bar:rigor(poc/prototype/production) plus thesurfacesbeing built (rest-api, cli, library, worker, iac, db-migration, frontend, ci-cd, orother:<label>via a design-timelibrarianlookup), which flows into every downstream reviewer, with an evidence-cited rejection protocol for convention findings. When a design hinges on deep external unknowns, it escalates research from alibrarianlookup to a fulldeep-researchspawn (always withautonomy: autonomous, so the child never stalls on its approval gate). -
coder: An agent to assist you with all your coding tasks -
code-reviewer: A CodeRabbit-style graph-based review orchestrator. This is the deterministic shell around LLM review judgment. Scripts resolve the diff, the quality bar (rigor+surfaces: caller-passed, read from the active plan's frontmatter, or detected from the diff), repo conventions, matching review-miss ledger patterns, and repo-history signals (fix/revert clusters plus prior PR review comments on fragile paths); domain linters (tflint, hadolint, actionlint,buf breaking) run unconditionally when installed. Changed files are grouped into DOMAIN slices with deterministic proposal, LLM refinement, and an exact-cover gate so no file can be lost, and each slice is reviewed in parallel by adomain-reviewerleader with peer messaging between leaders; every leader report passes a completeness gate (mandatory sections present even when clean, reject-and-retry once). A synthesis node with NO verdict authority merges findings,finding-verifierre-validates every 🔴/🟡 on an unskippable graph edge, and a SCRIPT computes the rigor folding (below-threshold[convention]findings defer; 🔴 never folds) and theMERGE-READY/NEEDS-HUMANverdict with deterministic always-human triggers (destructive migrations, auth surface) — the model that wrote the findings never grades its own homework. Optional lanes: operational history (prior_art_agent), org context (org_context_agent), and a downstream-consumers sweep (consumers_root/github_org). -
demo: An example agent to use for reference when learning to create your own agents -
deep-research: A graph-based deep-investigation agent for questions that deserve hours, not minutes. Plans the investigation, decomposes it into research questions, and fans each question out to its ownlibrarianrunning atresearch_depth: deep, then vets the combined sources (dead links, paywalled citations) and runs a critique -> re-research reflexion loop until the critique passes or thereflexion_budgetvariable (default2) is spent; raise it to let the report churn longer. Anautonomyvariable governs the final gate:supervised(default) pauses for your approval and feedback on the report;autonomousskips the human gate and returns the report plus the source check. Set it whenever it's spawned as a subagent so an unattended run never parks on an approval prompt. Designed to be delegated to bysisyphusandarchitectas the top rung of the research ladder:librarian(standard) ->librarian(deep) ->deep-research. -
domain-reviewer: A domain-slice review leader (used by thecode-reviewergraph). Wherefile-reviewerreviews one file in isolation,domain-reviewerreviews a group of RELATED changed files (e.g. the DB migration plus the queries that use it, the routes plus their handlers, etc.) as one coherent unit: guard symmetry across the slice's sibling paths, literal/enum drift between files, schema <-> usage mismatch, and slice-level blast-radius / test-adequacy / removed-guard checks reported even when clean. Small slices are reviewed directly with the full skill stack; larger ones fan out onefile-reviewerper file with a sibling roster. Leaders run as parallel map branches with teammate messaging armed, so the DB leader can warn the API leader about a renamed column mid-review. Emits a machine-checked report contract ending inDOMAIN_REVIEW_COMPLETE. -
explore: An agent designed to help you explore and understand your codebase -
file-reviewer: An agent designed to perform code-review on a single file (used bydomain-reviewerinside thecode-reviewergraph). Reviews with thecode-reviewandai-slop-removerskills, and additionally loads thetransactional-integrityskill when the diff touches state-changing code (e.g. DB writes, transactions, queue/webhook/job handlers, retries, or external side effects, etc.) to catch atomicity gaps, read-modify-write races, non-idempotent handlers of at-least-once inputs, and dual-writes with no reconciliation. Also loadslogging-disciplineon diffs with operational surface, and, when the orchestrator's surface routing names them, the surface-contract review skills (rest-api-review,cli-review,library-review,worker-review,iac-review,migration-review,cicd-review), whose findings carry[convention]/[correctness]markers used for rigor folding and the evidence-cited rejection protocol downstream. -
finding-verifier: A graph-based fact-checker for code-review findings (wired into thecode-reviewergraph as an unskippable edge). Maps over a review's 🔴/🟡 findings in parallel, fresh context per finding, so the verifier inherits none of the reviewer's assumptions, re-reads the cited code, and returnsVERIFIED(with quoted evidence),FALSE(only with proof, e.g. the "missing" guard exists at another path:line), orUNVERIFIABLE(kept, marked). Each branch's verdict passes a deterministic contract gate (machine-validated JSON, authoritative id stamped from the finding itself, retry once; malformed output recordsUNVERIFIABLE, so a bad branch never sinks the run). It never re-judges severity. It only tests whether a claim is true.code-revieweruses the verdicts to drop provably-false findings before the final report; dropped findings are tallied, never silently vanished. -
gatekeeper: A plan self-containedness gate (via theplan-gatekeepingskill). Audits a plan against the "sealed container" standard, meaning every question a context-free implementer will hit is either answered inline or delegated via a VERIFIED pointer to the exact code/docs that answer it (where infra code goes, what DB tech, which layout to mirror, exact test commands, how to boot the service locally from a clean state), and returns a blockingPLAN_GATE: SEALED/LEAKYverdict with the missing questions. The pre-implementation context counterpart toplan-review(executability) andadversary(post-implementation conformance). Designed to be delegated to byarchitect. -
librarian: A graph-based agent that researches external references. It finds official docs, production OSS examples, and web best practices. The "external grep" sibling ofexplore(which handles internal/codebase grep). Designed to be delegated to bysisyphuswhenever an unfamiliar library, API, or framework is involved. Aresearch_depthvariable (standard/deep) sets the effort ceiling:deeproughly triples the search/OSS-example/fetch budgets and the source quota (and extends the graph timeout to match) for exhaustive coverage; the modedeep-researchuses for every one of its research lanes. -
oracle: An agent for high-level architecture, design decisions, complex debugging, and reviewing implementation plans before execution (via theplan-reviewskill, returning aPLAN_REVIEW: OKAY/REJECTverdict) -
probe: A black-box usage-pattern verifier (via theusage-pattern-testingskill). Where every other review gate reads text,probeverifies behavior: it boots the changed code locally from a clean, isolated state and exercises the consumer-facing surface (HTTP APIs, RPCs, CLIs) as a real cold-start consumer. Runs the repo's existing usage suites first (regression check), derives expected behaviors from the SPEC, never the implementation, so the implementer's misreadings can't become its assertions, and authors tests for uncovered patterns in the repo's suite conventions (tools like Hurl/curl/grpcurl are examples, not requirements). Returns a blockingUSAGE_PROBE: PASS/FAIL/INCONCLUSIVEverdict; its tests are deliverables adopted as permanent regression coverage. Designed to be delegated to bysisyphusandarchitectwhen a change touches consumer-visible surface. -
report-writer: An agent to polish research findings into clear, citation-preserving final reports -
review-gauntlet: A graph-based "done gate" that runs the whole review stack as ONE spawn. A script derives deterministic change signals (auth surface, dependency/migration/exec changes, consumer-facing surface), an LLM node may ADD lanes but never remove them, and a rules floor guarantees the minimum panel:code-revieweralways,adversarywhenever plan context exists,security-revieweron security-relevant signals, andprobewhen consumer surface changed and a local-run recipe exists. Lanes run in parallel with verdict independence (no teammate messaging between reviewers), each lane's sentinel is machine-parsed (a missing or malformed verdict BLOCKS, it never passes), and a script computes the finalGAUNTLET: PASS/BLOCKEDverdict (probeINCONCLUSIVEblocks with an environment note, never coerced to pass or fail). The preferred way forsisyphusandarchitectto run the review stack. -
security-reviewer: A security analyst for code changes (via thesecurity-reviewskill). Traces untrusted data from sources to dangerous sinks (injection, committed secrets, authn/authz gaps, path traversal, SSRF, unsafe deserialization, supply chain), then returns a posture-gatedSECURITY_REVIEW: PASS/FAILverdict. Thesecurity_posturevariable (prototype/standard/hardened) sets the blocking threshold so POCs aren't held to production strictness, while Critical findings (leaked credentials, host-endangering code) block in every posture. Runs a mechanized pre-pass first (gitleaks/semgrep/dependency audits when already installed) whose hits are leads to trace, never pasted findings. Designed to be delegated to bysisyphusalongsidecode-reviewerandadversary. -
sisyphus: A powerhouse orchestrator agent for writing complex code and acting as a natural language interface for your codebase (similar to ClaudeCode, Gemini CLI, Codex, or OpenCode). Uses sub-agent spawning to delegate toexplore,librarian,deep-research,coder,oracle,review-gauntlet,code-reviewer,adversary,security-reviewer,probe,step-runner, andwhetstone. On non-trivial work it runs the review stack, preferably as ONEreview-gauntletspawn (the done gate that machine-parses every lane's verdict), else as up to four individual review passes:code-reviewerfor code quality,adversaryfor plan conformance, andsecurity-reviewerfor exploitability when the change touches attack surface, andprobefor black-box usage-pattern verification when the change touches consumer-facing surface. After a run that addressed review feedback from outside its own lanes (humans, or a teammate's independent review agent), it spawnswhetstoneonce to fold the review misses back into the suite as user-approved improvements. Anescalation_policyvariable governs mid-run decisions:autonomousresolves them from standing rulings (<config_dir>/share/standing-rulings.md, recorded via therecord-rulingmacro) or a clearly recommended option, amends plan/criteria text before any re-review, and reports every self-made call in aDecisions taken autonomouslydigest: secrets, money movement, and destructive actions always escalate. Also supports plan-driven workflows: authoring phased implementation plans (via theplan-authoringskill), havingoraclereview them, and executing them one reviewed step at a time. Handles review findings quality-bar-aware: the plan'srigorcalibrates which severities block (🔴 always does) and maps to a defaultsecurity_posture; below-threshold[convention]findings defer into follow-ups instead of blocking; and a convention finding can be rejected only with cited evidence (a repo convention at file:line or a recorded plan decision). Never 🔴 or[correctness]findings. -
sql: A universal SQL agent that enables you to talk to any relational database in natural language -
step-runner: A graph-based agent that executes ONE step of a phased implementation plan (aplans/repo authored with theplan-authoringskill) with the step protocol enforced as graph edges: orient from the previous handoff -> staleness-check the plan -> implement (delegating tocoder) -> format/lint/build/full-test verification -> edge-case sweep -> optional independent review (code-reviewer) -> evidence-backed handoff -> hard-stop user approval gate. ReturnsSTEP_COMPLETE/STEP_BLOCKED/STEP_REJECTED/STEP_FAILEDto the caller. Designed to be delegated to bysisyphus. -
whetstone: A continuous-improvement agent for the review suite; the review suite reviews code; whetstone reviews the review. Spawned bysisyphusandarchitectat the end of a run that addressed review feedback from outside the run's own lanes (humans, or a teammate's independent review agent, never its own suite's output; third-party agent findings count only when the author actually addressed them). Classifies each piece of feedback as CATCH/PARTIAL/MISS against what the suite's own reviewers flagged, then generalizes the misses: taste is dropped, one-offs go to a candidates inbox, org-specific classes become review-miss ledger entries, and general classes are drafted as suite edits in house style. Everything is applied only with user approval, and edits go where the suite lives: a configuredsuite_repo(then optionally mirrored to the live config, drift-guarded), or directly into the live config with per-file backups when no repo is set. Additive only; it never weakens an existing rule. Proposals are persisted before they are proposed: every surviving draft is written toshare/whetstone-pending/<UTC ts>-<owner>-<repo>-<N>.md(one## P<n>block per proposal, with the quoted evidence and the full remediation text) before the approval prompt, so an unanswered prompt, a headless spawn, or a session without write tools never loses it. Applied and declined blocks are deleted, unanswered ones staystatus: pending, and emptied files are removed. To pick them up later runcoyote --agent whetstoneand sayapply pending: that mode skips classification, lists every pending block oldest first, asks which to apply, and routes the approved ones through the normal apply path. A spawner that declares itself headless (e.g. a PR-watch round) gets the persisted file and no prompt.
Coyote writes these built-in agents to your agents directory on first run and never overwrites them afterward, so any
edits you make to them are preserved across Coyote updates. To discard your local changes and reinstall the built-in
agents from the current Coyote build, run coyote --install agents (or .install agents in the REPL). Agents you created
yourself are not affected.
If you run Coyote inside a Sandbox, agents that need external binaries or specific network domains
must ship a per-agent sbx-mixin.yaml alongside their config.yaml (or graph.yaml). Coyote discovers these
automatically on every coyote --sandbox. No flags or registration required.
Layout:
<coyote-config-dir>/agents/<my-agent>/
├── config.yaml
├── tools.sh
├── sbx-mixin.yaml # <- gets auto-applied alongside the embedded base kit
└── files/ # <- optional sibling: dotfiles/scripts/refs auto-mounted into the sandbox
└── home/
└── .config/my-tool/settings.json
workspace/
└── .editorconfig
The sibling files/ directory is optional. If present, its contents are staged into the mixin's kit and injected into
the sandbox via sbx's static-files convention:
files/home/* lands under /home/agent/, files/workspace/* lands under the mounted workspace. See
Sandboxes: Bundling static files alongside a mixin for the
caveats (symlinks rejected, tree contents hashed into the kit identity, exec bits preserved).
A custom agent that uses httpie to call an internal API:
# <coyote-config-dir>/agents/internal-api-agent/sbx-mixin.yaml
schemaVersion: "2"
kind: mixin
name: agent-internal-api
description: Installs httpie and allows access to our internal API host
permissions:
network:
allow:
- "internal-api.example.com"
setup:
install:
- command: |
sudo apt-get update
sudo apt-get install -y httpie
user: "1000"
description: Install httpie for the internal-api-agentSee Sandboxes: Extending the Sandbox for the full discovery path table and the official sbx mixin reference for the schema.
-
Per-agent mixins are applied on every
coyote --sandboxregardless of which agent you invoke. Mixin discovery is global by design (matches Coyote's "everything in the config dir is live" model). Users with many agents pay the install cost for all of their mixins once on first-run; subsequent attaches are instant. - Agents that spawn sub-agents need the spawned agents' mixin requirements too. Either co-locate them in the parent agent's mixin or rely on each spawned agent shipping its own; both work.
-
Built-in agents shipped via
coyote --install agentsmay project newsbx-mixin.yamlfiles into your config on update. Re-running the sandbox after such an install surfaces them in the verbose log with the extra installs/domains they bring. Not a bug, just visibility. -
The
sqlagent'susqlinstall is in the base kit, not a per-agent mixin. Same forpandoc(used byfetch_url_via_curl). Both ship with every Coyote sandbox automatically. Seeassets/sbx-kit/spec.yamlfor the full base prereq list.