Skip to main content
Response Agents as Code lets you define and manage your response agents as YAML files in your own GitHub repository. Cotool polls the repo on a schedule, reconciles the files into your agents, and records every synced change in the agent’s version history. This gives you a GitOps workflow for agents: code review on pull requests, change history in Git, and the ability to roll out the same agents across environments by editing files instead of clicking through the UI.
Managed vs. manual agents: An agent synced from a repo is engine-owned — it is read-only in the Cotool UI. You change a managed agent by editing its YAML and letting the next sync apply it. Agents created in the UI are unaffected; the two can coexist in the same organization.

How It Works

1

You keep agent YAML in a Git repo

Each agent is one YAML file in a folder you choose (default cotool/agents). The file describes the whole agent: prompt, model, tools, skills, inputs, triggers, and more.
2

Cotool polls and reconciles

Roughly every 5 minutes, Cotool fetches the configured branch and makes your agents match the files — creating new agents, updating changed ones, and removing agents whose file disappeared. A commit-SHA check skips the fetch entirely when the branch hasn’t moved.
3

Every change is versioned

Each synced create or update appends an entry to the agent’s version history, tagged with the source commit. An unchanged re-sync adds nothing.

Setup

Response Agents as Code syncs from the GitHub integration. Connect GitHub first, then configure the sync.
1

Connect GitHub

Go to Settings > Integrations and connect GitHub. Install the GitHub App on the organization and repositories you want to sync from.
2

Open the Response Agents as Code card

On the GitHub tool page, find the Response Agents as Code card. (You need the tool.manage permission to configure it.)
3

Choose repo, branch, and paths

Pick the repository, branch, and the folder that contains your agent YAML files. The dropdowns are populated from the repos the GitHub App can access. The agents path defaults to cotool/agents, and the skills path (the folder scanned for SKILL.md definitions) defaults to cotool/skills.
4

Enable and save

Turn on Enable sync and click Save config. While enabled, the periodic sync includes your organization; while off, it skips it.
5

Sync now (optional)

Click Sync now to run a sync immediately instead of waiting for the next cycle. The card shows last-sync status and a per-file table with the synced agent or any error.

File Format

Each file defines one response agent. The format is intentionally distinct from the UI’s bulk export/import format — here it’s one agent per file, with the system prompt either inline or in a separate .md file.

Top-level fields

metadata

spec

The YAML is strict — unknown or misspelled keys (for example syncKey instead of sync_key) are rejected with a validation error for that file rather than being silently ignored.

Identity

Each file maps to exactly one agent. Cotool decides which agent a file represents by:
  1. metadata.sync_key — an author-chosen identity, unique within your organization. Recommended. Because identity is independent of file content and location, you can rename the agent or move/rename the file without churning the agent or losing its triggers and history.
  2. File path — used when no sync_key is set. Renaming or moving the file then changes its identity (Cotool treats it as a new agent).
Set a sync_key for any agent you expect to rename or reorganize. It’s the only way to keep a webhook trigger’s minted secret and URL stable across a rename.

Triggers

Declare triggers inline under spec.triggers. The sync engine materializes them as engine-owned, read-only triggers on the agent. Supported sources: cron, webhook, jira, jira-automation, slack, linear, and email.
Secrets never live in YAML. Webhook secrets and inbound URLs are minted server-side; Slack and Linear bind to your organization’s connected integration at sync time. If the required integration isn’t connected, that trigger is skipped with a diagnostic — the agent itself still syncs, and the file’s status shows integration not connected rather than failing.
A trigger’s sync_key is its stable identity within the agent (it defaults to a slug of name). Set it explicitly to rename a trigger without recreating its row — which, for webhook-family triggers, is what preserves the minted secret and URL. For the full set of fields per source, see Triggering Agents.

Agent-to-Agent References

A YAML agent can call another managed agent as a tool by referencing its sync_key:
References are resolved in a second pass after all agents exist, so forward references work — the order of files doesn’t matter. Always reference other agents via agentTools and sync_key, not via toolNames.

