/output-dev-agent-class
Use the Agent class for multi-step tool loops, conversation history, and reusable LLM agents. Use when building agents with skills, structured output, or stateful conversations.
$ npx -y skills add growthxai/output --skill output-dev-agent-class --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/output-dev-agent-class
Context preview
The summary Claude sees to decide when to auto-load this skill.
Use the Agent class for multi-step tool loops, conversation history, and reusable LLM agents. Use when building agents with skills, structured output, or stateful conversations.
SKILL.md
output-dev-agent-class.SKILL.mdname: output-dev-agent-class
description: Use the Agent class for multi-step tool loops, conversation history, and reusable LLM agents. Use when building agents with skills, structured output, or stateful conversations.
allowed-tools: [Read, Write, Edit]
Using the Agent Class
Overview
The `Agent` class extends AI SDK's `ToolLoopAgent` with Output prompt files and the skills system. Use it when you need multi-step tool execution, conversation history, or a reusable agent instance. For single-shot LLM calls without tools, `generateText` is simpler.
When to Use This Skill
- Building multi-step agents that call tools in a loop
- Using skills (lazy-loaded instructions) with an agent
- Creating agents with structured output via `Output.object()`
- Implementing stateful conversations with `conversationStore`
- Deciding between `Agent` and `generateText`
Import Pattern
import { Agent, createMemoryConversationStore, skill, Output } from '@outputai/llm';
import { z } from '@outputai/core';`Agent`, `createMemoryConversationStore`, `skill`, and `Output` all come from `@outputai/llm`. Import `z` from `@outputai/core` (never from `zod` directly).
Construction
The prompt file is loaded and rendered at construction time. Variables, skills, and tools are fixed at construction. The agent is ready to call `generate()` or `stream()` immediately.
const agent = new Agent( {
prompt: 'writing_assistant@v1',
variables: {
content_type: input.contentType,
focus: input.focus,
content: input.content
},
skills: [ audienceSkill ],
output: Output.object( { schema: reviewSchema } ),
maxSteps: 5
} );Constructor Options
| Option | Type | Default | Description | |--------|------|---------|-------------| | `prompt` | `string` | *(required)* | Prompt file name (e.g. `'writing_assistant@v1'`) | | `variables` | `Record<string, unknown>` | `{}` | Template variables rendered at construction | | `skills` | `Skill[]` | `[]` | Skill packages for the LLM (see `output-dev-skill-file`) | | `tools` | `ToolSet` | `{}` | AI SDK tools available during the loop | | `maxSteps` | `number` | `10` | Maximum tool-loop iterations | | `stopWhen` | `StopCondition` | - | Custom stop condition (overrides `maxSteps`) | | `output` | `Output` | - | Structured output spec (e.g. `Output.object({ schema })`) | | `conversationStore` | `ConversationStore` | - | Pluggable store for multi-turn history | | `temperature` | `number` | - | Override prompt file temperature | | `onStepFinish` | `Function` | - | Callback after each tool-loop step | | `prepareStep` | `Function` | - | Customize each step before execution |
generate()
Run the agent and return when complete:
const result = await agent.generate();
console.log( result.text ); // Generated text
console.log( result.output ); // Structured output (when using Output.object)
console.log( result.usage ); // Token counts
The result has the same shape as `generateText`: `text`, `result` (alias for `text`), `output`, `usage`, `finishReason`, `toolCalls`, etc.
Passing Additional Messages
Extend the conversation with extra messages:
const result = await agent.generate( {
messages: [ { role: 'user', content: 'Focus on the introduction section.' } ]
} );Messages are appended after the initial prompt messages (and any conversation store history).
stream()
Stream the agent's response:
const stream = await agent.stream();
for await ( const chunk of stream.textStream ) {
process.stdout.write( chunk );
}Like `streamText`, the stream result provides `textStream` and `fullStream` iterables, plus promise-based properties (`text`, `usage`, `finishReason`) that resolve on completion.
**Important**: `stream()` does not automatically append messages to the conversation store. If you use streaming with a conversation store, persist messages manually.
Structured Output
Use `Output.object()` to get typed responses:
const reviewSchema = z.object( {
issues: z.array( z.string() ).describe( 'List of issues found' ),
suggestions: z.array( z.string() ).describe( 'Actionable suggestions' ),
score: z.number().describe( 'Quality score 0-100' ),
summary: z.string().describe( 'Brief overall assessment' )
} );
const agent = new Agent( {
prompt: 'writing_assistant@v1',
variables: { content_type: 'documentation', focus: 'clarity', content: markdownContent },
output: Output.object( { schema: reviewSchema } ),
maxSteps: 5
} );
const { output } = await agent.generate();
// output: { issues: string[], suggestions: string[], score: number, summary: string }Use `.describe()` on schema fields instead of `.min()/.max()` for number constraints. Anthropic does not support `minimum`/`maximum` JSON Schema constraints in tool definitions.
Conversation Store
By default, Agent is stateless. Each `generate()` call starts fresh with only the initial prompt messages. Pass a `conversationStore` to maintain history across calls:
import { Agent, createMemoryConversationStore } from '@outputai/llm';
const store = createMemoryConversationStore();
const chatbot = new Agent( {
prompt: 'chatbot@v1',
conversationStore: store
} );
const r1 = await chatbot.generate( {
messages: [ { role: 'user', content: 'Hello, tell me about Output.' } ]
} );
// r1.text: "Output is an AI framework for..."
const r2 = await chatbot.generate( {
messages: [ { role: 'user', content: 'How does it handle retries?' } ]
} );
// r2 sees the full conversation history from r1Custom Store
For production use, implement the `ConversationStore` interface with your database:
interface ConversationStore {
getMessages(): ModelMessage[] | Promise<ModelMessage[]>;
addMessages(messages: ModelMessage[]): void | Promise<void>;
}`createMemoryConversationStore()` is the built-in in-memory implementation.
Using Agent in
Read more
name: output-dev-agent-class description: Use the Agent class for multi-step tool loops, conversation history, and reusable LLM agents. Use when building agents with skills, structured output, or stateful conversations. allowed-tools: [Read, Write, Edit]
Using the Agent Class
Overview
The `Agent` class extends AI SDK's `ToolLoopAgent` with Output prompt files and the skills system. Use it when you need multi-step tool execution, conversation history, or a reusable agent instance. For single-shot LLM calls without tools, `generateText` is simpler.
When to Use This Skill
- Building multi-step agents that call tools in a loop
- Using skills (lazy-loaded instructions) with an agent
- Creating agents with structured output via `Output.object()`
- Implementing stateful conversations with `conversationStore`
- Deciding between `Agent` and `generateText`
Import Pattern
import { Agent, createMemoryConversationStore, skill, Output } from '@outputai/llm';
import { z } from '@outputai/core';`Agent`, `createMemoryConversationStore`, `skill`, and `Output` all come from `@outputai/llm`. Import `z` from `@outputai/core` (never from `zod` directly).
Construction
The prompt file is loaded and rendered at construction time. Variables, skills, and tools are fixed at construction. The agent is ready to call `generate()` or `stream()` immediately.
const agent = new Agent( {
prompt: 'writing_assistant@v1',
variables: {
content_type: input.contentType,
focus: input.focus,
content: input.content
},
skills: [ audienceSkill ],
output: Output.object( { schema: reviewSchema } ),
maxSteps: 5
} );Constructor Options
| Option | Type | Default | Description | |--------|------|---------|-------------| | `prompt` | `string` | *(required)* | Prompt file name (e.g. `'writing_assistant@v1'`) | | `variables` | `Record<string, unknown>` | `{}` | Template variables rendered at construction | | `skills` | `Skill[]` | `[]` | Skill packages for the LLM (see `output-dev-skill-file`) | | `tools` | `ToolSet` | `{}` | AI SDK tools available during the loop | | `maxSteps` | `number` | `10` | Maximum tool-loop iterations | | `stopWhen` | `StopCondition` | - | Custom stop condition (overrides `maxSteps`) | | `output` | `Output` | - | Structured output spec (e.g. `Output.object({ schema })`) | | `conversationStore` | `ConversationStore` | - | Pluggable store for multi-turn history | | `temperature` | `number` | - | Override prompt file temperature | | `onStepFinish` | `Function` | - | Callback after each tool-loop step | | `prepareStep` | `Function` | - | Customize each step before execution |
generate()
Run the agent and return when complete:
const result = await agent.generate(); console.log( result.text ); // Generated text console.log( result.output ); // Structured output (when using Output.object) console.log( result.usage ); // Token counts
The result has the same shape as `generateText`: `text`, `result` (alias for `text`), `output`, `usage`, `finishReason`, `toolCalls`, etc.
Passing Additional Messages
Extend the conversation with extra messages:
const result = await agent.generate( {
messages: [ { role: 'user', content: 'Focus on the introduction section.' } ]
} );Messages are appended after the initial prompt messages (and any conversation store history).
stream()
Stream the agent's response:
const stream = await agent.stream();
for await ( const chunk of stream.textStream ) {
process.stdout.write( chunk );
}Like `streamText`, the stream result provides `textStream` and `fullStream` iterables, plus promise-based properties (`text`, `usage`, `finishReason`) that resolve on completion.
**Important**: `stream()` does not automatically append messages to the conversation store. If you use streaming with a conversation store, persist messages manually.
Structured Output
Use `Output.object()` to get typed responses:
const reviewSchema = z.object( {
issues: z.array( z.string() ).describe( 'List of issues found' ),
suggestions: z.array( z.string() ).describe( 'Actionable suggestions' ),
score: z.number().describe( 'Quality score 0-100' ),
summary: z.string().describe( 'Brief overall assessment' )
} );
const agent = new Agent( {
prompt: 'writing_assistant@v1',
variables: { content_type: 'documentation', focus: 'clarity', content: markdownContent },
output: Output.object( { schema: reviewSchema } ),
maxSteps: 5
} );
const { output } = await agent.generate();
// output: { issues: string[], suggestions: string[], score: number, summary: string }Use `.describe()` on schema fields instead of `.min()/.max()` for number constraints. Anthropic does not support `minimum`/`maximum` JSON Schema constraints in tool definitions.
Conversation Store
By default, Agent is stateless. Each `generate()` call starts fresh with only the initial prompt messages. Pass a `conversationStore` to maintain history across calls:
import { Agent, createMemoryConversationStore } from '@outputai/llm';
const store = createMemoryConversationStore();
const chatbot = new Agent( {
prompt: 'chatbot@v1',
conversationStore: store
} );
const r1 = await chatbot.generate( {
messages: [ { role: 'user', content: 'Hello, tell me about Output.' } ]
} );
// r1.text: "Output is an AI framework for..."
const r2 = await chatbot.generate( {
messages: [ { role: 'user', content: 'How does it handle retries?' } ]
} );
// r2 sees the full conversation history from r1Custom Store
For production use, implement the `ConversationStore` interface with your database:
interface ConversationStore {
getMessages(): ModelMessage[] | Promise<ModelMessage[]>;
addMessages(messages: ModelMessage[]): void | Promise<void>;
}`createMemoryConversationStore()` is the built-in in-memory implementation.
Using Agent in
The open-source TypeScript framework for building AI workflows and agents. Designed for Claude Code — describe what you want, Claude builds it, with all the best practices already in place. One framework.
Repo: growthxai/output
Other skills on output.
- /llm-output-schema-constraints
Zod schema constraints that Anthropic rejects or silently ignores when sent as structured-output tool definitions via Output.object(). Use when writing or reviewing Zod schemas passed to Output.object(), or debugging structured-output validation errors.
Open skill - /prompt-file-provider-options
Guide to the providerOptions structure in .prompt files — decision tree for where an option goes, common mistakes, per-provider quick reference, and Anthropic prompt caching. Use when writing or reviewing .prompt file frontmatter (provider, model, providerOptions,
Open skill - /validate
Run lint, build, and tests to validate changes are correct
Open skill - /output-build-workflow
Implement an Output SDK workflow from a plan document. Use when the user asks to build, implement, or code a workflow from an existing plan, or after output-plan-workflow has produced a plan and the user is ready to build.
Open skill - /output-credentials-edit
View and edit encrypted credentials in an Output.ai project. Use when adding secrets, updating API keys, verifying credential values, or retrieving a specific credential.
Open skill - /output-credentials-env-vars
Wire encrypted credentials to environment variables using the credential: convention. Use when setting up LLM provider keys (ANTHROPIC_API_KEY, OPENAI_API_KEY) or any env var that should come from encrypted credentials.
Open skill

