Skip to content

Agent files

Reference (the persisted form of an agent’s identity + persona, read by every launcher) · For: operators · ACL semantics: SPEC §9, Appendix B

An agent’s identity and persona live in one Markdown file instead of being passed flag-by-flag, the same shape Claude Code uses for subagents:

.cotal/agents/<name>.md
---
name: dave # → COTAL_NAME / card.name
role: builder # → COTAL_ROLE / card.role (presence + anycast address)
description: … # → card.description
tags: [edit, test] # → card.tags ("what it can do")
subscribe: [general, team.backend] # channels it reads at boot
allowSubscribe: [general, team.>] # read ACL (omit = same as subscribe)
allowPublish: [general, team.backend] # post ACL (omit = none, default-deny)
model: opus # optional model override
variant: high # optional connector-defined model variant
capabilities: [spawn] # control-plane capabilities (may start/despawn teammates)
---
You are a builder on a shared mesh of peer agents… ← the body is the persona

Frontmatter is identity (an A2A-style AgentCard, SPEC §6); the body is the persona, appended to the session’s system prompt at launch: the one field that must be applied at launch, because a session cannot change its system prompt afterward.

Authoritative shape: agent-file.ts.

Field Type Meaning
name string, required Display name → card.name. A launcher resolves a bare name to .cotal/agents/<name>.md.
role string The addressable service: presence label and the anycast address (SPEC §3).
kind agent | endpoint Participation class; default agent.
description string One-line summary → card.description.
tags string[] Capability tags → card.tags.
subscribe string[] The active read set: channels subscribed at boot (mutable at runtime via join/leave). Must be ⊆ allowSubscribe. Default [general].
allowSubscribe string[] The read ACL: channels it may read. Wildcard subtrees allowed (team.>). Omitted ⇒ same as subscribe.
allowPublish string[] The post ACL: channels it may publish to. Omitted ⇒ deny; posting is the dangerous capability, declare it explicitly.
quiet string[] Per-channel attention default: delivered but never wakes this agent (per-channel dnd). Concrete channels within the read ACL.
muted string[] Per-channel attention default: dropped on receive, @mentions included.
model string Model override handed to the agent CLI (Claude: opus / full id; OpenCode: provider/model).
variant string Connector-defined model variant (e.g. an OpenCode variant, see cotal models).
capabilities string[] Control-plane capabilities minted into the cred. spawn is the only one today: it grants the privileged control subject (spawn / named stop / persona definition), default-deny when absent, enforced by the broker, not a handler.
owner string Policy, not content: set once by definePersona (owner = creator); only the owner (or admin) may redefine the file over the wire. Never write it by hand.
(any other key) string Kept verbatim in meta so a connector can read its own launcher hints without core knowing them.

The three channel verbs on one card, with the common recipes: Channels & permissions. Attention semantics (quiet / muted are one-way defaults; the runtime toggle is per-instance and resets on restart): Connect Claude.

  • By name. A launcher resolves a bare name to .cotal/agents/<name>.md (project catalog). This is a directory convention, not an HTTP well-known; mesh discovery stays NATS presence. The card built from the file is what gets broadcast.
  • One ref. The launcher sets COTAL_AGENT_FILE=<abs path> (the who) the way COTAL_LINK carries the where; the joined session reads its card straight from the file. Individual COTAL_* vars still override it (config).
  • Defaults. A bare cotal spawn uses the default persona (COTAL_DEFAULT_PERSONA changes the fallback); the harness comes from --agent / COTAL_DEFAULT_AGENT, else Claude. An explicit flag always wins over the file (run a mesh).

Every launcher consumes the file the same way; they differ only in how they run the spec:

Launcher How to point at a file
Manager (cotal spawn --detach dave) auto-discovers .cotal/agents/dave.md in the manager’s workspace, or --config <persona-or-path>; same grammar as foreground (--model, --variant, --cwd, --prompt, ACL overrides, --share-tools).
Foreground (cotal spawn dave) same resolution; the real agent TUI takes over this terminal. Works from any directory via the mesh registry.

.cotal/ is gitignored (user-local, like .claude/); commit persona files you want shared some other way. The demo ships committed examples under examples/01-lateral-coordination/agents/.

Expert-persona prompts (“you are a world-class…”) do not reliably improve accuracy. Keep the body to what the agent does and how it coordinates; a persona that needs facts should point at the source (the repo’s docs, a URL), not assert them.

cotal_persona(name, prompt, model?) sends a persona to the manager, which writes the same file and announces it; a later cotal_spawn(name, role?, agent?, model?, variant?) brings it online, so a peer can mint a teammate with no hand-written file (tool catalog). The write path takes content only (model / persona); role, allowPublish, capabilities, and owner are policy and have no slot, so a peer cannot grant itself a capability by redefining a file.

The operator-side counterpart is cotal personas (list / show / edit / new / rm); it reads and writes the same files directly, offline, no mesh (CLI).