settings.json, CODEBUDDY.md, MCP servers, subagents, slash commands, Rules, and Skills. This is a key difference from using the CLI directly, ensuring that the behavior of SDK applications is fully controlled by code, with predictability and consistency.settingSources option to explicitly specify them. canUseTool callback.npm install @tencent-ai/agent-sdk# oryarn add @tencent-ai/agent-sdk# orpnpm add @tencent-ai/agent-sdk
uv add codebuddy-agent-sdk# orpip install codebuddy-agent-sdk
Language | Version Requirement |
TypeScript/JavaScript | Node.js >= 18.20 |
Python | Python >= 3.10 |
codebuddy command, the SDK automatically uses that authentication information, requiring no additional configuration.export CODEBUDDY_API_KEY="your-api-key"
Version | Obtaining the Address |
Overseas Edition | |
China Edition |
CODEBUDDY_API_KEY, you must correctly configure the CODEBUDDY_INTERNET_ENVIRONMENT environment variable according to your version:export CODEBUDDY_INTERNET_ENVIRONMENT=internalexport CODEBUDDY_INTERNET_ENVIRONMENT=ioaexport CODEBUDDY_INTERNET_ENVIRONMENT=cloudhostedexport CODEBUDDY_INTERNET_ENVIRONMENT=selfhostedenv option:const q = query({prompt: '...',options: {// Required only for Dedicated edition or self-hosted deployment users. Enter your enterprise service address:// endpoint: 'https://your-company.copilot.qq.com',env: {CODEBUDDY_API_KEY: process.env.MY_API_KEY,// Required for China edition users:// CODEBUDDY_INTERNET_ENVIRONMENT: 'internal'// Required for iOA edition users:// CODEBUDDY_INTERNET_ENVIRONMENT: 'ioa'// Required for Dedicated edition users:// CODEBUDDY_INTERNET_ENVIRONMENT: 'cloudhosted'// Required for self-hosted deployment users:// CODEBUDDY_INTERNET_ENVIRONMENT: 'selfhosted'}}});
options = CodeBuddyAgentOptions(# Required only for Dedicated edition or self-hosted deployment users. Enter your enterprise service address:# endpoint="https://your-company.copilot.qq.com",env={"CODEBUDDY_API_KEY": os.environ.get("MY_API_KEY"),# Required for China edition users:# "CODEBUDDY_INTERNET_ENVIRONMENT": "internal"# Required for iOA edition users:# "CODEBUDDY_INTERNET_ENVIRONMENT": "ioa"# Required for Dedicated edition users:# "CODEBUDDY_INTERNET_ENVIRONMENT": "cloudhosted"# Required for self-hosted deployment users:# "CODEBUDDY_INTERNET_ENVIRONMENT": "selfhosted"})
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;}// Obtain a token and call the SDKconst 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);}
import httpxfrom codebuddy_agent_sdk import query, CodeBuddyAgentOptionsasync 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"]# Obtain a token and call the SDKtoken = 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)
Variable Name | Description | Required |
CODEBUDDY_CODE_PATH | CodeBuddy CLI executable file path | Optional |
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();
import asynciofrom codebuddy_agent_sdk import query, CodeBuddyAgentOptionsfrom codebuddy_agent_sdk import AssistantMessage, TextBlockasync 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())
result message is received, which contains execution statistics:for await (const message of q) {if (message.type === 'result') {if (message.subtype === 'success') {console.log('Done! Duration:', message.duration_ms, 'ms');console.log('Cost:', message.total_cost_usd, 'USD');} else {console.log('Execution error');}}}
from codebuddy_agent_sdk import ResultMessageasync for message in query(prompt="...", options=options):if isinstance(message, ResultMessage):if message.subtype == "success":print(f"Done! Duration: {message.duration_ms} ms")print(f"Cost: {message.total_cost_usd} USD")else:print("Execution error")
for await (const message of q) {switch (message.type) {case 'system':// Session initialization messageconsole.log('Session ID:', message.session_id);console.log('Available tools:', message.tools);break;case 'assistant':// AI assistant responsefor (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 completedconsole.log('Execution completed. Duration:', message.duration_ms, 'ms');break;}}
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 messageprint(f"Session ID: {message.data.get('session_id')}")print(f"Available tools: {message.data.get('tools')}")elif isinstance(message, AssistantMessage):# AI assistant responsefor 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 completedprint(f"Execution completed. Duration: {message.duration_ms} ms")
permissionMode:Mode | Description |
default | Default mode. All operations require confirmation. |
acceptEdits | Automatically approves file edits. Bash still requires confirmation. |
plan | Planning mode. Only read operations are allowed. |
bypassPermissions | Skip all permission checks. Use with caution. |
const q = query({prompt: 'Analyze the project structure',options: {permissionMode: 'plan' // Read-only mode}});
options = CodeBuddyAgentOptions(permission_mode="plan" # Read-only mode)async for msg in query(prompt="Analyze the project structure", options=options):pass
const q = query({prompt: 'Read package.json',options: {cwd: '/path/to/project'}});
options = CodeBuddyAgentOptions(cwd="/path/to/project")
const q = query({prompt: '...',options: {model: 'deepseek-v3.1',fallbackModel: 'deepseek-v3.1'}});
options = CodeBuddyAgentOptions(model="deepseek-v3.1",fallback_model="deepseek-v3.1")
const q = query({prompt: '...',options: {maxTurns:20 // Maximum number of conversation turns}});
options = CodeBuddyAgentOptions(max_turns=20, # Maximum number of conversation turns)
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 |
Direct CLI run | ✔ Load all | ✔ Load all | ✔ Load all | ✔ Load all | ✔ Load all | ✔ Load all | ✔ Load all |
Configuration Type | User-Level Location | Project-Level Location | Description |
Settings | ~/.codebuddy/settings.json | .codebuddy/settings.json | Permissions, hooks, Environment Variables, and More |
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 subagents |
Commands | ~/.codebuddy/commands/ | .codebuddy/commands/ | Custom slash commands |
Rules | ~/.codebuddy/rules/ | .codebuddy/rules/ | Modular rule files |
Skills | ~/.codebuddy/skills/ | .codebuddy/skills/ | Skills automatically invoked by AI |
settingSources to explicitly specify them:const q = query({prompt: '...',options: {// Load project configuration (.codebuddy/settings.json, CODEBUDDY.md)settingSources: ['project'],// Or load all configurations.// settingSources: ['user', 'project', 'local']}});
options = CodeBuddyAgentOptions(# Load project configurationsetting_sources=["project"],# Or load all configurations.# setting_sources=["user", "project", "local"])
Value | Description | Position |
'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 |
// Load only the project configuration and ignore user and local configurations.const q = query({prompt: 'Run tests',options: {settingSources: ['project'],permissionMode: 'bypassPermissions'}});
# Load only the project configuration and ignore user and local configurations.options = CodeBuddyAgentOptions(setting_sources=["project"],permission_mode="bypassPermissions")
// Default behavior: Do not load any configuration.// All behaviors are explicitly defined through options.const q = query({prompt: '...',options: {agents: { /* Custom agent */ },mcpServers: { /* Custom MCP */ },allowedTools: ['Read', 'Grep', 'Glob']}});
# Default behavior: Do not load any configuration.# All behaviors are explicitly defined through options.options = CodeBuddyAgentOptions(agents={"reviewer": AgentDefinition(...)},mcp_servers={"db": {...}},allowed_tools=["Read", "Grep", "Glob"])
canUseTool callback:import { query } from '@tencent-ai/agent-sdk';const q = query({prompt: 'Analyze the project structure',options: {canUseTool: async (toolName, input, options) => {// Only read-only tools are allowed.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.`};}}});
from codebuddy_agent_sdk import (query, CodeBuddyAgentOptions,CanUseToolOptions, PermissionResultAllow, PermissionResultDeny)async def can_use_tool(tool_name: str,input_data: dict,options: CanUseToolOptions):# Only read-only tools are allowed.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)
const dangerousCommands = ['rm -rf', 'sudo', 'chmod 777'];const q = query({prompt: 'Clean up 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 blocked: ${dangerous}`,interrupt: true // Interrupt the entire session.};}}}return { behavior: 'allow', updatedInput: input };}}});
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 blocked: {dangerous}",interrupt=True # Interrupt the entire session.)return PermissionResultAllow(updated_input=input_data)
import { unstable_v2_createSession } from '@tencent-ai/agent-sdk';async function main() {const session = unstable_v2_createSession({model: 'deepseek-v3.1'});// First round of conversationawait session.send('Analyze the architecture of this project');for await (const message of session.stream()) {console.log(message);}// Second round of conversation (maintain context)await session.send('Please explain the third point in detail');for await (const message of session.stream()) {console.log(message);}session.close();}
import { unstable_v2_createSession } from '@tencent-ai/agent-sdk';async function main() {const session = unstable_v2_createSession({model: 'deepseek-v3.1'});// First round of conversationawait session.send('Analyze the architecture of this project');for await (const message of session.stream()) {console.log(message);}// Second round of conversation (maintain context)await session.send('Please explain the third point in detail');for await (const message of session.stream()) {console.log(message);}session.close();}
const q = query({ prompt: 'Execute a 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 executionbreak;}}}}}
async with CodeBuddySDKClient(options=options) as client:await client.query("Execute a long-running task...")count = 0async for message in client.receive_messages():if isinstance(message, AssistantMessage):for block in message.content:if isinstance(block, ToolUseBlock):count += 1if count >= 10:await client.interrupt() # Interrupt executionbreak
const q = query({prompt: 'Clean up temporary files',options: {hooks: {PreToolUse: [{matcher: 'Bash', // Match only the Bash toolhooks: [async (input, toolUseId) => {console.log('About to run the command:', input.command);// Can prevent executionif (input.command.includes('rm')) {return {decision: 'block',reason: 'Delete command blocked'};}return { continue: true };}]}]}}});
from codebuddy_agent_sdk import HookMatcher, HookContextasync def pre_tool_hook(input_data, tool_use_id, context: HookContext):print(f"About to run the command: {input_data.get('command')}")# Can prevent executionif "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])]})
Event | Trigger Timing |
PreToolUse | Before tool execution |
PostToolUse | After tool execution succeeds |
PostToolUseFailure | After tool execution fails |
UserPromptSubmit | When a user submits a prompt |
SessionStart | Session start |
SessionEnd | Session end |
WorktreeCreate | When creating an isolated worktree |
WorktreeRemove | When deleting an isolated worktree |
const q = query({prompt: 'Use code-reviewer to review the code',options: {agents: {'code-reviewer': {description: 'Professional code review assistant',tools: ['Read', 'Glob', 'Grep'], // Read-only access is allowed.disallowedTools: ['Bash', 'Write', 'Edit'],prompt: `You are a code review expert. Please check:1. Code standards2. Potential bugs3. Performance issues4. Security vulnerabilities`,model: 'deepseek-v3.1'}}}});
from codebuddy_agent_sdk import AgentDefinitionoptions = CodeBuddyAgentOptions(agents={"code-reviewer": AgentDefinition(description="Professional code review assistant",tools=["Read", "Glob", "Grep"], # Read-only access is allowed.disallowed_tools=["Bash", "Write", "Edit"],prompt="""You are a code review expert. Please check:1. Code standards2. Potential bugs3. Performance issues4. Security vulnerabilities""",model="deepseek-v3.1")})
const q = query({prompt: 'Query the database',options: {mcpServers: {'database': {type: 'stdio',command: 'node',args: ['./mcp-servers/db-server.js'],env: {DB_HOST: 'localhost',DB_PORT: '5432'}}}}});
options = CodeBuddyAgentOptions(mcp_servers={"database": {"type": "stdio","command": "node","args": ["./mcp-servers/db-server.js"],"env": {"DB_HOST": "localhost","DB_PORT": "5432"}}})
AskUserQuestion tool, which can be handled in the permission callback:const q = query({prompt: 'Configure the 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}`);// You can integrate actual user interaction hereanswers[q.question] = q.options[0].label;}return {behavior: 'allow',updatedInput: { ...input, answers }};}return { behavior: 'allow', updatedInput: input };}}})
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']}")# You can integrate actual user interaction hereanswers[q["question"]] = q["options"][0]["label"]return PermissionResultAllow(updated_input={**input_data, "answers": answers})return PermissionResultAllow(updated_input=input_data)
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('An error occurred:', error);}}
from codebuddy_agent_sdk import (query, CodeBuddySDKError,CLIConnectionError, CLINotFoundError)try:async for message in query(prompt="..."):passexcept 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}")
canUseTool to implement fine-grained permissions in production environments, and avoid using bypassPermissionsmaxTurns to limit the execution scope and prevent unexpected resource consumption.result message.Was this page helpful?
You can also Contact sales or Submit a Ticket for help.
Help us improve! Rate your documentation experience in 5 mins.
Feedback