Cogitator
Core

Agents

Configure and run AI agents with tools, instructions, and model parameters.

Overview

An Agent is a configured LLM instance with a name, system prompt, tools, and model parameters. Agents are stateless blueprints -- the Cogitator runtime handles execution, memory, and lifecycle.

import { Agent } from '@cogitator-ai/core';

const agent = new Agent({
  name: 'researcher',
  model: 'anthropic/claude-sonnet-5-5',
  instructions: 'You are a research assistant. Find accurate information and cite sources.',
});

AgentConfig

interface AgentConfig {
  id?: string;
  name: string;
  description?: string;
  model?: string; // default: llm.defaultModel of the Cogitator
  provider?: string;
  instructions: string;
  tools?: Tool[];
  skills?: Skill[]; // bundles of tools + instructions, merged in
  temperature?: number; // default: 0.7
  topP?: number;
  maxTokens?: number;
  stopSequences?: string[];
  responseFormat?: ResponseFormat;
  reasoning?: ReasoningConfig; // effort and summaries for reasoning models
  handoffs?: Array<Agent | Handoff>; // agents it can hand the conversation over to
  maxIterations?: number; // default: 10
  onIterationLimit?: 'answer' | 'stop'; // default: 'answer'
  timeout?: number; // default: limits.defaultTimeout of the Cogitator, else 120000 (2 min)
}

Model String Format

The model field uses a provider/model-name convention to route to the correct backend:

model: 'openai/gpt-6.1-sol';
model: 'anthropic/claude-sonnet-5-5';
model: 'ollama/llama3.3:latest';
model: 'google/gemini-3.8-flash';

If you omit the provider prefix (or the prefix is not a known provider, backend or plugin), the whole string goes to the defaultProvider from your runtime config, Ollama when unset. If you omit model altogether, the agent runs on llm.defaultModel; a run fails with a CONFIGURATION_ERROR when neither is set. cog.resolveModel(agent) returns the model a run would use.

Provider Override

For OpenAI-compatible APIs (OpenRouter, local vLLM, etc.), use the provider field to explicitly route:

const agent = new Agent({
  name: 'openrouter-agent',
  model: 'meta-llama/llama-3.3-70b-instruct',
  provider: 'openai',
  instructions: '...',
});

Agent with Tools

import { Agent, tool } from '@cogitator-ai/core';
import { z } from 'zod';

const searchWeb = tool({
  name: 'search_web',
  description: 'Search the internet for current information',
  parameters: z.object({
    query: z.string().describe('The search query'),
    limit: z.number().default(5),
  }),
  execute: async ({ query, limit }) => {
    const results = await searchAPI.search(query, limit);
    return results.map((r) => ({ title: r.title, url: r.url }));
  },
});

const researcher = new Agent({
  name: 'researcher',
  model: 'openai/gpt-6.1-sol',
  instructions: 'You are a research assistant. Use tools to find accurate information.',
  tools: [searchWeb],
  maxIterations: 15,
});

The agent loops up to maxIterations times. Each iteration the LLM may call tools or produce a final response. The loop ends when the LLM responds without tool calls. An empty turn (no text and no tool calls) is not taken as the final response: the run asks the model again, up to twice.

When tool calls use up maxIterations before the model answers, the run gets one more turn with toolChoice: 'none' and an instruction to answer from what it has, so it still ends with an answer and structured output can be parsed. Tools the model asks for on that turn are not run. result.iterationLimitReached is true either way, and onIterationLimit: 'stop' ends the run at the last tool turn instead, without an answer.

result.truncated is true when the model's last answer stopped at the output token limit (maxTokens): the output may be cut off, or empty when a reasoning model spent the whole limit thinking. Every provider reports it, so it is the way to tell a cut-off answer from a short one.

Agent with Structured Output

Force the LLM to respond with a specific JSON shape using Zod schemas:

const analyzer = new Agent({
  name: 'analyzer',
  model: 'openai/gpt-6.1-sol',
  instructions: 'Analyze the given text and extract structured information.',
  responseFormat: {
    type: 'json_schema',
    schema: z.object({
      summary: z.string(),
      sentiment: z.enum(['positive', 'negative', 'neutral']),
      keyPoints: z.array(z.string()),
    }),
  },
});

See Structured Outputs for details on json and json_schema modes.

Cloning Agents

Create variations of an agent with clone():

const baseAgent = new Agent({
  name: 'coder',
  model: 'anthropic/claude-sonnet-5-5',
  instructions: 'You are a senior software engineer.',
  temperature: 0.3,
});

const creativeAgent = baseAgent.clone({
  name: 'creative-coder',
  temperature: 0.9,
});

const fastAgent = baseAgent.clone({
  model: 'anthropic/claude-haiku-4-5',
});

A clone gets a new id unless the overrides set one.

Serialization

Agents can be serialized to JSON for storage and restored later:

import { writeFile } from 'node:fs/promises';

const snapshot = agent.serialize();
await writeFile('agent.json', JSON.stringify(snapshot, null, 2));

Restore with the tool implementations:

