@standardagents/cli 0.9.13 → 0.9.15

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/dist/index.js CHANGED
@@ -28,214 +28,995 @@ var logger = {
28
28
  console.log(message);
29
29
  }
30
30
  };
31
- var WRANGLER_TEMPLATE = (name) => `{
32
- "$schema": "node_modules/wrangler/config-schema.json",
33
- "name": "${name}",
34
- "main": "worker/index.ts",
35
- "compatibility_date": "2025-01-01",
36
- "compatibility_flags": ["nodejs_compat"],
37
- "observability": {
38
- "enabled": true
39
- },
40
- "assets": {
41
- "directory": "dist",
42
- "not_found_handling": "single-page-application",
43
- "binding": "ASSETS",
44
- "run_worker_first": ["/**"]
45
- },
46
- "durable_objects": {
47
- "bindings": [
48
- {
49
- "name": "AGENT_BUILDER_THREAD",
50
- "class_name": "DurableThread"
51
- },
52
- {
53
- "name": "AGENT_BUILDER",
54
- "class_name": "DurableAgentBuilder"
55
- }
56
- ]
57
- },
58
- "migrations": [
59
- {
60
- "tag": "v1",
61
- "new_sqlite_classes": ["DurableThread"]
62
- },
63
- {
64
- "tag": "v2",
65
- "new_sqlite_classes": ["DurableAgentBuilder"]
66
- }
67
- ]
31
+
32
+ // src/templates/root.ts
33
+ var ROOT_CLAUDE_MD = `# Standard Agents
34
+
35
+ This project uses Standard Agents - a framework for building AI agents on Cloudflare Workers.
36
+
37
+ ## Architecture Overview
38
+
39
+ Standard Agents uses a **composition model** where four core components work together:
40
+
41
+ \`\`\`
42
+ Model \u2192 Prompt \u2192 Agent \u2192 Thread
43
+ \u2502 \u2502 \u2502 \u2502
44
+ \u2502 \u2502 \u2502 \u2514\u2500 Conversation instance (Durable Object)
45
+ \u2502 \u2502 \u2514\u2500 Orchestrates conversation flow
46
+ \u2502 \u2514\u2500 System instructions + tools
47
+ \u2514\u2500 LLM provider configuration
48
+ \`\`\`
49
+
50
+ **Models** define which LLM to use (provider, model ID, pricing, fallbacks).
51
+ **Prompts** define what the LLM should do (system instructions, available tools).
52
+ **Agents** orchestrate conversations (which prompt, stop conditions, turn limits).
53
+ **Threads** are individual conversation instances with persistent storage.
54
+
55
+ ## How Components Compose
56
+
57
+ ### Basic Flow
58
+ 1. User creates a thread with an agent
59
+ 2. Agent uses its configured prompt
60
+ 3. Prompt specifies the model and tools
61
+ 4. LLM responds, potentially calling tools
62
+ 5. Tools execute and return results
63
+ 6. Loop continues until stop condition
64
+
65
+ ### Sub-Prompts (Prompts as Tools)
66
+ Prompts can call other prompts as tools. This enables:
67
+ - **Specialized reasoning**: A main prompt delegates to expert sub-prompts
68
+ - **Structured outputs**: Sub-prompt validates and formats data
69
+ - **Context isolation**: Sub-prompt gets fresh context without full history
70
+
71
+ \`\`\`typescript
72
+ // Main prompt can call 'data_extractor' as a tool
73
+ definePrompt({
74
+ name: 'main_assistant',
75
+ tools: ['data_extractor'], // Another prompt exposed as tool
76
+ // ...
77
+ });
78
+ \`\`\`
79
+
80
+ ### Agent Handoffs (Agents as Tools)
81
+ Agents can delegate to other agents entirely:
82
+ - **Specialization**: Route to domain-specific agents
83
+ - **Escalation**: Hand off to more capable agents
84
+ - **Workflows**: Chain agents for multi-step processes
85
+
86
+ \`\`\`typescript
87
+ defineAgent({
88
+ name: 'triage_agent',
89
+ exposeAsTool: true,
90
+ toolDescription: 'Hand off to triage for initial assessment',
91
+ // ...
92
+ });
93
+ \`\`\`
94
+
95
+ ## File Structure
96
+
97
+ \`\`\`
98
+ agents/
99
+ \u251C\u2500\u2500 CLAUDE.md # This file
100
+ \u251C\u2500\u2500 Thread.ts # Durable Object for conversation threads
101
+ \u251C\u2500\u2500 AgentBuilder.ts # Durable Object for metadata
102
+ \u251C\u2500\u2500 agents/ # Agent definitions (defineAgent)
103
+ \u2502 \u2514\u2500\u2500 CLAUDE.md
104
+ \u251C\u2500\u2500 prompts/ # Prompt definitions (definePrompt)
105
+ \u2502 \u2514\u2500\u2500 CLAUDE.md
106
+ \u251C\u2500\u2500 models/ # Model configurations (defineModel)
107
+ \u2502 \u2514\u2500\u2500 CLAUDE.md
108
+ \u251C\u2500\u2500 tools/ # Custom tools (defineTool)
109
+ \u2502 \u2514\u2500\u2500 CLAUDE.md
110
+ \u251C\u2500\u2500 hooks/ # Lifecycle hooks (defineHook)
111
+ \u2502 \u2514\u2500\u2500 CLAUDE.md
112
+ \u2514\u2500\u2500 api/ # Thread API endpoints (defineThreadEndpoint)
113
+ \u2514\u2500\u2500 CLAUDE.md
114
+ \`\`\`
115
+
116
+ Files are **auto-discovered** at runtime. No manual registration needed.
117
+
118
+ ## FlowState
119
+
120
+ \`FlowState\` is the central state object available in tools and hooks:
121
+
122
+ \`\`\`typescript
123
+ interface FlowState {
124
+ thread: {
125
+ id: string; // Thread identifier
126
+ instance: DurableThread; // Durable Object instance
127
+ };
128
+ agent: AgentConfig; // Current agent configuration
129
+ prompt: PromptConfig; // Current prompt configuration
130
+ model: ModelConfig; // Current model configuration
131
+ messages: Message[]; // Conversation history
132
+ env: Env; // Cloudflare Worker environment
133
+ rootState: FlowState; // Parent state (for sub-prompts)
68
134
  }
135
+ \`\`\`
136
+
137
+ Access in tools:
138
+ \`\`\`typescript
139
+ export default defineTool('...', schema, async (flow, args) => {
140
+ const threadId = flow.thread.id;
141
+ const messages = flow.messages;
142
+ // ...
143
+ });
144
+ \`\`\`
145
+
146
+ ## Quick Reference
147
+
148
+ | Function | Purpose | Directory |
149
+ |----------|---------|-----------|
150
+ | \`defineModel()\` | Configure LLM provider | \`agents/models/\` |
151
+ | \`definePrompt()\` | System instructions + tools | \`agents/prompts/\` |
152
+ | \`defineAgent()\` | Conversation orchestration | \`agents/agents/\` |
153
+ | \`defineTool()\` | Custom tool functions | \`agents/tools/\` |
154
+ | \`defineHook()\` | Lifecycle interception | \`agents/hooks/\` |
155
+ | \`defineThreadEndpoint()\` | Custom API routes | \`agents/api/\` |
156
+
157
+ ## Runtime Utilities
158
+
159
+ Import from \`@standardagents/builder\`:
160
+
161
+ \`\`\`typescript
162
+ import {
163
+ queueTool, // Queue another tool for execution
164
+ injectMessage, // Add message without triggering execution
165
+ getMessages, // Retrieve message history
166
+ emitThreadEvent, // Send custom WebSocket events
167
+ } from '@standardagents/builder';
168
+ \`\`\`
169
+
170
+ ## Documentation
171
+
172
+ Full documentation: https://docs.standardagentbuilder.com
173
+
174
+ - [Architecture Overview](https://docs.standardagentbuilder.com/introduction/architecture)
175
+ - [Models](https://docs.standardagentbuilder.com/core-concepts/models)
176
+ - [Prompts](https://docs.standardagentbuilder.com/core-concepts/prompts)
177
+ - [Agents](https://docs.standardagentbuilder.com/core-concepts/agents)
178
+ - [Tools](https://docs.standardagentbuilder.com/core-concepts/tools)
179
+ - [Hooks](https://docs.standardagentbuilder.com/core-concepts/hooks)
180
+ - [FlowState](https://docs.standardagentbuilder.com/core-concepts/flowstate)
181
+ - [Thread API](https://docs.standardagentbuilder.com/core-concepts/api)
69
182
  `;
