# How Freebuff Manages Public Agent Definitions: Bundled and Custom Agents Explained

> Discover how Freebuff manages public agent definitions using bundled and custom agents. Learn about its dual-layer system for efficient agent management and dynamic loading.

- Repository: [Codebuff/freebuff](https://github.com/CodebuffAI/freebuff)
- Tags: architecture
- Published: 2026-09-01

---

**Freebuff manages public agent definitions through a dual-layer system where bundled TypeScript modules are compiled into the CLI binary via a pre-build script, while user-provided definitions are dynamically loaded from `.agents` directories at runtime, with the SDK's loader merging both sources and giving precedence to user definitions.**

Freebuff's architecture for public agent definitions enables both immediate out-of-the-box functionality and deep customization. All agents in the CodebuffAI/freebuff repository conform to a standardized `AgentDefinition` interface, allowing the system to treat built-in and user-created agents identically while maintaining clear separation between immutable bundled resources and mutable local configurations.

## The AgentDefinition Schema

Every public agent in Freebuff is structured as a TypeScript module exporting an **`AgentDefinition`** object. This strict typing ensures consistency across bundled and user-provided agents.

The complete schema lives in [`agents/types/agent-definition.ts`](https://github.com/CodebuffAI/freebuff/blob/main/agents/types/agent-definition.ts) and defines every configurable aspect of an agent:

- **`id`** – Unique identifier used to invoke the agent
- **`model`** – Target LLM (e.g., `openai/gpt-5-mini`)
- **`toolNames`** – Array of available tools the agent can call
- **`spawnableAgents`** – Sub-agents this agent can instantiate
- **`handleSteps`** – Generator function defining the agent's execution flow
- **`displayName`** – Human-readable label for UI presentation

Both bundled and user-provided agents share this identical type definition, enabling seamless interoperability within the runtime environment.

## Bundled Public Agents

Freebuff embeds its built-in public agents directly into the CLI binary through a sophisticated pre-build process. This ensures critical agents remain available even when users have not configured a local `.agents` directory.

### The Pre-Build Pipeline

The script at [`cli/scripts/prebuild-agents.ts`](https://github.com/CodebuffAI/freebuff/blob/main/cli/scripts/prebuild-agents.ts) scans the repository's `.agents` directory tree (including paths like [`agents/reviewer/code-reviewer.ts`](https://github.com/CodebuffAI/freebuff/blob/main/agents/reviewer/code-reviewer.ts) and [`agents/general-agent/gpt-5-agent.ts`](https://github.com/CodebuffAI/freebuff/blob/main/agents/general-agent/gpt-5-agent.ts)). For each valid definition found:

1. Imports the TypeScript module
2. Stringifies any `handleSteps` generator functions
3. Generates a self-contained module at [`cli/src/agents/bundled-agents.generated.ts`](https://github.com/CodebuffAI/freebuff/blob/main/cli/src/agents/bundled-agents.generated.ts)

### Runtime Registry

At startup, [`cli/src/utils/local-agent-registry.ts`](https://github.com/CodebuffAI/freebuff/blob/main/cli/src/utils/local-agent-registry.ts) imports this generated bundle and exposes it through `getBundledAgentsAsLocalInfo()`. These agents carry the flag **`isBundled: true`**, distinguishing them from user overrides.

```typescript
import { getBundledAgentsAsLocalInfo } from '../agents/bundled-agents.generated'

const bundled = getBundledAgentsAsLocalInfo()
bundled.forEach(agent => {
  console.log(`${agent.id} (${agent.displayName}) – bundled: ${agent.isBundled}`)
})

```

Because these definitions are compiled into the binary, the `code-reviewer` and other public agents function immediately after installation without additional configuration.

## Loading User-Provided Agents

When the SDK initializes, it supplements bundled agents with custom definitions through the dynamic loader in [`sdk/src/agents/load-agents.ts`](https://github.com/CodebuffAI/freebuff/blob/main/sdk/src/agents/load-agents.ts).

### Discovery and Validation

The loader searches three default locations recursively:
- `{cwd}/.agents`
- `{cwd}/../.agents`
- `${HOME}/.agents`

Alternatively, users can specify a custom path via the `agentsPath` parameter. The loader collects all files matching `.ts`, `.tsx`, `.js`, `.mjs`, or `.cjs` extensions, validates them against the `AgentDefinition` schema, and returns a map keyed by the agent's `id`.

```typescript
import { loadLocalAgents } from '@codebuff/sdk/src/agents/load-agents'

const { agents, validationErrors } = await loadLocalAgents({
  validate: true,    // Enforce schema validation
  verbose: true,     // Log loading diagnostics
})

if (validationErrors.length) {
  console.error('Invalid agent definitions:', validationErrors)
}

```

### Environment Resolution and Debugging

The loader processes **`$ENV_VAR`** references within MCP server configurations, substituting actual environment values at runtime. Each loaded definition retains its original **`_sourceFilePath`** property, enabling precise debugging when agent validation fails.

## Runtime Precedence and Usage

Freebuff merges bundled and user-provided definitions at runtime, with **user agents taking precedence** over bundled equivalents when IDs collide. This allows seamless overrides of built-in behavior without modifying the core repository.

To use loaded agents with the SDK client:

```typescript
import { CodebuffClient } from '@codebuff/sdk/src/client'

const client = new CodebuffClient()
const result = await client.run({
  agent: 'code-reviewer',                     // References bundled or custom agent
  agentDefinitions: Object.values(agents),    // Merged registry
  prompt: 'Please review this pull request',
})

```

### Creating Custom Public Agents

Users can extend or override functionality by placing TypeScript files in any searched `.agents` directory:

```typescript
// .agents/my-agent.ts
import { AgentDefinition } from '@codebuff/common/templates/initial-agents-dir/types/agent-definition'

const myAgent: AgentDefinition = {
  id: 'my-agent',
  displayName: 'My Custom Agent',
  model: 'openai/gpt-5-mini',
  toolNames: ['read_files', 'write_file'],
  handleSteps: function* ({ logger }) {
    logger.info('Running custom step')
    const { toolResult } = yield {
      toolName: 'read_files',
      input: { paths: ['example.txt'] },
    }
    // Custom logic here
  },
}

export default myAgent

```

The next invocation of `loadLocalAgents()` automatically detects this file, overriding any bundled agent with the same `id` while preserving access to all other public definitions.

## Summary

- **Standardized Schema**: All public agent definitions conform to the `AgentDefinition` interface in [`agents/types/agent-definition.ts`](https://github.com/CodebuffAI/freebuff/blob/main/agents/types/agent-definition.ts), ensuring type safety across bundled and custom implementations.

- **Dual Loading Strategy**: Freebuff combines pre-bundled agents (generated via [`cli/scripts/prebuild-agents.ts`](https://github.com/CodebuffAI/freebuff/blob/main/cli/scripts/prebuild-agents.ts) and exposed through [`cli/src/utils/local-agent-registry.ts`](https://github.com/CodebuffAI/freebuff/blob/main/cli/src/utils/local-agent-registry.ts)) with dynamically loaded user agents from `.agents` directories.

- **Environment Awareness**: The SDK loader ([`sdk/src/agents/load-agents.ts`](https://github.com/CodebuffAI/freebuff/blob/main/sdk/src/agents/load-agents.ts)) resolves environment variables, validates schemas, and preserves source file paths for debugging.

- **Precedence Rules**: User-provided definitions automatically override bundled agents with matching IDs, enabling customization without forking the repository.

- **Binary Independence**: Bundled agents are embedded in the CLI binary, ensuring core functionality works immediately while still supporting extensibility through the `.agents` directory convention.

## Frequently Asked Questions

### Where are bundled public agent definitions stored in the Freebuff repository?

Bundled agent source files reside under the **`agents/`** directory tree in the repository root (e.g., [`agents/reviewer/code-reviewer.ts`](https://github.com/CodebuffAI/freebuff/blob/main/agents/reviewer/code-reviewer.ts)). During the build process, [`cli/scripts/prebuild-agents.ts`](https://github.com/CodebuffAI/freebuff/blob/main/cli/scripts/prebuild-agents.ts) scans these files and generates [`cli/src/agents/bundled-agents.generated.ts`](https://github.com/CodebuffAI/freebuff/blob/main/cli/src/agents/bundled-agents.generated.ts), which is then compiled into the CLI binary. At runtime, [`cli/src/utils/local-agent-registry.ts`](https://github.com/CodebuffAI/freebuff/blob/main/cli/src/utils/local-agent-registry.ts) imports this generated module to expose bundled agents with the `isBundled: true` flag.

### How do I override a bundled public agent with my own definition?

Create a TypeScript file in any recognized `.agents` directory (such as `{cwd}/.agents` or `${HOME}/.agents`) that exports an `AgentDefinition` with the same `id` as the bundled agent you wish to override. When [`sdk/src/agents/load-agents.ts`](https://github.com/CodebuffAI/freebuff/blob/main/sdk/src/agents/load-agents.ts) runs, it automatically gives precedence to user-provided definitions over bundled ones. The next SDK client initialization will use your custom implementation instead of the built-in version.

### What file formats does Freebuff support for agent definitions?

The loader in [`sdk/src/agents/load-agents.ts`](https://github.com/CodebuffAI/freebuff/blob/main/sdk/src/agents/load-agents.ts) recursively searches for files with the following extensions: **`.ts`**, **`.tsx`**, **`.js`**, **`.mjs`**, and **`.cjs`**. Both CommonJS and ES Module formats are supported, provided they export a default `AgentDefinition` object or an object conforming to the expected schema.

### How does Freebuff handle environment variables in agent configurations?

When loading agent definitions, Freebuff's SDK loader automatically detects and resolves **`$ENV_VAR`** syntax within MCP server configuration objects. This occurs during the validation phase in [`sdk/src/agents/load-agents.ts`](https://github.com/CodebuffAI/freebuff/blob/main/sdk/src/agents/load-agents.ts), substituting the literal string with the corresponding environment variable value at runtime. This allows sensitive credentials or host-specific settings to remain outside of version-controlled agent definition files.