Learn/Cookbook/Patterns

Chat with Conversation History

Build a chat agent that remembers previous messages using thread state

Use thread state to maintain conversation history across multiple requests. The thread persists for up to 1 hour, making it ideal for chat sessions.

The Pattern

Thread state stores conversation history automatically. Each browser session gets its own thread, and messages persist across requests.

import { createAgent, type AgentContext } from '@agentuity/runtime';
import { streamText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import { s } from '@agentuity/schema';
 
interface Message {
  role: 'user' | 'assistant';
  content: string;
}
 
const agent = createAgent('Chat Agent', {
  description: 'Conversational agent with memory',
  schema: {
    input: s.object({
      message: s.string(),
    }),
    stream: true,
  },
  handler: async (ctx: AgentContext, input) => {
    // Get or initialize conversation history
    const messages = (ctx.thread.state.get('messages') as Message[]) || [];
 
    // Add user message
    messages.push({ role: 'user', content: input.message });
 
    // Generate streaming response
    const { textStream, text } = streamText({
      model: anthropic('claude-sonnet-4-5'),
      system: 'You are a helpful assistant. Be concise but friendly.',
      messages: messages.map(m => ({
        role: m.role,
        content: m.content,
      })),
    });
 
    // Save assistant response after streaming completes
    ctx.waitUntil(async () => {
      const fullResponse = await text;
      messages.push({ role: 'assistant', content: fullResponse });
      ctx.thread.state.set('messages', messages);
      ctx.logger.info('Conversation updated', {
        messageCount: messages.length,
        threadId: ctx.thread.id,
      });
    });
 
    return textStream;
  },
});
 
export default agent;

Key Points

  • Thread state (ctx.thread.state) persists for up to 1 hour
  • Messages array stores the full conversation history
  • waitUntil saves the response after streaming completes
  • Thread ID (ctx.thread.id) identifies the conversation

Route Example

import { createRouter } from '@agentuity/runtime';
import chatAgent from '@agent/chat';
 
const router = createRouter();
 
router.post('/', async (c) => {
  const { message } = await c.req.json();
  return chatAgent.run({ message });
});
 
// Reset conversation
router.delete('/', async (c) => {
  await c.var.thread.destroy();
  return c.json({ reset: true });
});
 
export default router;

See Also

Need Help?

Join our DiscordCommunity for assistance or just to hang with other humans building agents.

Send us an email at hi@agentuity.com if you'd like to get in touch.

Please Follow us on

If you haven't already, please Signup for your free account now and start building your first agent!