Skip to content

Agents and skills

Two things an extension can contribute, often confused, worth keeping apart.

An agent is a worker: a model, a system prompt, a tool set, and limits. It is selected — by the user, or by another agent delegating to it — and it runs a turn.

A skill is knowledge: a name, a one-line trigger description, and a body of instructions. Routable skill descriptions appear in a bounded prompt catalog; the body is loaded when selected or explicitly preloaded by an agent.

The distinction that matters in practice: an agent answers who is doing this, a skill answers how it is done. Neither needs a permission, and neither needs any JavaScript — an extension that is nothing but a manifest and two markdown files is a working extension.

You want to… Ship
A reviewer with a narrow tool set and a strict prompt An agent
A researcher on a cheaper, faster model An agent
Work that runs on a schedule without a user An agent with a trigger
A procedure any agent might need occasionally A skill
Your product’s conventions, so the model follows them when relevant A skill
Knowledge one specific agent should always have An agent whose skills: names it

The cost asymmetry is the deciding factor. An agent’s prompt is paid on every turn that agent runs. A skill’s description is paid on every turn of every agent; its body is paid only on the turns that load it. Long content that is only sometimes relevant belongs in a skill body.

Placement is part of the contract, not just packaging:

Scope Put a skill here when…
Builtin It is universal, depends only on core primitives, and is required to operate or author the platform. Popularity alone is not enough.
Extension It teaches a tool, service, output type, or interaction owned by that extension. The skill should appear and disappear with the capability.
Registry It is useful but optional, vendor-specific, or vertical knowledge that should update independently of WAMP.
Workspace .agents/skills It encodes repository or team conventions and should be reviewed and versioned with the work.
User ~/.agents/skills It is a private cross-project routine or preference.

