agentpool 2.1.9__py3-none-any.whl → 2.2.3__py3-none-any.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.
- acp/__init__.py +13 -0
- acp/bridge/README.md +15 -2
- acp/bridge/__init__.py +3 -2
- acp/bridge/__main__.py +60 -19
- acp/bridge/ws_server.py +173 -0
- acp/bridge/ws_server_cli.py +89 -0
- acp/notifications.py +2 -1
- acp/stdio.py +39 -9
- acp/transports.py +362 -2
- acp/utils.py +15 -2
- agentpool/__init__.py +4 -1
- agentpool/agents/__init__.py +2 -0
- agentpool/agents/acp_agent/acp_agent.py +203 -88
- agentpool/agents/acp_agent/acp_converters.py +46 -21
- agentpool/agents/acp_agent/client_handler.py +157 -3
- agentpool/agents/acp_agent/session_state.py +4 -1
- agentpool/agents/agent.py +314 -107
- agentpool/agents/agui_agent/__init__.py +0 -2
- agentpool/agents/agui_agent/agui_agent.py +90 -21
- agentpool/agents/agui_agent/agui_converters.py +0 -131
- agentpool/agents/base_agent.py +163 -1
- agentpool/agents/claude_code_agent/claude_code_agent.py +626 -179
- agentpool/agents/claude_code_agent/converters.py +71 -3
- agentpool/agents/claude_code_agent/history.py +474 -0
- agentpool/agents/context.py +40 -0
- agentpool/agents/events/__init__.py +2 -0
- agentpool/agents/events/builtin_handlers.py +2 -1
- agentpool/agents/events/event_emitter.py +29 -2
- agentpool/agents/events/events.py +20 -0
- agentpool/agents/modes.py +54 -0
- agentpool/agents/tool_call_accumulator.py +213 -0
- agentpool/common_types.py +21 -0
- agentpool/config_resources/__init__.py +38 -1
- agentpool/config_resources/claude_code_agent.yml +3 -0
- agentpool/delegation/pool.py +37 -29
- agentpool/delegation/team.py +1 -0
- agentpool/delegation/teamrun.py +1 -0
- agentpool/diagnostics/__init__.py +53 -0
- agentpool/diagnostics/lsp_manager.py +1593 -0
- agentpool/diagnostics/lsp_proxy.py +41 -0
- agentpool/diagnostics/lsp_proxy_script.py +229 -0
- agentpool/diagnostics/models.py +398 -0
- agentpool/mcp_server/__init__.py +0 -2
- agentpool/mcp_server/client.py +12 -3
- agentpool/mcp_server/manager.py +25 -31
- agentpool/mcp_server/registries/official_registry_client.py +25 -0
- agentpool/mcp_server/tool_bridge.py +78 -66
- agentpool/messaging/__init__.py +0 -2
- agentpool/messaging/compaction.py +72 -197
- agentpool/messaging/message_history.py +12 -0
- agentpool/messaging/messages.py +52 -9
- agentpool/messaging/processing.py +3 -1
- agentpool/models/acp_agents/base.py +0 -22
- agentpool/models/acp_agents/mcp_capable.py +8 -148
- agentpool/models/acp_agents/non_mcp.py +129 -72
- agentpool/models/agents.py +35 -13
- agentpool/models/claude_code_agents.py +33 -2
- agentpool/models/manifest.py +43 -0
- agentpool/repomap.py +1 -1
- agentpool/resource_providers/__init__.py +9 -1
- agentpool/resource_providers/aggregating.py +52 -3
- agentpool/resource_providers/base.py +57 -1
- agentpool/resource_providers/mcp_provider.py +23 -0
- agentpool/resource_providers/plan_provider.py +130 -41
- agentpool/resource_providers/pool.py +2 -0
- agentpool/resource_providers/static.py +2 -0
- agentpool/sessions/__init__.py +2 -1
- agentpool/sessions/manager.py +31 -2
- agentpool/sessions/models.py +50 -0
- agentpool/skills/registry.py +13 -8
- agentpool/storage/manager.py +217 -1
- agentpool/testing.py +537 -19
- agentpool/utils/file_watcher.py +269 -0
- agentpool/utils/identifiers.py +121 -0
- agentpool/utils/pydantic_ai_helpers.py +46 -0
- agentpool/utils/streams.py +690 -1
- agentpool/utils/subprocess_utils.py +155 -0
- agentpool/utils/token_breakdown.py +461 -0
- {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/METADATA +27 -7
- {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/RECORD +170 -112
- {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/WHEEL +1 -1
- agentpool_cli/__main__.py +4 -0
- agentpool_cli/serve_acp.py +41 -20
- agentpool_cli/serve_agui.py +87 -0
- agentpool_cli/serve_opencode.py +119 -0
- agentpool_commands/__init__.py +30 -0
- agentpool_commands/agents.py +74 -1
- agentpool_commands/history.py +62 -0
- agentpool_commands/mcp.py +176 -0
- agentpool_commands/models.py +56 -3
- agentpool_commands/tools.py +57 -0
- agentpool_commands/utils.py +51 -0
- agentpool_config/builtin_tools.py +77 -22
- agentpool_config/commands.py +24 -1
- agentpool_config/compaction.py +258 -0
- agentpool_config/mcp_server.py +131 -1
- agentpool_config/storage.py +46 -1
- agentpool_config/tools.py +7 -1
- agentpool_config/toolsets.py +92 -148
- agentpool_server/acp_server/acp_agent.py +134 -150
- agentpool_server/acp_server/commands/acp_commands.py +216 -51
- agentpool_server/acp_server/commands/docs_commands/fetch_repo.py +10 -10
- agentpool_server/acp_server/server.py +23 -79
- agentpool_server/acp_server/session.py +181 -19
- agentpool_server/opencode_server/.rules +95 -0
- agentpool_server/opencode_server/ENDPOINTS.md +362 -0
- agentpool_server/opencode_server/__init__.py +27 -0
- agentpool_server/opencode_server/command_validation.py +172 -0
- agentpool_server/opencode_server/converters.py +869 -0
- agentpool_server/opencode_server/dependencies.py +24 -0
- agentpool_server/opencode_server/input_provider.py +269 -0
- agentpool_server/opencode_server/models/__init__.py +228 -0
- agentpool_server/opencode_server/models/agent.py +53 -0
- agentpool_server/opencode_server/models/app.py +60 -0
- agentpool_server/opencode_server/models/base.py +26 -0
- agentpool_server/opencode_server/models/common.py +23 -0
- agentpool_server/opencode_server/models/config.py +37 -0
- agentpool_server/opencode_server/models/events.py +647 -0
- agentpool_server/opencode_server/models/file.py +88 -0
- agentpool_server/opencode_server/models/mcp.py +25 -0
- agentpool_server/opencode_server/models/message.py +162 -0
- agentpool_server/opencode_server/models/parts.py +190 -0
- agentpool_server/opencode_server/models/provider.py +81 -0
- agentpool_server/opencode_server/models/pty.py +43 -0
- agentpool_server/opencode_server/models/session.py +99 -0
- agentpool_server/opencode_server/routes/__init__.py +25 -0
- agentpool_server/opencode_server/routes/agent_routes.py +442 -0
- agentpool_server/opencode_server/routes/app_routes.py +139 -0
- agentpool_server/opencode_server/routes/config_routes.py +241 -0
- agentpool_server/opencode_server/routes/file_routes.py +392 -0
- agentpool_server/opencode_server/routes/global_routes.py +94 -0
- agentpool_server/opencode_server/routes/lsp_routes.py +319 -0
- agentpool_server/opencode_server/routes/message_routes.py +705 -0
- agentpool_server/opencode_server/routes/pty_routes.py +299 -0
- agentpool_server/opencode_server/routes/session_routes.py +1205 -0
- agentpool_server/opencode_server/routes/tui_routes.py +139 -0
- agentpool_server/opencode_server/server.py +430 -0
- agentpool_server/opencode_server/state.py +121 -0
- agentpool_server/opencode_server/time_utils.py +8 -0
- agentpool_storage/__init__.py +16 -0
- agentpool_storage/base.py +103 -0
- agentpool_storage/claude_provider.py +907 -0
- agentpool_storage/file_provider.py +129 -0
- agentpool_storage/memory_provider.py +61 -0
- agentpool_storage/models.py +3 -0
- agentpool_storage/opencode_provider.py +730 -0
- agentpool_storage/project_store.py +325 -0
- agentpool_storage/session_store.py +6 -0
- agentpool_storage/sql_provider/__init__.py +4 -2
- agentpool_storage/sql_provider/models.py +48 -0
- agentpool_storage/sql_provider/sql_provider.py +134 -1
- agentpool_storage/sql_provider/utils.py +10 -1
- agentpool_storage/text_log_provider.py +1 -0
- agentpool_toolsets/builtin/__init__.py +0 -8
- agentpool_toolsets/builtin/code.py +95 -56
- agentpool_toolsets/builtin/debug.py +16 -21
- agentpool_toolsets/builtin/execution_environment.py +99 -103
- agentpool_toolsets/builtin/file_edit/file_edit.py +115 -7
- agentpool_toolsets/builtin/skills.py +86 -4
- agentpool_toolsets/fsspec_toolset/__init__.py +13 -1
- agentpool_toolsets/fsspec_toolset/diagnostics.py +860 -73
- agentpool_toolsets/fsspec_toolset/grep.py +74 -2
- agentpool_toolsets/fsspec_toolset/image_utils.py +161 -0
- agentpool_toolsets/fsspec_toolset/toolset.py +159 -38
- agentpool_toolsets/mcp_discovery/__init__.py +5 -0
- agentpool_toolsets/mcp_discovery/data/mcp_servers.parquet +0 -0
- agentpool_toolsets/mcp_discovery/toolset.py +454 -0
- agentpool_toolsets/mcp_run_toolset.py +84 -6
- agentpool_toolsets/builtin/agent_management.py +0 -239
- agentpool_toolsets/builtin/history.py +0 -36
- agentpool_toolsets/builtin/integration.py +0 -85
- agentpool_toolsets/builtin/tool_management.py +0 -90
- {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/entry_points.txt +0 -0
- {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""LSP Proxy - Helper for deploying and managing LSP proxy instances.
|
|
2
|
+
|
|
3
|
+
The actual proxy script is in lsp_proxy_script.py and is executed as a subprocess.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# Path to the proxy script
|
|
12
|
+
_PROXY_SCRIPT_PATH = Path(__file__).parent / "lsp_proxy_script.py"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class LSPProxy:
|
|
16
|
+
"""Helper class for deploying and managing LSP proxy instances."""
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def get_script_path() -> Path:
|
|
20
|
+
"""Get the path to the proxy script."""
|
|
21
|
+
return _PROXY_SCRIPT_PATH
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def get_start_command(lsp_command: str, port_file: str) -> list[str]:
|
|
25
|
+
"""Get command to start proxy as a background process.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
lsp_command: The LSP server command (e.g., "pyright-langserver --stdio")
|
|
29
|
+
port_file: Path for the port file (proxy writes its port here)
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Command and args for process_manager.start_process()
|
|
33
|
+
"""
|
|
34
|
+
return [
|
|
35
|
+
"python",
|
|
36
|
+
str(_PROXY_SCRIPT_PATH),
|
|
37
|
+
"--command",
|
|
38
|
+
lsp_command,
|
|
39
|
+
"--port-file",
|
|
40
|
+
port_file,
|
|
41
|
+
]
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"""LSP Proxy - Wraps stdio LSP server, exposes via TCP.
|
|
2
|
+
|
|
3
|
+
This script is executed as a subprocess to proxy between TCP clients
|
|
4
|
+
and a stdio-based LSP server.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import asyncio
|
|
11
|
+
import contextlib
|
|
12
|
+
import json
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
import sys
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class LSPProxy:
|
|
19
|
+
"""Proxies between TCP clients and a stdio-based LSP server."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, command: list[str], port_file: str):
|
|
22
|
+
self.command = command
|
|
23
|
+
self.port_file = port_file
|
|
24
|
+
self._port_file_path = Path(port_file)
|
|
25
|
+
self.process: asyncio.subprocess.Process | None = None
|
|
26
|
+
self.lock = asyncio.Lock()
|
|
27
|
+
self._request_id = 0
|
|
28
|
+
self.port: int | None = None
|
|
29
|
+
|
|
30
|
+
async def start(self) -> None:
|
|
31
|
+
"""Start the LSP server subprocess."""
|
|
32
|
+
# Remove existing port file if present
|
|
33
|
+
if self._port_file_path.exists():
|
|
34
|
+
self._port_file_path.unlink()
|
|
35
|
+
|
|
36
|
+
# Use asyncio subprocess for native async I/O
|
|
37
|
+
self.process = await asyncio.create_subprocess_exec(
|
|
38
|
+
*self.command,
|
|
39
|
+
stdin=asyncio.subprocess.PIPE,
|
|
40
|
+
stdout=asyncio.subprocess.PIPE,
|
|
41
|
+
stderr=asyncio.subprocess.PIPE,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
async def _read_lsp_message(self) -> dict[str, Any] | None:
|
|
45
|
+
"""Read a single JSON-RPC message from LSP server stdout."""
|
|
46
|
+
if not self.process or not self.process.stdout:
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
headers = {}
|
|
50
|
+
while True:
|
|
51
|
+
line = await self.process.stdout.readline()
|
|
52
|
+
if not line:
|
|
53
|
+
return None # EOF
|
|
54
|
+
line_str = line.decode().strip()
|
|
55
|
+
if not line_str:
|
|
56
|
+
break # Empty line = end of headers
|
|
57
|
+
if ": " in line_str:
|
|
58
|
+
key, value = line_str.split(": ", 1)
|
|
59
|
+
headers[key] = value
|
|
60
|
+
|
|
61
|
+
if "Content-Length" not in headers:
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
length = int(headers["Content-Length"])
|
|
65
|
+
body = await self.process.stdout.readexactly(length)
|
|
66
|
+
result: dict[str, Any] = json.loads(body)
|
|
67
|
+
return result
|
|
68
|
+
|
|
69
|
+
async def _send_to_lsp(self, message: dict[str, Any]) -> None:
|
|
70
|
+
"""Send JSON-RPC message to LSP server."""
|
|
71
|
+
if not self.process or not self.process.stdin:
|
|
72
|
+
raise RuntimeError("LSP server not running")
|
|
73
|
+
|
|
74
|
+
payload = json.dumps(message)
|
|
75
|
+
header = f"Content-Length: {len(payload)}\r\n\r\n"
|
|
76
|
+
self.process.stdin.write((header + payload).encode())
|
|
77
|
+
await self.process.stdin.drain()
|
|
78
|
+
|
|
79
|
+
async def send_request(self, method: str, params: dict[str, Any]) -> dict[str, Any]:
|
|
80
|
+
"""Send request to LSP and wait for response."""
|
|
81
|
+
if not self.process:
|
|
82
|
+
return {"error": "LSP server not running"}
|
|
83
|
+
|
|
84
|
+
async with self.lock:
|
|
85
|
+
self._request_id += 1
|
|
86
|
+
msg_id = self._request_id
|
|
87
|
+
request = {"jsonrpc": "2.0", "id": msg_id, "method": method, "params": params}
|
|
88
|
+
await self._send_to_lsp(request)
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
response = await asyncio.wait_for(self._read_lsp_message(), timeout=30.0)
|
|
92
|
+
except TimeoutError:
|
|
93
|
+
# Check stderr for error info
|
|
94
|
+
if self.process.stderr:
|
|
95
|
+
try:
|
|
96
|
+
err = await asyncio.wait_for(self.process.stderr.read(4096), timeout=0.1)
|
|
97
|
+
if err:
|
|
98
|
+
return {"error": f"Timeout, stderr: {err.decode()}"}
|
|
99
|
+
except TimeoutError:
|
|
100
|
+
pass
|
|
101
|
+
return {"error": "Timeout waiting for LSP response"}
|
|
102
|
+
|
|
103
|
+
if response is None:
|
|
104
|
+
# Check stderr for error info
|
|
105
|
+
if self.process.stderr:
|
|
106
|
+
try:
|
|
107
|
+
err = await asyncio.wait_for(self.process.stderr.read(4096), timeout=0.1)
|
|
108
|
+
if err:
|
|
109
|
+
return {"error": f"LSP error: {err.decode()}"}
|
|
110
|
+
except TimeoutError:
|
|
111
|
+
pass
|
|
112
|
+
return {"error": "No response from LSP server"}
|
|
113
|
+
|
|
114
|
+
return response
|
|
115
|
+
|
|
116
|
+
async def send_notification(self, method: str, params: dict[str, Any]) -> None:
|
|
117
|
+
"""Send notification to LSP (no response expected)."""
|
|
118
|
+
notification = {"jsonrpc": "2.0", "method": method, "params": params}
|
|
119
|
+
async with self.lock:
|
|
120
|
+
await self._send_to_lsp(notification)
|
|
121
|
+
|
|
122
|
+
async def handle_client(
|
|
123
|
+
self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter
|
|
124
|
+
) -> None:
|
|
125
|
+
"""Handle incoming client connection."""
|
|
126
|
+
try:
|
|
127
|
+
while True:
|
|
128
|
+
# Read Content-Length header
|
|
129
|
+
headers = b""
|
|
130
|
+
while b"\r\n\r\n" not in headers:
|
|
131
|
+
chunk = await reader.read(1)
|
|
132
|
+
if not chunk:
|
|
133
|
+
return # Client disconnected
|
|
134
|
+
headers += chunk
|
|
135
|
+
|
|
136
|
+
# Parse Content-Length
|
|
137
|
+
header_str = headers.decode()
|
|
138
|
+
length = None
|
|
139
|
+
for line in header_str.split("\r\n"):
|
|
140
|
+
if line.startswith("Content-Length:"):
|
|
141
|
+
length = int(line.split(":")[1].strip())
|
|
142
|
+
break
|
|
143
|
+
|
|
144
|
+
if length is None:
|
|
145
|
+
return
|
|
146
|
+
|
|
147
|
+
# Read body
|
|
148
|
+
body = await reader.read(length)
|
|
149
|
+
request = json.loads(body)
|
|
150
|
+
|
|
151
|
+
# Forward to LSP
|
|
152
|
+
if "id" in request:
|
|
153
|
+
# It's a request, wait for response
|
|
154
|
+
response = await self.send_request(request["method"], request.get("params", {}))
|
|
155
|
+
else:
|
|
156
|
+
# It's a notification
|
|
157
|
+
await self.send_notification(request["method"], request.get("params", {}))
|
|
158
|
+
continue # No response to send
|
|
159
|
+
|
|
160
|
+
# Send response back to client
|
|
161
|
+
payload = json.dumps(response)
|
|
162
|
+
header = f"Content-Length: {len(payload)}\r\n\r\n"
|
|
163
|
+
writer.write(header.encode() + payload.encode())
|
|
164
|
+
await writer.drain()
|
|
165
|
+
|
|
166
|
+
except (ConnectionError, json.JSONDecodeError, UnicodeDecodeError) as e:
|
|
167
|
+
print(f"Client error: {e}", file=sys.stderr, flush=True)
|
|
168
|
+
finally:
|
|
169
|
+
writer.close()
|
|
170
|
+
await writer.wait_closed()
|
|
171
|
+
|
|
172
|
+
async def run(self) -> None:
|
|
173
|
+
"""Start proxy server."""
|
|
174
|
+
await self.start()
|
|
175
|
+
|
|
176
|
+
# Ensure parent directory exists
|
|
177
|
+
self._port_file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
178
|
+
|
|
179
|
+
# Start TCP server on localhost with dynamic port
|
|
180
|
+
server = await asyncio.start_server(self.handle_client, host="127.0.0.1", port=0)
|
|
181
|
+
|
|
182
|
+
# Get the assigned port
|
|
183
|
+
addr = server.sockets[0].getsockname()
|
|
184
|
+
self.port = addr[1]
|
|
185
|
+
|
|
186
|
+
# Write port to file so clients know where to connect
|
|
187
|
+
self._port_file_path.write_text(str(self.port))
|
|
188
|
+
|
|
189
|
+
# Signal ready by creating a marker file
|
|
190
|
+
ready_path = Path(str(self.port_file) + ".ready")
|
|
191
|
+
ready_path.touch()
|
|
192
|
+
|
|
193
|
+
print(f"LSP Proxy listening on 127.0.0.1:{self.port}", file=sys.stderr, flush=True)
|
|
194
|
+
|
|
195
|
+
async with server:
|
|
196
|
+
await server.serve_forever()
|
|
197
|
+
|
|
198
|
+
async def shutdown(self) -> None:
|
|
199
|
+
"""Shutdown the proxy and LSP server."""
|
|
200
|
+
if self.process:
|
|
201
|
+
self.process.terminate()
|
|
202
|
+
try:
|
|
203
|
+
await asyncio.wait_for(self.process.wait(), timeout=5)
|
|
204
|
+
except TimeoutError:
|
|
205
|
+
self.process.kill()
|
|
206
|
+
|
|
207
|
+
# Cleanup files
|
|
208
|
+
ready_path = Path(str(self.port_file) + ".ready")
|
|
209
|
+
if self._port_file_path.exists():
|
|
210
|
+
self._port_file_path.unlink()
|
|
211
|
+
if ready_path.exists():
|
|
212
|
+
ready_path.unlink()
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def main() -> None:
|
|
216
|
+
"""Entry point for the LSP proxy script."""
|
|
217
|
+
parser = argparse.ArgumentParser(description="LSP Proxy Server")
|
|
218
|
+
parser.add_argument("--command", required=True, help="LSP server command")
|
|
219
|
+
parser.add_argument("--port-file", required=True, help="File to write port number to")
|
|
220
|
+
args = parser.parse_args()
|
|
221
|
+
|
|
222
|
+
proxy = LSPProxy(args.command.split(), args.port_file)
|
|
223
|
+
|
|
224
|
+
with contextlib.suppress(KeyboardInterrupt):
|
|
225
|
+
asyncio.run(proxy.run())
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
if __name__ == "__main__":
|
|
229
|
+
main()
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
"""Data models for diagnostics and LSP operations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import Any, Literal, TypedDict
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class Diagnostic:
|
|
11
|
+
"""A single diagnostic message from an LSP server or CLI tool."""
|
|
12
|
+
|
|
13
|
+
file: str
|
|
14
|
+
"""File path the diagnostic applies to."""
|
|
15
|
+
|
|
16
|
+
line: int
|
|
17
|
+
"""1-based line number."""
|
|
18
|
+
|
|
19
|
+
column: int
|
|
20
|
+
"""1-based column number."""
|
|
21
|
+
|
|
22
|
+
severity: Literal["error", "warning", "info", "hint"]
|
|
23
|
+
"""Severity level."""
|
|
24
|
+
|
|
25
|
+
message: str
|
|
26
|
+
"""Human-readable diagnostic message."""
|
|
27
|
+
|
|
28
|
+
source: str
|
|
29
|
+
"""Tool/server that produced this diagnostic (e.g., 'pyright', 'mypy')."""
|
|
30
|
+
|
|
31
|
+
code: str | None = None
|
|
32
|
+
"""Optional error code (e.g., 'reportGeneralTypeIssues')."""
|
|
33
|
+
|
|
34
|
+
end_line: int | None = None
|
|
35
|
+
"""End line for range diagnostics."""
|
|
36
|
+
|
|
37
|
+
end_column: int | None = None
|
|
38
|
+
"""End column for range diagnostics."""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class DiagnosticsResult:
|
|
43
|
+
"""Result of running diagnostics on files."""
|
|
44
|
+
|
|
45
|
+
diagnostics: list[Diagnostic] = field(default_factory=list)
|
|
46
|
+
"""List of diagnostics found."""
|
|
47
|
+
|
|
48
|
+
success: bool = True
|
|
49
|
+
"""Whether the diagnostic run completed without errors."""
|
|
50
|
+
|
|
51
|
+
duration: float = 0.0
|
|
52
|
+
"""Time taken in seconds."""
|
|
53
|
+
|
|
54
|
+
error: str | None = None
|
|
55
|
+
"""Error message if success is False."""
|
|
56
|
+
|
|
57
|
+
server_id: str | None = None
|
|
58
|
+
"""ID of the server that produced these results."""
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def error_count(self) -> int:
|
|
62
|
+
"""Count of error-level diagnostics."""
|
|
63
|
+
return sum(1 for d in self.diagnostics if d.severity == "error")
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def warning_count(self) -> int:
|
|
67
|
+
"""Count of warning-level diagnostics."""
|
|
68
|
+
return sum(1 for d in self.diagnostics if d.severity == "warning")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass
|
|
72
|
+
class LSPServerState:
|
|
73
|
+
"""State of a running LSP server."""
|
|
74
|
+
|
|
75
|
+
server_id: str
|
|
76
|
+
"""Identifier for this server (e.g., 'pyright', 'pylsp')."""
|
|
77
|
+
|
|
78
|
+
process_id: str
|
|
79
|
+
"""Process ID from ProcessManager."""
|
|
80
|
+
|
|
81
|
+
port: int
|
|
82
|
+
"""TCP port for communication."""
|
|
83
|
+
|
|
84
|
+
language: str
|
|
85
|
+
"""Primary language this server handles."""
|
|
86
|
+
|
|
87
|
+
root_uri: str | None = None
|
|
88
|
+
"""Workspace root URI."""
|
|
89
|
+
|
|
90
|
+
initialized: bool = False
|
|
91
|
+
"""Whether LSP initialize handshake completed."""
|
|
92
|
+
|
|
93
|
+
capabilities: dict[str, Any] = field(default_factory=dict)
|
|
94
|
+
"""Server capabilities from initialize response."""
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@dataclass
|
|
98
|
+
class Position:
|
|
99
|
+
"""A position in a text document (0-based line and character)."""
|
|
100
|
+
|
|
101
|
+
line: int
|
|
102
|
+
"""0-based line number."""
|
|
103
|
+
|
|
104
|
+
character: int
|
|
105
|
+
"""0-based character offset."""
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@dataclass
|
|
109
|
+
class Range:
|
|
110
|
+
"""A range in a text document."""
|
|
111
|
+
|
|
112
|
+
start: Position
|
|
113
|
+
"""Start position (inclusive)."""
|
|
114
|
+
|
|
115
|
+
end: Position
|
|
116
|
+
"""End position (exclusive)."""
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@dataclass
|
|
120
|
+
class Location:
|
|
121
|
+
"""A location in a resource (file + range)."""
|
|
122
|
+
|
|
123
|
+
uri: str
|
|
124
|
+
"""File URI."""
|
|
125
|
+
|
|
126
|
+
range: Range
|
|
127
|
+
"""Range within the file."""
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@dataclass
|
|
131
|
+
class SymbolInfo:
|
|
132
|
+
"""Information about a symbol (function, class, variable, etc.)."""
|
|
133
|
+
|
|
134
|
+
name: str
|
|
135
|
+
"""Symbol name."""
|
|
136
|
+
|
|
137
|
+
kind: str
|
|
138
|
+
"""Symbol kind (function, class, variable, etc.)."""
|
|
139
|
+
|
|
140
|
+
location: Location
|
|
141
|
+
"""Where the symbol is defined."""
|
|
142
|
+
|
|
143
|
+
container_name: str | None = None
|
|
144
|
+
"""Name of the containing symbol (e.g., class name for a method)."""
|
|
145
|
+
|
|
146
|
+
detail: str | None = None
|
|
147
|
+
"""Additional detail (e.g., type signature)."""
|
|
148
|
+
|
|
149
|
+
children: list[SymbolInfo] = field(default_factory=list)
|
|
150
|
+
"""Child symbols (for document symbols with hierarchy)."""
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@dataclass
|
|
154
|
+
class HoverInfo:
|
|
155
|
+
"""Hover information for a position."""
|
|
156
|
+
|
|
157
|
+
contents: str
|
|
158
|
+
"""Hover content (may be markdown)."""
|
|
159
|
+
|
|
160
|
+
range: Range | None = None
|
|
161
|
+
"""Range the hover applies to."""
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@dataclass
|
|
165
|
+
class CompletionItem:
|
|
166
|
+
"""A completion suggestion."""
|
|
167
|
+
|
|
168
|
+
label: str
|
|
169
|
+
"""The label shown in completion list."""
|
|
170
|
+
|
|
171
|
+
kind: str | None = None
|
|
172
|
+
"""Kind of completion (function, variable, class, etc.)."""
|
|
173
|
+
|
|
174
|
+
detail: str | None = None
|
|
175
|
+
"""Detail shown next to label."""
|
|
176
|
+
|
|
177
|
+
documentation: str | None = None
|
|
178
|
+
"""Documentation string."""
|
|
179
|
+
|
|
180
|
+
insert_text: str | None = None
|
|
181
|
+
"""Text to insert (if different from label)."""
|
|
182
|
+
|
|
183
|
+
sort_text: str | None = None
|
|
184
|
+
"""Sort order text."""
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@dataclass
|
|
188
|
+
class CodeAction:
|
|
189
|
+
"""A code action (quick fix, refactoring, etc.)."""
|
|
190
|
+
|
|
191
|
+
title: str
|
|
192
|
+
"""Title of the action."""
|
|
193
|
+
|
|
194
|
+
kind: str | None = None
|
|
195
|
+
"""Kind of action (quickfix, refactor, etc.)."""
|
|
196
|
+
|
|
197
|
+
diagnostics: list[Diagnostic] = field(default_factory=list)
|
|
198
|
+
"""Diagnostics this action resolves."""
|
|
199
|
+
|
|
200
|
+
is_preferred: bool = False
|
|
201
|
+
"""Whether this is the preferred action."""
|
|
202
|
+
|
|
203
|
+
edit: dict[str, Any] | None = None
|
|
204
|
+
"""Workspace edit to apply."""
|
|
205
|
+
|
|
206
|
+
command: dict[str, Any] | None = None
|
|
207
|
+
"""Command to execute."""
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@dataclass
|
|
211
|
+
class CallHierarchyItem:
|
|
212
|
+
"""An item in a call hierarchy."""
|
|
213
|
+
|
|
214
|
+
name: str
|
|
215
|
+
"""Name of the callable."""
|
|
216
|
+
|
|
217
|
+
kind: str
|
|
218
|
+
"""Symbol kind."""
|
|
219
|
+
|
|
220
|
+
uri: str
|
|
221
|
+
"""File URI."""
|
|
222
|
+
|
|
223
|
+
range: Range
|
|
224
|
+
"""Range of the callable."""
|
|
225
|
+
|
|
226
|
+
selection_range: Range
|
|
227
|
+
"""Range to select (usually the name)."""
|
|
228
|
+
|
|
229
|
+
detail: str | None = None
|
|
230
|
+
"""Additional detail."""
|
|
231
|
+
|
|
232
|
+
data: Any = None
|
|
233
|
+
"""Server-specific data for resolving."""
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
@dataclass
|
|
237
|
+
class CallHierarchyCall:
|
|
238
|
+
"""A call in a call hierarchy."""
|
|
239
|
+
|
|
240
|
+
item: CallHierarchyItem
|
|
241
|
+
"""The calling/called item."""
|
|
242
|
+
|
|
243
|
+
from_ranges: list[Range] = field(default_factory=list)
|
|
244
|
+
"""Ranges where the call occurs."""
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
@dataclass
|
|
248
|
+
class TypeHierarchyItem:
|
|
249
|
+
"""An item in a type hierarchy."""
|
|
250
|
+
|
|
251
|
+
name: str
|
|
252
|
+
"""Name of the type."""
|
|
253
|
+
|
|
254
|
+
kind: str
|
|
255
|
+
"""Symbol kind."""
|
|
256
|
+
|
|
257
|
+
uri: str
|
|
258
|
+
"""File URI."""
|
|
259
|
+
|
|
260
|
+
range: Range
|
|
261
|
+
"""Range of the type."""
|
|
262
|
+
|
|
263
|
+
selection_range: Range
|
|
264
|
+
"""Range to select (usually the name)."""
|
|
265
|
+
|
|
266
|
+
detail: str | None = None
|
|
267
|
+
"""Additional detail."""
|
|
268
|
+
|
|
269
|
+
data: Any = None
|
|
270
|
+
"""Server-specific data for resolving."""
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
@dataclass
|
|
274
|
+
class SignatureInfo:
|
|
275
|
+
"""Signature help information."""
|
|
276
|
+
|
|
277
|
+
label: str
|
|
278
|
+
"""The signature label."""
|
|
279
|
+
|
|
280
|
+
documentation: str | None = None
|
|
281
|
+
"""Documentation string."""
|
|
282
|
+
|
|
283
|
+
parameters: list[dict[str, Any]] = field(default_factory=list)
|
|
284
|
+
"""Parameter information."""
|
|
285
|
+
|
|
286
|
+
active_parameter: int | None = None
|
|
287
|
+
"""Index of the active parameter."""
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
@dataclass
|
|
291
|
+
class RenameResult:
|
|
292
|
+
"""Result of a rename operation."""
|
|
293
|
+
|
|
294
|
+
changes: dict[str, list[dict[str, Any]]]
|
|
295
|
+
"""Map of file URI to list of text edits."""
|
|
296
|
+
|
|
297
|
+
success: bool = True
|
|
298
|
+
"""Whether rename preparation succeeded."""
|
|
299
|
+
|
|
300
|
+
error: str | None = None
|
|
301
|
+
"""Error message if failed."""
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
# LSP Hover content types (from LSP spec)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
class MarkedStringDict(TypedDict):
|
|
308
|
+
"""MarkedString with language identifier (deprecated in LSP 3.0+)."""
|
|
309
|
+
|
|
310
|
+
language: str
|
|
311
|
+
value: str
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
class MarkupContent(TypedDict):
|
|
315
|
+
"""MarkupContent from LSP spec."""
|
|
316
|
+
|
|
317
|
+
kind: str # "plaintext" or "markdown"
|
|
318
|
+
value: str
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
# Union type for hover contents
|
|
322
|
+
type MarkedString = str | MarkedStringDict
|
|
323
|
+
type HoverContents = str | MarkupContent | MarkedStringDict | list[MarkedString]
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
# Symbol kind mapping from LSP numeric values
|
|
327
|
+
SYMBOL_KIND_MAP: dict[int, str] = {
|
|
328
|
+
1: "file",
|
|
329
|
+
2: "module",
|
|
330
|
+
3: "namespace",
|
|
331
|
+
4: "package",
|
|
332
|
+
5: "class",
|
|
333
|
+
6: "method",
|
|
334
|
+
7: "property",
|
|
335
|
+
8: "field",
|
|
336
|
+
9: "constructor",
|
|
337
|
+
10: "enum",
|
|
338
|
+
11: "interface",
|
|
339
|
+
12: "function",
|
|
340
|
+
13: "variable",
|
|
341
|
+
14: "constant",
|
|
342
|
+
15: "string",
|
|
343
|
+
16: "number",
|
|
344
|
+
17: "boolean",
|
|
345
|
+
18: "array",
|
|
346
|
+
19: "object",
|
|
347
|
+
20: "key",
|
|
348
|
+
21: "null",
|
|
349
|
+
22: "enum_member",
|
|
350
|
+
23: "struct",
|
|
351
|
+
24: "event",
|
|
352
|
+
25: "operator",
|
|
353
|
+
26: "type_parameter",
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
# Completion item kind mapping
|
|
358
|
+
COMPLETION_KIND_MAP: dict[int, str] = {
|
|
359
|
+
1: "text",
|
|
360
|
+
2: "method",
|
|
361
|
+
3: "function",
|
|
362
|
+
4: "constructor",
|
|
363
|
+
5: "field",
|
|
364
|
+
6: "variable",
|
|
365
|
+
7: "class",
|
|
366
|
+
8: "interface",
|
|
367
|
+
9: "module",
|
|
368
|
+
10: "property",
|
|
369
|
+
11: "unit",
|
|
370
|
+
12: "value",
|
|
371
|
+
13: "enum",
|
|
372
|
+
14: "keyword",
|
|
373
|
+
15: "snippet",
|
|
374
|
+
16: "color",
|
|
375
|
+
17: "file",
|
|
376
|
+
18: "reference",
|
|
377
|
+
19: "folder",
|
|
378
|
+
20: "enum_member",
|
|
379
|
+
21: "constant",
|
|
380
|
+
22: "struct",
|
|
381
|
+
23: "event",
|
|
382
|
+
24: "operator",
|
|
383
|
+
25: "type_parameter",
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
# Code action kind mapping
|
|
388
|
+
CODE_ACTION_KIND_MAP: dict[str, str] = {
|
|
389
|
+
"": "quickfix",
|
|
390
|
+
"quickfix": "quickfix",
|
|
391
|
+
"refactor": "refactor",
|
|
392
|
+
"refactor.extract": "refactor.extract",
|
|
393
|
+
"refactor.inline": "refactor.inline",
|
|
394
|
+
"refactor.rewrite": "refactor.rewrite",
|
|
395
|
+
"source": "source",
|
|
396
|
+
"source.organizeImports": "source.organize_imports",
|
|
397
|
+
"source.fixAll": "source.fix_all",
|
|
398
|
+
}
|
agentpool/mcp_server/__init__.py
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
from agentpool.mcp_server.client import MCPClient
|
|
4
4
|
from agentpool.mcp_server.tool_bridge import (
|
|
5
5
|
BridgeConfig,
|
|
6
|
-
ToolBridgeRegistry,
|
|
7
6
|
ToolManagerBridge,
|
|
8
7
|
create_tool_bridge,
|
|
9
8
|
)
|
|
@@ -11,7 +10,6 @@ from agentpool.mcp_server.tool_bridge import (
|
|
|
11
10
|
__all__ = [
|
|
12
11
|
"BridgeConfig",
|
|
13
12
|
"MCPClient",
|
|
14
|
-
"ToolBridgeRegistry",
|
|
15
13
|
"ToolManagerBridge",
|
|
16
14
|
"create_tool_bridge",
|
|
17
15
|
]
|