Skills

An agent references skills by name. It never defines a skill inline — skills are first-class resources whose lifecycle is independent of any one agent:
A referenced skill resolves to a skill that exists in your organization — either one created in the UI, or one defined as code in a SKILL.md synced from your repo (below). A reference that doesn’t resolve is skipped with a note (it does not fail the agent), so the agent still loads with the skills that resolve and the rest of its spec. References track each skill’s latest version.

Defining skills as code

Skills live in their own directory in the repo — the skills path (skills_path, default cotool/skills), configured separately from the agents path. Each skill is a directory containing a standard SKILL.md — YAML frontmatter (name, description, allowed-tools) plus a Markdown body — with any sibling references/, assets/, and scripts/ files synced too:
Cotool discovers each SKILL.md on its own — a skill exists because its file exists, not because an agent points at it. The skill’s name (from frontmatter) is its identity, so:
  • The skill is engine-managed: Cotool creates it and records a new version only when its content actually changes (a no-op re-sync adds none). Like a managed agent, it’s read-only in the UI — edit or remove it by changing its SKILL.md, not from the Skills page.
  • Any number of agents can reference it by name — and so can an agent managed in the UI. The skill is a single org-wide resource; referencing it never copies it.
  • If the SKILL.md is removed, the skill is gently soft-deleted (and resurrected if the file returns), just like agents. Because lifecycle tracks the file, retiring an agent that referenced the skill never deletes it — other agents keep working.
  • Two SKILL.md files with the same name are a conflict: both are flagged and neither is synced (names must be unique across the repo).
  • If the name collides with an existing non-managed organization skill created in the UI, the SKILL.md skill is not synced — Cotool never overwrites it. The collision is flagged on the skill file’s sync status. Personal skills use a separate per-user namespace and can share the managed skill’s name.
A skill’s required tools (its SKILL.md allowed-tools) must also be granted to every agent that attaches it, via toolNames (or cliNames). In the example above an agent attaching triage-runbook therefore needs that skill’s tools in its toolNames. If a required tool is missing, that one agent file syncs with a schema_error and is skipped — the rest of your agents still reconcile.
Skills round-trip through export cleanly, since agents carry only name references.

Reconcile Behavior

Each sync makes your agents match the files:
  • New file → a new managed agent is created.
  • Changed file → the managed agent is updated, and the change is recorded as a version.
  • Removed file → the agent is gently soft-deleted. This keeps its triggers and tags and is reversible: if the file returns, the same agent is resurrected (not a fresh copy).
  • Invalid file → the last good version of that agent is kept. The error is recorded against that file and never blocks the others, so one bad file can’t take down the rest of your agents.
The card’s per-file status table shows ok, integration not connected, or a parse/validation error for each file, with a link to the file at the synced commit.

Validate Before Syncing

POST /api/agent-sync/validate runs the exact same parse, schema, and cross-file graph validation as the sync engine, statelessly, over files you post — so it cannot drift from real sync behavior. It’s the engine behind the CI Action below, and you can call it directly:
The response carries a result per file — both agent YAML and any posted SKILL.md — with errors (would block sync) and warnings, plus an overall valid flag. Warnings are checks that depend on live org state the request can’t see: an unprovided systemPrompt.file, an agentTools sync_key not in the posted set, or a skill reference whose SKILL.md wasn’t posted (it may be a UI-created org skill). Post your SKILL.md files (and set skillsPath if it isn’t the default cotool/skills) to validate skill references and skill definitions too — with a skill’s SKILL.md posted, validation also errors on any agent that attaches it without granting the skill’s allowed-tools, exactly as sync would. See the API Reference for the full schema.
Prefer GitHub OIDC over an API key. The $COTOOL_API_KEY above is shown for direct or manual calls and for CI outside GitHub. For GitHub Actions, use the OIDC-based Action below instead — it mints a short-lived, repo-scoped token, stores no secret, and authorizes as a validate-only principal.A Cotool API key, by contrast, is long-lived and carries the full permissions of the user who created it — it is not narrowed to validation, even though the endpoint only needs tool.manage — so a leaked key is as powerful as that user. If you must use one (non-GitHub or self-hosted CI), create it under a dedicated, least-privilege service account, keep it in a secrets manager, and rotate it regularly.

