Skip to main content

Agents

What is an Agent?

An Agent in RadarOS is a conversational AI unit that processes user input, optionally calls tools, and produces text or structured output. Agents are model-agnostic—you plug in any supported LLM provider (OpenAI, Anthropic, Google Gemini, Ollama)—and can be extended with tools, memory, sessions, and guardrails.

Synchronous

Use run(input, opts?) for a single request-response cycle.

Streaming

Use stream(input, opts?) for token-by-token or chunk-by-chunk output.

AgentConfig

Configure an agent by passing an AgentConfig object to the Agent constructor. All properties except name and model are optional.
name
string
required
Display name for the agent. Used in logs and events.
model
ModelProvider
required
The LLM provider instance. Use factory functions like openai("gpt-4o"), anthropic("claude-sonnet-4-20250514"), or google("gemini-2.0-flash").
instructions
string | (ctx) => string
System instructions for the agent. Can be a static string or a function that receives RunContext and returns a string (useful for dynamic prompts).
tools
ToolDef[]
Array of tool definitions created with defineTool(). Enables function calling.
memory
Memory
Memory instance for short-term and optional long-term conversation context. See Memory.
storage
StorageDriver
Storage driver for sessions. Defaults to InMemoryStorage if not provided.
sessionId
string
Default session ID for this agent. Can be overridden per-run via RunOpts.
userId
string
Default user ID for this agent. Can be overridden per-run via RunOpts.
addHistoryToMessages
boolean
default:"true"
Whether to include session history in the messages sent to the LLM. Set to false to disable.
numHistoryRuns
number
Limits how many prior turns to include. Each turn = 2 messages (user + assistant). If unset, defaults to 20 messages.
maxToolRoundtrips
number
default:"10"
Maximum number of tool-call rounds before stopping. Prevents infinite loops.
temperature
number
Sampling temperature passed to the model (0–2 typically). Lower = more deterministic.
structuredOutput
ZodSchema
Zod schema to enforce structured JSON output. See Structured Output.
hooks
AgentHooks
Lifecycle hooks: beforeRun, afterRun, onToolCall, onError. See Hooks & Guardrails.
guardrails
{ input, output }
Input and output guardrails. Each is an array of validators. See Hooks & Guardrails.
eventBus
EventBus
Custom event bus for emitting agent events. Defaults to a new EventBus if not provided.
reasoning
ReasoningConfig
Enable extended thinking / chain-of-thought reasoning. See Reasoning.
userMemory
UserMemory
User memory instance for cross-session personalization. See User Memory.
logLevel
string
default:"silent"
Logging level: "debug" | "info" | "warn" | "error" | "silent". Default is "silent".
retry
Partial<RetryConfig>
Retry configuration for transient LLM API failures (429, 5xx, network errors). Default: 3 retries with exponential backoff.
maxTokens
number
Maximum output tokens per LLM call. Note: when reasoning is enabled, the Anthropic provider may override this to ensure enough room for both thinking and response tokens.
maxContextTokens
number
Maximum context window tokens. When set, conversation history is automatically trimmed (oldest messages first) to fit within this limit.
toolResultLimit
ToolResultLimitConfig
Limit large tool results before they’re sent back to the LLM. Prevents prompt token explosion when tools return massive payloads (e.g. MCP servers returning 200KB of data). See Tool Result Limits.

Methods

run(input, opts?) → RunOutput

Processes user input and returns the full response.
const result = await agent.run("What is the capital of France?");
console.log(result.text);
console.log(result.usage.totalTokens);

stream(input, opts?) → AsyncGenerator<StreamChunk>

Streams the response as chunks. Use for real-time UIs or SSE.
for await (const chunk of agent.stream("Tell me a short story.")) {
  if (chunk.type === "text") process.stdout.write(chunk.text);
  if (chunk.type === "finish") console.log("\nDone.", chunk.usage);
}

RunOpts

Options passed to run() or stream():
PropertyTypeDescription
sessionIdstringSession ID for multi-turn conversations. Auto-generated if omitted.
userIdstringUser identifier.
metadataRecord<string, unknown>Arbitrary metadata available in RunContext.
apiKeystringPer-request API key override for the model provider.

RunOutput

The object returned by run():
PropertyTypeDescription
textstringThe assistant’s text response.
toolCallsToolCallResult[]Results from any tool calls made during the run.
usageTokenUsageToken usage breakdown (see below).
structuredunknownParsed structured output when structuredOutput schema is set.
thinkingstringModel’s internal reasoning content (when reasoning is enabled).
durationMsnumberWall-clock duration of the run in milliseconds.
timeToFirstTokenMsnumberTime from request start to the first token received from the model.
runIdstringUnique identifier for this run (UUID).
agentNamestringName of the agent that produced this output.
sessionIdstringSession ID used for this run.
userIdstringUser ID used for this run.
modelstringModel identifier (e.g., "gpt-4o", "gemini-2.5-flash").
modelProviderstringProvider identifier (e.g., "openai", "vertex", "anthropic").
statusstringRun status: "completed" or "error".
createdAtnumberUnix timestamp (ms) when the run started.
responseIdstringProvider-specific response ID (e.g., OpenAI’s chatcmpl-*).
messagesChatMessage[]Full message history sent to the model (system + history + user).
metricsRunMetricsAggregated metrics for the run (see below).

