@standardagents/skill 0.0.0-dev.ffffff

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # @standardagents/skill
2
+
3
+ Markdown-authored coding skills for Standard Agents and AgentBuilder.
4
+
5
+ ## Overview
6
+
7
+ `@standardagents/skill` ships reusable skill folders that help coding agents
8
+ compose and build Standard Agents projects effectively. The skill content lives
9
+ in plain `.md` files so you can inspect, copy, and edit it without touching a
10
+ build step.
11
+
12
+ The first bundled skill is:
13
+
14
+ - `agentbuilder` - guidance for composing models, prompts, tools, hooks, APIs,
15
+ and subagents in Standard Agents projects
16
+
17
+ ## Installation
18
+
19
+ Recommended:
20
+
21
+ ```bash
22
+ npx @standardagents/cli skill
23
+ ```
24
+
25
+ This opens an interactive picker and installs the bundled `agentbuilder`
26
+ guidance into a supported coding agent. Currently:
27
+
28
+ - Codex gets a skill folder in `$CODEX_HOME/skills` or `~/.codex/skills`
29
+ - Claude Code gets a user subagent in `~/.claude/agents/agentbuilder.md`
30
+
31
+ Package install:
32
+
33
+ ```bash
34
+ pnpm add @standardagents/skill
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ Read the bundled skill directly from `node_modules`:
40
+
41
+ ```ts
42
+ import { listSkills, readSkillFile, resolveSkillDir } from '@standardagents/skill'
43
+
44
+ console.log(listSkills())
45
+ console.log(resolveSkillDir('agentbuilder'))
46
+
47
+ const skillMarkdown = await readSkillFile('agentbuilder')
48
+ console.log(skillMarkdown)
49
+ ```
50
+
51
+ Copy the editable markdown skill into a local skills directory:
52
+
53
+ ```ts
54
+ import { copySkill } from '@standardagents/skill'
55
+
56
+ await copySkill('agentbuilder', '.codex/skills', { overwrite: true })
57
+ ```
58
+
59
+ That creates:
60
+
61
+ ```text
62
+ .codex/skills/
63
+ agentbuilder/
64
+ SKILL.md
65
+ ```
66
+
67
+ ## Files
68
+
69
+ - `skills/agentbuilder/SKILL.md` - entrypoint skill file
70
+ - `src/index.ts` - helper API for locating, reading, and copying skills
71
+
72
+ ## Development
73
+
74
+ ```bash
75
+ pnpm build
76
+ pnpm typecheck
77
+ pnpm test:run
78
+ ```
@@ -0,0 +1,25 @@
1
+ declare const SKILL_CATALOG: {
2
+ readonly agentbuilder: {
3
+ readonly description: "Compose Standard Agents from models, prompts, tools, hooks, APIs, and subagents using markdown-authored guidance.";
4
+ };
5
+ };
6
+ type SkillName = keyof typeof SKILL_CATALOG;
7
+ interface SkillDefinition {
8
+ name: SkillName;
9
+ description: string;
10
+ directory: string;
11
+ entryFile: string;
12
+ }
13
+ interface CopySkillOptions {
14
+ overwrite?: boolean;
15
+ }
16
+ declare function listSkills(): SkillDefinition[];
17
+ declare function getSkill(name: SkillName): SkillDefinition;
18
+ declare function resolveSkillDir(name: SkillName): string;
19
+ declare function resolveSkillFile(name: SkillName, ...segments: string[]): string;
20
+ declare function readSkillFile(name: SkillName, ...segments: string[]): Promise<string>;
21
+ declare function copySkill(name: SkillName, destinationRoot: string, options?: CopySkillOptions): Promise<string>;
22
+ declare const skillPackageRoot: string;
23
+ declare const skillsRoot: string;
24
+
25
+ export { type CopySkillOptions, type SkillDefinition, type SkillName, copySkill, getSkill, listSkills, readSkillFile, resolveSkillDir, resolveSkillFile, skillPackageRoot, skillsRoot };
package/dist/index.js ADDED
@@ -0,0 +1,75 @@
1
+ // src/index.ts
2
+ import { cp, mkdir, readFile } from "fs/promises";
3
+ import { fileURLToPath } from "url";
4
+ import { isAbsolute, relative, resolve } from "path";
5
+ var PACKAGE_ROOT = fileURLToPath(new URL("../", import.meta.url));
6
+ var SKILLS_ROOT = resolve(PACKAGE_ROOT, "skills");
7
+ var SKILL_CATALOG = {
8
+ agentbuilder: {
9
+ description: "Compose Standard Agents from models, prompts, tools, hooks, APIs, and subagents using markdown-authored guidance."
10
+ }
11
+ };
12
+ function assertSkillName(name) {
13
+ if (!(name in SKILL_CATALOG)) {
14
+ throw new Error(
15
+ `Unknown Standard Agents skill "${name}". Expected one of: ${Object.keys(SKILL_CATALOG).join(", ")}.`
16
+ );
17
+ }
18
+ }
19
+ function resolveInside(baseDir, segments) {
20
+ const candidate = resolve(baseDir, ...segments);
21
+ const relativePath = relative(baseDir, candidate);
22
+ if (relativePath.startsWith("..") || isAbsolute(relativePath)) {
23
+ throw new Error("Skill paths must stay within the skill directory.");
24
+ }
25
+ return candidate;
26
+ }
27
+ function listSkills() {
28
+ return Object.keys(SKILL_CATALOG).map((name) => getSkill(name));
29
+ }
30
+ function getSkill(name) {
31
+ const definition = SKILL_CATALOG[name];
32
+ const directory = resolveSkillDir(name);
33
+ return {
34
+ name,
35
+ description: definition.description,
36
+ directory,
37
+ entryFile: resolveSkillFile(name, "SKILL.md")
38
+ };
39
+ }
40
+ function resolveSkillDir(name) {
41
+ assertSkillName(name);
42
+ return resolve(SKILLS_ROOT, name);
43
+ }
44
+ function resolveSkillFile(name, ...segments) {
45
+ return resolveInside(resolveSkillDir(name), segments);
46
+ }
47
+ async function readSkillFile(name, ...segments) {
48
+ const target = segments.length === 0 ? resolveSkillFile(name, "SKILL.md") : resolveSkillFile(name, ...segments);
49
+ return readFile(target, "utf8");
50
+ }
51
+ async function copySkill(name, destinationRoot, options = {}) {
52
+ const sourceDir = resolveSkillDir(name);
53
+ const targetRoot = resolve(destinationRoot);
54
+ const targetDir = resolve(targetRoot, name);
55
+ await mkdir(targetRoot, { recursive: true });
56
+ await cp(sourceDir, targetDir, {
57
+ recursive: true,
58
+ force: options.overwrite ?? false,
59
+ errorOnExist: !(options.overwrite ?? false)
60
+ });
61
+ return targetDir;
62
+ }
63
+ var skillPackageRoot = PACKAGE_ROOT;
64
+ var skillsRoot = SKILLS_ROOT;
65
+ export {
66
+ copySkill,
67
+ getSkill,
68
+ listSkills,
69
+ readSkillFile,
70
+ resolveSkillDir,
71
+ resolveSkillFile,
72
+ skillPackageRoot,
73
+ skillsRoot
74
+ };
75
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { cp, mkdir, readFile } from 'node:fs/promises'\nimport { fileURLToPath } from 'node:url'\nimport { isAbsolute, relative, resolve } from 'node:path'\n\nconst PACKAGE_ROOT = fileURLToPath(new URL('../', import.meta.url))\nconst SKILLS_ROOT = resolve(PACKAGE_ROOT, 'skills')\n\nconst SKILL_CATALOG = {\n agentbuilder: {\n description:\n 'Compose Standard Agents from models, prompts, tools, hooks, APIs, and subagents using markdown-authored guidance.',\n },\n} as const\n\nexport type SkillName = keyof typeof SKILL_CATALOG\n\nexport interface SkillDefinition {\n name: SkillName\n description: string\n directory: string\n entryFile: string\n}\n\nexport interface CopySkillOptions {\n overwrite?: boolean\n}\n\nfunction assertSkillName(name: string): asserts name is SkillName {\n if (!(name in SKILL_CATALOG)) {\n throw new Error(\n `Unknown Standard Agents skill \"${name}\". Expected one of: ${Object.keys(SKILL_CATALOG).join(', ')}.`\n )\n }\n}\n\nfunction resolveInside(baseDir: string, segments: readonly string[]): string {\n const candidate = resolve(baseDir, ...segments)\n const relativePath = relative(baseDir, candidate)\n\n if (relativePath.startsWith('..') || isAbsolute(relativePath)) {\n throw new Error('Skill paths must stay within the skill directory.')\n }\n\n return candidate\n}\n\nexport function listSkills(): SkillDefinition[] {\n return (Object.keys(SKILL_CATALOG) as SkillName[]).map((name) => getSkill(name))\n}\n\nexport function getSkill(name: SkillName): SkillDefinition {\n const definition = SKILL_CATALOG[name]\n const directory = resolveSkillDir(name)\n\n return {\n name,\n description: definition.description,\n directory,\n entryFile: resolveSkillFile(name, 'SKILL.md'),\n }\n}\n\nexport function resolveSkillDir(name: SkillName): string {\n assertSkillName(name)\n return resolve(SKILLS_ROOT, name)\n}\n\nexport function resolveSkillFile(name: SkillName, ...segments: string[]): string {\n return resolveInside(resolveSkillDir(name), segments)\n}\n\nexport async function readSkillFile(\n name: SkillName,\n ...segments: string[]\n): Promise<string> {\n const target = segments.length === 0\n ? resolveSkillFile(name, 'SKILL.md')\n : resolveSkillFile(name, ...segments)\n\n return readFile(target, 'utf8')\n}\n\nexport async function copySkill(\n name: SkillName,\n destinationRoot: string,\n options: CopySkillOptions = {}\n): Promise<string> {\n const sourceDir = resolveSkillDir(name)\n const targetRoot = resolve(destinationRoot)\n const targetDir = resolve(targetRoot, name)\n\n await mkdir(targetRoot, { recursive: true })\n await cp(sourceDir, targetDir, {\n recursive: true,\n force: options.overwrite ?? false,\n errorOnExist: !(options.overwrite ?? false),\n })\n\n return targetDir\n}\n\nexport const skillPackageRoot = PACKAGE_ROOT\nexport const skillsRoot = SKILLS_ROOT\n"],"mappings":";AAAA,SAAS,IAAI,OAAO,gBAAgB;AACpC,SAAS,qBAAqB;AAC9B,SAAS,YAAY,UAAU,eAAe;AAE9C,IAAM,eAAe,cAAc,IAAI,IAAI,OAAO,YAAY,GAAG,CAAC;AAClE,IAAM,cAAc,QAAQ,cAAc,QAAQ;AAElD,IAAM,gBAAgB;AAAA,EACpB,cAAc;AAAA,IACZ,aACE;AAAA,EACJ;AACF;AAeA,SAAS,gBAAgB,MAAyC;AAChE,MAAI,EAAE,QAAQ,gBAAgB;AAC5B,UAAM,IAAI;AAAA,MACR,kCAAkC,IAAI,uBAAuB,OAAO,KAAK,aAAa,EAAE,KAAK,IAAI,CAAC;AAAA,IACpG;AAAA,EACF;AACF;AAEA,SAAS,cAAc,SAAiB,UAAqC;AAC3E,QAAM,YAAY,QAAQ,SAAS,GAAG,QAAQ;AAC9C,QAAM,eAAe,SAAS,SAAS,SAAS;AAEhD,MAAI,aAAa,WAAW,IAAI,KAAK,WAAW,YAAY,GAAG;AAC7D,UAAM,IAAI,MAAM,mDAAmD;AAAA,EACrE;AAEA,SAAO;AACT;AAEO,SAAS,aAAgC;AAC9C,SAAQ,OAAO,KAAK,aAAa,EAAkB,IAAI,CAAC,SAAS,SAAS,IAAI,CAAC;AACjF;AAEO,SAAS,SAAS,MAAkC;AACzD,QAAM,aAAa,cAAc,IAAI;AACrC,QAAM,YAAY,gBAAgB,IAAI;AAEtC,SAAO;AAAA,IACL;AAAA,IACA,aAAa,WAAW;AAAA,IACxB;AAAA,IACA,WAAW,iBAAiB,MAAM,UAAU;AAAA,EAC9C;AACF;AAEO,SAAS,gBAAgB,MAAyB;AACvD,kBAAgB,IAAI;AACpB,SAAO,QAAQ,aAAa,IAAI;AAClC;AAEO,SAAS,iBAAiB,SAAoB,UAA4B;AAC/E,SAAO,cAAc,gBAAgB,IAAI,GAAG,QAAQ;AACtD;AAEA,eAAsB,cACpB,SACG,UACc;AACjB,QAAM,SAAS,SAAS,WAAW,IAC/B,iBAAiB,MAAM,UAAU,IACjC,iBAAiB,MAAM,GAAG,QAAQ;AAEtC,SAAO,SAAS,QAAQ,MAAM;AAChC;AAEA,eAAsB,UACpB,MACA,iBACA,UAA4B,CAAC,GACZ;AACjB,QAAM,YAAY,gBAAgB,IAAI;AACtC,QAAM,aAAa,QAAQ,eAAe;AAC1C,QAAM,YAAY,QAAQ,YAAY,IAAI;AAE1C,QAAM,MAAM,YAAY,EAAE,WAAW,KAAK,CAAC;AAC3C,QAAM,GAAG,WAAW,WAAW;AAAA,IAC7B,WAAW;AAAA,IACX,OAAO,QAAQ,aAAa;AAAA,IAC5B,cAAc,EAAE,QAAQ,aAAa;AAAA,EACvC,CAAC;AAED,SAAO;AACT;AAEO,IAAM,mBAAmB;AACzB,IAAM,aAAa;","names":[]}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@standardagents/skill",
3
+ "version": "0.0.0-dev.ffffff",
4
+ "private": false,
5
+ "publishConfig": {
6
+ "access": "restricted",
7
+ "registry": "https://registry.npmjs.org/"
8
+ },
9
+ "type": "module",
10
+ "description": "Markdown-authored coding skills for composing and building Standard Agents",
11
+ "main": "./dist/index.js",
12
+ "types": "./dist/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js"
17
+ },
18
+ "./skills/*": "./skills/*"
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "skills",
23
+ "README.md"
24
+ ],
25
+ "scripts": {
26
+ "build": "tsup",
27
+ "dev": "tsup --watch",
28
+ "typecheck": "tsc --noEmit",
29
+ "test": "vitest",
30
+ "test:run": "vitest run"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^22.18.13",
34
+ "tsup": "^8.3.5",
35
+ "typescript": "^5.9.0",
36
+ "vitest": "^2.1.8"
37
+ },
38
+ "keywords": [
39
+ "standardagents",
40
+ "skill",
41
+ "agentbuilder",
42
+ "ai",
43
+ "markdown"
44
+ ],
45
+ "author": "FormKit Inc.",
46
+ "license": "UNLICENSED"
47
+ }
@@ -0,0 +1,981 @@
1
+ ---
2
+ name: agentbuilder
3
+ description: >
4
+ Use this skill when creating, editing, debugging, reviewing, or documenting
5
+ Standard Agents or AgentBuilder projects. Apply it for work on agents,
6
+ prompts, models, tools, hooks, threads, APIs, subagents, provider setup,
7
+ model selection, environment variables, and spec-aligned architecture, even
8
+ if the user only says things like "build an agent", "write a prompt",
9
+ "choose a model", or "fix my AgentBuilder app" without explicitly naming
10
+ Standard Agents.
11
+ ---
12
+
13
+ # AgentBuilder Skill
14
+
15
+ The effective agents guide is a living record that is not part of the specification, but intended to help humans and agents use the specification to craft effective agents. It is updated regularly and not bound to any specific version of the specification.
16
+
17
+ Note: in this guide we occasionally discuss commands like `npmx` or `pnpm` — use whatever package manager the user prefers, these are just placeholders for the correct command.
18
+
19
+ ## What is a Standard Agent?
20
+
21
+ In the Standard Agent paradigm agents are the atomic unit of an AI system and it is the composition of many domain-specific agents that produces efficacy. How large should the concerns of a given agent be? There is no hard or fast rule here, the question is a bit like how big should a React component be. However, in general Standard Agents can be effective using smaller and cheaper models — but smaller and cheaper models typically suffer from poor tool discernment when presented with a broad range of tools, especially a broad range of similar tools.
22
+
23
+ A "gmail" agent, for example, will typically do better than a "google apps" agent. Those higher-level agents would instead be composed of many sub-service agents, and a larger coordinator agent would be the one with the primary objectives, business goals and logic, and ultimate authority. This is a fractal that can be scaled up to create even larger coordinators and larger agents. These agent-graphs, or agent-trees, are the core philosophy of Standard Agents and solves many industry problems such as progressive tool discovery, model-prompt tuning, context dilution, task resumability, and even compaction.
24
+
25
+ However, just because we can use subagents to create complex interactions doesn't mean we have to. A subagent is always a two-sided conversation where each side can perform multiple steps per turn. Sometimes you just need to use a feature of a different model, without it being a full subagent, for example using nano bananna to generate an image. If you just need to have a tool call that generates an image, use a subprompt. You may want an asset generator subagent if, for example, the agent needs to perform QA on the generated asset to ensure it meets certain criteria.
26
+
27
+ Another example may be using an instruct model variant to run an agent, but a more eloquent model to write the actual text of the email that is sent to people. Again, that could be a subprompt. Having another model proofread the email that is written — that would be a subagent. One small note on that: when performing QA in a subthread, try to use different models from different providers on the agent side that is doing the proofreading. Models from the same labs tend to think their own output is quite good, even if it is not.
28
+
29
+ Example structure:
30
+
31
+ - `personal_assistant_coordinator`
32
+ - central dual-ai coordinator
33
+ - decides which domain owns the work
34
+ - aggregates state, plans, and responses
35
+ - has subprompts for producing images, or using a more eloquent model for writing
36
+ - `communications_coordinator`
37
+ - owns all inbound and outbound communications work
38
+ - delegates to `gmail_agent`, `slack_agent`, and `text_message_agent`
39
+ - receives escalations from those channel agents and routes the next action
40
+ - `research_assistant`
41
+ - owns information gathering and synthesis
42
+ - delegates to `browser_use_agent`, `google_drive_agent`, and `notes_agent`
43
+ - optional additional branches
44
+ - `scheduling_coordinator`
45
+ - `travel_coordinator`
46
+ - `finance_ops_coordinator`
47
+ - `crm_coordinator`
48
+
49
+ ## Standard Agent Stack
50
+
51
+ The standard agent spec defines exactly what an agent is made up of:
52
+
53
+ - Providers (define how to interact with different LLM providers, and different model variants.)
54
+ - Prompts (system instructions, and how to use tools, subagents, and subprompts)
55
+ - Tools (define the functions that an agent can call to interact with the world, and other agents)
56
+ - Agents (define the agent itself, how it uses prompts and tools)
57
+ - Hooks (define custom code that can perform various internal operations such as modifying the prompt or changing the message history)
58
+ - Effects (custom code that executes at a certain time)
59
+ - Threads (the instance of a given agent, with its own state, message history, and filesystem)
60
+ - Endpoints (built-in and custom endpoints exposed by an agent thread)
61
+
62
+ Each of these is defined by the Standard Agent Specification.
63
+
64
+ ## Models
65
+
66
+ The standard agent spec is model-agnostic, but it is important to understand how to use different models effectively within the paradigm. The most effective agents use a combination of smaller and larger models. The smallest model that produces sufficiently good results is always the best choice. Smaller models are cheaper, faster, and more likely to run on local hardware (eventually). However smaller models often struggle with discernment, coordination, and higher order reasoning like business objectives. So typically we'll want to use larger models for coordinators, and smaller models for discrete subagents.
67
+
68
+ Different models are good at different things. The Standard Agents organization has identified a number of well-suited models for various tasks. To get the latest curated list run:
69
+
70
+ ```bash
71
+ pnpm exec agents current-models
72
+ ```
73
+
74
+ When writing an agent, use `current-models` to choose a model that fits the use case, then use `available-models` to check the exact model string your installed provider exposes. If you have more than one configured provider, pass `--provider=<name>`.
75
+
76
+ ```bash
77
+ pnpm exec agents available-models --provider=openai
78
+ ```
79
+
80
+ Note: the `name` of a model should indicate the use case for the model, for example: `extra_reasoning`, or `fast_tool_calls` or `image_generation`. Fallback models can be created for similar use cases, and should typically use different providers to increase the chances of at least one model producing good results. For example, you may have `extra_reasoning` models from both OpenAI and Anthropic.
81
+
82
+ ### Prompts
83
+
84
+ Prompts define what actually gets sent to the LLM. They encompass model instructions, tool definitions, subprompts, and subagent tools, history inclusion, model selection, and more. The typescript definition of a prompt is as follows:
85
+
86
+ ```ts
87
+ export interface PromptDefinition<
88
+ N extends string = string,
89
+ S extends ToolArgs = ToolArgs,
90
+ > {
91
+ /**
92
+ * Unique name for this prompt.
93
+ * Used as the identifier when referencing from agents or as a tool.
94
+ * Should be snake_case (e.g., 'customer_support', 'data_analyst').
95
+ */
96
+ name: N;
97
+
98
+ /**
99
+ * Description shown when this prompt is exposed as a tool.
100
+ * Should clearly describe what this prompt does for LLM tool selection.
101
+ */
102
+ toolDescription: string;
103
+
104
+ /**
105
+ * The system prompt content sent to the LLM.
106
+ * Can be either a plain string or a structured array for composition.
107
+ */
108
+ prompt: PromptContent;
109
+
110
+ /**
111
+ * Model to use for this prompt.
112
+ * Must reference a model defined in agents/models/.
113
+ */
114
+ model: StandardAgentSpec.Models;
115
+
116
+ /**
117
+ * Include full chat history in the LLM context.
118
+ * @default false
119
+ */
120
+ includeChat?: boolean;
121
+
122
+ /**
123
+ * Include results from past tool calls in the LLM context.
124
+ * @default false
125
+ */
126
+ includePastTools?: boolean;
127
+
128
+ /**
129
+ * Allow parallel execution of multiple tool calls.
130
+ * @default false
131
+ */
132
+ parallelToolCalls?: boolean;
133
+
134
+ /**
135
+ * Tool calling strategy for the LLM.
136
+ *
137
+ * - `auto`: Model decides when to call tools (default)
138
+ * - `none`: Disable tool calling entirely
139
+ * - `required`: Force the model to call at least one tool
140
+ *
141
+ * @default 'auto'
142
+ */
143
+ toolChoice?: 'auto' | 'none' | 'required';
144
+
145
+ /**
146
+ * Zod schema for validating inputs when this prompt is called as a tool.
147
+ */
148
+ requiredSchema?: S;
149
+
150
+ /**
151
+ * Declared variables for this prompt.
152
+ */
153
+ variables?: VariableDefinition[];
154
+
155
+ /**
156
+ * Tools available to this prompt.
157
+ * Can be:
158
+ * - string: Simple tool name (custom or provider tool)
159
+ * - SubpromptConfig: Sub-prompt used as a tool
160
+ * - PromptToolConfig: Tool with environment values and/or options
161
+ * - SubagentToolConfig: `dual_ai` subagent invocation behavior
162
+ *
163
+ * To enable handoffs, include ai_human agent names in this array.
164
+ *
165
+ * @example
166
+ * ```typescript
167
+ * tools: [
168
+ * 'custom_tool', // Simple tool name
169
+ * { name: 'other_prompt' }, // Sub-prompt as tool
170
+ * { name: 'file_search', env: { VECTOR_STORE_ID: 'vs_123' } }, // Tool with env values
171
+ * ]
172
+ * ```
173
+ */
174
+ tools?: (
175
+ | StandardAgentSpec.Callables
176
+ | SubpromptConfig
177
+ | PromptToolConfig
178
+ | SubagentToolConfig
179
+ )[];
180
+
181
+ /**
182
+ * Environment values provided by this prompt.
183
+ * Prompt values are the lowest-precedence source in runtime resolution.
184
+ */
185
+ env?: Record<string, string>;
186
+
187
+ /**
188
+ * Reasoning configuration for models that support extended thinking.
189
+ */
190
+ reasoning?: ReasoningConfig;
191
+
192
+ /**
193
+ * Number of recent messages to keep actual images for in context.
194
+ * @default 10
195
+ */
196
+ recentImageThreshold?: number;
197
+
198
+ /**
199
+ * Provider-specific options passed through to the provider.
200
+ * These override model-level providerOptions for this prompt.
201
+ *
202
+ * Options are merged in order (later wins):
203
+ * 1. model.providerOptions (defaults)
204
+ * 2. prompt.providerOptions (this field - overrides)
205
+ *
206
+ * @example
207
+ * ```typescript
208
+ * providerOptions: {
209
+ * response_format: { type: 'json_object' },
210
+ * }
211
+ * ```
212
+ */
213
+ providerOptions?: Record<string, unknown>;
214
+
215
+ /**
216
+ * Hook IDs to run when this prompt is active.
217
+ * References hooks by their unique `id` property from defineHook().
218
+ * If not specified, falls back to agent-level hooks.
219
+ *
220
+ * @example
221
+ * ```typescript
222
+ * hooks: ['limit_to_20_messages', 'log_tool_calls']
223
+ * ```
224
+ */
225
+ hooks?: StandardAgentSpec.HookIds[];
226
+ }
227
+ ```
228
+
229
+ The `prompt` property is the system message for that LLM step. It is defined as a string or array of parts. If defined with parts, the prompt is rendered before being sent to the LLM and can contain dynamic content. For example, let's say our agent is powering a chatbot for a website that sells athletic shoes. We want the chatbot to be able to answer questions about the shoes, billing, shipping etc. To do this, we have many prompts, but we always want to use the same friendly and helpful tone.
230
+
231
+ To share "tone" prompt that just defines the tone and style of the chatbot, and then reference that prompt in all our other prompts as a part. This allows us to change the tone of the chatbot across all its functions by just changing one prompt.
232
+
233
+ ```ts
234
+ const tonePrompt: PromptDefinition = {
235
+ name: 'company_tone',
236
+ toolDescription: 'Defines the tone and style of the chatbot.',
237
+ prompt: `You are a friendly and helpful customer support assistant for an athletic shoe company. You always respond in a positive and upbeat tone, even when the customer is upset. You use simple language and avoid technical jargon. Your goal is to help the customer with their issue and make them feel good about shopping with us.`,
238
+ model: 'tiny_model',
239
+ };
240
+ ```
241
+
242
+ Then in another prompt we can reference that tone prompt:
243
+
244
+ ```ts
245
+ const shippingPrompt: PromptDefinition = {
246
+ name: 'shipping_inquiries',
247
+ toolDescription: 'Handles customer questions about shipping.',
248
+ prompt: [
249
+ { type: 'include', prompt: 'company_tone' }, // Reference the tone prompt as a part
250
+ { type: 'text', content: `Details about the products:...` }
251
+ ],
252
+ model: 'tiny_model',
253
+ };
254
+ ```
255
+
256
+ Additionally, we can use the `env` property to provide dynamic content that can be referenced in the prompt. This can be especially helpful for packed agents that get distributed to other people, businesses, or industries. For example, we could create a generic ecommerce assistant agent that uses a `PRODUCT_INVENTORY` env to customize the agent to the specific products of the business that is using it.
257
+
258
+ ```ts
259
+ {
260
+ // ...
261
+ name: 'ecommerce_assistant',
262
+ toolDescription: 'An assistant for ecommerce businesses that can answer questions about products, inventory, and orders.',
263
+ prompt: [
264
+ { type: 'text', content: `You are an assistant for an ecommerce business. You help customers with their questions about products, inventory, and orders. Here is the current product inventory: ` },
265
+ { type: 'env', property: 'PRODUCT_INVENTORY' }, // Reference the PRODUCT_INVENTORY env variable
266
+ ],
267
+ model: 'tiny_model',
268
+ variables: [
269
+ {
270
+ /** Environment variable/property name */
271
+ name: 'PRODUCT_INVENTORY',
272
+ /** Value type: 'text' or 'secret' */
273
+ type: 'text',
274
+ /** Whether this variable is required to execute */
275
+ required: true;
276
+ /**
277
+ * Whether this variable is scoped to the declarer agent subtree.
278
+ *
279
+ * Scoped variables do not inherit parent thread env values. Descendants of
280
+ * the declarer still inherit scoped values from that declarer thread.
281
+ *
282
+ * @default false
283
+ */
284
+ scoped: false;
285
+ /** Human-readable description (empty string when not provided) */
286
+ description: 'The full inventory of products, including names, descriptions, and stock levels.',
287
+ }
288
+ ]
289
+ }
290
+ ```
291
+
292
+ ## Tools
293
+
294
+ Tools are a generic term for anything an agent can "call". In practice there are 3 types of tools:
295
+
296
+ 1. Callables: these are TypeScript functions that are defined via `defineTool` in the `tools` directory. These should be created as the primary mechanism for interfacing with the outside world, interacting with APIs, database, business logic, and often even other agents. Each callable receives a `ThreadState` object. You should not assume that the function is being executed on a node server; it may be an edge function, for example, so do not use node imports if possible. Instead use the `ThreadState` to perform any necessary operations (see the ThreadState documentation for more details and examples).
297
+
298
+ 2. Subprompts: these are prompts that can be called as tools. They are defined in the `prompts` directory and have a `toolDescription` property that indicates they can be used as tools, and are useful for switching models for a small task, like writing some text, or generating an image.
299
+
300
+ 3. Subagents: these are full agents that can be called as tools. They are defined in the `agents` directory and have `exposeAsTool: true` in their agent definition. Subagents are useful when you need a more complex interaction that requires multiple steps, or when you want to leverage the unique capabilities of a different model that is better suited for a specific task.
301
+
302
+ There are lots of options for how tools are defined on their respective prompt:
303
+
304
+ ```ts
305
+ {
306
+ // ...
307
+ tools: {
308
+ /**
309
+ * Agent callable name.
310
+ *
311
+ * Must reference a `dual_ai` agent with `exposeAsTool: true`.
312
+ */
313
+ name: T;
314
+
315
+ /**
316
+ * Whether parent execution blocks until the subagent returns a result.
317
+ *
318
+ * - `true`: Parent waits for completion (tool-call style)
319
+ * - `false`: Parent continues immediately and receives results asynchronously
320
+ *
321
+ * @default true
322
+ */
323
+ blocking?: boolean;
324
+
325
+ /**
326
+ * Property from tool-call arguments used as the initial message sent to the
327
+ * subagent on invocation.
328
+ *
329
+ * Uses the same semantics as {@link SubpromptConfig.initUserMessageProperty}.
330
+ */
331
+ initUserMessageProperty?: StandardAgentSpec.SchemaFields<T>;
332
+
333
+ /**
334
+ * Property from tool-call arguments containing attachment path(s) that should
335
+ * be sent to the subagent on invocation.
336
+ *
337
+ * Uses the same semantics as {@link SubpromptConfig.initAttachmentsProperty}.
338
+ */
339
+ initAttachmentsProperty?: StandardAgentSpec.SchemaFields<T>;
340
+
341
+ /**
342
+ * Property from tool-call arguments used to assign a human-readable name for
343
+ * each spawned child thread instance.
344
+ *
345
+ * Implementations SHOULD store this as a thread tag in the form
346
+ * `name:<value>` so UIs can render a concise per-instance title.
347
+ */
348
+ initAgentNameProperty?: StandardAgentSpec.SchemaFields<T>;
349
+
350
+ /**
351
+ * Execute this tool immediately when the prompt becomes active.
352
+ *
353
+ * - `true`: Execute immediately using runtime defaults.
354
+ * - Object: Execute immediately with explicit per-instance env relationships.
355
+ *
356
+ * When the object form is used:
357
+ * - `scopedEnv` names the per-instance env values copied into the child thread.
358
+ * - `nameEnv` and `descriptionEnv` identify the only per-instance env values
359
+ * that runtimes may expose to an internal bootstrap model when deriving
360
+ * initial child arguments.
361
+ *
362
+ * Runtimes MUST NOT expose `scopedEnv` values to the model unless the same env
363
+ * name is explicitly designated by `nameEnv` or `descriptionEnv`.
364
+ *
365
+ * Immediate tools run before the first LLM step for that activation.
366
+ */
367
+ immediate?:
368
+ | boolean
369
+ | {
370
+ /**
371
+ * Scoped env name whose value may be used as the safe per-instance name
372
+ * hint for child bootstrap.
373
+ */
374
+ nameEnv?: string;
375
+
376
+ /**
377
+ * Scoped env name whose value may be used as the safe per-instance
378
+ * description hint for child bootstrap.
379
+ */
380
+ descriptionEnv?: string;
381
+
382
+ /**
383
+ * Scoped env names that should be copied into the child thread for each
384
+ * immediate instance group.
385
+ */
386
+ scopedEnv?: string[];
387
+ };
388
+
389
+ /**
390
+ * Optional branch flag env name.
391
+ *
392
+ * When set, this subagent is only enabled when the named env resolves to
393
+ * `true`, `1`, or `yes` (case-insensitive).
394
+ */
395
+ optional?: string;
396
+
397
+ /**
398
+ * Resumability configuration.
399
+ *
400
+ * - `false` (default): Non-resumable subagent
401
+ * - Object: Resumable subagent with message routing and instance limits
402
+ *
403
+ * When resumable mode is enabled, runtimes SHOULD provide a built-in create
404
+ * and message lifecycle interface instead of exposing raw agent callables for
405
+ * new instance creation.
406
+ */
407
+ resumable?:
408
+ | false
409
+ | {
410
+ /**
411
+ * Which side of the child `dual_ai` conversation receives parent messages.
412
+ *
413
+ * - `side_a`: Messages are queued as `role: 'user'`
414
+ * - `side_b`: Messages are queued as `role: 'assistant'`
415
+ */
416
+ receives_messages: 'side_a' | 'side_b';
417
+
418
+ /**
419
+ * Maximum concurrent instances for this subagent tool.
420
+ *
421
+ * When reached, implementations may remove this tool from subsequent LLM
422
+ * requests and route new messages to existing instances.
423
+ *
424
+ * @default unlimited
425
+ */
426
+ maxInstances?: number;
427
+
428
+ /**
429
+ * How this child reports back to its parent.
430
+ *
431
+ * - `implicit` (default): Child completion is automatically queued to the parent.
432
+ * - `explicit`: The runtime does not auto-queue child completion; tools/hooks may
433
+ * use thread APIs such as `state.notifyParent()` when they choose to escalate.
434
+ */
435
+ parentCommunication?: 'implicit' | 'explicit';
436
+ };
437
+ } |
438
+ {
439
+ /**
440
+ * Name of the tool (custom tool or provider tool).
441
+ */
442
+ name: StandardAgentSpec.Callables;
443
+
444
+ /**
445
+ * Environment variable values for this tool.
446
+ */
447
+ env?: Record<string, string>;
448
+ /**
449
+ * @deprecated Use `env` instead.
450
+ */
451
+ tenvs?: Record<string, unknown>;
452
+
453
+ /**
454
+ * Static options for this tool.
455
+ * Passed to the tool handler at execution time.
456
+ */
457
+ options?: Record<string, unknown>;
458
+ } | {
459
+ /**
460
+ * Name of the sub-prompt or agent to call.
461
+ * Must be a prompt defined in agents/prompts/ or an agent in agents/agents/.
462
+ */
463
+ name: T;
464
+
465
+ /**
466
+ * Include text response content from sub-prompt execution in the result string.
467
+ * @default true
468
+ */
469
+ includeTextResponse?: boolean;
470
+
471
+ /**
472
+ * Serialize tool calls made by the sub-prompt (and their results) into the result string.
473
+ * @default true
474
+ */
475
+ includeToolCalls?: boolean;
476
+
477
+ /**
478
+ * Serialize any errors from the sub-prompt into the result string.
479
+ * @default true
480
+ */
481
+ includeErrors?: boolean;
482
+
483
+ /**
484
+ * Property from the tool call arguments to use as the initial user message
485
+ * when invoking the sub-prompt or agent.
486
+ *
487
+ * Autocompletes to fields from the prompt's requiredSchema (or agent's side_a prompt schema).
488
+ *
489
+ * @example
490
+ * If the tool is called with `{ query: "search term", limit: 10 }` and
491
+ * `initUserMessageProperty: 'query'`, the sub-prompt will receive
492
+ * "search term" as the initial user message.
493
+ */
494
+ initUserMessageProperty?: StandardAgentSpec.SchemaFields<T>;
495
+
496
+ /**
497
+ * Property containing attachment path(s) to include as multimodal content
498
+ * when invoking the sub-prompt or agent.
499
+ *
500
+ * Autocompletes to fields from the prompt's requiredSchema (or agent's side_a prompt schema).
501
+ * Supports both a single path string or an array of paths.
502
+ *
503
+ * @example
504
+ * If the tool is called with `{ image: "/attachments/123.jpg" }` and
505
+ * `initAttachmentsProperty: 'image'`, the sub-prompt will receive
506
+ * the image as an attachment in the user message.
507
+ *
508
+ * @example
509
+ * If the tool is called with `{ images: ["/attachments/a.jpg", "/attachments/b.jpg"] }` and
510
+ * `initAttachmentsProperty: 'images'`, the sub-prompt will receive
511
+ * both images as attachments.
512
+ */
513
+ initAttachmentsProperty?: StandardAgentSpec.SchemaFields<T>;
514
+ } |
515
+ string /* simple tool name with no extra options * /;
516
+ }
517
+ ```
518
+
519
+ Excellent orchestration is the creation of models definitions, prompts, tools, subprompts, subagents, and agent definitions to tie everything together.
520
+
521
+
522
+ ## Subprompts vs Handoffs vs Subagents
523
+
524
+ A subprompt allows a `prompt` definition to be run with a single step as if it was a tool. The result of the subprompt is returned to the thread as if it is a tool result. Subprompts are a useful way to use a different model for a specific task. For example, an agent may use a subprompt to leverage a better image generation model.
525
+
526
+ Subprompts can receive the entire context of the parent thread if the prompt definition has `includeChat: true` and `includePastTools: true`. If those are set to false, then the subprompt receives no context at all, and it should use `initUserMessageProperty` and optionally `initAttachmentsProperty` instead. These properties specify what explicit context from the parent thread should be exposed, and their values are provided as if it is a `user` requesting the subprompt to run. The exact text the user provides will be the `initUserMessageProperty` value, and optionally any file attachments specified using the `/attachments/{filename}.{ext}` convention (`initAttachmentsProperty` should be an array of strings).
527
+
528
+ A subagent on the other hand is a full agent thread, with its own `ThreadState`. Subagents can be:
529
+
530
+ - Blocking and non-resumable
531
+ - Blocking and resumable
532
+ - Non-blocking and non-resumable
533
+ - Non-blocking and resumable
534
+
535
+ These distinctions indicate how a subagent appears to its parent agent. A non-resumable subagent is very much like a tool call from the parent's perspective. It calls the "tool" the subagent runs for a while and returns a result. A resumable subagent however, becomes an addressable part of the agent graph, the parent can communicate with it receive results from it, and inspect its status. Resumable subagents can be as long-lived as the parent itself. For example a LinkedIn subagent may be permanently monitoring a particular linked in account and advising the parent coordinator when messages that would be important for the owner are received.
536
+ Blocking and non-blocking indicates whether the parent thread waits for the subagent to finish before it continues. A non-blocking subagent allows the parent thread to continue its work while the subagent is running, and the parent can receive messages from the subagent.
537
+
538
+ A handoff is when an `ai_human` agent is used as a tool by another `ai_human` agent. This is a special case and rather than spawn a new thread, it changes which prompt "owns" that thread — in other words it hands off control from one agent to another. This is very useful in a number of applications where a human is being led through a series of steps, for example, an onboarding agent may collect necessary information from a user, and then "handoff" to another agent which performs a different function, for example, a scheduling agent that books meetings based on the information collected by the onboarding agent.
539
+
540
+ Subagents should always be `dual_ai`. `dual_ai` agents are fully autonomous and require no input from a human. Use `side_a` and `side_b` to perform reflection and reasoning on the results of the communication. For example if a subagent is tasked with generating assets for a video game, and those assets need to be on a green background, the `side_a` of the subagent can generate the assets, and the `side_b` can review the assets and determine if they meet the criteria. If they do, the `side_b` can communicate back to the parent coordinator via the tool defined as the `sessionStop` tool. If they do not meet the criteria, `side_b` can inform `side_a` in which ways the image needs to change before it can be approved.
541
+
542
+ The following is the official shape of the agent definition by which these kinds of interactions can be configured:
543
+
544
+ ```ts
545
+ export interface AgentDefinition<
546
+ N extends string = string,
547
+ Prompt extends string = StandardAgentSpec.Prompts,
548
+ Callable extends string = StandardAgentSpec.Callables,
549
+ > {
550
+ /**
551
+ * Unique name for this agent.
552
+ * Used as the identifier for thread creation and handoffs.
553
+ * Should be snake_case (e.g., 'support_agent', 'research_flow').
554
+ */
555
+ name: N;
556
+
557
+ /**
558
+ * Agent conversation type.
559
+ *
560
+ * - `ai_human`: AI conversing with a human user (default)
561
+ * - `dual_ai`: Two AI participants conversing
562
+ *
563
+ * @default 'ai_human'
564
+ */
565
+ type?: AgentType;
566
+
567
+ /**
568
+ * Maximum total turns across both sides.
569
+ * Only applies to `dual_ai` agents.
570
+ * Prevents infinite loops in AI-to-AI conversations.
571
+ */
572
+ maxSessionTurns?: number;
573
+
574
+ /**
575
+ * Configuration for Side A.
576
+ * For `ai_human`: This is the AI side.
577
+ * For `dual_ai`: This is the first AI participant.
578
+ */
579
+ sideA: SideConfig<Prompt, Callable>;
580
+
581
+ /**
582
+ * Configuration for Side B.
583
+ * For `ai_human`: Optional, the human side doesn't need config.
584
+ * For `dual_ai`: Required, the second AI participant.
585
+ */
586
+ sideB?: SideConfig<Prompt, Callable>;
587
+
588
+ /**
589
+ * Expose this agent as a tool for other prompts.
590
+ * Enables agent composition and handoffs.
591
+ * When true, other prompts can invoke this agent as a tool.
592
+ * @default false
593
+ */
594
+ exposeAsTool?: boolean;
595
+
596
+ /**
597
+ * Description shown when agent is used as a tool.
598
+ * Required if exposeAsTool is true.
599
+ * Should clearly describe what this agent does.
600
+ */
601
+ toolDescription?: string;
602
+
603
+ /**
604
+ * Brief description of what this agent does.
605
+ * Useful for UIs and documentation.
606
+ *
607
+ * @example 'Handles customer support inquiries and resolves issues'
608
+ */
609
+ description?: string;
610
+
611
+ /**
612
+ * Icon URL or absolute path for the agent.
613
+ * Absolute paths (starting with `/`) are converted to full URLs in API responses.
614
+ *
615
+ * @example 'https://example.com/icon.svg' or '/icons/support.svg'
616
+ */
617
+ icon?: string;
618
+
619
+ /**
620
+ * Environment values provided by this agent.
621
+ * Agent values are lower priority than thread/account/instance values.
622
+ */
623
+ env?: Record<string, string>;
624
+
625
+ // ============================================================================
626
+ // Package Metadata (for packing/unpacking)
627
+ // ============================================================================
628
+
629
+ /**
630
+ * npm package name for this agent when packed.
631
+ * Used by the packing system to maintain consistent package identity
632
+ * across pack/unpack cycles.
633
+ *
634
+ * @example 'standardagent-support-agent', '@myorg/support-agent'
635
+ */
636
+ packageName?: string;
637
+
638
+ /**
639
+ * Package version (semver format).
640
+ * Used by the packing system to track versions across pack/unpack cycles.
641
+ * When re-packing, this version is auto-incremented by the pack modal.
642
+ *
643
+ * @example '1.0.0', '2.3.1-beta.1'
644
+ */
645
+ version?: string;
646
+
647
+ /**
648
+ * Package author/copyright holder.
649
+ * Used by the packing system for the LICENSE file and package.json author field.
650
+ *
651
+ * @example 'John Doe', 'Acme Corp'
652
+ */
653
+ author?: string;
654
+
655
+ /**
656
+ * License identifier (SPDX format).
657
+ * Used by the packing system for LICENSE file generation.
658
+ *
659
+ * @example 'MIT', 'Apache-2.0', 'ISC'
660
+ */
661
+ license?: string;
662
+
663
+ /**
664
+ * Hook IDs to run for this agent.
665
+ * References hooks by their unique `id` property from defineHook().
666
+ * These run when prompts don't specify their own hooks.
667
+ *
668
+ * @example
669
+ * ```typescript
670
+ * hooks: ['log_messages', 'track_tool_usage']
671
+ * ```
672
+ */
673
+ hooks?: StandardAgentSpec.HookIds[];
674
+ }
675
+ ```
676
+
677
+ ## Receiving and sending input
678
+
679
+ Within an agent graph, input can come directly via a user, a tool call, or even subagents. In its simplest form, you may have an ai+human agent at the top of the graph, although this works well in simple cases, it's often preferable to have the highest order agent be a high-level coordinator that uses a more intelligent discerning model and is guided by prompting to achieve high-level objectives (ex: "You are project manager for the Jichael Mordan line of shoes at an athletic shoe company. You are in charge of...").
680
+
681
+ It is quite possible to have an entire agent graph with no `ai_human` agent types, where the only i/o with a human is through a subagent that performs tool calls to, for example, Slack. New messages are queued and posted by `side_b` as "Message from user: "Mow the lawn clanker", and responses would be sent via a tool call send_to_slack(channel, "I don't know how to do that"). However, for a chat-bot-like implementation it's often preferable to use `ai_human` as the top-level coordinator.
682
+
683
+ ## Inter-agent communication
684
+
685
+ A standard agent will often be communicating with many other agents. In almost every situation the agent graph is a tree, meaning each agent can have children, and can have parents. There are a few options that are important architectural decisions when creating these parent/child relationships. The shape of the options is this:
686
+
687
+ ```ts
688
+ interface SubagentToolConfig<T extends string = StandardAgentSpec.Callables> {
689
+ /**
690
+ * Agent callable name.
691
+ *
692
+ * Must reference a `dual_ai` agent with `exposeAsTool: true`.
693
+ */
694
+ name: T;
695
+ /**
696
+ * Whether parent execution blocks until the subagent returns a result.
697
+ *
698
+ * - `true`: Parent waits for completion (tool-call style)
699
+ * - `false`: Parent continues immediately and receives results asynchronously
700
+ *
701
+ * @default true
702
+ */
703
+ blocking?: boolean;
704
+ /**
705
+ * Property from tool-call arguments used as the initial message sent to the
706
+ * subagent on invocation.
707
+ *
708
+ * Uses the same semantics as {@link SubpromptConfig.initUserMessageProperty}.
709
+ */
710
+ initUserMessageProperty?: StandardAgentSpec.SchemaFields<T>;
711
+ /**
712
+ * Property from tool-call arguments containing attachment path(s) that should
713
+ * be sent to the subagent on invocation.
714
+ *
715
+ * Uses the same semantics as {@link SubpromptConfig.initAttachmentsProperty}.
716
+ */
717
+ initAttachmentsProperty?: StandardAgentSpec.SchemaFields<T>;
718
+ /**
719
+ * Property from tool-call arguments used to assign a human-readable name for
720
+ * each spawned child thread instance.
721
+ *
722
+ * Implementations SHOULD store this as a thread tag in the form
723
+ * `name:<value>` so UIs can render a concise per-instance title.
724
+ */
725
+ initAgentNameProperty?: StandardAgentSpec.SchemaFields<T>;
726
+ /**
727
+ * Execute this tool immediately when the prompt becomes active.
728
+ *
729
+ * - `true`: Execute immediately using runtime defaults.
730
+ * - Object: Execute immediately with explicit per-instance env relationships.
731
+ *
732
+ * When the object form is used:
733
+ * - `scopedEnv` names the per-instance env values copied into the child thread.
734
+ * - `nameEnv` and `descriptionEnv` identify the only per-instance env values
735
+ * that runtimes may expose to an internal bootstrap model when deriving
736
+ * initial child arguments.
737
+ *
738
+ * Runtimes MUST NOT expose `scopedEnv` values to the model unless the same env
739
+ * name is explicitly designated by `nameEnv` or `descriptionEnv`.
740
+ *
741
+ * Immediate tools run before the first LLM step for that activation.
742
+ */
743
+ immediate?: boolean | {
744
+ /**
745
+ * Scoped env name whose value may be used as the safe per-instance name
746
+ * hint for child bootstrap.
747
+ */
748
+ nameEnv?: string;
749
+ /**
750
+ * Scoped env name whose value may be used as the safe per-instance
751
+ * description hint for child bootstrap.
752
+ */
753
+ descriptionEnv?: string;
754
+ /**
755
+ * Scoped env names that should be copied into the child thread for each
756
+ * immediate instance group.
757
+ */
758
+ scopedEnv?: string[];
759
+ };
760
+ /**
761
+ * Optional branch flag env name.
762
+ *
763
+ * When set, this subagent is only enabled when the named env resolves to
764
+ * `true`, `1`, or `yes` (case-insensitive).
765
+ */
766
+ optional?: string;
767
+ /**
768
+ * Resumability configuration.
769
+ *
770
+ * - `false` (default): Non-resumable subagent
771
+ * - Object: Resumable subagent with message routing and instance limits
772
+ *
773
+ * When resumable mode is enabled, runtimes SHOULD provide a built-in create
774
+ * and message lifecycle interface instead of exposing raw agent callables for
775
+ * new instance creation.
776
+ */
777
+ resumable?: false | {
778
+ /**
779
+ * Which side of the child `dual_ai` conversation receives parent messages.
780
+ *
781
+ * - `side_a`: Messages are queued as `role: 'user'`
782
+ * - `side_b`: Messages are queued as `role: 'assistant'`
783
+ */
784
+ receives_messages: 'side_a' | 'side_b';
785
+ /**
786
+ * Maximum concurrent instances for this subagent tool.
787
+ *
788
+ * When reached, implementations may remove this tool from subsequent LLM
789
+ * requests and route new messages to existing instances.
790
+ *
791
+ * @default unlimited
792
+ */
793
+ maxInstances?: number;
794
+ /**
795
+ * How this child reports back to its parent.
796
+ *
797
+ * - `implicit` (default): Child completion is automatically queued to the parent.
798
+ * - `explicit`: The runtime does not auto-queue child completion; tools/hooks may
799
+ * use thread APIs such as `state.notifyParent()` when they choose to escalate.
800
+ */
801
+ parentCommunication?: 'implicit' | 'explicit';
802
+ };
803
+ }
804
+ ```
805
+
806
+ 1. Parents always create children.
807
+ 1. Parents explicitly create children by calling the tool `subagent_create`.
808
+ 2. Parents implicitly create children by having a subagent tool call with `immediate: { ... }` which creates a child thread as soon as the parent thread is activated, without the parent having to explicitly call the tool.
809
+ 2. Children only communicate back to their parents:
810
+ 1. Implicit communication: the child thread automatically queues a message to the parent thread when it ends a "session". A session ends, typically, when one side calls the tools assigned to `sessionStop` or `sessionFail` properties in the agent definition, or the `maxSessionTurns` is reached. All subagents communicate implicitly, unless they are resumable and have `resumable.parentCommunication` set to `explicit`.
811
+ 2. Explicit communication: the child thread never communicates with the parent unless `state.notifyParent()` is called. Typically this is done in the tools that are given to the child thread. These calls are independent from session management. Subagents that receive a lot of inbound traffic, for example a Slack subagent, may want to use explicit communication to have more control over when the parent is notified and reduce noise.
812
+ 3. Resumable subagents can receive messages from their parents. Which side receives the message is indicated by the `resumable.receives_messages`.
813
+ 4. When sending messages back to the parent, subagents MUST indicate if they require the parent to provide a response or not (in plain english, for example "After researching you must provide a response back so I can continue with sending this email."). For example, a Gmail subagent may ask its parent for guidance on how to respond to an email regarding company policy, the coordinator may ask another subagent with expertise in legal matters and company policy for advice. The legal subagent does not need a response, it is just providing information, but the gmail subagent does require a response in order to proceed. Thus it is critical that the gmail subagent indicates to the parent that it needs a response in order to continue.
814
+ 5. File attachments are represented by simple strings. Anytime a file is added to a standard agent's filesystem by generation or upload it is given an explicit attachment "path" `/attachments/{filename}.{ext}`. This MUST be used anytime an agent is coordinating with subagents. The tool definition for a subagent can indicate an `initAttachmentsProperty`, which should be an array of strings — if these strings are valid attachments in the parent's file system, then those attachments will be copied into the subagent's file system and attached along with the `initUserMessageProperty` when the subagent is created (note: this also is true for sub-prompts).
815
+ 6. Resumable agents communicate from one agent to another via "silent" user messages. These messages indicate which agent instance they are from (via uuid) as well as the content of the message. The parent agent can then decide what to do with the message, whether to respond to it, or just use the information in the message to make a decision.
816
+ 7. If a message is sent to a parent agent or a sub agent that is currently busy, it will be queued and sent when the agent is free.
817
+ 8. When writing the language for prompts that include resumable subagents, ensure you clearly describe when the subagent should be created (assuming it's not immediate) via `subagent_create` vs when it should just be given a message via `subagent_message`. For example, if you have a research subagent that is researching a given topic, and another subagent needs additional information on that topic, just send a message back to the same resumable subagent, so its existing context can be useful. But if it's requesting information on a totally new topic, then perhaps it should use `subagent_create`.
818
+
819
+
820
+ ## Coordinators and the agent graph
821
+
822
+ The coordinator pattern is akin to the actor model. It can work well when a number of related subagents need to work together. Coordinators provide two significant benefits:
823
+
824
+ 1. Provide subagent communication for similar domains. For example, a coding agent with a `research_agent` and a `bash_agent`. The coordinator could request the `research_agent` research a given operation. Once the `research_agent` completes its task, it communicates back to the coordinator. The coordinator, being satisfied with the results, can then instruct the `bash_agent` to run certain commands and provide the results. In this scenario, the bash subagent didn't even need to know the research subagent existed in order to benefit from it.
825
+ 2. Filtering. Imagine a `gmail_agent`. On its own it can probably do a pretty good job of filtering out spam. But the `gmail_agent` shouldn't be responsible for the higher-level objectives of an organization. A coordinator that sits above a `gmail_agent` could have directives for what kind of email really matters to the CEO of the sprinkler hose company and can further reduce clutter before it passes email results to it's parent.
826
+
827
+ The agent graph is critical to get right. The deeper a graph, the more latency is introduced and the more chances for inter-agent communication to fail. However, deep graphs can also allow for better parallelization. A `marketing_communications_agent` may have a social media subagent: `social_media_agent` which may in turn have a `twitter_agent`, `linkedin_agent`, and `facebook_agent` as subagents. This subtree can perform many of its functions without ever even involving the top-level coordinator, allowing it additional flexibility.
828
+
829
+ ## Hooks
830
+
831
+ Hooks are a powerful mechanism for extending the functionality of agents without having to modify their core logic. They can be used for a variety of purposes, such as logging, monitoring, modifying inputs/outputs, and more. Hooks are defined via `defineHook` and can be referenced in both agent and prompt definitions by their unique `id`.
832
+
833
+ A common use for a hook is to truncate the conversation history, or inject artificial tool calls into the conversation. This can be useful, for example, to give a model awareness of the real time world clock, or to provide additional context without an explicit tool call. The following hooks are supported:
834
+
835
+ | Hook | Execution Point | Purpose |
836
+ |------|-----------------|---------|
837
+ | filter_messages | Before LLM context assembly | Filter/transform message history |
838
+ | prefilter_llm_history | After context assembly | Final adjustments before LLM request |
839
+ | before_create_message | Before message insert | Transform message before storage |
840
+ | after_create_message | After message insert | Side effects after storage |
841
+ | before_update_message | Before message update | Transform update data |
842
+ | after_update_message | After message update | Side effects after update |
843
+ | before_store_tool_result | Before tool result storage | Transform tool results |
844
+ | after_tool_call_success | After successful tool call | Post-process success results |
845
+ | after_tool_call_failure | After failed tool call | Handle/recover from errors |
846
+
847
+ Note: Do be careful when applying hooks to ensure matching tool calls and results are not separated or truncated off as this can lead to errors in many models.
848
+
849
+ ## Variables and Environment
850
+
851
+ Variables allow tools, prompts, and agents to specify dynamic content they require to function properly. For example a `weather_agent` may have a variable `LOCATION` that it needs in order to provide accurate weather information. Variables can be one of two types: `text` or `secret`. Text variables are simple strings that can be used for any purpose. Secret variables are encrypted and MUST only be used in tools to prevent exposing them to a model. For example, an `GMAIL_API_KEY` variable should be of type `secret` and only used in a tool that makes API calls, it should never be included in a prompt or exposed to the model in any way.
852
+
853
+ When creating a new thread, all required variables used within the agent graph must be provided. Variables can have their values provided by prompts, tools, or threads. An instance of a variable is called an "environment variable" or `env` whereas the definition of the variable is just called a variable.
854
+
855
+ ## ThreadState
856
+
857
+ The `ThreadState` object is a powerful tool that is passed to all callables, hooks, and endpoints. It provides access to the current thread's context, including its messages, tools, variables, filesystem, and more. To increase portability, callable tools should only use the `ThreadState` APIs for interacting with the execution environment. For example, rather than using `node:fs` to read files, use `state.readFile()` which will work regardless of the underlying runtime environment.
858
+
859
+ ```ts
860
+ /**
861
+ * Thread state interface - the unified API for thread interactions.
862
+ * Available to tools, hooks, and endpoints.
863
+ */
864
+ interface ThreadState {
865
+ // ─────────────────────────────────────────────────────────────────────────
866
+ // Identity (readonly)
867
+ // ─────────────────────────────────────────────────────────────────────────
868
+ readonly threadId: string;
869
+ readonly agentId: string;
870
+ readonly userId: string | null;
871
+ readonly createdAt: number;
872
+ readonly children: SubagentRegistryEntry[];
873
+ readonly terminated: number | null;
874
+
875
+ // ─────────────────────────────────────────────────────────────────────────
876
+ // Messages
877
+ // ─────────────────────────────────────────────────────────────────────────
878
+ getMessages(options?: GetMessagesOptions): Promise<MessagesResult>;
879
+ getMessage(messageId: string): Promise<Message | null>;
880
+ injectMessage(input: InjectMessageInput): Promise<Message>;
881
+ queueMessage(input: QueueMessageInput): Promise<void>;
882
+ updateMessage(messageId: string, updates: MessageUpdates): Promise<Message>;
883
+
884
+ // ─────────────────────────────────────────────────────────────────────────
885
+ // Logs
886
+ // ─────────────────────────────────────────────────────────────────────────
887
+ getLogs(options?: GetLogsOptions): Promise<Log[]>;
888
+
889
+ // ─────────────────────────────────────────────────────────────────────────
890
+ // Resource Loading
891
+ // ─────────────────────────────────────────────────────────────────────────
892
+ loadModel<T = unknown>(name: string): Promise<T>;
893
+ loadPrompt<T = unknown>(name: string): Promise<T>;
894
+ loadAgent<T = unknown>(name: string): Promise<T>;
895
+ getChildThread(referenceId: string): Promise<ThreadState | null>;
896
+ getParentThread(): Promise<ThreadState | null>;
897
+ getPromptNames(): string[];
898
+ getAgentNames(): string[];
899
+ getModelNames(): string[];
900
+ env(propertyName: string): Promise<string>;
901
+ setEnv(propertyName: string, value: string): Promise<void>;
902
+ notifyParent(content: string): Promise<void>;
903
+ setStatus(status: string): Promise<void>;
904
+
905
+ // ─────────────────────────────────────────────────────────────────────────
906
+ // Tool Invocation
907
+ // ─────────────────────────────────────────────────────────────────────────
908
+ queueTool(toolName: string, args: Record<string, unknown>): void;
909
+ invokeTool(toolName: string, args: Record<string, unknown>): Promise<ToolResult>;
910
+
911
+ // ─────────────────────────────────────────────────────────────────────────
912
+ // Effect Scheduling
913
+ // ─────────────────────────────────────────────────────────────────────────
914
+ scheduleEffect(name: string, args: Record<string, unknown>, delay?: number): Promise<string>;
915
+ getScheduledEffects(name?: string): Promise<ScheduledEffect[]>;
916
+ removeScheduledEffect(id: string): Promise<boolean>;
917
+
918
+ // ─────────────────────────────────────────────────────────────────────────
919
+ // Events
920
+ // ─────────────────────────────────────────────────────────────────────────
921
+ emit(event: string, data: unknown): void;
922
+
923
+ // ─────────────────────────────────────────────────────────────────────────
924
+ // Context Storage
925
+ // ─────────────────────────────────────────────────────────────────────────
926
+ context: Record<string, unknown>;
927
+
928
+ // ─────────────────────────────────────────────────────────────────────────
929
+ // File System
930
+ // ─────────────────────────────────────────────────────────────────────────
931
+ writeFile(path: string, data: ArrayBuffer | string, mimeType: string, options?: WriteFileOptions): Promise<FileRecord>;
932
+ readFile(path: string): Promise<ArrayBuffer | null>;
933
+ readFileStream(path: string, options?: ReadFileStreamOptions): Promise<AsyncIterable<FileChunk> | null>;
934
+ statFile(path: string): Promise<FileRecord | null>;
935
+ readdirFile(path: string): Promise<ReaddirResult>;
936
+ unlinkFile(path: string): Promise<void>;
937
+ mkdirFile(path: string): Promise<FileRecord>;
938
+ rmdirFile(path: string): Promise<void>;
939
+ getFileStats(): Promise<FileStats>;
940
+ grepFiles(pattern: string): Promise<GrepResult[]>;
941
+ findFiles(pattern: string): Promise<FindResult>;
942
+ getFileThumbnail(path: string): Promise<ArrayBuffer | null>;
943
+
944
+ // ─────────────────────────────────────────────────────────────────────────
945
+ // Execution State
946
+ // ─────────────────────────────────────────────────────────────────────────
947
+ execution: ExecutionState | null;
948
+ terminate(): Promise<void>;
949
+
950
+ // ─────────────────────────────────────────────────────────────────────────
951
+ // Runtime Context (Non-Portable)
952
+ // ─────────────────────────────────────────────────────────────────────────
953
+ readonly _notPackableRuntimeContext?: Record<string, unknown>;
954
+ }
955
+ ```
956
+
957
+
958
+ ## Further reading
959
+
960
+ To get an even more detailed understanding of how to create agents, read the following:
961
+
962
+ Specification Docummentation: https://standardagentspec.org/llms.txt
963
+ Agent Builder Documentation: https://docs.standardagentbuilder.com/llms.txt
964
+
965
+
966
+ ## Implementation checking
967
+
968
+ When you edit `agents/`, `prompts/`, `models/`, `tools/`, `hooks/`, or `worker/`,
969
+ validate before claiming the change is done.
970
+
971
+ Validation order:
972
+ 1. Read `package.json` and prefer project scripts if they exist.
973
+ 2. Refresh Cloudflare types:
974
+ - use `pnpm cf-typegen` if that script exists
975
+ - otherwise run `npx wrangler types`
976
+ 3. Regenerate AgentBuilder types by running the project's build command:
977
+ - usually `pnpm build`, `npm run build`, `pnpm run dev` or equivalent
978
+ 4. Run the project's type checker:
979
+ - prefer `pnpm type-check` / `npm run type-check` if present
980
+ - otherwise use the installed checker directly, such as `pnpm exec vue-tsc --build`, `pnpm exec tsc -b`, or `pnpm exec tsc --noEmit`
981
+ 5. If any validation step cannot run, state exactly what is missing.