diff --git a/.gitignore b/.gitignore index 6238d347500..e74d6ad847a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,7 @@ distllms/ .astro-cache/ # skills/ is fetched from middlecache via bin/fetch-skills.ts -skills/ +/skills/ !.agents/skills/ !.agents/commands/ !.agents/agents/ diff --git a/public/__redirects b/public/__redirects index 56f7907c1a9..e6987de3b87 100644 --- a/public/__redirects +++ b/public/__redirects @@ -258,6 +258,8 @@ /agents/runtime/communication/readonly-connections/ /agents/agent-class/readonly-connections/ 301 /agents/runtime/communication/routing/ /agents/agent-class/routing/ 301 /agents/runtime/communication/websockets/ /agents/agent-class/websockets/ 301 +/agents/runtime/execution/ /agents/skills/ 301 +/agents/runtime/execution/agent-skills/ /agents/skills/execution/ 301 /agents/runtime/execution/agent-tools/ /agents/agent-class/agent-tools/ 301 /agents/runtime/execution/durable-execution/ /agents/agent-class/durable-execution/ 301 /agents/runtime/execution/queue-tasks/ /agents/agent-class/queue-tasks/ 301 diff --git a/src/content/changelog/agents/2026-10-20-think-computer-workspace.mdx b/src/content/changelog/agents/2026-10-20-think-computer-workspace.mdx index d130c8e2703..f342c6ea090 100644 --- a/src/content/changelog/agents/2026-10-20-think-computer-workspace.mdx +++ b/src/content/changelog/agents/2026-10-20-think-computer-workspace.mdx @@ -9,7 +9,7 @@ date: 2026-10-20 import { TypeScriptExample } from "~/components"; -[Think](/agents/harnesses/think/) and [Agent Skills](/agents/runtime/execution/agent-skills/) now use the workspace from [`@cloudflare/computer`](https://github.com/cloudflare/computer) as well as the filesystem tools. A JavaScript execution environment can be configured by adding the `WorkerJavaScriptBackend`. +[Think](/agents/harnesses/think/) and [Agent Skills](/agents/skills/) now use the workspace from [`@cloudflare/computer`](https://github.com/cloudflare/computer) as well as the filesystem tools. A JavaScript execution environment can be configured by adding the `WorkerJavaScriptBackend`. `@cloudflare/shell` is deprecated, and so is Think's `createExecuteTool`. These will no longer be developed nor supported from today onwards. diff --git a/src/content/changelog/agents/2026-10-29-agent-skills-from-files.mdx b/src/content/changelog/agents/2026-10-29-agent-skills-from-files.mdx new file mode 100644 index 00000000000..d8f64d867af --- /dev/null +++ b/src/content/changelog/agents/2026-10-29-agent-skills-from-files.mdx @@ -0,0 +1,38 @@ +--- +title: "Agents SDK: Agent Skills are files in your agent's Workspace" +description: Load Agent Skills from a Computer Workspace, R2, or code. Skills written to the Workspace are available on the agent's next turn. +products: + - agents +date: 2026-10-29 +--- + +import { TypeScriptExample } from "~/components"; + +[Agent Skills](/agents/skills/) in the [Cloudflare Agents SDK](/agents/) are now files. Write a folder with a `SKILL.md` into your agent's [Computer](https://github.com/cloudflare/computer) Workspace, under `/workspace/.agents/skills/`, and the agent can use it on its next turn. You do not need a build step or a deploy, and the agent can write its own skills. + + + +```ts +import { Think, skills } from "@cloudflare/think"; + +export class MyAgent extends Think { + getSkills() { + return [ + skills.workspace(this.workspace), // /workspace/.agents/skills + skills.r2(this.env.SKILLS_BUCKET, { prefix: "skills/" }), + ]; + } +} +``` + + + +This release includes: + +- **Three sources.** Load skills from a [Computer Workspace](/agents/skills/sources/computer/), an [R2 bucket](/agents/skills/sources/r2/), or [code](/agents/skills/sources/code/). Think lists every source again before each turn. +- **The open format.** Every `SKILL.md` is checked against the [Agent Skills specification](https://agentskills.io/specification). As the specification's guide for agents recommends, a skill that breaks a rule still loads, with a logged warning. Use `parseSkill()` to check a skill before you write it. +- **Scripts in the Workspace.** Skill scripts are Workspace files, and the model runs them with the Workspace `exec` tool. Refer to [Execution](/agents/skills/execution/). +- **Skill tools for any harness.** Use the same `activate_skill` and `read_skill_resource` tools in Think, Pi Durable, or the AI SDK. Refer to [Skills tools](/agents/tools/skills/). +- **Warm prompt caches.** When a skill changes mid-conversation, Think sends the new catalog as a reminder instead of rebuilding the cached system prompt. An overridden `getSystemPrompt()` now stays at the top of the system prompt, above context blocks. + +This is a breaking change for code that uses `agents/skills` directly. The `agents:skills` Vite import, `agents/skills/compile`, `SkillRegistry`, and Think's `skillWorkspace` are removed, and `runner()` moved to `agents/skills/runner`. Install [Agents SDK v0.32.0](https://github.com/cloudflare/agents/releases/tag/agents%400.32.0) and [Think v0.23.0](https://github.com/cloudflare/agents/releases/tag/%40cloudflare%2Fthink%400.23.0) or later. Refer to the [`computer-skills`](https://github.com/cloudflare/agents/tree/main/examples/computer-skills) and [`r2-skills`](https://github.com/cloudflare/agents/tree/main/examples/r2-skills) examples. diff --git a/src/content/docs/agents/agent-class/index.mdx b/src/content/docs/agents/agent-class/index.mdx index a9a27df3c69..a72ec7542d3 100644 --- a/src/content/docs/agents/agent-class/index.mdx +++ b/src/content/docs/agents/agent-class/index.mdx @@ -106,7 +106,7 @@ flowchart TD | **Diagnostics channels** | `subscribe()`, diagnostics channels | [Diagnostics channels](/agents/runtime/observability/diagnostics-channels/) | | **Sub-agents** | `subAgent()`, `abortSubAgent()`, `deleteSubAgent()` | [Sub-agents](/agents/agent-class/sub-agents/) | | **Agents as tools** | `runAgentTool()`, `clearAgentToolRuns()`, `hasAgentToolRun()` | [Agents as tools](/agents/agent-class/agent-tools/) | -| **Agent Skills** | `skills` registry, bundled skill sources, script runners | [Agent Skills](/agents/runtime/execution/agent-skills/) | +| **Agent Skills** | `SKILL.md` parser, Workspace, R2, and manifest sources, script runner | [Agent Skills](/agents/skills/) | | **Sessions** | `Sessions` capability: message history, branches, compaction, search | [Sessions](/agents/runtime/capabilities/sessions/) | | **Think** | `Think` base class, workspace tools, lifecycle hooks, extensions | [Think](/agents/harnesses/think/) | | **Chat SDK** | `createChatSdkState()`, `ChatSdkStateAgent` | [Chat SDK](/agents/agent-class/communication-channels/chat-sdk/) | diff --git a/src/content/docs/agents/concepts/agent-skills.mdx b/src/content/docs/agents/concepts/agent-skills.mdx new file mode 100644 index 00000000000..fec110d1daa --- /dev/null +++ b/src/content/docs/agents/concepts/agent-skills.mdx @@ -0,0 +1,137 @@ +--- +title: Agent Skills +pcx_content_type: concept +description: What Agent Skills are, how agents load them through progressive disclosure, what the open SKILL.md format specifies, and what you can build with them. +tags: + - AI +sidebar: + order: 5 +products: + - agents +--- + +[Agent Skills](https://agentskills.io/) are an open format for giving an agent specialized knowledge and repeatable workflows. A skill is a folder with a `SKILL.md` file. The file holds a name, a description, and instructions for one kind of task. The folder can also hold scripts, reference documents, templates, and other files. + +Agent Skills was released as an open standard. Many agents support the standard, so a skill written once works across compatible agents. The [specification](https://agentskills.io/specification) defines what goes inside a skill folder. It does not define where skills are stored or how an agent loads them. + +## Why skills + +Models know a lot, but they do not know your team's procedures, conventions, or edge cases. You could put all of that in the system prompt, but every turn would then pay for instructions that most tasks do not need. Skills package that context so the agent loads it only when a task calls for it. + +Skills are useful for: + +- **Domain expertise**: a legal review checklist, your API's error codes, or your brand guidelines. +- **Repeatable workflows**: a release process, an incident runbook, or a data pipeline that should run the same way every time. +- **Reuse**: the same skill in a coding agent, a chat agent, and a scheduled agent. +- **Changing behavior without a deploy**: when skills are files the agent reads at runtime, editing a file changes what the agent does. + +## Progressive disclosure + +Agents load skills in three stages, so an agent can keep many skills on hand at a small cost: + +| Stage | What the agent loads | When | Approximate size | +| --- | --- | --- | --- | +| 1. Catalog | Each skill's `name` and `description` | When the session starts | 50 to 100 tokens per skill | +| 2. Instructions | The full `SKILL.md` body | When a task matches the description | Under 5,000 tokens recommended | +| 3. Resources | Scripts, references, and assets | When the instructions refer to them | Varies | + +An agent with 20 skills pays for 20 short catalog entries, not 20 sets of instructions. It loads the full instructions only for the skills it uses. + +The model decides when to activate a skill, based on the description alone. A clear description that says when to use the skill matters more than any other field. + +## Anatomy of a skill + +A skill is a directory whose name matches the skill's `name`: + +```text +release-notes/ +├── SKILL.md # Required: frontmatter and instructions +├── scripts/ # Optional: code the agent can run +├── references/ # Optional: documents the agent reads when needed +└── assets/ # Optional: templates, images, data files +``` + +`SKILL.md` starts with YAML frontmatter, followed by Markdown instructions: + +```md +--- +name: release-notes +description: Turn a list of changes into release notes. Use when the user asks for release notes, a changelog entry, or a summary of what shipped. +license: Apache-2.0 +compatibility: Requires Node.js 20 or later +metadata: + author: my-team + version: "1.2" +--- + +1. Read `references/style-guide.md`. +2. Run `scripts/format-release-notes.js` with one line per change. +3. Reply with the formatted notes. +``` + +### Frontmatter fields + +| Field | Required | Rules | +| --- | --- | --- | +| `name` | Yes | 1 to 64 characters. Lowercase letters, digits, and hyphens. Cannot start or end with a hyphen or contain `--`. Must match the directory name. | +| `description` | Yes | 1 to 1,024 characters. Says what the skill does and when to use it. | +| `license` | No | A license name or a bundled license file. | +| `compatibility` | No | 1 to 500 characters. Environment requirements, such as required tools or network access. | +| `metadata` | No | A map of string keys to string values, for properties the specification does not define. | +| `allowed-tools` | No | A space-separated list of tools the skill may use without asking. Experimental, and support varies by agent. | + +Agents may also read their own extra fields. For example, several agents hide a skill from the model when it sets `disable-model-invocation: true`. + +### Body and resources + +The specification places no rules on the Markdown body. Keep it focused: the recommended size is under 500 lines. Move detailed material into `references/` and tell the agent when to read each file, for example: "Read `references/api-errors.md` if the API returns a non-200 status code." + +Skill files refer to each other with paths relative to the skill's directory, such as `scripts/extract.py`. Keep references one level deep from `SKILL.md`. + +## Scripts + +A skill can bundle scripts in `scripts/` and tell the agent to run them. A script turns a fragile, multi-step procedure into one reliable command. Supported languages depend on where the agent runs. + +Scripts written for agents work best when they: + +- Never wait for interactive input. Take arguments, environment variables, or standard input instead. +- Explain their usage with `--help`, and say what went wrong and what to try in error messages. +- Print structured output, such as JSON, and keep diagnostics separate from data. +- Keep output small, or let the agent request more with an offset. +- Are safe to run twice, since agents retry. + +## Write good skills + +- **Start from real work.** A skill extracted from a task you completed with an agent, or from your own runbooks, is more useful than generic advice. +- **Add what the agent does not know.** Skip explanations of common concepts. Include your conventions, the tools to use, and the edge cases that go wrong. +- **Scope a skill like a function.** One coherent unit of work. Too narrow, and several skills load for one task. Too broad, and the skill is hard to activate precisely. +- **Give defaults, not menus.** Pick one approach and mention alternatives briefly. +- **Write the description for the model.** Phrase it as when to act, such as "Use when the user asks for…", and include cases where the user does not name the domain. +- **Test it.** Run the skill against real prompts, including near-misses that should not trigger it, and revise. + +## Trust + +A skill adds instructions to the agent's context, and its scripts can run code. Treat skills like code: + +- Only load skills from sources you trust. A skill from an untrusted repository can inject instructions. +- Limit what scripts can do. Give them only the tools, network access, and file access they need. +- Review changes to skills the agent or users can edit. + +## Skills in the Agents SDK + +The Agents SDK reads the open format in `agents/skills` and checks every `SKILL.md` against the specification. As the specification's guide for agents recommends, it loads skills that break a rule and logs a warning, so skills written for other agents still work. Skills that cannot be used, such as ones without a description, are skipped. To check a skill when you write it, parse it with `parseSkill()` and require no warnings. It loads skills from three sources: + +| Source | Use it when | +| --- | --- | +| [Computer Workspace](/agents/skills/sources/computer/) | Skills should be files the agent, a user, or your code can add and edit at runtime | +| [R2](/agents/skills/sources/r2/) | Many agents share one set of skills managed outside your Worker | +| [Code](/agents/skills/sources/code/) | Tests, or a small fixed set of skills that ships with your Worker | + +The SDK uses the `.agents/skills/` folder convention that several agents share, so `/workspace/.agents/skills/` in a Workspace holds the agent's skills. The model loads skills with the [skill tools](/agents/tools/skills/) `activate_skill` and `read_skill_resource`. With a Workspace backend, the model runs skill scripts with the Workspace `exec` tool. Refer to [Execution](/agents/skills/execution/). + +## Related + +- [Agent Skills](/agents/skills/): use skills in Think, Pi, and the AI SDK +- [Skills tools](/agents/tools/skills/): add the skill tools to your harness +- [Conversation state and memory](/agents/concepts/conversation-state-and-memory/): how skills compare to other kinds of context +- [Agent Skills specification](https://agentskills.io/specification): the full format diff --git a/src/content/docs/agents/concepts/conversation-state-and-memory.mdx b/src/content/docs/agents/concepts/conversation-state-and-memory.mdx index 534f3ded746..da8f8aabac9 100644 --- a/src/content/docs/agents/concepts/conversation-state-and-memory.mdx +++ b/src/content/docs/agents/concepts/conversation-state-and-memory.mdx @@ -14,7 +14,7 @@ import { TypeScriptExample, LinkCard, WranglerConfig } from "~/components"; Agents need memory to be useful over time. Without it, every conversation starts from zero. The agent forgets who the user is, what it learned, and what it was doing. Memory is what turns a stateless LLM call into a persistent, context-aware agent. -The [Session API](/agents/runtime/capabilities/sessions/) provides the memory layer for agents built on the Cloudflare Agents SDK. It manages two kinds of memory: **conversation history** (the messages and tool calls that make up a session) and **context memory** (persistent blocks injected into the system prompt that the agent can read, write, search, and load). +The [Session API](/agents/runtime/capabilities/sessions/) provides the memory layer for agents built on the Cloudflare Agents SDK. It manages two kinds of memory: **conversation history** (the messages and tool calls that make up a session) and **context memory** (persistent blocks injected into the system prompt that the agent can read, write, and search). Use this page when you need more than simple synced state or flat chat history. For small UI state, use [Store and sync state](/agents/agent-class/state/). For basic chat persistence, `AIChatAgent` stores messages for you. For opinionated long-term memory, Think builds on Session and context blocks. @@ -204,182 +204,17 @@ The agent gets a `search_context` tool for querying and a `set_context` tool for This is the right choice when the agent needs to find specific pieces of information from a large collection, rather than loading entire documents. -### Loadable context (Skills) +### On-demand skills -Skills are large pieces of context (complete documents, reference guides, runbooks, templates) that the agent can discover and load on demand. Think of them as reference material on a shelf: the agent sees a list of titles and descriptions, picks what is relevant to the current task, loads it, uses it, and unloads it when done. +Some context is too large to keep in every prompt: runbooks, reference guides, templates, and scripts. [Agent Skills](/agents/skills/) cover this case. The agent sees a short catalog of skill names and descriptions, and loads a full skill with the `activate_skill` tool only when a task needs it. -Unlike searchable context where the agent retrieves small chunks from a larger collection, skills are designed to be loaded whole. When an agent loads a skill, it gets the entire document in its context window. - -Skills are backed by the `SkillProvider` interface. A skill provider has three methods: - -- **`get()`** returns a metadata listing (titles and descriptions) that appears in the system prompt -- **`load(key)`** fetches the full content of a specific skill -- **`set(key, content, description?)`** writes or updates a skill entry (optional) - -The system prompt shows available skills as a listing. The `[loadable]` tag tells the LLM that these entries are not inline. It needs to use a tool to access the full content: - -```txt -══════════════════════════════════════════════ -SKILLS [loadable] -══════════════════════════════════════════════ -- api-ref: API Reference documentation -- style-guide: Company style guide -- deploy-checklist: Production deployment checklist -``` - -The agent sees the titles, decides which skill is relevant to the current task, and uses `load_context` to pull the full content into its working context. When it is done, it uses `unload_context` to free the space. When the skill provider implements `set()`, the agent can also write back, updating existing skills or creating new ones. - -```txt -Agent sees: "- deploy-checklist: Production deployment checklist" -User asks: "Walk me through a production deployment" -Agent calls: load_context({ block: "skills", key: "deploy-checklist" }) -→ Full checklist content is loaded into the agent's working context -``` - -#### R2-backed skills - -The built-in `R2SkillProvider` stores skills in a Cloudflare R2 bucket. Each skill is an R2 object with optional custom metadata for descriptions. - - - -```ts -import { Session, R2SkillProvider } from "agents/experimental/memory/session"; - -const session = Session.create(this) - .withContext("soul", { - provider: { - get: async () => - [ - "You are a helpful assistant with access to skills.", - "When a user asks you to do something, check the SKILLS section", - "for a relevant skill and use load_context to load it.", - ].join("\n"), - }, - }) - .withContext("memory", { - description: "Learned facts", - maxTokens: 1100, - }) - .withContext("skills", { - provider: new R2SkillProvider(env.SKILLS_BUCKET, { prefix: "skills/" }), - }) - .withCachedPrompt(); -``` - - - -The `prefix` option scopes the provider to a subdirectory in the bucket. Skill keys in the metadata listing are shown without the prefix, so `skills/api-ref` becomes `api-ref` in the system prompt. - -Use `keys` to allowlist specific prefix-relative skills for `get()` and `load()`: - - - -```ts -new R2SkillProvider(env.SKILLS_BUCKET, { - prefix: "skills/", - keys: ["deploy-checklist", "api-ref"], -}); -``` - - - -Add an R2 bucket binding to your Wrangler configuration: - - - -```jsonc -{ - "r2_buckets": [ - { - "binding": "SKILLS_BUCKET", - "bucket_name": "my-agent-skills" - } - ] -} -``` - - - -Skills are regular R2 objects. Upload them through any R2 interface (the Wrangler CLI, the dashboard, or the Workers API): - -```sh -# Upload a skill from a file -wrangler r2 object put my-agent-skills/skills/style-guide --file ./docs/style-guide.md --content-type text/markdown -``` - -To add descriptions (shown in the metadata listing), set custom metadata on the R2 object: - - - -```ts -await env.SKILLS_BUCKET.put("skills/api-ref", content, { - customMetadata: { description: "API Reference documentation" }, -}); -``` - - - -#### Custom skill providers - -You can back skills with any storage by implementing the `SkillProvider` interface: - - - -```ts -import type { SkillProvider } from "agents/experimental/memory/session"; - -class DatabaseSkillProvider implements SkillProvider { - private db: D1Database; - - constructor(db: D1Database) { - this.db = db; - } - - async get(): Promise { - const rows = await this.db - .prepare("SELECT key, description FROM skills ORDER BY key") - .all(); - if (rows.results.length === 0) return null; - return rows.results - .map((r) => `- ${r.key}${r.description ? `: ${r.description}` : ""}`) - .join("\n"); - } - - async load(key: string): Promise { - const row = await this.db - .prepare("SELECT content FROM skills WHERE key = ?") - .bind(key) - .first(); - return row ? (row.content as string) : null; - } - - async set(key: string, content: string, description?: string): Promise { - await this.db - .prepare( - "INSERT INTO skills (key, content, description) VALUES (?, ?, ?) " + - "ON CONFLICT(key) DO UPDATE SET content = ?, description = ?", - ) - .bind(key, content, description ?? null, content, description ?? null) - .run(); - } -} -``` - - - -The Session detects the `load()` method via duck-typing and generates the appropriate tools automatically. - -#### Skills vs other memory types +Refer to [Agent Skills](/agents/concepts/agent-skills/) for how the format works. Skills are files, not memory blocks. Store them in the agent's [Computer Workspace](/agents/skills/sources/computer/), in [R2](/agents/skills/sources/r2/), or [in code](/agents/skills/sources/code/). | Aspect | Skills | Writable context | Searchable context | | -------------------- | ----------------------------------- | ------------------------ | ---------------------------------- | -| **In system prompt** | Metadata listing only | Full content | Summary count | -| **Access pattern** | Load whole document by key | Always visible | Search by query | -| **Best for** | Large documents, reference material | Short notes, preferences | Large collections of small entries | -| **Context cost** | Low (until loaded) | Proportional to content | Low (until searched) | -| **Agent writes?** | Optional (if `set` implemented) | Yes (via `set_context`) | Yes (via `set_context`) | - -The key distinction: skills are **lazy**. They cost nearly nothing in the system prompt until the agent decides it needs one. This makes them ideal for large reference material where only a subset is relevant to any given conversation. +| **In system prompt** | Names and descriptions only | Full content | Entry count only | +| **Loaded** | Whole skill, on demand | Always | Top matching chunks, on demand | +| **Best for** | Procedures, references, scripts | Facts, preferences | Large knowledge bases | ## How agents interact with memory @@ -407,9 +242,7 @@ The Session generates tools dynamically based on what provider types are present | Tool | Generated when | What it does | | -------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | -| **`set_context`** | Any writable, skill, or search block exists | Writes content to a named block. For writable blocks, replaces or appends. For skill/search blocks, writes a keyed entry. | -| **`load_context`** | Any skill block exists | Loads full content of a document by key into the agent's context. | -| **`unload_context`** | Any skill block exists | Frees context space by removing a previously loaded document. The document remains available for re-loading. | +| **`set_context`** | Any writable or search block exists | Writes content to a named block. For writable blocks, replaces or appends. For search blocks, writes a keyed entry. | | **`search_context`** | Any search block exists | Full-text search within a searchable block. Returns the top results ranked by relevance. | | **`session_search`** | Using `SessionManager` | Searches across all sessions (cross-conversation search). | @@ -437,15 +270,9 @@ User's project uses React and TypeScript. KNOWLEDGE (Searchable knowledge base) [searchable] ══════════════════════════════════════════════ 12 entries indexed. - -══════════════════════════════════════════════ -SKILLS [loadable] -══════════════════════════════════════════════ -- api-ref: API Reference documentation -- style-guide: Company style guide ``` -The tags (`[readonly]`, `[writable]`, `[searchable]`, `[loadable]`) tell the LLM what kind of interaction is possible with each block. Token budgets show the agent how much space remains in writable blocks, helping it manage its own memory. +The tags (`[readonly]`, `[writable]`, `[searchable]`) tell the LLM what kind of interaction is possible with each block. Token budgets show the agent how much space remains in writable blocks, helping it manage its own memory. ## Gotchas diff --git a/src/content/docs/agents/harnesses/opencode.mdx b/src/content/docs/agents/harnesses/opencode.mdx index e72696bae4b..35fe053a084 100644 --- a/src/content/docs/agents/harnesses/opencode.mdx +++ b/src/content/docs/agents/harnesses/opencode.mdx @@ -339,4 +339,4 @@ export default Plugin.define({ -Import this plugin and add it to `plugins`. The `content` supplies instructions without reading a local file at runtime. Refer to [OpenCode skills](https://opencode.ai/v2/docs/build/plugins/#skills) for registration and [Agent Skills](/agents/runtime/execution/agent-skills/) for authoring guidance. +Import this plugin and add it to `plugins`. The `content` supplies instructions without reading a local file at runtime. Refer to [OpenCode skills](https://opencode.ai/v2/docs/build/plugins/#skills) for registration and [Agent Skills](/agents/skills/) for authoring guidance. diff --git a/src/content/docs/agents/harnesses/pi/extensions.mdx b/src/content/docs/agents/harnesses/pi/extensions.mdx index c5bda34f4e8..c330efe392f 100644 --- a/src/content/docs/agents/harnesses/pi/extensions.mdx +++ b/src/content/docs/agents/harnesses/pi/extensions.mdx @@ -129,15 +129,14 @@ this.registry.install({ ## Add skills -`skills(sources)` from `agents/harness/pi` turns [Agent Skills](/agents/runtime/execution/agent-skills/) sources from `agents/skills` into a Pi extension named `agents.skills`. It has two tools, `activate_skill` and `read_skill_resource`, and a `skills` section that lists the skills. It reads the sources, so install it in an `async` factory: +`skills(sources)` from `agents/harness/pi` turns [Agent Skills](/agents/skills/) sources from `agents/skills` into a Pi extension named `agents.skills`. Sources can read skills from a [Computer Workspace](/agents/skills/sources/computer/), [R2](/agents/skills/sources/r2/), or a [manifest](/agents/skills/sources/code/). The extension has two tools, `activate_skill` and `read_skill_resource`, and a `skills` section that lists the skills. It reads the sources, so install it in an `async` factory: ```ts import { skills } from "agents/harness/pi"; -import { fromManifest } from "agents/skills"; +import { manifest } from "agents/skills"; -const handbook = fromManifest({ +const handbook = manifest({ id: "handbook", - fingerprint: "v1", skills: [ { name: "release-notes", @@ -155,7 +154,7 @@ harness = new PiHarness({ }); ``` -The tools match the ones [Think](/agents/harnesses/think/) offers, so a skill written for one works in the other. The model can read a skill's instructions and resources. Skill scripts do not run. +The tools match the ones [Think](/agents/harnesses/think/) offers, so a skill written for one works in the other. The sources are read once, when the factory builds the extension. The model can read a skill's instructions and resources. To run skill scripts, give the model a shell or Workspace tool. Refer to [Execution](/agents/skills/execution/). ## Use more of Pi Durable diff --git a/src/content/docs/agents/harnesses/think/configuration.mdx b/src/content/docs/agents/harnesses/think/configuration.mdx index 6867a37882a..5b167a0bc2c 100644 --- a/src/content/docs/agents/harnesses/think/configuration.mdx +++ b/src/content/docs/agents/harnesses/think/configuration.mdx @@ -17,7 +17,7 @@ Think is configured by overriding methods and properties on your `Think` subclas | Method / Property | Default | Description | | -------------------------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `getModel()` | throws | Return the `LanguageModel` to use | -| `getSystemPrompt()` | `"You are a helpful assistant."` | System prompt (fallback when no context blocks) | +| `getSystemPrompt()` | `"You are a helpful assistant."` | Instructions at the top of the system prompt, above context blocks. The default is used only with no context blocks | | `getTools()` | `{}` | AI SDK `ToolSet` for the agentic loop | | `getScheduledTasks()` | `{}` | Code-declared recurring prompts or handlers — refer to [Scheduled tasks](/agents/harnesses/think/scheduled-tasks/) | | `getDefaultTimezone()` | `undefined` | Default timezone for wall-clock scheduled tasks | @@ -25,8 +25,8 @@ Think is configured by overriding methods and properties on your `Think` subclas | `maxSteps` | `10` | Max tool-call rounds per turn | | `sendReasoning` | `true` | Send reasoning chunks to chat clients | | `configureSession()` | identity | Add context blocks, compaction, search, skills — refer to [Sessions](/agents/runtime/capabilities/sessions/) | -| `getSkills()` | `[]` | Return Agent Skills sources for on-demand skill activation — refer to [Agent Skills](/agents/runtime/execution/agent-skills/) | -| `getSkillScriptRunner()` | `null` | Enable the optional `run_skill_script` tool | +| `getSkills()` | `[]` | Return Agent Skills sources for on-demand skill activation — refer to [Agent Skills](/agents/skills/) | +| `getSkillScriptRunner()` | `null` | Enable the experimental `run_skill_script` tool — refer to [Execution](/agents/skills/execution/) | | `messageConcurrency` | `"queue"` | How overlapping submits behave — refer to [Client tools](/agents/harnesses/think/client-tools/#message-concurrency) | | `includeMcpTools` | `true` | Convert connected MCP tools to AI SDK tools and add them to model turns. Refer to [MCP tools](/agents/harnesses/think/tools/#mcp-tools) | | `waitForMcpConnections` | `false` | Wait for MCP servers before inference | @@ -115,7 +115,7 @@ export class MyAgent extends Think { -When `configureSession` adds context blocks, Think builds the system prompt from those blocks instead of using `getSystemPrompt()`. Think's `this.messages` getter reads directly from Session's tree-structured storage. +When `configureSession` adds context blocks, Think builds the system prompt from an overridden `getSystemPrompt()` followed by those blocks. The default `getSystemPrompt()` is only used when there are no blocks. Think's `this.messages` getter reads directly from Session's tree-structured storage. For the full Session API — context blocks, compaction, search, skills, and multi-session support — refer to the [Sessions documentation](/agents/runtime/capabilities/sessions/). @@ -150,4 +150,4 @@ Bundled with `@cloudflare/think`: | ---------------------- | ---------------------------------------- | | `@cloudflare/codemode` | Code execution for `createExecuteTool()` | -The Agent Skills engine and its script runner live in [`agents/skills`](/agents/runtime/execution/agent-skills/), so skill scripts pull `@cloudflare/worker-bundler` and `just-bash` through `agents`, not Think. +The Agent Skills parser, sources, and script runner live in [`agents/skills`](/agents/skills/), so the script runner pulls `just-bash` through `agents`, not Think. diff --git a/src/content/docs/agents/harnesses/think/getting-started.mdx b/src/content/docs/agents/harnesses/think/getting-started.mdx index 849498adae0..a5148790226 100644 --- a/src/content/docs/agents/harnesses/think/getting-started.mdx +++ b/src/content/docs/agents/harnesses/think/getting-started.mdx @@ -252,7 +252,7 @@ export class MyAgent extends Think { Now the model sees a `MEMORY` section in its system prompt and gets a `set_context` tool to update it. Facts written to memory persist in SQLite and survive Durable Object hibernation and restarts. -When you use `configureSession`, the system prompt is built from context blocks rather than `getSystemPrompt()`. The `"soul"` block above acts as the system identity — it is read-only and always appears first. The `"memory"` block is writable, and the model proactively updates it when it learns something useful. +When you use `configureSession`, the system prompt is built from context blocks, with an overridden `getSystemPrompt()` at the top. The `"soul"` block in the previous example acts as the system identity — it is read-only and always appears first. The `"memory"` block is writable, and the model proactively updates it when it learns something useful. Refer to the [Sessions documentation](/agents/runtime/capabilities/sessions/) for context blocks, compaction, search, skills, and multi-session support. diff --git a/src/content/docs/agents/harnesses/think/index.mdx b/src/content/docs/agents/harnesses/think/index.mdx index 8e1ae903497..859ae55a7a8 100644 --- a/src/content/docs/agents/harnesses/think/index.mdx +++ b/src/content/docs/agents/harnesses/think/index.mdx @@ -374,7 +374,7 @@ Use [`startFiber()`](/agents/agent-class/durable-execution/#startfiber) outside /> diff --git a/src/content/docs/agents/harnesses/think/lifecycle-hooks.mdx b/src/content/docs/agents/harnesses/think/lifecycle-hooks.mdx index 241f25649dd..f79b47aad94 100644 --- a/src/content/docs/agents/harnesses/think/lifecycle-hooks.mdx +++ b/src/content/docs/agents/harnesses/think/lifecycle-hooks.mdx @@ -60,7 +60,7 @@ beforeTurn(ctx: TurnContext): TurnConfig | void | Promise | Field | Type | Description | | -------------- | ------------------------- | ---------------------------------------------------------------------------- | -| `system` | `string` | Assembled system prompt (from context blocks or `getSystemPrompt()`) | +| `system` | `string` | Assembled system prompt (`getSystemPrompt()` override, then context blocks) | | `messages` | `ModelMessage[]` | Assembled model messages (truncated, pruned) | | `tools` | `ToolSet` | Merged tool set (workspace + getTools + session + extensions + MCP + client) | | `model` | `LanguageModel` | The model from `getModel()` | diff --git a/src/content/docs/agents/harnesses/think/tools.mdx b/src/content/docs/agents/harnesses/think/tools.mdx index 5328b49d3f6..6e06aed2214 100644 --- a/src/content/docs/agents/harnesses/think/tools.mdx +++ b/src/content/docs/agents/harnesses/think/tools.mdx @@ -20,7 +20,7 @@ On every turn, Think merges tools from multiple sources. Later sources override 2. **`getTools()`** — your custom server-side tools 3. **Extension tools** — tools from loaded extensions (prefixed by extension name) 4. **Session tools** — `set_context`, `load_context`, `search_context` (from `configureSession`) -5. **Skill tools** — `activate_skill`, `read_skill_resource`, `run_skill_script` (from `getSkills()`, refer to [Agent Skills](/agents/runtime/execution/agent-skills/)) +5. **Skill tools** — `activate_skill`, `read_skill_resource`, `run_skill_script` (from `getSkills()`, refer to [Agent Skills](/agents/skills/)) 6. **MCP tools** — from connected MCP servers when `includeMcpTools` is `true` 7. **Client tools** — from the browser (refer to [Client tools](/agents/harnesses/think/client-tools/)) diff --git a/src/content/docs/agents/runtime/execution/agent-skills.mdx b/src/content/docs/agents/runtime/execution/agent-skills.mdx deleted file mode 100644 index 7bef060ae58..00000000000 --- a/src/content/docs/agents/runtime/execution/agent-skills.mdx +++ /dev/null @@ -1,141 +0,0 @@ ---- -title: Agent Skills -pcx_content_type: reference -description: Give an agent a catalog of on-demand instructions, resources, and scripts with agents/skills, activated by the model only when a task matches. -sidebar: - order: 17 -products: - - agents ---- - -import { TypeScriptExample, WranglerConfig } from "~/components"; - -Agent Skills are on-demand instructions, resources, and scripts. A skill source provides a catalog of skill names and descriptions; the agent adds that catalog to the system prompt and exposes tools the model can use when a user task matches a skill — so a large library of capabilities does not bloat every prompt. - -:::note -Agent Skills are experimental, and script execution in particular is early. The API may change in a future release. -::: - -The skills engine lives in `agents/skills` and is framework-agnostic, so any agent (including a plain [`AIChatAgent`](/agents/agent-class/communication-channels/chat/chat-agents/) `onChatMessage`) can build a `SkillRegistry`. [`@cloudflare/think`](/agents/harnesses/think/) re-exports it as the `skills` namespace and wires `getSkills()` into the turn automatically. - -## Using skills with Think - -Bundled skills are usually imported with the Agents Vite plugin: - - - -```ts -import { Think, skills } from "@cloudflare/think"; -import bundledSkills from "agents:skills"; // resolves to ./skills next to this file - -type Env = { - AI: Ai; - LOADER: WorkerLoader; - SKILLS_BUCKET: R2Bucket; -}; - -export class MyAgent extends Think { - getSkills() { - return [ - bundledSkills, - skills.r2(this.env.SKILLS_BUCKET, { prefix: "skills/" }), - ]; - } - - getSkillScriptRunner() { - return skills.runner({ - loader: this.env.LOADER, - workspaceInstance: this.workspace, - }); - } -} -``` - - - -`agents:skills` resolves to a `./skills` directory next to the importing file; use `agents:skills/` to point at a differently named sibling directory. The `agents:skills` import is typed by ambient declarations that ship with `agents`, so importing `Think` in the same file brings the type into scope (for a file that imports only the specifier, add `/// `). If you are not using the Agents Vite plugin, build a source with `skills.fromManifest(...)` instead. - -Sources are applied in order; the first source to register a skill name wins, and later duplicates (or a source that fails to load) are skipped with a logged warning rather than failing the agent. - -The imported directory should contain one child directory per skill: - -```text -src/skills/release-notes/SKILL.md -src/skills/release-notes/scripts/format-release-notes.ts -src/skills/release-notes/references/style-guide.md -``` - -## Skill tools - -When skills are available, the agent exposes: - -| Tool | Purpose | -| --------------------- | ------------------------------------------------------------------- | -| `activate_skill` | Load a matching skill's instructions and bundled resource list | -| `read_skill_resource` | Read a bundled resource by `{ name, path }` or `skill-name/path` | -| `run_skill_script` | Run a bundled script when `getSkillScriptRunner()` returns a runner | - -Skills are not always-on system prompt text. Use `getSystemPrompt()` or a Session context block for behavior that should apply to every turn. Use skills for task-specific procedures, references, scripts, templates, and assets that should be loaded only when relevant. - -## Script execution - -Script execution is opt-in and requires a Worker Loader binding: - - - -```jsonc -{ - "worker_loaders": [{ "binding": "LOADER" }] -} -``` - - - -`skills.runner()` is experimental and runs JavaScript, TypeScript, Python, and Bash scripts under `scripts/`. TypeScript is compiled with `@cloudflare/worker-bundler`; Python runs as Python Dynamic Workers; Bash runs through `just-bash`. - -JavaScript and TypeScript scripts are function-style: - - - -```ts -import type { SkillRunContext } from "@cloudflare/think"; - -export default async function run(input: unknown, ctx: SkillRunContext) { - const guide = ctx.files["references/style-guide.md"]; // bundled text resources - const docs = await ctx.workspace.readFile("README.md"); // gated by permission - const summary = await ctx.tools.call("summarize", { input }); // explicit tools - await ctx.output.writeFile("notes.md", summary); // scratch artifact - return { ok: true }; -} -``` - - - -`ctx` is `{ skill, files, workspace, tools, output }`. `ctx.files` holds bundled text resources by relative path, `ctx.workspace` is gated by the workspace permission, `ctx.tools` only exposes tools the runner was given, and `ctx.output.writeFile(name, content)` returns scratch artifacts to the model (it does not mutate the workspace). Python and Bash use the path-based contract instead: `/input.json`, `/context.json`, bundled resources under `/skill`, and `/output` for artifacts. - -`workspaceInstance` must be a `Workspace` from [`@cloudflare/computer`](https://github.com/cloudflare/computer). In Think, `this.workspace` already is one. Relative paths in skill scripts resolve against `/workspace`. - -Passing `workspaceInstance` gives scripts read-only workspace access by default. Network access, tools, and workspace writes are opt-in. The default timeout is 30 seconds. - -## Example - - - -```ts -import { Think, skills } from "@cloudflare/think"; - -export class SkillsAgent extends Think { - getSkills() { - return [skills.r2(this.env.SKILLS_BUCKET, { prefix: "skills/" })]; - } -} -``` - - - -Refer to the [`agent-skills` example](https://github.com/cloudflare/agents/tree/main/examples/agent-skills) for bundled skills, R2-backed skills, and script execution. - -## Related - -- [Think](/agents/harnesses/think/) — wires `getSkills()` and `getSkillScriptRunner()` into the agentic loop -- [Think tools](/agents/harnesses/think/tools/) — how skill tools merge with workspace, custom, MCP, and client tools diff --git a/src/content/docs/agents/runtime/execution/index.mdx b/src/content/docs/agents/runtime/execution/index.mdx deleted file mode 100644 index 35ffa967167..00000000000 --- a/src/content/docs/agents/runtime/execution/index.mdx +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: Execution -pcx_content_type: navigation -sidebar: - order: 5 - group: - hideIndex: true ---- - -import { DirectoryListing } from "~/components"; - - diff --git a/src/content/docs/agents/skills/execution.mdx b/src/content/docs/agents/skills/execution.mdx new file mode 100644 index 00000000000..c2e86008161 --- /dev/null +++ b/src/content/docs/agents/skills/execution.mdx @@ -0,0 +1,144 @@ +--- +title: Execution +pcx_content_type: reference +description: Run Agent Skills scripts with a Computer Workspace backend, or with the experimental skill script runner. +sidebar: + order: 3 +products: + - agents +--- + +import { TypeScriptExample, WranglerConfig } from "~/components"; + +[Agent Skills](/agents/skills/) can bundle scripts under `scripts/`. An agent runs them in one of two ways: + +| Where the skill lives | How scripts run | +| ------------------------------------------------------ | ------------------------------------------------------------- | +| [Computer Workspace](/agents/skills/sources/computer/) | The model runs the file with the Workspace `exec` tool | +| [R2](/agents/skills/sources/r2/) or a [manifest](/agents/skills/sources/code/) | The experimental script runner, through `run_skill_script` | + +## Run scripts in the Workspace + +When skills are files in a Workspace, a script is a file the agent can run like any other. Give the Workspace a backend, and Think adds the `exec` tool. Refer to [Computer](/agents/skills/sources/computer/#run-skill-scripts) for the setup. + +Activating a skill returns its folder as `location`, for example `/workspace/.agents/skills/release-notes`. With the JavaScript backend, the skill's instructions can tell the model to run a script from that folder: + +```js +export { default } from "./scripts/format-release-notes.js"; +``` + +The script exports a default async function. It receives the `input` that the model passes to `exec`, and can use `node:fs` to read the Workspace: + +```js +import { readdir } from "node:fs/promises"; + +export default async function (input) { + const entries = await readdir(input.root ?? "/workspace"); + return { count: entries.length }; +} +``` + +## Script runner + +:::caution +The script runner is experimental, and its options may change. Prefer storing skills in a Computer Workspace and running scripts with `exec`. +::: + +For skills that do not live in a Workspace, `skills.runner()` runs bundled scripts in a Dynamic Worker and adds the `run_skill_script` tool. It needs a Worker Loader binding: + + + +```jsonc +{ + "worker_loaders": [{ "binding": "LOADER" }], +} +``` + + + +Return the runner from `getSkillScriptRunner()`: + + + +```ts +import { Think, skills } from "@cloudflare/think"; + +export class MyAgent extends Think { + getSkills() { + return [skills.r2(this.env.SKILLS_BUCKET, { prefix: "skills/" })]; + } + + getSkillScriptRunner() { + return skills.runner({ loader: this.env.LOADER }); + } +} +``` + + + +Outside Think, import the runner from `agents/skills/runner`. It is a separate entry point because it loads `just-bash` and Code Mode: + + + +```ts +import { runner } from "agents/skills/runner"; + +const scriptRunner = runner({ loader: env.LOADER }); +``` + + + +The runner runs JavaScript, Python, and Bash scripts under `scripts/`. Python runs as a Python Dynamic Worker, and Bash runs through `just-bash`. There is no bundler at runtime: a JavaScript script must be one self-contained module. Compile TypeScript and multi-file scripts, for example with esbuild, before you store the skill. + +### JavaScript scripts + +JavaScript scripts export a default function that takes the input and a context object: + +```js +export default async function run(input, ctx) { + const guide = ctx.files["references/style-guide.md"]; // bundled text resources + const docs = await ctx.workspace.readFile("README.md"); // gated by permission + const summary = await ctx.tools.call("summarize", { input }); // explicit tools + await ctx.output.writeFile("notes.md", summary); // scratch artifact + return { ok: true }; +} +``` + +`ctx` is typed as `SkillRunContext` and contains: + +| Field | Description | +| ----------- | --------------------------------------------------------------------------------- | +| `skill` | The skill's name, description, and metadata | +| `files` | The skill's text resources, by relative path | +| `workspace` | Workspace reads and writes, gated by the `workspace` option | +| `tools` | Only the tools passed to the runner, as `tools.call(name, input)` or `tools.name(input)` | +| `output` | `writeFile(name, content)` returns scratch files to the model without changing the Workspace | + +### Python and Bash scripts + +Python and Bash scripts use files instead of a context object: + +- `/input.json` holds the input. +- `/context.json` holds the skill metadata. +- `/skill` holds `SKILL.md` and the skill's resources. +- Files written to `/output` are returned to the model. + +Python scripts can also define `def run(input, ctx)`. + +### Runner options + +| Option | Default | Description | +| ------------------- | ---------------------------------------- | ---------------------------------------------------- | +| `loader` | Required | Worker Loader binding | +| `workspaceInstance` | None | A Computer Workspace that scripts can access | +| `workspace` | `"read"` with `workspaceInstance`, else `"none"` | `"none"`, `"read"`, or `"read-write"` | +| `tools` | None | Tools scripts can call | +| `network` | `false` | Allow outbound network access | +| `timeout` | `30000` | Timeout in milliseconds | + +Scripts get no network access and no tools unless you grant them. Relative paths in `ctx.workspace` resolve against `/workspace`. + +## Related + +- [Think tools](/agents/harnesses/think/tools/): how skill tools merge with workspace, custom, MCP, and client tools +- [Sandbox](/agents/tools/sandbox/): run code in a container diff --git a/src/content/docs/agents/skills/index.mdx b/src/content/docs/agents/skills/index.mdx new file mode 100644 index 00000000000..5914137777e --- /dev/null +++ b/src/content/docs/agents/skills/index.mdx @@ -0,0 +1,167 @@ +--- +title: Agent Skills +pcx_content_type: overview +description: Give an agent on-demand instructions, references, and scripts as Agent Skills, loaded from a Computer Workspace, R2, or code. +sidebar: + order: 4.8 + group: + hideIndex: false + label: Skills +products: + - agents +--- + +import { DirectoryListing, TypeScriptExample } from "~/components"; + +[Agent Skills](https://agentskills.io/) are on-demand instructions, references, and scripts for an agent. Each skill is a directory with a `SKILL.md` file. The agent sees a short catalog of skill names and descriptions. It loads a full skill only when a task matches it, so a large library of skills does not fill every prompt. + +For background on the format and how agents use it, refer to [Agent Skills concepts](/agents/concepts/agent-skills/). + +:::note +Agent Skills are experimental. The API may change in a future release. These pages describe [Agents SDK v0.32.0](https://github.com/cloudflare/agents/releases/tag/agents%400.32.0) and Think v0.23.0 or later. +::: + +## What a skill looks like + +A skill is a directory named after the skill. It holds a `SKILL.md` file and, optionally, other files: + +```text +release-notes/ + SKILL.md + references/style-guide.md + scripts/format-release-notes.js + assets/logo.png +``` + +`SKILL.md` starts with YAML frontmatter, followed by Markdown instructions: + +```md +--- +name: release-notes +description: Turn a list of changes into release notes. Use when the user asks for release notes or a changelog entry. +metadata: + author: my-team +--- + +1. Read `references/style-guide.md`. +2. Group the changes under Features, Fixes, and Other. +``` + +The files in `scripts/`, `references/`, and `assets/` are the skill's resources. The model reads them when the instructions call for them. + +## Load skills into an agent + +Skills come from a source. Return sources from `getSkills()` in a [Think](/agents/harnesses/think/) agent: + + + +```ts +import { Think, skills } from "@cloudflare/think"; + +export class MyAgent extends Think { + getSkills() { + return [ + skills.workspace(this.workspace), + skills.r2(this.env.SKILLS_BUCKET, { prefix: "skills/" }), + ]; + } +} +``` + + + +| Source | Reads skills from | +| -------------------------------------------------------- | ---------------------------------------------------------- | +| [`skills.workspace()`](/agents/skills/sources/computer/) | Files in a `@cloudflare/computer` Workspace | +| [`skills.r2()`](/agents/skills/sources/r2/) | Objects in an R2 bucket | +| [`skills.manifest()`](/agents/skills/sources/code/) | Skills defined in code | + +Sources are applied in order. The first source to provide a skill name wins. Later duplicates are skipped with a logged warning. Think lists every source again before each turn, so new, edited, and deleted skills take effect on the next turn. + +The [Pi harness](/agents/harnesses/pi/extensions/#add-skills) takes the same sources through `skills(sources)` from `agents/harness/pi`. + +## Skill tools + +When at least one skill is available, the agent gets these tools: + +| Tool | Purpose | +| --------------------- | ----------------------------------------------------------------------------- | +| `activate_skill` | Load a skill's instructions and its list of resources | +| `read_skill_resource` | Read a resource by `{ name, path }` or a qualified path such as `name/path` | +| `run_skill_script` | Run a bundled script, when a [script runner](/agents/skills/execution/) is set | + +The catalog is added to the system prompt as its own context block, below your `getSystemPrompt()` instructions. When a skill changes mid-conversation, Think does not rebuild the system prompt, so the model provider's prompt cache stays warm. The new catalog reaches the model as a reminder on the next turn and moves into the system prompt at the next compaction or history clear. + +Use `getSystemPrompt()` or a context block from `configureContext()` for instructions that apply to every turn. Use skills for procedures, references, and scripts that only some tasks need. + +## Validation + +Every `SKILL.md` is checked against these rules from the [Agent Skills specification](https://agentskills.io/specification): + +- `name` must be 1 to 64 lowercase letters, digits, and hyphens. It cannot start or end with a hyphen or contain `--`. It must match the skill's directory name. +- `description` is required and can be at most 1,024 characters. +- `compatibility`, when present, must be 1 to 500 characters. +- `metadata`, when present, must map string keys to string values. +- `allowed-tools`, when present, must be a space-separated string. + +Skills load the way the specification's [guide for agents](https://agentskills.io/client-implementation/adding-skills-support) recommends. Skills written for other agents often break a rule, so a skill that breaks one still loads, and the agent logs a warning: + +| Problem | What happens | +| --- | --- | +| `name` breaks the format, is too long, or does not match the directory | Loads, with a warning | +| `name` is missing | Loads with the directory name, with a warning | +| `description` or `compatibility` is too long | Loads, with a warning | +| `license`, `metadata`, or `allowed-tools` is invalid | Loads without that field, with a warning | +| A value contains an unquoted `: `, so the YAML does not parse | Loads after quoting the value, with a warning | +| `description` is missing | Skipped and logged | +| `name` contains `/`, `\`, or control characters, or is `.` or `..` | Skipped and logged | +| The frontmatter is missing or still not YAML | Skipped and logged | + +Skipped skills are not shown to the model. Frontmatter keys that the specification does not define are kept in `skill.extraFields`. A skill with `disable-model-invocation: true` is not shown to the model. + +To check a skill before you write or publish it, parse it with `parseSkill()` from `agents/skills`. A skill follows the specification when it parses with no warnings: + + + +```ts +import { parseSkill } from "agents/skills"; + +const parsed = parseSkill(markdown, { directory: "release-notes" }); +if (!parsed.ok) { + console.log(parsed.error.message); // the skill cannot be used +} else { + for (const warning of parsed.warnings) { + console.log(warning.code, warning.message); // for example, "name-mismatch" + } +} +``` + + + +## Use skills without Think + +The parser, sources, and catalog live in `agents/skills` and do not depend on Think. `loadSkillCatalog()` merges sources. `createSkillTools()` from `agents/skills/tools/ai-sdk` builds the [AI SDK](https://ai-sdk.dev/) tools for a catalog: + + + +```ts +import { streamText } from "ai"; +import { loadSkillCatalog, workspace } from "agents/skills"; +import { createSkillTools } from "agents/skills/tools/ai-sdk"; + +// `computerWorkspace` is a Workspace from @cloudflare/computer. +const catalog = await loadSkillCatalog([workspace(computerWorkspace)]); + +const result = streamText({ + model, + system: catalog.prompt() ?? undefined, + tools: createSkillTools(catalog), + messages, +}); +``` + + + +## Pages in this section + + diff --git a/src/content/docs/agents/skills/sources/code.mdx b/src/content/docs/agents/skills/sources/code.mdx new file mode 100644 index 00000000000..64a1c59a92d --- /dev/null +++ b/src/content/docs/agents/skills/sources/code.mdx @@ -0,0 +1,84 @@ +--- +title: Code +pcx_content_type: how-to +description: Define Agent Skills in code with skills.manifest(), for tests and small fixed sets of skills. +sidebar: + order: 3 +products: + - agents +--- + +import { TypeScriptExample } from "~/components"; + +`skills.manifest()` creates a source from [Agent Skills](/agents/skills/) defined in code. It needs no Workspace or bucket, which suits tests and small sets of skills that ship with your Worker. + +## Define skills + +Each entry holds the `SKILL.md` fields and, optionally, its resources: + + + +```ts +import { Think, skills } from "@cloudflare/think"; + +const handbook = skills.manifest({ + id: "handbook", + skills: [ + { + name: "release-notes", + description: "Turn a list of changes into release notes.", + body: "Read `references/style-guide.md`, then write one line per change.", + metadata: { author: "my-team" }, + resources: [ + { + path: "references/style-guide.md", + content: "Start each line with a past-tense verb.", + }, + ], + }, + ], +}); + +export class MyAgent extends Think { + getSkills() { + return [handbook]; + } +} +``` + + + +## Entry fields + +| Field | Required | Description | +| --------------- | -------- | ---------------------------------------------------------------- | +| `name` | Yes | Skill name. Must follow the [naming rules](/agents/skills/#validation) | +| `description` | Yes | What the skill does and when to use it | +| `body` | Yes | Markdown instructions | +| `license` | No | License name or bundled license file | +| `compatibility` | No | Environment requirements | +| `metadata` | No | String key-value metadata | +| `allowedTools` | No | Space-separated list of pre-approved tools | +| `resources` | No | Files as `{ path, content }`. Use `encoding: "base64"` for binary files | + +Entries pass the same [checks](/agents/skills/#validation) as `SKILL.md` files: an entry that breaks a rule loads with a warning, and one with no `description` is skipped and logged. + +`skills.fromManifest()` is a deprecated alias of `skills.manifest()`. + +## Combine with other sources + +A manifest works next to other sources. Sources are applied in order, so put the source you want to win first: + + + +```ts +export class MyAgent extends Think { + getSkills() { + return [skills.workspace(this.workspace), handbook]; + } +} +``` + + + +Here, a Workspace skill named `release-notes` replaces the manifest's version. diff --git a/src/content/docs/agents/skills/sources/computer.mdx b/src/content/docs/agents/skills/sources/computer.mdx new file mode 100644 index 00000000000..56ade0807b8 --- /dev/null +++ b/src/content/docs/agents/skills/sources/computer.mdx @@ -0,0 +1,146 @@ +--- +title: Computer +pcx_content_type: how-to +description: Store Agent Skills as files in a Computer Workspace, so skills the agent or a user writes are available on the next turn. +sidebar: + order: 1 +products: + - agents +--- + +import { TypeScriptExample, WranglerConfig } from "~/components"; + +`skills.workspace()` reads [Agent Skills](/agents/skills/) from files in a Workspace from [`@cloudflare/computer`](https://github.com/cloudflare/computer). A skill is a folder in the Workspace. Writing, editing, or deleting a `SKILL.md` changes the agent's skills on its next turn, with no deploy. + +## Read skills from the Workspace + +By default, every `/workspace/.agents/skills//SKILL.md` is a skill. The files beside it are its resources: + +```text +/workspace/.agents/skills/ + release-notes/ + SKILL.md + references/style-guide.md + scripts/format-release-notes.js + pirate-voice/ + SKILL.md +``` + +Return the source from `getSkills()`. In [Think](/agents/harnesses/think/), `this.workspace` is already a Computer Workspace: + + + +```ts +import { Think, skills } from "@cloudflare/think"; + +export class MyAgent extends Think { + getSkills() { + return [skills.workspace(this.workspace)]; + } +} +``` + + + +To read skills from another directory, pass an absolute `root`: + +```ts +skills.workspace(this.workspace, { root: "/workspace/skills" }); +``` + +If the root directory does not exist, the source lists no skills. `.git`, `node_modules`, and `.DS_Store` inside a skill directory are ignored. + +## Add skills to the Workspace + +Skills are plain files, so anything that writes to the Workspace can add one: the agent's own `write` tool, a user upload, or your code. For example, write the starter skills the first time the agent starts: + + + +```ts +import { Think, skills } from "@cloudflare/think"; + +const ROOT = "/workspace/.agents/skills"; + +const STARTER_SKILLS: Record = { + "pirate-voice/SKILL.md": `--- +name: pirate-voice +description: Answer in a pirate voice. Use when the user asks for a pirate tone. +--- + +Answer accurately, then rewrite the answer in a light pirate voice. +`, +}; + +export class MyAgent extends Think { + getSkills() { + return [skills.workspace(this.workspace)]; + } + + async onStart() { + for (const [path, content] of Object.entries(STARTER_SKILLS)) { + const file = `${ROOT}/${path}`; + await this.workspace.fs.mkdir(file.slice(0, file.lastIndexOf("/")), { + recursive: true, + }); + await this.workspace.fs.writeFile(file, content); + } + } +} +``` + + + +Each skill is checked against the [specification](/agents/skills/#validation) when it is listed. A skill whose `name` does not match its folder still loads with a warning. A skill with no `description` is skipped and logged. Use `skills.parseSkill()` to check a skill before you write it: it follows the specification when it parses with no warnings. + +## Run skill scripts + +Skill scripts are Workspace files too. Give the Workspace a backend, and the model runs scripts with the `exec` tool. Activating a skill tells the model where the skill's folder is, so it can run a script from there: + + + +```ts +import { Workspace } from "@cloudflare/computer"; +import { WorkerJavaScriptBackend } from "@cloudflare/computer/backends/worker-javascript"; +import { Think, skills } from "@cloudflare/think"; + +export class MyAgent extends Think { + workspace = new Workspace({ + storage: this.ctx.storage, + backends: [ + new WorkerJavaScriptBackend({ + id: "js", + loader: this.env.LOADER, + access: "read-write", + }), + ], + }); + + getSkills() { + return [skills.workspace(this.workspace)]; + } +} +``` + + + + + +```jsonc +{ + "worker_loaders": [{ "binding": "LOADER" }], +} +``` + + + +With the JavaScript backend, a skill can tell the model to run a script by re-exporting it, with `cwd` set to the skill's folder: + +```js +export { default } from "./scripts/format-release-notes.js"; +``` + +Refer to [Execution](/agents/skills/execution/) for the options. + +## Example + +The [`computer-skills` example](https://github.com/cloudflare/agents/tree/main/examples/computer-skills) stores skills in a Workspace, runs their scripts with `exec`, and lets you add and delete skills from the UI. diff --git a/src/content/docs/agents/skills/sources/index.mdx b/src/content/docs/agents/skills/sources/index.mdx new file mode 100644 index 00000000000..eb36afe877a --- /dev/null +++ b/src/content/docs/agents/skills/sources/index.mdx @@ -0,0 +1,17 @@ +--- +title: Sources +pcx_content_type: navigation +description: Load Agent Skills from a Computer Workspace, an R2 bucket, or code. +sidebar: + order: 2 + group: + hideIndex: true +products: + - agents +--- + +import { DirectoryListing } from "~/components"; + +A source tells an agent where its [Agent Skills](/agents/skills/) live. + + diff --git a/src/content/docs/agents/skills/sources/r2.mdx b/src/content/docs/agents/skills/sources/r2.mdx new file mode 100644 index 00000000000..03930668d0c --- /dev/null +++ b/src/content/docs/agents/skills/sources/r2.mdx @@ -0,0 +1,79 @@ +--- +title: R2 +pcx_content_type: how-to +description: Load Agent Skills from an R2 bucket that uses the Agent Skills directory layout. +sidebar: + order: 2 +products: + - agents +--- + +import { TypeScriptExample, WranglerConfig } from "~/components"; + +`skills.r2()` reads [Agent Skills](/agents/skills/) from an [R2](/r2/) bucket. Use it to share one set of skills across many agents, or to manage skills outside your Worker's code. + +## Store skills in a bucket + +The bucket uses the same layout as a skills folder, under a key prefix. Every `/SKILL.md` object is a skill, and the objects beside it are its resources: + +```text +skills/release-notes/SKILL.md +skills/release-notes/references/style-guide.md +skills/release-notes/scripts/format-release-notes.js +skills/pirate-voice/SKILL.md +``` + +Upload skills with any R2 interface. For example, with Wrangler: + +```sh +npx wrangler r2 object put my-agent-skills/skills/pirate-voice/SKILL.md --file ./pirate-voice/SKILL.md --remote +``` + +## Read skills from the bucket + +Bind the bucket to your Worker: + + + +```jsonc +{ + "r2_buckets": [{ "binding": "SKILLS_BUCKET", "bucket_name": "my-agent-skills" }], +} +``` + + + +Return the source from `getSkills()`: + + + +```ts +import { Think, skills } from "@cloudflare/think"; + +export class MyAgent extends Think { + getSkills() { + return [skills.r2(this.env.SKILLS_BUCKET, { prefix: "skills/" })]; + } +} +``` + + + +## Options + +| Option | Default | Description | +| ------------------- | ------------------ | ----------------------------------------------------------------------- | +| `prefix` | `""` | Key prefix that holds the skill directories | +| `skills` | all skills | Only serve these skill names | +| `refreshIntervalMs` | `60000` | How long to reuse a bucket listing before listing again. `0` lists on every call | +| `id` | `r2:` | Source id used in diagnostics | + +Each `SKILL.md` is checked against the [specification](/agents/skills/#validation) when the bucket is listed. Skills that break a rule load with a warning, and skills with no `description` are skipped and logged. + +## Run skill scripts + +R2 stores files but cannot run them. To run scripts from R2 skills, set the experimental [script runner](/agents/skills/execution/#script-runner), which adds the `run_skill_script` tool. To store and run skills as files, use a [Computer Workspace](/agents/skills/sources/computer/) instead. + +## Example + +The [`r2-skills` example](https://github.com/cloudflare/agents/tree/main/examples/r2-skills) loads skills from a bucket, runs a script with the script runner, and lets you upload and delete skills from the UI. diff --git a/src/content/docs/agents/tools/skills.mdx b/src/content/docs/agents/tools/skills.mdx new file mode 100644 index 00000000000..4f92969d618 --- /dev/null +++ b/src/content/docs/agents/tools/skills.mdx @@ -0,0 +1,184 @@ +--- +title: Skills +description: Give your agent Agent Skills tools to discover, load, and read skills on demand. Use Think, Pi Durable, AI SDK, or your own harness. +pcx_content_type: integration-guide +sidebar: + order: 5 +products: + - agents +--- + +import { PackageManagers, Steps, TypeScriptExample } from "~/components"; + +Give your agent tools to use [Agent Skills](/agents/skills/): on-demand instructions, references, and scripts stored as `SKILL.md` folders. The model sees a short catalog of skill names and descriptions, then loads a skill only when a task matches it. + +Skills come from [sources](/agents/skills/sources/computer/): a Computer Workspace, an R2 bucket, or code. The tools are the same whichever source you use. + +Use the supplied adapters with [Think](/agents/harnesses/think/), [Pi Durable](/agents/harnesses/pi/), or the AI SDK. For another harness, use the catalog functions in `agents/skills`. + +## What the tools add + +| Tool | What it does | +| --------------------- | ------------------------------------------------------------------------------------------------------------- | +| `activate_skill` | Returns a skill's instructions, its folder when it has one, and the list of files bundled with it. | +| `read_skill_resource` | Returns one bundled file, by `{ name, path }` or a qualified path such as `release-notes/references/guide.md`. | +| `run_skill_script` | Runs a bundled script. Only added when you set the experimental [script runner](/agents/skills/execution/#script-runner). | + +The tools only appear when at least one skill is available. `name` is limited to the skills in the catalog. Binary files are returned as base64. + +Alongside the tools, the catalog provides system prompt text that lists each skill: + +```txt +Available skills. When a task matches a skill, use activate_skill with its name before proceeding. + +- release-notes: Turn a list of changes into release notes. +- pirate-voice: Answer in a pirate voice. +``` + +## Add the tools to your harness + +These examples require an existing harness. For a new agent, start with [Think](/agents/harnesses/think/getting-started/) or the [Pi harness guide](/agents/harnesses/pi/). + + + +1. Update the Agents SDK in your Worker project: + + + +2. Choose a [source](/agents/skills/sources/computer/) for your skills. + +3. Register the tools with your harness using one of the examples that follow. + + + +### Think + +Think adds the tools and the catalog for you. Return your sources from `getSkills()`: + + + +```ts +import { Think, skills } from "@cloudflare/think"; + +export class MyAgent extends Think { + getSkills() { + return [skills.workspace(this.workspace)]; + } +} +``` + + + +Think lists the sources again before each turn, so new and edited skills take effect on the next turn. + +### Pi Durable + +In your [Pi harness factory](/agents/harnesses/pi/extensions/#add-skills), install the `skills()` extension. It adds `activate_skill`, `read_skill_resource`, and a `skills` system prompt section: + + + +```ts +import { skills } from "agents/harness/pi"; +import { r2 } from "agents/skills"; + +// Inside the harness factory, before Harness.open(): +this.registry.install( + await skills([r2(this.env.SKILLS_BUCKET, { prefix: "skills/" })]), +); +``` + + + +The extension reads the sources once, when the factory builds it. Both tools are marked `replay: "safe"`. + +### AI SDK + +Load a catalog with `loadSkillCatalog()`, then pass `createSkillTools()` to your `streamText()` or `generateText()` call. Add the catalog prompt to your system prompt: + + + +```ts +import { streamText } from "ai"; +import { loadSkillCatalog, r2 } from "agents/skills"; +import { createSkillTools } from "agents/skills/tools/ai-sdk"; + +const catalog = await loadSkillCatalog([ + r2(env.SKILLS_BUCKET, { prefix: "skills/" }), +]); + +const result = streamText({ + model, + system: [instructions, catalog.prompt()].filter(Boolean).join("\n\n"), + tools: { ...tools, ...createSkillTools(catalog) }, + messages, +}); +``` + + + +Load the catalog again to pick up changed skills, and build the tools from the new catalog. `catalog.fingerprint` changes whenever the catalog prompt changes, so compare it to decide when to rebuild a cached system prompt. + +To add `run_skill_script`, pass a runner from `agents/skills/runner`: `createSkillTools(catalog, { scriptRunner: runner({ loader: env.LOADER }) })`. Refer to [Execution](/agents/skills/execution/). + +### Other harnesses + +Use the catalog directly inside your harness's tool handlers. `activate()` and `readResource()` return results instead of throwing, and render the same text as the supplied adapters: + + + +```ts +import { + formatSkillDiagnostic, + loadSkillCatalog, + renderSkillResource, + workspace, +} from "agents/skills"; + +// `computerWorkspace` is a Workspace from @cloudflare/computer. +const catalog = await loadSkillCatalog([workspace(computerWorkspace)]); + +for (const diagnostic of catalog.diagnostics) { + console.warn(formatSkillDiagnostic(diagnostic)); +} + +// Register these handlers with your harness's tool interface. +function activateSkill(input: { name: string }): string { + const activated = catalog.activate(input.name); + // Report failures as a tool error, for example an unknown skill. + if (!activated.ok) throw activated.error; + return activated.value; +} + +async function readSkillResource(input: { + name?: string; + path: string; +}): Promise { + const read = await catalog.readResource(input); + if (!read.ok) throw read.error; + return renderSkillResource(read.value); +} +``` + + + +Define `name` as one of `catalog.skills.map((entry) => entry.skill.name)`. Add `catalog.prompt()` to your system prompt. It returns `null` when there are no skills. + +## Handle problems + +A broken skill never stops the agent. `loadSkillCatalog()` reports problems in `catalog.diagnostics` and keeps the other skills: + +| Diagnostic | Cause | +| ---------------- | ---------------------------------------------------------------------------------------------- | +| `SkillWarning` | A skill breaks a [specification rule](/agents/skills/#validation) but was loaded anyway. | +| `InvalidSkill` | A skill could not be loaded, for example because it has no `description`. It is not shown to the model. | +| `SourceFailed` | A source could not be listed, for example because the bucket was unavailable. | +| `DuplicateSkill` | Two sources provide the same skill name. The first source wins. | + +Tool failures, such as an unknown skill or a missing file, are returned to the model as tool errors so it can try again. The AI SDK tools throw, and the Pi tools return `isError: true`. + +## Related + +- [Agent Skills concepts](/agents/concepts/agent-skills/): the open format and progressive disclosure +- [Agent Skills](/agents/skills/): sources and validation in the Agents SDK +- [Execution](/agents/skills/execution/): run skill scripts +- [File system](/agents/tools/filesystem/): give the agent a Workspace to store skills in