codebuddy-agent-sdk 0.1.27__py3-none-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of codebuddy-agent-sdk might be problematic. Click here for more details.

@@ -0,0 +1,171 @@
1
+ """Subprocess transport for CLI communication."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import json
7
+ import os
8
+ from collections.abc import AsyncIterable, AsyncIterator
9
+ from typing import Any
10
+
11
+ from .._binary import get_cli_path
12
+ from ..types import AppendSystemPrompt, CodeBuddyAgentOptions
13
+ from .base import Transport
14
+
15
+
16
+ class SubprocessTransport(Transport):
17
+ """Transport that communicates with CLI via subprocess."""
18
+
19
+ def __init__(
20
+ self,
21
+ options: CodeBuddyAgentOptions,
22
+ prompt: str | AsyncIterable[dict[str, Any]] | None = None,
23
+ ):
24
+ self.options = options
25
+ self.prompt = prompt
26
+ self._process: asyncio.subprocess.Process | None = None
27
+ self._closed = False
28
+
29
+ def _get_cli_path(self) -> str:
30
+ """Get the path to CLI executable."""
31
+ # User-specified path takes highest precedence
32
+ if self.options.codebuddy_code_path:
33
+ return str(self.options.codebuddy_code_path)
34
+
35
+ # Use the binary resolver (env var -> package binary -> monorepo)
36
+ return get_cli_path()
37
+
38
+ def _build_args(self) -> list[str]:
39
+ """Build CLI arguments from options."""
40
+ args = [
41
+ "--input-format",
42
+ "stream-json",
43
+ "--verbose",
44
+ "--output-format",
45
+ "stream-json",
46
+ ]
47
+ opts = self.options
48
+
49
+ # Model options
50
+ if opts.model:
51
+ args.extend(["--model", opts.model])
52
+ if opts.fallback_model:
53
+ args.extend(["--fallback-model", opts.fallback_model])
54
+
55
+ # Permission options
56
+ if opts.permission_mode:
57
+ args.extend(["--permission-mode", opts.permission_mode])
58
+
59
+ # Turn limits
60
+ if opts.max_turns:
61
+ args.extend(["--max-turns", str(opts.max_turns)])
62
+
63
+ # Session options
64
+ if opts.continue_conversation:
65
+ args.append("--continue")
66
+ if opts.resume:
67
+ args.extend(["--resume", opts.resume])
68
+ if opts.fork_session:
69
+ args.append("--fork-session")
70
+
71
+ # Tool options
72
+ if opts.allowed_tools:
73
+ args.extend(["--allowedTools", ",".join(opts.allowed_tools)])
74
+ if opts.disallowed_tools:
75
+ args.extend(["--disallowedTools", ",".join(opts.disallowed_tools)])
76
+
77
+ # MCP options
78
+ if opts.mcp_servers and isinstance(opts.mcp_servers, dict):
79
+ args.extend(["--mcp-config", json.dumps({"mcpServers": opts.mcp_servers})])
80
+
81
+ # Settings
82
+ # SDK default: don't load any filesystem settings for clean environment isolation
83
+ # When setting_sources is explicitly provided (including empty list), use it
84
+ # When not provided (None), default to 'none' for SDK isolation
85
+ if opts.setting_sources is not None:
86
+ setting_value = (
87
+ "none" if len(opts.setting_sources) == 0 else ",".join(opts.setting_sources)
88
+ )
89
+ args.extend(["--setting-sources", setting_value])
90
+ else:
91
+ # SDK default behavior: no filesystem settings loaded
92
+ args.extend(["--setting-sources", "none"])
93
+
94
+ # Output options
95
+ if opts.include_partial_messages:
96
+ args.append("--include-partial-messages")
97
+
98
+ # System prompt options
99
+ if opts.system_prompt is not None:
100
+ if isinstance(opts.system_prompt, str):
101
+ args.extend(["--system-prompt", opts.system_prompt])
102
+ elif isinstance(opts.system_prompt, AppendSystemPrompt):
103
+ args.extend(["--append-system-prompt", opts.system_prompt.append])
104
+
105
+ # Extra args (custom flags)
106
+ for flag, value in opts.extra_args.items():
107
+ if value is None:
108
+ args.append(f"--{flag}")
109
+ else:
110
+ args.extend([f"--{flag}", value])
111
+
112
+ return args
113
+
114
+ async def connect(self) -> None:
115
+ """Start the subprocess."""
116
+ cli_path = self._get_cli_path()
117
+ args = self._build_args()
118
+ cwd = str(self.options.cwd) if self.options.cwd else os.getcwd()
119
+
120
+ env = {
121
+ **os.environ,
122
+ **self.options.env,
123
+ "CODEBUDDY_CODE_ENTRYPOINT": "sdk-py",
124
+ }
125
+
126
+ self._process = await asyncio.create_subprocess_exec(
127
+ cli_path,
128
+ *args,
129
+ stdin=asyncio.subprocess.PIPE,
130
+ stdout=asyncio.subprocess.PIPE,
131
+ stderr=asyncio.subprocess.PIPE,
132
+ cwd=cwd,
133
+ env=env,
134
+ )
135
+
136
+ # Start stderr reader if callback provided
137
+ if self.options.stderr and self._process.stderr:
138
+ asyncio.create_task(self._read_stderr())
139
+
140
+ async def _read_stderr(self) -> None:
141
+ """Read stderr and call callback."""
142
+ if self._process and self._process.stderr and self.options.stderr:
143
+ async for line in self._process.stderr:
144
+ self.options.stderr(line.decode())
145
+
146
+ async def read(self) -> AsyncIterator[str]:
147
+ """Read lines from stdout."""
148
+ if not self._process or not self._process.stdout:
149
+ return
150
+
151
+ async for line in self._process.stdout:
152
+ if self._closed:
153
+ break
154
+ yield line.decode().strip()
155
+
156
+ async def write(self, data: str) -> None:
157
+ """Write data to stdin."""
158
+ if self._process and self._process.stdin:
159
+ self._process.stdin.write((data + "\n").encode())
160
+ await self._process.stdin.drain()
161
+
162
+ async def close(self) -> None:
163
+ """Close the subprocess."""
164
+ if self._closed:
165
+ return
166
+
167
+ self._closed = True
168
+
169
+ if self._process:
170
+ self._process.kill()
171
+ await self._process.wait()
@@ -0,0 +1,330 @@
1
+ """Type definitions for CodeBuddy Agent SDK."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Awaitable, Callable
6
+ from dataclasses import dataclass, field
7
+ from pathlib import Path
8
+ from typing import TYPE_CHECKING, Any, Literal, NotRequired, TypedDict
9
+
10
+ if TYPE_CHECKING:
11
+ from .mcp.types import SdkMcpServer
12
+
13
+ # ============= AskUserQuestion Types =============
14
+
15
+
16
+ @dataclass
17
+ class AskUserQuestionOption:
18
+ """Option for AskUserQuestion tool."""
19
+
20
+ label: str
21
+ description: str
22
+
23
+
24
+ @dataclass
25
+ class AskUserQuestionQuestion:
26
+ """Question for AskUserQuestion tool."""
27
+
28
+ question: str
29
+ header: str
30
+ options: list[AskUserQuestionOption]
31
+ multi_select: bool
32
+
33
+
34
+ @dataclass
35
+ class AskUserQuestionInput:
36
+ """Input for AskUserQuestion tool."""
37
+
38
+ questions: list[AskUserQuestionQuestion]
39
+ answers: dict[str, str] | None = None
40
+
41
+
42
+ # ============= Permission Types =============
43
+
44
+
45
+ @dataclass
46
+ class CanUseToolOptions:
47
+ """Options passed to canUseTool callback."""
48
+
49
+ tool_use_id: str
50
+ signal: Any | None = None
51
+ agent_id: str | None = None
52
+ suggestions: list[dict[str, Any]] | None = None
53
+ blocked_path: str | None = None
54
+ decision_reason: str | None = None
55
+
56
+
57
+ @dataclass
58
+ class PermissionResultAllow:
59
+ """Allow permission result."""
60
+
61
+ updated_input: dict[str, Any]
62
+ behavior: Literal["allow"] = "allow"
63
+ updated_permissions: list[dict[str, Any]] | None = None
64
+
65
+
66
+ @dataclass
67
+ class PermissionResultDeny:
68
+ """Deny permission result."""
69
+
70
+ message: str
71
+ behavior: Literal["deny"] = "deny"
72
+ interrupt: bool = False
73
+
74
+
75
+ PermissionResult = PermissionResultAllow | PermissionResultDeny
76
+
77
+ # CanUseTool callback type
78
+ CanUseTool = Callable[
79
+ [str, dict[str, Any], CanUseToolOptions],
80
+ Awaitable[PermissionResult],
81
+ ]
82
+
83
+
84
+ # Permission modes
85
+ PermissionMode = Literal["default", "acceptEdits", "plan", "bypassPermissions"]
86
+
87
+ # Hook events
88
+ HookEvent = (
89
+ Literal["PreToolUse"]
90
+ | Literal["PostToolUse"]
91
+ | Literal["UserPromptSubmit"]
92
+ | Literal["Stop"]
93
+ | Literal["SubagentStop"]
94
+ | Literal["PreCompact"]
95
+ )
96
+
97
+ # Setting sources
98
+ SettingSource = Literal["user", "project", "local"]
99
+
100
+
101
+ # Agent definition
102
+ @dataclass
103
+ class AgentDefinition:
104
+ """Agent definition configuration."""
105
+
106
+ description: str
107
+ prompt: str
108
+ tools: list[str] | None = None
109
+ disallowed_tools: list[str] | None = None
110
+ model: str | None = None
111
+
112
+
113
+ # Content block types
114
+ @dataclass
115
+ class TextBlock:
116
+ """Text content block."""
117
+
118
+ text: str
119
+
120
+
121
+ @dataclass
122
+ class ThinkingBlock:
123
+ """Thinking content block."""
124
+
125
+ thinking: str
126
+ signature: str
127
+
128
+
129
+ @dataclass
130
+ class ToolUseBlock:
131
+ """Tool use content block."""
132
+
133
+ id: str
134
+ name: str
135
+ input: dict[str, Any]
136
+
137
+
138
+ @dataclass
139
+ class ToolResultBlock:
140
+ """Tool result content block."""
141
+
142
+ tool_use_id: str
143
+ content: str | list[dict[str, Any]] | None = None
144
+ is_error: bool | None = None
145
+
146
+
147
+ ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlock
148
+
149
+
150
+ # Message types
151
+ @dataclass
152
+ class UserMessage:
153
+ """User message."""
154
+
155
+ content: str | list[ContentBlock]
156
+ uuid: str | None = None
157
+ parent_tool_use_id: str | None = None
158
+
159
+
160
+ @dataclass
161
+ class AssistantMessage:
162
+ """Assistant message with content blocks."""
163
+
164
+ content: list[ContentBlock]
165
+ model: str
166
+ parent_tool_use_id: str | None = None
167
+ error: str | None = None
168
+
169
+
170
+ @dataclass
171
+ class SystemMessage:
172
+ """System message with metadata."""
173
+
174
+ subtype: str
175
+ data: dict[str, Any]
176
+
177
+
178
+ @dataclass
179
+ class ResultMessage:
180
+ """Result message with cost and usage information."""
181
+
182
+ subtype: str
183
+ duration_ms: int
184
+ duration_api_ms: int
185
+ is_error: bool
186
+ num_turns: int
187
+ session_id: str
188
+ total_cost_usd: float | None = None
189
+ usage: dict[str, Any] | None = None
190
+ result: str | None = None
191
+ errors: list[str] | None = None
192
+
193
+
194
+ @dataclass
195
+ class StreamEvent:
196
+ """Stream event for partial message updates during streaming."""
197
+
198
+ uuid: str
199
+ session_id: str
200
+ event: dict[str, Any]
201
+ parent_tool_use_id: str | None = None
202
+
203
+
204
+ @dataclass
205
+ class ErrorMessage:
206
+ """Error message from CLI."""
207
+
208
+ error: str
209
+ session_id: str | None = None
210
+
211
+
212
+ Message = (
213
+ UserMessage | AssistantMessage | SystemMessage | ResultMessage | StreamEvent | ErrorMessage
214
+ )
215
+
216
+
217
+ # Hook types
218
+ class HookContext(TypedDict):
219
+ """Context information for hook callbacks."""
220
+
221
+ signal: Any | None
222
+
223
+
224
+ class SyncHookJSONOutput(TypedDict):
225
+ """Synchronous hook output with control and decision fields."""
226
+
227
+ continue_: NotRequired[bool]
228
+ suppressOutput: NotRequired[bool]
229
+ stopReason: NotRequired[str]
230
+ decision: NotRequired[Literal["block"]]
231
+ reason: NotRequired[str]
232
+
233
+
234
+ HookJSONOutput = SyncHookJSONOutput
235
+
236
+ HookCallback = Callable[
237
+ [Any, str | None, HookContext],
238
+ Awaitable[HookJSONOutput],
239
+ ]
240
+
241
+
242
+ @dataclass
243
+ class HookMatcher:
244
+ """Hook matcher configuration."""
245
+
246
+ matcher: str | None = None
247
+ hooks: list[HookCallback] = field(default_factory=list)
248
+ timeout: float | None = None
249
+
250
+
251
+ # MCP Server config
252
+ class McpStdioServerConfig(TypedDict):
253
+ """MCP stdio server configuration."""
254
+
255
+ type: NotRequired[Literal["stdio"]]
256
+ command: str
257
+ args: NotRequired[list[str]]
258
+ env: NotRequired[dict[str, str]]
259
+
260
+
261
+ class McpSdkServerConfig(TypedDict):
262
+ """
263
+ SDK MCP Server configuration - for servers running within the SDK process.
264
+ Created via create_sdk_mcp_server().
265
+ """
266
+
267
+ type: Literal["sdk"]
268
+ name: str
269
+ server: SdkMcpServer
270
+
271
+
272
+ McpServerConfig = McpStdioServerConfig | McpSdkServerConfig
273
+
274
+
275
+ # System prompt configuration
276
+ @dataclass
277
+ class AppendSystemPrompt:
278
+ """Append to the default system prompt."""
279
+
280
+ append: str
281
+
282
+
283
+ # Main configuration
284
+ @dataclass
285
+ class CodeBuddyAgentOptions:
286
+ """Query options for CodeBuddy Agent SDK."""
287
+
288
+ allowed_tools: list[str] = field(default_factory=list)
289
+ """
290
+ List of tool names that are auto-allowed without prompting for permission.
291
+ These tools will execute automatically without asking the user for approval.
292
+ """
293
+
294
+ disallowed_tools: list[str] = field(default_factory=list)
295
+ """
296
+ List of tool names that are disallowed. When the model attempts to use
297
+ these tools, the request will be denied. MCP tools matching this list
298
+ are also filtered from the model's context.
299
+ """
300
+
301
+ system_prompt: str | AppendSystemPrompt | None = None
302
+ """
303
+ System prompt configuration.
304
+
305
+ - `str`: Override the entire system prompt
306
+ - `AppendSystemPrompt`: Append to the default system prompt
307
+ """
308
+
309
+ mcp_servers: dict[str, McpServerConfig] | str | Path = field(default_factory=dict)
310
+ permission_mode: PermissionMode | None = None
311
+ continue_conversation: bool = False
312
+ resume: str | None = None
313
+ max_turns: int | None = None
314
+ model: str | None = None
315
+ fallback_model: str | None = None
316
+ cwd: str | Path | None = None
317
+ codebuddy_code_path: str | Path | None = None
318
+ env: dict[str, str] = field(default_factory=dict)
319
+ extra_args: dict[str, str | None] = field(default_factory=dict)
320
+ stderr: Callable[[str], None] | None = None
321
+ hooks: dict[HookEvent, list[HookMatcher]] | None = None
322
+ include_partial_messages: bool = False
323
+ fork_session: bool = False
324
+ agents: dict[str, AgentDefinition] | None = None
325
+ setting_sources: list[SettingSource] | None = None
326
+ can_use_tool: CanUseTool | None = None
327
+ """
328
+ Custom permission handler callback.
329
+ Called when a tool requires permission approval.
330
+ """
@@ -0,0 +1,89 @@
1
+ Metadata-Version: 2.4
2
+ Name: codebuddy-agent-sdk
3
+ Version: 0.1.27
4
+ Summary: CodeBuddy Code SDK for Python
5
+ Author-email: ninoyi <ninoyi@tencent.com>
6
+ Keywords: agent,ai,codebuddy,sdk
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Typing :: Typed
14
+ Requires-Python: >=3.10
15
+ Requires-Dist: typing-extensions>=4.0.0
16
+ Provides-Extra: dev
17
+ Requires-Dist: mypy>=1.10.0; extra == 'dev'
18
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
19
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
20
+ Requires-Dist: ruff>=0.4.0; extra == 'dev'
21
+ Description-Content-Type: text/markdown
22
+
23
+ # CodeBuddy Agent SDK for Python
24
+
25
+ SDK for building AI agents with CodeBuddy Code's capabilities. Programmatically interact with AI to build autonomous agents that can understand codebases, edit files, and execute workflows.
26
+
27
+ ## Installation
28
+
29
+ ```bash
30
+ # Using uv (recommended)
31
+ uv add codebuddy-agent-sdk
32
+
33
+ # Using pip
34
+ pip install codebuddy-agent-sdk
35
+ ```
36
+
37
+ ## Quick Start
38
+
39
+ ```python
40
+ import asyncio
41
+ from codebuddy_agent_sdk import query
42
+
43
+ async def main():
44
+ async for message in query(
45
+ prompt="What files are in this directory?",
46
+ permission_mode="bypassPermissions",
47
+ ):
48
+ if message.type == "assistant":
49
+ for block in message.content:
50
+ if hasattr(block, "text"):
51
+ print(block.text)
52
+
53
+ asyncio.run(main())
54
+ ```
55
+
56
+ ## API Reference
57
+
58
+ ### `query(prompt, **options)`
59
+
60
+ Create a query to interact with the agent.
61
+
62
+ ```python
63
+ async for message in query(
64
+ prompt="Your prompt here",
65
+ model="sonnet", # Model to use
66
+ permission_mode="bypassPermissions", # Permission mode
67
+ max_turns=10, # Maximum conversation turns
68
+ cwd="/path/to/project", # Working directory
69
+ ):
70
+ # Handle message
71
+ pass
72
+ ```
73
+
74
+ ### Message Types
75
+
76
+ - `system` - Session initialization info
77
+ - `assistant` - Agent responses (text, tool calls)
78
+ - `result` - Query completion status
79
+
80
+ ## Related Links
81
+
82
+ - [CodeBuddy Code CLI](https://www.npmjs.com/package/@tencent-ai/codebuddy-code)
83
+ - [Documentation](https://cnb.cool/codebuddy/codebuddy-code/-/blob/main/docs)
84
+ - [Issues](https://cnb.cool/codebuddy/codebuddy-code/-/issues)
85
+
86
+ ## Feedback
87
+
88
+ - Submit issues at [Issues](https://cnb.cool/codebuddy/codebuddy-code/-/issues)
89
+ - Contact: codebuddy@tencent.com
@@ -0,0 +1,20 @@
1
+ codebuddy_agent_sdk/__init__.py,sha256=GKrVvH2KKyuA9ht-7uMVHJJn7eWBKOi0_DKc2Ef5RCU,2690
2
+ codebuddy_agent_sdk/_binary.py,sha256=rQFj2B__X7zHMxFazp_0vgXQFsqPi578f8g4jSkLUBc,4338
3
+ codebuddy_agent_sdk/_errors.py,sha256=bLmyebUowNlbUomXyuS1L6J6ZbwHohvzqH_1GmN4_oM,1248
4
+ codebuddy_agent_sdk/_message_parser.py,sha256=vtyeuwIFdl9Wz1WrWEg7G6E3OECDZT4G6bvpH15Q3VY,3504
5
+ codebuddy_agent_sdk/_version.py,sha256=PuGt-TG_y-epKt7rwV96SbTWUkhFlvkyVOJnLXXGVW8,75
6
+ codebuddy_agent_sdk/client.py,sha256=JfdA0wq4KOOrP4mZXfVuF4uwdrEbVHGIUrzpt4Bt_ZM,10803
7
+ codebuddy_agent_sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ codebuddy_agent_sdk/query.py,sha256=tCfFomCNGS1Sygbk0U9j25JLsF2azWa-3AuckThYaII,17341
9
+ codebuddy_agent_sdk/types.py,sha256=rw5jrIg8LWtZN1Km_7TzY7SP_wFRJwKNGwPxgRcQ47A,7473
10
+ codebuddy_agent_sdk/bin/codebuddy,sha256=u0t0idfxMHpMFBhg8-w6Xyc1eleqz_VlMxTDP8Pz7yU,77940417
11
+ codebuddy_agent_sdk/mcp/__init__.py,sha256=0aCmHN0MnMCCUU0bdTWBRlxAblnxA-vR5Og0y3BA480,764
12
+ codebuddy_agent_sdk/mcp/create_sdk_mcp_server.py,sha256=ig0b0ghf0T8GGjltFBRSmQSTFsaJ6fBI8S1Pjupqhps,4959
13
+ codebuddy_agent_sdk/mcp/sdk_control_server_transport.py,sha256=CIG3h5gULubGcQPHPVd-GG9cLswwVDfgDlLba6ZPQbs,2944
14
+ codebuddy_agent_sdk/mcp/types.py,sha256=CPEBUW5vCt93cBgu3HvAacXMsa5FdRWwWxPeicQ0PWw,8578
15
+ codebuddy_agent_sdk/transport/__init__.py,sha256=zv_8OJHgnWjCInqOiu3GOFby4XVGvDwICHpOsymOlko,166
16
+ codebuddy_agent_sdk/transport/base.py,sha256=XtLquCmt4yzPhTBHcVU0XFb016uG0E-12ETc-N9tzIk,662
17
+ codebuddy_agent_sdk/transport/subprocess.py,sha256=VHoGXoP0qm597bhIE_mTV9xyIDUM-nMk7fwZwntx94c,5701
18
+ codebuddy_agent_sdk-0.1.27.dist-info/METADATA,sha256=7I6H0xtclNvPAkqs79en6VZFVEQgzoFPia6Me2z0C8c,2539
19
+ codebuddy_agent_sdk-0.1.27.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
20
+ codebuddy_agent_sdk-0.1.27.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: false
4
+ Tag: py3-none-macosx_11_0_arm64