# Agents and skills Ship a configured agent — model, prompt, tool set — and ship skills the model loads on demand when it needs them. Source: https://docs.vampikez.fun/build/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 | 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 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 ### Author one Drop a markdown file in `agents/`. No manifest entry, no build step. ```markdown --- 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. ### 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](/build/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 `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 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. ```yaml 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. :::caution[`tools:` chooses a toolkit; it is not a sandbox] Treat the list as the agent's intended equipment, not as an enforced boundary. Extension-contributed tools in particular remain reachable by an agent that did not name them. If a tool must never run in some context, gate it in the tool itself — see [Contributing tools](/build/tools/) — rather than relying on an agent definition to withhold it. ::: ### 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 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()`. ### 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](/build/manifest/) and the [manifest reference](/reference/manifest/#contributesagentruntimes). 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 ### 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: ```markdown --- 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: ```json { "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 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 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 ` or `$invoice-conventions `. 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 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. ```yaml # 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 This is the rule that costs the most when broken, and it looks harmless. ```markdown 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. ### 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 One agent and one skill, no `main`, no build. ```json check title="extension.json" { "name": "Invoice Assistant", "version": "1.0.0", "description": "An invoice reviewer agent plus this product's invoice conventions.", "permissions": [], "contributes": { "skills": true } } ``` ```markdown --- 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: ```markdown ## 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. ## Next - [Contributing tools](/build/tools/) — the abilities an agent's `tools:` list selects from. - [Scheduled work](/build/scheduled-work/) — the `triggers` block in full. - [The manifest](/build/manifest/) — every `contributes` key in one place.