pikku-ai-agent

$npx mdskill add pikkujs/pikku/pikku-ai-agent

Build autonomous agents that execute Pikku tools with memory and streaming.

  • Enables agents to call Pikku functions as tools for complex tasks.
  • Integrates with LLM models and Pikku function registries.
  • Decides execution flow through configurable instructions and sub-agents.
  • Delivers results via streaming output and persistent conversation memory.

SKILL.md

.github/skills/pikku-ai-agentView on GitHub ↗
---
name: pikku-ai-agent
description: 'Use when building AI agents, chatbots, or LLM-powered assistants with Pikku. Covers pikkuAIAgent, tool registration, memory, streaming, and agent invocation.
TRIGGER when: code uses pikkuAIAgent/runAIAgent/streamAIAgent, user asks about AI agents, chatbots, LLM assistants, tool-calling agents, or agent memory/streaming.
DO NOT TRIGGER when: user asks about MCP tool exposure (use pikku-mcp) or general function definitions (use pikku-concepts).'
---

# Pikku AI Agent Wiring

Build AI agents that use Pikku functions as tools. Agents support conversation memory, streaming, and multi-step tool execution.

## Before You Start

```bash
pikku info functions --verbose   # See existing functions that can be used as agent tools
pikku info tags --verbose        # Understand project organization
```

See `pikku-concepts` for the core mental model.

## API Reference

### `pikkuAIAgent(config)`

```typescript
import { pikkuAIAgent } from '#pikku'

pikkuAIAgent({
  name: string,                  // Unique agent identifier
  description: string,           // What the agent does
  instructions: string | string[],  // System prompt / behavior instructions
  model: string,                 // LLM model (e.g. 'openai/gpt-4o-mini')
  tools?: PikkuFunc[],           // Pikku functions the agent can call
  agents?: AIAgentConfig[],      // Sub-agents this agent can delegate to
  memory?: {
    storage?: string,            // Service name for persistence (e.g. 'aiStorage')
    vector?: string,             // Vector store service name
    embedder?: string,           // Embedding service name
    lastMessages?: number,       // How many messages to retain in context
    workingMemory?: ZodSchema,   // Schema for structured working memory
  },
  maxSteps?: number,             // Max tool-call rounds per invocation
  temperature?: number,          // LLM temperature (0-1)
  toolChoice?: 'auto' | 'required' | 'none',
  input?: ZodSchema,             // Input validation schema
  output?: ZodSchema,            // Output validation schema
  tags?: string[],               // For grouping and middleware targeting
  aiMiddleware?: PikkuAIMiddlewareHooks[],  // AI-specific middleware
  middleware?: PikkuMiddleware[],
  permissions?: PermissionGroup,
})
```

### `runAIAgent(name, input, options)` — Non-streaming

```typescript
const result = await runAIAgent(
  agentName,
  {
    message: string, // User message
    threadId: string, // Conversation thread ID
    resourceId: string, // User/resource identifier
  },
  { singletonServices }
)

result.text // Agent's text response
result.steps // Array of tool calls made
result.usage // Token usage { inputTokens, outputTokens }
```

### `streamAIAgent(name, input, channel, options)` — Streaming

```typescript
await streamAIAgent(
  agentName,
  {
    message: string,
    threadId: string,
    resourceId: string,
  },
  channel,
  { singletonServices }
)

// Channel receives events:
// { type: 'step-start', stepNumber: 1 }
// { type: 'text-delta', text: '...' }
// { type: 'reasoning-delta', text: '...' }
// { type: 'tool-call', toolCallId, toolName, args }
// { type: 'tool-result', toolCallId, toolName, result }
// { type: 'agent-call', agentName, session, input }
// { type: 'agent-result', agentName, session, result }
// { type: 'approval-request', toolCallId, toolName, args, reason? }
// { type: 'usage', tokens: { input, output }, model }
// { type: 'error', message }
// { type: 'done' }
```

## Usage Patterns

### Define an Agent

```typescript
const todoAssistant = pikkuAIAgent({
  name: 'todo-assistant',
  description: 'A helpful assistant that manages todos',
  instructions:
    'You help users manage their todo lists. Be concise and helpful.',
  model: 'openai/gpt-4o-mini',
  tools: [listTodos, createTodo, completeTodo],
  memory: {
    storage: 'aiStorage',
    lastMessages: 20,
  },
  maxSteps: 5,
  temperature: 0.7,
})
```

