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,24 @@
|
|
|
1
|
+
"""Shared FastAPI dependencies."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Annotated, cast
|
|
6
|
+
|
|
7
|
+
from fastapi import Depends, Request # noqa: TC002
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from agentpool_server.opencode_server.state import ServerState
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_state(request: Request) -> ServerState:
|
|
15
|
+
"""Get server state from request.
|
|
16
|
+
|
|
17
|
+
The state is stored on app.state.server_state during app creation.
|
|
18
|
+
"""
|
|
19
|
+
from agentpool_server.opencode_server.state import ServerState
|
|
20
|
+
|
|
21
|
+
return cast(ServerState, request.app.state.server_state)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
StateDep = Annotated["ServerState", Depends(get_state)]
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"""OpenCode-based input provider for agent interactions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
8
|
+
|
|
9
|
+
from mcp import types
|
|
10
|
+
|
|
11
|
+
from agentpool.log import get_logger
|
|
12
|
+
from agentpool.ui.base import InputProvider
|
|
13
|
+
from agentpool_server.opencode_server.models.events import PermissionRequestEvent
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from agentpool.agents.context import ConfirmationResult
|
|
18
|
+
from agentpool.messaging import ChatMessage
|
|
19
|
+
from agentpool.messaging.context import NodeContext
|
|
20
|
+
from agentpool.tools.base import Tool
|
|
21
|
+
from agentpool_server.opencode_server.state import ServerState
|
|
22
|
+
|
|
23
|
+
logger = get_logger(__name__)
|
|
24
|
+
|
|
25
|
+
# OpenCode permission responses
|
|
26
|
+
PermissionResponse = str # "once" | "always" | "reject"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class PendingPermission:
|
|
31
|
+
"""A pending permission request awaiting user response."""
|
|
32
|
+
|
|
33
|
+
permission_id: str
|
|
34
|
+
tool_name: str
|
|
35
|
+
args: dict[str, Any]
|
|
36
|
+
future: asyncio.Future[PermissionResponse]
|
|
37
|
+
created_at: float = field(default_factory=lambda: __import__("time").time())
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class OpenCodeInputProvider(InputProvider):
|
|
41
|
+
"""Input provider that uses OpenCode SSE/REST for user interactions.
|
|
42
|
+
|
|
43
|
+
This provider enables tool confirmation and elicitation requests
|
|
44
|
+
through the OpenCode protocol. When a tool needs confirmation:
|
|
45
|
+
1. A permission request is created and stored
|
|
46
|
+
2. An SSE event is broadcast to notify clients
|
|
47
|
+
3. The provider awaits a response via the REST endpoint
|
|
48
|
+
4. The client POSTs to /session/{id}/permissions/{permissionID} to respond
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def __init__(self, state: ServerState, session_id: str) -> None:
|
|
52
|
+
"""Initialize OpenCode input provider.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
state: Server state for broadcasting events
|
|
56
|
+
session_id: The session ID for this provider
|
|
57
|
+
"""
|
|
58
|
+
self.state = state
|
|
59
|
+
self.session_id = session_id
|
|
60
|
+
self._pending_permissions: dict[str, PendingPermission] = {}
|
|
61
|
+
self._tool_approvals: dict[str, str] = {} # tool_name -> "always" | "reject"
|
|
62
|
+
self._id_counter = 0
|
|
63
|
+
|
|
64
|
+
def _generate_permission_id(self) -> str:
|
|
65
|
+
"""Generate a unique permission ID."""
|
|
66
|
+
self._id_counter += 1
|
|
67
|
+
return f"perm_{self._id_counter}_{int(__import__('time').time() * 1000)}"
|
|
68
|
+
|
|
69
|
+
async def get_tool_confirmation(
|
|
70
|
+
self,
|
|
71
|
+
context: NodeContext[Any],
|
|
72
|
+
tool: Tool,
|
|
73
|
+
args: dict[str, Any],
|
|
74
|
+
message_history: list[ChatMessage[Any]] | None = None,
|
|
75
|
+
) -> ConfirmationResult:
|
|
76
|
+
"""Get tool execution confirmation via OpenCode permission request.
|
|
77
|
+
|
|
78
|
+
Creates a pending permission, broadcasts an SSE event, and waits
|
|
79
|
+
for the client to respond via POST /session/{id}/permissions/{permissionID}.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
context: Current node context
|
|
83
|
+
tool: Information about the tool to be executed
|
|
84
|
+
args: Tool arguments that will be passed to the tool
|
|
85
|
+
message_history: Optional conversation history
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
Confirmation result indicating whether to allow, skip, or abort
|
|
89
|
+
"""
|
|
90
|
+
try:
|
|
91
|
+
# Check if we have a standing approval/rejection for this tool
|
|
92
|
+
if tool.name in self._tool_approvals:
|
|
93
|
+
standing_decision = self._tool_approvals[tool.name]
|
|
94
|
+
if standing_decision == "always":
|
|
95
|
+
logger.debug("Auto-allowing tool", tool_name=tool.name, reason="always")
|
|
96
|
+
return "allow"
|
|
97
|
+
if standing_decision == "reject":
|
|
98
|
+
logger.debug("Auto-rejecting tool", tool_name=tool.name, reason="reject")
|
|
99
|
+
return "skip"
|
|
100
|
+
|
|
101
|
+
# Create a pending permission request
|
|
102
|
+
permission_id = self._generate_permission_id()
|
|
103
|
+
future: asyncio.Future[PermissionResponse] = asyncio.get_event_loop().create_future()
|
|
104
|
+
|
|
105
|
+
pending = PendingPermission(
|
|
106
|
+
permission_id=permission_id,
|
|
107
|
+
tool_name=tool.name,
|
|
108
|
+
args=args,
|
|
109
|
+
future=future,
|
|
110
|
+
)
|
|
111
|
+
self._pending_permissions[permission_id] = pending
|
|
112
|
+
|
|
113
|
+
max_preview_args = 3
|
|
114
|
+
args_preview = ", ".join(f"{k}={v!r}" for k, v in list(args.items())[:max_preview_args])
|
|
115
|
+
if len(args) > max_preview_args:
|
|
116
|
+
args_preview += ", ..."
|
|
117
|
+
|
|
118
|
+
# Extract call_id from AgentContext if available (set by ClaudeCodeAgent from streaming)
|
|
119
|
+
# Fall back to a generated ID if not available
|
|
120
|
+
call_id = getattr(context, "tool_call_id", None)
|
|
121
|
+
if call_id is None:
|
|
122
|
+
# Generate a synthetic call_id - won't match TUI tool parts but allows display
|
|
123
|
+
call_id = f"toolu_{permission_id}"
|
|
124
|
+
# TODO: Extract message_id from context when available
|
|
125
|
+
|
|
126
|
+
event = PermissionRequestEvent.create(
|
|
127
|
+
session_id=self.session_id,
|
|
128
|
+
permission_id=permission_id,
|
|
129
|
+
tool_name=tool.name,
|
|
130
|
+
args_preview=args_preview,
|
|
131
|
+
message=f"Tool '{tool.name}' wants to execute with args: {args_preview}",
|
|
132
|
+
call_id=call_id,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
await self.state.broadcast_event(event)
|
|
136
|
+
logger.info("Permission requested", permission_id=permission_id, tool_name=tool.name)
|
|
137
|
+
# Wait for the client to respond
|
|
138
|
+
try:
|
|
139
|
+
response = await future
|
|
140
|
+
except asyncio.CancelledError:
|
|
141
|
+
logger.warning("Permission request cancelled", permission_id=permission_id)
|
|
142
|
+
return "skip"
|
|
143
|
+
finally:
|
|
144
|
+
# Clean up the pending permission
|
|
145
|
+
self._pending_permissions.pop(permission_id, None)
|
|
146
|
+
|
|
147
|
+
# Map OpenCode response to our confirmation result
|
|
148
|
+
return self._handle_permission_response(response, tool.name)
|
|
149
|
+
|
|
150
|
+
except Exception:
|
|
151
|
+
logger.exception("Failed to get tool confirmation")
|
|
152
|
+
return "abort_run"
|
|
153
|
+
|
|
154
|
+
def _handle_permission_response(
|
|
155
|
+
self, response: PermissionResponse, tool_name: str
|
|
156
|
+
) -> ConfirmationResult:
|
|
157
|
+
"""Handle permission response and update tool approval state."""
|
|
158
|
+
match response:
|
|
159
|
+
case "once":
|
|
160
|
+
return "allow"
|
|
161
|
+
case "always":
|
|
162
|
+
self._tool_approvals[tool_name] = "always"
|
|
163
|
+
logger.info("Tool approval set", tool_name=tool_name, approval="always")
|
|
164
|
+
return "allow"
|
|
165
|
+
case "reject":
|
|
166
|
+
return "skip"
|
|
167
|
+
case _:
|
|
168
|
+
logger.warning("Unknown permission response", response=response)
|
|
169
|
+
return "abort_run"
|
|
170
|
+
|
|
171
|
+
def resolve_permission(self, permission_id: str, response: PermissionResponse) -> bool:
|
|
172
|
+
"""Resolve a pending permission request.
|
|
173
|
+
|
|
174
|
+
Called by the REST endpoint when the client responds.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
permission_id: The permission request ID
|
|
178
|
+
response: The client's response ("once", "always", or "reject")
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
True if the permission was found and resolved, False otherwise
|
|
182
|
+
"""
|
|
183
|
+
pending = self._pending_permissions.get(permission_id)
|
|
184
|
+
if pending is None:
|
|
185
|
+
logger.warning("Permission not found", permission_id=permission_id)
|
|
186
|
+
return False
|
|
187
|
+
|
|
188
|
+
if pending.future.done():
|
|
189
|
+
logger.warning("Permission already resolved", permission_id=permission_id)
|
|
190
|
+
return False
|
|
191
|
+
|
|
192
|
+
pending.future.set_result(response)
|
|
193
|
+
logger.info(
|
|
194
|
+
"Permission resolved",
|
|
195
|
+
permission_id=permission_id,
|
|
196
|
+
response=response,
|
|
197
|
+
)
|
|
198
|
+
return True
|
|
199
|
+
|
|
200
|
+
def get_pending_permissions(self) -> list[dict[str, Any]]:
|
|
201
|
+
"""Get all pending permission requests.
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
List of pending permission info dicts
|
|
205
|
+
"""
|
|
206
|
+
return [
|
|
207
|
+
{
|
|
208
|
+
"permission_id": p.permission_id,
|
|
209
|
+
"tool_name": p.tool_name,
|
|
210
|
+
"args": p.args,
|
|
211
|
+
"created_at": p.created_at,
|
|
212
|
+
}
|
|
213
|
+
for p in self._pending_permissions.values()
|
|
214
|
+
]
|
|
215
|
+
|
|
216
|
+
async def get_elicitation(
|
|
217
|
+
self,
|
|
218
|
+
params: types.ElicitRequestParams,
|
|
219
|
+
) -> types.ElicitResult | types.ErrorData:
|
|
220
|
+
"""Get user response to elicitation request.
|
|
221
|
+
|
|
222
|
+
For now, this returns a basic decline since OpenCode doesn't have
|
|
223
|
+
a full elicitation UI like ACP. Future versions could add support
|
|
224
|
+
for more complex elicitation flows.
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
params: MCP elicit request parameters
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
Elicit result with user's response or error data
|
|
231
|
+
"""
|
|
232
|
+
# For URL elicitation, we could open the URL
|
|
233
|
+
if isinstance(params, types.ElicitRequestURLParams):
|
|
234
|
+
logger.info(
|
|
235
|
+
"URL elicitation request",
|
|
236
|
+
message=params.message,
|
|
237
|
+
url=params.url,
|
|
238
|
+
)
|
|
239
|
+
# Could potentially open URL in browser here
|
|
240
|
+
return types.ElicitResult(action="decline")
|
|
241
|
+
|
|
242
|
+
# For form elicitation, we don't have UI support yet
|
|
243
|
+
logger.info(
|
|
244
|
+
"Form elicitation request (not supported)",
|
|
245
|
+
message=params.message,
|
|
246
|
+
schema=getattr(params, "requestedSchema", None),
|
|
247
|
+
)
|
|
248
|
+
return types.ElicitResult(action="decline")
|
|
249
|
+
|
|
250
|
+
def clear_tool_approvals(self) -> None:
|
|
251
|
+
"""Clear all stored tool approval decisions."""
|
|
252
|
+
approval_count = len(self._tool_approvals)
|
|
253
|
+
self._tool_approvals.clear()
|
|
254
|
+
logger.info("Cleared tool approval decisions", count=approval_count)
|
|
255
|
+
|
|
256
|
+
def cancel_all_pending(self) -> int:
|
|
257
|
+
"""Cancel all pending permission requests.
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
Number of permissions cancelled
|
|
261
|
+
"""
|
|
262
|
+
count = 0
|
|
263
|
+
for pending in list(self._pending_permissions.values()):
|
|
264
|
+
if not pending.future.done():
|
|
265
|
+
pending.future.cancel()
|
|
266
|
+
count += 1
|
|
267
|
+
self._pending_permissions.clear()
|
|
268
|
+
logger.info("Cancelled all pending permissions", count=count)
|
|
269
|
+
return count
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"""OpenCode API models.
|
|
2
|
+
|
|
3
|
+
All models inherit from OpenCodeBaseModel which provides:
|
|
4
|
+
- populate_by_name=True for camelCase alias support
|
|
5
|
+
- by_alias=True serialization by default
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from agentpool_server.opencode_server.models.base import OpenCodeBaseModel
|
|
9
|
+
from agentpool_server.opencode_server.models.common import (
|
|
10
|
+
TimeCreated,
|
|
11
|
+
TimeCreatedUpdated,
|
|
12
|
+
)
|
|
13
|
+
from agentpool_server.opencode_server.models.app import (
|
|
14
|
+
App,
|
|
15
|
+
AppTimeInfo,
|
|
16
|
+
HealthResponse,
|
|
17
|
+
PathInfo,
|
|
18
|
+
Project,
|
|
19
|
+
ProjectTime,
|
|
20
|
+
VcsInfo,
|
|
21
|
+
)
|
|
22
|
+
from agentpool_server.opencode_server.models.provider import (
|
|
23
|
+
Model,
|
|
24
|
+
ModelCost,
|
|
25
|
+
ModelLimit,
|
|
26
|
+
Mode,
|
|
27
|
+
ModeModel,
|
|
28
|
+
Provider,
|
|
29
|
+
ProviderListResponse,
|
|
30
|
+
ProvidersResponse,
|
|
31
|
+
)
|
|
32
|
+
from agentpool_server.opencode_server.models.session import (
|
|
33
|
+
Session,
|
|
34
|
+
SessionCreateRequest,
|
|
35
|
+
SessionForkRequest,
|
|
36
|
+
SessionInitRequest,
|
|
37
|
+
SessionRevert,
|
|
38
|
+
SessionShare,
|
|
39
|
+
SessionStatus,
|
|
40
|
+
SessionUpdateRequest,
|
|
41
|
+
SummarizeRequest,
|
|
42
|
+
Todo,
|
|
43
|
+
)
|
|
44
|
+
from agentpool_server.opencode_server.models.message import (
|
|
45
|
+
AssistantMessage,
|
|
46
|
+
CommandRequest,
|
|
47
|
+
FilePartInput,
|
|
48
|
+
MessagePath,
|
|
49
|
+
MessageRequest,
|
|
50
|
+
MessageTime,
|
|
51
|
+
MessageWithParts,
|
|
52
|
+
PartInput,
|
|
53
|
+
ShellRequest,
|
|
54
|
+
TextPartInput,
|
|
55
|
+
Tokens,
|
|
56
|
+
TokensCache,
|
|
57
|
+
UserMessage,
|
|
58
|
+
)
|
|
59
|
+
from agentpool_server.opencode_server.models.parts import (
|
|
60
|
+
FilePart,
|
|
61
|
+
Part,
|
|
62
|
+
StepFinishPart,
|
|
63
|
+
StepStartPart,
|
|
64
|
+
TextPart,
|
|
65
|
+
TimeStart,
|
|
66
|
+
TimeStartEnd,
|
|
67
|
+
TimeStartEndCompacted,
|
|
68
|
+
TimeStartEndOptional,
|
|
69
|
+
ToolPart,
|
|
70
|
+
ToolState,
|
|
71
|
+
ToolStateCompleted,
|
|
72
|
+
ToolStateError,
|
|
73
|
+
ToolStatePending,
|
|
74
|
+
ToolStateRunning,
|
|
75
|
+
)
|
|
76
|
+
from agentpool_server.opencode_server.models.file import (
|
|
77
|
+
FileContent,
|
|
78
|
+
FileNode,
|
|
79
|
+
FileStatus,
|
|
80
|
+
FindMatch,
|
|
81
|
+
Symbol,
|
|
82
|
+
)
|
|
83
|
+
from agentpool_server.opencode_server.models.agent import (
|
|
84
|
+
Agent,
|
|
85
|
+
Command,
|
|
86
|
+
)
|
|
87
|
+
from agentpool_server.opencode_server.models.pty import (
|
|
88
|
+
PtyCreateRequest,
|
|
89
|
+
PtyInfo,
|
|
90
|
+
PtySize,
|
|
91
|
+
PtyUpdateRequest,
|
|
92
|
+
)
|
|
93
|
+
from agentpool_server.opencode_server.models.events import (
|
|
94
|
+
Event,
|
|
95
|
+
MessageUpdatedEvent,
|
|
96
|
+
MessageUpdatedEventProperties,
|
|
97
|
+
PartUpdatedEvent,
|
|
98
|
+
PartUpdatedEventProperties,
|
|
99
|
+
ServerConnectedEvent,
|
|
100
|
+
SessionCompactedEvent,
|
|
101
|
+
SessionCompactedProperties,
|
|
102
|
+
SessionCreatedEvent,
|
|
103
|
+
SessionDeletedEvent,
|
|
104
|
+
SessionDeletedProperties,
|
|
105
|
+
SessionErrorEvent,
|
|
106
|
+
SessionErrorInfo,
|
|
107
|
+
SessionErrorProperties,
|
|
108
|
+
SessionIdleEvent,
|
|
109
|
+
SessionIdleProperties,
|
|
110
|
+
SessionInfoProperties,
|
|
111
|
+
SessionStatusEvent,
|
|
112
|
+
SessionStatusProperties,
|
|
113
|
+
SessionUpdatedEvent,
|
|
114
|
+
)
|
|
115
|
+
from agentpool_server.opencode_server.models.mcp import (
|
|
116
|
+
LogRequest,
|
|
117
|
+
MCPStatus,
|
|
118
|
+
)
|
|
119
|
+
from agentpool_server.opencode_server.models.config import (
|
|
120
|
+
Config,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
__all__ = [
|
|
124
|
+
# Agent
|
|
125
|
+
"Agent",
|
|
126
|
+
# App
|
|
127
|
+
"App",
|
|
128
|
+
"AppTimeInfo",
|
|
129
|
+
# Message
|
|
130
|
+
"AssistantMessage",
|
|
131
|
+
"Command",
|
|
132
|
+
"CommandRequest",
|
|
133
|
+
# Config
|
|
134
|
+
"Config",
|
|
135
|
+
# Events
|
|
136
|
+
"Event",
|
|
137
|
+
# File
|
|
138
|
+
"FileContent",
|
|
139
|
+
"FileNode",
|
|
140
|
+
# Parts
|
|
141
|
+
"FilePart",
|
|
142
|
+
"FilePartInput",
|
|
143
|
+
"FileStatus",
|
|
144
|
+
"FindMatch",
|
|
145
|
+
"HealthResponse",
|
|
146
|
+
# MCP
|
|
147
|
+
"LogRequest",
|
|
148
|
+
"MCPStatus",
|
|
149
|
+
"MessagePath",
|
|
150
|
+
"MessageRequest",
|
|
151
|
+
"MessageTime",
|
|
152
|
+
"MessageUpdatedEvent",
|
|
153
|
+
"MessageUpdatedEventProperties",
|
|
154
|
+
"MessageWithParts",
|
|
155
|
+
"Mode",
|
|
156
|
+
"ModeModel",
|
|
157
|
+
# Provider
|
|
158
|
+
"Model",
|
|
159
|
+
"ModelCost",
|
|
160
|
+
"ModelLimit",
|
|
161
|
+
# Base
|
|
162
|
+
"OpenCodeBaseModel",
|
|
163
|
+
"Part",
|
|
164
|
+
"PartInput",
|
|
165
|
+
"PartUpdatedEvent",
|
|
166
|
+
"PartUpdatedEventProperties",
|
|
167
|
+
"PathInfo",
|
|
168
|
+
"Project",
|
|
169
|
+
"ProjectTime",
|
|
170
|
+
"Provider",
|
|
171
|
+
"ProviderListResponse",
|
|
172
|
+
"ProvidersResponse",
|
|
173
|
+
# PTY
|
|
174
|
+
"PtyCreateRequest",
|
|
175
|
+
"PtyInfo",
|
|
176
|
+
"PtySize",
|
|
177
|
+
"PtyUpdateRequest",
|
|
178
|
+
"ServerConnectedEvent",
|
|
179
|
+
# Session
|
|
180
|
+
"Session",
|
|
181
|
+
"SessionCompactedEvent",
|
|
182
|
+
"SessionCompactedProperties",
|
|
183
|
+
"SessionCreateRequest",
|
|
184
|
+
"SessionCreatedEvent",
|
|
185
|
+
"SessionDeletedEvent",
|
|
186
|
+
"SessionDeletedProperties",
|
|
187
|
+
"SessionErrorEvent",
|
|
188
|
+
"SessionErrorInfo",
|
|
189
|
+
"SessionErrorProperties",
|
|
190
|
+
"SessionForkRequest",
|
|
191
|
+
"SessionIdleEvent",
|
|
192
|
+
"SessionIdleProperties",
|
|
193
|
+
"SessionInfoProperties",
|
|
194
|
+
"SessionInitRequest",
|
|
195
|
+
"SessionRevert",
|
|
196
|
+
"SessionShare",
|
|
197
|
+
"SessionStatus",
|
|
198
|
+
"SessionStatusEvent",
|
|
199
|
+
"SessionStatusProperties",
|
|
200
|
+
"SessionUpdateRequest",
|
|
201
|
+
"SessionUpdatedEvent",
|
|
202
|
+
"ShellRequest",
|
|
203
|
+
"StepFinishPart",
|
|
204
|
+
"StepStartPart",
|
|
205
|
+
"SummarizeRequest",
|
|
206
|
+
"Symbol",
|
|
207
|
+
"TextPart",
|
|
208
|
+
"TextPartInput",
|
|
209
|
+
# Common
|
|
210
|
+
"TimeCreated",
|
|
211
|
+
"TimeCreatedUpdated",
|
|
212
|
+
# Time types (from parts.py)
|
|
213
|
+
"TimeStart",
|
|
214
|
+
"TimeStartEnd",
|
|
215
|
+
"TimeStartEndCompacted",
|
|
216
|
+
"TimeStartEndOptional",
|
|
217
|
+
"Todo",
|
|
218
|
+
"Tokens",
|
|
219
|
+
"TokensCache",
|
|
220
|
+
"ToolPart",
|
|
221
|
+
"ToolState",
|
|
222
|
+
"ToolStateCompleted",
|
|
223
|
+
"ToolStateError",
|
|
224
|
+
"ToolStatePending",
|
|
225
|
+
"ToolStateRunning",
|
|
226
|
+
"UserMessage",
|
|
227
|
+
"VcsInfo",
|
|
228
|
+
]
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Agent and command models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
from pydantic import Field
|
|
8
|
+
|
|
9
|
+
from agentpool_server.opencode_server.models.base import OpenCodeBaseModel
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AgentPermission(OpenCodeBaseModel):
|
|
13
|
+
"""Agent permission settings."""
|
|
14
|
+
|
|
15
|
+
edit: Literal["ask", "allow", "deny"] = "ask"
|
|
16
|
+
bash: dict[str, Literal["ask", "allow", "deny"]] = Field(default_factory=dict)
|
|
17
|
+
skill: dict[str, Literal["ask", "allow", "deny"]] = Field(default_factory=dict)
|
|
18
|
+
webfetch: Literal["ask", "allow", "deny"] | None = None
|
|
19
|
+
doom_loop: Literal["ask", "allow", "deny"] | None = None
|
|
20
|
+
external_directory: Literal["ask", "allow", "deny"] | None = None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AgentModel(OpenCodeBaseModel):
|
|
24
|
+
"""Agent model configuration."""
|
|
25
|
+
|
|
26
|
+
model_id: str
|
|
27
|
+
provider_id: str
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Agent(OpenCodeBaseModel):
|
|
31
|
+
"""Agent information matching SDK type."""
|
|
32
|
+
|
|
33
|
+
name: str
|
|
34
|
+
description: str | None = None
|
|
35
|
+
mode: Literal["subagent", "primary", "all"] = "primary"
|
|
36
|
+
native: bool | None = None
|
|
37
|
+
hidden: bool | None = None
|
|
38
|
+
default: bool | None = None
|
|
39
|
+
top_p: float | None = None
|
|
40
|
+
temperature: float | None = None
|
|
41
|
+
color: str | None = None
|
|
42
|
+
permission: AgentPermission = Field(default_factory=AgentPermission)
|
|
43
|
+
model: AgentModel | None = None
|
|
44
|
+
prompt: str | None = None
|
|
45
|
+
tools: dict[str, bool] = Field(default_factory=dict)
|
|
46
|
+
options: dict[str, str] = Field(default_factory=dict)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class Command(OpenCodeBaseModel):
|
|
50
|
+
"""Slash command."""
|
|
51
|
+
|
|
52
|
+
name: str
|
|
53
|
+
description: str = ""
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""App, project, and path related models."""
|
|
2
|
+
|
|
3
|
+
from agentpool_server.opencode_server.models.base import OpenCodeBaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class HealthResponse(OpenCodeBaseModel):
|
|
7
|
+
"""Response for /global/health endpoint."""
|
|
8
|
+
|
|
9
|
+
healthy: bool = True
|
|
10
|
+
version: str
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PathInfo(OpenCodeBaseModel):
|
|
14
|
+
"""Path information for app/project."""
|
|
15
|
+
|
|
16
|
+
config: str = ""
|
|
17
|
+
cwd: str
|
|
18
|
+
data: str = ""
|
|
19
|
+
root: str
|
|
20
|
+
state: str = ""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AppTimeInfo(OpenCodeBaseModel):
|
|
24
|
+
"""App time information."""
|
|
25
|
+
|
|
26
|
+
initialized: float | None = None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class App(OpenCodeBaseModel):
|
|
30
|
+
"""App information response."""
|
|
31
|
+
|
|
32
|
+
git: bool = False
|
|
33
|
+
hostname: str = "localhost"
|
|
34
|
+
path: PathInfo
|
|
35
|
+
time: AppTimeInfo
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ProjectTime(OpenCodeBaseModel):
|
|
39
|
+
"""Project time information."""
|
|
40
|
+
|
|
41
|
+
created: int
|
|
42
|
+
initialized: int | None = None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Project(OpenCodeBaseModel):
|
|
46
|
+
"""Project information."""
|
|
47
|
+
|
|
48
|
+
id: str
|
|
49
|
+
worktree: str
|
|
50
|
+
vcs_dir: str | None = None
|
|
51
|
+
vcs: str | None = None # "git" or None
|
|
52
|
+
time: ProjectTime
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class VcsInfo(OpenCodeBaseModel):
|
|
56
|
+
"""VCS (git) information."""
|
|
57
|
+
|
|
58
|
+
branch: str | None = None
|
|
59
|
+
dirty: bool = False
|
|
60
|
+
commit: str | None = None
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Base model for all OpenCode API models."""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, ConfigDict
|
|
4
|
+
from pydantic.alias_generators import to_camel
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def convert(text: str) -> str:
|
|
8
|
+
val = to_camel(text)
|
|
9
|
+
if val.endswith("Id"):
|
|
10
|
+
return val.rstrip("Id") + "ID"
|
|
11
|
+
return val
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class OpenCodeBaseModel(BaseModel):
|
|
15
|
+
"""Base model with OpenCode-compatible configuration.
|
|
16
|
+
|
|
17
|
+
All OpenCode models should inherit from this to ensure:
|
|
18
|
+
- Fields can be populated by their alias (camelCase) or Python name (snake_case)
|
|
19
|
+
- Serialization uses aliases by default for API compatibility
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
model_config = ConfigDict(
|
|
23
|
+
populate_by_name=True,
|
|
24
|
+
alias_generator=convert,
|
|
25
|
+
use_attribute_docstrings=True,
|
|
26
|
+
)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Common/shared models used across multiple domains."""
|
|
2
|
+
|
|
3
|
+
from agentpool_server.opencode_server.models.base import OpenCodeBaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TimeCreatedUpdated(OpenCodeBaseModel):
|
|
7
|
+
"""Timestamp with created and updated fields (milliseconds)."""
|
|
8
|
+
|
|
9
|
+
created: int
|
|
10
|
+
updated: int
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TimeCreated(OpenCodeBaseModel):
|
|
14
|
+
"""Timestamp with created field only (milliseconds)."""
|
|
15
|
+
|
|
16
|
+
created: int
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class TimeStartEnd(OpenCodeBaseModel):
|
|
20
|
+
"""Timestamp with start and optional end (milliseconds)."""
|
|
21
|
+
|
|
22
|
+
start: int
|
|
23
|
+
end: int | None = None
|