> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent

> The core Agent class for building AI agents in TypeScript

The `Agent` class is the primary entry point for the PraisonAI TypeScript SDK — instructions, tools, and optional persistence in one API.

<Note>
  As of PR #4841 every `Agent` constructor and `Agent.chat` option is honoured — see [Agent Options](#agent-options) below. Two remain **partial**: `attachments` and `toolConfig.parallel` work on the OpenAI-compatible backend and emit a notice on the AI SDK backend. See [Parity Notices](/docs/docs/js/typescript) for the ledger.
</Note>

<Note>
  Many Python `Agent` helper methods are not yet on the TypeScript `Agent`. The constructor and `chat` / `start` cover the common path; see the SDK [parity baseline](https://github.com/MervinPraison/PraisonAI/blob/main/src/praisonai/praisonai/_dev/parity/signatures/inventory-baseline.json) for the current method list.
</Note>

<Warning>
  Several options now **throw** on a typo instead of silently running locally. If you pass an unregistered `runOn` / `toolsRunOn` / `auth` name, an unknown `sandbox` field, or two conflicting placement options, construction fails with a `TypeError` or `Error`. See [When your option throws](#when-your-option-throws).
</Warning>

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    User([User]) --> Agent[Agent]
    Agent --> Tools[Tools]
    Tools --> Agent
    Agent --> Out([Response])

    classDef agent fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef tool fill:#189AB4,stroke:#7C90A0,color:#fff

    class Agent agent
    class Tools tool
    class User,Out tool
    classDef agent fill:#8B0000,color:#fff
    classDef tool fill:#189AB4,color:#fff
```

## Quick Start

<Steps>
  <Step title="Simple Usage">
    ```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    import { Agent } from 'praisonai';

    const agent = new Agent({ instructions: "You are a helpful assistant" });
    const response = await agent.chat("Hello!");
    console.log(response);
    ```
  </Step>

  <Step title="Install">
    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    npm install praisonai
    ```
  </Step>
</Steps>

## Basic Usage

### Simple Agent

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import { Agent } from 'praisonai';

const agent = new Agent({
  instructions: "You are a helpful AI assistant that provides concise answers."
});

const response = await agent.chat("What is the capital of France?");
console.log(response); // "Paris is the capital of France."
```

### Agent with Custom Name

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
const agent = new Agent({
  name: "ResearchBot",
  instructions: "You are a research assistant specializing in scientific topics."
});
```

### Agent with Model Selection

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
const agent = new Agent({
  instructions: "You are helpful",
  llm: "claude-3-5-sonnet-latest"  // or "gpt-4o", "gemini-2.0-flash", etc.
});
```

A bare `claude-*` or `gemini-*` name routes to Anthropic or Google — see [Model Routing](/docs/js/model-routing) for how names are resolved.

### Default model

Omit `llm` and the agent picks a model that matches whichever provider key you've set — no OpenAI key required.

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
// Only ANTHROPIC_API_KEY is set → uses anthropic/claude-3-5-sonnet-latest
const agent = new Agent({ instructions: "You are helpful" });

// Only GEMINI_API_KEY is set → uses gemini/gemini-1.5-flash
const gemini = new Agent({ instructions: "You are helpful" });

// Only OLLAMA_HOST is set → uses ollama/llama3.2 (runs locally, no cloud key)
const local = new Agent({ instructions: "You are helpful" });
```

Set your provider key and it just works. The model is chosen in this order:

| Order | Source                                   | Example                                                    |
| ----- | ---------------------------------------- | ---------------------------------------------------------- |
| 1     | Explicit `llm` / `model` on the agent    | `llm: "gpt-4o"`                                            |
| 2     | `OPENAI_MODEL_NAME` env var              | `OPENAI_MODEL_NAME=gpt-4o`                                 |
| 3     | `PRAISONAI_MODEL` env var                | `PRAISONAI_MODEL=gpt-4o`                                   |
| 4     | First provider key present (order below) | `ANTHROPIC_API_KEY` → `anthropic/claude-3-5-sonnet-latest` |
| 5     | Fallback                                 | `gpt-4o-mini`                                              |

Provider key → default model, checked top to bottom:

| Provider key        | Default model                        |
| ------------------- | ------------------------------------ |
| `OPENAI_API_KEY`    | `gpt-4o-mini`                        |
| `ANTHROPIC_API_KEY` | `anthropic/claude-3-5-sonnet-latest` |
| `GEMINI_API_KEY`    | `gemini/gemini-1.5-flash`            |
| `GOOGLE_API_KEY`    | `google/gemini-1.5-flash`            |
| `GROQ_API_KEY`      | `groq/llama-3.3-70b-versatile`       |
| `COHERE_API_KEY`    | `cohere/command-r`                   |
| `OLLAMA_HOST`       | `ollama/llama3.2`                    |

See [Model Routing](/docs/js/model-routing) for how the chosen model string reaches its vendor.

## Agent with Tools

Pass plain JavaScript functions as tools - schemas are auto-generated:

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import { Agent } from 'praisonai';

// Define a simple tool function
const getWeather = (city: string) => `Weather in ${city}: 22°C, Sunny`;

const agent = new Agent({
  instructions: "You are a weather assistant. Use the getWeather tool to answer weather questions.",
  tools: [getWeather]
});

const response = await agent.chat("What's the weather in Paris?");
console.log(response); // Uses the tool and responds with weather info
```

### Multiple Tools

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
const getWeather = (city: string) => `Weather in ${city}: 22°C`;
const getTime = (timezone: string) => `Current time in ${timezone}: 14:30`;
const calculate = (expression: string) => eval(expression).toString();

const agent = new Agent({
  instructions: "You can check weather, time, and do calculations.",
  tools: [getWeather, getTime, calculate]
});
```

## Agent with Persistence

Use the `db()` factory for easy database setup:

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import { Agent, db } from 'praisonai';

const agent = new Agent({
  instructions: "You are a helpful assistant with memory.",
  db: db("sqlite:./conversations.db"),
  sessionId: "user-123"
});

// Conversations are automatically persisted
await agent.chat("My name is Alice");
await agent.chat("What's my name?"); // Remembers: "Your name is Alice"
```

### Database URL Formats

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
// SQLite — durable, survives restarts (recommended)
db("sqlite:./data.db")
db("sqlite::memory:")   // real SQLite, but process-local

// In-memory (default) — lost on exit
db("memory:")
db(":memory:")

// PostgreSQL / Redis — throw today (remote-only transports, not session stores)
db("postgres://user:pass@host:5432/dbname")
db("redis://localhost:6379")
```

<Warning>
  Only `sqlite:` and `memory:` are wired to the Agent's session store. `db("postgres://…")` and `db("redis://…")` throw — see [Database](/docs/js/database#which-url-should-i-use).
</Warning>

## Agent Configuration

### Full Configuration Options

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
interface SimpleAgentConfig {
  // Required
  instructions: string;           // System prompt / agent instructions
  
  // Optional - Identity
  name?: string;                  // Agent name (auto-generated if not provided)
  
  // Optional - LLM
  llm?: string;                   // Model: "gpt-4o-mini", "claude-3-sonnet", etc.

  // Optional - Credentials & transport
  apiKey?: string;                // Per-agent API key; falls back to *_API_KEY env var
  baseURL?: string;               // Custom endpoint (OpenAI-compatible proxies etc.)
  fetch?: typeof fetch;           // Custom fetch (Tauri / native bridge); implies browser support
  
  // Optional - Behavior
  verbose?: boolean;              // Enable logging (default: true)
  pretty?: boolean;               // Pretty output formatting
  markdown?: boolean;             // Markdown in responses (default: true)
  stream?: boolean;               // Streaming responses (default: true)
  
  // Optional - Tools
  tools?: Function[];             // Array of tool functions
  toolFunctions?: Record<string, Function>;  // Named tool implementations
  maxIterations?: number;         // Max tool-call round-trips before the loop aborts (default: 20)
  maxToolCallsPerTurn?: number;   // Max tool calls executed per round (default: 10)

  // Optional - Structured output (works on every provider)
  outputSchema?: Record<string, any>;  // JSON Schema for structured output
  outputSchemaName?: string;           // Schema name (default: "response")

  // Optional - Persistence
  db?: DbAdapter;                 // Database adapter for persistence
  sessionId?: string;             // Session ID (auto-generated if not provided)
  runId?: string;                 // Run ID for tracing
  
  // Optional - Advanced Mode (role/goal/backstory) — stored as readable fields
  role?: string;                  // Agent role (default: "Assistant")
  goal?: string;                  // Agent goal (default: instructions, else "Help the user with their tasks")
  backstory?: string;             // Agent backstory (default: instructions, else "I am an AI assistant")
}
```

### Provider Credential Options

| Option    | Type     | Default     | Description                                                                                                                                                                       |
| --------- | -------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `apiKey`  | `string` | `undefined` | Per-agent API key. As of PR #4483 it is forwarded to whichever backend the model routes to — not only OpenAI.                                                                     |
| `baseURL` | `string` | `undefined` | Per-agent endpoint. When set and the `llm` string is a **bare** name, the agent stays on the OpenAI-compatible path (proxy case). An explicit `provider/model` prefix still wins. |

See [Model Routing](/docs/js/model-routing) for how bare names resolve and the `baseURL` exception.

### Structured Output Options

| Option             | Type                  | Default      | Description                                                                                                                                                                                                                                                                   |
| ------------------ | --------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `outputSchema`     | `Record<string, any>` | `undefined`  | JSON Schema for structured output. Works on every provider — OpenAI uses native `response_format: json_schema`, other providers route through the AI SDK backend's `generateObject({ schema })` ([PraisonAI PR #4412](https://github.com/MervinPraison/PraisonAI/pull/4412)). |
| `outputSchemaName` | `string`              | `'response'` | Name used for the JSON schema in `response_format.json_schema.name`.                                                                                                                                                                                                          |

See [Structured Output](/docs/js/structured-output) for the full guide.

### Tool-Call Loop

When an agent has `tools`, it loops between the model and your tool functions until the model produces a final answer. Two knobs bound the loop.

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import { Agent } from 'praisonai';

const agent = new Agent({
  name: "Researcher",
  instructions: "Answer questions using the available tools.",
  tools: [search, fetchUrl],
  maxIterations: 30,          // default is 20
  maxToolCallsPerTurn: 5,     // default is 10
});

const answer = await agent.chat("Summarise today's top AI story.");
```

| Option                | Type     | Default | Description                                                                                                                                                                                                                              |
| --------------------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `maxIterations`       | `number` | `20`    | Maximum tool-call round-trips before the loop aborts.                                                                                                                                                                                    |
| `maxToolCallsPerTurn` | `number` | `10`    | Maximum tool calls executed in a single round. Extras in one round are dropped with a warning; the assistant message sent back to the model lists only the executed calls, so `tool_call_id` pairing with tool results stays consistent. |

<Warning>
  If the loop still needs another tool round-trip after `maxIterations`, `chat()` **throws** and `agent.lastStopReason === 'max_steps'`:

  ```
  Agent <name>: reached maximum tool-call iterations (<n>) without a final answer.
  Increase maxIterations in the agent config if the task legitimately needs more tool round-trips.
  ```

  Previous versions used `maxIterations = 5` and silently returned `""` on exhaustion. Both defaults and behaviour changed — wrap `chat()` in `try/catch` and raise `maxIterations` if you see this error.
</Warning>

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    U[📝 User prompt] --> A[🤖 Agent]
    A --> M{Model wants a tool?}
    M -- yes --> T[🔧 Run tool] --> A
    M -- no --> R[✅ Final answer]
    A -. iterations >= maxIterations .-> E[❌ Throw]

    classDef input fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef process fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef llm fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef result fill:#10B981,stroke:#7C90A0,color:#fff

    class U input
    class M process
    class A,T llm
    class R result
    class E llm
```

### Exported Types

Import these types for the streaming, chat, and execute surfaces:

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import type {
  AgentEvent,
  AgentStreamOptions,
  AgentChatCallOptions,  // new — chat() with errorsAsNull
  AgentExecuteTask,      // new — execute() first-argument union
  AgentTaskLike,         // new — duck-type: { description?: string, ... }
} from 'praisonai';
```

`AgentEvent` is the discriminated union yielded by `streamEvents()`; `AgentStreamOptions` is the optional second argument to `stream()` / `streamEvents()`. See [Streaming](/docs/js/streaming) for both reference tables. `AgentChatCallOptions` extends `AgentChatOptions` with `errorsAsNull`; `AgentExecuteTask` is `string | AgentTaskLike | Task`, the first argument to `execute()`.

### Behaviour notes (v1.7.4)

* Importing `praisonai` no longer requires `OPENAI_API_KEY`. The key is checked at OpenAI client creation, so Anthropic / Google users can `require('praisonai')` without it.
* Default `temperature: 0.7` is **omitted** for reasoning-family models (`gpt-5*`, `o1*`, `o3*`, `o4*`), which reject any `temperature` field. An explicit `temperature` still takes effect.
* `Agent.chat()` no longer double-appends the user prompt to history. This is a bugfix — no code change needed.
* Env-only `OpenAI` client now rebuilds when `OPENAI_API_KEY` or `OPENAI_BASE_URL` changes between calls — rotating a key in a settings screen no longer requires restarting the process. See [JS Credential Rotation](/docs/features/js-credential-rotation).
* `OPENAI_BASE_URL` is now forwarded on the env-only path, so exporting it works for module-level convenience functions and env-only `Agent`s the same way an explicit `baseURL` on the config already did.
* New public export `resetOpenAIClient()` forces a rebuild on the next call.

## Runtime compatibility

The `Agent` module has no static dependency on any Node.js builtin, so it imports and runs anywhere JavaScript runs.

| Runtime                                     | Import-time | Random IDs (`runId`, approvals)                             |
| ------------------------------------------- | ----------- | ----------------------------------------------------------- |
| Node 19+                                    | ✅           | `crypto.randomUUID` (WebCrypto global)                      |
| Node 18                                     | ✅           | `crypto.getRandomValues` fallback                           |
| Browser / Electron renderer / Tauri webview | ✅           | `crypto.randomUUID`                                         |
| React Native                                | ✅           | `crypto.getRandomValues` fallback                           |
| Older webviews without WebCrypto            | ✅           | `Math.random()` fallback (weak source, still valid v4 UUID) |

See **[Browser & Webview Runtimes](/docs/js/browser-runtimes)** for how to run agents in a browser, Tauri app, Electron renderer, or React Native app.

### Credentials & Transport

Pass `apiKey` and `baseURL` directly on the Agent — no environment variables required.

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import { Agent } from 'praisonai';

const agent = new Agent({
  instructions: "Use my local model",
  llm: "local-llama",
  apiKey: "not-needed",
  baseURL: "http://localhost:1234/v1",  // LM Studio, vLLM, etc.
});
```

Supply a custom `fetch` to route requests through native code (Tauri, native bridge). See [Browser & Webview Runtimes](/docs/js/browser-runtimes) for the full story.

| Option    | Type           | Default                                      | Description                                         |
| --------- | -------------- | -------------------------------------------- | --------------------------------------------------- |
| `apiKey`  | `string`       | `undefined` (falls back to `OPENAI_API_KEY`) | Per-agent OpenAI key.                               |
| `baseURL` | `string`       | `undefined`                                  | Per-agent base URL for OpenAI-compatible endpoints. |
| `fetch`   | `typeof fetch` | `undefined`                                  | Custom transport; implies browser support.          |

## Advanced Mode (Role/Goal/Backstory)

For more structured agent definitions:

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
const agent = new Agent({
  role: "Senior Research Analyst",
  goal: "Analyze market trends and provide investment insights",
  backstory: "You have 20 years of experience in financial markets..."
});

// Equivalent to:
const agent2 = new Agent({
  instructions: `You are a Senior Research Analyst.
Your goal is: Analyze market trends and provide investment insights
Background: You have 20 years of experience in financial markets...`
});
```

`role`, `goal`, and `backstory` are stored as readable fields on every agent:

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
const agent = new Agent({ role: "Support Specialist", goal: "Resolve customer tickets" });

console.log(agent.role);      // "Support Specialist"
console.log(agent.goal);      // "Resolve customer tickets"
console.log(agent.backstory); // "I am an AI assistant" (default)
```

| Field       | Type     | Default                                                        |
| ----------- | -------- | -------------------------------------------------------------- |
| `role`      | `string` | `"Assistant"`                                                  |
| `goal`      | `string` | `instructions` if set, else `"Help the user with their tasks"` |
| `backstory` | `string` | `instructions` if set, else `"I am an AI assistant"`           |

<Note>
  When an agent is a [handoff](/docs/js/handoffs) target, its `role` and `goal` are woven into the auto-generated handoff description — so setting them improves routing accuracy for free.
</Note>

## Using Agent in a webview / mobile app

`Agent` is safe to import in a browser, Electron renderer, Tauri webview, or React Native bundle when you import it from the `praisonai/mobile` entry. That entry is guaranteed by CI to have no static Node-builtin imports on the `Agent` graph.

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
// ✅ mobile / webview bundle
import { Agent } from 'praisonai/mobile';
```

<Note>
  The package root (`import { Agent } from 'praisonai'`) re-exports the CLI and MCP server and is not webview-safe by design. See [Browser & Webview Runtimes](/docs/js/browser-runtimes) for the full contract, supported browsers, and the load guarantee.
</Note>

## Session Management

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
const agent = new Agent({
  instructions: "You are helpful",
  sessionId: "custom-session-id"
});

// Get session and run IDs
console.log(agent.getSessionId()); // "custom-session-id"
console.log(agent.getRunId());     // Auto-generated UUID
```

## Methods

### chat(prompt: string)

Send a message and get a response. Pass a third `signal?: AbortSignal` argument to stop the call — see [Cancellation](/docs/js/cancellation):

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
const response = await agent.chat("Hello!");

// With a Stop button:
const controller = new AbortController();
await agent.chat("Write an essay", undefined, controller.signal);
```

#### Return `null` on error (`errorsAsNull`)

By default `chat()` rejects on any error. Pass `{ errorsAsNull: true }` for Python's contract — the promise resolves with `null` instead. The return type widens to `Promise<string | null>` only on this call.

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
const result: string | null = await agent.chat(
  "Hello!",
  undefined,
  undefined,
  { errorsAsNull: true },
);

if (result === null) {
  // the turn failed; the error was still logged via Logger.error
}
```

Still fatal, even with `errorsAsNull: true` (Python re-raises these too):

| Error                         | Why                                       |
| ----------------------------- | ----------------------------------------- |
| `AbortError` / aborted signal | An aborted turn is not an empty answer    |
| `InterruptedError`            | Same                                      |
| `ToolExecutionError`          | A tool failed — the caller must handle it |
| `BudgetExceededError`         | A budget stop is not a model outcome      |

Only `chat()` honours `errorsAsNull`. `start()` and `stream()` accept `AgentChatOptions` and ignore it — see the [Parity Notices](/docs/docs/features/typescript-parity-notices) ledger for why an accepted-but-ignored option is the defect this package tracks.

### start(prompt?: string)

Run the agent. The prompt is **optional** — omit it and the agent falls back to its `instructions` (or `"Hello"` when `instructions` is not set).

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import { Agent } from 'praisonai';

const agent = new Agent({ instructions: "Write a haiku about the ocean" });
await agent.start(); // uses instructions as the prompt
```

Pass a prompt to override that fallback:

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
const response = await agent.start("Begin the task");
```

### execute(task?, context?)

Run a task on this agent. Matches Python's `Agent.execute(task, context=None)`.

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import { Agent } from 'praisonai';

const agent = new Agent({ instructions: "You are a research assistant" });

// A string runs as the task
await agent.execute("Summarise the Q3 revenue report.");

// A task-like object is read through `description`
await agent.execute({ description: "Write the release notes." });

// A real Task instance also works
import { Task } from 'praisonai';
const task = new Task({ name: "research", description: "…", expected_output: "…" });
await agent.execute(task);

// Chain prior output — substituted for {{previous}} in the prompt
await agent.execute("Improve this draft: {{previous}}", "the rough draft");

// An array of dependency results is joined with blank lines
await agent.execute("Merge: {{previous}}", ["result A", "result B"]);

// No task → runs the agent's own instructions (unchanged)
await agent.execute();
```

The prompt is resolved in four branches:

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    Start([execute task, context]) --> Q1{task given?}
    Q1 -->|no| Instr[Run instructions]
    Q1 -->|yes| Q2{typeof task}
    Q2 -->|string| Str[Run the string]
    Q2 -->|object| Q3{task.description<br/>is a string?}
    Q3 -->|yes| Desc[Run task.description]
    Q3 -->|no| Cast[Run String task]

    classDef input fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef decision fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef result fill:#10B981,stroke:#7C90A0,color:#fff

    class Start input
    class Q1,Q2,Q3 decision
    class Instr,Str,Desc,Cast result
```

**Resolution order** for the prompt:

1. `execute()` with no argument → runs `instructions`
2. `execute(str)` → runs `str` as the task
3. `execute(task)` with a string `task.description` → runs `task.description`
4. `execute(other)` → runs `String(other)`

The optional second argument, `context`, is prior output substituted for `{{previous}}` in the resolved prompt. An array is joined with blank lines (fan-in of dependency results). `execute()` routes through `chat()`, so the turn is recorded in history and in `getResult()`.

<Warning>
  **Breaking change (v2).** Before this release, `execute(str)` treated the string as `previousResult` (substituted for `{{previous}}` in the instructions) and **silently discarded** it if the instructions had no placeholder. To reproduce the old chaining call, write:

  ```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  await agent.execute(undefined, previousResult);
  ```
</Warning>

### getResult(): string | null

The text this agent produced on its most recent completed turn. `null` until the agent has run at least once.

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
const agent = new Agent({ instructions: "You are helpful" });

agent.getResult(); // null

await agent.chat("Hello!");
agent.getResult(); // "Hi, how can I help?"
```

Recording covers every entry point — `start()`, `chat()`, `execute()`, `stream()`, `streamEvents()`, and cache hits on `chat()`. A turn that throws or is cancelled leaves the previous value in place: the field records the last result there **was**, not the last attempt.

## History (save & restore a chat)

Persist a conversation and reopen it later. The next `chat()` picks up where the previous run left off — the model regains its memory of the conversation, tool calls included.

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import { Agent, type AgentMessage } from 'praisonai';

const agent = new Agent({ instructions: 'You are helpful' });
await agent.chat('My name is Alice');

const saved: AgentMessage[] = agent.getHistory();  // persist this
// … later …
const restored = new Agent({ instructions: 'You are helpful' });
restored.setHistory(saved);
await restored.chat('What is my name?');           // "Your name is Alice"
```

### `getHistory(): AgentMessage[]`

Returns a **copy** of the full conversation, including `tool_calls` / `tool_call_id`. Persisting this output is the intended way to save a chat.

### `setHistory(messages: readonly AgentMessage[]): void`

Validates and restores a saved conversation. Throws on: non-array input, unknown `role`, orphaned `tool_call_id`, or a non-leading `system` message. A **leading** `system` message is accepted and stripped (the agent's own instructions are prepended on every run). See [Chat History Restore](/docs/js/history-restore) for the full contract.

<Note>
  Restored history is now actually replayed to the provider on the next `start()` / `chat()`. Previous versions (pre-#4513) called `generateText(prompt)` on the no-tools path, which dropped every earlier turn — the model behaved as though the conversation never happened. Existing code that already used `getHistory()` sees a stricter return type (`AgentMessage[]`) and no runtime break.
</Note>

## Properties

### `lastStopReason`

Terminal reason for the most recent run. `null` until the agent has run at least once.

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import { Agent } from 'praisonai';
import type { StopReason } from 'praisonai';

const agent = new Agent({ instructions: 'You are helpful', tools: [search] });

try {
  await agent.chat('Do a hard task');
  console.log(agent.lastStopReason); // 'completed'
} catch (err) {
  console.log(agent.lastStopReason); // 'max_steps' or 'error'
}
```

| Value         | Meaning                                                                                    |
| ------------- | ------------------------------------------------------------------------------------------ |
| `'completed'` | The run finished normally with a final answer.                                             |
| `'max_steps'` | The tool loop was aborted because `maxIterations` was reached. Paired with a thrown error. |
| `'cancelled'` | The run was cancelled by the caller.                                                       |
| `'error'`     | The run threw an error other than `max_steps` exhaustion.                                  |
| `null`        | The agent has not been run yet.                                                            |

<Note>
  Import the `StopReason` union type from either `praisonai` or `praisonai/agent`:

  ```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  import type { StopReason } from 'praisonai';
  ```

  This mirrors Python's `Agent.last_stop_reason`, so a run's terminal state is the same story across both SDKs.
</Note>

### stream(prompt: string, opts?: AgentStreamOptions)

Return an `AsyncIterable<string>` of text tokens — iterate them to render the response live in any host. See [Streaming](/docs/js/streaming).

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
for await (const token of agent.stream("Tell me a story")) {
  process.stdout.write(token);
}
```

### streamEvents(prompt: string, opts?: AgentStreamOptions)

Return an `AsyncIterable<AgentEvent>` of structured events (`text`, `tool_call`, `tool_result`, `finish`, `error`) — use when you need finish/error/tool semantics. See [Streaming](/docs/js/streaming).

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
for await (const event of agent.streamEvents("Hi")) {
  if (event.type === 'text') process.stdout.write(event.delta);
}
```

## Environment Variables

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Model selection — override the provider-aware default (see Default model above)
OPENAI_MODEL_NAME=gpt-4o
PRAISONAI_MODEL=gpt-4o

# Behavior
PRAISON_VERBOSE=true
PRAISON_PRETTY=true

# API Keys
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
GOOGLE_API_KEY=...
```

<Note>
  Environment variables remain the default fallback. A per-agent `apiKey` / `baseURL` wins when both are set.

  `OPENAI_BASE_URL` is now honoured in the env-only fallback path — not only for a per-agent `baseURL`. Exporting it points module-level convenience functions and env-only `Agent`s at your proxy or gateway, and a change is picked up on the next call. See [JS Credential Rotation](/docs/features/js-credential-rotation).
</Note>

## Examples

### Research Agent

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
const researcher = new Agent({
  name: "Researcher",
  instructions: `You are a research assistant. 
  - Search for information on topics
  - Summarize findings concisely
  - Cite sources when possible`,
  llm: "gpt-4o"
});

const findings = await researcher.chat("Research the latest AI developments in 2024");
```

### Code Assistant

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
const coder = new Agent({
  name: "CodeHelper",
  instructions: `You are a coding assistant.
  - Write clean, well-documented code
  - Explain your solutions
  - Follow best practices`,
  tools: [runCode, searchDocs]
});

const code = await coder.chat("Write a function to sort an array");
```

## Agent Options

PR #4841 wired up 15 constructor options and 6 per-call `chat` options that were previously accepted for parity and dropped. Each snippet below is the shortest way to turn the behaviour on.

### Constructor options

| Option            | Type                                                        | Notes                                                                                                                                                                                               |
| ----------------- | ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `auth`            | `string`                                                    | Subscription provider; picks a default model (`'claude-code' → 'anthropic/claude-sonnet-4-5'`, `'qwen-cli' → 'openai/qwen3-coder-plus'`). Throws on an unregistered provider. See [Auth](/docs/js/auth). |
| `toolsets`        | `string[]`                                                  | Named toolset groups. Throws on an unknown name.                                                                                                                                                    |
| `reflection`      | `boolean \| preset \| ReflectionConfig`                     | Critique-and-regenerate loop. Presets: `minimal`, `standard`, `thorough`. Rounds at `agent.lastReflections`.                                                                                        |
| `autonomy`        | `'suggest' \| 'auto_edit' \| 'full_auto' \| AutonomyConfig` | Enables the loop and a doom-loop guard (`autonomy.doomLoopThreshold`, default 3).                                                                                                                   |
| `templates`       | `TemplateConfig`                                            | `system`, `prompt`, `response`, `useSystemPrompt`. Throws on an unknown field.                                                                                                                      |
| `selfImprove`     | `boolean \| 'inline' \| 'background' \| SelfImproveConfig`  | Isolated skill-review turn after a tool-using turn; proposals at `agent.lastSkillProposals`.                                                                                                        |
| `toolConfig`      | `boolean \| ToolConfig`                                     | `timeout`, `retryPolicy`, `outputLimit`, `outputDirection`, `parallel`, `allowGlobalTools`. See [Tool Config](/docs/js/tool-config).                                                                     |
| `learn`           | `boolean \| 'agentic' \| 'propose' \| LearnConfig`          | Extracts learnings and injects `Learned Insights`. See [Learn](/docs/js/learn).                                                                                                                          |
| `backend`         | `ManagedBackendLike`                                        | Custom object exposing `execute(prompt, options)`. Throws without `execute()`. See [Placement](/docs/js/placement).                                                                                      |
| `runOn`           | `string \| ManagedBackendLike`                              | A managed runtime hosts the whole agent. Throws on an unknown name.                                                                                                                                 |
| `toolsRunOn`      | `string \| ToolPlaceLike`                                   | A tool place runs the tools; the loop stays local. Throws on an unknown name.                                                                                                                       |
| `runtime`         | `boolean \| string \| AgentRuntimeConfig`                   | Delegates the turn to a registered runtime (`DEFAULT_RUNTIME_ID = 'praisonai'`). See [Runtime](/docs/js/runtime).                                                                                        |
| `toolSearch`      | `boolean \| 'on' \| 'off' \| 'auto' \| ToolSearchConfig`    | Progressive tool disclosure via `tool_search` / `tool_describe` / `tool_call`.                                                                                                                      |
| `messageSteering` | `boolean \| MessageSteeringProtocol`                        | Enables `agent.steer()`.                                                                                                                                                                            |
| `sandbox`         | `boolean \| string \| SandboxConfig`                        | Sandboxed code execution. See [Sandbox](/docs/js/sandbox).                                                                                                                                               |

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import { Agent } from 'praisonai';

// auth — bill against a subscription seat; picks the provider's default model
const a1 = new Agent({ instructions: 'You are helpful', auth: 'claude-code' });

// toolsets — attach a named group
const a2 = new Agent({ instructions: 'Search the web', toolsets: ['web'] });

// reflection — critique and regenerate, then read the rounds
const a3 = new Agent({ instructions: 'Answer carefully', reflection: 'minimal' });
await a3.chat('Explain TLS');
console.log(a3.lastReflections);

// autonomy — approve edits automatically, prompt for the rest
const a4 = new Agent({ instructions: 'Refactor code', tools: [], autonomy: 'auto_edit' });

// templates — control the system prompt
const a5 = new Agent({
  instructions: 'Be terse',
  templates: { system: 'You are {name}. {instructions}', useSystemPrompt: true },
});

// selfImprove — review skills after a tool-using turn without blocking
const a6 = new Agent({ instructions: 'Do work', selfImprove: 'background' });

// toolConfig — cap a tool at 10s with two retries
const a7 = new Agent({
  instructions: 'Use tools',
  toolConfig: { timeout: 10, retryPolicy: { maxAttempts: 2 } },
});

// learn — extract and inject learnings each turn
const a8 = new Agent({ instructions: 'Assist', learn: 'agentic' });

// backend — run the whole agent on a custom managed backend
const a9 = new Agent({
  instructions: 'Hosted',
  backend: { async execute(prompt) { return `handled: ${prompt}`; } },
});

// runOn / toolsRunOn — see /js/placement for registration
// const a10 = new Agent({ instructions: 'Hosted', runOn: 'my-runtime' });
// const a11 = new Agent({ instructions: 'Local loop', toolsRunOn: 'local' });

// runtime — delegate the turn to the builtin runtime
const a12 = new Agent({ instructions: 'Delegated', runtime: true });

// toolSearch — defer big tool lists behind bridge tools
const a13 = new Agent({ instructions: 'Many tools', toolSearch: 'auto' });

// messageSteering — accept live guidance
const a14 = new Agent({ instructions: 'Steerable', messageSteering: true });

// sandbox — give the agent a subprocess sandbox
const a15 = new Agent({ instructions: 'Run code', sandbox: true });
```

### Per-call `chat` options

`AgentChatOptions` is the second argument to `chat()`.

| Option            | Type                      | Notes                                                                                                                                                                                |
| ----------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `attachments`     | `readonly string[]`       | Image paths, `http(s)://` or `data:` URIs. **Images only** (`.jpg/.jpeg/.png/.gif/.webp`); other inputs warn-and-skip. Ephemeral. Emits a notice on the AI SDK backend.              |
| `reasoningSteps`  | `boolean`                 | Forces a single non-streaming completion. Beats `stream`.                                                                                                                            |
| `taskName`        | `string`                  | Merged into `agent_start` / `agent_complete` hook contexts; read via `agent.getTaskContext()`.                                                                                       |
| `taskDescription` | `string`                  | As `taskName`.                                                                                                                                                                       |
| `taskId`          | `string`                  | As `taskName`.                                                                                                                                                                       |
| `config`          | `Record<string, unknown>` | Forwarded verbatim to a managed backend's `execute()`.                                                                                                                               |
| `errorsAsNull`    | `boolean`                 | `chat()` only. Resolve with `null` instead of rejecting. Cancellation, `ToolExecutionError`, and `BudgetExceededError` still reject. Widens the return to `Promise<string \| null>`. |

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import { Agent } from 'praisonai';

const agent = new Agent({ instructions: 'Describe images' });

// attachments — images for this turn only
await agent.chat('What is in this picture?', { attachments: ['./photo.png'] });

// reasoningSteps — one non-streaming reasoning completion
await agent.chat('Prove the theorem', { reasoningSteps: true });

// task metadata — readable afterwards
await agent.chat('Step 1', { taskName: 'ingest', taskDescription: 'load data', taskId: 't-1' });
console.log(agent.getTaskContext()); // { name: 'ingest', description: 'load data', id: 't-1' }

// config — forwarded to a managed backend's execute()
await agent.chat('Run remotely', { config: { region: 'eu' } });
```

## When your option throws

Previous code that mis-typed a placement name, an auth provider, or a sandbox field ran silently in-process. The same code now raises at construction (or on `executeCode()`), so a typo never becomes a wrong-machine run. Grep for the exact text:

<Warning>
  ```text theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  # runOn names an unknown managed runtime
  TypeError: Agent(runOn: 'X') is not a known managed runtime. ...

  # runOn names a tool place (should be toolsRunOn)
  TypeError: Agent(runOn: 'X') is not valid: runOn places the whole agent ... use toolsRunOn

  # toolsRunOn names a managed runtime (should be runOn)
  TypeError: Agent(toolsRunOn: 'X') is not valid: 'X' hosts an entire agent, not individual tools.

  # toolsRunOn names an unknown place
  TypeError: Agent(toolsRunOn: 'X') is not a known place. ...

  # two options that set the same thing
  TypeError: Agent(runOn, backend) sets the agent's runtime twice. ...
  TypeError: Agent(runOn, toolsRunOn) points the tools at two machines. ...
  TypeError: Agent(backend, toolsRunOn) points the tools at two machines: ...

  # backend without execute()
  TypeError: Agent(backend) does not support execute(): a managed backend must expose execute(prompt, options) returning the response.

  # runtime combined with backend/runOn
  TypeError: Agent(runtime, backend/runOn) sets where the loop runs twice. ...

  # auth names an unregistered provider
  Error: Unknown subscription provider 'X'. Registered: ... register one with registerAuthProvider('X', resolver). ...

  # runtime lacks a required capability
  Error: Runtime 'X' does not provide the required capability: ...

  # sandbox: unknown field(s)
  Error: Unknown sandbox field(s): ... Valid fields: sandboxType, image, workingDir, env, autoCleanup, persistFiles, timeout, metadata

  # sandbox: unknown runner
  Error: No sandbox runner is registered for "X" (available: ...). ... register one with registerSandboxRunner('X', ...).

  # executeCode() with no sandbox configured
  Error: Agent NAME: no sandbox configured. Either name a place on the call -- agent.executeCode(code, { runIn: 'subprocess' }) -- or set a default for every call with new Agent({ sandbox: true }).
  ```
</Warning>

Other typed inputs throw their own tested messages: `toolsets: ['unknown-name']`, `reflection: 'exhaustive'`, `templates: { greeting: 'hi' }`, an invalid `messageSteering`, and an invalid `runtime` type.

## New methods and fields

PR #4841 adds these instance methods and public fields.

### `steer(message: string, priority?: number)`

Queue priority-aware guidance for the next turn (requires `messageSteering: true`). Returns a message id, or `''` when steering is off or the queue is full.

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import { Agent } from 'praisonai';
import { SteeringPriority } from 'praisonai';

const agent = new Agent({ instructions: 'Assist', messageSteering: true });
agent.steer('check the staging config first', SteeringPriority.HIGH);
await agent.chat('Deploy the service');
```

`SteeringPriority` is `{ LOW: 1, NORMAL: 5, HIGH: 10, URGENT: 20, INTERRUPT: 30 }`. Drained notes are prefixed `[USER GUIDANCE]`, `[URGENT USER GUIDANCE]` or `[INTERRUPT USER GUIDANCE]` by priority.

### `executeCode(code: string, options?: ExecuteCodeOptions)`

Run a one-shot snippet in the agent's sandbox. `options` is `{ language?, checkSecurity?, runIn? }`.

```typescript theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import { Agent } from 'praisonai';

const agent = new Agent({ instructions: 'Run code', sandbox: true });
const result = await agent.executeCode('print(2 + 2)', { language: 'python' });
console.log(result.stdout, result.metadata?.securityWarnings);
```

### Getters and fields

| Member                       | Returns                                                                                 |
| ---------------------------- | --------------------------------------------------------------------------------------- |
| `agent.getTaskContext()`     | `TaskContext \| undefined` — the last turn's `taskName` / `taskDescription` / `taskId`. |
| `agent.getLearnManager()`    | The lazily-built learn manager, or `undefined`.                                         |
| `agent.getManagedBackend()`  | The resolved `backend` / `runOn` target, or `undefined`.                                |
| `agent.getToolPlace()`       | The resolved `toolsRunOn` place, or `undefined`.                                        |
| `agent.getMessageSteering()` | The `MessageSteeringProtocol`, or `undefined`.                                          |
| `agent.getRuntime()`         | The resolved runtime, or `undefined`.                                                   |
| `agent.getAutonomy()`        | The `AutonomyConfig`, or `undefined`.                                                   |
| `agent.getSandboxConfig()`   | The `SandboxConfig`, or `undefined`.                                                    |
| `agent.hasSandbox`           | `boolean` — whether a sandbox is configured.                                            |
| `agent.lastSkillProposals`   | `SkillProposal[]` — proposals from the last self-improve review.                        |
| `agent.lastReflections`      | `ReflectionRound[]` — critiques from the last reflection loop.                          |

## Related

<CardGroup cols={2}>
  <Card title="AgentTeam" icon="users" href="/docs/js/agent-team">
    Multi-agent orchestration
  </Card>

  <Card title="Placement" icon="server" href="/docs/js/placement">
    backend / runOn / toolsRunOn
  </Card>

  <Card title="Auth" icon="key" href="/docs/js/auth">
    Subscription auth
  </Card>

  <Card title="Sandbox" icon="box" href="/docs/js/sandbox">
    executeCode & SandboxConfig
  </Card>

  <Card title="AgentFlow" icon="diagram-project" href="/docs/js/agent-flow">
    Step-based workflows
  </Card>

  <Card title="Tools" icon="wrench" href="/docs/js/tools">
    Custom tool development
  </Card>

  <Card title="Cancellation" icon="octagon-x" href="/docs/js/cancellation">
    Stop a running agent
  </Card>
</CardGroup>