70
- var THREAD_TS = `import { DurableThread } from 'virtual:@standardagents/builder'
71
183
 
72
- export default class Thread extends DurableThread {}
184
+ // src/templates/models.ts
185
+ var MODELS_CLAUDE_MD = `# Model Definitions
186
+
187
+ Models define which LLM provider and model to use for prompts.
188
+
189
+ ## Properties
190
+
191
+ | Property | Type | Required | Description |
192
+ |----------|------|----------|-------------|
193
+ | \`name\` | \`string\` | Yes | Unique identifier for this model configuration |
194
+ | \`provider\` | \`'openai' \\| 'anthropic' \\| 'openrouter' \\| 'google'\` | Yes | LLM provider |
195
+ | \`model\` | \`string\` | Yes | Model ID sent to provider API |
196
+ | \`fallbacks\` | \`string[]\` | No | Fallback model names to try if primary fails |
197
+ | \`inputPrice\` | \`number\` | No | Cost per 1M input tokens (USD) |
198
+ | \`outputPrice\` | \`number\` | No | Cost per 1M output tokens (USD) |
199
+ | \`cachedPrice\` | \`number\` | No | Cost per 1M cached input tokens |
200
+ | \`includedProviders\` | \`string[]\` | No | Provider prefixes for OpenRouter routing |
201
+
202
+ ## Example
203
+
204
+ \`\`\`typescript
205
+ import { defineModel } from '@standardagents/builder';
206
+
207
+ export default defineModel({
208
+ name: 'gpt-4o',
209
+ provider: 'openai',
210
+ model: 'gpt-4o',
211
+ fallbacks: ['gpt-4-turbo', 'gpt-3.5-turbo'],
212
+ inputPrice: 2.5,
213
+ outputPrice: 10,
214
+ });
215
+ \`\`\`
216
+
217
+ ## Provider API Keys
218
+
219
+ Set these environment variables in \`.dev.vars\` (local) or Cloudflare secrets (production):
220
+
221
+ | Provider | Environment Variable |
222
+ |----------|---------------------|
223
+ | OpenAI | \`OPENAI_API_KEY\` |
224
+ | Anthropic | \`ANTHROPIC_API_KEY\` |
225
+ | OpenRouter | \`OPENROUTER_API_KEY\` |
226
+ | Google | \`GOOGLE_API_KEY\` |
227
+
228
+ ## Fallback Strategy
229
+
230
+ When a model fails, fallbacks are tried in order:
231
+ 1. Primary model (2 attempts)
232
+ 2. First fallback (2 attempts)
233
+ 3. Second fallback (2 attempts)
234
+ 4. ...and so on
235
+
236
+ Retries occur on: network errors, rate limits (429), server errors (5xx), auth errors (401).
237
+
238
+ ## OpenRouter Configuration
239
+
240
+ For OpenRouter, use the full model path:
241
+
242
+ \`\`\`typescript
243
+ export default defineModel({
244
+ name: 'claude-3-opus',
245
+ provider: 'openrouter',
246
+ model: 'anthropic/claude-3-opus',
247
+ includedProviders: ['anthropic'], // Prefer Anthropic's servers
248
+ });
249
+ \`\`\`
250
+
251
+ ## Best Practices
252
+
253
+ - **Name by use case**, not model ID (e.g., \`fast_reasoning\` not \`gpt-4o-mini\`)
254
+ - **Configure fallbacks** for production reliability
255
+ - **Set pricing** for cost tracking in logs
256
+ - **Use environment variables** for API keys, never hardcode
257
+
258
+ ## Documentation
259
+
260
+ Full reference: https://docs.standardagentbuilder.com/api-reference/define/model
73
261
  `;
