@standardagents/cli 0.9.13 → 0.9.16
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 +915 -129
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -28,214 +28,995 @@ var logger = {
|
|
|
28
28
|
console.log(message);
|
|
29
29
|
}
|
|
30
30
|
};
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
434
|
+
tags: ['support', 'tier-1'],
|
|
435
|
+
});
|
|
436
|
+
\`\`\`
|
|
88
437
|
|
|
89
|
-
|
|
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
|
-
|
|
499
|
+
// src/templates/tools.ts
|
|
500
|
+
var TOOLS_CLAUDE_MD = `# Custom Tools
|
|
94
501
|
|
|
95
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
'
|
|
522
|
+
'Search the knowledge base for articles matching a query',
|
|
113
523
|
z.object({
|
|
114
|
-
|
|
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
|
-
|
|
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:
|
|
578
|
+
result: new Date().toISOString(),
|
|
121
579
|
};
|
|
122
580
|
}
|
|
123
581
|
);
|
|
124
582
|
\`\`\`
|
|
125
583
|
|
|
126
|
-
|
|
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
|
-
|
|
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
|
-
|
|
712
|
+
### Filter Messages (Limit Context)
|
|
133
713
|
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
|
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
|
-
|
|
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
|
-
##
|
|
815
|
+
## File-Based Routing
|
|
157
816
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
##
|
|
826
|
+
## Basic Example
|
|
165
827
|
|
|
166
828
|
\`\`\`typescript
|
|
167
|
-
// agents/api/
|
|
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({
|
|
173
|
-
|
|
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
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
851
|
+
## Thread Methods
|
|
183
852
|
|
|
184
|
-
|
|
853
|
+
Access thread data through the instance:
|
|
185
854
|
|
|
186
855
|
\`\`\`typescript
|
|
187
|
-
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
-
|
|
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
|
-
|
|
876
|
+
\`\`\`typescript
|
|
877
|
+
// agents/api/export.post.ts -> POST /api/threads/:id/export
|
|
878
|
+
import { defineThreadEndpoint } from '@standardagents/builder';
|
|
206
879
|
|
|
207
|
-
|
|
880
|
+
export default defineThreadEndpoint(async (thread, request, env) => {
|
|
881
|
+
const body = await request.json();
|
|
882
|
+
const { format = 'json' } = body;
|
|
208
883
|
|
|
209
|
-
|
|
210
|
-
import { definePrompt } from '@standardagents/builder';
|
|
884
|
+
const messages = await thread.getMessages();
|
|
211
885
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
-
|
|
899
|
+
## Nested Routes
|
|
223
900
|
|
|
224
|
-
|
|
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
|
-
|
|
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
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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");
|