Skip to main content

Creating Agents

This subsection is about authoring agents. If you want an AI specialised for your codebase, your domain, your coding conventions — this is where you build it.

For extending existing agents without writing a new one, see Agent Extensions.

What a custom agent is

An agent is a program the server spawns when there's work to do. It:

  1. Receives a task (a chat message, a delegation from another agent, a trigger from a flow).
  2. Runs a loop: assemble context → call the LLM → execute tool calls → reflect → repeat.
  3. Eventually reports done, fails, or gets interrupted.

Every agent runs in its own process, supervised by AgentProcessManager. This isolation means your agent can crash, hang, or leak memory without affecting anything else.

Agent lifecycle

Agent lifecycle: trigger through cleanupTRIGGERSTAGE 2SPAWNprocess forked · heartbeat startsSTAGE 3BOOTyour handler startsAGENT LOOPDELIBERATEDELIBERATEassemble context · call LLMEXECUTEEXECUTErun tool callsREFLECTREFLECTwrite memory · decide to loopMOREWORKDONETERMINATESTAGE 6 — after any terminal state, the server runs cleanup: process reaped · event log finalized · memory ingestedTERMINATECOMPLETEDreturned a resultFAILEDthrew or set failureREJECTEDguardrail deniedKILLEDtimeout · budget

For the full breakdown of each stage, see Lifecycle.

How a run works end-to-end

Agent run end-to-end: spawn, LLM proxy, tool execution, UI broadcast, chat reply01 · SPAWN02 · LLM CALL03 · TOOL EXEC04 · LLM CALL05 · DELIVERAGENT LOOPSEND MESSAGESTART RUNFORK PROCESSuserMsg · auto-pushInference · llm.chat()API REQUESTLLM RESP · tool_calls[]LLM RESP · tool_calls[]LLM STREAMEXECUTE read_filetool resultTOOL ACTIVITYInference · + resultAPI REQUESTFINAL ANSWERFINAL ANSWERLLM STREAMsendMessage(text)CHAT MESSAGEEXTClientbrowser · IDESRVServerllmService · toolServicePROCAgentyour handlerAPILLM ProviderOpenAI · AnthropicLEGENDFocal actorActiveRequest / callReturn / asyncUI broadcastPrimary response

The Server is the execution hub. When the agent process connects, the server auto-pushes the user message immediately — the agent never polls for it. Every LLM call is proxied through llmService to the LLM Provider; every tool call runs on the server via executeToolService. After each operation, sendResponseAndNotify fires both at once: the result goes to the Agent over WebSocket, and a broadcast goes to the Client UI — so tool activity and streaming output appear in the chat panel in real time.

Six creation paths

There are six ways to bring an agent into Codebolt, from lowest to highest authorship cost:

PathWhat you writeGood for
Level 0 — RemixA YAML file overriding an existing agent's prompt, model, or toolsMost use cases. Start here.
Level 1 — FrameworkA small TypeScript file using the Codebolt agent frameworkCustom behaviour with batteries included
Level 2 — codeboltjsCode directly against the codeboltjs SDKNon-standard loop shape
Level 3 — Raw WebSocketThe wire protocol directlyLanguages the SDK doesn't cover
Flow — Visual BuilderA drag-and-drop node graph in the Agent Flow editorPrototyping, pipeline agents, non-engineer authoring
Third-Party WrappingAn adapter around an existing external agentRunning Claude Code / Codex / Cursor etc. as a Codebolt agent

The most important thing to know: level 0 and level 1 cover the vast majority of real use cases. Level 2 is for people building infrastructure on top of Codebolt. Level 3 is for people writing Codebolt agents in Go, Rust, or Python. The visual flow builder is best for pipeline-style agents or rapid prototyping. Don't start higher than you need.

Hello World agent

The simplest possible agent — listens for a user message and replies:

codebolt agent create --framework --name hello-world

Open hello-world/src/index.ts and replace its contents with:

import codebolt from '@codebolt/codeboltjs';

codebolt.onMessage(async (reqMessage) => {
codebolt.chat.sendMessage(`Hello World! You said: ${reqMessage.userMessage}`);
});

Build and run:

cd hello-world
npm install
npm run build

Send any message from the chat — you'll see the echo in reply.

codebolt.onMessage() is the standard entry point for all agents. It registers a handler that fires when the user sends a message. The WebSocket connection is handled automatically — you don't need to call any connection method yourself.

For a real agent with LLM loop, use level 1 with CodeboltAgent:

import codebolt from '@codebolt/codeboltjs';
import { CodeboltAgent } from '@codebolt/agent/unified';

const agent = new CodeboltAgent({
instructions: 'You are a helpful coding assistant.',
});

codebolt.onMessage(async (reqMessage) => {
const result = await agent.processMessage(reqMessage);
return result.finalMessage;
});

The framework (level 1) in one paragraph

At level 1 you write a small TypeScript file that:

  • Declares a codeboltagent.yaml with metadata (name, description, default model, allowed tools).
  • Exports a handler that receives a typed input and returns a typed output.
  • Optionally uses CodeboltAgent or Agent from @codebolt/agent/unified (see Patterns) to get the full agent loop without writing boilerplate.
  • Optionally registers processors that modify the prompt assembly, tool calling, or response handling (see Processors).

The framework handles: the agent loop, context assembly, tool validation, memory writes, event log integration, heartbeats. You handle: what makes your agent different from the default.

Then your agent consumes Agent Extensions — skills, action blocks, MCP tools — the same way every other agent does.

Agent patterns

You don't write the agent loop from scratch at level 1. You pick a pattern:

  • CodeboltAgent — the recommended default. Batteries-included with a default message modifier pipeline, compaction, and tool management. Start here.
  • Processor Pattern — customize the pipeline by plugging processors from @codebolt/agent/processor-pieces into CodeboltAgent's five slots.
  • Agent — the bare class with no default modifiers. Use when you need full control over which processors run.
  • Building blocksInitialPromptGenerator, AgentStep, ResponseExecutor can be used directly when you need a custom loop shape.

Pick CodeboltAgent unless you have a specific reason not to.

The anatomy of an agent

Every agent has:

  • A codeboltagent.yaml — metadata, manifest, entry points. See agent.yaml reference.
  • A lifecycle — start, deliberate, execute, reflect, terminate. See Lifecycle.
  • A context — the working state carried across phases. See Context.

These are the same for every agent regardless of level. The levels differ in how much code you write to express them.

Testing and debugging

Agents are programs; test them like programs.

  • Agent Debug Panel — real-time logs for all running agents, with full history. See Testing and debugging.
  • Unit tests — processors and tools are plain objects with a modify or execute method. Test them directly.
  • Console logging — everything your agent writes to stdout/stderr is captured and shown in the debug panel.

Publishing

When you're ready, your agent can be:

  • Private to your project — lives in .codebolt/agents/. No publishing needed.
  • Published to the CodeBolt registrycodebolt agent publish. See Publishing.

See also