Merge branch 'skills'
+8
-2
@@ -736,14 +736,20 @@ available; only the auto-injected prompt text is suppressed.
|
||||
Coyote comes packaged with some useful built-in agents:
|
||||
|
||||
* `coder`: An agent to assist you with all your coding tasks
|
||||
* `code-reviewer`: A [CodeRabbit](https://coderabbit.ai)-style code reviewer that spawns per-file reviewers using the teammate messaging pattern
|
||||
* `code-reviewer`: A [CodeRabbit](https://coderabbit.ai)-style code reviewer that spawns per-file reviewers using the
|
||||
teammate messaging pattern
|
||||
* `demo`: An example agent to use for reference when learning to create your own agents
|
||||
* `deep-research`: A graph-based agent designed to perform deep web research
|
||||
* `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 by the `code-reviewer` agent)
|
||||
* `librarian`: A graph-based agent that researches external references. It finds official docs, production OSS examples,
|
||||
and web best practices. The "external grep" sibling of `explore` (which handles internal/codebase grep). Designed to
|
||||
be delegated to by `sisyphus` whenever an unfamiliar library, API, or framework is involved.
|
||||
* `oracle`: An agent for high-level architecture, design decisions, and complex debugging
|
||||
* `report-writer`: An agent to polish research findings into clear, citation-preserving final reports
|
||||
* `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 to `explore`, `coder`, and `oracle`.
|
||||
* `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 to
|
||||
`explore`, `librarian`, `coder`, and `oracle`.
|
||||
* `sql`: A universal SQL agent that enables you to talk to any relational database in natural language
|
||||
|
||||
Coyote writes these built-in agents to your agents directory on first run and never overwrites them afterward, so any
|
||||
|
||||
@@ -284,6 +284,58 @@ $ ./get_current_time.sh
|
||||
Fri Oct 24 05:55:04 PM MDT 2025
|
||||
```
|
||||
|
||||
# Reading argument values from `LLM_TOOL_RAW_JSON`
|
||||
|
||||
Coyote dispatches a bash tool call by converting the LLM's JSON arguments into shell `--option=<value>` flags via `jq`,
|
||||
then `eval`-ing the result. The flag values reach your `main` function as `argc_*` variables. For short, single-line
|
||||
values this works fine.
|
||||
|
||||
However, for **large multi-line values, or values dense with shell-significant characters** (markdown table pipes (`|`),
|
||||
single quotes, em-dashes, etc.), the shell-quoting round-trip can occasionally drop characters or truncate the value
|
||||
before it reaches your `argc_*` variable. Symptoms include `argc_*` being shorter than what the LLM sent, or starting
|
||||
mid-content.
|
||||
|
||||
To sidestep the shell-quoting layer entirely, read the value directly from the raw JSON envelope that Coyote exports as
|
||||
the `LLM_TOOL_RAW_JSON` environment variable:
|
||||
|
||||
```bash
|
||||
# shellcheck disable=SC2154
|
||||
main() {
|
||||
argc_contents="$(jq -r '.contents' <<< "$LLM_TOOL_RAW_JSON")"
|
||||
argc_path="$(jq -r '.path' <<< "$LLM_TOOL_RAW_JSON")"
|
||||
|
||||
# ... rest of your tool logic using $argc_contents and $argc_path
|
||||
}
|
||||
```
|
||||
|
||||
The `jq -r` ("raw") flag preserves every byte of the original LLM-sent value, including newlines, quotes, em-dashes,
|
||||
and shell-special characters, without any shell-quoting layer in between. This is the pattern Coyote's bundled
|
||||
`fs_write`, `fs_patch`, `execute_command`, `execute_sql_code`, and `send_mail` tools use for their large-payload
|
||||
options. The argc `# @option --foo!` directives stay in your script so Coyote can build the JSON schema for the LLM
|
||||
and validate the call, but your `main()` reads from `LLM_TOOL_RAW_JSON` instead of trusting argc's value capture.
|
||||
|
||||
## When to use this
|
||||
|
||||
- Your option's value can legitimately be many KB of text (file contents, code, email bodies, SQL queries).
|
||||
- Your option's value can contain shell-significant characters in dense patterns (pipes, single quotes, ANSI escapes).
|
||||
- You observe that `argc_<option>` is shorter than what the LLM sent, or has corruption near the beginning/middle of the
|
||||
value.
|
||||
|
||||
If your tool only takes short string values (paths, IDs, search queries), you don't need the bypass. The standard argc
|
||||
flow handles those reliably.
|
||||
|
||||
## For agent-local tools
|
||||
|
||||
If you're writing tools inside an agent's `tools.sh` (under `<config_dir>/agents/<agent>/tools.sh`), the same value is
|
||||
exposed as `LLM_AGENT_RAW_JSON` (the raw JSON for the agent function call). The semantics are identical; only the
|
||||
variable name differs:
|
||||
|
||||
```bash
|
||||
argc_some_field="$(jq -r '.some_field' <<< "$LLM_AGENT_RAW_JSON")"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Prompt Helpers
|
||||
It's often useful to create interactive prompts for our bash tools so that our tools can get input from
|
||||
users.
|
||||
|
||||
+63
-6
@@ -27,16 +27,73 @@ to enable it globally. See the [Tools](Tools#enablingdisabling-global-tools) doc
|
||||
## Environment Variables
|
||||
All tools have access to the following environment variables that provide context about the current execution environment:
|
||||
|
||||
| Variable | Description |
|
||||
|----------------------|--------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `LLM_OUTPUT` | Indicates where the output of the tool should go. <br>In certain situations, this may be set to a temporary file instead of `/dev/stdout`. |
|
||||
| `LLM_ROOT_DIR` | The root `config_dir` directory for Coyote <br>(i.e. `dirname $(coyote --info \| grep config_file \| awk '{print $2}')`) |
|
||||
| `LLM_TOOL_NAME` | The name of the tool being executed |
|
||||
| `LLM_TOOL_CACHE_DIR` | A directory specific to the tool for storing cache or temporary files |
|
||||
| Variable | Description |
|
||||
|----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `LLM_OUTPUT` | Indicates where the output of the tool should go. <br>In certain situations, this may be set to a temporary file instead of `/dev/stdout`. |
|
||||
| `LLM_ROOT_DIR` | The root `config_dir` directory for Coyote <br>(i.e. `dirname $(coyote --info \| grep config_file \| awk '{print $2}')`) |
|
||||
| `LLM_TOOL_NAME` | The name of the tool being executed |
|
||||
| `LLM_TOOL_CACHE_DIR` | A directory specific to the tool for storing cache or temporary files |
|
||||
| `LLM_TOOL_RAW_JSON` | The raw JSON envelope the LLM sent for this tool call, exactly as received. See [Reading values via LLM_TOOL_RAW_JSON](#reading-values-via-llm_tool_raw_json) below. |
|
||||
|
||||
Coyote also searches the tools directory on startup for a `.env` file. If found, all tools in `functions/tools/` will have
|
||||
the environment variables defined in the `.env` file available to them.
|
||||
|
||||
## Reading values via `LLM_TOOL_RAW_JSON`
|
||||
|
||||
Coyote exports the raw JSON envelope it received from the LLM as the `LLM_TOOL_RAW_JSON` environment variable on every
|
||||
tool invocation. Tools can use this to read option values directly from the JSON rather than going through the
|
||||
`argc_*` variables.
|
||||
|
||||
### When to use it
|
||||
|
||||
**Bash tools**: This is the recommended pattern for any option that may carry large multi-line content, code, file
|
||||
contents, or values dense with shell-significant characters (markdown table pipes, single quotes, em-dashes, etc.).
|
||||
Coyote's bash dispatcher converts JSON to shell `--option=<value>` flags via `jq` and `eval`-s the result; for large or
|
||||
special-character values, that shell-quoting round-trip can occasionally drop characters or misalign content before it
|
||||
reaches `argc_*`. Reading from `LLM_TOOL_RAW_JSON` bypasses the shell layer entirely.
|
||||
|
||||
```bash
|
||||
main() {
|
||||
argc_contents="$(jq -r '.contents' <<< "$LLM_TOOL_RAW_JSON")"
|
||||
argc_path="$(jq -r '.path' <<< "$LLM_TOOL_RAW_JSON")"
|
||||
|
||||
# ... rest of your tool logic using $argc_contents and $argc_path
|
||||
}
|
||||
```
|
||||
|
||||
This is the pattern Coyote's bundled `fs_write`, `fs_patch`, `execute_command`, `execute_sql_code`, and `send_mail` tools
|
||||
use for their large-payload options. The argc `# @option --foo!` directives stay in your script so Coyote can build the
|
||||
JSON schema for the LLM, but your `main()` reads from `LLM_TOOL_RAW_JSON` instead of trusting argc's value capture.
|
||||
|
||||
**Python and TypeScript tools**: Coyote's Python and TypeScript dispatchers parse the JSON envelope natively (`json.loads`
|
||||
/ `JSON.parse`) and pass values directly to your `run()` function as native types. They don't go through shell quoting,
|
||||
so the `LLM_*_RAW_JSON` escape hatch that bash tools need doesn't affect them. Declared parameters arrive in your
|
||||
function correctly without needing `LLM_TOOL_RAW_JSON`.
|
||||
|
||||
Python and TypeScript tools may still want to read `LLM_TOOL_RAW_JSON` for other reasons:
|
||||
- Accessing fields the LLM passed that aren't declared in your `run()` signature (telemetry, optional metadata).
|
||||
- Auditing or logging the original LLM-sent JSON verbatim.
|
||||
- Debugging when a value isn't what you expected.
|
||||
|
||||
```python
|
||||
# Python: parse the raw JSON when you need beyond-signature access
|
||||
import json, os
|
||||
payload = json.loads(os.environ["LLM_TOOL_RAW_JSON"])
|
||||
extra_field = payload.get("extra_field")
|
||||
```
|
||||
|
||||
```typescript
|
||||
// TypeScript: parse the raw JSON when you need beyond-signature access
|
||||
const payload = JSON.parse(process.env.LLM_TOOL_RAW_JSON!);
|
||||
const extraField = (payload as Record<string, unknown>).extra_field;
|
||||
```
|
||||
|
||||
### Agent-local tools
|
||||
|
||||
For tools written under `<config_dir>/agents/<agent>/tools.sh` (or `.py` / `.ts`), the same value is exposed as
|
||||
`LLM_AGENT_RAW_JSON`, the raw JSON payload for the agent function call. The semantics are identical; only the variable
|
||||
name differs.
|
||||
|
||||
## Custom Bash-Based Tools
|
||||
To create a Bash-based tool, refer to the [custom bash tools documentation](Custom-Bash-Tools).
|
||||
|
||||
|
||||
+3
-2
@@ -4,8 +4,9 @@ After installation, you can generate the configuration files and directories by
|
||||
coyote --info
|
||||
```
|
||||
|
||||
Then, you need to set up the Coyote vault by creating a vault password file. Coyote will do this for you automatically and
|
||||
guide you through the process when you first attempt to access the vault. So, to get started, you can run:
|
||||
Then, you need to set up the Coyote vault. On your first run, Coyote walks you through choosing a secrets provider
|
||||
(Local, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, gopass, or 1Password) and configuring it. See the
|
||||
[vault documentation](Vault) for the full list of providers and configuration details. To get started, you can run:
|
||||
|
||||
```sh
|
||||
coyote --list-secrets
|
||||
|
||||
+61
@@ -68,6 +68,10 @@ global_tools: # global tools available to nodes
|
||||
- web_search_coyote.sh
|
||||
mcp_servers: # MCP servers available to nodes
|
||||
- pubmed-search
|
||||
skills_enabled: true # optional; master switch for skills in `llm` nodes
|
||||
enabled_skills: # optional; the *universe* of skills referenceable by any llm node
|
||||
- code-review
|
||||
- git-master
|
||||
conversation_starters: # suggested prompts in the UI
|
||||
- "Research WebAssembly outside of the browser"
|
||||
variables: # optional; agent variables
|
||||
@@ -138,6 +142,13 @@ nodes:
|
||||
write in the same super-step (the validator catches missing reducers at
|
||||
load time). See [Parallel Execution](#parallel-execution) for the full
|
||||
reducer reference and merge semantics.
|
||||
- **`skills_enabled` / `enabled_skills`:** Optional [skills](Skills) policy
|
||||
for the graph. `skills_enabled: false` turns skills off for every `llm`
|
||||
node in the graph regardless of node-level settings. `enabled_skills` is
|
||||
the *universe*: the set of skill names any `llm` node may reference in
|
||||
its own `enabled_skills`. Subset-violations are caught by the validator at
|
||||
load time. See [Skills in Graph Agents](Skills#skills-in-graph-agents) for
|
||||
the full per-node story.
|
||||
|
||||
### `{{initial_prompt}}`: Automatically Seeded
|
||||
|
||||
@@ -458,6 +469,51 @@ failure message containing one of: `timed out`, `rate limit`, `429`,
|
||||
other error fails immediately without consuming further attempts. The
|
||||
default is `1` (no retries).
|
||||
|
||||
### Skills on `llm` nodes
|
||||
|
||||
`llm` nodes are the only place [skills](Skills) attach inside a graph. Two
|
||||
optional fields, mirroring the same fields on roles and agents:
|
||||
|
||||
```yaml
|
||||
implement:
|
||||
type: llm
|
||||
prompt: "{{plan_summary}}"
|
||||
tools: [fs_write, fs_patch, fs_cat]
|
||||
skills_enabled: true # optional; default inherits from graph level
|
||||
enabled_skills: # optional per-node restriction; must be a subset
|
||||
- code-review # of graph-level enabled_skills
|
||||
- verification-gates
|
||||
```
|
||||
|
||||
Semantics (discovery-only, no auto-load):
|
||||
|
||||
- **`skills_enabled`:** Per-node override of the skills master switch.
|
||||
`false` disables skills for this node only: no `skill__list` /
|
||||
`skill__load` / `skill__unload` meta-tools are exposed to the model.
|
||||
`true` or omitted inherits the graph-level / agent-level setting.
|
||||
- **`enabled_skills`:** Per-node restriction of which skills the model can
|
||||
see and load. The graph-level `enabled_skills` is the universe; the
|
||||
per-node list must be a subset (the validator catches violations at
|
||||
load time). When set, `skill__list` shows only these skills and
|
||||
`skill__load` only accepts these names.
|
||||
- **The model loads what it needs.** Nothing is auto-loaded. While the
|
||||
node runs, the model uses `skill__list` to discover what's available
|
||||
and `skill__load` to bring a skill's body / tools / MCP servers into
|
||||
scope when it actually needs them. This matches the design intent of
|
||||
skills as a dynamic capability layer.
|
||||
- **Per-node policy isolation.** The node temporarily narrows the active
|
||||
agent's skill policy to the node's `enabled_skills`. The original
|
||||
policy is restored when the node finishes, so subsequent nodes see
|
||||
their own policy. Any skills the model loaded during the node persist
|
||||
into subsequent nodes by default; they're real registry insertions,
|
||||
not node-scoped state. (Use the skill's own `auto_unload: true`
|
||||
frontmatter for skills that should clean up automatically at turn
|
||||
end.)
|
||||
|
||||
Agent nodes (which spawn full sub-agents) intentionally have no
|
||||
`enabled_skills` field. The spawned child agent's own
|
||||
`config.yaml`/`graph.yaml` declares its skill policy.
|
||||
|
||||
---
|
||||
|
||||
## rag
|
||||
@@ -1460,6 +1516,11 @@ A short, honest list of things that bite people:
|
||||
- [`graph.example.yaml`](https://github.com/Dark-Alex-17/coyote/blob/main/graph.example.yaml) - A fully-commented, full-featured reference
|
||||
graph agent at the root of the Coyote repository (every top-level field,
|
||||
every node type).
|
||||
- Built-in graph agents shipped with Coyote:
|
||||
[`coder`](https://github.com/Dark-Alex-17/coyote/blob/main/assets/agents/coder) (implement -> verify_build -> verify_tests -> self_review -> fix-loop),
|
||||
[`deep-research`](https://github.com/Dark-Alex-17/coyote/blob/main/assets/agents/deep-research) (the canonical reference that exercises every node type),
|
||||
[`librarian`](https://github.com/Dark-Alex-17/coyote/blob/main/assets/agents/librarian) (triage -> parallel doc + OSS search -> synthesize -> trim; a compact illustration of static fan-out with reducers).
|
||||
See [Agents > Built-In Agents](Agents#built-in-agents) for descriptions.
|
||||
- [Agents](Agents) - non-graph agent system (config.yaml + LLM loop)
|
||||
- [Custom Tools](Custom-Tools) - building `tools.sh` / `tools.py` /
|
||||
`tools.ts` files for use in graph nodes
|
||||
|
||||
+10
-4
@@ -170,11 +170,16 @@ The following settings are available in the global configuration for MCP servers
|
||||
mcp_server_support: true # Enables or disables MCP server support (globally).
|
||||
mapping_mcp_servers: # Alias for an MCP server or set of servers
|
||||
git: github,gitmcp
|
||||
enabled_mcp_servers: null # Which MCP servers to enable by default (e.g. 'github,slack')
|
||||
enabled_mcp_servers: null # Which MCP servers to enable by default.
|
||||
# Accepts either a YAML list or a comma-separated string. Examples:
|
||||
# enabled_mcp_servers: github,slack
|
||||
# enabled_mcp_servers:
|
||||
# - github
|
||||
# - slack
|
||||
```
|
||||
|
||||
A special note about `enabled_mcp_servers`: a user can set this to `all` to enable all configured MCP servers in the
|
||||
`functions/mcp.json` configuration.
|
||||
A special note about `enabled_mcp_servers`: a user can set this to `all` (or include `all` in the list) to enable all
|
||||
configured MCP servers in the `functions/mcp.json` configuration.
|
||||
|
||||
(See the [Configuration Example](https://github.com/Dark-Alex-17/coyote/blob/main/config.example.yaml) file for an example global configuration with all options.)
|
||||
|
||||
@@ -187,7 +192,8 @@ When running in REPL-mode, the `mcp_server_support` and `enabled_mcp_servers` se
|
||||
When you create a role, you have the following MCP-related configuration options available to you:
|
||||
|
||||
```yaml
|
||||
enabled_mcp_servers: github # Which MCP servers the role uses.
|
||||
enabled_mcp_servers: # Which MCP servers the role uses. Accepts either a YAML list (as shown)
|
||||
- github # or a comma-separated string (e.g. `enabled_mcp_servers: github,slack`).
|
||||
```
|
||||
|
||||
The values for `mapping_mcp_servers` are inherited from the `[global configuration](#global-configuration)`.
|
||||
|
||||
+24
-19
@@ -78,6 +78,9 @@ cannot be persisted to a file and saved.
|
||||
Skills are modular knowledge or capability packs the LLM can load and unload mid-conversation. Multiple skills can be
|
||||
loaded at once; their instructions stack and their tools/MCP servers union with the active role/agent/session.
|
||||
|
||||
> **Requires function calling.** Skills depend on Coyote's function calling system. If `function_calling_support: false`
|
||||
> in your global config, the `.skill load` and `.skill unload` commands refuse, and the model cannot load skills itself.
|
||||
|
||||
| Command | Description |
|
||||
|------------------------|---------------------------------------------------------------------------------------------------|
|
||||
| `.skill loaded` | List currently-loaded skills in this session |
|
||||
@@ -206,25 +209,27 @@ file.
|
||||
|
||||
The following settings can be adjusted at runtime:
|
||||
|
||||
| Setting | Type | Description |
|
||||
|----------------------------|---------|--------------------------------------------------------------------------|
|
||||
| `auto_continue` | boolean | Enable/disable the [Todo System](TODO-System) auto-continuation |
|
||||
| `max_auto_continues` | integer | Maximum number of automatic continuations |
|
||||
| `inject_todo_instructions` | boolean | Inject default todo instructions into the system prompt |
|
||||
| `continuation_prompt` | string | Custom continuation prompt (supports multi-word values; `null` to reset) |
|
||||
| `temperature` | float | Model temperature parameter |
|
||||
| `top_p` | float | Model top-p parameter |
|
||||
| `enabled_tools` | string | Comma-separated list of enabled tools |
|
||||
| `enabled_mcp_servers` | string | Comma-separated list of enabled MCP servers |
|
||||
| `save_session` | boolean | Whether to auto-save sessions |
|
||||
| `compression_threshold` | integer | Token threshold for session compression |
|
||||
| `max_output_tokens` | integer | Maximum output tokens for the current model |
|
||||
| `dry_run` | boolean | Enable/disable dry run mode |
|
||||
| `function_calling_support` | boolean | Enable/disable function calling |
|
||||
| `mcp_server_support` | boolean | Enable/disable MCP server support |
|
||||
| `stream` | boolean | Enable/disable streaming |
|
||||
| `save` | boolean | Enable/disable saving responses |
|
||||
| `highlight` | boolean | Enable/disable syntax highlighting |
|
||||
| Setting | Type | Description |
|
||||
|----------------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `auto_continue` | boolean | Enable/disable the [Todo System](TODO-System) auto-continuation |
|
||||
| `max_auto_continues` | integer | Maximum number of automatic continuations |
|
||||
| `inject_todo_instructions` | boolean | Inject default todo instructions into the system prompt |
|
||||
| `continuation_prompt` | string | Custom continuation prompt (supports multi-word values; `null` to reset) |
|
||||
| `temperature` | float | Model temperature parameter |
|
||||
| `top_p` | float | Model top-p parameter |
|
||||
| `enabled_tools` | string | Comma-separated list of enabled tools (e.g. `fs_ls,fs_cat` or `all`); the saved YAML config also accepts a list form |
|
||||
| `enabled_mcp_servers` | string | Comma-separated list of enabled MCP servers (e.g. `github,slack` or `all`); the saved YAML config also accepts a list form |
|
||||
| `enabled_skills` | string | Comma-separated list of enabled [skills](Skills) (e.g. `git-master,ai-slop-remover`); `null` clears the override |
|
||||
| `save_session` | boolean | Whether to auto-save sessions |
|
||||
| `compression_threshold` | integer | Token threshold for session compression |
|
||||
| `max_output_tokens` | integer | Maximum output tokens for the current model |
|
||||
| `dry_run` | boolean | Enable/disable dry run mode |
|
||||
| `function_calling_support` | boolean | Enable/disable function calling |
|
||||
| `mcp_server_support` | boolean | Enable/disable MCP server support |
|
||||
| `skills_enabled` | boolean | Master switch for [skills](Skills). Accepts `true`, `false`, or `null` (inside a session, `null` clears the session-level override; otherwise resets to the default `true`) |
|
||||
| `stream` | boolean | Enable/disable streaming |
|
||||
| `save` | boolean | Enable/disable saving responses |
|
||||
| `highlight` | boolean | Enable/disable syntax highlighting |
|
||||
|
||||

|
||||
|
||||
|
||||
+4
-2
@@ -56,8 +56,10 @@ The following table lists the available configuration settings and their default
|
||||
| `model` | Default configured model or currently in-use model (REPL mode) | The preferred model to use with this role |
|
||||
| `temperature` | Default `temperature` for the preferred model | Controls the creativity and randomness of the model's responses |
|
||||
| `top_p` | Default `top_p` for the preferred model | Alternative way to control the model's output diversity, affecting the <br>probability distribution of tokens |
|
||||
| `enabled_tools` | Global setting for `enabled_tools` | The tools that this role utilizes |
|
||||
| `enabled_mcp_servers` | Global setting for `enabled_mcp_servers` | The MCP servers that this role utilizes |
|
||||
| `enabled_tools` | Global setting for `enabled_tools` | The tools that this role utilizes. Accepts either a YAML list or a comma-separated string |
|
||||
| `enabled_mcp_servers` | Global setting for `enabled_mcp_servers` | The MCP servers that this role utilizes. Accepts either a YAML list or a comma-separated string |
|
||||
| `skills_enabled` | Global setting for `skills_enabled` | Master switch for [skills](Skills) under this role. Set to `false` to hide all skills |
|
||||
| `enabled_skills` | Global setting for `enabled_skills` | The [skills](Skills) this role activates. Accepts either a YAML list or a comma-separated string |
|
||||
| `auto_continue` | Global setting for `auto_continue` | Enable the [Todo System](TODO-System) auto-continuation for this role |
|
||||
| `max_auto_continues` | Global setting for `max_auto_continues` | Maximum number of automatic continuations before stopping |
|
||||
| `inject_todo_instructions` | Global setting for `inject_todo_instructions` | Inject default todo tool usage instructions into the system prompt |
|
||||
|
||||
+8
-6
@@ -117,11 +117,13 @@ The following settings are available to customize the default behavior of sessio
|
||||
In addition to the global settings above, individual sessions can override the following settings. These can be set
|
||||
at runtime using the `.set` command or configured in the session's YAML file:
|
||||
|
||||
| Setting | Default | Description |
|
||||
|----------------------------|-----------------------------------------|---------------------------------------------------------------------------------------------------------------|
|
||||
| `auto_continue` | Global `auto_continue` value | Enable the [Todo System](TODO-System) auto-continuation for this session. Overrides global and role settings. |
|
||||
| `max_auto_continues` | Global `max_auto_continues` value | Maximum number of automatic continuations before stopping. Overrides global and role settings. |
|
||||
| `inject_todo_instructions` | Global `inject_todo_instructions` value | Inject default todo tool usage instructions into the system prompt. Overrides global and role settings. |
|
||||
| `continuation_prompt` | Global `continuation_prompt` value | Custom prompt used when auto-continuing. Overrides global and role settings. |
|
||||
| Setting | Default | Description |
|
||||
|----------------------------|-----------------------------------------|---------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `auto_continue` | Global `auto_continue` value | Enable the [Todo System](TODO-System) auto-continuation for this session. Overrides global and role settings. |
|
||||
| `max_auto_continues` | Global `max_auto_continues` value | Maximum number of automatic continuations before stopping. Overrides global and role settings. |
|
||||
| `inject_todo_instructions` | Global `inject_todo_instructions` value | Inject default todo tool usage instructions into the system prompt. Overrides global and role settings. |
|
||||
| `continuation_prompt` | Global `continuation_prompt` value | Custom prompt used when auto-continuing. Overrides global and role settings. |
|
||||
| `skills_enabled` | Global `skills_enabled` value | Master switch for [skills](Skills) in this session. `.set skills_enabled null` clears the session override. |
|
||||
| `enabled_skills` | Inherited via the skill cascade | The active [skills](Skills) for this session. Accepts either a YAML list or a comma-separated string in the saved session YAML. |
|
||||
|
||||
For more information on the Todo System, see the [Todo System documentation](TODO-System).
|
||||
|
||||
@@ -197,7 +197,8 @@ MCP server entries (and other config files) can reference vault secrets with `{{
|
||||
install completes, Coyote scans the resulting `mcp.json` for placeholders that are not yet in your vault and either:
|
||||
|
||||
- **In a TTY:** prompts you, one secret at a time, whether to add it to the vault now. On the first "Yes", Coyote
|
||||
initializes the vault password file (if needed). On "No", the secret is deferred and reported at the end.
|
||||
initializes the vault (if needed; for the Local provider, this just means creating the password file). On "No", the
|
||||
secret is deferred and reported at the end.
|
||||
- **In a non-TTY environment:** skips prompts entirely; lists every missing secret in a final reminder block, with the
|
||||
commands you can run later (`coyote --add-secret <NAME>` or `.vault add <NAME>`).
|
||||
|
||||
@@ -249,8 +250,8 @@ asset type plus a `README` describing the customization workflow.
|
||||
|
||||
The following are **intentionally** outside the install-remote feature's scope:
|
||||
|
||||
- **`config.yaml`** (the global Coyote config). It holds user-specific things like editor preference, vault password file
|
||||
path, client API keys, OAuth tokens, and similar. Merging a shared `config.yaml` would risk breaking auth or routing
|
||||
- **`config.yaml`** (the global Coyote config). It holds user-specific things like editor preference, secrets provider
|
||||
configuration, client API keys, OAuth tokens, and similar. Merging a shared `config.yaml` would risk breaking auth or routing
|
||||
traffic somewhere unexpected. Settings that genuinely benefit from sharing (like default models) already belong
|
||||
inside individual agents' or roles' configs.
|
||||
- **Sessions, RAGs, agent runtime data.** These accumulate as you use Coyote and aren't shareable in a meaningful way.
|
||||
|
||||
+131
-24
@@ -11,6 +11,13 @@ Common uses:
|
||||
- **Toolkit unlocks** that grant a small bundle of tools or MCP servers without changing the active role.
|
||||
- **One-shot helpers** that auto-unload after the model finishes a task (see [auto_unload](#auto-unload)).
|
||||
|
||||
> **Prerequisite: function calling must be enabled.** Skills are built on top of Coyote's function calling system so as
|
||||
> to not overwhelm the context with skill descriptions. `skill__list`, `skill__load`, and `skill__unload` meta-tools are
|
||||
> themselves function calls, and most skills grant tools the model needs to invoke. If your global config has
|
||||
> `function_calling_support: false`, the entire skills system is disabled: the meta-tools are not registered, the REPL
|
||||
> `.skill load` command refuses, and `coyote --skill <name>` exits with a clear error. See [Function Calling](Tools) to
|
||||
> enable it.
|
||||
|
||||
---
|
||||
|
||||
# Skill Definition
|
||||
@@ -61,12 +68,12 @@ To see complete examples, look at the [bundled built-in skills](https://github.c
|
||||
The YAML frontmatter at the top of `SKILL.md` is where you declare the skill's metadata and what extra capabilities it
|
||||
grants when loaded. All fields are optional.
|
||||
|
||||
| Field | Default | Description |
|
||||
|-----------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `description` | empty | Short one-line description shown to the model when it lists available skills. Make it specific: this is what helps the model decide when to load. |
|
||||
| `enabled_tools` | none | Comma-separated tool names that become available while the skill is loaded. Union with the active role/agent/session's tools. Tools must exist in [visible tools](Tools). |
|
||||
| `enabled_mcp_servers` | none | Comma-separated MCP server names. Skills can reference servers from your `mcp.json`; those servers are auto-acquired on load and released on unload via reference counting. |
|
||||
| `auto_unload` | `false` | If `true`, the skill is automatically removed from the registry at the end of every turn where the model produced a final response (no more tool calls). |
|
||||
| Field | Default | Description |
|
||||
|-----------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `description` | empty | Short one-line description shown to the model when it lists available skills. Make it specific: this is what helps the model decide when to load. |
|
||||
| `enabled_tools` | none | Comma-separated tool names that become available while the skill is loaded. Union with the active role/agent/session's tools. Tools must exist in [visible tools](Tools). |
|
||||
| `enabled_mcp_servers` | none | MCP server names the skill needs. Accepts a YAML list (preferred) or a comma-separated string. Skills can reference servers from your `mcp.json`; those servers are auto-acquired on load and released on unload via reference counting. |
|
||||
| `auto_unload` | `false` | If `true`, the skill is automatically removed from the registry at the end of every turn where the model produced a final response (no more tool calls). |
|
||||
|
||||
## Body
|
||||
|
||||
@@ -99,16 +106,18 @@ your current context with git knowledge, frontend conventions, or any other modu
|
||||
|
||||
## REPL commands
|
||||
|
||||
| Command | Effect |
|
||||
|---------------------------------------------|--------------------------------------------------------------------------------------------------|
|
||||
| `.skill loaded` | List currently-loaded skills in this session. |
|
||||
| `.skill load <name>` | Load a skill. Validates policy + compatibility, then refreshes the tool scope. |
|
||||
| `.skill unload <name>` | Unload a loaded skill. Releases any MCP servers it pulled in. |
|
||||
| `.edit skill <name>` | Open an existing skill in `$EDITOR` (fails if the skill doesn't exist). |
|
||||
| `.skill <name>` | Open the skill in `$EDITOR`. Creates a scaffolded `SKILL.md` if missing. |
|
||||
| `.delete skill` | Interactive prompt to choose installed skills to delete. |
|
||||
| `.install skills` | Reinstall all bundled built-in skills, overwriting existing copies after confirmation. |
|
||||
| `.install remote <git-url> --filter skills` | Install only `skills/` from a remote repo. See [Sharing Configurations](Sharing-Configurations). |
|
||||
| Command | Effect |
|
||||
|---------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `.skill loaded` | List currently-loaded skills in this session. |
|
||||
| `.skill load <name>` | Load a skill. Validates policy + compatibility, then refreshes the tool scope. |
|
||||
| `.skill unload <name>` | Unload a loaded skill. Releases any MCP servers it pulled in. |
|
||||
| `.edit skill <name>` | Open an existing skill in `$EDITOR` (fails if the skill doesn't exist). |
|
||||
| `.skill <name>` | Open the skill in `$EDITOR`. Creates a scaffolded `SKILL.md` if missing. |
|
||||
| `.delete skill` | Interactive prompt to choose installed skills to delete. |
|
||||
| `.install skills` | Reinstall all bundled built-in skills, overwriting existing copies after confirmation. |
|
||||
| `.install remote <git-url> --filter skills` | Install only `skills/` from a remote repo. See [Sharing Configurations](Sharing-Configurations). |
|
||||
| `.set skills_enabled <true\|false\|null>` | Flip the master switch at runtime. Inside a session this sets the session override; otherwise it updates the global default (`null` restores the default `true`). |
|
||||
| `.set enabled_skills <csv\|null>` | Replace the global default-active skill list at runtime, e.g. `.set enabled_skills git-master,ai-slop-remover`. Use `null` to clear. |
|
||||
|
||||
## CLI flags
|
||||
|
||||
@@ -165,23 +174,119 @@ Example role that opts into a different skill set than the global default:
|
||||
```markdown
|
||||
---
|
||||
name: frontend-dev
|
||||
enabled_skills: git-master, ai-slop-remover, frontend-ui-ux
|
||||
enabled_skills:
|
||||
- git-master
|
||||
- ai-slop-remover
|
||||
- frontend-ui-ux
|
||||
---
|
||||
You are a frontend specialist.
|
||||
```
|
||||
|
||||
`enabled_skills` accepts either a YAML list (as shown above) or a comma-separated string
|
||||
(`enabled_skills: git-master,ai-slop-remover,frontend-ui-ux`). Both forms parse to the same value.
|
||||
|
||||
When this role is active, the model sees exactly those three skills in `skill__list`. Any other installed skill is
|
||||
filtered out of the response and cannot be loaded. `skill__load` rejects it with `"Skill 'X' is not enabled in this context"`.
|
||||
|
||||
## Validation
|
||||
|
||||
Three validation points enforce these rules:
|
||||
Four validation points enforce these rules:
|
||||
|
||||
| Where | What is checked | Severity |
|
||||
|------------------------|---------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------|
|
||||
| **Config load** | Every name in any `enabled_skills` exists on disk AND is in global `visible_skills` (when `visible_skills` is set). | **Hard error**. Refuses to start with the offending name + level. |
|
||||
| **Runtime list/build** | For each enabled skill: declared tools require function calling; declared MCPs require MCP support. | **Filter + warn**. Incompatible skills are dropped from `skill__list`. |
|
||||
| **`skill__load` call** | Skill is in the effective enabled set AND compatible with current feature flags. | **Tool error to model** with a distinct message per failure mode. |
|
||||
| Where | What is checked | Severity |
|
||||
|-------------------------|---------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
|
||||
| **Global feature gate** | `function_calling_support: true` is required for skills to exist at all (registry/tools/REPL/CLI). | **Skills are silently disabled**. No `skill__*` tools, `.skill load` and `--skill` refuse. |
|
||||
| **Config load** | Every name in any `enabled_skills` exists on disk and is in global `visible_skills` (when `visible_skills` is set). | **Hard error**. Refuses to start with the offending name + level. |
|
||||
| **Runtime list/build** | For each enabled skill: declared MCPs require MCP support. | **Filter + warn**. Incompatible skills are dropped from `skill__list`. |
|
||||
| **`skill__load` call** | Skill is in the effective enabled set and its declared MCPs are usable. | **Tool error to model** with a distinct message per failure mode. |
|
||||
|
||||
> **Skills require function calling.** Skills are useless without it. The meta-tools (`skill__list`, `skill__load`,
|
||||
> `skill__unload`) are themselves function calls, and most skills grant tools the model needs to invoke. If
|
||||
> `function_calling_support: false` in your global config, the entire skills feature is off. The REPL `.skill load`
|
||||
> command, the CLI `--skill <name>` flag, and the model's own `skill__load` invocation all refuse with a clear error.
|
||||
|
||||
---
|
||||
|
||||
# Skills in Graph Agents
|
||||
|
||||
[Graph agents](Graph-Agents) integrate with the skill system at two levels: a graph-wide *universe* set at the top of
|
||||
`graph.yaml`, and a per-node *auto-load* set on `llm` nodes. Other node types (script, approval, input, rag, map, end)
|
||||
have no skill surface. Skills only apply where a model call happens.
|
||||
|
||||
## Graph level — the universe
|
||||
|
||||
The top-level `skills_enabled` and `enabled_skills` fields in `graph.yaml` work just like they do on a normal agent's
|
||||
`config.yaml`: they declare the policy ceiling for the whole graph.
|
||||
|
||||
```yaml
|
||||
name: coder
|
||||
skills_enabled: true
|
||||
enabled_skills:
|
||||
- code-review
|
||||
- git-master
|
||||
- verification-gates
|
||||
```
|
||||
|
||||
- **`skills_enabled: false`** at the graph level turns skills off for every `llm` node in the graph regardless of
|
||||
node-level fields. Equivalent to "skills don't exist for this graph."
|
||||
- **`enabled_skills`** at the graph level is the *universe*: the set of skill names any `llm` node in the graph is
|
||||
allowed to reference in its own `enabled_skills`. The validator rejects per-node entries that aren't in this set at
|
||||
load time.
|
||||
- Omitting `enabled_skills` inherits whatever the role / global cascade resolves to (typically "all visible").
|
||||
|
||||
## Per-node: discovery-only on `llm` nodes
|
||||
|
||||
`llm` nodes can independently declare `skills_enabled` and `enabled_skills`. The graph-level field gates what's
|
||||
*allowed* in the graph at all; the node-level field narrows that universe to what *this* node's model can see and
|
||||
load. Nothing is auto-loaded. The model uses `skill__list` and `skill__load` to bring skills in as it needs them,
|
||||
matching the rest of Coyote's skill model.
|
||||
|
||||
```yaml
|
||||
nodes:
|
||||
implement:
|
||||
type: llm
|
||||
prompt: "{{plan_summary}}"
|
||||
tools: [fs_write, fs_patch, fs_cat]
|
||||
enabled_skills: # must be a subset of graph-level enabled_skills
|
||||
- code-review
|
||||
- verification-gates
|
||||
# skills_enabled: false # would disable skills for this node only
|
||||
```
|
||||
|
||||
Semantics:
|
||||
|
||||
- **Discovery, not auto-load.** When the node runs, `skill__list` is exposed to the model along with `skill__load`
|
||||
and `skill__unload`. The model decides which skills (if any) to load based on the task at hand. Loaded skills
|
||||
compose into the system prompt and union tools/MCP servers via the standard `effective_role` pipeline; the
|
||||
loading itself goes through the same `refresh_tool_scope` path as `.skill load` in the REPL.
|
||||
- **Per-node policy.** When `enabled_skills` is set on the node, the graph's effective skill policy is temporarily
|
||||
narrowed to that subset for the duration of the node. `skill__list` will only show those skills and `skill__load`
|
||||
will only accept those names. When the node finishes, the original policy is restored so the next node sees its
|
||||
own.
|
||||
- **`skills_enabled: false`** at the node level is an off-switch: no `skill__*` meta-tools are exposed and no skills
|
||||
can be loaded from this node. Useful when one node in an otherwise skill-enabled graph should run with a strict,
|
||||
narrow context (for example a structured-output extraction step that shouldn't load editorial conventions).
|
||||
- **Skill state carry-over.** Skills the model loads during a node persist into subsequent nodes (they're real
|
||||
registry insertions, not node-scoped state). If you want a skill to clean up automatically at turn end, mark it
|
||||
`auto_unload: true` in the skill's own frontmatter.
|
||||
|
||||
## Agent nodes have no skill surface
|
||||
|
||||
`agent` nodes spawn a full child agent (graph or normal). That child agent declares its own skill policy in its own
|
||||
`config.yaml` / `graph.yaml`. Adding a skill field on the agent node would be redundant at best and surprising at
|
||||
worst. The child author already controls what skills the child uses. So agent nodes intentionally don't accept
|
||||
`enabled_skills` / `skills_enabled`.
|
||||
|
||||
## Validation
|
||||
|
||||
| Where | Check | Severity |
|
||||
|----------------|------------------------------------------------------------------------------------------------------------|------------|
|
||||
| Graph load | Per-node `enabled_skills` entries are non-empty strings | Hard error |
|
||||
| Graph load | Per-node `enabled_skills` are a subset of graph-level `enabled_skills` (when set) | Hard error |
|
||||
| Node execution | Each auto-loaded skill resolves to a real installed skill (otherwise that skill is skipped with a warning) | Warn |
|
||||
| Node execution | Each auto-loaded skill's MCP / function-calling requirements are satisfied | Warn |
|
||||
|
||||
The validator only runs `validate_before_run` (the default); inspecting a graph via `coyote --info -a <name>` does
|
||||
not trigger validation, so subset-violation errors surface on the first real run.
|
||||
|
||||
---
|
||||
|
||||
@@ -274,4 +379,6 @@ When skills are enabled, the model has three tools available for managing them i
|
||||
| `skill__unload` | `name: string` | Unloads a loaded skill. Releases its MCP server handles. |
|
||||
|
||||
These are how the model discovers and uses skills mid-conversation. You can disable this discovery channel by setting
|
||||
`skills_enabled: false` at any level; the three tools then disappear from the model's function list entirely.
|
||||
`skills_enabled: false` at any level; the three tools then disappear from the model's function list entirely. The same
|
||||
happens if `function_calling_support: false` is set globally. Without function calling, the skill system has no surface
|
||||
to operate on, so it is silently disabled across REPL, CLI, agents, and the model's tool list.
|
||||
|
||||
+9
-3
@@ -74,7 +74,12 @@ The following settings are available in the global configuration for tools:
|
||||
function_calling_support: true # Enables or disables function calling in any context
|
||||
mapping_tools: # Alias for a tool or toolset
|
||||
fs: 'fs_cat,fs_ls,fs_mkdir,fs_rm,fs_write'
|
||||
enabled_tools: null # Which tools to use by default. (e.g. 'fs,web_search_coyote')
|
||||
enabled_tools: null # Which tools to use by default.
|
||||
# Accepts either a YAML list or a comma-separated string. Examples:
|
||||
# enabled_tools: fs,web_search_coyote
|
||||
# enabled_tools:
|
||||
# - fs
|
||||
# - web_search_coyote
|
||||
visible_tools: # Which tools are visible to be compiled (and are thus able to be defined in 'enabled_tools')
|
||||
# - demo_py.py
|
||||
- execute_command.sh
|
||||
@@ -113,7 +118,8 @@ context.
|
||||
When you create a role, you have the following global tool-related configuration options available to you:
|
||||
|
||||
```yaml
|
||||
enabled_tools: query_jira_issues # Which tools the role uses.
|
||||
enabled_tools: # Which tools the role uses. Accepts a YAML list (as shown)
|
||||
- web_search_coyote # or a comma-separated string (e.g. `enabled_tools: web_search_coyote`).
|
||||
```
|
||||
|
||||
The values for `mapping_tools` are inherited from the [global configuration](#global-configuration).
|
||||
@@ -125,7 +131,7 @@ When you create an agent, you have the following global tool-related configurati
|
||||
|
||||
```yaml
|
||||
global_tools: # Which global tools the agent uses
|
||||
- query_jira_issues.sh
|
||||
- web_search_coyote.sh
|
||||
- fs_cat.sh
|
||||
- fs_ls.sh
|
||||
```
|
||||
|
||||
+112
-27
@@ -1,15 +1,17 @@
|
||||
The Coyote vault lets users store sensitive secrets and credentials securely so that there's no plaintext secrets
|
||||
anywhere in your configurations.
|
||||
anywhere in your configurations.
|
||||
|
||||
It's based on the [G-Man library](https://github.com/Dark-Alex-17/gman) (which also comes in a binary format) which
|
||||
functions as a universal secret management tool.
|
||||
It's built on the [G-Man library](https://github.com/Dark-Alex-17/gman), which supports multiple secrets providers:
|
||||
a local encrypted file, AWS Secrets Manager, Google Cloud Secret Manager, Azure Key Vault, gopass, and 1Password. You
|
||||
pick the one that fits your workflow and Coyote handles the rest.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
# Usage
|
||||
The Coyote vault can be used in one of two ways: via the CLI or via the REPL for interactive usage.
|
||||
The Coyote vault can be used in one of two ways: via the CLI or via the REPL for interactive usage. The same commands
|
||||
work regardless of which provider you've configured.
|
||||
|
||||
## CLI Usage
|
||||
The vault is utilized from the CLI with the following flags:
|
||||
@@ -33,6 +35,94 @@ The vault can be accessed from within the Coyote REPL using the `.vault` command
|
||||
|
||||
The manipulation of your vault is guided in the same way as the CLI usage, ensuring ease of use.
|
||||
|
||||
# Supported Providers
|
||||
Coyote supports six secrets providers via [G-Man](https://github.com/Dark-Alex-17/gman). The default is **Local** (an encrypted file on this machine), but you
|
||||
can switch to any of the others.
|
||||
|
||||
| Provider | Storage | What it needs |
|
||||
|-----------------------|---------------------------------------------------------------|---------------------------------------------------------------|
|
||||
| `local` (default) | Encrypted file at `vault.yml` in your Coyote config directory | A password file you create on first run |
|
||||
| `aws_secrets_manager` | AWS Secrets Manager | An authenticated AWS CLI (`aws sso login` or `aws configure`) |
|
||||
| `gcp_secret_manager` | Google Cloud Secret Manager | `gcloud auth application-default login` |
|
||||
| `azure_key_vault` | Azure Key Vault | `az login` |
|
||||
| `gopass` | The `gopass` password manager | The `gopass` CLI installed and initialized |
|
||||
| `one_password` | 1Password | The `op` CLI installed and signed in (`op signin`) |
|
||||
|
||||
If you're not logged into the relevant CLI when Coyote needs to read a secret, you'll get an auth error with the
|
||||
canonical login command. Coyote does not try to log you in automatically.
|
||||
|
||||
# Configuration
|
||||
There are two ways to configure your secrets provider in `config.yaml`:
|
||||
|
||||
## Shorthand: `vault_password_file`
|
||||
If all you want is the default Local provider, just set the path to a password file:
|
||||
|
||||
```yaml
|
||||
vault_password_file: ~/.coyote_password
|
||||
```
|
||||
|
||||
This is shorthand for "use the Local provider with this password file". It's the simplest setup if you don't
|
||||
want to use a dedicated secrets provider external to Coyote.
|
||||
|
||||
## Explicit: `secrets_provider`
|
||||
For any non-Local provider (or if you want to be explicit about your Local setup), use the `secrets_provider` block:
|
||||
|
||||
```yaml
|
||||
# Local
|
||||
secrets_provider:
|
||||
type: local
|
||||
password_file: ~/.coyote_password
|
||||
|
||||
# AWS Secrets Manager
|
||||
secrets_provider:
|
||||
type: aws_secrets_manager
|
||||
aws_profile: default
|
||||
aws_region: us-east-1
|
||||
|
||||
# Google Cloud Secret Manager
|
||||
secrets_provider:
|
||||
type: gcp_secret_manager
|
||||
gcp_project_id: my-project-id
|
||||
|
||||
# Azure Key Vault
|
||||
secrets_provider:
|
||||
type: azure_key_vault
|
||||
vault_name: my-vault-name
|
||||
|
||||
# gopass
|
||||
secrets_provider:
|
||||
type: gopass
|
||||
store: my-store # Optional; omit to use the default store
|
||||
|
||||
# 1Password
|
||||
secrets_provider:
|
||||
type: one_password
|
||||
vault: Production # Optional; omit to use the default vault
|
||||
account: my.1password.com # Optional; omit to use the default account
|
||||
```
|
||||
|
||||
When `secrets_provider` is set, the legacy `vault_password_file` field is ignored.
|
||||
|
||||
> ⚠️ **Important:** The `secrets_provider` block itself cannot use `{{SECRET_NAME}}` interpolation. Coyote needs to
|
||||
> initialize the vault *before* it can resolve any secrets, so the provider's own configuration must be literal values.
|
||||
> All *other* fields in your config (API keys, MCP server env vars, agent variables, etc.) support `{{SECRET_NAME}}`
|
||||
> references as normal.
|
||||
|
||||
# First-Run Setup
|
||||
The first time you start Coyote without a config file, a wizard walks you through picking a secrets provider:
|
||||
|
||||
1. Choose a provider from the menu (Local, AWS, GCP, Azure, gopass, 1Password).
|
||||
2. Coyote prompts you for the provider-specific config (AWS profile/region, GCP project ID, Azure vault name, etc.).
|
||||
3. For non-Local providers, Coyote performs a **round-trip validation**: it writes a probe secret to the backend, reads
|
||||
it back, then deletes it. If your credentials don't have the right permissions, or if you're not logged in, Coyote
|
||||
bails out *before* you fill out the rest of the wizard, with a hint pointing to the correct login command.
|
||||
4. For the Local provider, Coyote prompts you to create a password file.
|
||||
|
||||
Once the provider is set up, the wizard continues with your LLM/API provider selection and writes your `config.yaml`.
|
||||
|
||||
If you set up Coyote with one provider and later want to switch, just edit your `config.yaml` to change (or add) the
|
||||
`secrets_provider` block.
|
||||
|
||||
# Motivation
|
||||
Coyote is intended to be highly configurable and adaptable to many different use cases. This means that users of Coyote
|
||||
should be able to share configurations for agents, tools, roles, etc. with other users or even entire teams.
|
||||
@@ -40,29 +130,24 @@ should be able to share configurations for agents, tools, roles, etc. with other
|
||||
My objective is to encourage this, and to make it so that users can easily version their configurations using version
|
||||
control. Good VCS hygiene dictates that one *never* commits secrets or sensitive information to a repository.
|
||||
|
||||
Since a number of files and configurations in Coyote may contain sensitive information, the vault exists to solve this problem.
|
||||
Since a number of files and configurations in Coyote may contain sensitive information, the vault exists to solve this
|
||||
problem. How you share secrets across a team depends on your provider:
|
||||
|
||||
Users can either share the vault password with a team, making it so a single configuration can be pulled from VCS and used
|
||||
by said team. Alternatively, each user can maintain their own vault password and expect other users to replace secret values
|
||||
with their user-specific secrets.
|
||||
- **Local:** Either share the vault password with the team (one config + one shared password file) or have each user
|
||||
maintain their own password and substitute their own secret values.
|
||||
- **AWS / GCP / Azure / gopass / 1Password:** Each team member uses their own credentials against the shared backend.
|
||||
The vault becomes a natural single source of truth. Rotating a secret in one place propagates to everyone using
|
||||
that config.
|
||||
|
||||
# How it works
|
||||
When you first start Coyote, if you don't already have a vault password file, it will prompt you to create one. This file
|
||||
houses the password that is used to encrypt and decrypt secrets within Coyote. This file exists so that you are not prompted
|
||||
for a password every time Coyote attempts to decrypt a secret.
|
||||
|
||||
When you encrypt a secret, it uses the local provider for `gman` to securely store those secrets in the Coyote vault file.
|
||||
This file is typically located at your Coyote configuration directory under `vault.yml`. If you open this file, you'll see a
|
||||
bunch of gibberish. This is because all secrets are encrypted using the password you provided, meaning only you can decrypt them.
|
||||
|
||||
Secrets are specified in Coyote configurations using the same variable templating as the [Jinja templating engine](https://jinja.palletsprojects.com/en/stable/):
|
||||
# Referencing Secrets
|
||||
Secrets are referenced in Coyote configurations using the same variable templating as the [Jinja templating engine](https://jinja.palletsprojects.com/en/stable/):
|
||||
|
||||
```
|
||||
{{some_variable}}
|
||||
```
|
||||
|
||||
So whenever you want Coyote to use a secret from the vault, you simply specify the secret name in this format in the applicable
|
||||
file.
|
||||
So whenever you want Coyote to use a secret from the vault, you simply specify the secret name in this format in the
|
||||
applicable file. The same syntax works regardless of which provider stores the secret.
|
||||
|
||||
**Example:**
|
||||
Suppose my vault has a secret called `GITHUB_TOKEN` in it, and I want to use that in the MCP configuration. Then, I simply replace
|
||||
@@ -93,16 +178,17 @@ the expected value in my `mcp.json` with the templated secret:
|
||||
}
|
||||
```
|
||||
|
||||
At runtime, Coyote will detect the templated secret and replace it with the decrypted value from the vault before executing.
|
||||
At runtime, Coyote will detect the templated secret and replace it with the decrypted value from the vault before
|
||||
executing.
|
||||
|
||||
# Supported Files
|
||||
At the time of writing, the following files support Coyote secret injection:
|
||||
|
||||
| File Type | Description | Limitations |
|
||||
|-------------------------|-----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `config.yaml` | The main Coyote configuration file | Cannot use secret injection on the `vault_password_file` field |
|
||||
| `functions/mcp.json` | The MCP server configuration file | |
|
||||
| `<agent>/tools.<py/sh>` | Tool files for agents | Specific configuration and only supported for Agents, not all global tools ([see below](#environment-variable-secret-injection-in-agents)) |
|
||||
| File Type | Description | Limitations |
|
||||
|-------------------------|------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `config.yaml` | The main Coyote configuration file | Cannot use secret injection on the `vault_password_file` field or anywhere inside the `secrets_provider` block |
|
||||
| `functions/mcp.json` | The MCP server configuration file | |
|
||||
| `<agent>/tools.<py/sh>` | Tool files for agents | Specific configuration and only supported for Agents, not all global tools ([see below](#environment-variable-secret-injection-in-agents)) |
|
||||
|
||||
|
||||
Note that all paths are relative to the Coyote configuration directory. The directory varies by system, so you can find yours by
|
||||
@@ -146,4 +232,3 @@ follows:
|
||||
```
|
||||
|
||||
For more information about variable usage within agents, refer to the [Variables section](Agents#user-defined-variables) of the [Agents documentation](Agents)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user