Skip to main content

Basic Agent

import { agent, serve } from "@reminix/runtime"

const hello = agent("hello", {
  description: "A simple greeting agent",
  handler: async (input) => {
    return `Hello! You said: ${input.prompt}`
  },
})

serve({ agents: [hello] })
When deployed, your agent is available at POST /v1/agents/hello/invoke.

agent() Options

description
string
Agent description. Used in the manifest for discovery and documentation.
type
AgentType
One of: prompt, chat, task, thread, workflow. When set, uses predefined input/output schemas. Defaults to prompt.
inputSchema
ZodType | JSONSchema
Custom input schema (Zod recommended). Overrides the default schema for the selected type. Zod schemas provide typed handlers and automatic runtime validation.
outputSchema
ZodType | JSONSchema
Custom output schema (Zod recommended). Overrides the default schema for the selected type.
stream
boolean
Set to true if the handler is an async generator. Enables streaming responses.
tags
string[]
Tags for filtering and organizing agents.
metadata
Record<string, unknown>
Additional metadata attached to the agent.
handler
function
required
The agent’s handler function. Receives (input, context?) and returns the agent’s output.

Agent Types

Each agent type comes with predefined input/output schemas tailored for its use case.

Prompt Agent

The default type. Accepts a prompt string and returns a string response.
const summarizer = agent("summarizer", {
  type: "prompt",
  description: "Summarize text",
  handler: async (input) => {
    return `Summary of: ${input.prompt}`
  },
})

Chat Agent

Accepts an array of messages and returns a string response.
const supportBot = agent("support-bot", {
  type: "chat",
  description: "Customer support chatbot",
  handler: async (input) => {
    const lastMessage = input.messages[input.messages.length - 1]
    return `I can help with: ${lastMessage.content}`
  },
})

Task Agent

Accepts structured input and returns a structured object.
const analyzer = agent("analyzer", {
  type: "task",
  description: "Analyze sentiment",
  handler: async (input) => {
    return { sentiment: "positive", confidence: 0.95 }
  },
})

Thread Agent

Accepts messages and returns the full message history including the assistant’s response.
const assistant = agent("assistant", {
  type: "thread",
  description: "Returns full message history",
  handler: async (input) => {
    return [...input.messages, { role: "assistant", content: "Response" }]
  },
})

Workflow Agent

Supports multi-step workflows with pause/resume capabilities.
const leadRouter = agent("lead-router", {
  type: "workflow",
  description: "Multi-step lead routing",
  handler: async (input) => {
    if (input.resume) {
      return { status: "completed", steps: [], result: { routed: true } }
    }
    return {
      status: "paused",
      steps: [{ name: "classify", status: "completed" }],
      pendingAction: {
        step: "approval",
        type: "confirmation",
        message: "Route to sales?",
        options: ["approve", "reject"],
      },
    }
  },
})

Context Parameter

The handler receives an optional second argument with execution context, including identity and metadata from the caller.
const myAgent = agent("my-agent", {
  type: "prompt",
  handler: async (input, context?) => {
    const identity = context?.identity
    return `Hello, ${identity || "anonymous"}!`
  },
})

Streaming

Set stream: true and use an async generator to stream responses incrementally.
const streamer = agent("streamer", {
  type: "prompt",
  stream: true,
  handler: async function* (input) {
    yield "Hello "
    yield "world!"
  },
})
Yield strings for text_delta events, or yield StreamEvent objects for finer control over the stream:
import { agent, serve, type StepEvent, type TextDeltaEvent } from "@reminix/runtime"

const workflowStreamer = agent("workflow-streamer", {
  type: "workflow",
  stream: true,
  handler: async function* (input) {
    yield { type: "step", name: "analyze", status: "running" } satisfies StepEvent
    yield { type: "text_delta", delta: "Analyzing..." } satisfies TextDeltaEvent
    yield { type: "step", name: "analyze", status: "completed", output: { result: "done" } } satisfies StepEvent
  },
})
Yielding plain strings is a shorthand for { type: "text_delta", delta: "..." }. Use explicit event objects when you need to emit step events, tool calls, or other event types.

Custom Schemas

Use Zod schemas for full control over input and output validation with typed handlers.
import { z } from "zod"

const calculator = agent("calculator", {
  description: "Calculate two numbers",
  inputSchema: z.object({
    a: z.number().describe("First number"),
    b: z.number().describe("Second number"),
    operation: z.enum(["add", "subtract", "multiply", "divide"]),
  }),
  outputSchema: z.number(),
  handler: async ({ a, b, operation }) => {
    if (operation === "add") return a + b
    if (operation === "subtract") return a - b
    if (operation === "multiply") return a * b
    return a / b
  },
})

Multiple Agents

Register multiple agents with a single serve() call. Each agent gets its own endpoint.
serve({ agents: [hello, supportBot, analyzer] })
Each agent is accessible at POST /agents/{name}/invoke.

serve() Options

agents
Agent[]
List of agents to serve. At least one agent or tool must be provided.
tools
Tool[]
List of tools to serve via MCP. At least one agent or tool must be provided.
port
number
Port number. Defaults to the PORT environment variable or 8080.
hostname
string
Hostname to bind. Defaults to the HOST environment variable or "0.0.0.0".
At least one agent or tool must be provided to serve().

Next steps

Creating Tools

Define MCP tools alongside your agents.

Deploying

Ship your serve() to production.

Configuration & Secrets

Pass API keys and config to your handlers.

Frameworks

Wrap OpenAI, Anthropic, LangChain, or Vercel AI agents.