74
- var AGENT_BUILDER_TS = `import { DurableAgentBuilder } from 'virtual:@standardagents/builder'
75
262
 
76
- export default class AgentBuilder extends DurableAgentBuilder {}
263
+ // src/templates/prompts.ts
264
+ var PROMPTS_CLAUDE_MD = `# Prompt Definitions
265
+
266
+ Prompts define system instructions, model selection, and available tools for LLM interactions.
267
+
268
+ ## Properties
269
+
270
+ | Property | Type | Required | Description |
271
+ |----------|------|----------|-------------|
272
+ | \`name\` | \`string\` | Yes | Unique identifier for this prompt |
273
+ | \`toolDescription\` | \`string\` | Yes | Description shown when used as a tool |
274
+ | \`prompt\` | \`string \\| StructuredPrompt\` | Yes | System instructions |
275
+ | \`model\` | \`string\` | Yes | Model name to use (references \`agents/models/\`) |
276
+ | \`tools\` | \`(string \\| ToolConfig)[]\` | No | Available tools for LLM |
277
+ | \`handoffAgents\` | \`string[]\` | No | Agents this prompt can hand off to |
278
+ | \`includeChat\` | \`boolean\` | No | Include full conversation history |
279
+ | \`includePastTools\` | \`boolean\` | No | Include previous tool results |
280
+ | \`parallelToolCalls\` | \`boolean\` | No | Allow multiple concurrent tool calls |
281
+ | \`toolChoice\` | \`'auto' \\| 'none' \\| 'required'\` | No | Tool calling strategy |
282
+ | \`requiredSchema\` | \`z.ZodObject\` | No | Input validation when called as tool |
283
+ | \`beforeTool\` | \`string\` | No | Tool to run before LLM request |
284
+ | \`afterTool\` | \`string\` | No | Tool to run after LLM response |
285
+ | \`reasoning\` | \`ReasoningConfig\` | No | Extended thinking configuration |
286
+
287
+ ## Basic Example
288
+
289
+ \`\`\`typescript
290
+ import { definePrompt } from '@standardagents/builder';
291
+
292
+ export default definePrompt({
293
+ name: 'customer_support',
294
+ toolDescription: 'Handle customer support inquiries',
295
+ model: 'gpt-4o',
296
+ prompt: \`You are a helpful customer support agent.
297
+ Always be polite and professional.
298
+ If you cannot help, escalate to a human.\`,
299
+ tools: ['search_knowledge_base', 'create_ticket'],
300
+ includeChat: true,
301
+ });
302
+ \`\`\`
303
+
304
+ ## Structured Prompts
305
+
306
+ Include other prompts for reusable instruction blocks:
307
+
308
+ \`\`\`typescript
309
+ export default definePrompt({
310
+ name: 'main_assistant',
311
+ toolDescription: 'Primary assistant',
312
+ model: 'gpt-4o',
313
+ prompt: [
314
+ { type: 'text', content: 'You are a helpful assistant.\\n\\n' },
315
+ { type: 'include', prompt: 'common_rules' }, // Includes another prompt
316
+ { type: 'text', content: '\\n\\nBe concise.' },
317
+ ],
318
+ });
319
+ \`\`\`
320
+
321
+ ## Tool Configuration
322
+
323
+ Tools can be simple names or detailed configs:
324
+
325
+ \`\`\`typescript
326
+ tools: [
327
+ 'simple_tool', // Just the tool name
328
+ {
329
+ name: 'complex_tool',
330
+ includeTextResponse: true, // Include sub-prompt text in result
331
+ includeToolCalls: false, // Exclude sub-prompt tool calls
332
+ includeErrors: true, // Include error details
333
+ initUserMessageProperty: 'query', // Use this arg as initial message
334
+ },
335
+ ],
336
+ \`\`\`
337
+
338
+ ## Input Validation
339
+
340
+ Validate inputs when prompt is called as a tool:
341
+
342
+ \`\`\`typescript
343
+ import { z } from 'zod';
344
+
345
+ export default definePrompt({
346
+ name: 'data_extractor',
347
+ toolDescription: 'Extract structured data from text',
348
+ model: 'gpt-4o',
349
+ prompt: 'Extract the requested data from the input.',
350
+ requiredSchema: z.object({
351
+ text: z.string().describe('Text to extract from'),
352
+ fields: z.array(z.string()).describe('Fields to extract'),
353
+ }),
354
+ });
355
+ \`\`\`
356
+
357
+ ## Extended Thinking (Reasoning)
358
+
359
+ Enable for complex reasoning tasks:
360
+
361
+ \`\`\`typescript
362
+ export default definePrompt({
363
+ name: 'code_reviewer',
364
+ toolDescription: 'Review code for issues',
365
+ model: 'claude-3-opus',
366
+ prompt: 'Review the code thoroughly...',
367
+ reasoning: {
368
+ effort: 'high', // 'low' | 'medium' | 'high'
369
+ maxTokens: 16000, // Max thinking tokens
370
+ exclude: true, // Don't include thinking in response
371
+ },
372
+ });
373
+ \`\`\`
374
+
375
+ ## Best Practices
376
+
377
+ - **Write clear instructions** - Structure with headers and bullet points
378
+ - **Describe tools thoroughly** - LLMs decide based on descriptions
379
+ - **Validate inputs with Zod** - Catch errors early
380
+ - **Use \`includeChat\`** for conversational context
381
+ - **Name by purpose**, not model (e.g., \`support_agent\` not \`gpt4_prompt\`)
382
+
383
+ ## Documentation
384
+
385
+ Full reference: https://docs.standardagentbuilder.com/api-reference/define/prompt
77
386
  `;
