How to Embed Freebuff Agents in External Applications Using the SDK

The Freebuff SDK provides a TypeScript wrapper around the Codebuff binary that lets you instantiate a CodebuffClient, load agent definitions, and stream real-time events via WebSocket in any external application.

The @codebuff/sdk package from the CodebuffAI/freebuff repository exposes a pure TypeScript runtime for embedding autonomous coding agents into CLI tools, web servers, or browser-based applications. By leveraging the SDK's WebSocket-based architecture, you can orchestrate agent runs, handle streaming responses, and inject custom tools without managing the underlying binary directly.

Initialize the CodebuffClient

The entry point for any integration is the CodebuffClient class exported from sdk/src/client.ts. This class handles API key resolution, connection health checks, and request construction.

You can instantiate the client with an explicit API key or rely on environment variable detection. According to the source code in sdk/src/client.ts, the constructor accepts a cwd (current working directory) parameter and automatically falls back to process.env.CODEBUFF_API_KEY if no key is provided.

import { CodebuffClient } from '@codebuff/sdk';

const client = new CodebuffClient({
  apiKey: process.env.CODEBUFF_API_KEY, // Optional: auto-detected from env
  cwd: process.cwd()
});

The client maintains a persistent WebSocket connection that is reused across multiple run() calls, making it efficient for long-running server processes.

Load and Configure Agent Definitions

Before running agents, you may need to load agent definitions. The SDK provides two approaches: using bundled agents or supplying custom configurations.

As implemented in sdk/src/agents/load-agents.ts, the loadLocalAgents() function scans for agent definitions in the specified working directory:

import { loadLocalAgents } from '@codebuff/sdk';

const { agents } = await loadLocalAgents({ cwd: process.cwd() });
console.log('Available agents:', agents.map(a => a.id));

If you only use the default base agent, this step is optional. The agents array contains metadata that can be passed to client.run() to select specific behavior profiles.

Execute Runs with Event Streaming

The core execution method is client.run(), defined in sdk/src/run.ts. This method accepts a RunOptions object and returns a promise resolving to the final run state.

Critical parameters include:

  • agent: The agent identifier (e.g., 'base' or a custom ID from loadLocalAgents)
  • prompt: The user instruction string
  • handleEvent: A callback function that receives PrintModeEvent objects for real-time UI updates
  • projectFiles and knowledgeFiles: Optional file snapshots to provide context without disk access
const runState = await client.run({
  agent: 'base',
  prompt: 'Refactor the function foo() to use async/await',
  env: { NODE_ENV: 'production' },
  handleEvent: (evt) => {
    if (evt.type === 'assistant-message') {
      process.stdout.write(evt.content); // Real-time streaming
    }
  }
});

The handleEvent callback receives events throughout the lifecycle, including assistant messages, tool calls, and errors. The final runState.output contains the complete response after the agent finishes execution.

Integration Examples

CLI Embedding

For command-line tools, instantiate the client and stream output directly to stdout. This pattern from sdk/src/index.ts demonstrates minimal embedding:

import { CodebuffClient, loadLocalAgents } from '@codebuff/sdk';

async function main() {
  const client = new CodebuffClient({ cwd: process.cwd() });
  const { agents } = await loadLocalAgents({ cwd: process.cwd() });
  
  const result = await client.run({
    agent: 'base',
    prompt: 'Explain the purpose of src/utils.ts',
    handleEvent: (ev) => {
      if (ev.type === 'assistant-message') {
        process.stdout.write(ev.content);
      }
    }
  });
  
  console.log('\nFinal output:', result.output);
}

main().catch(console.error);

Web Server Integration

In server environments like Express, reuse a singleton client across requests. The SDK supports injection of file contexts via projectFiles and knowledgeFiles parameters:

import express from 'express';
import { CodebuffClient } from '@codebuff/sdk';

const app = express();
app.use(express.json());

const client = new CodebuffClient({ 
  apiKey: process.env.CODEBUFF_API_KEY!, 
  cwd: process.cwd() 
});

app.post('/run-agent', async (req, res) => {
  const { agent = 'base', prompt, projectFiles, knowledgeFiles } = req.body;
  
  const runState = await client.run({
    agent,
    prompt,
    projectFiles,
    knowledgeFiles,
    handleEvent: (evt) => {
      // Log or broadcast events via WebSocket/SSE
      console.log('Event type:', evt.type);
    }
  });
  
  res.json({ output: runState.output });
});

app.listen(3000);

This approach works in serverless or edge environments where the SDK operates in WebSocket-only mode without invoking the local binary.

Extend Functionality with Custom Tools

You can augment agent capabilities by defining custom tools via the customTool helper exported from sdk/src/custom-tool.ts. These tools become available to the agent during execution.

import { CodebuffClient, customTool } from '@codebuff/sdk';
import { execSync } from 'child_process';

const gitBranchTool = customTool({
  name: 'git_branch',
  description: 'Returns the current Git branch name',
  inputSchema: {},
  handler: async () => {
    return execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf8' }).trim();
  }
});

const client = new CodebuffClient({
  cwd: process.cwd(),
  customToolDefinitions: [gitBranchTool]
});

await client.run({
  agent: 'base',
  prompt: 'What branch am I currently on?',
  handleEvent: (ev) => console.log(ev)
});

Custom tools follow the same interface as built-in utilities found in sdk/src/tools/, allowing seamless integration with the agent's decision-making loop.

Summary

  • Instantiate once: Create a CodebuffClient from sdk/src/client.ts with your API key and working directory.
  • Load definitions: Use loadLocalAgents from sdk/src/agents/load-agents.ts to discover available agent profiles.
  • Stream events: Pass a handleEvent callback to client.run() (defined in sdk/src/run.ts) to receive real-time updates via the PrintModeEvent interface.
  • Provide context: Inject projectFiles and knowledgeFiles to give agents codebase awareness without filesystem access.
  • Extend capabilities: Define custom tools using customTool from sdk/src/custom-tool.ts to expose domain-specific functionality.

Frequently Asked Questions

How does the SDK authenticate with the Freebuff API?

The CodebuffClient automatically resolves the API key from the CODEBUFF_API_KEY environment variable, as referenced in @codebuff/common/constants/paths. You can override this by passing an explicit apiKey in the constructor options.

Can I use the Freebuff SDK in a browser environment?

Yes. Because the SDK is pure TypeScript with WebSocket-only mode support, it functions in browser-based applications and edge runtimes where the Codebuff binary cannot be executed via child process. The same client.run() interface works across Node.js and browser contexts.

How do I handle real-time streaming from an agent?

Provide a handleEvent callback in your RunOptions object. This function receives PrintModeEvent objects (defined in @codebuff/common/types/print-mode) throughout the agent's execution lifecycle, enabling you to display partial outputs, progress indicators, or tool call notifications as they occur.

What is the difference between bundled and custom agents?

Bundled agents are pre-defined configurations included with the SDK that you can reference by simple IDs like 'base'. Custom agents are loaded via loadLocalAgents() from sdk/src/agents/load-agents.ts, allowing you to supply your own agent definitions with specialized instructions, tool access, and behavior parameters stored in your project's working directory.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →