Skip to content

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-writer
description: Writes release notes in the house style.
model: default # a model name from the Models page
tools: [read_file, write_file, edit_file] # allowlist; omit for all tools
maxTurns: 8
memory: true
group: Docs # optional folder in the UI
skills: [changelog-style] # pinned into every prompt
---
You write release notes for…

The agent editor

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.

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.

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.

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 why read_file takes offset and limit — 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_context tool 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_nudge and repeat_loop_nudge, so a run that went in circles says so.

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.

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.

An agent’s memory directory

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 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.

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:

  1. the agent’s own body (or a short default when a node names no definition);
  2. the repo instruction blocks described above;
  3. the available-skills list (name, tier, description);
  4. any pinned skills’ full bodies;
  5. 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.