78
- var WORKER_INDEX = `import { router } from "virtual:@standardagents/builder"
79
- import DurableThread from '../agents/Thread';
80
- import DurableAgentBuilder from '../agents/AgentBuilder';
81
387
 
82
- export default {
83
- async fetch(request, env) {
84
- const res = await router(request, env)
85
- return res ?? new Response(null, { status: 404 })
388
+ // src/templates/agents.ts
389
+ var AGENTS_CLAUDE_MD = `# Agent Definitions
390
+
391
+ Agents orchestrate conversations by defining prompts, stop conditions, and turn limits.
392
+
393
+ ## Agent Properties
394
+
395
+ | Property | Type | Required | Description |
396
+ |----------|------|----------|-------------|
397
+ | \`name\` | \`string\` | Yes | Unique identifier for this agent |
398
+ | \`type\` | \`'ai_human' \\| 'dual_ai'\` | No | Conversation type (default: \`ai_human\`) |
399
+ | \`sideA\` | \`SideConfig\` | Yes | Configuration for side A (AI in ai_human) |
400
+ | \`sideB\` | \`SideConfig\` | No | Configuration for side B (required for dual_ai) |
401
+ | \`maxSessionTurns\` | \`number\` | No | Max total turns across both sides |
402
+ | \`exposeAsTool\` | \`boolean\` | No | Allow other prompts to hand off to this agent |
403
+ | \`toolDescription\` | \`string\` | No | Description when used as tool (required if exposeAsTool) |
404
+ | \`tags\` | \`string[]\` | No | Tags for categorization and filtering |
405
+
406
+ ## Side Configuration
407
+
408
+ | Property | Type | Required | Description |
409
+ |----------|------|----------|-------------|
410
+ | \`label\` | \`string\` | No | Display name for this side |
411
+ | \`prompt\` | \`string\` | Yes | Prompt name to use (references \`agents/prompts/\`) |
412
+ | \`stopOnResponse\` | \`boolean\` | No | Stop turn when LLM returns text (default: true) |
413
+ | \`stopTool\` | \`string\` | No | Tool that stops this side's turn |
414
+ | \`stopToolResponseProperty\` | \`string\` | No | Property to extract from stop tool result |
415
+ | \`endConversationTool\` | \`string\` | No | Tool that ends the entire conversation |
416
+ | \`maxTurns\` | \`number\` | No | Maximum turns for this side |
417
+ | \`manualStopCondition\` | \`boolean\` | No | Enable custom stop handling via hooks |
418
+
419
+ ## Basic Example (AI-Human)
420
+
421
+ \`\`\`typescript
422
+ import { defineAgent } from '@standardagents/builder';
423
+
424
+ export default defineAgent({
425
+ name: 'support_agent',
426
+ type: 'ai_human',
427
+ sideA: {
428
+ label: 'Support',
429
+ prompt: 'customer_support',
430
+ stopOnResponse: true,
431
+ endConversationTool: 'close_ticket',
432
+ maxTurns: 50,
86
433
  },
87
- } satisfies ExportedHandler<Env>
434
+ tags: ['support', 'tier-1'],
435
+ });
436
+ \`\`\`
88
437
 
89
- export { DurableThread, DurableAgentBuilder }
438
+ ## Dual-AI Example
439
+
440
+ Two AI agents conversing (e.g., debate, iterative refinement):
441
+
442
+ \`\`\`typescript
443
+ export default defineAgent({
444
+ name: 'code_review_debate',
445
+ type: 'dual_ai',
446
+ maxSessionTurns: 20,
447
+ sideA: {
448
+ label: 'Reviewer',
449
+ prompt: 'code_reviewer',
450
+ stopOnResponse: true,
451
+ },
452
+ sideB: {
453
+ label: 'Developer',
454
+ prompt: 'code_defender',
455
+ stopOnResponse: true,
456
+ },
457
+ });
458
+ \`\`\`
459
+
460
+ ## Agent Handoffs
461
+
462
+ Expose an agent for handoffs from other prompts:
463
+
464
+ \`\`\`typescript
465
+ export default defineAgent({
466
+ name: 'escalation_agent',
467
+ type: 'ai_human',
468
+ exposeAsTool: true,
469
+ toolDescription: 'Escalate to senior support for complex issues',
470
+ sideA: {
471
+ prompt: 'senior_support',
472
+ },
473
+ });
474
+ \`\`\`
475
+
476
+ Other prompts can then include it in their \`handoffAgents\` array.
477
+
478
+ ## Stop Conditions (Priority Order)
479
+
480
+ 1. **endConversationTool** - Ends entire conversation (highest priority)
481
+ 2. **stopTool** - Ends current side's turn
482
+ 3. **maxTurns** - Ends side's participation when reached
483
+ 4. **stopOnResponse** - Ends turn when LLM returns text
484
+ 5. **maxSessionTurns** - Ends conversation (hard limit: 250)
485
+
486
+ ## Best Practices
487
+
488
+ - **Use descriptive names** (\`customer_support_agent\` not \`agent1\`)
489
+ - **Always set maxTurns** as a safety limit
490
+ - **Match stop conditions to use case** - chat apps use stopOnResponse, workflows use stopTool
491
+ - **Use labels** for clarity in logs and UI
492
+ - **Organize with tags** for filtering
493
+
494
+ ## Documentation
495
+
496
+ Full reference: https://docs.standardagentbuilder.com/api-reference/define/agent
90
497
  `;
91
- var TOOLS_CLAUDE_MD = `# Custom Tools
92
498
 
93
- This directory contains custom tools that your AI agents can call during execution.
499
+ // src/templates/tools.ts
500
+ var TOOLS_CLAUDE_MD = `# Custom Tools
94
501
 
95
- ## What Are Tools?
502
+ Tools extend agent capabilities beyond text generation. They allow LLMs to fetch data, perform calculations, execute side effects, and chain to other prompts or agents.
96
503
 
97
- Tools are functions that extend your agent's capabilities beyond text generation. They allow agents to:
98
- - Fetch external data (APIs, databases)
99
- - Perform calculations
100
- - Execute side effects (send emails, create records)
101
- - Chain to other prompts or agents
504
+ ## Function Signature
102
505
 
