# Core Components of the Dexter Agent: A Modular AI Architecture

> Explore the core components of the Dexter agent including the Agent Loop, Tool Executor, and Scratchpad. Discover how these modules create a modular AI architecture for efficient reasoning and answer synthesis.

- Repository: [Virat Singh/dexter](https://github.com/virattt/dexter)
- Tags: architecture
- Published: 2026-02-16

---

**The Dexter agent consists of eight specialized modules—including the Agent Loop, Tool Executor, Scratchpad, and Token Counter—that together orchestrate an iterative reasoning cycle while managing context windows and synthesizing final answers.**

The `virattt/dexter` repository implements a sophisticated AI research assistant built around a modular agent architecture. Understanding the core components of the Dexter agent is essential for developers looking to extend its capabilities or integrate its reasoning engine into custom workflows. This article examines the source code to reveal how each module contributes to the agent's decision-making loop.

## The Agent Loop: Orchestrating the Reasoning Cycle

At the center of the system lies the **Agent Loop**, implemented in [`src/agent/agent.ts`](https://github.com/virattt/dexter/blob/main/src/agent/agent.ts). This module drives the iterative "think‑tool‑think" cycle that defines Dexter's behavior.

The `Agent` class manages the entire lifecycle of a query. It repeatedly invokes the LLM, interprets whether the model wants to call a tool or provide a final answer, and caps the number of iterations to prevent infinite loops. Throughout this process, it emits typed events—such as `tool_start` and `tool_end`—that allow the UI to observe execution progress in real time.

## Tool Execution and Context Management

### AgentToolExecutor: Bridging LLM Calls and Tool Logic

The **AgentToolExecutor**, defined in [`src/agent/tool-executor.ts`](https://github.com/virattt/dexter/blob/main/src/agent/tool-executor.ts), serves as the bridge between the agent's reasoning loop and the concrete tool implementations. When the LLM outputs a tool‑call request, the executor looks up the appropriate registered tool, executes it with the provided arguments, and records the result.

This module abstracts away the specifics of individual tools—whether they perform financial searches, web searches, or browser automation—ensuring that the core agent logic remains agnostic to the tool's internal implementation.

### The Scratchpad: Maintaining Iterative Context

The **Scratchpad** ([`src/agent/scratchpad.ts`](https://github.com/virattt/dexter/blob/main/src/agent/scratchpad.ts)) acts as the single source of truth for every tool result generated during a query. It maintains a chronological record of all actions and observations, which is then fed back to the LLM on each subsequent turn.

This design pattern allows the model to reason with its own prior actions, creating a chain-of-thought effect that improves multi-step problem solving. The scratchpad's contents persist throughout the entire run, providing complete observability into the agent's reasoning trace.

## Context Management and Token Optimization

### RunContext: Per-Run Configuration and State

The **RunContext** module in [`src/agent/run-context.ts`](https://github.com/virattt/dexter/blob/main/src/agent/run-context.ts) holds the ephemeral state for a single agent execution. It encapsulates the original user query, the selected model identifier, the maximum iteration limit, and any runtime feature flags.

By centralizing this configuration, `RunContext` ensures that all downstream components—the executor, token counter, and scratchpad—operate with consistent parameters throughout the query lifecycle.

### TokenCounter: Managing Model Context Windows

Token management is handled by the **TokenCounter** ([`src/agent/token-counter.ts`](https://github.com/virattt/dexter/blob/main/src/agent/token-counter.ts)), which estimates the token usage of the current scratchpad and prompts. When the accumulated context approaches the model‑specific token budget, this module automatically prunes the oldest entries to maintain compliance with context window limits.

This pruning mechanism is essential for long-running queries that might otherwise exceed the LLM's maximum input length, ensuring the agent can continue operating without truncation errors.

### FinalAnswerContext: Synthesizing the Output

Once the tool‑calling loop terminates—either because the LLM indicates completion or the iteration limit is reached—the **FinalAnswerContext** ([`src/agent/final-answer-context.ts`](https://github.com/virattt/dexter/blob/main/src/agent/final-answer-context.ts)) constructs the prompt for the final synthesis step.

This module builds a clean prompt that includes the full scratchpad history but strips away tool‑specific instructions and bindings. The resulting prompt asks the LLM to craft a concise, human-readable answer based solely on the accumulated evidence.

## System Infrastructure

### Prompts and Tool Descriptions

The **Prompts** module ([`src/agent/prompts.ts`](https://github.com/virattt/dexter/blob/main/src/agent/prompts.ts)) stores the system‑prompt fragments that are injected into every LLM call. These include descriptions of available tools, the skill registry, and token‑budget hints that guide the model's behavior.

By externalizing these prompts into a dedicated module, Dexter allows for easy customization of the agent's personality and capabilities without modifying the core logic.

### Type Definitions and Event Interfaces

Finally, the **Types** module ([`src/agent/types.ts`](https://github.com/virattt/dexter/blob/main/src/agent/types.ts)) centralizes TypeScript interfaces for events, tool results, and the public API of the agent. This ensures type safety across the entire system and provides clear contracts for developers extending the platform.

## Implementation Examples

### Instantiating the Agent

To create a new agent instance, import the `Agent` class and provide a model provider along with configuration parameters:

```typescript
import { Agent } from "./src/agent/agent";
import { ModelProvider } from "./src/model/llm";

// Choose a model (e.g., the default fast model)
const llm = new ModelProvider("gpt-5.2");

// Create the agent with a user query
const dexter = new Agent({
  llm,
  maxIterations: 10,
  initialPrompt: "Give me a DCF valuation for Apple Inc."
});

// Run the agent – it returns a promise that resolves to the final answer string
dexter.run().then(answer => {
  console.log("Final answer:", answer);
});

```

### Adding a Custom Tool

Extend Dexter's capabilities by registering custom tools with the executor:

```typescript
import { registerTool } from "./src/tools/registry";
import { AgentToolExecutor } from "./src/agent/tool-executor";

// Define a simple echo tool
const echoTool = {
  name: "echo",
  description: "Returns the supplied text unchanged.",
  async run(input: { text: string }) {
    return { output: input.text };
  },
};

// Register it so the executor can find it
registerTool(echoTool);

// The agent can now invoke `echo` via the LLM's tool‑calling syntax.

```

### Inspecting the Scratchpad During Execution

Monitor the agent's reasoning process by listening to events and examining the scratchpad:

```typescript
dexter.on("tool_end", ({ toolName, result }) => {
  console.log(`Tool ${toolName} finished. Scratchpad now contains:`);
  console.log(dexter.scratchpad.getAll());
});

```

## Summary

- The **Agent Loop** in [`src/agent/agent.ts`](https://github.com/virattt/dexter/blob/main/src/agent/agent.ts) drives the iterative reasoning cycle, managing the conversation between the LLM and available tools.
- The **AgentToolExecutor** ([`src/agent/tool-executor.ts`](https://github.com/virattt/dexter/blob/main/src/agent/tool-executor.ts)) abstracts tool execution, while the **Scratchpad** ([`src/agent/scratchpad.ts`](https://github.com/virattt/dexter/blob/main/src/agent/scratchpad.ts)) maintains a complete history of tool results for context.
- **TokenCounter** ([`src/agent/token-counter.ts`](https://github.com/virattt/dexter/blob/main/src/agent/token-counter.ts)) automatically manages context window limits by pruning older entries when budgets are exceeded.
- **RunContext** ([`src/agent/run-context.ts`](https://github.com/virattt/dexter/blob/main/src/agent/run-context.ts)) and **FinalAnswerContext** ([`src/agent/final-answer-context.ts`](https://github.com/virattt/dexter/blob/main/src/agent/final-answer-context.ts)) handle per-run state and final answer synthesis, respectively.
- **Prompts** ([`src/agent/prompts.ts`](https://github.com/virattt/dexter/blob/main/src/agent/prompts.ts)) and **Types** ([`src/agent/types.ts`](https://github.com/virattt/dexter/blob/main/src/agent/types.ts)) provide the system instructions and TypeScript interfaces that ensure type safety across the architecture.

## Frequently Asked Questions

### What is the primary role of the Agent Loop in Dexter's architecture?

The **Agent Loop**, implemented in [`src/agent/agent.ts`](https://github.com/virattt/dexter/blob/main/src/agent/agent.ts), serves as the central orchestrator that drives the iterative "think‑tool‑think" cycle. It repeatedly invokes the LLM, interprets whether the model requests a tool execution or final answer, and enforces the maximum iteration limit to prevent infinite loops.

### How does Dexter manage token limits during long-running conversations?

The **TokenCounter** module in [`src/agent/token-counter.ts`](https://github.com/virattt/dexter/blob/main/src/agent/token-counter.ts) estimates token usage for the current scratchpad and prompts. When the accumulated context approaches the model-specific budget, it automatically prunes the oldest scratchpad entries, ensuring the agent continues operating without exceeding context window limits or triggering truncation errors.

### Can developers add custom tools to the Dexter agent?

Yes, developers can extend Dexter's capabilities by registering custom tools through the `registerTool` function from `src/tools/registry`. The **AgentToolExecutor** in [`src/agent/tool-executor.ts`](https://github.com/virattt/dexter/blob/main/src/agent/tool-executor.ts) automatically discovers and invokes these tools when the LLM requests them, recording results in the scratchpad for subsequent reasoning steps.

### What distinguishes the FinalAnswerContext from the regular agent loop?

The **FinalAnswerContext** in [`src/agent/final-answer-context.ts`](https://github.com/virattt/dexter/blob/main/src/agent/final-answer-context.ts) operates after the tool-calling loop terminates, constructing a specialized prompt that includes the full scratchpad history but strips away tool-specific instructions. This allows the LLM to synthesize a concise, human-readable answer based solely on accumulated evidence rather than continuing the iterative tool-selection process.