Local branch had diverged from origin/master (sibling commits on the same base). Rewrote local history linearly on top of origin/master, folding in all local content: exa/hot-platform discovery hunters, leaks ledger, nightly sweep orchestrator, updated .gitignore and skill docs, plus the latest hunt output and vault state. Remote-only files (vault keys, channel scripts) were restored rather than dropped, so the resulting tree is a full union of both sides.
1 line
29 KiB
JSON
1 line
29 KiB
JSON
{"repo": "artsmc/pi-dev-agents", "files": {"skills/mastra-agents/skill.sh": {"8537a1fdcb88a832636f168bfb6849058e5042cc": {"t": 1786068461.933973, "n": 28517, "h": "5071934d23da1d84", "c": "#!/usr/bin/env bash\n################################################################################\n# mastra-agents - Mastra Agent Development Skill\n#\n# Outputs comprehensive guidance for building AI agents with the Mastra framework.\n# Usage: bash skill.sh\n################################################################################\n\ncat << 'SKILL_EOF'\n# Mastra Agent Development Guide\n\n## Table of Contents\n1. Agent Basics\n2. Model Configuration\n3. Tool Integration\n4. Memory Integration\n5. Structured Output\n6. Agent Networks\n7. Processors (Guardrails)\n8. Agent Approval (Human-in-the-Loop)\n9. Voice Integration\n10. Common Patterns\n11. Debugging Tips\n\n---\n\n## 1. Agent Basics\n\nCreate an agent using the `Agent` class from `@mastra/core/agent`:\n\n```typescript\nimport { Agent } from '@mastra/core/agent';\n\nconst myAgent = new Agent({\n id: 'my-agent',\n name: 'My Agent',\n description: 'A helpful assistant that performs specific tasks',\n instructions: 'You are a helpful assistant. Always respond concisely and accurately.',\n model: 'anthropic/claude-sonnet-4-20250514',\n tools: {},\n});\n```\n\n### Constructor Parameters\n\n| Parameter | Type | Required | Description |\n|---------------------|----------------------------------------------|----------|-------------------------------------------------------|\n| `id` | `string` | No | Unique identifier (defaults to `name` if not set) |\n| `name` | `string` | Yes | Human-readable display name |\n| `description` | `string` | No | Description of the agent's purpose |\n| `instructions` | `SystemMessage \\| (ctx) => SystemMessage` | Yes | System prompt - string, array, or dynamic function |\n| `model` | `MastraLanguageModel \\| (ctx) => ...` | Yes | Model string like `'provider/model-name'` |\n| `tools` | `ToolsInput \\| (ctx) => ...` | No | Tools the agent can invoke |\n| `agents` | `Record<string, Agent> \\| (ctx) => ...` | No | Sub-agents (auto-converted to tools) |\n| `workflows` | `Record<string, Workflow> \\| (ctx) => ...` | No | Workflows (auto-converted to tools) |\n| `memory` | `MastraMemory \\| (ctx) => ...` | No | Memory instance for conversation persistence |\n| `inputProcessors` | `Processor[] \\| (ctx) => ...` | No | Input guardrails/processors |\n| `outputProcessors` | `Processor[] \\| (ctx) => ...` | No | Output guardrails/processors |\n| `voice` | `CompositeVoice` | No | Voice settings for TTS/STT |\n| `defaultOptions` | `AgentExecutionOptions \\| (ctx) => ...` | No | Default options for stream()/generate() |\n\n### Model String Format\n\nModels use the `'provider/model-name'` format:\n\n```typescript\nmodel: 'openai/gpt-5.1'\nmodel: 'anthropic/claude-sonnet-4-20250514'\nmodel: 'google/gemini-2.5-flash'\nmodel: 'groq/llama-3.1-70b-versatile'\nmodel: 'mistral/mistral-large-latest'\nmodel: 'deepseek/deepseek-chat'\n```\n\n### Instruction Formats\n\nInstructions support multiple formats:\n\n```typescript\n// String (most common)\ninstructions: 'You are a helpful assistant.'\n\n// Array of strings\ninstructions: ['You are a helpful assistant.', 'Always be polite.']\n\n// System message object\ninstructions: { role: 'system', content: 'You are a helpful assistant.' }\n\n// Array of system messages\ninstructions: [\n { role: 'system', content: 'You are a helpful assistant.' },\n { role: 'system', content: 'You have expertise in TypeScript.' },\n]\n\n// With provider-specific options (caching, reasoning)\ninstructions: {\n role: 'system',\n content: 'You are an expert code reviewer.',\n providerOptions: {\n openai: { reasoningEffort: 'high' },\n anthropic: { cacheControl: { type: 'ephemeral' } },\n },\n}\n```\n\n### Dynamic Instructions\n\nInstructions can be an async function for context-aware prompts:\n\n```typescript\nconst agent = new Agent({\n // ...\n instructions: async ({ requestContext }) => {\n const userTier = requestContext.get('user-tier');\n return `You are a helpful assistant. User tier: ${userTier}.`;\n },\n});\n```\n\n### Dynamic Model Selection\n\n```typescript\nconst agent = new Agent({\n // ...\n model: ({ requestContext }) => {\n const userTier = requestContext.get('user-tier');\n return userTier === 'enterprise' ? 'openai/gpt-5' : 'openai/gpt-4.1-nano';\n },\n});\n```\n\nDocs: https://mastra.ai/docs/agents/overview\n\n---\n\n## 2. Model Configuration\n\n### Supported Providers (600+ models)\n\n| Provider | Example Model String | Notes |\n|--------------|------------------------------------------|-----------------------------|\n| `openai` | `openai/gpt-5.1`, `openai/gpt-4o` | GPT family |\n| `anthropic` | `anthropic/claude-sonnet-4-20250514` | Claude family |\n| `google` | `google/gemini-2.5-flash` | Gemini family |\n| `groq` | `groq/llama-3.1-70b-versatile` | Fast inference |\n| `mistral` | `mistral/mistral-large-latest` | Mistral family |\n| `deepseek` | `deepseek/deepseek-chat` | DeepSeek family |\n| `fireworks` | `fireworks/...` | Open-source models |\n| `together` | `together/meta-llama/Llama-3-70b` | Open-source models |\n| `cohere` | `cohere/command-r-plus` | Cohere family |\n| `perplexity` | `perplexity/llama-3.1-sonar-large` | Search-augmented |\n\n### Model Settings (in generate/stream options)\n\n```typescript\nconst result = await agent.generate('Hello', {\n modelSettings: {\n maxOutputTokens: 4096,\n temperature: 0.7,\n topP: 0.9,\n },\n});\n```\n\nDocs: https://mastra.ai/models\n\n---\n\n## 3. Tool Integration\n\n### Creating Tools\n\n```typescript\nimport { createTool } from '@mastra/core/tools';\nimport { z } from 'zod';\n\nexport const weatherTool = createTool({\n id: 'weather-tool',\n description: 'Fetches weather for a location',\n inputSchema: z.object({\n location: z.string(),\n }),\n outputSchema: z.object({\n weather: z.string(),\n }),\n execute: async (inputData) => {\n const { location } = inputData;\n const response = await fetch(`https://wttr.in/${location}?format=3`);\n const weather = await response.text();\n return { weather };\n },\n});\n```\n\n### Attaching Tools to Agents\n\n```typescript\nimport { Agent } from '@mastra/core/agent';\nimport { weatherTool } from '../tools/weather-tool';\n\nexport const weatherAgent = new Agent({\n id: 'weather-agent',\n name: 'Weather Agent',\n instructions: `You are a helpful weather assistant.\n Use the weatherTool to fetch current weather data.`,\n model: 'openai/gpt-5.1',\n tools: { weatherTool },\n});\n```\n\n### Using Agents as Tools (Sub-agents)\n\nAgents can be added to other agents via `agents`. Mastra auto-converts them to tools named `agent-<key>`:\n\n```typescript\nconst parentAgent = new Agent({\n id: 'parent-agent',\n name: 'Parent Agent',\n instructions: 'You coordinate sub-agents.',\n model: 'openai/gpt-5.1',\n agents: {\n researcher: researchAgent, // becomes tool: agent-researcher\n writer: writingAgent, // becomes tool: agent-writer\n },\n});\n```\n\n### Using Workflows as Tools\n\nWorkflows can be added via `workflows`. Auto-converted to tools named `workflow-<key>`:\n\n```typescript\nconst agent = new Agent({\n id: 'research-agent',\n name: 'Research Agent',\n instructions: 'Use the research workflow to gather information.',\n model: 'openai/gpt-5.1',\n tools: { weatherTool },\n workflows: {\n research: researchWorkflow, // becomes tool: workflow-research\n },\n});\n```\n\n### MCP (Model Context Protocol) Tools\n\n```typescript\nimport { MCPClient } from '@mastra/mcp';\n\nconst mcpClient = new MCPClient({\n servers: {\n filesystem: {\n command: 'npx',\n args: ['-y', '@modelcontextprotocol/server-filesystem', '/tmp'],\n },\n github: {\n url: new URL('https://api.github.com/mcp'),\n requestInit: {\n headers: { Authorization: `Bearer ${process.env.GITHUB_TOKEN}` },\n },\n },\n },\n});\n\nconst agent = new Agent({\n // ...\n tools: {\n ...await mcpClient.getTools(),\n ...localTools,\n },\n});\n```\n\n### Tool Name in Stream Responses\n\nThe `toolName` in stream responses comes from the **object key**, not the tool's `id`:\n\n```typescript\ntools: { weatherTool } // toolName: \"weatherTool\"\ntools: { [weatherTool.id]: weatherTool } // toolName: \"weather-tool\"\ntools: { \"my-custom-name\": weatherTool } // toolName: \"my-custom-name\"\n```\n\nDocs: https://mastra.ai/docs/agents/using-tools\n\n---\n\n## 4. Memory Integration\n\n### Basic Memory Setup\n\n```typescript\nimport { Agent } from '@mastra/core/agent';\nimport { Memory } from '@mastra/memory';\n\nexport const memoryAgent = new Agent({\n id: 'memory-agent',\n name: 'Memory Agent',\n instructions: 'You are a helpful assistant with conversation memory.',\n model: 'anthropic/claude-sonnet-4-20250514',\n memory: new Memory({\n options: {\n lastMessages: 20,\n },\n }),\n});\n```\n\n### Storage Provider (required for persistence)\n\nAdd storage to the Mastra instance (shared across agents):\n\n```typescript\nimport { Mastra } from '@mastra/core';\nimport { LibSQLStore } from '@mastra/libsql';\n\nexport const mastra = new Mastra({\n agents: { memoryAgent },\n storage: new LibSQLStore({\n id: 'mastra-storage',\n url: ':memory:',\n }),\n});\n```\n\nOr add storage directly to an agent's memory:\n\n```typescript\nimport { Memory } from '@mastra/memory';\nimport { LibSQLStore } from '@mastra/libsql';\n\nconst memory = new Memory({\n storage: new LibSQLStore({\n id: 'mastra-storage',\n url: ':memory:',\n }),\n options: {\n lastMessages: 20,\n },\n});\n```\n\n### Using Memory in Conversations\n\nPass `memory` object with `resource` and `thread` to track history:\n\n```typescript\n// Store information\nconst response = await memoryAgent.generate('My favorite color is blue.', {\n memory: {\n resource: 'user-123',\n thread: 'conversation-456',\n },\n});\n\n// Recall information (same resource + thread)\nconst response2 = await memoryAgent.generate(\"What's my favorite color?\", {\n memory: {\n resource: 'user-123',\n thread: 'conversation-456',\n },\n});\n```\n\n### Observational Memory (Long-term)\n\nFor long-running conversations, observational memory compresses old messages:\n\n```typescript\nconst memoryAgent = new Agent({\n id: 'memory-agent',\n name: 'Memory Agent',\n memory: new Memory({\n options: {\n observationalMemory: true, // uses google/gemini-2.5-flash by default\n },\n }),\n});\n\n// Or with custom config:\nconst memoryAgent2 = new Agent({\n id: 'memory-agent-2',\n name: 'Memory Agent 2',\n memory: new Memory({\n options: {\n observationalMemory: {\n model: 'deepseek/deepseek-reasoner',\n observation: {\n messageTokens: 20_000,\n },\n },\n },\n }),\n});\n```\n\n### Memory Packages\n\n| Package | Purpose |\n|-------------------|------------------------------------------|\n| `@mastra/memory` | Core Memory class |\n| `@mastra/libsql` | LibSQL storage provider |\n| `@mastra/pg` | PostgreSQL storage provider |\n\nDocs: https://mastra.ai/docs/agents/agent-memory\n\n---\n\n## 5. Structured Output\n\n### Basic Structured Output (Zod)\n\n```typescript\nimport { z } from 'zod';\n\nconst response = await agent.generate('Help me plan my day.', {\n structuredOutput: {\n schema: z.array(\n z.object({\n name: z.string(),\n activities: z.array(z.string()),\n }),\n ),\n },\n});\n\nconsole.log(response.object);\n// [{ name: \"Morning Routine\", activities: [\"Wake up\", \"Exercise\"] }, ...]\n```\n\n### Structured Output with JSON Schema\n\n```typescript\nconst response = await agent.generate('Help me plan my day.', {\n structuredOutput: {\n schema: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n name: { type: 'string' },\n activities: { type: 'array', items: { type: 'string' } },\n },\n required: ['name', 'activities'],\n },\n },\n },\n});\n```\n\n### Separate Structuring Model\n\nWhen the main agent isn't good at structured output, use a second model:\n\n```typescript\nconst response = await agent.generate('Analyze TypeScript.', {\n structuredOutput: {\n schema: z.object({\n overview: z.string(),\n strengths: z.array(z.string()),\n weaknesses: z.array(z.string()),\n }),\n model: 'openai/gpt-4o', // Second model for structuring\n },\n});\n```\n\n### Structured Output with Streaming\n\n```typescript\nconst stream = await agent.stream('Help me plan my day.', {\n structuredOutput: {\n schema: z.array(\n z.object({\n name: z.string(),\n activities: z.array(z.string()),\n }),\n ),\n },\n});\n\nfor await (const chunk of stream.fullStream) {\n if (chunk.type === 'object-result') {\n console.log(chunk);\n }\n}\n\nconsole.log(await stream.object);\n```\n\n### Error Handling for Structured Output\n\n```typescript\nconst response = await agent.generate('Tell me about TypeScript.', {\n structuredOutput: {\n schema: z.object({\n summary: z.string(),\n keyFeatures: z.array(z.string()),\n }),\n errorStrategy: 'fallback', // 'strict' | 'warn' | 'fallback'\n fallbackValue: {\n summary: 'TypeScript is a typed superset of JavaScript',\n keyFeatures: ['Static typing', 'Compiles to JavaScript'],\n },\n },\n});\n```\n\n### JSON Prompt Injection (for models without response_format)\n\n```typescript\nconst response = await agent.generate('Help me plan my day.', {\n structuredOutput: {\n schema: mySchema,\n jsonPromptInjection: true, // Required for Gemini 2.5 with tools\n },\n});\n```\n\nDocs: https://mastra.ai/docs/agents/structured-output\n\n---\n\n## 6. Agent Networks\n\nAgent networks coordinate multiple agents, workflows, and tools. A routing agent uses LLM reasoning to decide which primitives to call.\n\n### Creating a Network\n\n```typescript\nimport { Agent } from '@mastra/core/agent';\nimport { Memory } from '@mastra/memory';\nimport { LibSQLStore } from '@mastra/libsql';\n\nconst researchAgent = new Agent({\n id: 'research-agent',\n name: 'Research Agent',\n description: 'Gathers research insights in bullet-point form.',\n instructions: 'You research topics thoroughly.',\n model: 'anthropic/claude-sonnet-4-20250514',\n tools: { searchTool },\n});\n\nconst writingAgent = new Agent({\n id: 'writing-agent',\n name: 'Writing Agent',\n description: 'Turns research into well-structured written content.',\n instructions: 'You write clear, engaging content based on research.',\n model: 'anthropic/claude-sonnet-4-20250514',\n});\n\n// The routing agent delegates to sub-agents, workflows, and tools\nconst routingAgent = new Agent({\n id: 'routing-agent',\n name: 'Routing Agent',\n instructions: `You are a network of writers and researchers.\n Always respond with a complete report in full paragraphs.`,\n model: 'openai/gpt-5.1',\n agents: {\n researchAgent,\n writingAgent,\n },\n workflows: {\n cityWorkflow,\n },\n tools: {\n weatherTool,\n },\n memory: new Memory({\n storage: new LibSQLStore({\n id: 'mastra-storage',\n url: 'file:../mastra.db',\n }),\n }),\n});\n```\n\n### Calling Agent Networks\n\nUse `.network()` to trigger the routing agent:\n\n```typescript\nconst result = await routingAgent.network('Tell me three cool ways to use Mastra');\n\nfor await (const chunk of result) {\n console.log(chunk.type);\n if (chunk.type === 'network-execution-event-step-finish') {\n console.log(chunk.payload.result);\n }\n}\n```\n\n### Network with Structured Output\n\n```typescript\nimport { z } from 'zod';\n\nconst stream = await routingAgent.network('Research AI trends', {\n structuredOutput: {\n schema: z.object({\n summary: z.string(),\n recommendations: z.array(z.string()),\n confidence: z.number().min(0).max(1),\n }),\n },\n});\n\nfor await (const chunk of stream) {\n if (chunk.type === 'network-object-result') {\n console.log('Final:', chunk.payload.object);\n }\n}\n\nconst result = await stream.object;\n```\n\n### Key Network Principles\n\n- **Memory is required** for `.network()` - stores task history and determines completion\n- **Descriptions matter** - primitives are selected based on their descriptions\n- **Schema helps routing** - `inputSchema`/`outputSchema` guide the routing agent\n\nDocs: https://mastra.ai/docs/agents/networks\n\n---\n\n## 7. Processors (Guardrails)\n\nProcessors apply guardrails to agent inputs and outputs. They run before or after each interaction to review, transform, or block information.\n\n### Input Processors\n\nApplied **before** messages reach the LLM:\n\n```typescript\nimport { Agent } from '@mastra/core/agent';\nimport { ModerationProcessor, PromptInjectionDetector, PIIDetector, UnicodeNormalizer } from '@mastra/core/processors';\n\nconst secureAgent = new Agent({\n id: 'secure-agent',\n name: 'Secure Agent',\n instructions: 'You are a helpful assistant.',\n model: 'openai/gpt-5.1',\n inputProcessors: [\n new UnicodeNormalizer({\n stripControlChars: true,\n collapseWhitespace: true,\n }),\n new PromptInjectionDetector({\n model: 'openrouter/openai/gpt-oss-safeguard-20b',\n threshold: 0.8,\n strategy: 'rewrite',\n detectionTypes: ['injection', 'jailbreak', 'system-override'],\n }),\n new PIIDetector({\n model: 'openrouter/openai/gpt-oss-safeguard-20b',\n threshold: 0.6,\n strategy: 'redact',\n redactionMethod: 'mask',\n detectionTypes: ['email', 'phone', 'credit-card'],\n }),\n new ModerationProcessor({\n model: 'openrouter/openai/gpt-oss-safeguard-20b',\n categories: ['hate', 'harassment', 'violence'],\n threshold: 0.7,\n strategy: 'block',\n }),\n ],\n});\n```\n\n### Output Processors\n\nApplied **after** the LLM generates a response:\n\n```typescript\nimport { BatchPartsProcessor, TokenLimiterProcessor, SystemPromptScrubber } from '@mastra/core/processors';\n\nconst agent = new Agent({\n // ...\n outputProcessors: [\n new BatchPartsProcessor({\n batchSize: 5,\n maxWaitTime: 100,\n emitOnNonText: true,\n }),\n new TokenLimiterProcessor({\n limit: 1000,\n strategy: 'truncate',\n countMode: 'cumulative',\n }),\n new SystemPromptScrubber({\n model: 'openrouter/openai/gpt-oss-safeguard-20b',\n strategy: 'redact',\n redactionMethod: 'placeholder',\n placeholderText: '[REDACTED]',\n }),\n ],\n});\n```\n\n### Available Processors\n\n**Input Processors:**\n| Processor | Description |\n|---------------------------|--------------------------------------------------|\n| `UnicodeNormalizer` | Cleans and normalizes Unicode, whitespace |\n| `PromptInjectionDetector` | Detects injection, jailbreak, system overrides |\n| `LanguageDetector` | Detects and translates user messages |\n\n**Output Processors:**\n| Processor | Description |\n|---------------------------|--------------------------------------------------|\n| `BatchPartsProcessor` | Batches streamed output for network efficiency |\n| `TokenLimiterProcessor` | Limits tokens in responses |\n| `SystemPromptScrubber` | Redacts system prompts from responses |\n\n**Hybrid Processors (input or output):**\n| Processor | Description |\n|---------------------------|--------------------------------------------------|\n| `ModerationProcessor` | Detects inappropriate/harmful content |\n| `PIIDetector` | Detects and redacts PII (emails, phones, etc.) |\n\n### Processor Strategies\n\nMost processors support `strategy`: `block`, `warn`, `detect`, or `redact`.\n\nWhen `block` is used, the processor calls `abort()` and stops the request:\n\n```typescript\n// Handling blocked requests\nconst result = await agent.generate('...');\n\nif (result.tripwire) {\n console.error('Blocked:', result.tripwire.reason);\n console.error('Processor:', result.tripwire.processorId);\n}\n```\n\n### Custom Processors\n\nCreate custom processors by extending the `Processor` class:\n\n```typescript\nexport class QualityChecker implements Processor {\n id = 'quality-checker';\n\n async processOutputStep({ text, abort, retryCount }) {\n const score = await evaluateQuality(text);\n\n if (score < 0.7 && retryCount < 3) {\n abort('Response quality too low.', { retry: true, metadata: { score } });\n }\n\n return [];\n }\n}\n```\n\nDocs: https://mastra.ai/docs/agents/guardrails\n\n---\n\n## 8. Agent Approval (Human-in-the-Loop)\n\nAgent approval enables human oversight of tool calls. Use `requireToolApproval` option:\n\n```typescript\nconst result = await agent.generate('Delete the old records', {\n requireToolApproval: true,\n maxSteps: 10,\n});\n\nif (result.finishReason === 'suspended') {\n console.log('Tool needs approval:', result.suspendPayload);\n // { toolCallId, toolName, args }\n\n // Approve:\n const approved = await agent.approveToolCallGenerate({\n runId: result.runId,\n toolCallId: result.suspendPayload.toolCallId,\n memory: { resource: 'user-123', thread: 'thread-456' },\n });\n\n // Or decline:\n const declined = await agent.declineToolCallGenerate({\n runId: result.runId,\n toolCallId: result.suspendPayload.toolCallId,\n memory: { resource: 'user-123', thread: 'thread-456' },\n });\n}\n```\n\nDocs: https://mastra.ai/docs/agents/agent-approval\n\n---\n\n## 9. Voice Integration\n\nMastra agents can integrate with TTS and STT providers.\n\n### Adding Voice to an Agent\n\n```typescript\nimport { Agent } from '@mastra/core/agent';\nimport { OpenAIVoice } from '@mastra/voice-openai';\n\nconst voice = new OpenAIVoice({\n apiKey: process.env.OPENAI_API_KEY,\n speaker: 'alloy', // alloy, echo, fable, onyx, nova, shimmer\n});\n\nconst agent = new Agent({\n id: 'voice-agent',\n name: 'Voice Agent',\n instructions: 'You are a voice-enabled assistant.',\n model: 'openai/gpt-4o',\n voice,\n});\n\n// Text-to-Speech\nconst audioStream = await agent.voice.speak('Hello!');\n\n// Speech-to-Text\nconst transcript = await agent.voice.listen(audioBuffer);\n```\n\n### Supported Voice Providers\n\n| Provider | Package | Features |\n|--------------|----------------------------|-----------|\n| OpenAI | `@mastra/voice-openai` | TTS + STT |\n| ElevenLabs | `@mastra/voice-elevenlabs` | TTS |\n| Google Cloud | `@mastra/voice-google` | TTS + STT |\n| Deepgram | `@mastra/voice-deepgram` | STT |\n| PlayHT | `@mastra/voice-playht` | TTS |\n\nDocs: https://mastra.ai/docs/agents/adding-voice\n\n---\n\n## 10. Common Patterns\n\n### Registering Agents with Mastra\n\n```typescript\nimport { Mastra } from '@mastra/core';\nimport { testAgent } from './agents/test-agent';\n\nexport const mastra = new Mastra({\n agents: { testAgent },\n});\n\n// Later, reference the agent\nconst agent = mastra.getAgent('testAgent');\n```\n\n### Multi-Tool Agent\n\n```typescript\nconst agent = new Agent({\n id: 'multi-tool-agent',\n name: 'Swiss Army Agent',\n instructions: `You have multiple tools. Choose the right one for each task.`,\n model: 'anthropic/claude-sonnet-4-20250514',\n tools: { searchTool, calculatorTool, emailTool },\n});\n\nconst result = await agent.generate('Find the population of Tokyo and calculate 15% of it', {\n maxSteps: 10,\n});\n```\n\n### Agent with Memory and Observational Memory\n\n```typescript\nconst agent = new Agent({\n id: 'memory-agent',\n name: 'Memory Agent',\n instructions: 'You remember past conversations.',\n model: 'anthropic/claude-sonnet-4-20250514',\n memory: new Memory({\n options: {\n lastMessages: 20,\n observationalMemory: true,\n },\n }),\n});\n\n// Conversations are automatically persisted\nawait agent.generate('My favorite color is blue', {\n memory: { resource: 'user-1', thread: 'thread-1' },\n});\n\n// Later, the agent can recall this\nawait agent.generate('What is my favorite color?', {\n memory: { resource: 'user-1', thread: 'thread-1' },\n});\n```\n\n### Streaming Agent Responses\n\n```typescript\nconst stream = await agent.stream('Tell me about quantum computing');\n\nfor await (const chunk of stream.textStream) {\n process.stdout.write(chunk);\n}\n\n// Or with full event stream\nfor await (const event of stream.fullStream) {\n switch (event.type) {\n case 'text-delta':\n process.stdout.write(event.textDelta);\n break;\n case 'tool-call':\n console.log('Tool called:', event.toolName);\n break;\n case 'tool-result':\n console.log('Tool result:', event.result);\n break;\n }\n}\n```\n\n### Multi-step with onStepFinish\n\n```typescript\nconst response = await agent.generate('Help me organize my day', {\n maxSteps: 10,\n onStepFinish: ({ text, toolCalls, toolResults, finishReason, usage }) => {\n console.log({ text, toolCalls, toolResults, finishReason, usage });\n },\n});\n```\n\n### Streaming with onFinish\n\n```typescript\nconst stream = await agent.stream('Help me organize my day', {\n onFinish: ({ steps, text, finishReason, usage }) => {\n console.log({ steps, text, finishReason, usage });\n },\n});\n\nfor await (const chunk of stream.textStream) {\n process.stdout.write(chunk);\n}\n```\n\n### Multi-step with prepareStep\n\nControl tools and output per step:\n\n```typescript\nconst result = await agent.stream('weather in vancouver?', {\n prepareStep: async ({ stepNumber }) => {\n if (stepNumber === 0) {\n return {\n model: 'anthropic/claude-sonnet-4-20250514',\n tools: { weatherTool },\n toolChoice: 'required',\n };\n }\n return {\n model: 'anthropic/claude-sonnet-4-20250514',\n tools: undefined,\n structuredOutput: {\n schema: z.object({\n temperature: z.number(),\n humidity: z.number(),\n }),\n },\n };\n },\n});\n```\n\n---\n\n## 11. Debugging Tips\n\n### Common Issues and Solutions\n\n1. **Model provider credential errors**\n - Mastra auto-detects env vars: `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, etc.\n - Verify: `console.log(process.env.MY_KEY?.slice(0, 8))`\n\n2. **Tool schema validation errors**\n - Ensure `inputSchema` and `outputSchema` use valid Zod schemas\n - Use `.describe()` on schema fields for better LLM understanding\n - Test parsing: `mySchema.parse(testData)`\n\n3. **Agent not using tools**\n - Make tool descriptions clear and specific\n - Mention tools in agent instructions\n - Verify tools are passed as an object (not array)\n - Increase `maxSteps` (default is 1)\n\n4. **Memory not persisting**\n - Ensure `resource` and `thread` are consistent across calls\n - Verify storage provider is configured (on Mastra or Memory instance)\n\n5. **Structured output parsing failures**\n - Try `errorStrategy: 'fallback'` with a `fallbackValue`\n - Use `jsonPromptInjection: true` for models without `response_format`\n - Try a separate structuring `model`\n\n6. **Enable tracing for debugging**\n ```typescript\n const mastra = new Mastra({\n agents: { myAgent },\n logger: { level: 'debug' },\n });\n ```\n\n7. **Test agents with Studio**\n - Use Mastra Studio to chat with agents, inspect tool calls, view traces\n - Default endpoint: `http://localhost:4111/api/agents/myAgent/generate`\n\n---\n\n## Quick Reference Links\n\n- Agents Overview: https://mastra.ai/docs/agents/overview\n- Using Tools: https://mastra.ai/docs/agents/using-tools\n- Agent Memory: https://mastra.ai/docs/agents/agent-memory\n- Structured Output: https://mastra.ai/docs/agents/structured-output\n- Agent Networks: https://mastra.ai/docs/agents/networks\n- Guardrails/Processors: https://mastra.ai/docs/agents/guardrails\n- Voice: https://mastra.ai/docs/agents/adding-voice\n- Agent Approval: https://mastra.ai/docs/agents/agent-approval\n- Model Providers: https://mastra.ai/models\n- MCP Integration: https://mastra.ai/docs/mcp/overview\n- API Reference - Agent: https://mastra.ai/reference/agents/agent\n- API Reference - generate(): https://mastra.ai/reference/agents/generate\n\nSKILL_EOF\n"}}}} |