103
- ## Creating a Tool
506
+ \`\`\`typescript
507
+ defineTool(
508
+ description: string, // What the tool does (shown to LLM)
509
+ args: z.ZodObject, // Input validation schema
510
+ handler: (flow, args) => ToolResult, // Implementation
511
+ returnSchema?: z.ZodObject // Optional output schema
512
+ )
513
+ \`\`\`
104
514
 
105
- Create a new file in this directory:
515
+ ## Basic Example
106
516
 
107
517
  \`\`\`typescript
108
518
  import { defineTool } from '@standardagents/builder';
109
519
  import { z } from 'zod';
110
520
 
111
521
  export default defineTool(
112
- 'Description of what this tool does',
522
+ 'Search the knowledge base for articles matching a query',
113
523
  z.object({
114
- param1: z.string().describe('Description of param1'),
524
+ query: z.string().describe('Search query'),
525
+ limit: z.number().optional().default(10).describe('Max results'),
115
526
  }),
116
527
  async (flow, args) => {
117
- // Tool implementation
528
+ const results = await searchKB(args.query, args.limit);
529
+ return {
530
+ status: 'success',
531
+ result: JSON.stringify(results),
532
+ };
533
+ }
534
+ );
535
+ \`\`\`
536
+
537
+ ## ToolResult Interface
538
+
539
+ | Property | Type | Description |
540
+ |----------|------|-------------|
541
+ | \`status\` | \`'success' \\| 'error'\` | Whether the tool succeeded |
542
+ | \`result\` | \`string\` | Tool output (required for success) |
543
+ | \`error\` | \`string\` | Error message (for error status) |
544
+
545
+ ## FlowState Access
546
+
547
+ The \`flow\` parameter provides access to:
548
+
549
+ \`\`\`typescript
550
+ async (flow, args) => {
551
+ // Thread information
552
+ const threadId = flow.thread.id;
553
+ const thread = flow.thread.instance;
554
+
555
+ // Configuration
556
+ const agentName = flow.agent.name;
557
+ const modelName = flow.model.name;
558
+
559
+ // Message history
560
+ const messages = flow.messages;
561
+
562
+ // Environment bindings
563
+ const env = flow.env;
564
+
565
+ // Parent state (for sub-prompts)
566
+ const root = flow.rootState;
567
+ }
568
+ \`\`\`
569
+
570
+ ## Tool Without Arguments
571
+
572
+ \`\`\`typescript
573
+ export default defineTool(
574
+ 'Get current server time',
575
+ async (flow) => {
118
576
  return {
119
577
  status: 'success',
120
- result: JSON.stringify({ data: 'result' })
578
+ result: new Date().toISOString(),
121
579
  };
122
580
  }
123
581
  );
124
582
  \`\`\`
125
583
 
126
- Tools are auto-discovered at runtime. No manual registration needed!
584
+ ## Error Handling
585
+
586
+ Return errors gracefully instead of throwing:
587
+
588
+ \`\`\`typescript
589
+ export default defineTool(
590
+ 'Fetch user data',
591
+ z.object({ userId: z.string() }),
592
+ async (flow, args) => {
593
+ try {
594
+ const user = await fetchUser(args.userId);
595
+ return { status: 'success', result: JSON.stringify(user) };
596
+ } catch (error) {
597
+ return {
598
+ status: 'error',
599
+ error: \`Failed to fetch user: \${error.message}\`,
600
+ };
601
+ }
602
+ }
603
+ );
604
+ \`\`\`
605
+
606
+ ## Queueing Additional Tools
607
+
608
+ Queue another tool to run after the current one:
609
+
610
+ \`\`\`typescript
611
+ import { queueTool } from '@standardagents/builder';
612
+
613
+ export default defineTool(
614
+ 'Create order and send confirmation',
615
+ z.object({ items: z.array(z.string()) }),
616
+ async (flow, args) => {
617
+ const order = await createOrder(args.items);
618
+
619
+ // Queue email tool to run next
620
+ queueTool(flow, 'send_confirmation_email', {
621
+ orderId: order.id,
622
+ email: flow.thread.metadata.userEmail,
623
+ });
624
+
625
+ return { status: 'success', result: JSON.stringify(order) };
626
+ }
627
+ );
628
+ \`\`\`
629
+
630
+ ## Injecting Messages
631
+
632
+ Add messages without triggering re-execution:
633
+
634
+ \`\`\`typescript
635
+ import { injectMessage } from '@standardagents/builder';
636
+
637
+ export default defineTool(
638
+ 'Log audit event',
639
+ z.object({ event: z.string() }),
640
+ async (flow, args) => {
641
+ await injectMessage(flow, {
642
+ role: 'system',
643
+ content: \`[AUDIT] \${args.event}\`,
644
+ });
645
+ return { status: 'success', result: 'Logged' };
646
+ }
647
+ );
648
+ \`\`\`
649
+
650
+ ## Best Practices
651
+
652
+ - **Write clear descriptions** - LLMs decide tool usage based on descriptions
653
+ - **Describe all parameters** with \`.describe()\` in Zod schemas
654
+ - **Handle errors gracefully** - return error status, don't throw
655
+ - **Use snake_case** for file names (\`search_knowledge_base.ts\`)
656
+ - **Keep tools focused** - one task per tool
657
+ - **Use \`flow.rootState\`** when queueing from sub-prompts
658
+
659
+ ## Supported Zod Types
660
+
661
+ - Primitives: \`string\`, \`number\`, \`boolean\`, \`null\`
662
+ - Enums: \`z.enum(['a', 'b', 'c'])\`
663
+ - Arrays: \`z.array(z.string())\`
664
+ - Objects: \`z.object({ ... })\`
665
+ - Optional: \`z.string().optional()\`
666
+ - Default: \`z.number().default(10)\`
667
+ - Nullable: \`z.string().nullable()\`
668
+
669
+ ## Documentation
670
+
671
+ Full reference: https://docs.standardagentbuilder.com/api-reference/define/tool
127
672
  `;
