codebuddy-agent-sdk 0.1.8__py3-none-manylinux_2_17_x86_64.whl → 0.2.0__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.

Potentially problematic release.


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

@@ -5,10 +5,22 @@ from ._errors import (
5
5
  CLIJSONDecodeError,
6
6
  CLINotFoundError,
7
7
  CodeBuddySDKError,
8
+ ExecutionError,
8
9
  ProcessError,
9
10
  )
10
11
  from ._version import __version__
11
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
+ )
12
24
  from .query import query
13
25
  from .transport import Transport
14
26
  from .types import (
@@ -22,12 +34,15 @@ from .types import (
22
34
  CanUseToolOptions,
23
35
  CodeBuddyAgentOptions,
24
36
  ContentBlock,
37
+ ErrorMessage,
25
38
  HookCallback,
26
39
  HookContext,
27
40
  HookEvent,
28
41
  HookJSONOutput,
29
42
  HookMatcher,
43
+ McpSdkServerConfig,
30
44
  McpServerConfig,
45
+ McpStdioServerConfig,
31
46
  Message,
32
47
  PermissionMode,
33
48
  PermissionResult,
@@ -50,6 +65,16 @@ __all__ = [
50
65
  "CodeBuddySDKClient",
51
66
  "Transport",
52
67
  "__version__",
68
+ # MCP Server API
69
+ "create_sdk_mcp_server",
70
+ "tool",
71
+ "SdkControlServerTransport",
72
+ "SdkMcpServerOptions",
73
+ "SdkMcpServerResult",
74
+ "SdkMcpToolDefinition",
75
+ "ToolHandler",
76
+ "CallToolResult",
77
+ "TextContent",
53
78
  # Types - Permission
54
79
  "PermissionMode",
55
80
  # Types - Messages
@@ -59,6 +84,7 @@ __all__ = [
59
84
  "SystemMessage",
60
85
  "ResultMessage",
61
86
  "StreamEvent",
87
+ "ErrorMessage",
62
88
  # Types - Content blocks
63
89
  "ContentBlock",
64
90
  "TextBlock",
@@ -88,10 +114,13 @@ __all__ = [
88
114
  "HookContext",
89
115
  # Types - MCP
90
116
  "McpServerConfig",
117
+ "McpStdioServerConfig",
118
+ "McpSdkServerConfig",
91
119
  # Errors
92
120
  "CodeBuddySDKError",
93
121
  "CLIConnectionError",
94
122
  "CLINotFoundError",
95
123
  "CLIJSONDecodeError",
96
124
  "ProcessError",
125
+ "ExecutionError",
97
126
  ]
@@ -5,6 +5,8 @@ This module provides functions to locate the CodeBuddy CLI binary.
5
5
  The binary is bundled in platform-specific wheels.
6
6
  """
7
7
 
8
+ from __future__ import annotations
9
+
8
10
  import os
9
11
  import platform
10
12
  import sys
@@ -1,5 +1,7 @@
1
1
  """Error definitions for CodeBuddy Agent SDK."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
 
4
6
  class CodeBuddySDKError(Exception):
5
7
  """Base exception for CodeBuddy SDK errors."""
@@ -37,3 +39,16 @@ class ProcessError(CodeBuddySDKError):
37
39
  """Raised when CLI process encounters an error."""
38
40
 
39
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
@@ -1,10 +1,13 @@
1
1
  """Message parser for CLI output."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  from typing import Any
4
6
 
5
7
  from .types import (
6
8
  AssistantMessage,
7
9
  ContentBlock,
10
+ ErrorMessage,
8
11
  Message,
9
12
  ResultMessage,
10
13
  StreamEvent,
@@ -99,6 +102,7 @@ def parse_message(data: dict[str, Any]) -> Message | None:
99
102
  total_cost_usd=data.get("total_cost_usd"),
100
103
  usage=data.get("usage"),
101
104
  result=data.get("result"),
105
+ errors=data.get("errors"),
102
106
  )
103
107
 
104
108
  if msg_type == "stream_event":
@@ -109,4 +113,10 @@ def parse_message(data: dict[str, Any]) -> Message | None:
109
113
  parent_tool_use_id=data.get("parent_tool_use_id"),
110
114
  )
111
115
 
116
+ if msg_type == "error":
117
+ return ErrorMessage(
118
+ error=data.get("error", ""),
119
+ session_id=data.get("session_id"),
120
+ )
121
+
112
122
  return None
@@ -1,3 +1,3 @@
1
1
  """Version information for CodeBuddy Agent SDK."""
2
2
 
3
- __version__ = "0.1.8"
3
+ __version__ = "0.2.0"
Binary file
@@ -1,14 +1,17 @@
1
1
  """CodeBuddy SDK Client for interactive conversations."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import json
4
6
  import os
5
7
  from collections.abc import AsyncIterable, AsyncIterator
8
+ from types import TracebackType
6
9
  from typing import Any
7
10
 
8
- from ._errors import CLIConnectionError
11
+ from ._errors import CLIConnectionError, ExecutionError
9
12
  from ._message_parser import parse_message
10
13
  from .transport import SubprocessTransport, Transport
11
- from .types import CanUseToolOptions, CodeBuddyAgentOptions, Message, ResultMessage
14
+ from .types import CanUseToolOptions, CodeBuddyAgentOptions, ErrorMessage, Message, ResultMessage
12
15
 
13
16
 
14
17
  class CodeBuddySDKClient:
@@ -47,9 +50,7 @@ class CodeBuddySDKClient:
47
50
 
48
51
  os.environ["CODEBUDDY_CODE_ENTRYPOINT"] = "sdk-py-client"
49
52
 
50
- async def connect(
51
- self, prompt: str | AsyncIterable[dict[str, Any]] | None = None
52
- ) -> None:
53
+ async def connect(self, prompt: str | AsyncIterable[dict[str, Any]] | None = None) -> None:
53
54
  """Connect to CodeBuddy with an optional initial prompt."""
54
55
  if self._custom_transport:
55
56
  self._transport = self._custom_transport
@@ -150,9 +151,7 @@ class CodeBuddySDKClient:
150
151
  }
151
152
  await self._transport.write(json.dumps(response))
152
153
 
153
- async def _handle_permission_request(
154
- self, request_id: str, request: dict[str, Any]
155
- ) -> None:
154
+ async def _handle_permission_request(self, request_id: str, request: dict[str, Any]) -> None:
156
155
  """Handle permission request from CLI."""
157
156
  if not self._transport:
158
157
  return
@@ -234,14 +233,23 @@ class CodeBuddySDKClient:
234
233
 
235
234
  async def receive_response(self) -> AsyncIterator[Message]:
236
235
  """
237
- Receive messages until and including a ResultMessage.
236
+ Receive messages until and including a ResultMessage or ErrorMessage.
238
237
 
239
238
  Yields each message as it's received and terminates after
240
- yielding a ResultMessage.
239
+ yielding a ResultMessage or ErrorMessage.
240
+ Raises ExecutionError if ResultMessage indicates an error.
241
241
  """
242
242
  async for message in self.receive_messages():
243
- yield message
243
+ # Check for execution error BEFORE yielding
244
244
  if isinstance(message, ResultMessage):
245
+ if message.is_error and message.errors and len(message.errors) > 0:
246
+ raise ExecutionError(message.errors, message.subtype)
247
+ yield message
248
+ return
249
+
250
+ yield message
251
+
252
+ if isinstance(message, ErrorMessage):
245
253
  return
246
254
 
247
255
  async def interrupt(self) -> None:
@@ -287,12 +295,17 @@ class CodeBuddySDKClient:
287
295
  self._transport = None
288
296
  self._connected = False
289
297
 
290
- async def __aenter__(self) -> "CodeBuddySDKClient":
298
+ async def __aenter__(self) -> CodeBuddySDKClient:
291
299
  """Enter async context - automatically connects."""
292
300
  await self.connect()
293
301
  return self
294
302
 
295
- async def __aexit__(self, *args: Any) -> bool:
303
+ async def __aexit__(
304
+ self,
305
+ exc_type: type[BaseException] | None,
306
+ exc_val: BaseException | None,
307
+ exc_tb: TracebackType | None,
308
+ ) -> bool:
296
309
  """Exit async context - always disconnects."""
297
310
  await self.disconnect()
298
311
  return False
@@ -0,0 +1,35 @@
1
+ """
2
+ MCP (Model Context Protocol) Integration
3
+
4
+ This module provides utilities for creating and managing SDK MCP servers
5
+ that can be integrated with the CLI via the control protocol.
6
+ """
7
+
8
+ from .create_sdk_mcp_server import (
9
+ create_sdk_mcp_server,
10
+ tool,
11
+ )
12
+ from .sdk_control_server_transport import SdkControlServerTransport
13
+ from .types import (
14
+ CallToolResult,
15
+ SdkMcpServerOptions,
16
+ SdkMcpServerResult,
17
+ SdkMcpToolDefinition,
18
+ TextContent,
19
+ ToolHandler,
20
+ )
21
+
22
+ __all__ = [
23
+ # Factory functions
24
+ "create_sdk_mcp_server",
25
+ "tool",
26
+ # Transport
27
+ "SdkControlServerTransport",
28
+ # Types
29
+ "SdkMcpServerOptions",
30
+ "SdkMcpServerResult",
31
+ "SdkMcpToolDefinition",
32
+ "ToolHandler",
33
+ "CallToolResult",
34
+ "TextContent",
35
+ ]
@@ -0,0 +1,154 @@
1
+ """
2
+ create_sdk_mcp_server - Create an SDK MCP Server
3
+
4
+ This function creates an MCP server that can be integrated into the SDK
5
+ and used with the CLI via the control protocol.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from collections.abc import Awaitable, Callable
11
+ from typing import Any, TypeVar
12
+
13
+ from .types import (
14
+ CallToolResult,
15
+ SdkMcpServer,
16
+ SdkMcpServerOptions,
17
+ SdkMcpServerResult,
18
+ SdkMcpToolDefinition,
19
+ ToolInputSchema,
20
+ )
21
+
22
+ # Tool handler type
23
+ ToolHandler = Callable[[dict[str, Any]], CallToolResult | Awaitable[CallToolResult]]
24
+
25
+ # Type variable for the decorated function
26
+ F = TypeVar("F", bound=ToolHandler)
27
+
28
+
29
+ def _python_type_to_json_schema(py_type: type) -> dict[str, Any]:
30
+ """Convert Python type to JSON Schema type."""
31
+ type_mapping = {
32
+ str: {"type": "string"},
33
+ int: {"type": "integer"},
34
+ float: {"type": "number"},
35
+ bool: {"type": "boolean"},
36
+ list: {"type": "array"},
37
+ dict: {"type": "object"},
38
+ }
39
+ return type_mapping.get(py_type, {"type": "string"})
40
+
41
+
42
+ def _convert_schema(input_schema: dict[str, Any]) -> tuple[dict[str, dict[str, Any]], list[str]]:
43
+ """
44
+ Convert input schema to JSON Schema format.
45
+
46
+ Supports multiple formats:
47
+ 1. Simple type mapping: {"latitude": float, "longitude": float}
48
+ 2. JSON Schema format: {"properties": {...}, "required": [...]}
49
+
50
+ Returns:
51
+ Tuple of (properties dict, required list)
52
+ """
53
+ # Check if it's already in JSON Schema format
54
+ if "properties" in input_schema:
55
+ return input_schema.get("properties", {}), input_schema.get("required", [])
56
+
57
+ if "type" in input_schema and input_schema.get("type") == "object":
58
+ return input_schema.get("properties", {}), input_schema.get("required", [])
59
+
60
+ # Simple type mapping format: {"param_name": type}
61
+ properties = {}
62
+ required = []
63
+
64
+ for param_name, param_type in input_schema.items():
65
+ if isinstance(param_type, type):
66
+ properties[param_name] = _python_type_to_json_schema(param_type)
67
+ required.append(param_name)
68
+ elif isinstance(param_type, dict):
69
+ properties[param_name] = param_type
70
+ if "default" not in param_type:
71
+ required.append(param_name)
72
+ else:
73
+ properties[param_name] = {"type": "string"}
74
+ required.append(param_name)
75
+
76
+ return properties, required
77
+
78
+
79
+ def tool(name: str, description: str, input_schema: dict[str, Any]) -> Callable[[F], F]:
80
+ """
81
+ Decorator to define an MCP tool.
82
+
83
+ Example:
84
+ ```python
85
+ @tool("get_weather", "Get current weather", {"latitude": float, "longitude": float})
86
+ async def get_weather(args: dict[str, Any]) -> dict[str, Any]:
87
+ return {"content": [{"type": "text", "text": f"Weather: sunny"}]}
88
+ ```
89
+
90
+ Args:
91
+ name: Tool name (unique within the server)
92
+ description: Tool description
93
+ input_schema: Input parameters schema. Supports:
94
+ - Simple types: {"latitude": float, "longitude": float}
95
+ - JSON Schema: {"properties": {...}, "required": [...]}
96
+
97
+ Returns:
98
+ Decorated function with tool metadata attached
99
+ """
100
+ properties, required = _convert_schema(input_schema)
101
+ tool_schema = ToolInputSchema(type="object", properties=properties, required=required)
102
+
103
+ def decorator(func: F) -> F:
104
+ func._tool_definition = SdkMcpToolDefinition( # type: ignore[attr-defined]
105
+ name=name,
106
+ description=description,
107
+ input_schema=tool_schema,
108
+ handler=func,
109
+ )
110
+ return func
111
+
112
+ return decorator
113
+
114
+
115
+ def create_sdk_mcp_server(
116
+ name: str,
117
+ version: str = "1.0.0",
118
+ tools: list[Callable[..., Any]] | None = None,
119
+ ) -> SdkMcpServerResult:
120
+ """
121
+ Create an SDK MCP Server.
122
+
123
+ Args:
124
+ name: Server name (unique within the session)
125
+ version: Server version (defaults to "1.0.0")
126
+ tools: List of functions decorated with @tool
127
+
128
+ Returns:
129
+ SDK MCP server result for use with query()
130
+
131
+ Example:
132
+ ```python
133
+ from codebuddy_agent_sdk import create_sdk_mcp_server, tool, query
134
+
135
+ @tool("get_weather", "Get weather", {"location": str})
136
+ async def get_weather(args: dict) -> dict:
137
+ return {"content": [{"type": "text", "text": f"Weather: sunny"}]}
138
+
139
+ server = create_sdk_mcp_server(
140
+ name="my-server",
141
+ tools=[get_weather]
142
+ )
143
+ ```
144
+ """
145
+ tool_definitions: list[SdkMcpToolDefinition] = []
146
+ if tools:
147
+ for func in tools:
148
+ if not hasattr(func, "_tool_definition"):
149
+ raise ValueError(f"Function {func.__name__} is not decorated with @tool")
150
+ tool_definitions.append(func._tool_definition)
151
+
152
+ server = SdkMcpServer(SdkMcpServerOptions(name=name, version=version, tools=tool_definitions))
153
+
154
+ return SdkMcpServerResult(type="sdk", name=name, server=server)
@@ -0,0 +1,95 @@
1
+ """
2
+ SDK Control Server Transport
3
+
4
+ Custom transport implementation that bridges SDK MCP servers to CLI process.
5
+ This transport forwards MCP messages through the control protocol.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from collections.abc import Callable
11
+
12
+ from .types import JSONRPCMessage
13
+
14
+ # Callback function type for sending MCP messages to CLI
15
+ SendMcpMessageCallback = Callable[[JSONRPCMessage], None]
16
+
17
+
18
+ class SdkControlServerTransport:
19
+ """
20
+ SdkControlServerTransport - bridges MCP servers to CLI via control messages.
21
+
22
+ This transport implements a simple interface for forwarding MCP messages
23
+ between the SDK MCP server and the CLI via the control protocol.
24
+ """
25
+
26
+ def __init__(self, send_mcp_message: SendMcpMessageCallback):
27
+ """
28
+ Create a new SDK Control Server Transport.
29
+
30
+ Args:
31
+ send_mcp_message: Callback function to forward MCP messages to CLI
32
+ """
33
+ self._send_mcp_message = send_mcp_message
34
+ self._is_closed = False
35
+ self._on_message: Callable[[JSONRPCMessage], None] | None = None
36
+ self._on_close: Callable[[], None] | None = None
37
+ self._on_error: Callable[[Exception], None] | None = None
38
+
39
+ @property
40
+ def closed(self) -> bool:
41
+ """Check if the transport is closed."""
42
+ return self._is_closed
43
+
44
+ def set_on_message(self, callback: Callable[[JSONRPCMessage], None] | None) -> None:
45
+ """Set the message callback."""
46
+ self._on_message = callback
47
+
48
+ def set_on_close(self, callback: Callable[[], None] | None) -> None:
49
+ """Set the close callback."""
50
+ self._on_close = callback
51
+
52
+ def set_on_error(self, callback: Callable[[Exception], None] | None) -> None:
53
+ """Set the error callback."""
54
+ self._on_error = callback
55
+
56
+ async def start(self) -> None:
57
+ """
58
+ Start the transport.
59
+ No-op since connection is already established via stdio.
60
+ """
61
+ pass
62
+
63
+ async def send(self, message: JSONRPCMessage) -> None:
64
+ """
65
+ Send a message to the CLI via control_request.
66
+
67
+ Args:
68
+ message: The JSON-RPC message to send
69
+ """
70
+ if self._is_closed:
71
+ raise RuntimeError("Transport is closed")
72
+ # Forward message to CLI via control_request
73
+ self._send_mcp_message(message)
74
+
75
+ async def close(self) -> None:
76
+ """Close the transport."""
77
+ if self._is_closed:
78
+ return
79
+
80
+ self._is_closed = True
81
+ if self._on_close:
82
+ self._on_close()
83
+
84
+ def handle_incoming_message(self, message: JSONRPCMessage) -> None:
85
+ """
86
+ Handle incoming message from CLI.
87
+ This method should be called when the CLI sends a message to this server.
88
+
89
+ Args:
90
+ message: The JSON-RPC message from CLI
91
+ """
92
+ if self._is_closed:
93
+ return
94
+ if self._on_message:
95
+ self._on_message(message)
@@ -0,0 +1,300 @@
1
+ """Type definitions for SDK MCP Server."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import inspect
6
+ from collections.abc import Awaitable, Callable
7
+ from dataclasses import dataclass, field
8
+ from typing import TYPE_CHECKING, Any, Literal, TypedDict, cast
9
+
10
+ if TYPE_CHECKING:
11
+ from .sdk_control_server_transport import SdkControlServerTransport
12
+
13
+
14
+ # ============= JSON-RPC Types =============
15
+
16
+
17
+ class JSONRPCRequest(TypedDict, total=False):
18
+ """JSON-RPC 2.0 request."""
19
+
20
+ jsonrpc: Literal["2.0"]
21
+ id: str | int
22
+ method: str
23
+ params: dict[str, Any] | list[Any] | None
24
+
25
+
26
+ class JSONRPCError(TypedDict, total=False):
27
+ """JSON-RPC 2.0 error."""
28
+
29
+ code: int
30
+ message: str
31
+ data: Any
32
+
33
+
34
+ class JSONRPCResponse(TypedDict, total=False):
35
+ """JSON-RPC 2.0 response."""
36
+
37
+ jsonrpc: Literal["2.0"]
38
+ id: str | int | None
39
+ result: Any
40
+ error: JSONRPCError
41
+
42
+
43
+ class JSONRPCNotification(TypedDict, total=False):
44
+ """JSON-RPC 2.0 notification."""
45
+
46
+ jsonrpc: Literal["2.0"]
47
+ method: str
48
+ params: dict[str, Any] | list[Any] | None
49
+
50
+
51
+ JSONRPCMessage = JSONRPCRequest | JSONRPCResponse | JSONRPCNotification
52
+
53
+
54
+ # ============= MCP Tool Types =============
55
+
56
+
57
+ class TextContent(TypedDict, total=False):
58
+ """Text content in tool result."""
59
+
60
+ type: Literal["text"]
61
+ text: str
62
+
63
+
64
+ class ImageContent(TypedDict, total=False):
65
+ """Image content in tool result."""
66
+
67
+ type: Literal["image"]
68
+ data: str
69
+ mimeType: str
70
+
71
+
72
+ class EmbeddedResource(TypedDict, total=False):
73
+ """Embedded resource content in tool result."""
74
+
75
+ type: Literal["resource"]
76
+ resource: dict[str, Any]
77
+
78
+
79
+ ToolResultContent = TextContent | ImageContent | EmbeddedResource
80
+
81
+
82
+ class CallToolResult(TypedDict, total=False):
83
+ """Result from calling a tool."""
84
+
85
+ content: list[ToolResultContent]
86
+ isError: bool
87
+
88
+
89
+ # Tool handler type - takes arguments dict and returns CallToolResult
90
+ ToolHandler = Callable[[dict[str, Any]], CallToolResult | Awaitable[CallToolResult]]
91
+
92
+
93
+ @dataclass
94
+ class ToolInputProperty:
95
+ """Property definition for tool input schema."""
96
+
97
+ type: str
98
+ description: str | None = None
99
+ enum: list[str] | None = None
100
+ default: Any = None
101
+ minimum: float | None = None
102
+ maximum: float | None = None
103
+
104
+
105
+ @dataclass
106
+ class ToolInputSchema:
107
+ """JSON Schema for tool input."""
108
+
109
+ type: Literal["object"] = "object"
110
+ properties: dict[str, dict[str, Any]] = field(default_factory=dict)
111
+ required: list[str] = field(default_factory=list)
112
+
113
+
114
+ @dataclass
115
+ class SdkMcpToolDefinition:
116
+ """
117
+ Tool definition for SDK MCP Server.
118
+
119
+ Example:
120
+ ```python
121
+ tool_def = SdkMcpToolDefinition(
122
+ name="get_weather",
123
+ description="Get the current weather for a location",
124
+ input_schema=ToolInputSchema(
125
+ properties={
126
+ "location": {"type": "string", "description": "The city name"},
127
+ "units": {"type": "string", "enum": ["celsius", "fahrenheit"]},
128
+ },
129
+ required=["location"],
130
+ ),
131
+ handler=get_weather_handler,
132
+ )
133
+ ```
134
+ """
135
+
136
+ name: str
137
+ description: str
138
+ input_schema: ToolInputSchema
139
+ handler: ToolHandler
140
+
141
+
142
+ @dataclass
143
+ class SdkMcpServerOptions:
144
+ """
145
+ Options for creating an SDK MCP Server.
146
+
147
+ Attributes:
148
+ name: Server name (must be unique within the session)
149
+ version: Server version (defaults to "1.0.0")
150
+ tools: List of tool definitions to register
151
+ """
152
+
153
+ name: str
154
+ version: str = "1.0.0"
155
+ tools: list[SdkMcpToolDefinition] = field(default_factory=list)
156
+
157
+
158
+ @dataclass
159
+ class SdkMcpServerResult:
160
+ """
161
+ Result type for create_sdk_mcp_server.
162
+
163
+ Attributes:
164
+ type: Type discriminator - always "sdk" for SDK MCP servers
165
+ name: Server name
166
+ server: The MCP server instance
167
+ """
168
+
169
+ type: Literal["sdk"]
170
+ name: str
171
+ server: SdkMcpServer
172
+
173
+
174
+ class SdkMcpServer:
175
+ """
176
+ SDK MCP Server implementation.
177
+
178
+ This class implements an MCP server that runs within the SDK process
179
+ and communicates with the CLI via the control protocol.
180
+ """
181
+
182
+ def __init__(self, options: SdkMcpServerOptions):
183
+ self.name = options.name
184
+ self.version = options.version
185
+ self.tools: dict[str, SdkMcpToolDefinition] = {}
186
+ self._transport: SdkControlServerTransport | None = None
187
+
188
+ # Register tools
189
+ for tool_def in options.tools:
190
+ self.tools[tool_def.name] = tool_def
191
+
192
+ def connect(self, transport: SdkControlServerTransport) -> None:
193
+ """Connect the server to a transport."""
194
+ self._transport = transport
195
+
196
+ async def handle_message(self, message: JSONRPCMessage) -> JSONRPCMessage | None:
197
+ """Handle an incoming JSON-RPC message."""
198
+ # Check if it's a request (has method and id)
199
+ if "method" not in message:
200
+ return None
201
+
202
+ method = cast(str, message.get("method", ""))
203
+ msg_id = cast("str | int | None", message.get("id"))
204
+ params = cast("dict[str, Any] | None", message.get("params", {}))
205
+
206
+ if method == "initialize":
207
+ return await self._handle_initialize(msg_id, params)
208
+ elif method == "tools/list":
209
+ return await self._handle_tools_list(msg_id)
210
+ elif method == "tools/call":
211
+ return await self._handle_tools_call(msg_id, params)
212
+ elif method == "notifications/initialized":
213
+ # Notification, no response needed
214
+ return None
215
+ else:
216
+ # Unknown method
217
+ if msg_id is not None:
218
+ return self._create_error_response(msg_id, -32601, f"Method not found: {method}")
219
+ return None
220
+
221
+ async def _handle_initialize(
222
+ self, msg_id: str | int | None, params: dict[str, Any] | None
223
+ ) -> JSONRPCMessage:
224
+ """Handle initialize request."""
225
+ result = {
226
+ "protocolVersion": "2024-11-05",
227
+ "capabilities": {
228
+ "tools": {},
229
+ },
230
+ "serverInfo": {
231
+ "name": self.name,
232
+ "version": self.version,
233
+ },
234
+ }
235
+ return self._create_response(msg_id, result)
236
+
237
+ async def _handle_tools_list(self, msg_id: str | int | None) -> JSONRPCMessage:
238
+ """Handle tools/list request."""
239
+ tools_list = []
240
+ for tool_def in self.tools.values():
241
+ tools_list.append(
242
+ {
243
+ "name": tool_def.name,
244
+ "description": tool_def.description,
245
+ "inputSchema": {
246
+ "type": tool_def.input_schema.type,
247
+ "properties": tool_def.input_schema.properties,
248
+ "required": tool_def.input_schema.required,
249
+ },
250
+ }
251
+ )
252
+ return self._create_response(msg_id, {"tools": tools_list})
253
+
254
+ async def _handle_tools_call(
255
+ self, msg_id: str | int | None, params: dict[str, Any] | None
256
+ ) -> JSONRPCMessage:
257
+ """Handle tools/call request."""
258
+ if not isinstance(params, dict):
259
+ return self._create_error_response(msg_id, -32602, "Invalid params")
260
+
261
+ tool_name = params.get("name", "")
262
+ arguments = params.get("arguments", {})
263
+
264
+ tool_def = self.tools.get(tool_name)
265
+ if not tool_def:
266
+ return self._create_error_response(msg_id, -32602, f"Tool not found: {tool_name}")
267
+
268
+ try:
269
+ # Call the handler
270
+ result = tool_def.handler(arguments)
271
+ # Handle async handlers
272
+ if inspect.isawaitable(result):
273
+ result = await result
274
+
275
+ return self._create_response(msg_id, result)
276
+ except Exception as e:
277
+ # Return error as tool result
278
+ error_result: CallToolResult = {
279
+ "content": [{"type": "text", "text": str(e)}],
280
+ "isError": True,
281
+ }
282
+ return self._create_response(msg_id, error_result)
283
+
284
+ def _create_response(self, msg_id: str | int | None, result: Any) -> JSONRPCResponse:
285
+ """Create a JSON-RPC response."""
286
+ return {
287
+ "jsonrpc": "2.0",
288
+ "id": msg_id,
289
+ "result": result,
290
+ }
291
+
292
+ def _create_error_response(
293
+ self, msg_id: str | int | None, code: int, message: str
294
+ ) -> JSONRPCResponse:
295
+ """Create a JSON-RPC error response."""
296
+ return {
297
+ "jsonrpc": "2.0",
298
+ "id": msg_id,
299
+ "error": {"code": code, "message": message},
300
+ }
@@ -1,23 +1,185 @@
1
1
  """Query function for one-shot interactions with CodeBuddy."""
2
2
 
3
+ from __future__ import annotations
4
+
5
+ import asyncio
3
6
  import json
4
7
  import os
5
- from collections.abc import AsyncIterable, AsyncIterator
8
+ from collections.abc import AsyncIterable, AsyncIterator, Callable
6
9
  from dataclasses import asdict
7
- from typing import Any
10
+ from typing import Any, TypeGuard
8
11
 
12
+ from ._errors import ExecutionError
9
13
  from ._message_parser import parse_message
14
+ from .mcp.sdk_control_server_transport import SdkControlServerTransport
15
+ from .mcp.types import JSONRPCMessage, SdkMcpServer
10
16
  from .transport import SubprocessTransport, Transport
11
17
  from .types import (
12
18
  AppendSystemPrompt,
13
19
  CanUseToolOptions,
14
20
  CodeBuddyAgentOptions,
21
+ ErrorMessage,
22
+ HookEvent,
15
23
  HookMatcher,
24
+ McpSdkServerConfig,
25
+ McpServerConfig,
16
26
  Message,
17
27
  ResultMessage,
18
28
  )
19
29
 
20
30
 
31
+ def _is_sdk_mcp_server(config: McpServerConfig) -> TypeGuard[McpSdkServerConfig]:
32
+ """Type guard to check if config is an SDK MCP server."""
33
+ return isinstance(config, dict) and config.get("type") == "sdk"
34
+
35
+
36
+ def _is_valid_hook_event(event: str) -> TypeGuard[HookEvent]:
37
+ """Type guard to check if event is a valid HookEvent."""
38
+ return event in {
39
+ "PreToolUse",
40
+ "PostToolUse",
41
+ "UserPromptSubmit",
42
+ "Stop",
43
+ "SubagentStop",
44
+ "PreCompact",
45
+ }
46
+
47
+
48
+ class QueryContext:
49
+ """Context for managing query state including SDK MCP servers."""
50
+
51
+ def __init__(self, options: CodeBuddyAgentOptions):
52
+ self.options = options
53
+ # SDK MCP Server management
54
+ self.sdk_mcp_transports: dict[str, SdkControlServerTransport] = {}
55
+ self.sdk_mcp_servers: dict[str, SdkMcpServer] = {}
56
+ self.pending_mcp_responses: dict[str, asyncio.Future[JSONRPCMessage]] = {}
57
+ self.sdk_mcp_server_names: list[str] = []
58
+
59
+ def extract_mcp_servers(
60
+ self,
61
+ ) -> tuple[dict[str, SdkMcpServer], dict[str, McpServerConfig] | None]:
62
+ """
63
+ Extract SDK MCP servers from the mcp_servers config.
64
+ SDK servers are identified by having type: 'sdk'.
65
+
66
+ Returns:
67
+ Tuple of (sdk_servers dict, regular_servers dict or None)
68
+ """
69
+ mcp_servers = self.options.mcp_servers
70
+
71
+ if not mcp_servers or not isinstance(mcp_servers, dict):
72
+ return {}, None
73
+
74
+ sdk_servers: dict[str, SdkMcpServer] = {}
75
+ regular_servers: dict[str, McpServerConfig] = {}
76
+
77
+ for name, config in mcp_servers.items():
78
+ if _is_sdk_mcp_server(config):
79
+ # SDK MCP server
80
+ sdk_servers[name] = config["server"]
81
+ self.sdk_mcp_server_names.append(name)
82
+ else:
83
+ # Regular MCP server (stdio)
84
+ regular_servers[name] = config
85
+
86
+ return (
87
+ sdk_servers,
88
+ regular_servers if regular_servers else None,
89
+ )
90
+
91
+ def connect_sdk_mcp_server(
92
+ self,
93
+ name: str,
94
+ server: SdkMcpServer,
95
+ send_callback: Callable[[str, JSONRPCMessage], None],
96
+ ) -> None:
97
+ """Connect an SDK MCP server."""
98
+
99
+ def _create_message_forwarder(
100
+ server_name: str,
101
+ ) -> Callable[[JSONRPCMessage], None]:
102
+ def forwarder(msg: JSONRPCMessage) -> None:
103
+ send_callback(server_name, msg)
104
+
105
+ return forwarder
106
+
107
+ # Create custom transport that forwards to CLI
108
+ transport = SdkControlServerTransport(_create_message_forwarder(name))
109
+
110
+ # Store transport and server
111
+ self.sdk_mcp_transports[name] = transport
112
+ self.sdk_mcp_servers[name] = server
113
+
114
+ # Connect server to transport
115
+ server.connect(transport)
116
+
117
+ async def handle_mcp_message_request(
118
+ self,
119
+ transport: Transport,
120
+ request_id: str,
121
+ request: dict[str, Any],
122
+ ) -> None:
123
+ """Handle MCP message control request from CLI."""
124
+ server_name = request.get("server_name", "")
125
+ message: JSONRPCMessage = request.get("message", {})
126
+
127
+ server = self.sdk_mcp_servers.get(server_name)
128
+
129
+ if not server:
130
+ response = {
131
+ "type": "control_response",
132
+ "response": {
133
+ "subtype": "error",
134
+ "request_id": request_id,
135
+ "error": f"SDK MCP server not found: {server_name}",
136
+ },
137
+ }
138
+ await transport.write(json.dumps(response))
139
+ return
140
+
141
+ try:
142
+ # Handle the message with the MCP server
143
+ mcp_response = await server.handle_message(message)
144
+
145
+ response = {
146
+ "type": "control_response",
147
+ "response": {
148
+ "subtype": "success",
149
+ "request_id": request_id,
150
+ "response": {
151
+ "mcp_response": mcp_response or {"jsonrpc": "2.0", "result": {}, "id": 0},
152
+ },
153
+ },
154
+ }
155
+ await transport.write(json.dumps(response))
156
+
157
+ except Exception as e:
158
+ response = {
159
+ "type": "control_response",
160
+ "response": {
161
+ "subtype": "error",
162
+ "request_id": request_id,
163
+ "error": str(e),
164
+ },
165
+ }
166
+ await transport.write(json.dumps(response))
167
+
168
+ async def cleanup(self) -> None:
169
+ """Cleanup all resources."""
170
+ # Cancel pending MCP responses
171
+ for future in self.pending_mcp_responses.values():
172
+ if not future.done():
173
+ future.cancel()
174
+ self.pending_mcp_responses.clear()
175
+
176
+ # Close all SDK MCP transports
177
+ for transport in self.sdk_mcp_transports.values():
178
+ await transport.close()
179
+ self.sdk_mcp_transports.clear()
180
+ self.sdk_mcp_servers.clear()
181
+
182
+
21
183
  async def query(
22
184
  *,
23
185
  prompt: str | AsyncIterable[dict[str, Any]],
@@ -52,13 +214,53 @@ async def query(
52
214
 
53
215
  os.environ["CODEBUDDY_CODE_ENTRYPOINT"] = "sdk-py"
54
216
 
217
+ # Create query context for managing SDK MCP servers
218
+ ctx = QueryContext(options)
219
+
220
+ # Extract SDK MCP servers and regular MCP servers
221
+ sdk_servers, regular_servers = ctx.extract_mcp_servers()
222
+
223
+ # Create modified options with only regular MCP servers for subprocess
224
+ modified_options = CodeBuddyAgentOptions(
225
+ allowed_tools=options.allowed_tools,
226
+ disallowed_tools=options.disallowed_tools,
227
+ system_prompt=options.system_prompt,
228
+ mcp_servers=regular_servers or {},
229
+ permission_mode=options.permission_mode,
230
+ continue_conversation=options.continue_conversation,
231
+ resume=options.resume,
232
+ max_turns=options.max_turns,
233
+ model=options.model,
234
+ fallback_model=options.fallback_model,
235
+ cwd=options.cwd,
236
+ codebuddy_code_path=options.codebuddy_code_path,
237
+ env=options.env,
238
+ extra_args=options.extra_args,
239
+ stderr=options.stderr,
240
+ hooks=options.hooks,
241
+ include_partial_messages=options.include_partial_messages,
242
+ fork_session=options.fork_session,
243
+ agents=options.agents,
244
+ setting_sources=options.setting_sources,
245
+ can_use_tool=options.can_use_tool,
246
+ )
247
+
55
248
  if transport is None:
56
- transport = SubprocessTransport(options=options, prompt=prompt)
249
+ transport = SubprocessTransport(options=modified_options, prompt=prompt)
57
250
 
58
251
  await transport.connect()
59
252
 
253
+ # Connect SDK MCP servers
254
+ def send_mcp_message(_server_name: str, _message: JSONRPCMessage) -> None:
255
+ # This callback sends MCP messages from server to CLI
256
+ # For now, SDK servers only respond to CLI requests, so this is rarely used
257
+ pass
258
+
259
+ for name, server in sdk_servers.items():
260
+ ctx.connect_sdk_mcp_server(name, server, send_mcp_message)
261
+
60
262
  try:
61
- await _send_initialize(transport, options)
263
+ await _send_initialize(transport, options, ctx.sdk_mcp_server_names)
62
264
  await _send_prompt(transport, prompt)
63
265
 
64
266
  async for line in transport.read():
@@ -68,35 +270,45 @@ async def query(
68
270
  try:
69
271
  data = json.loads(line)
70
272
 
71
- # Handle control requests (hooks)
273
+ # Handle control requests (hooks, permissions, MCP messages)
72
274
  if data.get("type") == "control_request":
73
- await _handle_control_request(transport, data, options)
275
+ await _handle_control_request(transport, data, options, ctx)
74
276
  continue
75
277
 
76
278
  message = parse_message(data)
77
279
  if message:
280
+ # Check for execution error BEFORE yielding
281
+ if isinstance(message, ResultMessage):
282
+ if message.is_error and message.errors and len(message.errors) > 0:
283
+ raise ExecutionError(message.errors, message.subtype)
284
+ yield message
285
+ break
286
+
78
287
  yield message
79
288
 
80
- if isinstance(message, ResultMessage):
289
+ if isinstance(message, ErrorMessage):
81
290
  break
82
291
 
83
292
  except json.JSONDecodeError:
84
293
  continue # Ignore non-JSON lines
85
294
 
86
295
  finally:
296
+ await ctx.cleanup()
87
297
  await transport.close()
88
298
 
89
299
 
90
- async def _send_initialize(transport: Transport, options: CodeBuddyAgentOptions) -> None:
300
+ async def _send_initialize(
301
+ transport: Transport,
302
+ options: CodeBuddyAgentOptions,
303
+ sdk_mcp_server_names: list[str],
304
+ ) -> None:
91
305
  """Send initialization control request."""
92
306
  hooks_config = _build_hooks_config(options.hooks) if options.hooks else None
93
307
  agents_config = (
94
- {name: asdict(agent) for name, agent in options.agents.items()}
95
- if options.agents
96
- else None
308
+ {name: asdict(agent) for name, agent in options.agents.items()} if options.agents else None
97
309
  )
98
310
 
99
- # 解析 system_prompt 配置
311
+ # Parse system_prompt config
100
312
  system_prompt: str | None = None
101
313
  append_system_prompt: str | None = None
102
314
  if isinstance(options.system_prompt, str):
@@ -113,14 +325,14 @@ async def _send_initialize(transport: Transport, options: CodeBuddyAgentOptions)
113
325
  "systemPrompt": system_prompt,
114
326
  "appendSystemPrompt": append_system_prompt,
115
327
  "agents": agents_config,
328
+ # Include SDK MCP server names
329
+ "sdkMcpServers": sdk_mcp_server_names if sdk_mcp_server_names else None,
116
330
  },
117
331
  }
118
332
  await transport.write(json.dumps(request))
119
333
 
120
334
 
121
- async def _send_prompt(
122
- transport: Transport, prompt: str | AsyncIterable[dict[str, Any]]
123
- ) -> None:
335
+ async def _send_prompt(transport: Transport, prompt: str | AsyncIterable[dict[str, Any]]) -> None:
124
336
  """Send user prompt."""
125
337
  if isinstance(prompt, str):
126
338
  message = {
@@ -139,6 +351,7 @@ async def _handle_control_request(
139
351
  transport: Transport,
140
352
  data: dict[str, Any],
141
353
  options: CodeBuddyAgentOptions,
354
+ ctx: QueryContext,
142
355
  ) -> None:
143
356
  """Handle control request from CLI."""
144
357
  request_id = data.get("request_id", "")
@@ -168,6 +381,9 @@ async def _handle_control_request(
168
381
  elif subtype == "can_use_tool":
169
382
  await _handle_permission_request(transport, request_id, request, options)
170
383
 
384
+ elif subtype == "mcp_message":
385
+ await ctx.handle_mcp_message_request(transport, request_id, request)
386
+
171
387
 
172
388
  async def _handle_permission_request(
173
389
  transport: Transport,
@@ -268,14 +484,19 @@ async def _execute_hook(
268
484
  return {"continue_": True}
269
485
 
270
486
  event = parts[1]
487
+
488
+ # Validate event is a known HookEvent using TypeGuard
489
+ if not _is_valid_hook_event(event):
490
+ return {"continue_": True}
491
+
271
492
  try:
272
493
  matcher_idx = int(parts[2])
273
494
  hook_idx = int(parts[3])
274
495
  except ValueError:
275
496
  return {"continue_": True}
276
497
 
277
- # Find the hook
278
- matchers = options.hooks.get(event) # type: ignore[arg-type]
498
+ # Find the hook - event is now narrowed to HookEvent by TypeGuard
499
+ matchers = options.hooks.get(event)
279
500
  if not matchers or matcher_idx >= len(matchers):
280
501
  return {"continue_": True}
281
502
 
@@ -305,9 +526,7 @@ def _build_hooks_config(
305
526
  config[str(event)] = [
306
527
  {
307
528
  "matcher": m.matcher,
308
- "hookCallbackIds": [
309
- f"hook_{event}_{i}_{j}" for j, _ in enumerate(m.hooks)
310
- ],
529
+ "hookCallbackIds": [f"hook_{event}_{i}_{j}" for j, _ in enumerate(m.hooks)],
311
530
  "timeout": m.timeout,
312
531
  }
313
532
  for i, m in enumerate(matchers)
@@ -1,5 +1,7 @@
1
1
  """Transport base class for CLI communication."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  from abc import ABC, abstractmethod
4
6
  from collections.abc import AsyncIterator
5
7
 
@@ -1,5 +1,7 @@
1
1
  """Subprocess transport for CLI communication."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import asyncio
4
6
  import json
5
7
  import os
@@ -81,8 +83,10 @@ class SubprocessTransport(Transport):
81
83
  # When setting_sources is explicitly provided (including empty list), use it
82
84
  # When not provided (None), default to 'none' for SDK isolation
83
85
  if opts.setting_sources is not None:
84
- value = "none" if len(opts.setting_sources) == 0 else ",".join(opts.setting_sources)
85
- args.extend(["--setting-sources", value])
86
+ setting_value = (
87
+ "none" if len(opts.setting_sources) == 0 else ",".join(opts.setting_sources)
88
+ )
89
+ args.extend(["--setting-sources", setting_value])
86
90
  else:
87
91
  # SDK default behavior: no filesystem settings loaded
88
92
  args.extend(["--setting-sources", "none"])
@@ -1,9 +1,14 @@
1
1
  """Type definitions for CodeBuddy Agent SDK."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  from collections.abc import Awaitable, Callable
4
6
  from dataclasses import dataclass, field
5
7
  from pathlib import Path
6
- from typing import Any, Literal, NotRequired, TypedDict
8
+ from typing import TYPE_CHECKING, Any, Literal, NotRequired, TypedDict
9
+
10
+ if TYPE_CHECKING:
11
+ from .mcp.types import SdkMcpServer
7
12
 
8
13
  # ============= AskUserQuestion Types =============
9
14
 
@@ -183,6 +188,7 @@ class ResultMessage:
183
188
  total_cost_usd: float | None = None
184
189
  usage: dict[str, Any] | None = None
185
190
  result: str | None = None
191
+ errors: list[str] | None = None
186
192
 
187
193
 
188
194
  @dataclass
@@ -195,7 +201,17 @@ class StreamEvent:
195
201
  parent_tool_use_id: str | None = None
196
202
 
197
203
 
198
- Message = UserMessage | AssistantMessage | SystemMessage | ResultMessage | StreamEvent
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
+ )
199
215
 
200
216
 
201
217
  # Hook types
@@ -242,7 +258,18 @@ class McpStdioServerConfig(TypedDict):
242
258
  env: NotRequired[dict[str, str]]
243
259
 
244
260
 
245
- McpServerConfig = McpStdioServerConfig
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
246
273
 
247
274
 
248
275
  # System prompt configuration
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codebuddy-agent-sdk
3
- Version: 0.1.8
3
+ Version: 0.2.0
4
4
  Summary: CodeBuddy Code SDK for Python
5
5
  Author-email: ninoyi <ninoyi@tencent.com>
6
6
  Keywords: agent,ai,codebuddy,sdk
@@ -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=EhZlhQ69654jiXymzw5m6O61qdW_fo3ug1b4ABdOtm4,74
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=pfQZlfnGCrEocOsuQPXb8bozsVAPnWKmV62w-EhGVeE,119690167
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.2.0.dist-info/METADATA,sha256=vATnYz7KEZ_IgyQqOsWBGLiVxdD-Z-_MV1RvKMK4TS0,2538
19
+ codebuddy_agent_sdk-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
20
+ codebuddy_agent_sdk-0.2.0.dist-info/RECORD,,
@@ -1,16 +0,0 @@
1
- codebuddy_agent_sdk/__init__.py,sha256=MF75KZvSF3PTGk9OwjFEW5R7-vI59lexKn5e9-FY1Dw,2052
2
- codebuddy_agent_sdk/_binary.py,sha256=7UBOzgooaojNAlb3tFvsN12TBAndcP3YLGD3Ic3LlmI,4302
3
- codebuddy_agent_sdk/_errors.py,sha256=5ABIym6mazInI33kmt5mR94v6c325jDbi0YCgQjrysY,816
4
- codebuddy_agent_sdk/_message_parser.py,sha256=U0dy1nqe2kap0_A_VWQN-KME-HEQmDhZQcCbCLVMe4s,3255
5
- codebuddy_agent_sdk/_version.py,sha256=Pi_yM0znG_C1DcEEzAPpeZEEBjrYZVv-JFkiLHeMm98,74
6
- codebuddy_agent_sdk/client.py,sha256=Bl6l2KFXkSs4j9usuzM7JMYsEO9Np4p6Csg9fR42LtI,10191
7
- codebuddy_agent_sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- codebuddy_agent_sdk/query.py,sha256=LNv4LIdzdg-fRunMlIicu0SAEv5AoAPpu3_w7iDGhYw,9544
9
- codebuddy_agent_sdk/types.py,sha256=PYCgC6rAElM5VAcDcdDDr4MXCnIXbRcSNwiuTf__Jrk,6926
10
- codebuddy_agent_sdk/bin/codebuddy,sha256=9blxztGO9Uu2AbDmfAmMfXbg85EYDj46vUBfgJbmUwk,122398201
11
- codebuddy_agent_sdk/transport/__init__.py,sha256=zv_8OJHgnWjCInqOiu3GOFby4XVGvDwICHpOsymOlko,166
12
- codebuddy_agent_sdk/transport/base.py,sha256=ps-4jWU3jL-IiTIL5Pv70zanxdmrknCHZfpxMGBTLoA,626
13
- codebuddy_agent_sdk/transport/subprocess.py,sha256=m3o-hx2q6U3yeaOPrMxqi6XetsPZdAjjAxBIDiq-vgU,5617
14
- codebuddy_agent_sdk-0.1.8.dist-info/METADATA,sha256=HXjZoWayfmxEIBJOX5pP9onqYrVWADHL9h1fEwleyaI,2538
15
- codebuddy_agent_sdk-0.1.8.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
16
- codebuddy_agent_sdk-0.1.8.dist-info/RECORD,,