Validate in CI

The cotool/validate-agents GitHub Action validates your agent YAML on every pull request and annotates problems inline. The Action authenticates only via GitHub OIDC — there is no API-key input, and it is the recommended way to validate in CI. The job mints a short-lived, repo-scoped OIDC token; Cotool verifies it and maps the repository to your org via your GitOps sync configuration. Nothing to store or rotate, and the token grants validate-only access — never your broader API-key permissions.
OIDC requires that this repository is configured as your org’s agents source under Settings → Agents → GitOps sync — that registration is what authorizes the repo. To validate before configuring sync, or outside GitHub CI, call the endpoint directly with an API key (the curl example above) rather than the Action — subject to the API-key trade-offs noted above.

Inputs

How validation gates a merge — and how syncing stays safe

The Action (and the /validate endpoint behind it) is advisory by default: it reports problems on the pull request, but on its own it does not stop anything from syncing. Cotool’s sync engine polls whatever is on your configured branch and reconciles it — it does not know or care whether a CI check ran or passed. Two separate mechanisms turn that into a safe GitOps workflow:
  1. Branch protection makes validation a merge gate (recommended). In your repository settings, mark the validation check as a required status check on the branch you sync from. GitHub then blocks any pull request from merging until validation passes, so only validated YAML ever reaches the branch Cotool polls. This is the piece that actually gates syncing, and it lives in your GitHub settings — not in Cotool.
  2. The sync engine re-validates as a backstop. Independently of CI, every sync runs the exact same parse, schema, and cross-file graph checks before applying a file. An invalid file is skipped per-file — its last good version is kept and the error is recorded against that file — so a bad file that slips past CI never corrupts your live agents (see Reconcile Behavior).
1

Open a pull request

An author edits agent YAML under your synced path and opens a PR. The validate-agents Action runs against the PR’s files and annotates any errors or warnings inline.
2

The required check gates the merge

With branch protection enabled on your sync branch, the PR cannot merge until the validation check is green. Reviewers only ever merge validated YAML; broken YAML is blocked at the door.
3

Merge to the sync branch

Once approved and green, the PR merges into the branch you configured under Settings → Agents → GitOps sync (commonly your default branch).
4

Cotool polls and reconciles

Within ~5 minutes the sync engine fetches the branch HEAD, re-validates, and applies the changes — recording each as a version. Anything still invalid is skipped per-file, never blocking the rest.
Cotool never reads your CI status — branch protection is what ensures only validated YAML lands on the sync branch. If you sync from an unprotected branch (or commit directly to it), the Action becomes a non-blocking signal and the server-side backstop is your only guard against invalid files.

Exporting an Existing Agent

To move a UI-built agent into Git, open the agent, use the version dropdown, and choose Export as YAML. The export emits the canonical sync shape — inline prompt as a literal block, stable sync_keys for agent-tool references, and triggers without any server-owned secrets — ready to commit to your repo.

Recovery

If the backing GitHub integration is disconnected, synced agents keep running — only syncing pauses. The card surfaces a Reconnect GitHub prompt. If you need to edit a managed agent while GitHub is disconnected, use convert to manual: Cotool makes an editable copy, moves the triggers to it, and parks the managed original (recoverable). The copy is an ordinary UI-owned agent from then on.

Agent Versioning — how synced and manual changes share one timeline

Triggering Agents — all trigger types and their fields

GitHub Integration — connect GitHub and configure repository sync

Creating Agents — building agents in the UI

Skills — reusable instructions, referenced by name or defined in code