673
+
674
+ // src/templates/hooks.ts
128
675
  var HOOKS_CLAUDE_MD = `# Lifecycle Hooks
129
676
 
130
- This directory contains lifecycle hooks that intercept and modify agent execution at key points.
677
+ Hooks intercept and modify agent execution at specific lifecycle points.
678
+
679
+ ## Available Hooks
680
+
681
+ | Hook | Signature | Purpose |
682
+ |------|-----------|---------|
683
+ | \`filter_messages\` | \`(state, rows) => Message[]\` | Filter raw message rows before LLM context |
684
+ | \`prefilter_llm_history\` | \`(state, messages) => Message[]\` | Modify messages after filtering, before LLM |
685
+ | \`before_create_message\` | \`(state, message) => Message\` | Modify message before database insert |
686
+ | \`after_create_message\` | \`(state, message) => void\` | Run logic after message created |
687
+ | \`before_update_message\` | \`(state, id, updates) => Updates\` | Modify updates before message update |
688
+ | \`after_update_message\` | \`(state, message) => void\` | Run logic after message updated |
689
+ | \`before_store_tool_result\` | \`(state, toolCall, result) => Result\` | Modify tool result before storage |
690
+ | \`after_tool_call_success\` | \`(state, toolCall, result) => Result\` | Process successful tool execution |
691
+ | \`after_tool_call_failure\` | \`(state, toolCall, result) => Result\` | Process failed tool execution |
692
+
693
+ ## File Naming
694
+
695
+ Hook files **must** be named exactly as the hook type:
696
+
697
+ \`\`\`
698
+ agents/hooks/
699
+ \u251C\u2500\u2500 filter_messages.ts
700
+ \u251C\u2500\u2500 prefilter_llm_history.ts
701
+ \u251C\u2500\u2500 before_create_message.ts
702
+ \u251C\u2500\u2500 after_create_message.ts
703
+ \u251C\u2500\u2500 before_update_message.ts
704
+ \u251C\u2500\u2500 after_update_message.ts
705
+ \u251C\u2500\u2500 before_store_tool_result.ts
706
+ \u251C\u2500\u2500 after_tool_call_success.ts
707
+ \u2514\u2500\u2500 after_tool_call_failure.ts
708
+ \`\`\`
709
+
710
+ ## Examples
131
711
 
132
- ## What Are Hooks?
712
+ ### Filter Messages (Limit Context)
133
713
 
134
- Hooks are optional functions that run at specific points during agent execution:
135
- - **Before** LLM requests (prefilter messages)
136
- - **After** LLM responses (post-process messages)
137
- - At other lifecycle events (message creation, tool execution, etc.)
714
+ \`\`\`typescript
715
+ import { defineHook } from '@standardagents/builder';
138
716
 
139
- ## Creating a Hook
717
+ export default defineHook('filter_messages', async (state, rows) => {
718
+ // Only include last 20 messages
719
+ return rows.slice(-20);
720
+ });
721
+ \`\`\`
722
+
723
+ ### Prefilter LLM History (Modify Content)
140
724
 
141
725
  \`\`\`typescript
142
726
  import { defineHook } from '@standardagents/builder';
143
727
 
144
728
  export default defineHook('prefilter_llm_history', async (state, messages) => {
145
- // Filter or modify messages before sending to LLM
146
- return messages;
729
+ // Remove sensitive data from message content
730
+ return messages.map(msg => ({
731
+ ...msg,
732
+ content: msg.content?.replace(/\\b\\d{4}-\\d{4}-\\d{4}-\\d{4}\\b/g, '[REDACTED]'),
733
+ }));
734
+ });
735
+ \`\`\`
736
+
737
+ ### Before Create Message (Add Metadata)
738
+
739
+ \`\`\`typescript
740
+ import { defineHook } from '@standardagents/builder';
741
+
742
+ export default defineHook('before_create_message', async (state, message) => {
743
+ return {
744
+ ...message,
745
+ metadata: {
746
+ ...message.metadata,
747
+ createdAt: Date.now(),
748
+ agentVersion: '1.0.0',
749
+ },
750
+ };
751
+ });
752
+ \`\`\`
753
+
754
+ ### After Tool Call Success (Logging)
755
+
756
+ \`\`\`typescript
757
+ import { defineHook } from '@standardagents/builder';
758
+
759
+ export default defineHook('after_tool_call_success', async (state, toolCall, result) => {
760
+ console.log(\`Tool \${toolCall.function.name} succeeded:\`, result);
761
+ return result; // Return result unchanged, or modify it
762
+ });
763
+ \`\`\`
764
+
765
+ ### After Tool Call Failure (Recovery)
766
+
767
+ \`\`\`typescript
768
+ import { defineHook } from '@standardagents/builder';
769
+
770
+ export default defineHook('after_tool_call_failure', async (state, toolCall, result) => {
771
+ // Override error with fallback result
772
+ if (toolCall.function.name === 'fetch_weather') {
773
+ return {
774
+ status: 'success',
775
+ result: JSON.stringify({ temp: 'unavailable', fallback: true }),
776
+ };
777
+ }
778
+ return result; // Return original error
147
779
  });
148
780
  \`\`\`
149
781
 
150
- Hook files must be named exactly as the hook type (e.g., \`prefilter_llm_history.ts\`).
782
+ ## Hook Categories
783
+
784
+ **Transformation hooks** (return modified data):
785
+ - \`filter_messages\`
786
+ - \`prefilter_llm_history\`
787
+ - \`before_create_message\`
788
+ - \`before_update_message\`
789
+ - \`before_store_tool_result\`
790
+ - \`after_tool_call_success\`
791
+ - \`after_tool_call_failure\`
792
+
793
+ **Event hooks** (side effects only):
794
+ - \`after_create_message\`
795
+ - \`after_update_message\`
796
+
797
+ ## Best Practices
798
+
799
+ - **Keep hooks fast** - target <100ms execution
800
+ - **Wrap in try-catch** - hooks continue on error
801
+ - **Validate input data** before processing
802
+ - **Document purpose** with clear comments
803
+ - **Use for cross-cutting concerns** - logging, redaction, enrichment
804
+
805
+ ## Documentation
806
+
807
+ Full reference: https://docs.standardagentbuilder.com/api-reference/define/hook
151
808
  `;
