| Build AI applications with OpenAI Agents SDK - text agents, voice agents, multi-agent handoffs, tools with Zod schemas, guardrails, and streaming. Prevents 11 documented errors.
npx skills add https://github.com/coco-research/coco --skill openai-agents
Build AI applications with text agents, voice agents (realtime), multi-agent workflows, tools, guardrails, and human-in-the-loop patterns.
npm install @openai/agents zod@4 # v0.4.0+ requires Zod 4 (breaking change)
npm install @openai/agents-realtime # Voice agents
export OPENAI_API_KEY="your-key"
Breaking Change (v0.4.0): Zod 3 no longer supported. Upgrade to zod@4.
Runtimes: Node.js 22+, Deno, Bun, Cloudflare Workers (experimental)
Agents: LLMs with instructions + tools
import { Agent } from '@openai/agents';
const agent = new Agent({ name: 'Assistant', tools: [myTool], model: 'gpt-5-mini' });
Tools: Functions with Zod schemas
import { tool } from '@openai/agents';
import { z } from 'zod';
const weatherTool = tool({
name: 'get_weather',
parameters: z.object({ city: z.string() }),
execute: async ({ city }) => `Weather in ${city}: sunny`,
});
Handoffs: Multi-agent delegation
const triageAgent = Agent.create({ handoffs: [specialist1, specialist2] });
Guardrails: Input/output validation
const agent = new Agent({ inputGuardrails: [detector], outputGuardrails: [filter] });
Structured Outputs: Type-safe responses
const agent = new Agent({ outputType: z.object({ sentiment: z.enum(['positive', 'negative']) }) });
Basic: const result = await run(agent, 'What is 2+2?')
Streaming:
const stream = await run(agent, 'Tell me a story', { stream: true });
for await (const event of stream) {
if (event.type === 'raw_model_stream_event') process.stdout.write(event.data?.choices?.[0]?.delta?.content || '');
}
const billingAgent = new Agent({ name: 'Billing', handoffDescription: 'For billing questions', tools: [refundTool] });
const techAgent = new Agent({ name: 'Technical', handoffDescription: 'For tech issues', tools: [ticketTool] });
const triageAgent = Agent.create({ name: 'Triage', handoffs: [billingAgent, techAgent] });
Agent-as-Tool Context Isolation: When using agent.asTool(), sub-agents do NOT share parent conversation history (intentional design to simplify debugging).
Workaround: Pass context via tool parameters:
const helperTool = tool({
name: 'use_helper',
parameters: z.object({
query: z.string(),
context: z.string().optional(),
}),
execute: async ({ query, context }) => {
return await run(subAgent, `${context}\n\n${query}`);
},
});
Source: Issue #806
Input: Validate before processing
const guardrail: InputGuardrail = {
execute: async ({ input }) => ({ tripwireTriggered: detectHomework(input) })
};
const agent = new Agent({ inputGuardrails: [guardrail] });
Output: Filter responses (PII detection, content safety)
const refundTool = tool({ name: 'process_refund', requiresApproval: true, execute: async ({ amount }) => `Refunded $${amount}` });
let result = await runner.run(input);
while (result.interruption?.type === 'tool_approval') {
result = await promptUser(result.interruption) ? result.state.approve(result.interruption) : result.state.reject(result.interruption);
}
Streaming HITL: When using stream: true with requiresApproval, must explicitly check interruptions:
const stream = await run(agent, input, { stream: true });
let result = await stream.finalResult();
while (result.interruption?.type === 'tool_approval') {
const approved = await promptUser(result.interruption);
result = approved
? await result.state.approve(result.interruption)
: await result.state.reject(result.interruption);
}
Example: human-in-the-loop-stream.ts
Create:
import { RealtimeAgent } from '@openai/agents-realtime';
const voiceAgent = new RealtimeAgent({
voice: 'alloy', // alloy, echo, fable, onyx, nova, shimmer
model: 'gpt-5-realtime',
tools: [weatherTool],
});
Browser Session:
import { RealtimeSession } from '@openai/agents-realtime';
const session = new RealtimeSession(voiceAgent, { apiKey: sessionApiKey, transport: 'webrtc' });
await session.connect();
CRITICAL: Never send OPENAI_API_KEY to browser! Generate ephemeral session tokens server-side.
Voice Handoffs: Voice/model must match across agents (cannot change during handoff)
Limitations:
Templates:
templates/realtime-agents/realtime-agent-basic.tstemplates/realtime-agents/realtime-session-browser.tsxtemplates/realtime-agents/realtime-handoffs.tsReferences:
references/realtime-transports.md - WebRTC vs WebSocketCloudflare Workers (experimental):
export default {
async fetch(request: Request, env: Env) {
// Disable tracing or use startTracingExportLoop()
process.env.OTEL_SDK_DISABLED = 'true';
process.env.OPENAI_API_KEY = env.OPENAI_API_KEY;
const agent = new Agent({ name: 'Assistant', model: 'gpt-5-mini' });
const result = await run(agent, (await request.json()).message);
return Response.json({ response: result.finalOutput, tokens: result.usage.totalTokens });
}
};
Limitations:
OTEL_SDK_DISABLED=true or call startTracingExportLoop() (Issue #16)Next.js: app/api/agent/route.ts → POST handler with run(agent, message)
Templates: cloudflare-workers/, nextjs/
Error: Type errors with tool parameters.
Workaround: Define schemas inline.
// ❌ Can cause type errors
parameters: mySchema
// ✅ Works reliably
parameters: z.object({ field: z.string() })
Note: As of v0.4.1, invalid JSON in tool call arguments is handled gracefully (previously caused SyntaxError crashes). (PR #887)
Source: GitHub #188
Error: "No existing trace found" with MCP servers.
Workaround:
import { initializeTracing } from '@openai/agents/tracing';
await initializeTracing();
Source: GitHub #580
Error: Agent loops infinitely.
Solution: Increase maxTurns or improve instructions:
const result = await run(agent, input, {
maxTurns: 20, // Increase limit
});
// Or improve instructions
instructions: `After using tools, provide a final answer.
Do not loop endlessly.`
Error: Tool execution fails.
Solution: Retry with exponential backoff:
for (let attempt = 1; attempt <= 3; attempt++) {
try {
return await run(agent, input);
} catch (error) {
if (error instanceof ToolCallError && attempt < 3) {
await sleep(1000 * Math.pow(2, attempt - 1));
continue;
}
throw error;
}
}
Error: Output doesn't match outputType.
Solution: Use stronger model or add validation instructions:
const agent = new Agent({
model: 'gpt-5', // More reliable than gpt-5-mini
instructions: 'CRITICAL: Return JSON matching schema exactly',
outputType: mySchema,
});
Error: Unexpected reasoning behavior after upgrading to v0.4.0.
Why It Happens: Default reasoning effort for gpt-5.1/5.2 changed from "low" to "none" in v0.4.0.
Prevention: Explicitly set reasoning effort if you need it.
// v0.4.0+ - default is now "none"
const agent = new Agent({
model: 'gpt-5.1',
reasoning: { effort: 'low' }, // Explicitly set if needed: 'low', 'medium', 'high'
});
Source: Release v0.4.0 | PR #876
Error: response_reasoning field appears in structured output unexpectedly.
Why It Happens: Model endpoint issue (not SDK bug) when using outputType with reasoning models.
Workaround: Filter out response_reasoning from output.
const result = await run(agent, input);
const { response_reasoning, ...cleanOutput } = result.finalOutput;
return cleanOutput;
Source: Issue #844
Status: Model-side issue, coordinating with OpenAI teams
All Errors: See references/common-errors.md
Template: templates/shared/error-handling.ts
LLM-Based: Agent decides routing autonomously (adaptive, higher tokens)
Code-Based: Explicit control flow with conditionals (predictable, lower cost)
Parallel: Promise.all([run(agent1, text), run(agent2, text)]) (concurrent execution)
process.env.DEBUG = '@openai/agents:*'; // Verbose logging
const result = await run(agent, input);
console.log(result.usage.totalTokens, result.history.length, result.currentAgent?.name);
❌ Don't use when:
openai-api skill instead)OPENAI_API_KEY as environment secretmaxTurns to prevent runaway costsgpt-5-mini where possible for cost efficiencyEstimated Savings: ~60%
| Task | Without Skill | With Skill | Savings |
|------|---------------|------------|---------|
| Multi-agent setup | ~12k tokens | ~5k tokens | 58% |
| Voice agent | ~10k tokens | ~4k tokens | 60% |
| Error debugging | ~8k tokens | ~3k tokens | 63% |
| Average | ~10k | ~4k | ~60% |
Errors Prevented: 11 documented issues = 100% error prevention
Text Agents (8):
agent-basic.ts - Simple agent with toolsagent-handoffs.ts - Multi-agent triageagent-structured-output.ts - Zod schemasagent-streaming.ts - Real-time eventsagent-guardrails-input.ts - Input validationagent-guardrails-output.ts - Output filteringagent-human-approval.ts - HITL patternagent-parallel.ts - Concurrent executionRealtime Agents (3):
realtime-agent-basic.ts - Voice setup10. realtime-session-browser.tsx - React client
11. realtime-handoffs.ts - Voice delegation
Framework Integration (4):
12. worker-text-agent.ts - Cloudflare Workers
13. worker-agent-hono.ts - Hono framework
14. api-agent-route.ts - Next.js API
15. api-realtime-route.ts - Next.js voice
Utilities (2):
16. error-handling.ts - Comprehensive errors
17. tracing-setup.ts - Debugging
agent-patterns.md - Orchestration strategiescommon-errors.md - 9 errors with workaroundsrealtime-transports.md - WebRTC vs WebSocketcloudflare-integration.md - Workers limitationsofficial-links.md - Documentation linksVersion: SDK v0.4.1
Last Verified: 2026-01-21
Skill Author: Jeremy Dawes (Jezweb)
Production Tested: Yes
Changes: Added v0.4.0 breaking changes (Zod 4, reasoning defaults), invalid JSON handling (v0.4.1), reasoning output leaks, streaming HITL pattern, agent-as-tool context isolation, video limitations, Cloudflare tracing setup
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.
Build agentic applications with GitHub Copilot SDK. Use when embedding AI agents in apps, creating custom tools, implementing streaming responses, managing sessions, connecting to MCP servers, or creating custom agents. Triggers on Copilot SDK, GitHub SDK, agentic app, embed Copilot, programmable agent, MCP server, custom agent.
Coding Agent Session Search - unified CLI/TUI to index and search local coding agent history from Claude Code, Codex, Gemini, Cursor, Aider, ChatGPT, Pi-Agent, Factory, and more. Purpose-built for AI agent consumption with robot mode.
Destructive Command Guard - High-performance Rust hook for Claude Code that blocks dangerous commands before execution. SIMD-accelerated, modular pack system, whitelist-first architecture. Essential safety layer for agent workflows.
Makepad UI development skills for Rust apps: setup, patterns, shaders, packaging, and troubleshooting.
Secure environment variable management ensuring secrets are never exposed in Claude sessions, terminals, logs, or git commits
Prompt for generating an AGENTS.md file for a repository
Take coco-research/openai-agents from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.
The instructions reference npm.
Without those the skill loads but fails at the first command.