agentpool 2.1.9__py3-none-any.whl → 2.5.0__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 -4
- acp/acp_requests.py +20 -77
- acp/agent/connection.py +8 -0
- acp/agent/implementations/debug_server/debug_server.py +6 -2
- acp/agent/protocol.py +6 -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/client/connection.py +38 -29
- acp/client/implementations/default_client.py +3 -2
- acp/client/implementations/headless_client.py +2 -2
- acp/connection.py +2 -2
- acp/notifications.py +20 -50
- acp/schema/__init__.py +2 -0
- acp/schema/agent_responses.py +21 -0
- acp/schema/client_requests.py +3 -3
- acp/schema/session_state.py +63 -29
- acp/stdio.py +39 -9
- acp/task/supervisor.py +2 -2
- acp/transports.py +362 -2
- acp/utils.py +17 -4
- agentpool/__init__.py +6 -1
- agentpool/agents/__init__.py +2 -0
- agentpool/agents/acp_agent/acp_agent.py +407 -277
- agentpool/agents/acp_agent/acp_converters.py +196 -38
- agentpool/agents/acp_agent/client_handler.py +191 -26
- agentpool/agents/acp_agent/session_state.py +17 -6
- agentpool/agents/agent.py +607 -572
- agentpool/agents/agui_agent/__init__.py +0 -2
- agentpool/agents/agui_agent/agui_agent.py +176 -110
- agentpool/agents/agui_agent/agui_converters.py +0 -131
- agentpool/agents/agui_agent/helpers.py +3 -4
- agentpool/agents/base_agent.py +632 -17
- agentpool/agents/claude_code_agent/FORKING.md +191 -0
- agentpool/agents/claude_code_agent/__init__.py +13 -1
- agentpool/agents/claude_code_agent/claude_code_agent.py +1058 -291
- agentpool/agents/claude_code_agent/converters.py +74 -143
- agentpool/agents/claude_code_agent/history.py +474 -0
- agentpool/agents/claude_code_agent/models.py +77 -0
- agentpool/agents/claude_code_agent/static_info.py +100 -0
- agentpool/agents/claude_code_agent/usage.py +242 -0
- agentpool/agents/context.py +40 -0
- agentpool/agents/events/__init__.py +24 -0
- agentpool/agents/events/builtin_handlers.py +67 -1
- agentpool/agents/events/event_emitter.py +32 -2
- agentpool/agents/events/events.py +104 -3
- agentpool/agents/events/infer_info.py +145 -0
- agentpool/agents/events/processors.py +254 -0
- agentpool/agents/interactions.py +41 -6
- agentpool/agents/modes.py +67 -0
- agentpool/agents/slashed_agent.py +5 -4
- agentpool/agents/tool_call_accumulator.py +213 -0
- agentpool/agents/tool_wrapping.py +18 -6
- agentpool/common_types.py +56 -21
- agentpool/config_resources/__init__.py +38 -1
- agentpool/config_resources/acp_assistant.yml +2 -2
- agentpool/config_resources/agents.yml +3 -0
- agentpool/config_resources/agents_template.yml +1 -0
- agentpool/config_resources/claude_code_agent.yml +10 -6
- agentpool/config_resources/external_acp_agents.yml +2 -1
- agentpool/delegation/base_team.py +4 -30
- agentpool/delegation/pool.py +136 -289
- agentpool/delegation/team.py +58 -57
- agentpool/delegation/teamrun.py +51 -55
- 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/functional/run.py +10 -4
- agentpool/mcp_server/__init__.py +0 -2
- agentpool/mcp_server/client.py +76 -32
- agentpool/mcp_server/conversions.py +54 -13
- agentpool/mcp_server/manager.py +34 -54
- agentpool/mcp_server/registries/official_registry_client.py +35 -1
- agentpool/mcp_server/tool_bridge.py +186 -139
- agentpool/messaging/__init__.py +0 -2
- agentpool/messaging/compaction.py +72 -197
- agentpool/messaging/connection_manager.py +11 -10
- agentpool/messaging/event_manager.py +5 -5
- agentpool/messaging/message_container.py +6 -30
- agentpool/messaging/message_history.py +99 -8
- agentpool/messaging/messagenode.py +52 -14
- agentpool/messaging/messages.py +54 -35
- agentpool/messaging/processing.py +12 -22
- agentpool/models/__init__.py +1 -1
- agentpool/models/acp_agents/base.py +6 -24
- agentpool/models/acp_agents/mcp_capable.py +126 -157
- agentpool/models/acp_agents/non_mcp.py +129 -95
- agentpool/models/agents.py +98 -76
- agentpool/models/agui_agents.py +1 -1
- agentpool/models/claude_code_agents.py +144 -19
- agentpool/models/file_parsing.py +0 -1
- agentpool/models/manifest.py +113 -50
- agentpool/prompts/conversion_manager.py +1 -1
- agentpool/prompts/prompts.py +5 -2
- agentpool/repomap.py +1 -1
- agentpool/resource_providers/__init__.py +11 -1
- agentpool/resource_providers/aggregating.py +56 -5
- agentpool/resource_providers/base.py +70 -4
- agentpool/resource_providers/codemode/code_executor.py +72 -5
- agentpool/resource_providers/codemode/helpers.py +2 -2
- agentpool/resource_providers/codemode/provider.py +64 -12
- agentpool/resource_providers/codemode/remote_mcp_execution.py +2 -2
- agentpool/resource_providers/codemode/remote_provider.py +9 -12
- agentpool/resource_providers/filtering.py +3 -1
- agentpool/resource_providers/mcp_provider.py +89 -12
- agentpool/resource_providers/plan_provider.py +228 -46
- agentpool/resource_providers/pool.py +7 -3
- agentpool/resource_providers/resource_info.py +111 -0
- agentpool/resource_providers/static.py +4 -2
- agentpool/sessions/__init__.py +4 -1
- agentpool/sessions/manager.py +33 -5
- agentpool/sessions/models.py +59 -6
- agentpool/sessions/protocol.py +28 -0
- agentpool/sessions/session.py +11 -55
- agentpool/skills/registry.py +13 -8
- agentpool/storage/manager.py +572 -49
- agentpool/talk/registry.py +4 -4
- agentpool/talk/talk.py +9 -10
- agentpool/testing.py +538 -20
- agentpool/tool_impls/__init__.py +6 -0
- agentpool/tool_impls/agent_cli/__init__.py +42 -0
- agentpool/tool_impls/agent_cli/tool.py +95 -0
- agentpool/tool_impls/bash/__init__.py +64 -0
- agentpool/tool_impls/bash/helpers.py +35 -0
- agentpool/tool_impls/bash/tool.py +171 -0
- agentpool/tool_impls/delete_path/__init__.py +70 -0
- agentpool/tool_impls/delete_path/tool.py +142 -0
- agentpool/tool_impls/download_file/__init__.py +80 -0
- agentpool/tool_impls/download_file/tool.py +183 -0
- agentpool/tool_impls/execute_code/__init__.py +55 -0
- agentpool/tool_impls/execute_code/tool.py +163 -0
- agentpool/tool_impls/grep/__init__.py +80 -0
- agentpool/tool_impls/grep/tool.py +200 -0
- agentpool/tool_impls/list_directory/__init__.py +73 -0
- agentpool/tool_impls/list_directory/tool.py +197 -0
- agentpool/tool_impls/question/__init__.py +42 -0
- agentpool/tool_impls/question/tool.py +127 -0
- agentpool/tool_impls/read/__init__.py +104 -0
- agentpool/tool_impls/read/tool.py +305 -0
- agentpool/tools/__init__.py +2 -1
- agentpool/tools/base.py +114 -34
- agentpool/tools/manager.py +57 -1
- agentpool/ui/base.py +2 -2
- agentpool/ui/mock_provider.py +2 -2
- agentpool/ui/stdlib_provider.py +2 -2
- 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 +616 -2
- agentpool/utils/subprocess_utils.py +155 -0
- agentpool/utils/token_breakdown.py +461 -0
- agentpool/vfs_registry.py +7 -2
- {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/METADATA +41 -27
- agentpool-2.5.0.dist-info/RECORD +579 -0
- {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/WHEEL +1 -1
- agentpool_cli/__main__.py +24 -0
- agentpool_cli/create.py +1 -1
- agentpool_cli/serve_acp.py +100 -21
- agentpool_cli/serve_agui.py +87 -0
- agentpool_cli/serve_opencode.py +119 -0
- agentpool_cli/ui.py +557 -0
- agentpool_commands/__init__.py +42 -5
- agentpool_commands/agents.py +75 -2
- agentpool_commands/history.py +62 -0
- agentpool_commands/mcp.py +176 -0
- agentpool_commands/models.py +56 -3
- agentpool_commands/pool.py +260 -0
- agentpool_commands/session.py +1 -1
- agentpool_commands/text_sharing/__init__.py +119 -0
- agentpool_commands/text_sharing/base.py +123 -0
- agentpool_commands/text_sharing/github_gist.py +80 -0
- agentpool_commands/text_sharing/opencode.py +462 -0
- agentpool_commands/text_sharing/paste_rs.py +59 -0
- agentpool_commands/text_sharing/pastebin.py +116 -0
- agentpool_commands/text_sharing/shittycodingagent.py +112 -0
- agentpool_commands/tools.py +57 -0
- agentpool_commands/utils.py +80 -30
- agentpool_config/__init__.py +30 -2
- agentpool_config/agentpool_tools.py +498 -0
- agentpool_config/builtin_tools.py +77 -22
- agentpool_config/commands.py +24 -1
- agentpool_config/compaction.py +258 -0
- agentpool_config/converters.py +1 -1
- agentpool_config/event_handlers.py +42 -0
- agentpool_config/events.py +1 -1
- agentpool_config/forward_targets.py +1 -4
- agentpool_config/jinja.py +3 -3
- agentpool_config/mcp_server.py +132 -6
- agentpool_config/nodes.py +1 -1
- agentpool_config/observability.py +44 -0
- agentpool_config/session.py +0 -3
- agentpool_config/storage.py +82 -38
- agentpool_config/task.py +3 -3
- agentpool_config/tools.py +11 -22
- agentpool_config/toolsets.py +109 -233
- agentpool_server/a2a_server/agent_worker.py +307 -0
- agentpool_server/a2a_server/server.py +23 -18
- agentpool_server/acp_server/acp_agent.py +234 -181
- agentpool_server/acp_server/commands/acp_commands.py +151 -156
- agentpool_server/acp_server/commands/docs_commands/fetch_repo.py +18 -17
- agentpool_server/acp_server/event_converter.py +651 -0
- agentpool_server/acp_server/input_provider.py +53 -10
- agentpool_server/acp_server/server.py +24 -90
- agentpool_server/acp_server/session.py +173 -331
- agentpool_server/acp_server/session_manager.py +8 -34
- agentpool_server/agui_server/server.py +3 -1
- agentpool_server/mcp_server/server.py +5 -2
- agentpool_server/opencode_server/.rules +95 -0
- agentpool_server/opencode_server/ENDPOINTS.md +401 -0
- agentpool_server/opencode_server/OPENCODE_UI_TOOLS_COMPLETE.md +202 -0
- agentpool_server/opencode_server/__init__.py +19 -0
- agentpool_server/opencode_server/command_validation.py +172 -0
- agentpool_server/opencode_server/converters.py +975 -0
- agentpool_server/opencode_server/dependencies.py +24 -0
- agentpool_server/opencode_server/input_provider.py +421 -0
- agentpool_server/opencode_server/models/__init__.py +250 -0
- agentpool_server/opencode_server/models/agent.py +53 -0
- agentpool_server/opencode_server/models/app.py +72 -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 +821 -0
- agentpool_server/opencode_server/models/file.py +88 -0
- agentpool_server/opencode_server/models/mcp.py +44 -0
- agentpool_server/opencode_server/models/message.py +179 -0
- agentpool_server/opencode_server/models/parts.py +323 -0
- agentpool_server/opencode_server/models/provider.py +81 -0
- agentpool_server/opencode_server/models/pty.py +43 -0
- agentpool_server/opencode_server/models/question.py +56 -0
- agentpool_server/opencode_server/models/session.py +111 -0
- agentpool_server/opencode_server/routes/__init__.py +29 -0
- agentpool_server/opencode_server/routes/agent_routes.py +473 -0
- agentpool_server/opencode_server/routes/app_routes.py +202 -0
- agentpool_server/opencode_server/routes/config_routes.py +302 -0
- agentpool_server/opencode_server/routes/file_routes.py +571 -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 +761 -0
- agentpool_server/opencode_server/routes/permission_routes.py +63 -0
- agentpool_server/opencode_server/routes/pty_routes.py +300 -0
- agentpool_server/opencode_server/routes/question_routes.py +128 -0
- agentpool_server/opencode_server/routes/session_routes.py +1276 -0
- agentpool_server/opencode_server/routes/tui_routes.py +139 -0
- agentpool_server/opencode_server/server.py +475 -0
- agentpool_server/opencode_server/state.py +151 -0
- agentpool_server/opencode_server/time_utils.py +8 -0
- agentpool_storage/__init__.py +12 -0
- agentpool_storage/base.py +184 -2
- agentpool_storage/claude_provider/ARCHITECTURE.md +433 -0
- agentpool_storage/claude_provider/__init__.py +42 -0
- agentpool_storage/claude_provider/provider.py +1089 -0
- agentpool_storage/file_provider.py +278 -15
- agentpool_storage/memory_provider.py +193 -12
- agentpool_storage/models.py +3 -0
- agentpool_storage/opencode_provider/ARCHITECTURE.md +386 -0
- agentpool_storage/opencode_provider/__init__.py +16 -0
- agentpool_storage/opencode_provider/helpers.py +414 -0
- agentpool_storage/opencode_provider/provider.py +895 -0
- agentpool_storage/project_store.py +325 -0
- agentpool_storage/session_store.py +26 -6
- agentpool_storage/sql_provider/__init__.py +4 -2
- agentpool_storage/sql_provider/models.py +48 -0
- agentpool_storage/sql_provider/sql_provider.py +269 -3
- agentpool_storage/sql_provider/utils.py +12 -13
- agentpool_storage/zed_provider/__init__.py +16 -0
- agentpool_storage/zed_provider/helpers.py +281 -0
- agentpool_storage/zed_provider/models.py +130 -0
- agentpool_storage/zed_provider/provider.py +442 -0
- agentpool_storage/zed_provider.py +803 -0
- agentpool_toolsets/__init__.py +0 -2
- agentpool_toolsets/builtin/__init__.py +2 -12
- agentpool_toolsets/builtin/code.py +96 -57
- agentpool_toolsets/builtin/debug.py +118 -48
- agentpool_toolsets/builtin/execution_environment.py +115 -230
- agentpool_toolsets/builtin/file_edit/file_edit.py +115 -7
- agentpool_toolsets/builtin/skills.py +9 -4
- agentpool_toolsets/builtin/subagent_tools.py +64 -51
- agentpool_toolsets/builtin/workers.py +4 -2
- agentpool_toolsets/composio_toolset.py +2 -2
- agentpool_toolsets/entry_points.py +3 -1
- agentpool_toolsets/fsspec_toolset/__init__.py +13 -1
- agentpool_toolsets/fsspec_toolset/diagnostics.py +860 -73
- agentpool_toolsets/fsspec_toolset/grep.py +99 -7
- agentpool_toolsets/fsspec_toolset/helpers.py +3 -2
- agentpool_toolsets/fsspec_toolset/image_utils.py +161 -0
- agentpool_toolsets/fsspec_toolset/toolset.py +500 -95
- agentpool_toolsets/mcp_discovery/__init__.py +5 -0
- agentpool_toolsets/mcp_discovery/data/mcp_servers.parquet +0 -0
- agentpool_toolsets/mcp_discovery/toolset.py +511 -0
- agentpool_toolsets/mcp_run_toolset.py +87 -12
- agentpool_toolsets/notifications.py +33 -33
- agentpool_toolsets/openapi.py +3 -1
- agentpool_toolsets/search_toolset.py +3 -1
- agentpool-2.1.9.dist-info/RECORD +0 -474
- agentpool_config/resources.py +0 -33
- agentpool_server/acp_server/acp_tools.py +0 -43
- agentpool_server/acp_server/commands/spawn.py +0 -210
- agentpool_storage/text_log_provider.py +0 -275
- agentpool_toolsets/builtin/agent_management.py +0 -239
- agentpool_toolsets/builtin/chain.py +0 -288
- agentpool_toolsets/builtin/history.py +0 -36
- agentpool_toolsets/builtin/integration.py +0 -85
- agentpool_toolsets/builtin/tool_management.py +0 -90
- agentpool_toolsets/builtin/user_interaction.py +0 -52
- agentpool_toolsets/semantic_memory_toolset.py +0 -536
- {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/entry_points.txt +0 -0
- {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""Resource information model with read capability."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Awaitable, Callable
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from typing import TYPE_CHECKING, Any, Self
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from mcp.types import Resource as MCPResource
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# Type alias for the reader function
|
|
15
|
+
ResourceReader = Callable[[str], Awaitable[list[str]]]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class ResourceInfo:
|
|
20
|
+
"""Information about an available resource with read capability.
|
|
21
|
+
|
|
22
|
+
This class provides essential information about a resource and can read
|
|
23
|
+
its content when a reader is available.
|
|
24
|
+
|
|
25
|
+
Example:
|
|
26
|
+
```python
|
|
27
|
+
# List resources from tool manager
|
|
28
|
+
resources = await agent.tools.list_resources()
|
|
29
|
+
|
|
30
|
+
# Read a specific resource
|
|
31
|
+
for resource in resources:
|
|
32
|
+
if resource.name == "config.json":
|
|
33
|
+
content = await resource.read()
|
|
34
|
+
print(content)
|
|
35
|
+
```
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
name: str
|
|
39
|
+
"""Name of the resource"""
|
|
40
|
+
|
|
41
|
+
uri: str
|
|
42
|
+
"""URI identifying the resource location"""
|
|
43
|
+
|
|
44
|
+
description: str | None = None
|
|
45
|
+
"""Optional description of the resource's content or purpose"""
|
|
46
|
+
|
|
47
|
+
mime_type: str | None = None
|
|
48
|
+
"""MIME type of the resource content"""
|
|
49
|
+
|
|
50
|
+
client: str | None = None
|
|
51
|
+
"""Name of the MCP client/server providing this resource"""
|
|
52
|
+
|
|
53
|
+
annotations: dict[str, Any] = field(default_factory=dict)
|
|
54
|
+
"""Additional annotations/metadata for the resource"""
|
|
55
|
+
|
|
56
|
+
_reader: ResourceReader | None = field(default=None, repr=False, compare=False)
|
|
57
|
+
"""Internal reader function for fetching content"""
|
|
58
|
+
|
|
59
|
+
async def read(self) -> list[str]:
|
|
60
|
+
"""Read the resource content.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
List of text contents from the resource
|
|
64
|
+
|
|
65
|
+
Raises:
|
|
66
|
+
RuntimeError: If no reader is available or read fails
|
|
67
|
+
"""
|
|
68
|
+
if self._reader is None:
|
|
69
|
+
msg = f"No reader available for resource: {self.uri}"
|
|
70
|
+
raise RuntimeError(msg)
|
|
71
|
+
return await self._reader(self.uri)
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def can_read(self) -> bool:
|
|
75
|
+
"""Check if this resource can be read."""
|
|
76
|
+
return self._reader is not None
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
async def from_mcp_resource(
|
|
80
|
+
cls,
|
|
81
|
+
resource: MCPResource,
|
|
82
|
+
client_name: str | None = None,
|
|
83
|
+
reader: ResourceReader | None = None,
|
|
84
|
+
) -> Self:
|
|
85
|
+
"""Create ResourceInfo from MCP resource.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
resource: MCP resource object
|
|
89
|
+
client_name: Name of the MCP client providing this resource
|
|
90
|
+
reader: Optional reader function for fetching content
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
ResourceInfo instance
|
|
94
|
+
"""
|
|
95
|
+
annotations: dict[str, Any] = {}
|
|
96
|
+
if resource.annotations:
|
|
97
|
+
# Convert annotations to simple dict
|
|
98
|
+
if hasattr(resource.annotations, "model_dump"):
|
|
99
|
+
annotations = resource.annotations.model_dump(exclude_none=True)
|
|
100
|
+
elif isinstance(resource.annotations, dict):
|
|
101
|
+
annotations = resource.annotations
|
|
102
|
+
|
|
103
|
+
return cls(
|
|
104
|
+
name=resource.name,
|
|
105
|
+
uri=str(resource.uri),
|
|
106
|
+
description=resource.description,
|
|
107
|
+
mime_type=resource.mimeType,
|
|
108
|
+
client=client_name,
|
|
109
|
+
annotations=annotations,
|
|
110
|
+
_reader=reader,
|
|
111
|
+
)
|
|
@@ -14,8 +14,8 @@ if TYPE_CHECKING:
|
|
|
14
14
|
from agentpool import Agent, MessageNode
|
|
15
15
|
from agentpool.common_types import ToolSource, ToolType
|
|
16
16
|
from agentpool.prompts.prompts import BasePrompt
|
|
17
|
+
from agentpool.resource_providers.resource_info import ResourceInfo
|
|
17
18
|
from agentpool.tools.base import Tool
|
|
18
|
-
from agentpool_config.resources import ResourceInfo
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class StaticResourceProvider(ResourceProvider):
|
|
@@ -26,6 +26,8 @@ class StaticResourceProvider(ResourceProvider):
|
|
|
26
26
|
to the common ResourceProvider interface.
|
|
27
27
|
"""
|
|
28
28
|
|
|
29
|
+
kind = "tools"
|
|
30
|
+
|
|
29
31
|
def __init__(
|
|
30
32
|
self,
|
|
31
33
|
name: str = "static",
|
|
@@ -46,7 +48,7 @@ class StaticResourceProvider(ResourceProvider):
|
|
|
46
48
|
self._prompts = list(prompts) if prompts else []
|
|
47
49
|
self._resources = list(resources) if resources else []
|
|
48
50
|
|
|
49
|
-
async def get_tools(self) ->
|
|
51
|
+
async def get_tools(self) -> Sequence[Tool]:
|
|
50
52
|
"""Get pre-configured tools."""
|
|
51
53
|
return self._tools
|
|
52
54
|
|
agentpool/sessions/__init__.py
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
"""Session management package."""
|
|
2
2
|
|
|
3
|
-
from agentpool.sessions.models import SessionData
|
|
3
|
+
from agentpool.sessions.models import ProjectData, SessionData
|
|
4
4
|
from agentpool.sessions.store import SessionStore
|
|
5
5
|
from agentpool.sessions.manager import SessionManager
|
|
6
6
|
from agentpool.sessions.session import ClientSession
|
|
7
|
+
from agentpool.sessions.protocol import SessionInfo
|
|
7
8
|
|
|
8
9
|
__all__ = [
|
|
9
10
|
"ClientSession",
|
|
11
|
+
"ProjectData",
|
|
10
12
|
"SessionData",
|
|
13
|
+
"SessionInfo",
|
|
11
14
|
"SessionManager",
|
|
12
15
|
"SessionStore",
|
|
13
16
|
]
|
agentpool/sessions/manager.py
CHANGED
|
@@ -4,7 +4,6 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
6
|
from typing import TYPE_CHECKING, Any, Self
|
|
7
|
-
from uuid import uuid4
|
|
8
7
|
|
|
9
8
|
from agentpool.log import get_logger
|
|
10
9
|
from agentpool.sessions.models import SessionData
|
|
@@ -17,6 +16,7 @@ if TYPE_CHECKING:
|
|
|
17
16
|
|
|
18
17
|
from agentpool.delegation.pool import AgentPool
|
|
19
18
|
from agentpool.sessions.store import SessionStore
|
|
19
|
+
from agentpool.storage.manager import StorageManager
|
|
20
20
|
|
|
21
21
|
logger = get_logger(__name__)
|
|
22
22
|
|
|
@@ -37,6 +37,7 @@ class SessionManager:
|
|
|
37
37
|
pool: AgentPool[Any],
|
|
38
38
|
store: SessionStore | None = None,
|
|
39
39
|
pool_id: str | None = None,
|
|
40
|
+
storage: StorageManager | None = None,
|
|
40
41
|
) -> None:
|
|
41
42
|
"""Initialize session manager.
|
|
42
43
|
|
|
@@ -44,10 +45,12 @@ class SessionManager:
|
|
|
44
45
|
pool: Agent pool for agent access
|
|
45
46
|
store: Session persistence backend (defaults to MemorySessionStore)
|
|
46
47
|
pool_id: Optional identifier for this pool (for multi-pool setups)
|
|
48
|
+
storage: Optional storage manager for project tracking
|
|
47
49
|
"""
|
|
48
50
|
self._pool = pool
|
|
49
51
|
self._store = store or MemorySessionStore()
|
|
50
52
|
self._pool_id = pool_id
|
|
53
|
+
self._storage = storage
|
|
51
54
|
self._active: dict[str, ClientSession] = {}
|
|
52
55
|
self._lock = asyncio.Lock()
|
|
53
56
|
logger.debug("Initialized session manager", pool_id=pool_id)
|
|
@@ -94,8 +97,14 @@ class SessionManager:
|
|
|
94
97
|
logger.debug("Session manager closed", session_count=len(sessions))
|
|
95
98
|
|
|
96
99
|
def generate_session_id(self) -> str:
|
|
97
|
-
"""Generate a unique session ID.
|
|
98
|
-
|
|
100
|
+
"""Generate a unique, chronologically sortable session ID.
|
|
101
|
+
|
|
102
|
+
Uses OpenCode-compatible format: ses_{hex_timestamp}{random_base62}
|
|
103
|
+
IDs are lexicographically sortable by creation time.
|
|
104
|
+
"""
|
|
105
|
+
from agentpool.utils.identifiers import generate_session_id
|
|
106
|
+
|
|
107
|
+
return generate_session_id()
|
|
99
108
|
|
|
100
109
|
async def create(
|
|
101
110
|
self,
|
|
@@ -139,12 +148,31 @@ class SessionManager:
|
|
|
139
148
|
msg = f"Session '{session_id}' already exists"
|
|
140
149
|
raise ValueError(msg)
|
|
141
150
|
|
|
151
|
+
# Get or create project if cwd provided and storage available
|
|
152
|
+
project_id: str | None = None
|
|
153
|
+
if cwd and self._storage:
|
|
154
|
+
try:
|
|
155
|
+
from agentpool_storage.project_store import ProjectStore
|
|
156
|
+
|
|
157
|
+
project_store = ProjectStore(self._storage)
|
|
158
|
+
project = await project_store.get_or_create(cwd)
|
|
159
|
+
project_id = project.project_id
|
|
160
|
+
logger.debug(
|
|
161
|
+
"Associated session with project",
|
|
162
|
+
session_id=session_id,
|
|
163
|
+
project_id=project_id,
|
|
164
|
+
worktree=project.worktree,
|
|
165
|
+
)
|
|
166
|
+
except Exception:
|
|
167
|
+
logger.exception("Failed to create/get project for session")
|
|
168
|
+
|
|
142
169
|
# Create session data
|
|
143
170
|
data = SessionData(
|
|
144
171
|
session_id=session_id,
|
|
145
172
|
agent_name=agent_name,
|
|
146
|
-
conversation_id=conversation_id or
|
|
173
|
+
conversation_id=conversation_id or session_id,
|
|
147
174
|
pool_id=self._pool_id,
|
|
175
|
+
project_id=project_id,
|
|
148
176
|
cwd=cwd,
|
|
149
177
|
metadata=metadata or {},
|
|
150
178
|
)
|
|
@@ -280,7 +308,7 @@ class SessionManager:
|
|
|
280
308
|
if active_only:
|
|
281
309
|
sessions = list(self._active.keys())
|
|
282
310
|
if agent_name:
|
|
283
|
-
sessions = [sid for sid, s in self._active.items() if s.
|
|
311
|
+
sessions = [sid for sid, s in self._active.items() if s.agent.name == agent_name]
|
|
284
312
|
return sessions
|
|
285
313
|
|
|
286
314
|
return await self._store.list_sessions(
|
agentpool/sessions/models.py
CHANGED
|
@@ -11,6 +11,47 @@ from schemez import Schema
|
|
|
11
11
|
from agentpool.utils.now import get_now
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
class ProjectData(Schema):
|
|
15
|
+
"""Persistable project/worktree state.
|
|
16
|
+
|
|
17
|
+
Represents a codebase/worktree that agentpool operates on.
|
|
18
|
+
Sessions are associated with projects.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
project_id: str
|
|
22
|
+
"""Unique identifier (hash of canonical worktree path)."""
|
|
23
|
+
|
|
24
|
+
worktree: str
|
|
25
|
+
"""Absolute path to the project root/worktree."""
|
|
26
|
+
|
|
27
|
+
name: str | None = None
|
|
28
|
+
"""Optional friendly name for the project."""
|
|
29
|
+
|
|
30
|
+
vcs: str | None = None
|
|
31
|
+
"""Version control system type ('git', 'hg', or None)."""
|
|
32
|
+
|
|
33
|
+
config_path: str | None = None
|
|
34
|
+
"""Path to the project's config file, or None for auto-discovery."""
|
|
35
|
+
|
|
36
|
+
created_at: datetime = Field(default_factory=get_now)
|
|
37
|
+
"""When the project was first registered."""
|
|
38
|
+
|
|
39
|
+
last_active: datetime = Field(default_factory=get_now)
|
|
40
|
+
"""Last activity timestamp."""
|
|
41
|
+
|
|
42
|
+
settings: dict[str, Any] = Field(default_factory=dict)
|
|
43
|
+
"""Project-specific settings overrides."""
|
|
44
|
+
|
|
45
|
+
def touch(self) -> ProjectData:
|
|
46
|
+
"""Return copy with updated last_active timestamp."""
|
|
47
|
+
return self.model_copy(update={"last_active": get_now()})
|
|
48
|
+
|
|
49
|
+
def with_settings(self, **kwargs: Any) -> ProjectData:
|
|
50
|
+
"""Return copy with updated settings."""
|
|
51
|
+
new_settings = {**self.settings, **kwargs}
|
|
52
|
+
return self.model_copy(update={"settings": new_settings, "last_active": get_now()})
|
|
53
|
+
|
|
54
|
+
|
|
14
55
|
class SessionData(Schema):
|
|
15
56
|
"""Persistable session state.
|
|
16
57
|
|
|
@@ -27,12 +68,18 @@ class SessionData(Schema):
|
|
|
27
68
|
conversation_id: str
|
|
28
69
|
"""Links to conversation in StorageManager."""
|
|
29
70
|
|
|
30
|
-
title: str | None = None
|
|
31
|
-
"""AI-generated or user-provided title for the conversation."""
|
|
32
|
-
|
|
33
71
|
pool_id: str | None = None
|
|
34
72
|
"""Optional pool/manifest identifier for multi-pool setups."""
|
|
35
73
|
|
|
74
|
+
project_id: str | None = None
|
|
75
|
+
"""Project identifier (e.g., for OpenCode compatibility)."""
|
|
76
|
+
|
|
77
|
+
parent_id: str | None = None
|
|
78
|
+
"""Parent session ID for forked sessions."""
|
|
79
|
+
|
|
80
|
+
version: str = "1"
|
|
81
|
+
"""Session version string."""
|
|
82
|
+
|
|
36
83
|
cwd: str | None = None
|
|
37
84
|
"""Working directory for the session."""
|
|
38
85
|
|
|
@@ -66,6 +113,12 @@ class SessionData(Schema):
|
|
|
66
113
|
new_metadata = {**self.metadata, **kwargs}
|
|
67
114
|
return self.model_copy(update={"metadata": new_metadata, "last_active": get_now()})
|
|
68
115
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
116
|
+
@property
|
|
117
|
+
def title(self) -> str | None:
|
|
118
|
+
"""Human-readable title (from metadata, for protocol compatibility)."""
|
|
119
|
+
return self.metadata.get("title")
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def updated_at(self) -> str | None:
|
|
123
|
+
"""ISO timestamp of last activity (for protocol compatibility)."""
|
|
124
|
+
return self.last_active.isoformat() if self.last_active else None
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Session protocol for unified session management across agent types."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Protocol, runtime_checkable
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@runtime_checkable
|
|
9
|
+
class SessionInfo(Protocol):
|
|
10
|
+
"""Protocol for session information.
|
|
11
|
+
|
|
12
|
+
This protocol provides a unified interface for session metadata across
|
|
13
|
+
different agent implementations (ACP, ClaudeCode, native agents).
|
|
14
|
+
|
|
15
|
+
Both ACP's SessionInfo and our SessionData can fulfill this protocol.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
session_id: str
|
|
19
|
+
"""Unique identifier for the session."""
|
|
20
|
+
|
|
21
|
+
cwd: str | None
|
|
22
|
+
"""Working directory for the session (absolute path)."""
|
|
23
|
+
|
|
24
|
+
title: str | None
|
|
25
|
+
"""Human-readable title for the session."""
|
|
26
|
+
|
|
27
|
+
updated_at: str | None
|
|
28
|
+
"""ISO 8601 timestamp of last activity."""
|
agentpool/sessions/session.py
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import asyncio
|
|
6
5
|
from typing import TYPE_CHECKING, Any, Self
|
|
7
6
|
|
|
8
7
|
from agentpool.log import get_logger
|
|
@@ -57,16 +56,9 @@ class ClientSession:
|
|
|
57
56
|
self._manager = manager
|
|
58
57
|
self._agent: Agent[Any, Any] | None = None
|
|
59
58
|
self._closed = False
|
|
60
|
-
self._title_generation_triggered = False
|
|
61
|
-
self._title_task: asyncio.Task[None] | None = None
|
|
62
59
|
# Session owns conversation history - agent is stateless
|
|
63
60
|
self._history = MessageHistory()
|
|
64
|
-
|
|
65
|
-
logger.debug(
|
|
66
|
-
"Created client session",
|
|
67
|
-
session_id=data.session_id,
|
|
68
|
-
agent=data.agent_name,
|
|
69
|
-
)
|
|
61
|
+
logger.debug("Created client session", session_id=data.session_id, agent=data.agent_name)
|
|
70
62
|
|
|
71
63
|
@property
|
|
72
64
|
def session_id(self) -> str:
|
|
@@ -90,16 +82,16 @@ class ClientSession:
|
|
|
90
82
|
self._agent = self._pool.get_agent(self._data.agent_name)
|
|
91
83
|
return self._agent
|
|
92
84
|
|
|
93
|
-
@property
|
|
94
|
-
def agent_name(self) -> str:
|
|
95
|
-
"""Get current agent name."""
|
|
96
|
-
return self._data.agent_name
|
|
97
|
-
|
|
98
85
|
@property
|
|
99
86
|
def conversation_id(self) -> str:
|
|
100
87
|
"""Get conversation ID for message storage."""
|
|
101
88
|
return self._data.conversation_id
|
|
102
89
|
|
|
90
|
+
@property
|
|
91
|
+
def title(self) -> str | None:
|
|
92
|
+
"""Get conversation title (delegated to agent)."""
|
|
93
|
+
return self._agent.conversation_title if self._agent is not None else None
|
|
94
|
+
|
|
103
95
|
@property
|
|
104
96
|
def history(self) -> MessageHistory:
|
|
105
97
|
"""Get the session's conversation history."""
|
|
@@ -130,6 +122,9 @@ class ClientSession:
|
|
|
130
122
|
the agent stateless from the session's perspective. Messages
|
|
131
123
|
are automatically added to the session's history.
|
|
132
124
|
|
|
125
|
+
Title generation is handled automatically by the agent's log_conversation
|
|
126
|
+
call when the conversation is first created.
|
|
127
|
+
|
|
133
128
|
Args:
|
|
134
129
|
prompt: User prompt to send to the agent
|
|
135
130
|
**kwargs: Additional arguments passed to agent.run()
|
|
@@ -137,42 +132,13 @@ class ClientSession:
|
|
|
137
132
|
Returns:
|
|
138
133
|
The agent's response message
|
|
139
134
|
"""
|
|
140
|
-
|
|
135
|
+
return await self.agent.run(
|
|
141
136
|
prompt,
|
|
142
137
|
message_history=self._history,
|
|
143
138
|
conversation_id=self.conversation_id,
|
|
144
139
|
**kwargs,
|
|
145
140
|
)
|
|
146
141
|
|
|
147
|
-
# Trigger title generation after first exchange (fire-and-forget)
|
|
148
|
-
# Note: Message logging is handled by the agent itself via MessageNode.log_message
|
|
149
|
-
if not self._title_generation_triggered and self._pool.storage:
|
|
150
|
-
self._title_generation_triggered = True
|
|
151
|
-
self._title_task = asyncio.create_task(self._generate_title())
|
|
152
|
-
|
|
153
|
-
return result
|
|
154
|
-
|
|
155
|
-
async def _generate_title(self) -> None:
|
|
156
|
-
"""Generate conversation title in the background."""
|
|
157
|
-
if not self._pool.storage:
|
|
158
|
-
return
|
|
159
|
-
try:
|
|
160
|
-
messages = self._history.get_history()
|
|
161
|
-
if messages:
|
|
162
|
-
title = await self._pool.storage.generate_conversation_title(
|
|
163
|
-
self.conversation_id,
|
|
164
|
-
messages,
|
|
165
|
-
)
|
|
166
|
-
# Also update SessionData so title is available when listing sessions
|
|
167
|
-
if title and self._manager:
|
|
168
|
-
self._data = self._data.with_title(title)
|
|
169
|
-
await self._manager.save(self._data)
|
|
170
|
-
except Exception:
|
|
171
|
-
logger.exception(
|
|
172
|
-
"Failed to generate conversation title",
|
|
173
|
-
conversation_id=self.conversation_id,
|
|
174
|
-
)
|
|
175
|
-
|
|
176
142
|
async def switch_agent(self, agent_name: str) -> None:
|
|
177
143
|
"""Switch to a different agent.
|
|
178
144
|
|
|
@@ -190,16 +156,10 @@ class ClientSession:
|
|
|
190
156
|
|
|
191
157
|
self._agent = self._pool.get_agent(agent_name)
|
|
192
158
|
self._data = self._data.with_agent(agent_name)
|
|
193
|
-
|
|
194
159
|
# Persist the change
|
|
195
160
|
if self._manager:
|
|
196
161
|
await self._manager.save(self._data)
|
|
197
|
-
|
|
198
|
-
logger.info(
|
|
199
|
-
"Switched agent",
|
|
200
|
-
session_id=self.session_id,
|
|
201
|
-
agent=agent_name,
|
|
202
|
-
)
|
|
162
|
+
logger.info("Switched agent", session_id=self.session_id, agent=agent_name)
|
|
203
163
|
|
|
204
164
|
async def touch(self) -> None:
|
|
205
165
|
"""Update last_active timestamp and persist."""
|
|
@@ -230,10 +190,6 @@ class ClientSession:
|
|
|
230
190
|
"""
|
|
231
191
|
self._data = self._data.with_metadata(**kwargs)
|
|
232
192
|
|
|
233
|
-
def clear_history(self) -> None:
|
|
234
|
-
"""Clear the session's conversation history."""
|
|
235
|
-
self._history.clear()
|
|
236
|
-
|
|
237
193
|
def get_history_messages(self) -> list[ChatMessage[Any]]:
|
|
238
194
|
"""Get all messages in the session's history."""
|
|
239
195
|
return self._history.get_history()
|
agentpool/skills/registry.py
CHANGED
|
@@ -72,7 +72,9 @@ class SkillsRegistry(BaseRegistry[str, Skill]):
|
|
|
72
72
|
if not isinstance(fs, AsyncFileSystem):
|
|
73
73
|
fs = AsyncFileSystemWrapper(fs)
|
|
74
74
|
search_path = base_path if base_path is not None else fs.root_marker
|
|
75
|
+
original_skills_dir: UPath | None = None
|
|
75
76
|
else:
|
|
77
|
+
original_skills_dir = to_upath(skills_dir)
|
|
76
78
|
fs = upath_to_fs(skills_dir, **storage_options)
|
|
77
79
|
search_path = fs.root_marker
|
|
78
80
|
|
|
@@ -93,16 +95,19 @@ class SkillsRegistry(BaseRegistry[str, Skill]):
|
|
|
93
95
|
return
|
|
94
96
|
logger.info("Found skills", skills=skill_dirs, skills_dir=search_path)
|
|
95
97
|
for skill_entry in skill_dirs:
|
|
96
|
-
|
|
97
|
-
#
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
# entry["name"] is relative to the filesystem root
|
|
99
|
+
# We need to construct the full path for _parse_skill
|
|
100
|
+
entry_name = skill_entry["name"]
|
|
101
|
+
if original_skills_dir is not None:
|
|
102
|
+
# When we created fs from a path, entry names are relative to that path
|
|
103
|
+
skill_dir_path = original_skills_dir / entry_name
|
|
101
104
|
else:
|
|
102
|
-
|
|
103
|
-
|
|
105
|
+
# When fs was provided directly, entry names should be usable as-is
|
|
106
|
+
skill_dir_path = to_upath(entry_name)
|
|
107
|
+
# For fs._cat_file, use the path relative to the filesystem
|
|
108
|
+
fs_skill_md_path = f"{entry_name}/SKILL.md"
|
|
104
109
|
try:
|
|
105
|
-
await fs._cat_file(
|
|
110
|
+
await fs._cat_file(fs_skill_md_path)
|
|
106
111
|
except FileNotFoundError:
|
|
107
112
|
continue
|
|
108
113
|
|