809
+
810
+ // src/templates/api.ts
152
811
  var API_CLAUDE_MD = `# Thread-Specific API Endpoints
153
812
 
154
- This directory contains custom API endpoints that operate on specific threads.
813
+ Define custom API endpoints that operate on specific threads with access to the thread's Durable Object instance and SQLite storage.
155
814
 
156
- ## What Are Thread Endpoints?
815
+ ## File-Based Routing
157
816
 
158
- Thread endpoints are API routes that:
159
- - Automatically receive a specific thread's Durable Object instance
160
- - Can access thread-local SQLite storage
161
- - Perform operations in the context of a conversation
162
- - Use file-based routing for automatic discovery
817
+ | File Pattern | HTTP Method | Route |
818
+ |--------------|-------------|-------|
819
+ | \`name.ts\` | GET | \`/api/threads/:id/name\` |
820
+ | \`name.get.ts\` | GET | \`/api/threads/:id/name\` |
821
+ | \`name.post.ts\` | POST | \`/api/threads/:id/name\` |
822
+ | \`name.put.ts\` | PUT | \`/api/threads/:id/name\` |
823
+ | \`name.delete.ts\` | DELETE | \`/api/threads/:id/name\` |
824
+ | \`nested/route.ts\` | GET | \`/api/threads/:id/nested/route\` |
163
825
 
164
- ## Creating an Endpoint
826
+ ## Basic Example
165
827
 
166
828
  \`\`\`typescript
167
- // agents/api/summary.get.ts -> GET /agents/api/threads/:id/summary
829
+ // agents/api/status.get.ts -> GET /api/threads/:id/status
168
830
  import { defineThreadEndpoint } from '@standardagents/builder';
169
831
 
170
832
  export default defineThreadEndpoint(async (thread, request, env) => {
171
833
  const messages = await thread.getMessages();
172
- return new Response(JSON.stringify({ count: messages.length }), {
173
- headers: { 'Content-Type': 'application/json' }
834
+ return new Response(JSON.stringify({
835
+ messageCount: messages.length,
836
+ status: 'active',
837
+ }), {
838
+ headers: { 'Content-Type': 'application/json' },
174
839
  });
175
840
  });
176
841
  \`\`\`
177
842
 
178
- Pattern: \`{name}.{method}.ts\` (e.g., \`summary.get.ts\`, \`export.post.ts\`)
179
- `;
180
- var AGENTS_CLAUDE_MD = `# Agent Definitions
843
+ ## Handler Parameters
844
+
845
+ | Parameter | Type | Description |
846
+ |-----------|------|-------------|
847
+ | \`thread\` | \`DurableThread\` | The thread's Durable Object instance |
848
+ | \`request\` | \`Request\` | The incoming HTTP request |
849
+ | \`env\` | \`Env\` | Cloudflare Worker environment bindings |
181
850
 
182
- This directory contains your AI agent definitions.
851
+ ## Thread Methods
183
852
 
184
- ## Creating an Agent
853
+ Access thread data through the instance:
185
854
 
186
855
  \`\`\`typescript
187
- import { defineAgent } from '@standardagents/builder';
856
+ export default defineThreadEndpoint(async (thread, request, env) => {
857
+ // Message operations
858
+ const messages = await thread.getMessages({ limit: 100, offset: 0 });
859
+ const count = await thread.getMessageCount();
188
860
 
189
- export default defineAgent({
190
- name: 'my-agent',
191
- type: 'ai_human',
192
- title: 'My Agent',
193
- defaultPrompt: 'my-prompt',
194
- defaultModel: 'gpt-4o',
195
- tools: ['my_tool'],
861
+ // Thread metadata
862
+ const metadata = thread.getMetadata();
863
+
864
+ // Execution control
865
+ await thread.stop();
866
+
867
+ // Custom SQL queries
868
+ const result = thread.ctx.storage.sql.exec(\`
869
+ SELECT * FROM messages WHERE role = 'user'
870
+ \`);
196
871
  });
197
872
  \`\`\`
198
873
 
199
- Agent types:
200
- - \`ai_human\`: Human interacts with AI agent
201
- - \`dual_ai\`: Two AI agents interact with each other
202
- `;
203
- var PROMPTS_CLAUDE_MD = `# Prompt Definitions
874
+ ## POST with Request Body
204
875
 
205
- This directory contains your prompt/system message definitions.
876
+ \`\`\`typescript
877
+ // agents/api/export.post.ts -> POST /api/threads/:id/export
878
+ import { defineThreadEndpoint } from '@standardagents/builder';
206
879
 
207
- ## Creating a Prompt
880
+ export default defineThreadEndpoint(async (thread, request, env) => {
881
+ const body = await request.json();
882
+ const { format = 'json' } = body;
208
883
 
209
- \`\`\`typescript
210
- import { definePrompt } from '@standardagents/builder';
884
+ const messages = await thread.getMessages();
211
885
 
212
- export default definePrompt({
213
- name: 'my-prompt',
214
- model: 'gpt-4o',
215
- systemPrompt: 'You are a helpful assistant...',
216
- tools: ['search', 'calculate'],
886
+ if (format === 'csv') {
887
+ const csv = messages.map(m => \`\${m.role},\${m.content}\`).join('\\n');
888
+ return new Response(csv, {
889
+ headers: { 'Content-Type': 'text/csv' },
890
+ });
891
+ }
892
+
893
+ return new Response(JSON.stringify(messages), {
894
+ headers: { 'Content-Type': 'application/json' },
895
+ });
217
896
  });
218
897
  \`\`\`
219
- `;
220
- var MODELS_CLAUDE_MD = `# Model Definitions
221
898
 
222
- This directory contains your AI model configurations.
899
+ ## Nested Routes
223
900
 
224
- ## Creating a Model
901
+ Create directories for nested routes:
902
+
903
+ \`\`\`
904
+ agents/api/
905
+ \u251C\u2500\u2500 status.get.ts -> GET /api/threads/:id/status
906
+ \u251C\u2500\u2500 export.post.ts -> POST /api/threads/:id/export
907
+ \u2514\u2500\u2500 messages/
908
+ \u251C\u2500\u2500 count.ts -> GET /api/threads/:id/messages/count
909
+ \u2514\u2500\u2500 search.post.ts -> POST /api/threads/:id/messages/search
910
+ \`\`\`
911
+
912
+ ## Error Handling
225
913
 
226
914
  \`\`\`typescript
227
- import { defineModel } from '@standardagents/builder';
915
+ export default defineThreadEndpoint(async (thread, request, env) => {
916
+ try {
917
+ const data = await riskyOperation();
918
+ return new Response(JSON.stringify(data), {
919
+ headers: { 'Content-Type': 'application/json' },
920
+ });
921
+ } catch (error) {
922
+ return new Response(JSON.stringify({
923
+ error: error.message,
924
+ }), {
925
+ status: 500,
926
+ headers: { 'Content-Type': 'application/json' },
927
+ });
928
+ }
929
+ });
930
+ \`\`\`
228
931
 
229
- export default defineModel({
230
- name: 'gpt-4o',
231
- model: 'gpt-4o',
232
- provider: 'openai',
233
- inputPrice: 2.5,
234
- outputPrice: 10,
932
+ ## Using Environment Variables
933
+
934
+ \`\`\`typescript
935
+ export default defineThreadEndpoint(async (thread, request, env) => {
936
+ // Access secrets and bindings
937
+ const apiKey = env.EXTERNAL_API_KEY;
938
+ const kv = env.MY_KV_NAMESPACE;
939
+
940
+ const data = await fetchExternalAPI(apiKey);
941
+ await kv.put(\`thread:\${thread.id}\`, JSON.stringify(data));
942
+
943
+ return new Response('OK');
235
944
  });
236
945
  \`\`\`
237
946
 
238
- Supported providers: \`openai\`, \`anthropic\`, \`openrouter\`, \`google\`
947
+ ## Best Practices
948
+
949
+ - **Keep handlers fast** - endpoints are in the request path
950
+ - **Paginate large queries** - use limit/offset for messages
951
+ - **Validate input data** - check request body before processing
952
+ - **Use descriptive file names** - \`export.post.ts\` not \`ep1.ts\`
953
+ - **Return proper status codes** - 200, 400, 404, 500
954
+
955
+ ## Documentation
956
+
957
+ Full reference: https://docs.standardagentbuilder.com/core-concepts/api
958
+ `;
959
+
960
+ // src/commands/scaffold.ts
961
+ var WRANGLER_TEMPLATE = (name) => `{
962
+ "$schema": "node_modules/wrangler/config-schema.json",
963
+ "name": "${name}",
964
+ "main": "worker/index.ts",
965
+ "compatibility_date": "2025-01-01",
966
+ "compatibility_flags": ["nodejs_compat"],
967
+ "observability": {
968
+ "enabled": true
969
+ },
970
+ "assets": {
971
+ "directory": "dist",
972
+ "not_found_handling": "single-page-application",
973
+ "binding": "ASSETS",
974
+ "run_worker_first": ["/**"]
975
+ },
976
+ "durable_objects": {
977
+ "bindings": [
978
+ {
979
+ "name": "AGENT_BUILDER_THREAD",
980
+ "class_name": "DurableThread"
981
+ },
982
+ {
983
+ "name": "AGENT_BUILDER",
984
+ "class_name": "DurableAgentBuilder"
985
+ }
986
+ ]
987
+ },
988
+ "migrations": [
989
+ {
990
+ "tag": "v1",
991
+ "new_sqlite_classes": ["DurableThread"]
992
+ },
993
+ {
994
+ "tag": "v2",
995
+ "new_sqlite_classes": ["DurableAgentBuilder"]
996
+ }
997
+ ]
998
+ }
999
+ `;
1000
+ var THREAD_TS = `import { DurableThread } from 'virtual:@standardagents/builder'
1001
+
1002
+ export default class Thread extends DurableThread {}
1003
+ `;
1004
+ var AGENT_BUILDER_TS = `import { DurableAgentBuilder } from 'virtual:@standardagents/builder'
1005
+
1006
+ export default class AgentBuilder extends DurableAgentBuilder {}
1007
+ `;
1008
+ var WORKER_INDEX = `import { router } from "virtual:@standardagents/builder"
1009
+ import DurableThread from '../agents/Thread';
1010
+ import DurableAgentBuilder from '../agents/AgentBuilder';
1011
+
1012
+ export default {
1013
+ async fetch(request, env) {
1014
+ const res = await router(request, env)
1015
+ return res ?? new Response(null, { status: 404 })
1016
+ },
1017
+ } satisfies ExportedHandler<Env>
1018
+
1019
+ export { DurableThread, DurableAgentBuilder }
239
1020
  `;
