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
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from
|
|
6
|
-
|
|
5
|
+
from pydantic_ai import ModelRequest, ModelResponse # noqa: TC002
|
|
7
6
|
from slashed import CommandContext # noqa: TC002
|
|
8
7
|
|
|
9
8
|
from agentpool.messaging.context import NodeContext # noqa: TC001
|
|
@@ -12,10 +11,6 @@ from agentpool_config.session import SessionQuery
|
|
|
12
11
|
from agentpool_server.acp_server.session import ACPSession # noqa: TC001
|
|
13
12
|
|
|
14
13
|
|
|
15
|
-
if TYPE_CHECKING:
|
|
16
|
-
from pydantic_ai import ModelRequest, ModelResponse
|
|
17
|
-
|
|
18
|
-
|
|
19
14
|
class ListSessionsCommand(NodeCommand):
|
|
20
15
|
"""List all available ACP sessions.
|
|
21
16
|
|
|
@@ -27,17 +22,23 @@ class ListSessionsCommand(NodeCommand):
|
|
|
27
22
|
Options:
|
|
28
23
|
--active Show only active sessions
|
|
29
24
|
--stored Show only stored sessions
|
|
25
|
+
--detail Show detailed view (default: compact table)
|
|
26
|
+
--page Page number (1-based, default: 1)
|
|
27
|
+
--per-page Items per page (default: 20)
|
|
30
28
|
"""
|
|
31
29
|
|
|
32
30
|
name = "list-sessions"
|
|
33
31
|
category = "acp"
|
|
34
32
|
|
|
35
|
-
async def execute_command(
|
|
33
|
+
async def execute_command( # noqa: PLR0915
|
|
36
34
|
self,
|
|
37
35
|
ctx: CommandContext[NodeContext[ACPSession]],
|
|
38
36
|
*,
|
|
39
37
|
active: bool = False,
|
|
40
38
|
stored: bool = False,
|
|
39
|
+
detail: bool = False,
|
|
40
|
+
page: int = 1,
|
|
41
|
+
per_page: int = 20,
|
|
41
42
|
) -> None:
|
|
42
43
|
"""List available ACP sessions.
|
|
43
44
|
|
|
@@ -45,6 +46,9 @@ class ListSessionsCommand(NodeCommand):
|
|
|
45
46
|
ctx: Command context with ACP session
|
|
46
47
|
active: Show only active sessions
|
|
47
48
|
stored: Show only stored sessions
|
|
49
|
+
detail: Show detailed view instead of compact table
|
|
50
|
+
page: Page number (1-based)
|
|
51
|
+
per_page: Number of items per page
|
|
48
52
|
"""
|
|
49
53
|
session = ctx.context.data
|
|
50
54
|
if not session:
|
|
@@ -54,6 +58,11 @@ class ListSessionsCommand(NodeCommand):
|
|
|
54
58
|
await ctx.output.print("❌ **Session manager not available**")
|
|
55
59
|
return
|
|
56
60
|
|
|
61
|
+
# Validate pagination params
|
|
62
|
+
page = max(page, 1)
|
|
63
|
+
if per_page < 1:
|
|
64
|
+
per_page = 20
|
|
65
|
+
|
|
57
66
|
# If no filter specified, show both
|
|
58
67
|
if not active and not stored:
|
|
59
68
|
active = stored = True
|
|
@@ -61,63 +70,117 @@ class ListSessionsCommand(NodeCommand):
|
|
|
61
70
|
try:
|
|
62
71
|
output_lines = ["## 📋 ACP Sessions\n"]
|
|
63
72
|
|
|
64
|
-
#
|
|
73
|
+
# Collect all sessions to paginate
|
|
74
|
+
all_sessions: list[tuple[str, str, dict[str, str | None]]] = [] # (id, type, info)
|
|
75
|
+
|
|
76
|
+
# Collect active sessions
|
|
65
77
|
if active:
|
|
66
|
-
output_lines.append("### 🟢 Active Sessions")
|
|
67
78
|
active_sessions = session.manager._active
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
output_lines.append(f" - Directory: `{cwd}`")
|
|
86
|
-
output_lines.append("")
|
|
87
|
-
|
|
88
|
-
# Show stored sessions
|
|
79
|
+
for session_id, sess in active_sessions.items():
|
|
80
|
+
session_data = await session.manager.session_manager.store.load(session_id)
|
|
81
|
+
title = session_data.title if session_data else None
|
|
82
|
+
is_current = session_id == session.session_id
|
|
83
|
+
all_sessions.append((
|
|
84
|
+
session_id,
|
|
85
|
+
"active",
|
|
86
|
+
{
|
|
87
|
+
"agent_name": sess.current_agent_name,
|
|
88
|
+
"cwd": sess.cwd or "unknown",
|
|
89
|
+
"title": title,
|
|
90
|
+
"is_current": "yes" if is_current else None,
|
|
91
|
+
"last_active": None,
|
|
92
|
+
},
|
|
93
|
+
))
|
|
94
|
+
|
|
95
|
+
# Collect stored sessions
|
|
89
96
|
if stored:
|
|
90
|
-
output_lines.append("### 💾 Stored Sessions")
|
|
91
|
-
|
|
92
97
|
try:
|
|
93
98
|
stored_session_ids = await session.manager.session_manager.store.list_sessions()
|
|
94
|
-
# Filter out active ones if we already
|
|
99
|
+
# Filter out active ones if we already collected them
|
|
95
100
|
if active:
|
|
96
101
|
stored_session_ids = [
|
|
97
102
|
sid for sid in stored_session_ids if sid not in session.manager._active
|
|
98
103
|
]
|
|
99
104
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
)
|
|
117
|
-
output_lines.append("")
|
|
105
|
+
for session_id in stored_session_ids:
|
|
106
|
+
session_data = await session.manager.session_manager.store.load(session_id)
|
|
107
|
+
if session_data:
|
|
108
|
+
all_sessions.append((
|
|
109
|
+
session_id,
|
|
110
|
+
"stored",
|
|
111
|
+
{
|
|
112
|
+
"agent_name": session_data.agent_name,
|
|
113
|
+
"cwd": session_data.cwd or "unknown",
|
|
114
|
+
"title": session_data.title,
|
|
115
|
+
"is_current": None,
|
|
116
|
+
"last_active": session_data.last_active.strftime(
|
|
117
|
+
"%Y-%m-%d %H:%M"
|
|
118
|
+
),
|
|
119
|
+
},
|
|
120
|
+
))
|
|
118
121
|
except Exception as e: # noqa: BLE001
|
|
119
122
|
output_lines.append(f"*Error loading stored sessions: {e}*\n")
|
|
120
123
|
|
|
124
|
+
# Calculate pagination
|
|
125
|
+
total_count = len(all_sessions)
|
|
126
|
+
total_pages = (total_count + per_page - 1) // per_page if total_count > 0 else 1
|
|
127
|
+
page = min(page, total_pages)
|
|
128
|
+
|
|
129
|
+
start_idx = (page - 1) * per_page
|
|
130
|
+
end_idx = start_idx + per_page
|
|
131
|
+
page_sessions = all_sessions[start_idx:end_idx]
|
|
132
|
+
|
|
133
|
+
if not page_sessions:
|
|
134
|
+
output_lines.append("*No sessions found*\n")
|
|
135
|
+
elif detail:
|
|
136
|
+
# Detailed view (original format)
|
|
137
|
+
active_in_page = [(s, i) for s, t, i in page_sessions if t == "active"]
|
|
138
|
+
stored_in_page = [(s, i) for s, t, i in page_sessions if t == "stored"]
|
|
139
|
+
|
|
140
|
+
if active_in_page:
|
|
141
|
+
output_lines.append("### 🟢 Active Sessions")
|
|
142
|
+
for session_id, info in active_in_page:
|
|
143
|
+
status = " *(current)*" if info["is_current"] else ""
|
|
144
|
+
title_text = f": {info['title']}" if info["title"] else ""
|
|
145
|
+
output_lines.append(f"- **{session_id}**{status}{title_text}")
|
|
146
|
+
output_lines.append(f" - Agent: `{info['agent_name']}`")
|
|
147
|
+
output_lines.append(f" - Directory: `{info['cwd']}`")
|
|
148
|
+
output_lines.append("")
|
|
149
|
+
|
|
150
|
+
if stored_in_page:
|
|
151
|
+
output_lines.append("### 💾 Stored Sessions")
|
|
152
|
+
for session_id, info in stored_in_page:
|
|
153
|
+
title_text = f": {info['title']}" if info["title"] else ""
|
|
154
|
+
output_lines.append(f"- **{session_id}**{title_text}")
|
|
155
|
+
output_lines.append(f" - Agent: `{info['agent_name']}`")
|
|
156
|
+
output_lines.append(f" - Directory: `{info['cwd']}`")
|
|
157
|
+
if info["last_active"]:
|
|
158
|
+
output_lines.append(f" - Last active: {info['last_active']}")
|
|
159
|
+
output_lines.append("")
|
|
160
|
+
else:
|
|
161
|
+
# Compact table view (default)
|
|
162
|
+
output_lines.append("| Title | Agent | Last Active |")
|
|
163
|
+
output_lines.append("|-------|-------|-------------|")
|
|
164
|
+
for session_id, _session_type, info in page_sessions:
|
|
165
|
+
title = info["title"] or session_id[:16]
|
|
166
|
+
if info["is_current"]:
|
|
167
|
+
title = f"▶️ {title}"
|
|
168
|
+
agent = info["agent_name"]
|
|
169
|
+
last_active = info["last_active"] or "-"
|
|
170
|
+
output_lines.append(f"| {title} | {agent} | {last_active} |")
|
|
171
|
+
output_lines.append("")
|
|
172
|
+
|
|
173
|
+
# Add pagination info
|
|
174
|
+
output_lines.append(f"---\n*Page {page}/{total_pages} ({total_count} total sessions)*")
|
|
175
|
+
if total_pages > 1:
|
|
176
|
+
nav_hints = []
|
|
177
|
+
if page > 1:
|
|
178
|
+
nav_hints.append(f"`/list-sessions --page {page - 1}` for previous")
|
|
179
|
+
if page < total_pages:
|
|
180
|
+
nav_hints.append(f"`/list-sessions --page {page + 1}` for next")
|
|
181
|
+
if nav_hints:
|
|
182
|
+
output_lines.append(f"*{', '.join(nav_hints)}*")
|
|
183
|
+
|
|
121
184
|
await ctx.output.print("\n".join(output_lines))
|
|
122
185
|
|
|
123
186
|
except Exception as e: # noqa: BLE001
|
|
@@ -582,6 +645,107 @@ class SetPoolCommand(NodeCommand):
|
|
|
582
645
|
await ctx.output.print(f"❌ **Error switching pool:** {e}")
|
|
583
646
|
|
|
584
647
|
|
|
648
|
+
class CompactCommand(NodeCommand):
|
|
649
|
+
"""Compact the conversation history to reduce context size.
|
|
650
|
+
|
|
651
|
+
Uses the configured compaction pipeline from the agent pool manifest,
|
|
652
|
+
or falls back to a default summarizing pipeline.
|
|
653
|
+
|
|
654
|
+
This will:
|
|
655
|
+
- Apply configured compaction steps (filter, truncate, summarize)
|
|
656
|
+
- Reduce the message history while preserving important context
|
|
657
|
+
- Report the reduction in message count
|
|
658
|
+
|
|
659
|
+
Options:
|
|
660
|
+
--preset <name> Use a specific preset (minimal, balanced, summarizing)
|
|
661
|
+
|
|
662
|
+
Examples:
|
|
663
|
+
/compact
|
|
664
|
+
/compact --preset=minimal
|
|
665
|
+
"""
|
|
666
|
+
|
|
667
|
+
name = "compact"
|
|
668
|
+
category = "acp"
|
|
669
|
+
|
|
670
|
+
async def execute_command(
|
|
671
|
+
self,
|
|
672
|
+
ctx: CommandContext[NodeContext[ACPSession]],
|
|
673
|
+
*,
|
|
674
|
+
preset: str | None = None,
|
|
675
|
+
) -> None:
|
|
676
|
+
"""Compact the conversation history.
|
|
677
|
+
|
|
678
|
+
Args:
|
|
679
|
+
ctx: Command context with ACP session
|
|
680
|
+
preset: Optional preset name (minimal, balanced, summarizing)
|
|
681
|
+
"""
|
|
682
|
+
session = ctx.context.data
|
|
683
|
+
if not session:
|
|
684
|
+
raise RuntimeError("Session not available in command context")
|
|
685
|
+
|
|
686
|
+
agent = session.agent
|
|
687
|
+
|
|
688
|
+
# Check if there's any history to compact
|
|
689
|
+
if not agent.conversation.get_history():
|
|
690
|
+
await ctx.output.print("📭 **No message history to compact**")
|
|
691
|
+
return
|
|
692
|
+
|
|
693
|
+
try:
|
|
694
|
+
# Get compaction pipeline
|
|
695
|
+
from agentpool.messaging.compaction import (
|
|
696
|
+
balanced_context,
|
|
697
|
+
minimal_context,
|
|
698
|
+
summarizing_context,
|
|
699
|
+
)
|
|
700
|
+
|
|
701
|
+
pipeline = None
|
|
702
|
+
|
|
703
|
+
# Check for preset override
|
|
704
|
+
if preset:
|
|
705
|
+
match preset.lower():
|
|
706
|
+
case "minimal":
|
|
707
|
+
pipeline = minimal_context()
|
|
708
|
+
case "balanced":
|
|
709
|
+
pipeline = balanced_context()
|
|
710
|
+
case "summarizing":
|
|
711
|
+
pipeline = summarizing_context()
|
|
712
|
+
case _:
|
|
713
|
+
await ctx.output.print(
|
|
714
|
+
f"⚠️ **Unknown preset:** `{preset}`\n"
|
|
715
|
+
"Available: minimal, balanced, summarizing"
|
|
716
|
+
)
|
|
717
|
+
return
|
|
718
|
+
|
|
719
|
+
# Fall back to pool's configured pipeline
|
|
720
|
+
if pipeline is None:
|
|
721
|
+
pipeline = session.agent_pool.compaction_pipeline
|
|
722
|
+
|
|
723
|
+
# Fall back to default summarizing pipeline
|
|
724
|
+
if pipeline is None:
|
|
725
|
+
pipeline = summarizing_context()
|
|
726
|
+
|
|
727
|
+
await ctx.output.print("🔄 **Compacting conversation history...**")
|
|
728
|
+
|
|
729
|
+
# Apply the pipeline using shared helper
|
|
730
|
+
from agentpool.messaging.compaction import compact_conversation
|
|
731
|
+
|
|
732
|
+
original_count, compacted_count = await compact_conversation(
|
|
733
|
+
pipeline, agent.conversation
|
|
734
|
+
)
|
|
735
|
+
reduction = original_count - compacted_count
|
|
736
|
+
|
|
737
|
+
await ctx.output.print(
|
|
738
|
+
f"✅ **Compaction complete**\n"
|
|
739
|
+
f"- Messages: {original_count} → {compacted_count} ({reduction} removed)\n"
|
|
740
|
+
f"- Reduction: {reduction / original_count * 100:.1f}%"
|
|
741
|
+
if original_count > 0
|
|
742
|
+
else "✅ **Compaction complete** (no messages)"
|
|
743
|
+
)
|
|
744
|
+
|
|
745
|
+
except Exception as e: # noqa: BLE001
|
|
746
|
+
await ctx.output.print(f"❌ **Error compacting history:** {e}")
|
|
747
|
+
|
|
748
|
+
|
|
585
749
|
def get_acp_commands() -> list[type[NodeCommand]]:
|
|
586
750
|
"""Get all ACP-specific slash commands."""
|
|
587
751
|
return [
|
|
@@ -591,4 +755,5 @@ def get_acp_commands() -> list[type[NodeCommand]]:
|
|
|
591
755
|
DeleteSessionCommand,
|
|
592
756
|
ListPoolsCommand,
|
|
593
757
|
SetPoolCommand,
|
|
758
|
+
CompactCommand,
|
|
594
759
|
]
|
|
@@ -66,10 +66,7 @@ class FetchRepoCommand(NodeCommand):
|
|
|
66
66
|
"""
|
|
67
67
|
session = ctx.context.data
|
|
68
68
|
assert session
|
|
69
|
-
|
|
70
|
-
# Generate tool call ID
|
|
71
|
-
tool_call_id = f"fetch-repo-{uuid.uuid4().hex[:8]}"
|
|
72
|
-
|
|
69
|
+
tc_id = f"fetch-repo-{uuid.uuid4().hex[:8]}"
|
|
73
70
|
try:
|
|
74
71
|
# Build URL
|
|
75
72
|
base_url = f"https://uithub.com/{repo}"
|
|
@@ -113,7 +110,7 @@ class FetchRepoCommand(NodeCommand):
|
|
|
113
110
|
display_path += f":{path}"
|
|
114
111
|
|
|
115
112
|
await session.notifications.tool_call_start(
|
|
116
|
-
tool_call_id=
|
|
113
|
+
tool_call_id=tc_id,
|
|
117
114
|
title=f"Fetching repository: {display_path}",
|
|
118
115
|
kind="fetch",
|
|
119
116
|
)
|
|
@@ -134,11 +131,10 @@ class FetchRepoCommand(NodeCommand):
|
|
|
134
131
|
content=f"Repository contents from {display_path}:\n\n{content}"
|
|
135
132
|
)
|
|
136
133
|
session.staged_content.add([staged_part])
|
|
137
|
-
|
|
138
134
|
# Send successful result - wrap in code block for proper display
|
|
139
135
|
staged_count = len(session.staged_content)
|
|
140
136
|
await session.notifications.tool_call_progress(
|
|
141
|
-
tool_call_id=
|
|
137
|
+
tool_call_id=tc_id,
|
|
142
138
|
status="completed",
|
|
143
139
|
title=f"Repository {display_path} fetched and staged ({staged_count} total parts)",
|
|
144
140
|
content=[f"```\n{content}\n```"],
|
|
@@ -149,21 +145,25 @@ class FetchRepoCommand(NodeCommand):
|
|
|
149
145
|
"HTTP error fetching repository", repo=repo, status=e.response.status_code
|
|
150
146
|
)
|
|
151
147
|
await session.notifications.tool_call_progress(
|
|
152
|
-
tool_call_id=
|
|
148
|
+
tool_call_id=tc_id,
|
|
153
149
|
status="failed",
|
|
154
150
|
title=f"HTTP {e.response.status_code}: Failed to fetch {repo}",
|
|
155
151
|
)
|
|
156
152
|
except httpx.RequestError as e:
|
|
157
153
|
logger.exception("Request error fetching repository", repo=repo)
|
|
158
154
|
await session.notifications.tool_call_progress(
|
|
159
|
-
tool_call_id=
|
|
155
|
+
tool_call_id=tc_id,
|
|
160
156
|
status="failed",
|
|
161
157
|
title=f"Network error: {e}",
|
|
162
158
|
)
|
|
163
159
|
except Exception as e:
|
|
164
160
|
logger.exception("Unexpected error fetching repository", repo=repo)
|
|
165
161
|
await session.notifications.tool_call_progress(
|
|
166
|
-
tool_call_id=
|
|
162
|
+
tool_call_id=tc_id,
|
|
167
163
|
status="failed",
|
|
168
164
|
title=f"Error: {e}",
|
|
169
165
|
)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
if __name__ == "__main__":
|
|
169
|
+
cmd = FetchRepoCommand()
|
|
@@ -7,14 +7,10 @@ the Agent Client Protocol.
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
9
|
import asyncio
|
|
10
|
-
from datetime import timedelta
|
|
11
10
|
import functools
|
|
12
11
|
from typing import TYPE_CHECKING, Any, Self
|
|
13
12
|
|
|
14
|
-
import
|
|
15
|
-
|
|
16
|
-
from acp import AgentSideConnection
|
|
17
|
-
from acp.stdio import stdio_streams
|
|
13
|
+
from acp import serve
|
|
18
14
|
from agentpool import AgentPool
|
|
19
15
|
from agentpool.log import get_logger
|
|
20
16
|
from agentpool.models.manifest import AgentsManifest
|
|
@@ -23,41 +19,14 @@ from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent
|
|
|
23
19
|
|
|
24
20
|
|
|
25
21
|
if TYPE_CHECKING:
|
|
26
|
-
from collections.abc import Sequence
|
|
27
|
-
|
|
28
|
-
from tokonomics.model_discovery import ProviderType
|
|
29
|
-
from tokonomics.model_discovery.model_info import ModelInfo
|
|
30
22
|
from upathtools import JoinablePathLike
|
|
31
23
|
|
|
32
|
-
from acp
|
|
24
|
+
from acp import Transport
|
|
33
25
|
|
|
34
26
|
|
|
35
27
|
logger = get_logger(__name__)
|
|
36
28
|
|
|
37
29
|
|
|
38
|
-
def _convert_to_acp_model_info(
|
|
39
|
-
toko_models: Sequence[ModelInfo],
|
|
40
|
-
) -> list[ACPModelInfo]:
|
|
41
|
-
"""Convert tokonomics ModelInfo list to ACP ModelInfo list.
|
|
42
|
-
|
|
43
|
-
Args:
|
|
44
|
-
toko_models: List of tokonomics ModelInfo objects
|
|
45
|
-
|
|
46
|
-
Returns:
|
|
47
|
-
List of ACP ModelInfo objects with pydantic_ai_id as model_id
|
|
48
|
-
"""
|
|
49
|
-
from acp.schema import ModelInfo as ACPModelInfo
|
|
50
|
-
|
|
51
|
-
return [
|
|
52
|
-
ACPModelInfo(
|
|
53
|
-
model_id=model.pydantic_ai_id,
|
|
54
|
-
name=f"{model.provider}: {model.name}" if model.provider else model.name,
|
|
55
|
-
description=model.format(),
|
|
56
|
-
)
|
|
57
|
-
for model in toko_models
|
|
58
|
-
]
|
|
59
|
-
|
|
60
|
-
|
|
61
30
|
class ACPServer(BaseServer):
|
|
62
31
|
"""ACP (Agent Client Protocol) server for agentpool using external library.
|
|
63
32
|
|
|
@@ -75,13 +44,13 @@ class ACPServer(BaseServer):
|
|
|
75
44
|
name: str | None = None,
|
|
76
45
|
file_access: bool = True,
|
|
77
46
|
terminal_access: bool = True,
|
|
78
|
-
providers: list[ProviderType] | None = None,
|
|
79
47
|
debug_messages: bool = False,
|
|
80
48
|
debug_file: str | None = None,
|
|
81
49
|
debug_commands: bool = False,
|
|
82
50
|
agent: str | None = None,
|
|
83
51
|
load_skills: bool = True,
|
|
84
52
|
config_path: str | None = None,
|
|
53
|
+
transport: Transport = "stdio",
|
|
85
54
|
) -> None:
|
|
86
55
|
"""Initialize ACP server with configuration.
|
|
87
56
|
|
|
@@ -90,27 +59,24 @@ class ACPServer(BaseServer):
|
|
|
90
59
|
name: Optional Server name (auto-generated if None)
|
|
91
60
|
file_access: Whether to support file access operations
|
|
92
61
|
terminal_access: Whether to support terminal access operations
|
|
93
|
-
providers: List of providers to use for model discovery (None = openrouter)
|
|
94
62
|
debug_messages: Whether to enable debug message logging
|
|
95
63
|
debug_file: File path for debug message logging
|
|
96
64
|
debug_commands: Whether to enable debug slash commands for testing
|
|
97
65
|
agent: Optional specific agent name to use (defaults to first agent)
|
|
98
66
|
load_skills: Whether to load client-side skills from .claude/skills
|
|
99
67
|
config_path: Path to the configuration file (for tracking/hot-switching)
|
|
68
|
+
transport: Transport configuration ("stdio", "websocket", or transport object)
|
|
100
69
|
"""
|
|
101
70
|
super().__init__(pool, name=name, raise_exceptions=True)
|
|
102
71
|
self.file_access = file_access
|
|
103
72
|
self.terminal_access = terminal_access
|
|
104
|
-
self.providers = providers or ["openai", "anthropic", "gemini"]
|
|
105
73
|
self.debug_messages = debug_messages
|
|
106
74
|
self.debug_file = debug_file
|
|
107
75
|
self.debug_commands = debug_commands
|
|
108
76
|
self.agent = agent
|
|
109
77
|
self.load_skills = load_skills
|
|
110
78
|
self.config_path = config_path
|
|
111
|
-
|
|
112
|
-
self._available_models: list[ACPModelInfo] = []
|
|
113
|
-
self._models_initialized = False
|
|
79
|
+
self.transport: Transport = transport
|
|
114
80
|
|
|
115
81
|
@classmethod
|
|
116
82
|
def from_config(
|
|
@@ -119,12 +85,12 @@ class ACPServer(BaseServer):
|
|
|
119
85
|
*,
|
|
120
86
|
file_access: bool = True,
|
|
121
87
|
terminal_access: bool = True,
|
|
122
|
-
providers: list[ProviderType] | None = None,
|
|
123
88
|
debug_messages: bool = False,
|
|
124
89
|
debug_file: str | None = None,
|
|
125
90
|
debug_commands: bool = False,
|
|
126
91
|
agent: str | None = None,
|
|
127
92
|
load_skills: bool = True,
|
|
93
|
+
transport: Transport = "stdio",
|
|
128
94
|
) -> Self:
|
|
129
95
|
"""Create ACP server from existing agentpool configuration.
|
|
130
96
|
|
|
@@ -132,12 +98,12 @@ class ACPServer(BaseServer):
|
|
|
132
98
|
config_path: Path to agentpool YAML config file
|
|
133
99
|
file_access: Enable file system access
|
|
134
100
|
terminal_access: Enable terminal access
|
|
135
|
-
providers: List of provider types to use for model discovery
|
|
136
101
|
debug_messages: Enable saving JSON messages to file
|
|
137
102
|
debug_file: Path to debug file
|
|
138
103
|
debug_commands: Enable debug slash commands for testing
|
|
139
104
|
agent: Optional specific agent name to use (defaults to first agent)
|
|
140
105
|
load_skills: Whether to load client-side skills from .claude/skills
|
|
106
|
+
transport: Transport configuration ("stdio", "websocket", or transport object)
|
|
141
107
|
|
|
142
108
|
Returns:
|
|
143
109
|
Configured ACP server instance with agent pool from config
|
|
@@ -148,13 +114,13 @@ class ACPServer(BaseServer):
|
|
|
148
114
|
pool,
|
|
149
115
|
file_access=file_access,
|
|
150
116
|
terminal_access=terminal_access,
|
|
151
|
-
providers=providers,
|
|
152
117
|
debug_messages=debug_messages,
|
|
153
118
|
debug_file=debug_file or "acp-debug.jsonl" if debug_messages else None,
|
|
154
119
|
debug_commands=debug_commands,
|
|
155
120
|
agent=agent,
|
|
156
121
|
load_skills=load_skills,
|
|
157
122
|
config_path=str(config_path),
|
|
123
|
+
transport=transport,
|
|
158
124
|
)
|
|
159
125
|
agent_names = list(server.pool.agents.keys())
|
|
160
126
|
|
|
@@ -170,13 +136,13 @@ class ACPServer(BaseServer):
|
|
|
170
136
|
|
|
171
137
|
async def _start_async(self) -> None:
|
|
172
138
|
"""Start the ACP server (blocking async - runs until stopped)."""
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
139
|
+
transport_name = (
|
|
140
|
+
type(self.transport).__name__ if not isinstance(self.transport, str) else self.transport
|
|
141
|
+
)
|
|
142
|
+
self.log.info("Starting ACP server", transport=transport_name)
|
|
176
143
|
create_acp_agent = functools.partial(
|
|
177
144
|
AgentPoolACPAgent,
|
|
178
145
|
agent_pool=self.pool,
|
|
179
|
-
available_models=self._available_models,
|
|
180
146
|
file_access=self.file_access,
|
|
181
147
|
terminal_access=self.terminal_access,
|
|
182
148
|
debug_commands=self.debug_commands,
|
|
@@ -184,21 +150,24 @@ class ACPServer(BaseServer):
|
|
|
184
150
|
load_skills=self.load_skills,
|
|
185
151
|
server=self,
|
|
186
152
|
)
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
conn = AgentSideConnection(create_acp_agent, writer, reader, debug_file=file)
|
|
153
|
+
|
|
154
|
+
debug_file = self.debug_file if self.debug_messages else None
|
|
190
155
|
self.log.info("ACP server started", file=self.file_access, terminal=self.terminal_access)
|
|
191
|
-
|
|
192
|
-
|
|
156
|
+
|
|
157
|
+
try:
|
|
158
|
+
await serve(
|
|
159
|
+
create_acp_agent,
|
|
160
|
+
transport=self.transport,
|
|
161
|
+
shutdown_event=self._shutdown_event,
|
|
162
|
+
debug_file=debug_file,
|
|
163
|
+
)
|
|
193
164
|
except asyncio.CancelledError:
|
|
194
165
|
self.log.info("ACP server shutdown requested")
|
|
195
166
|
raise
|
|
196
167
|
except KeyboardInterrupt:
|
|
197
168
|
self.log.info("ACP server shutdown requested")
|
|
198
169
|
except Exception:
|
|
199
|
-
self.log.exception("
|
|
200
|
-
finally:
|
|
201
|
-
await conn.close()
|
|
170
|
+
self.log.exception("ACP server error")
|
|
202
171
|
|
|
203
172
|
async def swap_pool(
|
|
204
173
|
self,
|
|
@@ -261,28 +230,3 @@ class ACPServer(BaseServer):
|
|
|
261
230
|
|
|
262
231
|
self.log.info("Pool swapped successfully", agent_names=agent_names, default_agent=agent)
|
|
263
232
|
return agent_names
|
|
264
|
-
|
|
265
|
-
@logfire.instrument("ACP: Initializing models.")
|
|
266
|
-
async def _initialize_models(self) -> None:
|
|
267
|
-
"""Initialize available models using tokonomics model discovery.
|
|
268
|
-
|
|
269
|
-
Converts tokonomics ModelInfo to ACP ModelInfo format at startup
|
|
270
|
-
so all downstream code works with ACP types consistently.
|
|
271
|
-
"""
|
|
272
|
-
from tokonomics.model_discovery import get_all_models
|
|
273
|
-
|
|
274
|
-
if self._models_initialized:
|
|
275
|
-
return
|
|
276
|
-
try:
|
|
277
|
-
self.log.info("Discovering available models...")
|
|
278
|
-
delta = timedelta(days=200)
|
|
279
|
-
toko_models = await get_all_models(providers=self.providers, max_age=delta)
|
|
280
|
-
# Convert to ACP format once at startup
|
|
281
|
-
self._available_models = _convert_to_acp_model_info(toko_models)
|
|
282
|
-
self._models_initialized = True
|
|
283
|
-
self.log.info("Discovered models", count=len(self._available_models))
|
|
284
|
-
except Exception:
|
|
285
|
-
self.log.exception("Failed to discover models")
|
|
286
|
-
self._available_models = []
|
|
287
|
-
finally:
|
|
288
|
-
self._models_initialized = True
|