Agents
An agent is a file: <home>/agents/<name>.md, frontmatter plus a system prompt body. That
makes “the release-notes writer” a thing you reference from a workflow node or the chat
panel instead of re-specifying a prompt every time it’s needed — and a thing you can
diff, review, and version like any other document.
---name: release-writerdescription: Writes release notes in the house style.model: default # a model name from the Models pagetools: [read_file, write_file, edit_file] # allowlist; omit for all toolsmaxTurns: 8memory: truegroup: Docs # optional folder in the UIskills: [changelog-style] # pinned into every prompt---You write release notes for…
Agents are edited as markdown, with frontmatter driving the runtime.
Write the file by hand, or use Settings → Agents, which edits the same file and records
a version under <home>/history/agent/<name>/ on each save. The registry re-reads
agents/*.md from disk per run, so an edit applies to the next run with no restart.
Frontmatter, field by field
Section titled “Frontmatter, field by field”| Field | Default | Effect |
|---|---|---|
name |
the filename without .md |
How workflow nodes and the chat picker refer to it; also the memory directory name |
description |
empty | Shown in the chat picker, the agent list, and the workflow inspector’s agent card |
model |
the default model | A model name from the Models page — a connected provider’s name, or local:<name> for a model on this Mac. A name that doesn’t exist falls back to the default rather than failing |
tools |
all tools | An allowlist over the whole registry, built-ins and MCP alike |
maxTurns |
150 in a workflow node | Runaway backstop on the tool loop, not a work budget |
memory |
off | true gives it persistent notes plus the three memory tools |
group |
none | A folder in the agent list and an optgroup in the chat picker |
skills |
none | Skill names whose full bodies are inlined into every prompt |
The body — everything after the closing --- — is the system prompt.
Where an agent gets used
Section titled “Where an agent gets used”From a workflow node — set config.agent on an agent node.
The definition supplies the system prompt, tool allowlist, model and turn budget; the
node’s own config is the fallback for each. The inspector shows a read-only card naming
the persona, telling you which other workflows share it, and linking into Settings.
From chat — the chat panel’s one picker lists your models
(local and connected) under Models and your agents under Agents, grouped by
group. Choosing
an agent swaps the persona, the tool allowlist and the model for that conversation.
From another agent — the delegate tool runs a named definition in a fresh context and
returns its result. Delegation is depth-capped at 2, the subagent’s tokens are not streamed
to the parent surface, and its events are attributed to a parent▸agent-name node id, so a
delegated workstream is legible in the Run Monitor without
drowning it.
As a dashboard or atlas persona — a dashboard or
atlas definition’s agent: field supplies persona and model
for the orchestrator, appended after that feature’s own system prompt.
The tool loop
Section titled “The tool loop”An agent node (or a chat turn) runs a multi-turn loop: the model emits tool calls, the
tool registry executes them behind the permission gate, and results feed back for the next
turn. maxTurns bounds it. Every call surfaces as a tool.call / tool.result pair on
the canvas, in the events panel, and in the Run Monitor —
there is no hidden work.
config.tools on a node is true for everything or an allowlist; config.provider
overrides which model the node uses (a connected provider’s name, or local:<name>).
Three details of the loop are worth knowing because you will see them in the event stream:
- Tool results are clipped at 8000 characters before they go back to the model, with a
…[truncated]marker. This is whyread_filetakesoffsetandlimit— paging is how an agent reads a large file completely. - History is compacted when it passes roughly 300,000 characters: everything after the
system prompt is summarised into one note and the loop continues. It happens at a turn
boundary, so no dangling tool call is ever orphaned, and it shows up as a
compact_contexttool result. - The loop nudges rather than spins. An empty completion, tool-call syntax leaking into
plain text, or the same tool called eight times in a row each produce a corrective user
turn (twice at most, each). Those nudges appear as tool results named
empty_reply_nudge,malformed_tool_call_nudgeandrepeat_loop_nudge, so a run that went in circles says so.
Built-in tools
Section titled “Built-in tools”Everything an agent can do goes through one registry. These ship in the box; MCP
servers add theirs to the same list, namespaced <server>_<tool>.
| Tool | What it does |
|---|---|
read_file |
Read a file. offset / limit page through large ones; a ranged read reports the total line count |
write_file |
Create or overwrite a file |
edit_file |
Replace an exact string — it must appear exactly once, or the call fails and says how many matches there were |
list_dir |
List a directory |
run_shell |
bash in the workspace or a mount, with a 5-minute timeout, a 30KB output cap, and a destructive-command denylist |
search_code |
Semantic search across workspace + mounts |
check_diagnostics |
Run the project’s own typechecker and return structured errors |
set_plan |
Publish a step list — drives the live plan strip and a plan.update event |
web_fetch / web_search |
Fetch a URL as readable text; keyless web search |
load_skill |
Pull one skill’s full body on demand |
memory_list / memory_read / memory_write |
Persistent notes, for agents with memory: true |
latchai_activity |
LatchAI’s own recent runs — id, status, duration, tokens, source |
delegate |
Run another agent definition in a fresh context |
publish_dashboard / dashboard_source |
Publish widgets to a dashboard; read the archived payload behind a number |
record_card / publish_atlas / read_card / atlas_source |
The four tools a Code Atlas build runs on |
Everything that touches the filesystem goes through the multi-root filesystem and must
stay inside the workspace and its mounts; an attempt to escape emits permission.denied
rather than silently failing. That guard is re-checked against the real path, so a
symlink planted inside the workspace can’t be used to read your home directory.
latchai_activity exists because <home>/runs/ sits deliberately outside the workspace: no
file tool can reach the run logs, so an agent asked “what happened overnight?” would
otherwise have nothing to read.
Memory
Section titled “Memory”Set memory: true and the agent gets persistent notes at
workspace/memory/<agent>/*.md, with memory_list, memory_read, and memory_write
tools. An index of the agent’s current memory — names and summaries — is injected into
its system prompt, so it knows what it knows before it starts working.
The summary in that index is each file’s first non-empty line, so the convention that makes memory legible is one topic per file with a one-line headline at the top.
Because memory lives in the workspace, memory writes flow through checkpoints and the review gate like any other file change. An agent quietly rewriting its own beliefs shows up in a diff.

Memory is markdown in the workspace, not a hidden store.
The memory tools are scoped to the running definition’s name and refuse to run at all outside a named agent — there is no shared pool an agent can read another’s notes from.
Skills
Section titled “Skills”Skills follow the Claude Code layout — <tier>/.claude/skills/<name>/SKILL.md — read from
the user tier (~/.claude/skills), then the workspace, then mounts, with later tiers
shadowing earlier ones by name. Names and descriptions go into every agent prompt; the
load_skill tool pulls the full body on demand, so a large library costs almost no
context until it’s used. An agent definition may pin skills (skills: [a, b]) to inline
their bodies every turn, and the user tier can be excluded from agent prompts entirely
via skillTiers.user: false in latchai.config.json.
A pinned skill that no longer exists degrades to a visible note in the prompt rather than failing the run, so deleting a skill can’t strand every agent that referenced it.
Settings → Skills manages the workspace tier; the user tier is read-only there.
Repo instructions
Section titled “Repo instructions”Agents and chat inherit the instruction files your repositories already maintain. For the
workspace and every mount, LatchAI reads CLAUDE.md, AGENTS.md, .cursorrules,
.cursor/rules/*.mdc|md (frontmatter stripped, description kept), and
.github/copilot-instructions.md, layered after the user tier (~/.claude/CLAUDE.md),
size-capped, and re-read every turn. Drop LatchAI into a repo with existing AI guidance and
it follows it immediately.
The layering is Claude Code’s: the user tier first, then each root in turn, with each
block labelled by where it came from (### Instructions from projects/api/CLAUDE.md) and
the prompt telling the model that more specific tiers win on conflict. Caps are 8,000
characters per file and 16,000 per root — a very long CLAUDE.md is truncated with a
marker rather than crowding out everything else.
What an agent’s prompt actually contains
Section titled “What an agent’s prompt actually contains”In order, every turn:
- the agent’s own body (or a short default when a node names no definition);
- the repo instruction blocks described above;
- the available-skills list (name, tier, description);
- any pinned skills’ full bodies;
- the memory index, when memory is on.
Chat adds one more block — a compact one-line-per-workflow index of your automations — so the assistant can answer “what runs overnight?” without reading files.
Because all of it is assembled from files on disk each turn, the way to debug a prompt is to read those files: there is no hidden layer, and nothing is cached across runs.