import { readFile } from 'node:fs/promises';
import { Agent } from '@cogitator-ai/core';
import type { AgentSnapshot } from '@cogitator-ai/types';

const saved = JSON.parse(await readFile('agent.json', 'utf-8')) as AgentSnapshot;
const restored = Agent.deserialize(saved, {
  tools: [searchWeb, readUrl],
});

Tools are stored by name in the snapshot. During deserialization, each tool name is resolved from toolRegistry (any { get(name) }, such as a ToolRegistry) when given, otherwise from the tools array; a missing tool throws AgentDeserializationError. overrides replaces any config field of the restored agent.

A snapshot keeps the model, instructions, sampling parameters, reasoning, maxIterations, onIterationLimit and timeout, but not handoffs or skills (their tools are kept by name). A json_schema response format is saved by name only, so pass the Zod schema back as overrides.responseFormat. Agent.validateSnapshot(value) checks a parsed snapshot; it rejects snapshots without a model, so give agents you serialize an explicit model.

Skills

A skill bundles tools with the instructions for using them. skills on an agent adds each skill's tools (unless the agent already has a tool of that name) and appends its instructions to the agent's:

import { defineSkill } from '@cogitator-ai/core';

const weatherSkill = defineSkill({
  name: 'weather',
  version: '1.0.0',
  description: 'Current weather and forecasts',
  tools: [getWeather],
  instructions: 'Use get_weather for any question about the weather.',
  env: ['WEATHER_API_KEY'],
});

const assistant = new Agent({
  name: 'assistant',
  model: 'openai/gpt-6.1-sol',
  instructions: 'You are a helpful assistant.',
  skills: [weatherSkill],
});

validateSkill(skill) reports missing fields, environment variables and dependencies; the CLI scaffolds and validates skill packages with cogitator skill.

Agents as Tools

Wrap an agent as a tool so other agents can delegate to it:

import { agentAsTool } from '@cogitator-ai/core';

const researchTool = agentAsTool(cogitator, researcher, {
  name: 'research',
  description: 'Delegate a research task to the research agent',
});

const orchestrator = new Agent({
  name: 'orchestrator',
  model: 'openai/gpt-6.1-sol',
  instructions: 'You coordinate tasks. Use the research tool for information gathering.',
  tools: [researchTool],
});

The tool takes a single task string and returns { output, success, error? }; includeUsage and includeToolCalls add the inner run's usage and tool calls, and timeout limits it. The inner run inherits the caller's userId and abort signal. Calls of the inner agent that need approval are declined unless you pass onApproval, since nobody can answer them mid-delegation. See Agent as Tool.

Reasoning

Reasoning models think before they answer. reasoning sets how hard, in one vocabulary for every provider, and can ask for a readable summary of the thinking:

const analyst = new Agent({
  name: 'analyst',
  model: 'anthropic/claude-opus-5-5',
  instructions: 'Answer questions about the quarterly numbers.',
  reasoning: { effort: 'high', summary: true },
});

const result = await cog.run(analyst, {
  input: 'Why did margins drop in Q3?',
  stream: true,
  onReasoning: (delta) => process.stdout.write(delta), // the summary, while it streams
});

result.reasoning; // the whole summary
result.usage.reasoningTokens; // thinking tokens, billed as output

effort goes from none to max; a run can override it with cog.run(agent, { reasoning }). How each provider applies it is in LLM Backends.

Handoffs

An agent can hand the conversation over to another one. handoffs lists the agents it may pass to; each becomes a transfer_to_<name> tool described by the target's description. When the model calls it, the rest of the run goes on as the target agent — its instructions, tools, model and reasoning — with the whole conversation so far:

const billing = new Agent({
  name: 'billing',
  description: 'Invoices, payments and refunds',
  model,
  instructions,
  tools: [lookupInvoice],
});
const support = new Agent({
  name: 'tech_support',
  description: 'Login problems and outages',
  model,
  instructions,
  tools: [serviceStatus],
});

const triage = new Agent({
  name: 'triage',
  model,
  instructions: 'Greet the customer and hand the conversation to the right specialist.',
  handoffs: [billing, support], // or { agent: billing, toolName: 'ask_billing', description: '...' }
});

const result = await cog.run(triage, {
  input: 'How much do I owe on INV-204?',
  onHandoff: console.log,
});
result.handoffs; // [{ from: 'triage', to: 'billing', reason: '...' }]
result.finalAgent; // 'billing' — send the next message of this thread to it

Unlike agents as tools, where a sub-agent answers the caller and the caller keeps the conversation, a handoff gives the conversation away. Unlike swarms, there is no coordinator: each agent decides when to pass on. A run that pauses for approval after a handoff resumes in the agent it was handed to.

Temperature Guidelines

Use CaseTemperatureNotes
Code generation0.0 -- 0.2Deterministic, correct code
Planning / classification0.2 -- 0.4Consistent but flexible
General assistant0.5 -- 0.7Balanced (default: 0.7)
Creative writing0.8 -- 1.2More varied output
Brainstorming1.0 -- 1.5Maximum creativity

On this page