TokenUsage

interface TokenUsage {
  promptTokens: number;
  completionTokens: number;
  totalTokens: number;
  reasoningTokens?: number;
  cachedTokens?: number;
  audioInputTokens?: number;
  audioOutputTokens?: number;
  providerMetrics?: Record<string, unknown>;
}
The providerMetrics field contains the raw, unmodified usage object returned by the underlying provider API. This gives you full transparency into provider-specific metrics (e.g., thoughtsTokenCount from Gemini, prompt_tokens_details from OpenAI, cache_read_input_tokens from Anthropic).

RunMetrics

interface RunMetrics {
  inputTokens: number;
  outputTokens: number;
  totalTokens: number;
  timeToFirstTokenMs?: number;
  durationMs?: number;
}

Basic Example

import { Agent, openai } from "@radaros/core";

const agent = new Agent({
  name: "Assistant",
  model: openai("gpt-4o"),
  instructions: "You are a helpful assistant. Be concise.",
});

const result = await agent.run("What is the capital of France?");
console.log(result.text);

A comprehensive example showing all major config options together:
import {
  Agent, anthropic, CostTracker, MongoDBStorage,
  defineTool, type ContentPart,
} from "@radaros/core";
import { z } from "zod";

const storage = new MongoDBStorage("mongodb://localhost:27017/myapp");
const costTracker = new CostTracker({
  budget: { maxCostPerSession: 2.0, onBudgetExceeded: "warn" },
});

const searchTool = defineTool({
  name: "searchDatabase",
  description: "Search the product database by query",
  parameters: z.object({
    query: z.string().describe("Search query"),
    limit: z.number().optional().describe("Max results (default 10)"),
  }),
  execute: async ({ query, limit }) => {
    const results = await db.search(query, limit ?? 10);
    return JSON.stringify(results);
  },
});

const agent = new Agent({
  name: "product-assistant",
  model: anthropic("claude-sonnet-4-6"),
  instructions: "You help customers find products. Be concise and helpful.",
  tools: [searchTool],

  // Memory: persistent sessions + summaries + user personalization
  memory: {
    storage,
    maxMessages: 20,
    summaries: true,
    userFacts: true,
    userProfile: true,
    model: anthropic("claude-haiku-4-5-20251001"),
  },

  // Tool routing: only send relevant tools to the model
  toolRouter: {
    model: anthropic("claude-haiku-4-5-20251001"),
    maxTools: 5,
  },

  // Tool result limits: summarize large API responses
  toolResultLimit: {
    maxChars: 20000,
    strategy: "summarize",
    model: anthropic("claude-haiku-4-5-20251001"),
  },

  // Extended thinking for complex queries
  reasoning: { enabled: true, budgetTokens: 2000 },

  // Cost tracking with session budget
  costTracker,

  // Lifecycle hooks
  hooks: {
    beforeRun: async (ctx) => {
      console.log(`[${ctx.runId}] Starting run for session ${ctx.sessionId}`);
    },
    afterRun: async (ctx, output) => {
      console.log(`[${ctx.runId}] Completed in ${output.durationMs}ms, ${output.usage.totalTokens} tokens`);
    },
    onError: async (ctx, error) => {
      console.error(`[${ctx.runId}] Error: ${error.message}`);
    },
  },

  logLevel: "info",
  maxToolRoundtrips: 3,
});

const result = await agent.run("Find me wireless headphones under $100", {
  sessionId: "session-abc",
  userId: "user-42",
});

console.log(result.text);
console.log(`Cost: $${costTracker.getSummary().totalCost.toFixed(4)}`);

Dynamic Instructions

Instructions can be a function that receives RunContext, allowing you to customize the system prompt per request:
const agent = new Agent({
  name: "support-bot",
  model: openai("gpt-4o"),
  instructions: (ctx) => {
    const role = ctx.metadata?.role ?? "customer";
    const lang = ctx.metadata?.language ?? "English";
    return `You are a support agent. The user's role is "${role}".
Respond in ${lang}. Be concise and professional.
Session state: ${JSON.stringify(ctx.sessionState)}`;
  },
});

// The instructions function is called fresh on every run
const result = await agent.run("How do I reset my password?", {
  metadata: { role: "admin", language: "Spanish" },
});
This is useful for multi-tenant apps, internationalization, or injecting session-specific context into the prompt.