# Patterns & Use Cases

## AI Chat w/Tools

**Server (AIChatAgent):**

```ts
import { AIChatAgent } from 'agents'
import { openai } from '@ai-sdk/openai'
import { tool } from 'ai'
import { z } from 'zod'

export class ChatAgent extends AIChatAgent<Env> {
  async onChatMessage(onFinish) {
    return this.streamText({
      model: openai('gpt-4'),
      messages: this.messages, // Auto-managed
      tools: {
        getWeather: tool({
          description: 'Get current weather',
          parameters: z.object({ city: z.string() }),
          execute: async ({ city }) => `Weather in ${city}: Sunny, 72°F`,
        }),
        searchDocs: tool({
          description: 'Search documentation',
          parameters: z.object({ query: z.string() }),
          execute: async ({ query }) =>
            JSON.stringify(
              this.sql<{
                title
                content
              }>`SELECT title, content FROM docs WHERE content LIKE ${'%' + query + '%'}`
            ),
        }),
      },
      onFinish,
    })
  }
}
```

**Client (React):**

```tsx
import { useAgent } from 'agents/react'
import { useAgentChat } from 'agents/ai-react'

function ChatUI() {
  const agent = useAgent({ agent: 'ChatAgent' })
  const { messages, input, handleInputChange, handleSubmit, isLoading } = useAgentChat({ agent })

  return (
    <div>
      {messages.map((m) => (
        <div key={m.id}>
          {m.role}: {m.content}
        </div>
      ))}
      <form onSubmit={handleSubmit}>
        <input value={input} onChange={handleInputChange} disabled={isLoading} />
        <button disabled={isLoading}>Send</button>
      </form>
    </div>
  )
}
```

## Human-in-the-Loop (Client Tools)

Server defines tool, client executes:

```ts
// Server
export class ChatAgent extends AIChatAgent<Env> {
  async onChatMessage(onFinish) {
    return this.streamText({
      model: openai('gpt-4'),
      messages: this.messages,
      tools: {
        confirmAction: tool({
          description: 'Ask user to confirm',
          parameters: z.object({ action: z.string() }),
          execute: 'client', // Client-side execution
        }),
      },
      onFinish,
    })
  }
}

// Client
const { messages } = useAgentChat({
  agent,
  onToolCall: async (toolCall) => {
    if (toolCall.toolName === 'confirmAction') {
      return { confirmed: window.confirm(`Confirm: ${toolCall.args.action}?`) }
    }
  },
})
```

## Task Queue & Scheduled Processing

```ts
export class TaskAgent extends Agent<Env> {
  onStart() {
    this.schedule('*/5 * * * *', 'processQueue', {}) // Every 5 min
    this.schedule('0 0 * * *', 'dailyCleanup', {}) // Daily
  }

  async onRequest(req: Request) {
    await this.queue('processVideo', { videoId: (await req.json()).videoId })
    return Response.json({ queued: true })
  }

  async processQueue() {
    const tasks = await this.dequeue(10)
    for (const task of tasks) {
      if (task.name === 'processVideo') await this.processVideo(task.data.videoId)
    }
  }

  async dailyCleanup() {
    this.sql`DELETE FROM logs WHERE created_at < ${Date.now() - 86400000}`
  }
}
```

## Manual WebSocket Chat

Custom protocols (non-AI):

```ts
export class ChatAgent extends Agent<Env> {
  async onConnect(conn: Connection, ctx: ConnectionContext) {
    conn.accept()
    conn.setState({ userId: ctx.request.headers.get('X-User-ID') || 'anon' })
    conn.send(JSON.stringify({ type: 'history', messages: this.state.messages }))
  }

  async onMessage(conn: Connection, msg: WSMessage) {
    const newMsg = {
      userId: conn.state.userId,
      text: JSON.parse(msg as string).text,
      timestamp: Date.now(),
    }
    this.setState({ messages: [...this.state.messages, newMsg] })
    this.connections.forEach((c) => c.send(JSON.stringify(newMsg)))
  }
}
```

## Email Processing w/AI

```ts
export class EmailAgent extends Agent<Env> {
  async onEmail(email: AgentEmail) {
    const [text, from, subject] = [
      await email.text(),
      email.from,
      email.headers.get('subject') || '',
    ]
    this.sql`INSERT INTO emails (from_addr, subject, body) VALUES (${from}, ${subject}, ${text})`

    const { text: summary } = await generateText({
      model: openai('gpt-4o-mini'),
      prompt: `Summarize: ${subject}\n\n${text}`,
    })

    this.connections.forEach((c) => c.send(JSON.stringify({ type: 'new_email', from, summary })))
    if (summary.includes('urgent')) await this.schedule(0, 'sendAutoReply', { to: from })
  }
}
```

## Real-time Collaboration

```ts
export class GameAgent extends Agent<Env> {
  initialState = { players: [], gameStarted: false }

  async onConnect(conn: Connection, ctx: ConnectionContext) {
    conn.accept()
    const playerId = ctx.request.headers.get('X-Player-ID') || crypto.randomUUID()
    conn.setState({ playerId })

    const newPlayer = { id: playerId, score: 0 }
    this.setState({ ...this.state, players: [...this.state.players, newPlayer] })
    this.connections.forEach((c) =>
      c.send(JSON.stringify({ type: 'player_joined', player: newPlayer }))
    )
  }

  async onMessage(conn: Connection, msg: WSMessage) {
    const m = JSON.parse(msg as string)

    if (m.type === 'move') {
      this.setState({
        ...this.state,
        players: this.state.players.map((p) =>
          p.id === conn.state.playerId ? { ...p, score: p.score + m.points } : p
        ),
      })
      this.connections.forEach((c) =>
        c.send(JSON.stringify({ type: 'player_moved', playerId: conn.state.playerId }))
      )
    }

    if (m.type === 'start' && this.state.players.length >= 2) {
      this.setState({ ...this.state, gameStarted: true })
      this.connections.forEach((c) => c.send(JSON.stringify({ type: 'game_started' })))
    }
  }
}
```
