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.
Which one do you want
Section titled “Which one do you want”| 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.
Where a skill should live
Section titled “Where a skill should live”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.
Agents
Section titled “Agents”Author one
Section titled “Author one”Drop a markdown file in agents/. No manifest entry, no build step.
---name: Release Notes Writerdescription: Turns a commit range into user-facing release notes. Use when preparing a changelog or a release announcement.model: balancedtools: [file, terminal]deny_tools: [write, edit]max_turns: 20icon: scroll-textcolor_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 auser would notice, and drop anything with no user-visible effect.
Output exactly three sections: **Added**, **Fixed**, **Changed**. One line peritem, present tense, no commit hashes, no author names. If a section would beempty, 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.
Frontmatter
Section titled “Frontmatter”| 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.
Choosing the model
Section titled “Choosing the model”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.
Choosing the tool set
Section titled “Choosing the tool set”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 tooldeny_tools: [write, edit] # subtract the writes back outThree 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.
Ids, collisions, and overriding
Section titled “Ids, collisions, and overriding”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.
How an agent gets used
Section titled “How an agent gets used”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. Setuser_invocable: falsefor 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
descriptionis the entire basis for that choice, so write it as “use when…”, not as a title. - A trigger fires it. A cron
triggersblock 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().
Shipping an agent runtime
Section titled “Shipping an agent runtime”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.
Skills
Section titled “Skills”Author one
Section titled “Author one”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:
---name: invoice-conventionsdescription: 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, neverreset 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. Adraft 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 producestotals 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.shThe 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.
Frontmatter
Section titled “Frontmatter”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.
How a skill reaches the model
Section titled “How a skill reaches the model”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.
Writing a skill that actually gets loaded
Section titled “Writing a skill that actually gets loaded”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/. KeepSKILL.mdas 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:
- Inline it. If the model needs the content, put the content in the body.
- 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. - 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.
Name collisions
Section titled “Name collisions”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.
A complete zero-JavaScript extension
Section titled “A complete zero-JavaScript extension”One agent and one skill, no main, no build.
{ "name": "Invoice Assistant", "version": "1.0.0", "description": "An invoice reviewer agent plus this product's invoice conventions.", "permissions": [], "contributes": { "skills": true }}---name: Invoice Reviewerdescription: Checks a draft invoice against this product's conventions and reports problems. Use before an invoice is sent.model: balancedtools: [file]skills: [invoice-conventions]max_turns: 15icon: receiptcolor_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, theper-line rounding, and the total. Report every problem you find as a singleline: what is wrong, what it should be, and which line it is on. If everythingis 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 failureA total off by exactly one or two cents is almost always sum-then-round. Checkthe 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.
- Contributing tools — the abilities an agent’s
tools:list selects from. - Scheduled work — the
triggersblock in full. - The manifest — every
contributeskey in one place.