CodeBuddy Agent SDK
Version Requirements: This documentation is for CodeBuddy Agent SDK v0.1.0 and above. Feature Status: The SDK is currently in Preview stage, and interfaces and behaviors may be adjusted in future versions.
Important: Environment Isolation
The SDK does not load any filesystem configurations by default, including settings.json, CODEBUDDY.md, MCP servers, sub-agents, slash commands, Rules, and Skills. This is a key difference from direct CLI usage, ensuring SDK application behavior is completely controlled by code with predictability and consistency.
To load these configurations, use the settingSources option to explicitly specify. See Environment Isolation section for details.
CodeBuddy Agent SDK allows you to programmatically control CodeBuddy Agent in your applications. It supports TypeScript/JavaScript and Python, enabling scenarios such as automated task execution, custom permission control, and building AI-powered development tools.
Why Use the SDK
CodeBuddy Agent SDK gives you programmatic access to all of CodeBuddy's capabilities, not just through command-line interaction.
Beyond Command Line
- Programmatic Control: Embed AI programming assistants in your applications to implement automated workflows
- Custom Interactions: Build user interfaces and interaction methods that suit your needs
- Batch Processing: Perform batch AI operations on multiple files or projects
- Integrate with Existing Systems: Seamlessly integrate AI capabilities into CI/CD, IDE plugins, or other development tools
Fine-grained Control
- Permission Management: Implement enterprise-level permission policies through
canUseToolcallbacks - Behavior Customization: Use the Hook system to intercept and modify Agent behavior
- Resource Limits: Control token consumption, execution time, and budget
- Session Management: Persist and restore conversation contexts
Extended Capabilities
- Custom Agents: Create specialized sub-agents to handle domain-specific tasks
- MCP Integration: Connect custom tools and services
- Multi-model Support: Flexibly switch and configure different AI models
What You Can Build
Development Tool Enhancements
- IDE Plugins: Build intelligent programming assistants for VS Code, JetBrains, and other IDEs
- Code Review Tools: Automate code quality checks and security scans
- Documentation Generators: Automatically generate API documentation, READMEs, and code comments
Automated Workflows
- CI/CD Integration: Perform intelligent code analysis and fixes in pipelines
- Test Generation: Automatically generate unit tests and integration tests
- Refactoring Assistants: Batch execute code refactoring and migration tasks
Enterprise Applications
- Internal Development Platforms: Build enterprise-level AI programming platforms
- Knowledge Base Q&A: Intelligent Q&A systems based on codebases
- Training Tools: Interactive programming learning and code review systems
Feature Overview
- Message Streaming: Receive system messages, assistant responses, and tool call results in real-time
- Multi-turn Conversations: Support maintaining conversation context across multiple reasoning calls
- Session Management: Continue or resume existing conversations through session IDs
- Permission Control: Fine-grained tool access permission management
- Hook System: Insert custom logic before and after tool execution
- Custom Agents: Define specialized sub-agents to handle specific tasks
- MCP Integration: Support configuring custom MCP servers to extend functionality
Installation
bash
npm install @tencent-ai/agent-sdk
# or
yarn add @tencent-ai/agent-sdk
# or
pnpm add @tencent-ai/agent-sdkbash
uv add codebuddy-agent-sdk
# or
pip install codebuddy-agent-sdkEnvironment Requirements
| Language | Version Requirement |
|---|---|
| TypeScript/JavaScript | Node.js >= 18.20 |
| Python | Python >= 3.10 |
Authentication Configuration
Using Existing Login Credentials
If you've already completed interactive login in the terminal via the codebuddy command, the SDK will automatically use that authentication information without additional configuration.
Using API Key
If not logged in or need to use different credentials, you can authenticate via API Key:
bash
export CODEBUDDY_API_KEY="your-api-key"Get API Key:
| Edition | Get API Key at |
|---|---|
| International | https://www.codebuddy.ai/profile/keys |
| China | https://copilot.tencent.com/profile/ |
| iOA | https://tencent.sso.copilot.tencent.com/profile/keys |
Note: When using
CODEBUDDY_API_KEY, you must correctly configure theCODEBUDDY_INTERNET_ENVIRONMENTenvironment variable based on your edition:
- International: Do not set (default)
- China:
export CODEBUDDY_INTERNET_ENVIRONMENT=internal- iOA:
export CODEBUDDY_INTERNET_ENVIRONMENT=ioaSee Identity and Access Management documentation for details.
You can also pass it in code through the env option:
typescript
const q = query({
prompt: '...',
options: {
env: {
CODEBUDDY_API_KEY: process.env.MY_API_KEY,
// China edition users need to set:
// CODEBUDDY_INTERNET_ENVIRONMENT: 'internal'
// iOA edition users need to set:
// CODEBUDDY_INTERNET_ENVIRONMENT: 'ioa'
}
}
});python
options = CodeBuddyAgentOptions(
env={
"CODEBUDDY_API_KEY": os.environ.get("MY_API_KEY"),
# China edition users need to set:
# "CODEBUDDY_INTERNET_ENVIRONMENT": "internal"
# iOA edition users need to set:
# "CODEBUDDY_INTERNET_ENVIRONMENT": "ioa"
}
)Enterprise Users: OAuth Client Credentials
Currently only introducing the Client Credentials authorization method, suitable for server-side applications and CI/CD scenarios.
Enterprise users need to first obtain an access token through the OAuth 2.0 Client Credentials flow, then pass it to the SDK.
Step 1: Create Application to Obtain Credentials
Refer to Enterprise Developer Quick Start to create an application and obtain Client ID and Client Secret.
Step 2: Get Token and Call SDK
typescript
async function getOAuthToken(clientId: string, clientSecret: string): Promise<string> {
const response = await fetch('https://copilot.tencent.com/oauth2/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: clientId,
client_secret: clientSecret,
}),
});
const data = await response.json();
return data.access_token;
}
// Get token and call SDK
const token = await getOAuthToken('your-client-id', 'your-client-secret');
for await (const msg of query({
prompt: 'Hello',
options: {
env: { CODEBUDDY_AUTH_TOKEN: token },
},
})) {
console.log(msg);
}python
import httpx
from codebuddy_agent_sdk import query, CodeBuddyAgentOptions
async def get_oauth_token(client_id: str, client_secret: str) -> str:
async with httpx.AsyncClient() as client:
response = await client.post(
"https://copilot.tencent.com/oauth2/token",
data={
"grant_type": "client_credentials",
"client_id": client_id,
"client_secret": client_secret,
},
)
return response.json()["access_token"]
# Get token and call SDK
token = await get_oauth_token("your-client-id", "your-client-secret")
options = CodeBuddyAgentOptions(
env={"CODEBUDDY_AUTH_TOKEN": token}
)
async for msg in query(prompt="Hello", options=options):
print(msg)For detailed authentication configuration instructions, please refer to Identity Authentication.
Other Environment Variables
| Variable Name | Description | Required |
|---|---|---|
CODEBUDDY_CODE_PATH | Path to CodeBuddy CLI executable | Optional |
If not set, the SDK will automatically attempt to locate the CLI.
Basic Usage
Simple Query
The most basic usage is to send a prompt and handle the response:
typescript
import { query } from '@tencent-ai/agent-sdk';
async function main() {
const q = query({
prompt: 'Please explain what a recursive function is',
options: {
permissionMode: 'bypassPermissions'
}
});
for await (const message of q) {
if (message.type === 'assistant') {
for (const block of message.message.content) {
if (block.type === 'text') {
console.log(block.text);
}
}
}
}
}
main();python
import asyncio
from codebuddy_agent_sdk import query, CodeBuddyAgentOptions
from codebuddy_agent_sdk import AssistantMessage, TextBlock
async def main():
options = CodeBuddyAgentOptions(
permission_mode="bypassPermissions"
)
async for message in query(prompt="Please explain what a recursive function is", options=options):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(block.text)
asyncio.run(main())Extracting Results
After the query completes, you'll receive a result message containing execution statistics:
typescript
for await (const message of q) {
if (message.type === 'result') {
if (message.subtype === 'success') {
console.log('Complete! Duration:', message.duration_ms, 'ms');
console.log('Cost:', message.total_cost_usd, 'USD');
} else {
console.log('Execution error');
}
}
}python
from codebuddy_agent_sdk import ResultMessage
async for message in query(prompt="...", options=options):
if isinstance(message, ResultMessage):
if message.subtype == "success":
print(f"Complete! Duration: {message.duration_ms} ms")
print(f"Cost: {message.total_cost_usd} USD")
else:
print("Execution error")Message Type Handling
The SDK returns multiple types of messages:
typescript
for await (const message of q) {
switch (message.type) {
case 'system':
// Session initialization message
console.log('Session ID:', message.session_id);
console.log('Available tools:', message.tools);
break;
case 'assistant':
// AI assistant response
for (const block of message.message.content) {
if (block.type === 'text') {
console.log('[Text]', block.text);
} else if (block.type === 'tool_use') {
console.log('[Tool Call]', block.name, block.input);
} else if (block.type === 'tool_result') {
console.log('[Tool Result]', block.content);
}
}
break;
case 'result':
// Query complete
console.log('Execution complete, duration:', message.duration_ms, 'ms');
break;
}
}python
from codebuddy_agent_sdk import (
SystemMessage, AssistantMessage, ResultMessage,
TextBlock, ToolUseBlock, ToolResultBlock
)
async for message in query(prompt="...", options=options):
if isinstance(message, SystemMessage):
# Session initialization message
print(f"Session ID: {message.data.get('session_id')}")
print(f"Available tools: {message.data.get('tools')}")
elif isinstance(message, AssistantMessage):
# AI assistant response
for block in message.content:
if isinstance(block, TextBlock):
print(f"[Text] {block.text}")
elif isinstance(block, ToolUseBlock):
print(f"[Tool Call] {block.name}: {block.input}")
elif isinstance(block, ToolResultBlock):
print(f"[Tool Result] {block.content}")
elif isinstance(message, ResultMessage):
# Query complete
print(f"Execution complete, duration: {message.duration_ms} ms")Configuration Options
Permission Mode
Control tool invocation permission behavior through permissionMode:
| Mode | Description |
|---|---|
default | Default mode, all operations require confirmation |
acceptEdits | Automatically approve file edits, Bash still requires confirmation |
plan | Plan mode, only read operations allowed |
bypassPermissions | Skip all permission checks (use with caution) |
typescript
const q = query({
prompt: 'Analyze project structure',
options: {
permissionMode: 'plan' // Read-only mode
}
});python
options = CodeBuddyAgentOptions(
permission_mode="plan" # Read-only mode
)
async for msg in query(prompt="Analyze project structure", options=options):
passWorking Directory
Specify the Agent's working directory:
typescript
const q = query({
prompt: 'Read package.json',
options: {
cwd: '/path/to/project'
}
});python
options = CodeBuddyAgentOptions(
cwd="/path/to/project"
)Model Selection
Specify the AI model to use:
typescript
const q = query({
prompt: '...',
options: {
model: 'deepseek-v3.1',
fallbackModel: 'deepseek-v3.1'
}
});python
options = CodeBuddyAgentOptions(
model="deepseek-v3.1",
fallback_model="deepseek-v3.1"
)Resource Limits
Limit execution scope:
typescript
const q = query({
prompt: '...',
options: {
maxTurns: 20 // Maximum conversation turns
}
});python
options = CodeBuddyAgentOptions(
max_turns=20, # Maximum conversation turns
)Environment Isolation (settingSources)
Design Philosophy
The SDK does not load any filesystem configurations by default, providing a completely clean runtime environment. This is a key difference from direct CLI usage.
Why This Design?
- Predictability: SDK application behavior is completely controlled by code, unaffected by user or project configuration files
- Isolation: Avoid user preferences or project settings interfering with SDK application logic
- Security: Sensitive configurations (such as hooks, permission rules) won't accidentally leak into the SDK environment
- Consistency: Behavior remains consistent when running on different machines
Default Behavior Comparison
| Scenario | Settings | Memory | MCP | Subagent | Commands | Rules | Skills |
|---|---|---|---|---|---|---|---|
| SDK Call (default) | ✗ Not loaded | ✗ Not loaded | ✗ Not loaded | ✗ Not loaded | ✗ Not loaded | ✗ Not loaded | ✗ Not loaded |
| CLI Direct Run | ✓ Load all | ✓ Load all | ✓ Load all | ✓ Load all | ✓ Load all | ✓ Load all | ✓ Load all |
Configuration File Location Reference:
| Config Type | User Level Location | Project Level Location | Description |
|---|---|---|---|
| Settings | ~/.codebuddy/settings.json | .codebuddy/settings.json | Permissions, hooks, environment variables, etc. |
| Memory | ~/.codebuddy/CODEBUDDY.md | CODEBUDDY.md | Project instructions and context |
| MCP | ~/.codebuddy/.mcp.json | .mcp.json | MCP server configuration |
| Subagent | ~/.codebuddy/agents/ | .codebuddy/agents/ | Custom sub-agents |
| Commands | ~/.codebuddy/commands/ | .codebuddy/commands/ | Custom slash commands |
| Rules | ~/.codebuddy/rules/ | .codebuddy/rules/ | Modular rule files |
| Skills | ~/.codebuddy/skills/ | .codebuddy/skills/ | AI auto-invoked skills |
Explicitly Loading Configuration
To load filesystem configurations, use settingSources to explicitly specify:
typescript
const q = query({
prompt: '...',
options: {
// Load project configuration (.codebuddy/settings.json, CODEBUDDY.md)
settingSources: ['project'],
// Or load all configurations
// settingSources: ['user', 'project', 'local']
}
});python
options = CodeBuddyAgentOptions(
# Load project configuration
setting_sources=["project"],
# Or load all configurations
# setting_sources=["user", "project", "local"]
)Configuration Source Descriptions
| Value | Description | Location |
|---|---|---|
'user' | Global user settings | ~/.codebuddy/settings.json, ~/.codebuddy/CODEBUDDY.md |
'project' | Project shared settings | .codebuddy/settings.json, CODEBUDDY.md |
'local' | Project local settings | .codebuddy/settings.local.json, CODEBUDDY.local.md |
Typical Use Cases
CI/CD Environment:
typescript
// Only load project configuration, ignore user and local configurations
const q = query({
prompt: 'Run tests',
options: {
settingSources: ['project'],
permissionMode: 'bypassPermissions'
}
});python
# Only load project configuration, ignore user and local configurations
options = CodeBuddyAgentOptions(
setting_sources=["project"],
permission_mode="bypassPermissions"
)Fully Programmatic Control:
typescript
// Default behavior: Don't load any configuration
// All behavior explicitly defined through options
const q = query({
prompt: '...',
options: {
agents: { /* custom agent */ },
mcpServers: { /* custom MCP */ },
allowedTools: ['Read', 'Grep', 'Glob']
}
});python
# Default behavior: Don't load any configuration
# All behavior explicitly defined through options
options = CodeBuddyAgentOptions(
agents={"reviewer": AgentDefinition(...)},
mcp_servers={"db": {...}},
allowed_tools=["Read", "Grep", "Glob"]
)Permission Control
canUseTool Callback
Implement fine-grained permission control through the canUseTool callback:
typescript
import { query } from '@tencent-ai/agent-sdk';
const q = query({
prompt: 'Analyze project structure',
options: {
canUseTool: async (toolName, input, options) => {
// Only allow read-only tools
const readOnlyTools = ['Read', 'Glob', 'Grep'];
if (readOnlyTools.includes(toolName)) {
return {
behavior: 'allow',
updatedInput: input
};
}
// Deny other tools
return {
behavior: 'deny',
message: `Tool ${toolName} is not allowed`
};
}
}
});python
from codebuddy_agent_sdk import (
query, CodeBuddyAgentOptions,
CanUseToolOptions, PermissionResultAllow, PermissionResultDeny
)
async def can_use_tool(
tool_name: str,
input_data: dict,
options: CanUseToolOptions
):
# Only allow read-only tools
read_only_tools = ["Read", "Glob", "Grep"]
if tool_name in read_only_tools:
return PermissionResultAllow(updated_input=input_data)
# Deny other tools
return PermissionResultDeny(
message=f"Tool {tool_name} is not allowed"
)
options = CodeBuddyAgentOptions(can_use_tool=can_use_tool)Intercepting Dangerous Operations
Combine permission callbacks to intercept dangerous commands:
typescript
const dangerousCommands = ['rm -rf', 'sudo', 'chmod 777'];
const q = query({
prompt: 'Clean temporary files',
options: {
canUseTool: async (toolName, input) => {
if (toolName === 'Bash') {
const command = input.command as string;
for (const dangerous of dangerousCommands) {
if (command.includes(dangerous)) {
return {
behavior: 'deny',
message: `Dangerous command intercepted: ${dangerous}`,
interrupt: true // Interrupt the entire session
};
}
}
}
return { behavior: 'allow', updatedInput: input };
}
}
});python
dangerous_commands = ["rm -rf", "sudo", "chmod 777"]
async def can_use_tool(tool_name, input_data, options):
if tool_name == "Bash":
command = input_data.get("command", "")
for dangerous in dangerous_commands:
if dangerous in command:
return PermissionResultDeny(
message=f"Dangerous command intercepted: {dangerous}",
interrupt=True # Interrupt the entire session
)
return PermissionResultAllow(updated_input=input_data)Multi-turn Conversations
Using Session/Client API
For scenarios requiring multi-turn interactions, use the Session (TypeScript) or Client (Python) API:
typescript
import { unstable_v2_createSession } from '@tencent-ai/agent-sdk';
async function main() {
const session = unstable_v2_createSession({
model: 'deepseek-v3.1'
});
// First turn
await session.send('Analyze this project\'s architecture');
for await (const message of session.stream()) {
console.log(message);
}
// Second turn (maintaining context)
await session.send('Please explain point three in detail');
for await (const message of session.stream()) {
console.log(message);
}
session.close();
}python
from codebuddy_agent_sdk import CodeBuddySDKClient, CodeBuddyAgentOptions
async def main():
options = CodeBuddyAgentOptions(model="deepseek-v3.1")
async with CodeBuddySDKClient(options=options) as client:
# First turn
await client.query("Analyze this project's architecture")
async for message in client.receive_response():
print(message)
# Second turn (maintaining context)
await client.query("Please explain point three in detail")
async for message in client.receive_response():
print(message)
asyncio.run(main())Interrupting Execution
Interrupt execution during runtime:
typescript
const q = query({ prompt: 'Execute long-running task...' });
let count = 0;
for await (const message of q) {
if (message.type === 'assistant') {
for (const block of message.message.content) {
if (block.type === 'tool_use') {
count++;
if (count >= 10) {
await q.interrupt(); // Interrupt execution
break;
}
}
}
}
}python
async with CodeBuddySDKClient(options=options) as client:
await client.query("Execute long-running task...")
count = 0
async for message in client.receive_messages():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, ToolUseBlock):
count += 1
if count >= 10:
await client.interrupt() # Interrupt execution
breakHook System
Hooks allow you to insert custom logic before and after tool execution.
PreToolUse Hook
Intercept and handle before tool execution:
typescript
const q = query({
prompt: 'Clean temporary files',
options: {
hooks: {
PreToolUse: [{
matcher: 'Bash', // Only match Bash tool
hooks: [
async (input, toolUseId) => {
console.log('About to execute command:', input.command);
// Can block execution
if (input.command.includes('rm')) {
return {
decision: 'block',
reason: 'Delete command blocked'
};
}
return { continue: true };
}
]
}]
}
}
});python
from codebuddy_agent_sdk import HookMatcher, HookContext
async def pre_tool_hook(input_data, tool_use_id, context: HookContext):
print(f"About to execute command: {input_data.get('command')}")
# Can block execution
if "rm" in input_data.get("command", ""):
return {"continue_": False, "reason": "Delete command blocked"}
return {"continue_": True}
options = CodeBuddyAgentOptions(
hooks={
"PreToolUse": [
HookMatcher(matcher="Bash", hooks=[pre_tool_hook])
]
}
)Hook Event Types
| Event | Trigger Timing |
|---|---|
PreToolUse | Before tool execution |
PostToolUse | After successful tool execution |
PostToolUseFailure | After failed tool execution |
UserPromptSubmit | User submits prompt |
SessionStart | Session starts |
SessionEnd | Session ends |
Extended Capabilities
Custom Agents
Define specialized sub-agents:
typescript
const q = query({
prompt: 'Use code-reviewer to review code',
options: {
agents: {
'code-reviewer': {
description: 'Professional code review assistant',
tools: ['Read', 'Glob', 'Grep'], // Read-only
disallowedTools: ['Bash', 'Write', 'Edit'],
prompt: `You are a code review expert. Please check:
1. Code standards
2. Potential bugs
3. Performance issues
4. Security vulnerabilities`,
model: 'deepseek-v3.1'
}
}
}
});python
from codebuddy_agent_sdk import AgentDefinition
options = CodeBuddyAgentOptions(
agents={
"code-reviewer": AgentDefinition(
description="Professional code review assistant",
tools=["Read", "Glob", "Grep"], # Read-only
disallowed_tools=["Bash", "Write", "Edit"],
prompt="""You are a code review expert. Please check:
1. Code standards
2. Potential bugs
3. Performance issues
4. Security vulnerabilities""",
model="deepseek-v3.1"
)
}
)MCP Server Configuration
Integrate custom MCP servers:
typescript
const q = query({
prompt: 'Query database',
options: {
mcpServers: {
'database': {
type: 'stdio',
command: 'node',
args: ['./mcp-servers/db-server.js'],
env: {
DB_HOST: 'localhost',
DB_PORT: '5432'
}
}
}
}
});python
options = CodeBuddyAgentOptions(
mcp_servers={
"database": {
"type": "stdio",
"command": "node",
"args": ["./mcp-servers/db-server.js"],
"env": {
"DB_HOST": "localhost",
"DB_PORT": "5432"
}
}
}
)Handling AskUserQuestion
AI may ask users questions through the AskUserQuestion tool, which can be handled in the permission callback:
typescript
const q = query({
prompt: 'Configure database connection',
options: {
canUseTool: async (toolName, input) => {
if (toolName === 'AskUserQuestion') {
const questions = input.questions as any[];
const answers: Record<string, string> = {};
for (const q of questions) {
console.log(`Question: ${q.question}`);
// Can integrate actual user interaction here
answers[q.question] = q.options[0].label;
}
return {
behavior: 'allow',
updatedInput: { ...input, answers }
};
}
return { behavior: 'allow', updatedInput: input };
}
}
});python
async def can_use_tool(tool_name, input_data, options):
if tool_name == "AskUserQuestion":
questions = input_data.get("questions", [])
answers = {}
for q in questions:
print(f"Question: {q['question']}")
# Can integrate actual user interaction here
answers[q["question"]] = q["options"][0]["label"]
return PermissionResultAllow(
updated_input={**input_data, "answers": answers}
)
return PermissionResultAllow(updated_input=input_data)Error Handling
typescript
import { query, AbortError } from '@tencent-ai/agent-sdk';
try {
const q = query({ prompt: '...' });
for await (const message of q) {
// ...
}
} catch (error) {
if (error instanceof AbortError) {
console.log('Operation aborted');
} else {
console.error('Error occurred:', error);
}
}python
from codebuddy_agent_sdk import (
query, CodeBuddySDKError,
CLIConnectionError, CLINotFoundError
)
try:
async for message in query(prompt="..."):
pass
except CLINotFoundError as e:
print(f"CLI not found: {e}")
except CLIConnectionError as e:
print(f"Connection failed: {e}")
except CodeBuddySDKError as e:
print(f"SDK error: {e}")Best Practices
- Permission Control: In production environments, use
canUseToolfor fine-grained permissions, avoid usingbypassPermissions - Resource Limits: Use
maxTurnsto limit execution scope and prevent unexpected resource consumption - Error Handling: Always handle error states in
resultmessages - Hook Timeouts: Set reasonable timeout values for Hooks
Related Documentation
- TypeScript SDK Reference - Detailed TypeScript API reference
- Python SDK Reference - Detailed Python API reference
- Hook Reference Guide - Detailed Hook configuration instructions
- MCP Integration - MCP server configuration guide
- Sub-Agent System - Detailed sub-agent explanation
CodeBuddy Agent SDK - Integrate AI programming capabilities into your applications