codebuddy-agent-sdk 0.3.7__py3-none-manylinux_2_17_x86_64.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.
- codebuddy_agent_sdk/__init__.py +133 -0
- codebuddy_agent_sdk/_binary.py +150 -0
- codebuddy_agent_sdk/_errors.py +54 -0
- codebuddy_agent_sdk/_message_parser.py +122 -0
- codebuddy_agent_sdk/_version.py +3 -0
- codebuddy_agent_sdk/bin/codebuddy +0 -0
- codebuddy_agent_sdk/client.py +394 -0
- codebuddy_agent_sdk/mcp/__init__.py +35 -0
- codebuddy_agent_sdk/mcp/create_sdk_mcp_server.py +154 -0
- codebuddy_agent_sdk/mcp/sdk_control_server_transport.py +95 -0
- codebuddy_agent_sdk/mcp/types.py +300 -0
- codebuddy_agent_sdk/py.typed +0 -0
- codebuddy_agent_sdk/query.py +340 -0
- codebuddy_agent_sdk/transport/__init__.py +6 -0
- codebuddy_agent_sdk/transport/base.py +31 -0
- codebuddy_agent_sdk/transport/subprocess.py +341 -0
- codebuddy_agent_sdk/types.py +395 -0
- codebuddy_agent_sdk-0.3.7.dist-info/METADATA +89 -0
- codebuddy_agent_sdk-0.3.7.dist-info/RECORD +20 -0
- codebuddy_agent_sdk-0.3.7.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""CodeBuddy Agent SDK for Python."""
|
|
2
|
+
|
|
3
|
+
from ._errors import (
|
|
4
|
+
CLIConnectionError,
|
|
5
|
+
CLIJSONDecodeError,
|
|
6
|
+
CLINotFoundError,
|
|
7
|
+
CodeBuddySDKError,
|
|
8
|
+
ExecutionError,
|
|
9
|
+
ProcessError,
|
|
10
|
+
)
|
|
11
|
+
from ._version import __version__
|
|
12
|
+
from .client import CodeBuddySDKClient
|
|
13
|
+
from .mcp import (
|
|
14
|
+
CallToolResult,
|
|
15
|
+
SdkControlServerTransport,
|
|
16
|
+
SdkMcpServerOptions,
|
|
17
|
+
SdkMcpServerResult,
|
|
18
|
+
SdkMcpToolDefinition,
|
|
19
|
+
TextContent,
|
|
20
|
+
ToolHandler,
|
|
21
|
+
create_sdk_mcp_server,
|
|
22
|
+
tool,
|
|
23
|
+
)
|
|
24
|
+
from .query import query
|
|
25
|
+
from .transport import Transport
|
|
26
|
+
from .types import (
|
|
27
|
+
AgentDefinition,
|
|
28
|
+
AppendSystemPrompt,
|
|
29
|
+
AskUserQuestionInput,
|
|
30
|
+
AskUserQuestionOption,
|
|
31
|
+
AskUserQuestionQuestion,
|
|
32
|
+
AssistantMessage,
|
|
33
|
+
CanUseTool,
|
|
34
|
+
CanUseToolOptions,
|
|
35
|
+
Checkpoint,
|
|
36
|
+
CheckpointFileChangeStats,
|
|
37
|
+
CodeBuddyAgentOptions,
|
|
38
|
+
ContentBlock,
|
|
39
|
+
ErrorMessage,
|
|
40
|
+
FileVersion,
|
|
41
|
+
HookCallback,
|
|
42
|
+
HookContext,
|
|
43
|
+
HookEvent,
|
|
44
|
+
HookJSONOutput,
|
|
45
|
+
HookMatcher,
|
|
46
|
+
McpSdkServerConfig,
|
|
47
|
+
McpServerConfig,
|
|
48
|
+
McpStdioServerConfig,
|
|
49
|
+
Message,
|
|
50
|
+
PermissionMode,
|
|
51
|
+
PermissionResult,
|
|
52
|
+
PermissionResultAllow,
|
|
53
|
+
PermissionResultDeny,
|
|
54
|
+
ResultMessage,
|
|
55
|
+
SettingSource,
|
|
56
|
+
StreamEvent,
|
|
57
|
+
SystemMessage,
|
|
58
|
+
TextBlock,
|
|
59
|
+
ThinkingBlock,
|
|
60
|
+
ToolResultBlock,
|
|
61
|
+
ToolUseBlock,
|
|
62
|
+
UserMessage,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
__all__ = [
|
|
66
|
+
# Main API
|
|
67
|
+
"query",
|
|
68
|
+
"CodeBuddySDKClient",
|
|
69
|
+
"Transport",
|
|
70
|
+
"__version__",
|
|
71
|
+
# MCP Server API
|
|
72
|
+
"create_sdk_mcp_server",
|
|
73
|
+
"tool",
|
|
74
|
+
"SdkControlServerTransport",
|
|
75
|
+
"SdkMcpServerOptions",
|
|
76
|
+
"SdkMcpServerResult",
|
|
77
|
+
"SdkMcpToolDefinition",
|
|
78
|
+
"ToolHandler",
|
|
79
|
+
"CallToolResult",
|
|
80
|
+
"TextContent",
|
|
81
|
+
# Types - Permission
|
|
82
|
+
"PermissionMode",
|
|
83
|
+
# Types - Messages
|
|
84
|
+
"Message",
|
|
85
|
+
"UserMessage",
|
|
86
|
+
"AssistantMessage",
|
|
87
|
+
"SystemMessage",
|
|
88
|
+
"ResultMessage",
|
|
89
|
+
"StreamEvent",
|
|
90
|
+
"ErrorMessage",
|
|
91
|
+
# Types - Content blocks
|
|
92
|
+
"ContentBlock",
|
|
93
|
+
"TextBlock",
|
|
94
|
+
"ThinkingBlock",
|
|
95
|
+
"ToolUseBlock",
|
|
96
|
+
"ToolResultBlock",
|
|
97
|
+
# Types - Configuration
|
|
98
|
+
"CodeBuddyAgentOptions",
|
|
99
|
+
"AgentDefinition",
|
|
100
|
+
"AppendSystemPrompt",
|
|
101
|
+
"SettingSource",
|
|
102
|
+
# Types - Permission
|
|
103
|
+
"CanUseTool",
|
|
104
|
+
"CanUseToolOptions",
|
|
105
|
+
"PermissionResult",
|
|
106
|
+
"PermissionResultAllow",
|
|
107
|
+
"PermissionResultDeny",
|
|
108
|
+
# Types - AskUserQuestion
|
|
109
|
+
"AskUserQuestionOption",
|
|
110
|
+
"AskUserQuestionQuestion",
|
|
111
|
+
"AskUserQuestionInput",
|
|
112
|
+
# Types - Hooks
|
|
113
|
+
"HookEvent",
|
|
114
|
+
"HookCallback",
|
|
115
|
+
"HookMatcher",
|
|
116
|
+
"HookJSONOutput",
|
|
117
|
+
"HookContext",
|
|
118
|
+
# Types - Checkpoint
|
|
119
|
+
"Checkpoint",
|
|
120
|
+
"CheckpointFileChangeStats",
|
|
121
|
+
"FileVersion",
|
|
122
|
+
# Types - MCP
|
|
123
|
+
"McpServerConfig",
|
|
124
|
+
"McpStdioServerConfig",
|
|
125
|
+
"McpSdkServerConfig",
|
|
126
|
+
# Errors
|
|
127
|
+
"CodeBuddySDKError",
|
|
128
|
+
"CLIConnectionError",
|
|
129
|
+
"CLINotFoundError",
|
|
130
|
+
"CLIJSONDecodeError",
|
|
131
|
+
"ProcessError",
|
|
132
|
+
"ExecutionError",
|
|
133
|
+
]
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Binary locator for CodeBuddy CLI.
|
|
3
|
+
|
|
4
|
+
This module provides functions to locate the CodeBuddy CLI binary.
|
|
5
|
+
The binary is bundled in platform-specific wheels.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import platform
|
|
12
|
+
import sys
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from ._errors import CLINotFoundError
|
|
16
|
+
|
|
17
|
+
__all__ = ["get_cli_path", "try_cli_path", "get_platform_info"]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# Platform mapping for goreleaser output directories (for monorepo development)
|
|
21
|
+
# Note: For musl (Alpine), the correct binary is bundled in the wheel at build time
|
|
22
|
+
_PLATFORM_DIRS: dict[tuple[str, str], str] = {
|
|
23
|
+
("Darwin", "arm64"): "darwin-arm64_bun-darwin-arm64",
|
|
24
|
+
("Darwin", "x86_64"): "darwin-x64_bun-darwin-x64",
|
|
25
|
+
("Linux", "x86_64"): "linux-x64_bun-linux-x64",
|
|
26
|
+
("Linux", "aarch64"): "linux-arm64_bun-linux-arm64",
|
|
27
|
+
("Windows", "AMD64"): "windows-x64_bun-windows-x64",
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
# Binary name per platform
|
|
31
|
+
_BINARY_NAMES: dict[str, str] = {
|
|
32
|
+
"Darwin": "codebuddy",
|
|
33
|
+
"Linux": "codebuddy",
|
|
34
|
+
"Windows": "codebuddy.exe",
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _get_package_binary_path() -> Path | None:
|
|
39
|
+
"""Get the binary path bundled in the package."""
|
|
40
|
+
# Binary is bundled at package_root/bin/codebuddy
|
|
41
|
+
pkg_root = Path(__file__).parent
|
|
42
|
+
system = platform.system()
|
|
43
|
+
binary_name = _BINARY_NAMES.get(system, "codebuddy")
|
|
44
|
+
bin_path = pkg_root / "bin" / binary_name
|
|
45
|
+
|
|
46
|
+
if bin_path.exists() and bin_path.is_file():
|
|
47
|
+
return bin_path
|
|
48
|
+
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _get_monorepo_binary_path() -> Path | None:
|
|
53
|
+
"""Get the binary path from monorepo development structure."""
|
|
54
|
+
system = platform.system()
|
|
55
|
+
machine = platform.machine()
|
|
56
|
+
key = (system, machine)
|
|
57
|
+
|
|
58
|
+
dir_name = _PLATFORM_DIRS.get(key)
|
|
59
|
+
if not dir_name:
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
binary_name = _BINARY_NAMES.get(system, "codebuddy")
|
|
63
|
+
|
|
64
|
+
# Try relative to this package (agent-sdk-python -> agent-cli)
|
|
65
|
+
pkg_root = Path(__file__).parent
|
|
66
|
+
monorepo_path = pkg_root.parent.parent.parent / "agent-cli" / "out" / dir_name / binary_name
|
|
67
|
+
|
|
68
|
+
if monorepo_path.exists() and monorepo_path.is_file():
|
|
69
|
+
return monorepo_path
|
|
70
|
+
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def get_cli_path() -> str:
|
|
75
|
+
"""
|
|
76
|
+
Get the path to the CodeBuddy CLI binary.
|
|
77
|
+
|
|
78
|
+
Resolution order:
|
|
79
|
+
1. CODEBUDDY_CODE_PATH environment variable
|
|
80
|
+
2. Binary bundled in the package (wheel)
|
|
81
|
+
3. Monorepo development path
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
The absolute path to the CLI binary.
|
|
85
|
+
|
|
86
|
+
Raises:
|
|
87
|
+
CLINotFoundError: If the binary cannot be found.
|
|
88
|
+
"""
|
|
89
|
+
system = platform.system()
|
|
90
|
+
machine = platform.machine()
|
|
91
|
+
|
|
92
|
+
# 1. Environment variable takes precedence
|
|
93
|
+
env_path = os.environ.get("CODEBUDDY_CODE_PATH")
|
|
94
|
+
if env_path:
|
|
95
|
+
path = Path(env_path)
|
|
96
|
+
if path.exists():
|
|
97
|
+
return str(path)
|
|
98
|
+
# Warn but continue to try other methods
|
|
99
|
+
print(
|
|
100
|
+
f"Warning: CODEBUDDY_CODE_PATH is set to '{env_path}' but file does not exist. "
|
|
101
|
+
"Falling back to other resolution methods.",
|
|
102
|
+
file=sys.stderr,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# 2. Try package bundled binary
|
|
106
|
+
pkg_path = _get_package_binary_path()
|
|
107
|
+
if pkg_path:
|
|
108
|
+
return str(pkg_path)
|
|
109
|
+
|
|
110
|
+
# 3. Try monorepo development path
|
|
111
|
+
monorepo_path = _get_monorepo_binary_path()
|
|
112
|
+
if monorepo_path:
|
|
113
|
+
return str(monorepo_path)
|
|
114
|
+
|
|
115
|
+
# 4. Nothing found - raise helpful error
|
|
116
|
+
supported = ", ".join(f"{s}-{m}" for s, m in _PLATFORM_DIRS)
|
|
117
|
+
raise CLINotFoundError(
|
|
118
|
+
f"CodeBuddy CLI binary not found for platform '{system}-{machine}'.\n\n"
|
|
119
|
+
f"Possible solutions:\n"
|
|
120
|
+
f" 1. Reinstall the package to get the correct platform wheel:\n"
|
|
121
|
+
f" pip install --force-reinstall codebuddy-agent-sdk\n\n"
|
|
122
|
+
f" 2. Set CODEBUDDY_CODE_PATH environment variable to the CLI binary path\n\n"
|
|
123
|
+
f" 3. Install the CLI separately and set the path\n\n"
|
|
124
|
+
f"Supported platforms: {supported}",
|
|
125
|
+
system,
|
|
126
|
+
machine,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def try_cli_path() -> str | None:
|
|
131
|
+
"""
|
|
132
|
+
Try to get the CLI path without raising an exception.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
The CLI path if found, None otherwise.
|
|
136
|
+
"""
|
|
137
|
+
try:
|
|
138
|
+
return get_cli_path()
|
|
139
|
+
except CLINotFoundError:
|
|
140
|
+
return None
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def get_platform_info() -> tuple[str, str]:
|
|
144
|
+
"""
|
|
145
|
+
Get the current platform information.
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
A tuple of (system, machine).
|
|
149
|
+
"""
|
|
150
|
+
return platform.system(), platform.machine()
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Error definitions for CodeBuddy Agent SDK."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CodeBuddySDKError(Exception):
|
|
7
|
+
"""Base exception for CodeBuddy SDK errors."""
|
|
8
|
+
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CLIConnectionError(CodeBuddySDKError):
|
|
13
|
+
"""Raised when connection to CLI fails or is not established."""
|
|
14
|
+
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class CLINotFoundError(CodeBuddySDKError):
|
|
19
|
+
"""Raised when CLI executable is not found."""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
message: str,
|
|
24
|
+
platform: str | None = None,
|
|
25
|
+
arch: str | None = None,
|
|
26
|
+
):
|
|
27
|
+
super().__init__(message)
|
|
28
|
+
self.platform = platform
|
|
29
|
+
self.arch = arch
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class CLIJSONDecodeError(CodeBuddySDKError):
|
|
33
|
+
"""Raised when JSON decoding from CLI output fails."""
|
|
34
|
+
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ProcessError(CodeBuddySDKError):
|
|
39
|
+
"""Raised when CLI process encounters an error."""
|
|
40
|
+
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ExecutionError(CodeBuddySDKError):
|
|
45
|
+
"""Raised when execution fails (e.g., authentication error, API error).
|
|
46
|
+
|
|
47
|
+
Contains the errors array from the ResultMessage.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(self, errors: list[str], subtype: str):
|
|
51
|
+
message = errors[0] if errors else "Execution failed"
|
|
52
|
+
super().__init__(message)
|
|
53
|
+
self.errors = errors
|
|
54
|
+
self.subtype = subtype
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Message parser for CLI output."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from .types import (
|
|
8
|
+
AssistantMessage,
|
|
9
|
+
ContentBlock,
|
|
10
|
+
ErrorMessage,
|
|
11
|
+
Message,
|
|
12
|
+
ResultMessage,
|
|
13
|
+
StreamEvent,
|
|
14
|
+
SystemMessage,
|
|
15
|
+
TextBlock,
|
|
16
|
+
ThinkingBlock,
|
|
17
|
+
ToolResultBlock,
|
|
18
|
+
ToolUseBlock,
|
|
19
|
+
UserMessage,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def parse_content_block(data: dict[str, Any]) -> ContentBlock | None:
|
|
24
|
+
"""Parse a content block from raw data."""
|
|
25
|
+
block_type = data.get("type")
|
|
26
|
+
|
|
27
|
+
if block_type == "text":
|
|
28
|
+
return TextBlock(text=data.get("text", ""))
|
|
29
|
+
|
|
30
|
+
if block_type == "thinking":
|
|
31
|
+
return ThinkingBlock(
|
|
32
|
+
thinking=data.get("thinking", ""),
|
|
33
|
+
signature=data.get("signature", ""),
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
if block_type == "tool_use":
|
|
37
|
+
return ToolUseBlock(
|
|
38
|
+
id=data.get("id", ""),
|
|
39
|
+
name=data.get("name", ""),
|
|
40
|
+
input=data.get("input", {}),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
if block_type == "tool_result":
|
|
44
|
+
return ToolResultBlock(
|
|
45
|
+
tool_use_id=data.get("tool_use_id", ""),
|
|
46
|
+
content=data.get("content"),
|
|
47
|
+
is_error=data.get("is_error"),
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def parse_content_blocks(content: list[dict[str, Any]]) -> list[ContentBlock]:
|
|
54
|
+
"""Parse a list of content blocks."""
|
|
55
|
+
blocks: list[ContentBlock] = []
|
|
56
|
+
for item in content:
|
|
57
|
+
block = parse_content_block(item)
|
|
58
|
+
if block:
|
|
59
|
+
blocks.append(block)
|
|
60
|
+
return blocks
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def parse_message(data: dict[str, Any]) -> Message | None:
|
|
64
|
+
"""Parse a message from raw JSON data."""
|
|
65
|
+
msg_type = data.get("type")
|
|
66
|
+
|
|
67
|
+
if msg_type == "user":
|
|
68
|
+
message_data = data.get("message", {})
|
|
69
|
+
content = message_data.get("content", "")
|
|
70
|
+
if isinstance(content, list):
|
|
71
|
+
content = parse_content_blocks(content)
|
|
72
|
+
return UserMessage(
|
|
73
|
+
content=content,
|
|
74
|
+
uuid=data.get("uuid"),
|
|
75
|
+
parent_tool_use_id=data.get("parent_tool_use_id"),
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
if msg_type == "assistant":
|
|
79
|
+
message_data = data.get("message", {})
|
|
80
|
+
content = message_data.get("content", [])
|
|
81
|
+
return AssistantMessage(
|
|
82
|
+
content=parse_content_blocks(content) if isinstance(content, list) else [],
|
|
83
|
+
model=data.get("model", ""),
|
|
84
|
+
parent_tool_use_id=data.get("parent_tool_use_id"),
|
|
85
|
+
error=data.get("error"),
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
if msg_type == "system":
|
|
89
|
+
return SystemMessage(
|
|
90
|
+
subtype=data.get("subtype", ""),
|
|
91
|
+
data=data,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if msg_type == "result":
|
|
95
|
+
return ResultMessage(
|
|
96
|
+
subtype=data.get("subtype", ""),
|
|
97
|
+
duration_ms=data.get("duration_ms", 0),
|
|
98
|
+
duration_api_ms=data.get("duration_api_ms", 0),
|
|
99
|
+
is_error=data.get("is_error", False),
|
|
100
|
+
num_turns=data.get("num_turns", 0),
|
|
101
|
+
session_id=data.get("session_id", ""),
|
|
102
|
+
total_cost_usd=data.get("total_cost_usd"),
|
|
103
|
+
usage=data.get("usage"),
|
|
104
|
+
result=data.get("result"),
|
|
105
|
+
errors=data.get("errors"),
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
if msg_type == "stream_event":
|
|
109
|
+
return StreamEvent(
|
|
110
|
+
uuid=data.get("uuid", ""),
|
|
111
|
+
session_id=data.get("session_id", ""),
|
|
112
|
+
event=data.get("event", {}),
|
|
113
|
+
parent_tool_use_id=data.get("parent_tool_use_id"),
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
if msg_type == "error":
|
|
117
|
+
return ErrorMessage(
|
|
118
|
+
error=data.get("error", ""),
|
|
119
|
+
session_id=data.get("session_id"),
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
return None
|
|
Binary file
|