src — sdk
src — sdk
The src/sdk/agent-sdk.ts module provides the Agent SDK, a programmatic interface designed to embed Code Buddy's agent capabilities into other applications. It allows developers to define custom tools, provide prompts, and receive structured or streaming responses, effectively turning Code Buddy into an extensible agent.
1. Overview
The Agent SDK acts as a lightweight orchestrator for executing a sequence of operations, primarily involving custom tools and, optionally, an underlying Large Language Model (LLM) for summarization. It abstracts away the complexities of tool parsing, execution, and result aggregation, offering a clean API for agent interaction.
Key features include:
- Custom Tool Registration: Define and register application-specific tools.
- Prompt-driven Tool Invocation: Tools are invoked directly from the user prompt using a specific syntax.
- Synchronous and Streaming Execution: Choose between a single final result or a stream of events.
- LLM Integration: Optionally leverages Code Buddy's LLM client for intelligent summarization of tool outputs.
2. Core Concepts
AgentSDKConfig
This interface defines the configuration options for an AgentSDK instance:
export interface AgentSDKConfig {
model?: string; class="hl-cmt">// The LLM model to use (e.g., 39;grok-1.0-pro39;). Defaults to MODEL_ROLES.fast.xai.
tools?: string[]; class="hl-cmt">// List of built-in tool names (not currently used for execution, but for listing).
maxTurns?: number; class="hl-cmt">// Maximum number of tool invocations allowed per run. Defaults to 10.
systemPrompt?: string; class="hl-cmt">// The system prompt for the LLM. Defaults to 39;You are a helpful coding assistant.39;.
}
SDKToolDefinition
To extend the agent's capabilities, developers can define and register custom tools. Each tool must conform to this interface:
export interface SDKToolDefinition {
name: string;
description: string;
parameters: Record<string, unknown>; class="hl-cmt">// JSON schema for tool input validation (not enforced by SDK, but for LLM context).
execute: (input: Record<string, unknown>) => Promise<string>; class="hl-cmt">// The function that performs the tool39;s action.
}
The execute method is the core logic of the tool, taking a JSON object as input and returning a string representing the tool's output.
SDKStreamEvent
When using the streaming API (runStreaming), the agent yields events of various types:
export interface SDKStreamEvent {
type: 39;text39; | 39;tool_use39; | 39;tool_result39; | 39;done39;;
data: unknown; class="hl-cmt">// The payload associated with the event type.
}
tool_use: Indicates a tool is about to be executed,datacontainsSDKToolInvocation.tool_result: Provides the output of a tool execution,datacontains{ name: string; output: string }.text: The final textual output from the agent.done: Signals the completion of the agent run,datacontainsAgentSDKResultsummary.
Tool Invocation Pattern
The Agent SDK expects tool invocations to be embedded directly within the user prompt using a specific markdown-like syntax:
@tool <tool_name> <JSON_input_object>
For example:
"Please create a file. @tool createFile {"path": "src/index.ts", "content": "console.log('Hello');"}"
The AgentSDK will parse these patterns, extract the tool name and its JSON input, and execute the corresponding registered tool.
3. The AgentSDK Class
The AgentSDK class is the primary entry point for interacting with the agent.
Initialization
export class AgentSDK {
private config: AgentSDKConfig;
private customTools: Map<string, SDKToolDefinition> = new Map();
private client: SDKClient | null = null;
constructor(config: AgentSDKConfig = {}) {
this.config = {
model: config.model ?? MODEL_ROLES.fast.xai,
tools: config.tools ?? [],
maxTurns: config.maxTurns ?? 10,
systemPrompt: config.systemPrompt ?? 39;You are a helpful coding assistant.39;,
};
logger.debug(`AgentSDK initialized with model: ${this.config.model}`);
}
class="hl-cmt">// ...
}
The constructor initializes the agent with a provided configuration, applying sensible defaults if options are omitted. It also sets up internal storage for custom tools.
Public API
run(prompt: string): Promise
Executes the agent loop for a given prompt and returns a single AgentSDKResult object containing the final output, number of tool calls, and estimated cost. This method is suitable for scenarios where only the final outcome is needed.
interface AgentSDKResult {
success: boolean;
output: string;
toolCalls: number;
cost: number;
}
runStreaming(prompt: string): AsyncGenerator
Executes the agent loop and yields SDKStreamEvent objects as the process unfolds. This is ideal for user interfaces or applications that need real-time feedback on tool execution and intermediate steps.
Tool Management
addTool(definition: SDKToolDefinition): void: Registers a custom tool with the SDK. Throws an error if a tool with the same name is already registered.removeTool(name: string): boolean: Unregisters a custom tool by its name. Returnstrueif the tool was found and removed,falseotherwise.getTools(): string[]: Returns a list of all available tool names, including any built-in tools specified in the configuration and all registered custom tools.
Configuration Management
setSystemPrompt(prompt: string): void: Updates the agent's system prompt, which influences the LLM's persona and behavior during summarization.getConfig(): AgentSDKConfig: Returns a copy of the current agent configuration.
Internal Mechanisms
The AgentSDK class encapsulates several private methods that handle the core logic of parsing, executing, and summarizing.
private async getClient(): Promise:
This method is responsible for lazily initializing the CodeBuddyClient (from ../codebuddy/client.js). It checks for the GROK_API_KEY environment variable. If the key is not present, it returns null, indicating that LLM-based summarization cannot be performed.
private parseToolInvocations(prompt: string): SDKToolInvocation[]:
Uses a regular expression (/@tool\s+([a-zA-Z0-9_-]+)\s+({[\s\S]*?})/g) to find and parse @tool invocations within the input prompt. It extracts the tool name and its JSON input, limiting the number of invocations to config.maxTurns.
private async executeToolInvocations(...):
Iterates through the parsed tool invocations. For each invocation, it looks up the corresponding SDKToolDefinition in this.customTools and calls its execute method. It collects the results of each execution.
private async buildFinalOutput(...):
This method constructs the final output string. If an SDKClient (i.e., CodeBuddyClient) is available, it attempts to use the LLM to summarize the original prompt and the tool execution results. If the LLM summary fails or no client is available, it falls back to a simple concatenation of the prompt and tool outputs.
private estimateCost(toolCalls: number, output: string): number:
Provides a very basic estimation of the cost based on the number of tool calls and the length of the final output.
4. Execution Flow
The core execution flow for both run and runStreaming methods follows a similar pattern:
graph TD
A[AgentSDK.run/runStreaming(prompt)] --> B{Prompt Validation}
B -- Valid --> C[parseToolInvocations]
C --> D[executeToolInvocations]
D --> E{getClient (CodeBuddyClient)}
E -- Client Available --> F[buildFinalOutput (LLM Summary)]
E -- No Client --> G[buildFinalOutput (Local Summary)]
F --> H[estimateCost]
G --> H
H --> I[Return AgentSDKResult / Yield SDKStreamEvent]
B -- Invalid --> J[Throw Error]
- Prompt Validation: The input
promptis checked to ensure it's not empty. - Tool Parsing:
parseToolInvocationsextracts all@toolcalls from the prompt. - Tool Execution:
executeToolInvocationssequentially executes each identified tool using its registeredexecutemethod. - Client Retrieval:
getClientattempts to initialize or retrieve theCodeBuddyClientfor LLM interaction. - Output Construction:
buildFinalOutputgenerates the final response. If theCodeBuddyClientis available, it sends the original prompt and tool results to the LLM for a concise summary. Otherwise, it constructs a basic summary from the prompt and tool outputs. - Cost Estimation:
estimateCostcalculates a rudimentary cost based on activity. - Result Delivery: The final
AgentSDKResultis returned (run) orSDKStreamEvents are yielded (runStreaming).
5. Integration and Dependencies
External Dependencies
The AgentSDK module relies on a few internal Code Buddy utilities:
../utils/logger.js: For logging debug and error messages.../config/model-defaults.js: To provide default LLM model names.../codebuddy/client.js: Dynamically imported bygetClient()to interact with the Code Buddy LLM service. This dependency is optional at runtime, as the SDK can operate without an LLM client for tool execution only.
Consumers
The AgentSDK is primarily designed for external integration and testing within the Code Buddy ecosystem:
- Tests:
tests/unit/agent-sdk-tools.test.ts: Unit tests for tool management and execution.tests/features/cicd-chrome-sdk-pr.test.ts: Feature tests demonstrating end-to-end usage, including adding/removing tools, setting prompts, and running streaming.- Automation Scripts:
scripts/tests/cat-automation-sdk.ts: Used in automation scripts, likely for integration testing or specific agent-driven tasks.
These consumers demonstrate the intended usage patterns for developers looking to integrate the Agent SDK into their applications.
6. Usage Example
Here's a conceptual example of how a developer might use the AgentSDK:
import { createAgent, SDKToolDefinition, SDKStreamEvent } from 39;./agent-sdk.js39;;
class="hl-cmt">// 1. Define a custom tool
const myCustomTool: SDKToolDefinition = {
name: 39;greetUser39;,
description: 39;Greets a user by name.39;,
parameters: {
type: 39;object39;,
properties: {
name: { type: 39;string39;, description: 39;The name of the user39; },
},
required: [39;name39;],
},
execute: async (input: Record<string, unknown>): Promise<string> => {
const name = input.name as string;
if (!name) {
throw new Error(39;Name is required for greetUser tool.39;);
}
return `Hello, ${name}! Welcome to the Code Buddy SDK.`;
},
};
async function runMyAgent() {
class="hl-cmt">// 2. Create an AgentSDK instance
const agent = createAgent({
systemPrompt: 39;You are a friendly assistant that can greet users.39;,
maxTurns: 5,
});
class="hl-cmt">// 3. Register the custom tool
agent.addTool(myCustomTool);
class="hl-cmt">// 4. Prepare a prompt that invokes the tool
const prompt = "Please greet John Doe. @tool greetUser {\"name\": \"John Doe\"}";
console.log(39;--- Running agent in streaming mode ---39;);
for await (const event of agent.runStreaming(prompt)) {
switch (event.type) {
case 39;tool_use39;:
console.log(`[STREAM] Using tool: ${(event.data as any).name}`);
break;
case 39;tool_result39;:
console.log(`[STREAM] Tool result: ${(event.data as any).output}`);
break;
case 39;text39;:
console.log(`[STREAM] Final text: ${event.data}`);
break;
case 39;done39;:
console.log(`[STREAM] Agent finished. Success: ${(event.data as any).success}`);
break;
}
}
console.log(39;\n--- Running agent in non-streaming mode ---39;);
const result = await agent.run(prompt);
console.log(`Final Output: ${result.output}`);
console.log(`Tool Calls: ${result.toolCalls}`);
console.log(`Estimated Cost: ${result.cost}`);
}
runMyAgent().catch(console.error);