240
1021
  function getProjectName(cwd) {
241
1022
  const packageJsonPath = path.join(cwd, "package.json");
@@ -313,13 +1094,12 @@ async function modifyViteConfig(configPath, force) {
313
1094
  }
314
1095
  }
315
1096
  function createOrUpdateWranglerConfig(cwd, projectName, force) {
316
- var _a, _b;
317
1097
  const existingConfig = findWranglerConfig(cwd);
318
1098
  if (existingConfig && !force) {
319
1099
  try {
320
1100
  const text = fs.readFileSync(existingConfig, "utf-8");
321
1101
  const config = parse(text);
322
- const hasBindings = (_b = (_a = config.durable_objects) == null ? void 0 : _a.bindings) == null ? void 0 : _b.some(
1102
+ const hasBindings = config.durable_objects?.bindings?.some(
323
1103
  (b) => b.name === "AGENT_BUILDER_THREAD" || b.name === "AGENT_BUILDER"
324
1104
  );
325
1105
  if (hasBindings) {
@@ -485,6 +1265,12 @@ function createDurableObjects(cwd) {
485
1265
  }
486
1266
  }
487
1267
  function createDirectoriesAndDocs(cwd) {
1268
+ const agentsDir = path.join(cwd, "agents");
1269
+ const rootDocPath = path.join(agentsDir, "CLAUDE.md");
1270
+ if (!fs.existsSync(rootDocPath)) {
1271
+ fs.writeFileSync(rootDocPath, ROOT_CLAUDE_MD, "utf-8");
1272
+ logger.success("Created agents/CLAUDE.md");
1273
+ }
488
1274
  const directories = [
489
1275
  { path: "agents/agents", doc: AGENTS_CLAUDE_MD },
490
1276
  { path: "agents/prompts", doc: PROMPTS_CLAUDE_MD },
@@ -507,7 +1293,6 @@ function createDirectoriesAndDocs(cwd) {
507
1293
  }
508
1294
  }
509
1295
  function updateTsConfig(cwd) {
510
- var _a;
511
1296
  const tsconfigPath = path.join(cwd, "tsconfig.json");
512
1297
  if (!fs.existsSync(tsconfigPath)) {
513
1298
  logger.info("No tsconfig.json found, skipping TypeScript configuration");
@@ -517,7 +1302,7 @@ function updateTsConfig(cwd) {
517
1302
  const text = fs.readFileSync(tsconfigPath, "utf-8");
518
1303
  const config = parse(text);
519
1304
  let result = text;
520
- const types = ((_a = config.compilerOptions) == null ? void 0 : _a.types) || [];
1305
+ const types = config.compilerOptions?.types || [];
521
1306
  const newTypes = [
522
1307
  "./worker-configuration.d.ts",
523
1308
  "./.agents/types.d.ts",
@@ -604,6 +1389,7 @@ async function scaffold(options = {}) {
604
1389
  logger.log("Your project structure:");
605
1390
  logger.log("");
606
1391
  logger.log(" agents/");
1392
+ logger.log(" \u251C\u2500\u2500 CLAUDE.md # Architecture documentation");
607
1393
  logger.log(" \u251C\u2500\u2500 Thread.ts # Durable Object for threads");
608
1394
  logger.log(" \u251C\u2500\u2500 AgentBuilder.ts # Durable Object for agent state");
609
1395
  logger.log(" \u251C\u2500\u2500 agents/ # Agent definitions");