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.
Files changed (174) hide show
  1. acp/__init__.py +13 -0
  2. acp/bridge/README.md +15 -2
  3. acp/bridge/__init__.py +3 -2
  4. acp/bridge/__main__.py +60 -19
  5. acp/bridge/ws_server.py +173 -0
  6. acp/bridge/ws_server_cli.py +89 -0
  7. acp/notifications.py +2 -1
  8. acp/stdio.py +39 -9
  9. acp/transports.py +362 -2
  10. acp/utils.py +15 -2
  11. agentpool/__init__.py +4 -1
  12. agentpool/agents/__init__.py +2 -0
  13. agentpool/agents/acp_agent/acp_agent.py +203 -88
  14. agentpool/agents/acp_agent/acp_converters.py +46 -21
  15. agentpool/agents/acp_agent/client_handler.py +157 -3
  16. agentpool/agents/acp_agent/session_state.py +4 -1
  17. agentpool/agents/agent.py +314 -107
  18. agentpool/agents/agui_agent/__init__.py +0 -2
  19. agentpool/agents/agui_agent/agui_agent.py +90 -21
  20. agentpool/agents/agui_agent/agui_converters.py +0 -131
  21. agentpool/agents/base_agent.py +163 -1
  22. agentpool/agents/claude_code_agent/claude_code_agent.py +626 -179
  23. agentpool/agents/claude_code_agent/converters.py +71 -3
  24. agentpool/agents/claude_code_agent/history.py +474 -0
  25. agentpool/agents/context.py +40 -0
  26. agentpool/agents/events/__init__.py +2 -0
  27. agentpool/agents/events/builtin_handlers.py +2 -1
  28. agentpool/agents/events/event_emitter.py +29 -2
  29. agentpool/agents/events/events.py +20 -0
  30. agentpool/agents/modes.py +54 -0
  31. agentpool/agents/tool_call_accumulator.py +213 -0
  32. agentpool/common_types.py +21 -0
  33. agentpool/config_resources/__init__.py +38 -1
  34. agentpool/config_resources/claude_code_agent.yml +3 -0
  35. agentpool/delegation/pool.py +37 -29
  36. agentpool/delegation/team.py +1 -0
  37. agentpool/delegation/teamrun.py +1 -0
  38. agentpool/diagnostics/__init__.py +53 -0
  39. agentpool/diagnostics/lsp_manager.py +1593 -0
  40. agentpool/diagnostics/lsp_proxy.py +41 -0
  41. agentpool/diagnostics/lsp_proxy_script.py +229 -0
  42. agentpool/diagnostics/models.py +398 -0
  43. agentpool/mcp_server/__init__.py +0 -2
  44. agentpool/mcp_server/client.py +12 -3
  45. agentpool/mcp_server/manager.py +25 -31
  46. agentpool/mcp_server/registries/official_registry_client.py +25 -0
  47. agentpool/mcp_server/tool_bridge.py +78 -66
  48. agentpool/messaging/__init__.py +0 -2
  49. agentpool/messaging/compaction.py +72 -197
  50. agentpool/messaging/message_history.py +12 -0
  51. agentpool/messaging/messages.py +52 -9
  52. agentpool/messaging/processing.py +3 -1
  53. agentpool/models/acp_agents/base.py +0 -22
  54. agentpool/models/acp_agents/mcp_capable.py +8 -148
  55. agentpool/models/acp_agents/non_mcp.py +129 -72
  56. agentpool/models/agents.py +35 -13
  57. agentpool/models/claude_code_agents.py +33 -2
  58. agentpool/models/manifest.py +43 -0
  59. agentpool/repomap.py +1 -1
  60. agentpool/resource_providers/__init__.py +9 -1
  61. agentpool/resource_providers/aggregating.py +52 -3
  62. agentpool/resource_providers/base.py +57 -1
  63. agentpool/resource_providers/mcp_provider.py +23 -0
  64. agentpool/resource_providers/plan_provider.py +130 -41
  65. agentpool/resource_providers/pool.py +2 -0
  66. agentpool/resource_providers/static.py +2 -0
  67. agentpool/sessions/__init__.py +2 -1
  68. agentpool/sessions/manager.py +31 -2
  69. agentpool/sessions/models.py +50 -0
  70. agentpool/skills/registry.py +13 -8
  71. agentpool/storage/manager.py +217 -1
  72. agentpool/testing.py +537 -19
  73. agentpool/utils/file_watcher.py +269 -0
  74. agentpool/utils/identifiers.py +121 -0
  75. agentpool/utils/pydantic_ai_helpers.py +46 -0
  76. agentpool/utils/streams.py +690 -1
  77. agentpool/utils/subprocess_utils.py +155 -0
  78. agentpool/utils/token_breakdown.py +461 -0
  79. {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/METADATA +27 -7
  80. {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/RECORD +170 -112
  81. {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/WHEEL +1 -1
  82. agentpool_cli/__main__.py +4 -0
  83. agentpool_cli/serve_acp.py +41 -20
  84. agentpool_cli/serve_agui.py +87 -0
  85. agentpool_cli/serve_opencode.py +119 -0
  86. agentpool_commands/__init__.py +30 -0
  87. agentpool_commands/agents.py +74 -1
  88. agentpool_commands/history.py +62 -0
  89. agentpool_commands/mcp.py +176 -0
  90. agentpool_commands/models.py +56 -3
  91. agentpool_commands/tools.py +57 -0
  92. agentpool_commands/utils.py +51 -0
  93. agentpool_config/builtin_tools.py +77 -22
  94. agentpool_config/commands.py +24 -1
  95. agentpool_config/compaction.py +258 -0
  96. agentpool_config/mcp_server.py +131 -1
  97. agentpool_config/storage.py +46 -1
  98. agentpool_config/tools.py +7 -1
  99. agentpool_config/toolsets.py +92 -148
  100. agentpool_server/acp_server/acp_agent.py +134 -150
  101. agentpool_server/acp_server/commands/acp_commands.py +216 -51
  102. agentpool_server/acp_server/commands/docs_commands/fetch_repo.py +10 -10
  103. agentpool_server/acp_server/server.py +23 -79
  104. agentpool_server/acp_server/session.py +181 -19
  105. agentpool_server/opencode_server/.rules +95 -0
  106. agentpool_server/opencode_server/ENDPOINTS.md +362 -0
  107. agentpool_server/opencode_server/__init__.py +27 -0
  108. agentpool_server/opencode_server/command_validation.py +172 -0
  109. agentpool_server/opencode_server/converters.py +869 -0
  110. agentpool_server/opencode_server/dependencies.py +24 -0
  111. agentpool_server/opencode_server/input_provider.py +269 -0
  112. agentpool_server/opencode_server/models/__init__.py +228 -0
  113. agentpool_server/opencode_server/models/agent.py +53 -0
  114. agentpool_server/opencode_server/models/app.py +60 -0
  115. agentpool_server/opencode_server/models/base.py +26 -0
  116. agentpool_server/opencode_server/models/common.py +23 -0
  117. agentpool_server/opencode_server/models/config.py +37 -0
  118. agentpool_server/opencode_server/models/events.py +647 -0
  119. agentpool_server/opencode_server/models/file.py +88 -0
  120. agentpool_server/opencode_server/models/mcp.py +25 -0
  121. agentpool_server/opencode_server/models/message.py +162 -0
  122. agentpool_server/opencode_server/models/parts.py +190 -0
  123. agentpool_server/opencode_server/models/provider.py +81 -0
  124. agentpool_server/opencode_server/models/pty.py +43 -0
  125. agentpool_server/opencode_server/models/session.py +99 -0
  126. agentpool_server/opencode_server/routes/__init__.py +25 -0
  127. agentpool_server/opencode_server/routes/agent_routes.py +442 -0
  128. agentpool_server/opencode_server/routes/app_routes.py +139 -0
  129. agentpool_server/opencode_server/routes/config_routes.py +241 -0
  130. agentpool_server/opencode_server/routes/file_routes.py +392 -0
  131. agentpool_server/opencode_server/routes/global_routes.py +94 -0
  132. agentpool_server/opencode_server/routes/lsp_routes.py +319 -0
  133. agentpool_server/opencode_server/routes/message_routes.py +705 -0
  134. agentpool_server/opencode_server/routes/pty_routes.py +299 -0
  135. agentpool_server/opencode_server/routes/session_routes.py +1205 -0
  136. agentpool_server/opencode_server/routes/tui_routes.py +139 -0
  137. agentpool_server/opencode_server/server.py +430 -0
  138. agentpool_server/opencode_server/state.py +121 -0
  139. agentpool_server/opencode_server/time_utils.py +8 -0
  140. agentpool_storage/__init__.py +16 -0
  141. agentpool_storage/base.py +103 -0
  142. agentpool_storage/claude_provider.py +907 -0
  143. agentpool_storage/file_provider.py +129 -0
  144. agentpool_storage/memory_provider.py +61 -0
  145. agentpool_storage/models.py +3 -0
  146. agentpool_storage/opencode_provider.py +730 -0
  147. agentpool_storage/project_store.py +325 -0
  148. agentpool_storage/session_store.py +6 -0
  149. agentpool_storage/sql_provider/__init__.py +4 -2
  150. agentpool_storage/sql_provider/models.py +48 -0
  151. agentpool_storage/sql_provider/sql_provider.py +134 -1
  152. agentpool_storage/sql_provider/utils.py +10 -1
  153. agentpool_storage/text_log_provider.py +1 -0
  154. agentpool_toolsets/builtin/__init__.py +0 -8
  155. agentpool_toolsets/builtin/code.py +95 -56
  156. agentpool_toolsets/builtin/debug.py +16 -21
  157. agentpool_toolsets/builtin/execution_environment.py +99 -103
  158. agentpool_toolsets/builtin/file_edit/file_edit.py +115 -7
  159. agentpool_toolsets/builtin/skills.py +86 -4
  160. agentpool_toolsets/fsspec_toolset/__init__.py +13 -1
  161. agentpool_toolsets/fsspec_toolset/diagnostics.py +860 -73
  162. agentpool_toolsets/fsspec_toolset/grep.py +74 -2
  163. agentpool_toolsets/fsspec_toolset/image_utils.py +161 -0
  164. agentpool_toolsets/fsspec_toolset/toolset.py +159 -38
  165. agentpool_toolsets/mcp_discovery/__init__.py +5 -0
  166. agentpool_toolsets/mcp_discovery/data/mcp_servers.parquet +0 -0
  167. agentpool_toolsets/mcp_discovery/toolset.py +454 -0
  168. agentpool_toolsets/mcp_run_toolset.py +84 -6
  169. agentpool_toolsets/builtin/agent_management.py +0 -239
  170. agentpool_toolsets/builtin/history.py +0 -36
  171. agentpool_toolsets/builtin/integration.py +0 -85
  172. agentpool_toolsets/builtin/tool_management.py +0 -90
  173. {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/entry_points.txt +0 -0
  174. {agentpool-2.1.9.dist-info → agentpool-2.2.3.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,299 @@
1
+ """PTY (Pseudo-Terminal) routes.
2
+
3
+ Uses the agent's execution environment PTY manager for terminal sessions.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import asyncio
9
+ import contextlib
10
+ from dataclasses import dataclass, field
11
+ from typing import TYPE_CHECKING, Any
12
+
13
+ from fastapi import APIRouter, HTTPException, WebSocketDisconnect
14
+
15
+ from agentpool_server.opencode_server.models import PtyInfo
16
+
17
+
18
+ if TYPE_CHECKING:
19
+ from exxec.pty_manager import PtyManagerProtocol
20
+ from fastapi import WebSocket
21
+
22
+ from agentpool_server.opencode_server.dependencies import StateDep
23
+ from agentpool_server.opencode_server.models import PtyCreateRequest, PtyUpdateRequest
24
+ from agentpool_server.opencode_server.state import ServerState
25
+
26
+
27
+ router = APIRouter(prefix="/pty", tags=["pty"])
28
+
29
+
30
+ @dataclass
31
+ class PtySession:
32
+ """Active PTY session with WebSocket subscribers."""
33
+
34
+ pty_id: str
35
+ subscribers: set[WebSocket] = field(default_factory=set)
36
+ read_task: asyncio.Task[Any] | None = None
37
+ buffer: str = ""
38
+
39
+
40
+ # Track WebSocket subscribers per PTY session
41
+ _pty_sessions: dict[str, PtySession] = {}
42
+
43
+
44
+ def _get_pty_manager(state: StateDep) -> PtyManagerProtocol:
45
+ """Get PTY manager from agent's execution environment.
46
+
47
+ Args:
48
+ state: Server state with agent
49
+
50
+ Returns:
51
+ PTY manager from the agent's execution environment
52
+
53
+ Raises:
54
+ HTTPException: If PTY is not supported
55
+ """
56
+ try:
57
+ return state.agent.env.get_pty_manager()
58
+ except NotImplementedError as e:
59
+ raise HTTPException(
60
+ status_code=501, detail="PTY not supported by this execution environment"
61
+ ) from e
62
+
63
+
64
+ def _convert_pty_info(info: Any, title: str | None = None) -> PtyInfo:
65
+ """Convert exxec PtyInfo to OpenCode PtyInfo model.
66
+
67
+ Args:
68
+ info: PtyInfo from exxec
69
+ title: Optional title override
70
+
71
+ Returns:
72
+ OpenCode PtyInfo model
73
+ """
74
+ return PtyInfo(
75
+ id=info.id,
76
+ title=title or f"Terminal {info.id[-4:]}",
77
+ command=info.command,
78
+ args=info.args,
79
+ cwd=info.cwd or "",
80
+ status=info.status,
81
+ pid=info.pid,
82
+ )
83
+
84
+
85
+ @router.get("")
86
+ async def list_ptys(state: StateDep) -> list[PtyInfo]:
87
+ """List all PTY sessions."""
88
+ manager = _get_pty_manager(state)
89
+ sessions = await manager.list_sessions()
90
+ return [_convert_pty_info(s) for s in sessions]
91
+
92
+
93
+ @router.post("")
94
+ async def create_pty(request: PtyCreateRequest, state: StateDep) -> PtyInfo:
95
+ """Create a new PTY session."""
96
+ from agentpool_server.opencode_server.models.events import PtyCreatedEvent
97
+
98
+ manager = _get_pty_manager(state)
99
+
100
+ # Use working dir from state if not specified
101
+ cwd = request.cwd or state.working_dir
102
+
103
+ try:
104
+ info = await manager.create(
105
+ command=request.command,
106
+ args=request.args,
107
+ cwd=cwd,
108
+ env=request.env,
109
+ )
110
+ except Exception as e:
111
+ raise HTTPException(status_code=400, detail=f"Failed to create PTY: {e}") from e
112
+
113
+ pty_id = info.id
114
+ title = request.title or f"Terminal {pty_id[-4:]}"
115
+
116
+ # Create session tracker for WebSocket subscribers
117
+ session = PtySession(pty_id=pty_id)
118
+ _pty_sessions[pty_id] = session
119
+
120
+ # Start background task to read output and distribute to subscribers
121
+ session.read_task = asyncio.create_task(_read_pty_output(manager, pty_id, state))
122
+
123
+ pty_info = _convert_pty_info(info, title=title)
124
+
125
+ # Broadcast PTY created event
126
+ event = PtyCreatedEvent.create(info=pty_info.model_dump(by_alias=True))
127
+ await state.broadcast_event(event)
128
+
129
+ return pty_info
130
+
131
+
132
+ async def _read_pty_output(manager: PtyManagerProtocol, pty_id: str, state: ServerState) -> None:
133
+ """Background task to read PTY output and distribute to subscribers."""
134
+ from agentpool_server.opencode_server.models.events import PtyExitedEvent
135
+
136
+ session = _pty_sessions.get(pty_id)
137
+ if not session:
138
+ return
139
+
140
+ exit_code = 0
141
+ try:
142
+ async for data in manager.stream(pty_id):
143
+ decoded = data.decode("utf-8", errors="replace")
144
+
145
+ if session.subscribers:
146
+ # Send to all connected WebSocket clients
147
+ disconnected: set[WebSocket] = set()
148
+ for ws in session.subscribers:
149
+ try:
150
+ await ws.send_text(decoded)
151
+ except Exception: # noqa: BLE001
152
+ disconnected.add(ws)
153
+ session.subscribers -= disconnected
154
+ else:
155
+ # Buffer output if no subscribers
156
+ session.buffer += decoded
157
+ # Limit buffer size
158
+ if len(session.buffer) > 100000: # noqa: PLR2004
159
+ session.buffer = session.buffer[-50000:]
160
+
161
+ except asyncio.CancelledError:
162
+ return # Don't broadcast exit if cancelled
163
+ except Exception: # noqa: BLE001
164
+ exit_code = -1
165
+
166
+ # Stream ended - process exited, broadcast event
167
+ event = PtyExitedEvent.create(pty_id=pty_id, exit_code=exit_code)
168
+ await state.broadcast_event(event)
169
+
170
+
171
+ @router.get("/{pty_id}")
172
+ async def get_pty(pty_id: str, state: StateDep) -> PtyInfo:
173
+ """Get PTY session details."""
174
+ manager = _get_pty_manager(state)
175
+ info = await manager.get_info(pty_id)
176
+ if not info:
177
+ raise HTTPException(status_code=404, detail="PTY session not found")
178
+ return _convert_pty_info(info)
179
+
180
+
181
+ @router.patch("/{pty_id}")
182
+ async def update_pty(pty_id: str, request: PtyUpdateRequest, state: StateDep) -> PtyInfo:
183
+ """Update PTY session (title, resize)."""
184
+ from exxec.pty_manager import PtySize
185
+
186
+ from agentpool_server.opencode_server.models.events import PtyUpdatedEvent
187
+
188
+ manager = _get_pty_manager(state)
189
+ info = await manager.get_info(pty_id)
190
+ if not info:
191
+ raise HTTPException(status_code=404, detail="PTY session not found")
192
+
193
+ # Handle resize if requested
194
+ if request.size:
195
+ await manager.resize(pty_id, PtySize(rows=request.size.rows, cols=request.size.cols))
196
+ # Refresh info after resize
197
+ info = await manager.get_info(pty_id)
198
+ if not info:
199
+ raise HTTPException(status_code=404, detail="PTY session not found after resize")
200
+
201
+ # Title is handled at the API level, not in the PTY manager
202
+ title = request.title if request.title else f"Terminal {pty_id[-4:]}"
203
+
204
+ pty_info = _convert_pty_info(info, title=title)
205
+
206
+ # Broadcast PTY updated event
207
+ event = PtyUpdatedEvent.create(info=pty_info.model_dump(by_alias=True))
208
+ await state.broadcast_event(event)
209
+
210
+ return pty_info
211
+
212
+
213
+ @router.delete("/{pty_id}")
214
+ async def remove_pty(pty_id: str, state: StateDep) -> dict[str, bool]:
215
+ """Remove/kill PTY session."""
216
+ from agentpool_server.opencode_server.models.events import PtyDeletedEvent
217
+
218
+ manager = _get_pty_manager(state)
219
+
220
+ # Kill the PTY session
221
+ success = await manager.kill(pty_id)
222
+ if not success:
223
+ raise HTTPException(status_code=404, detail="PTY session not found")
224
+
225
+ # Cleanup session tracker
226
+ session = _pty_sessions.pop(pty_id, None)
227
+ if session:
228
+ # Cancel read task
229
+ if session.read_task and not session.read_task.done():
230
+ session.read_task.cancel()
231
+ with contextlib.suppress(asyncio.CancelledError):
232
+ await session.read_task
233
+
234
+ # Close all WebSocket connections
235
+ for ws in session.subscribers:
236
+ with contextlib.suppress(Exception):
237
+ await ws.close()
238
+
239
+ # Broadcast PTY deleted event
240
+ event = PtyDeletedEvent.create(pty_id=pty_id)
241
+ await state.broadcast_event(event)
242
+
243
+ return {"success": True}
244
+
245
+
246
+ @router.websocket("/{pty_id}/connect")
247
+ async def connect_pty(websocket: WebSocket, pty_id: str) -> None:
248
+ """Connect to PTY via WebSocket for interactive terminal."""
249
+ # Get state from websocket's app
250
+
251
+ state: ServerState = websocket.app.state.server_state
252
+
253
+ try:
254
+ manager = _get_pty_manager(state)
255
+ except HTTPException:
256
+ await websocket.close(code=4501, reason="PTY not supported")
257
+ return
258
+
259
+ info = await manager.get_info(pty_id)
260
+ if not info:
261
+ await websocket.close(code=4004, reason="PTY session not found")
262
+ return
263
+
264
+ await websocket.accept()
265
+
266
+ # Get or create session tracker
267
+ if pty_id not in _pty_sessions:
268
+ _pty_sessions[pty_id] = PtySession(pty_id=pty_id)
269
+ session = _pty_sessions[pty_id]
270
+ session.subscribers.add(websocket)
271
+
272
+ # Send buffered output
273
+ if session.buffer:
274
+ try:
275
+ await websocket.send_text(session.buffer)
276
+ session.buffer = ""
277
+ except Exception: # noqa: BLE001
278
+ pass
279
+
280
+ try:
281
+ while True:
282
+ # Receive input from client
283
+ data = await websocket.receive_text()
284
+
285
+ # Write to PTY stdin
286
+ info = await manager.get_info(pty_id)
287
+ if info and info.status == "running":
288
+ try:
289
+ await manager.write(pty_id, data.encode())
290
+ except Exception: # noqa: BLE001
291
+ break
292
+ else:
293
+ break
294
+ except WebSocketDisconnect:
295
+ pass
296
+ except Exception: # noqa: BLE001
297
+ pass
298
+ finally:
299
+ session.subscribers.discard(websocket)