llm-output-schema-cons…
Zod schema constraints that Anthropic rejects or silently ignores when sent as structured-output tool definitions via aiSdk.Output.object(). Use when writing…
Use the Agent class for multi-step tool loops, conversation history, streaming progress, and reusable LLM agents. Use when building agents with skills, structured output, stateful conversations, or streaming callbacks.
$ 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.
/output-dev-agent-classContext preview
The summary Claude sees to decide when to auto-load this skill.
Use the Agent class for multi-step tool loops, conversation history, streaming progress, and reusable LLM agents. Use when building agents with skills, structured output, stateful conversations, or streaming callbacks.
name: output-dev-agent-class description: Use the Agent class for multi-step tool loops, conversation history, streaming progress, and reusable LLM agents. Use when building agents with skills, structured output, stateful conversations, or streaming callbacks. allowed-tools: [Read, Write, Edit]
The `Agent` class uses an internal AI SDK `ToolLoopAgent` through composition with Output prompt files and the skills system. It does not inherit from `ToolLoopAgent`. 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.
import { Agent, aiSdk } from '@outputai/llm';
import type { MessageStore } from '@outputai/llm';
import { z } from '@outputai/core';`Agent` comes from `@outputai/llm`. Use `aiSdk.Output` for structured output. Import `z` from `@outputai/core` (never from `zod` directly). `MessageStore` is the type for a pluggable `getMessages` / `addMessages` store; implement it yourself.
The prompt file is loaded and rendered at construction time. Variables and tools are fixed at construction. Skills and `maxSteps` come from the prompt file. The agent is ready to call `generate()`, `generateWithStreaming()`, or `stream()` immediately.
const agent = new Agent( {
prompt: 'writing_assistant@v1',
variables: {
content_type: input.contentType,
focus: input.focus,
content: input.content
},
output: aiSdk.Output.object( { schema: reviewSchema } )
} );| Option | Type | Default | Description | |--------|------|---------|-------------| | `prompt` | `string` | *(required)* | Prompt file name (e.g. `'writing_assistant@v1'`) | | `promptDir` | `string` | - | Override the stack-resolved prompt directory | | `variables` | `PromptVariables` | - | Template variables rendered at construction | | `tools` | AI SDK tools | - | Caller tools; merged with prompt YAML tools (`load_skill` last) | | `stopWhen` | function or function[] | - | Custom stop condition (overrides prompt `maxSteps` when tools exist) | | `output` | `aiSdk.Output` | - | Structured output spec (e.g. `aiSdk.Output.object({ schema })`) | | `messageStore` | `MessageStore` | - | Pluggable store for multi-turn history |
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 aiSdk.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.
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 message-store history). You can also pass `abortSignal` and `toolChoice`.
Use `generateWithStreaming()` when you need progress callbacks and a complete result:
const result = await agent.generateWithStreaming( {
onChunk( { chunk } ) {
if ( chunk.type === 'text-delta' ) {
process.stdout.write( chunk.text );
}
}
} );The method behaves like `generate()` while using streaming internally. It returns the complete response, rejects on stream errors, and automatically appends messages to the configured message store. It accepts the same `messages`, `abortSignal`, and `toolChoice` as `generate()`, plus `onChunk`. Prefer it over `stream()` in Temporal activity steps unless direct access to the stream result is required.
Use `stream()` when direct control over `textStream` or `stream` is required. It accepts the same `messages`, `abortSignal`, and `toolChoice` as `generate()`, plus `onChunk`, `onEnd`, and `onError`:
const stream = await agent.stream();
for await ( const chunk of stream.textStream ) {
process.stdout.write( chunk );
}Like `streamText`, the stream result provides `textStream` and `stream` iterables, plus promise-based properties (`text`, `usage`, `finishReason`) that resolve on completion.
`stream()` appends messages to the message store in its wrapped `onEnd` when `finishReason` is not `'error'`. See `output-dev-llm-streaming` for streaming and error-handling guidance.
Use `aiSdk.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: aiSdk.Output.object( { schema: reviewSchema } )
} );
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.
By default, Agent is stateless. Each `generate()` call starts fresh with only the initial prompt messages. Pass a
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
Zod schema constraints that Anthropic rejects or silently ignores when sent as structured-output tool definitions via aiSdk.Output.object(). Use when writing…
Guide to the providerOptions structure in .prompt files — decision tree for where an option goes, common mistakes, per-provider quick reference, and Anthropic…
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…
View and edit encrypted credentials in an Output.ai project. Use when adding secrets, updating API keys, verifying credential values, or retrieving a specific…
Wire encrypted credentials to environment variables using the credential: convention. Use when setting up LLM provider keys (ANTHROPIC_API_KEY, OPENAI_API_KEY)…