### Invoke Non-Streaming

```typescript
const result = await runAIAgent(
  'todo-assistant',
  {
    message: 'Create a task for tomorrow: buy groceries',
    threadId: 'thread-123',
    resourceId: 'user-456',
  },
  { singletonServices }
)

console.log(result.text) // "I've created a task 'buy groceries' for tomorrow."
console.log(result.steps) // [{ tool: 'createTodo', args: {...}, result: {...} }]
console.log(result.usage) // { inputTokens: 150, outputTokens: 42 }
```

### Stream Responses

```typescript
await streamAIAgent(
  'todo-assistant',
  {
    message: 'Create a task for tomorrow',
    threadId: 'thread-123',
    resourceId: 'user-456',
  },
  channel,
  { singletonServices }
)
```

## Complete Example

```typescript
// functions/todos.functions.ts
export const listTodos = pikkuSessionlessFunc({
  description: 'List all todo items',
  func: async ({ db }, { status }) => {
    return { todos: await db.listTodos(status) }
  },
})

export const createTodo = pikkuFunc({
  description: 'Create a new todo item',
  func: async ({ db }, { text, priority, dueDate }) => {
    return await db.createTodo({ text, priority, dueDate })
  },
})

export const completeTodo = pikkuFunc({
  description: 'Mark a todo as complete',
  func: async ({ db }, { todoId }) => {
    return await db.completeTodo(todoId)
  },
})

// agents/todo-assistant.ts
const todoAssistant = pikkuAIAgent({
  name: 'todo-assistant',
  description: 'A helpful assistant that manages todos',
  instructions: `You help users manage their todo lists.
    - Be concise and helpful
    - When creating todos, infer priority if not specified
    - When listing todos, summarize the results`,
  model: 'openai/gpt-4o-mini',
  tools: [listTodos, createTodo, completeTodo],
  memory: {
    storage: 'aiStorage',
    lastMessages: 20,
  },
  maxSteps: 5,
  temperature: 0.7,
})

// Wire to HTTP for chat endpoint
wireHTTP({
  method: 'post',
  route: '/chat',
  func: pikkuFunc({
    title: 'Chat',
    func: async (services, { message, threadId }, wire) => {
      const session = await wire.session.get()
      return await runAIAgent(
        'todo-assistant',
        {
          message,
          threadId,
          resourceId: session.userId,
        },
        { singletonServices: services }
      )
    },
  }),
})
```

More from pikkujs/pikku

SkillDescription
pikku-addon'Use when creating or consuming reusable function packages (addons) in Pikku. Covers wireAddon, addon(), pikkuAddonServices, pikkuAddonWireServices, addon package structure, and cross-project function sharing.
pikku-ai-vercel'Use when setting up AI agent execution with the Vercel AI SDK in a Pikku app. Covers VercelAIAgentRunner for streaming and non-streaming AI agent steps.
pikku-ai-voice'Use when adding voice input (speech-to-text) or voice output (text-to-speech) to AI agents in a Pikku app. Covers voiceInput/voiceOutput middleware hooks and STT/TTS service interfaces.
pikku-auth-js'Use when integrating Auth.js (NextAuth) with a Pikku app. Covers createAuthHandler, createAuthRoutes, and Auth.js configuration.
pikku-aws'Use when setting up AWS services (S3, SQS, Secrets Manager) in a Pikku app. Covers S3Content for file storage, SQSQueueService for queues, and AWSSecrets for secret management.
pikku-backblaze'Use when setting up Backblaze B2 file storage in a Pikku app. Covers B2Content for file uploads, downloads, and signed URLs.
pikku-cli'Use when building CLI commands with Pikku. Covers wireCLI, pikkuCLICommand, subcommands, options, parameters, custom renderers, and nested command groups.
pikku-concepts'Foundational guide to Pikku framework concepts. Use this skill when working with any Pikku codebase, starting a new Pikku project, or migrating a backend to Pikku. Covers the core mental model, function types, project structure, code generation, testing, and how Pikku maps to traditional backend patterns.
pikku-config'Use when managing secrets, environment variables, config, or OAuth2 credentials in a Pikku app. Covers wireSecret, wireVariable, wireOAuth2Credential, and typed config access.
pikku-cron'Use when adding scheduled tasks, recurring jobs, or cron-based automation to a Pikku app. Covers wireScheduler, cron expressions, scheduled task wire object, and scheduler middleware.