Default registry search covers Anthropic’s maintained skills/ subtree, OpenAI’s .curated subtree, and community ClawHub. A GitHub source can restrict discovery to one directory with github:owner/repo/path/to/skills[#ref]; this keeps templates, internal packages, and unrelated skills out of the catalog.

Skills remain knowledge, not authority. A tool name in skill metadata does not grant access to that tool, and deterministic multi-agent coordination belongs in a workflow rather than a skill-specific orchestration language.

Drop a markdown file in agents/. No manifest entry, no build step.

agents/release-notes.md
---
name: Release Notes Writer
description: Turns a commit range into user-facing release notes. Use when preparing a changelog or a release announcement.
model: balanced
tools: [file, terminal]
deny_tools: [write, edit]
max_turns: 20
icon: scroll-text
color_token: blue
---
You write release notes for humans, not for other engineers.
Read the commit range you are given with `git log`, group changes by what a
user would notice, and drop anything with no user-visible effect.
Output exactly three sections: **Added**, **Fixed**, **Changed**. One line per
item, present tense, no commit hashes, no author names. If a section would be
empty, omit it.

Everything below the frontmatter is the system prompt. The agent id is the filename — agents/release-notes.md is the agent release-notes — and name is only the display label. The directory is scanned one level deep, .md only.

Key Type Default Notes
name string — Required. A file without it is skipped with a warning.
description string '' Shown in pickers, and to other agents deciding whether to delegate here. Write it as a trigger.
model string inherits Either a tier — fast, balanced, powerful — or a specific model id.
tools string[] every registered tool Mixed list of tool categories and exact tool names.
deny_tools string[] — Removed after categories expand. Applied last, so it always wins.
skills string[] — Skill names whose bodies are inlined into this agent’s prompt on every run.
max_turns number 25 Ceiling on loop iterations.
max_tokens number model default Output cap per turn, clamped to the model’s maximum.
thinking none|low|medium|high — Anything else warns and is dropped.
budget mapping — { turns?, cost_usd?, time_minutes?, mode? }, mode is soft (default) or hard.
icon string — A Lucide icon name.
color_token string — blue, purple, emerald, pink, gray, indigo, amber, rose.
user_invocable boolean true Set false for a delegation/trigger-only worker. It stays registered but is absent from new-chat, @, and Library run controls.
triggers array — Cron scheduling. Each firing opens a chat when the engine enables schedules. See Scheduled work.

A cost_usd cap stops with cost_unknown before another model call if any charge in the Run or its delegation forest is unknown. The stop applies in both modes and makes no paid wrap-up call.

An unrecognized key is dropped with a warning, and common wrong names get a “did you mean” — max_iterations and max_steps point at max_turns, color at color_token, allowed_tools at tools, and system_prompt or prompt at “the markdown body below the frontmatter”, which is where the prompt actually goes.

model: balanced is usually right. The three tiers resolve through user-editable slots, and the balanced slot defaults to following the chat model — so a balanced agent tracks whatever the user has chosen, while fast and powerful deliberately do not.

Naming a specific model id pins it. If that id is not in the catalogue, the host warns and falls back to the tier, or to the default agent slot. Pinning is worth it only when the agent genuinely depends on one model’s behavior.

Each entry in tools is resolved as a category if the platform knows that category name, and as an exact tool name otherwise. There is no glob syntax.

The categories: file, terminal, analysis, memory, delegation, mcp, workflow, meta, presentation, media, search, testing, storage — plus any category an extension has introduced.

tools: [file, search, crm_create_contact] # two categories and one specific tool
deny_tools: [write, edit] # subtract the writes back out

Three behaviors to know. Omitting tools entirely — or giving an empty array — means every registered tool. Any agent that does narrow tools loads skills by reading them, so give it the file category or pre-load them with skills:. And a misspelled entry is neither a category nor a tool, so it silently contributes nothing; the host logs one validation line per agent naming the unresolvable entry, which is where to look when an agent seems to be missing a tool.

Agents live in four scopes, in increasing precedence: built-in, extension, user, workspace. Two agents with the same id in the same scope is an error — a second extension registration throws, naming the extension that got there first. The same id in a higher scope is a legitimate override, and it is a full replacement rather than a field-by-field merge: the winning definition’s prompt, tools, and model are used, and nothing is inherited from the one it shadows.

So pick ids a user is unlikely to collide with, and expect that a user can deliberately shadow your agent with one of their own.

Three real paths:

  • The user launches it. A user-invocable agent appears in new-chat, @, and Library run controls. Running one opens a new chat bound to that agent. Set user_invocable: false for a worker that should only be delegated to or triggered.
  • Another agent delegates to it. The default agent can spawn a sub-agent in an isolated context, choosing from a catalogue of agent ids and descriptions injected into its prompt. Your description is the entire basis for that choice, so write it as “use when…”, not as a title.
  • A trigger fires it. A cron triggers block opens a new chat for each firing while an enabled engine runs. Desktop enables schedules for its local engine.

Typing @release-notes in the composer autocompletes from the user-invocable list, but be clear on what it does: it inserts the text of the mention into your message. The bound agent reads it and may delegate accordingly. It is a way to refer to an agent, not a switch that rebinds the turn.

An extension can also start an agent run itself with ctx.api.agents.delegate, and list what is available with pluginAPI.agents.list().

An agent definition (the markdown file above) is a WAMP worker. An agent runtime is a foreign ACP process that answers a chat turn. Ship one with contributes.agentRuntimes. Claude Code, Codex, Grok Build, and Pi are first-party examples. The bundled Codex extension 0.3.6 uses codex-acp 2.0.0 with the @openai/codex 0.158.0 binary pin.

The field — transport, command, which WAMP tools the runtime is lent, how the user’s skill set is handed over — is documented in The manifest and the manifest reference.

Lent tools arrive with an MCP instructions block: a fixed platform sentence (deliverables reach the user only through present_files) followed by the instructions each lent tool declared at registration. Skills arrive as one staged set through the runtime’s native root mechanism; a login that cannot take them is reported, never silently skipped.

A skill is a directory holding SKILL.md. Even a prose-only skill gets its own directory, so it can grow references and scripts without moving:

skills/invoice-conventions/SKILL.md
---
name: invoice-conventions
description: How this product numbers, dates, and rounds invoices. Use before creating, editing, or explaining any invoice.
---
# Invoice conventions
## Numbering
`INV-{YYYY}-{sequence}`, sequence padded to four digits, never reused, never
reset mid-year. A credit note uses the same sequence with a `CN-` prefix.
## Dates
`issuedAt` is the date the invoice was sent, not the date it was created. A
draft has no `issuedAt` at all — leave it null rather than guessing.
## Rounding
Round each line to two decimals, then sum. Never sum then round; it produces
totals that disagree with the printed lines by a cent.

Files travel beside it:

skills/
release-process/
SKILL.md
references/
hotfix.md
rollback.md
scripts/
check-tags.sh

The manifest must be named exactly SKILL.md, and its containing directory must match frontmatter name. A subdirectory without one is ignored. Only references/, scripts/, and assets/ are scanned for bundled resources; resources may be nested up to the runtime’s bounded depth and symlinks are rejected.

Declare the directory in the manifest:

{ "contributes": { "skills": true } }

true scans skills/. A string names one relative skill root; an array names several. Each root may be a collection of immediate child skill directories or a single skill directory containing SKILL.md. Use "." to declare the extension root as one skill. An undeclared skills/ directory loads nothing. For wamp ext pack, keep skills under skills/; its archive builder currently copies only that skill root.

Skills require lowercase name and a non-empty description; invalid packages are excluded and surfaced as Library load diagnostics. version, tags, license, compatibility, string metadata, disable-model-invocation, and user-invocable use the same validated contract for builtin, user, workspace, registry, and extension sources. disable-model-invocation: true keeps a skill out of model routing but still permits explicit user invocation.

Two layers, and understanding them is what makes a skill get used.

The listing. Routable skills appear in the system prompt under “Available Skills” with their descriptions and the absolute path of each SKILL.md. Under catalog pressure descriptions shorten first; when even names and paths do not fit, the listing names the directories that hold the rest.

The load. When the model decides a skill applies, it reads that SKILL.md with its ordinary read tool. The read activates the exact skill for the current conversation. Subsequent turns reattach a bounded copy outside lossy history, including after full compaction; if the body or combined active set exceeds the prompt budget, the model receives an explicit marker telling it to re-read the file. Clearing or evicting the conversation clears this transient activation state.

The body names bundled files relative to its own directory — references/hotfix.md — and the model reads them the same way, only when it needs them. That is the Agent Skills convention, so the same skill text works in every runtime that loads skills.

An agent can also pre-load skill bodies into its prompt by naming them in its skills: frontmatter — useful when the knowledge is not optional for that worker.

Users can activate a user-invocable skill directly with /invoice-conventions <request> or $invoice-conventions <request>. The request continues through the ordinary agent run with the skill active. If two publishers use the same short name, WAMP reports the ambiguity and offers their qualified identities instead of silently choosing one.

The description is a trigger, not a title. The model reads it to decide whether to spend a tool call. Say the situation, not the subject.

# Weak — a topic. The model has no idea when this applies.
description: Information about invoices.
# Strong — a situation with verbs.
description: How this product numbers, dates, and rounds invoices. Use before
creating, editing, or explaining any invoice.

Then, for the body:

  • Write a procedure, not an essay. Steps, rules, tables, and the specific strings and formats to use. The model is going to act on this immediately.
  • One skill, one job. A skill that covers three unrelated areas will be loaded for one of them and waste context on the other two. Split it.
  • Say the non-obvious thing. The model already knows how to write TypeScript. It does not know your numbering scheme, your rounding rule, or which of two plausible approaches your codebase has settled on.
  • Put long detail in references/. Keep SKILL.md as the map — when to use which resource — and let the model pull only the branch it needs.
  • Include the failure mode. “If X, the symptom is Y” saves more turns than any amount of correct-path prose.

Never point a skill at a path in another repository

Section titled “Never point a skill at a path in another repository”

This is the rule that costs the most when broken, and it looks harmless.

<!-- Wrong. Do not do this. -->
For the full type definitions, read
`packages/extension-sdk/types/wamp-extension-sdk.d.ts`.

The skill ships inside your extension, while the model’s working directory is the user’s project. A repository-relative reference can resolve to an unrelated checkout or a stale file. Use packaged references instead of assuming the model is reading your extension’s source tree.

Three correct alternatives, in order of preference:

  1. Inline it. If the model needs the content, put the content in the body.
  2. Bundle it. Ship it under references/ and name it relative to the skill — references/types.md. The model resolves it against the skill’s own base directory, inside your extension, so it cannot pick up a stray copy.
  3. Name it without pointing at it. If the model does not need to read a file but does need to know it exists, say so and say explicitly not to open it: “the platform typechecks every save, so trust the error rather than opening the declaration files.”

The same applies to absolute paths, ~-relative paths, and repository URLs. If the body contains a path, ask what happens when that path exists on the user’s machine and contains something else.

Extension and registry skills carry publisher-qualified identities, so two publishers with the same short name remain visible and neither silently shadows the other. A bare name is an invocation alias only while it is unique; on a collision use the qualified identity shown by /skills or Library. Builtin, user, and workspace compatibility roots intentionally represent one authored identity, with the nearer workspace copy taking precedence.

One agent and one skill, no main, no build.

extension.json
{
"name": "Invoice Assistant",
"version": "1.0.0",
"description": "An invoice reviewer agent plus this product's invoice conventions.",
"permissions": [],
"contributes": { "skills": true }
}
agents/invoice-reviewer.md
---
name: Invoice Reviewer
description: Checks a draft invoice against this product's conventions and reports problems. Use before an invoice is sent.
model: balanced
tools: [file]
skills: [invoice-conventions]
max_turns: 15
icon: receipt
color_token: amber
---
You review draft invoices and report problems. You do not fix them.
For each invoice you are given, check the number format, the issue date, the
per-line rounding, and the total. Report every problem you find as a single
line: what is wrong, what it should be, and which line it is on. If everything
is correct, say so in one sentence and stop.
Never edit a file. Never send anything.

skills/invoice-conventions/SKILL.md is the file shown earlier, with one section added at the end — the kind of content that saves the most turns:

## Common failure
A total off by exactly one or two cents is almost always sum-then-round. Check
the line values before looking anywhere else.

The agent names the skill in skills:, so its body is in the prompt on every run — the knowledge is not optional for this worker. The skill is also in the general listing, so any other agent can load it when an invoice comes up.