agentpool 2.2.3__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 +0 -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/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 +18 -49
- 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/task/supervisor.py +2 -2
- acp/utils.py +2 -2
- agentpool/__init__.py +2 -0
- agentpool/agents/acp_agent/acp_agent.py +278 -263
- agentpool/agents/acp_agent/acp_converters.py +150 -17
- agentpool/agents/acp_agent/client_handler.py +35 -24
- agentpool/agents/acp_agent/session_state.py +14 -6
- agentpool/agents/agent.py +471 -643
- agentpool/agents/agui_agent/agui_agent.py +104 -107
- agentpool/agents/agui_agent/helpers.py +3 -4
- agentpool/agents/base_agent.py +485 -32
- 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 +654 -334
- agentpool/agents/claude_code_agent/converters.py +4 -141
- 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/events/__init__.py +22 -0
- agentpool/agents/events/builtin_handlers.py +65 -0
- agentpool/agents/events/event_emitter.py +3 -0
- agentpool/agents/events/events.py +84 -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 +13 -0
- agentpool/agents/slashed_agent.py +5 -4
- agentpool/agents/tool_wrapping.py +18 -6
- agentpool/common_types.py +35 -21
- 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 +9 -8
- agentpool/config_resources/external_acp_agents.yml +2 -1
- agentpool/delegation/base_team.py +4 -30
- agentpool/delegation/pool.py +104 -265
- agentpool/delegation/team.py +57 -57
- agentpool/delegation/teamrun.py +50 -55
- agentpool/functional/run.py +10 -4
- agentpool/mcp_server/client.py +73 -38
- agentpool/mcp_server/conversions.py +54 -13
- agentpool/mcp_server/manager.py +9 -23
- agentpool/mcp_server/registries/official_registry_client.py +10 -1
- agentpool/mcp_server/tool_bridge.py +114 -79
- 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 +87 -8
- agentpool/messaging/messagenode.py +52 -14
- agentpool/messaging/messages.py +2 -26
- agentpool/messaging/processing.py +10 -22
- agentpool/models/__init__.py +1 -1
- agentpool/models/acp_agents/base.py +6 -2
- agentpool/models/acp_agents/mcp_capable.py +124 -15
- agentpool/models/acp_agents/non_mcp.py +0 -23
- agentpool/models/agents.py +66 -66
- agentpool/models/agui_agents.py +1 -1
- agentpool/models/claude_code_agents.py +111 -17
- agentpool/models/file_parsing.py +0 -1
- agentpool/models/manifest.py +70 -50
- agentpool/prompts/conversion_manager.py +1 -1
- agentpool/prompts/prompts.py +5 -2
- agentpool/resource_providers/__init__.py +2 -0
- agentpool/resource_providers/aggregating.py +4 -2
- agentpool/resource_providers/base.py +13 -3
- 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 +66 -12
- agentpool/resource_providers/plan_provider.py +111 -18
- agentpool/resource_providers/pool.py +5 -3
- agentpool/resource_providers/resource_info.py +111 -0
- agentpool/resource_providers/static.py +2 -2
- agentpool/sessions/__init__.py +2 -0
- agentpool/sessions/manager.py +2 -3
- agentpool/sessions/models.py +9 -6
- agentpool/sessions/protocol.py +28 -0
- agentpool/sessions/session.py +11 -55
- agentpool/storage/manager.py +361 -54
- agentpool/talk/registry.py +4 -4
- agentpool/talk/talk.py +9 -10
- agentpool/testing.py +1 -1
- 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/streams.py +21 -96
- agentpool/vfs_registry.py +7 -2
- {agentpool-2.2.3.dist-info → agentpool-2.5.0.dist-info}/METADATA +16 -22
- {agentpool-2.2.3.dist-info → agentpool-2.5.0.dist-info}/RECORD +242 -195
- {agentpool-2.2.3.dist-info → agentpool-2.5.0.dist-info}/WHEEL +1 -1
- agentpool_cli/__main__.py +20 -0
- agentpool_cli/create.py +1 -1
- agentpool_cli/serve_acp.py +59 -1
- agentpool_cli/serve_opencode.py +1 -1
- agentpool_cli/ui.py +557 -0
- agentpool_commands/__init__.py +12 -5
- agentpool_commands/agents.py +1 -1
- 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/utils.py +31 -32
- agentpool_config/__init__.py +30 -2
- agentpool_config/agentpool_tools.py +498 -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 +1 -5
- agentpool_config/nodes.py +1 -1
- agentpool_config/observability.py +44 -0
- agentpool_config/session.py +0 -3
- agentpool_config/storage.py +38 -39
- agentpool_config/task.py +3 -3
- agentpool_config/tools.py +11 -28
- agentpool_config/toolsets.py +22 -90
- agentpool_server/a2a_server/agent_worker.py +307 -0
- agentpool_server/a2a_server/server.py +23 -18
- agentpool_server/acp_server/acp_agent.py +125 -56
- agentpool_server/acp_server/commands/acp_commands.py +46 -216
- agentpool_server/acp_server/commands/docs_commands/fetch_repo.py +8 -7
- agentpool_server/acp_server/event_converter.py +651 -0
- agentpool_server/acp_server/input_provider.py +53 -10
- agentpool_server/acp_server/server.py +1 -11
- agentpool_server/acp_server/session.py +90 -410
- 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/ENDPOINTS.md +53 -14
- agentpool_server/opencode_server/OPENCODE_UI_TOOLS_COMPLETE.md +202 -0
- agentpool_server/opencode_server/__init__.py +0 -8
- agentpool_server/opencode_server/converters.py +132 -26
- agentpool_server/opencode_server/input_provider.py +160 -8
- agentpool_server/opencode_server/models/__init__.py +42 -20
- agentpool_server/opencode_server/models/app.py +12 -0
- agentpool_server/opencode_server/models/events.py +203 -29
- agentpool_server/opencode_server/models/mcp.py +19 -0
- agentpool_server/opencode_server/models/message.py +18 -1
- agentpool_server/opencode_server/models/parts.py +134 -1
- agentpool_server/opencode_server/models/question.py +56 -0
- agentpool_server/opencode_server/models/session.py +13 -1
- agentpool_server/opencode_server/routes/__init__.py +4 -0
- agentpool_server/opencode_server/routes/agent_routes.py +33 -2
- agentpool_server/opencode_server/routes/app_routes.py +66 -3
- agentpool_server/opencode_server/routes/config_routes.py +66 -5
- agentpool_server/opencode_server/routes/file_routes.py +184 -5
- agentpool_server/opencode_server/routes/global_routes.py +1 -1
- agentpool_server/opencode_server/routes/lsp_routes.py +1 -1
- agentpool_server/opencode_server/routes/message_routes.py +122 -66
- agentpool_server/opencode_server/routes/permission_routes.py +63 -0
- agentpool_server/opencode_server/routes/pty_routes.py +23 -22
- agentpool_server/opencode_server/routes/question_routes.py +128 -0
- agentpool_server/opencode_server/routes/session_routes.py +139 -68
- agentpool_server/opencode_server/routes/tui_routes.py +1 -1
- agentpool_server/opencode_server/server.py +47 -2
- agentpool_server/opencode_server/state.py +30 -0
- agentpool_storage/__init__.py +0 -4
- agentpool_storage/base.py +81 -2
- agentpool_storage/claude_provider/ARCHITECTURE.md +433 -0
- agentpool_storage/claude_provider/__init__.py +42 -0
- agentpool_storage/{claude_provider.py → claude_provider/provider.py} +190 -8
- agentpool_storage/file_provider.py +149 -15
- agentpool_storage/memory_provider.py +132 -12
- 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/session_store.py +20 -6
- agentpool_storage/sql_provider/sql_provider.py +135 -2
- agentpool_storage/sql_provider/utils.py +2 -12
- 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 -4
- agentpool_toolsets/builtin/code.py +4 -4
- agentpool_toolsets/builtin/debug.py +115 -40
- agentpool_toolsets/builtin/execution_environment.py +54 -165
- agentpool_toolsets/builtin/skills.py +0 -77
- 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/grep.py +25 -5
- agentpool_toolsets/fsspec_toolset/helpers.py +3 -2
- agentpool_toolsets/fsspec_toolset/toolset.py +350 -66
- agentpool_toolsets/mcp_discovery/data/mcp_servers.parquet +0 -0
- agentpool_toolsets/mcp_discovery/toolset.py +74 -17
- agentpool_toolsets/mcp_run_toolset.py +8 -11
- agentpool_toolsets/notifications.py +33 -33
- agentpool_toolsets/openapi.py +3 -1
- agentpool_toolsets/search_toolset.py +3 -1
- 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/opencode_provider.py +0 -730
- agentpool_storage/text_log_provider.py +0 -276
- agentpool_toolsets/builtin/chain.py +0 -288
- agentpool_toolsets/builtin/user_interaction.py +0 -52
- agentpool_toolsets/semantic_memory_toolset.py +0 -536
- {agentpool-2.2.3.dist-info → agentpool-2.5.0.dist-info}/entry_points.txt +0 -0
- {agentpool-2.2.3.dist-info → agentpool-2.5.0.dist-info}/licenses/LICENSE +0 -0
agentpool/agents/base_agent.py
CHANGED
|
@@ -4,39 +4,54 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from abc import abstractmethod
|
|
6
6
|
import asyncio
|
|
7
|
-
from
|
|
7
|
+
from collections.abc import Callable
|
|
8
|
+
from contextlib import suppress
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from typing import TYPE_CHECKING, Any, overload
|
|
8
11
|
|
|
9
|
-
from anyenv import MultiEventHandler
|
|
10
|
-
from anyenv.signals import BoundSignal
|
|
11
|
-
|
|
12
|
+
from anyenv import MultiEventHandler, method_spawner
|
|
13
|
+
from anyenv.signals import BoundSignal, Signal
|
|
14
|
+
import anyio
|
|
12
15
|
|
|
13
|
-
from agentpool.agents.events import resolve_event_handlers
|
|
16
|
+
from agentpool.agents.events import StreamCompleteEvent, resolve_event_handlers
|
|
17
|
+
from agentpool.agents.modes import ModeInfo
|
|
14
18
|
from agentpool.log import get_logger
|
|
15
|
-
from agentpool.messaging import MessageHistory, MessageNode
|
|
19
|
+
from agentpool.messaging import ChatMessage, MessageHistory, MessageNode
|
|
20
|
+
from agentpool.prompts.convert import convert_prompts
|
|
16
21
|
from agentpool.tools.manager import ToolManager
|
|
22
|
+
from agentpool.utils.inspection import call_with_context
|
|
23
|
+
from agentpool.utils.now import get_now
|
|
17
24
|
|
|
18
25
|
|
|
19
26
|
if TYPE_CHECKING:
|
|
20
27
|
from collections.abc import AsyncIterator, Sequence
|
|
28
|
+
from datetime import datetime
|
|
21
29
|
|
|
22
|
-
from
|
|
30
|
+
from evented_config import EventConfig
|
|
23
31
|
from exxec import ExecutionEnvironment
|
|
32
|
+
from pydantic_ai import UserContent
|
|
24
33
|
from slashed import BaseCommand, CommandStore
|
|
25
34
|
from tokonomics.model_discovery.model_info import ModelInfo
|
|
26
35
|
|
|
27
36
|
from acp.schema import AvailableCommandsUpdate, ConfigOptionUpdate
|
|
37
|
+
from agentpool.agents.agent import Agent
|
|
28
38
|
from agentpool.agents.context import AgentContext
|
|
29
39
|
from agentpool.agents.events import RichAgentStreamEvent
|
|
30
40
|
from agentpool.agents.modes import ModeCategory, ModeInfo
|
|
31
41
|
from agentpool.common_types import (
|
|
42
|
+
AgentName,
|
|
32
43
|
BuiltinEventHandlerType,
|
|
33
44
|
IndividualEventHandler,
|
|
34
45
|
MCPServerStatus,
|
|
46
|
+
ProcessorCallback,
|
|
47
|
+
PromptCompatible,
|
|
35
48
|
)
|
|
36
|
-
from agentpool.delegation import AgentPool
|
|
49
|
+
from agentpool.delegation import AgentPool, Team, TeamRun
|
|
50
|
+
from agentpool.messaging import ChatMessage
|
|
37
51
|
from agentpool.talk.stats import MessageStats
|
|
38
52
|
from agentpool.ui.base import InputProvider
|
|
39
53
|
from agentpool_config.mcp_server import MCPServerConfig
|
|
54
|
+
from agentpool_config.nodes import ToolConfirmationMode
|
|
40
55
|
|
|
41
56
|
# Union type for state updates emitted via state_updated signal
|
|
42
57
|
type StateUpdate = ModeInfo | ModelInfo | AvailableCommandsUpdate | ConfigOptionUpdate
|
|
@@ -45,9 +60,6 @@ if TYPE_CHECKING:
|
|
|
45
60
|
logger = get_logger(__name__)
|
|
46
61
|
|
|
47
62
|
|
|
48
|
-
ToolConfirmationMode = Literal["always", "never", "per_tool"]
|
|
49
|
-
|
|
50
|
-
|
|
51
63
|
class BaseAgent[TDeps = None, TResult = str](MessageNode[TDeps, TResult]):
|
|
52
64
|
"""Base class for Agent, ACPAgent, AGUIAgent, and ClaudeCodeAgent.
|
|
53
65
|
|
|
@@ -60,8 +72,37 @@ class BaseAgent[TDeps = None, TResult = str](MessageNode[TDeps, TResult]):
|
|
|
60
72
|
- _input_provider: Provider for user input/confirmations
|
|
61
73
|
- env: ExecutionEnvironment for running code/commands
|
|
62
74
|
- context property: Returns NodeContext for the agent
|
|
75
|
+
|
|
76
|
+
Signals:
|
|
77
|
+
- run_failed: Emitted when agent execution fails with error details
|
|
63
78
|
"""
|
|
64
79
|
|
|
80
|
+
@dataclass(frozen=True)
|
|
81
|
+
class RunFailedEvent:
|
|
82
|
+
"""Event emitted when agent execution fails."""
|
|
83
|
+
|
|
84
|
+
agent_name: str
|
|
85
|
+
"""Name of the agent that failed."""
|
|
86
|
+
message: str
|
|
87
|
+
"""Error description."""
|
|
88
|
+
exception: Exception
|
|
89
|
+
"""The exception that caused the failure."""
|
|
90
|
+
timestamp: Any = field(default_factory=get_now) # datetime
|
|
91
|
+
"""When the failure occurred."""
|
|
92
|
+
|
|
93
|
+
@dataclass(frozen=True)
|
|
94
|
+
class AgentReset:
|
|
95
|
+
"""Emitted when agent is reset."""
|
|
96
|
+
|
|
97
|
+
agent_name: AgentName
|
|
98
|
+
previous_tools: dict[str, bool]
|
|
99
|
+
new_tools: dict[str, bool]
|
|
100
|
+
timestamp: datetime = field(default_factory=get_now)
|
|
101
|
+
|
|
102
|
+
agent_reset = Signal[AgentReset]()
|
|
103
|
+
# Signal emitted when agent execution fails
|
|
104
|
+
run_failed: Signal[RunFailedEvent] = Signal()
|
|
105
|
+
|
|
65
106
|
def __init__(
|
|
66
107
|
self,
|
|
67
108
|
*,
|
|
@@ -97,6 +138,11 @@ class BaseAgent[TDeps = None, TResult = str](MessageNode[TDeps, TResult]):
|
|
|
97
138
|
event_handlers: Event handlers for this agent
|
|
98
139
|
commands: Slash commands to register with this agent
|
|
99
140
|
"""
|
|
141
|
+
from exxec import LocalExecutionEnvironment
|
|
142
|
+
from slashed import CommandStore
|
|
143
|
+
|
|
144
|
+
from agentpool_commands import get_commands
|
|
145
|
+
|
|
100
146
|
super().__init__(
|
|
101
147
|
name=name,
|
|
102
148
|
description=description,
|
|
@@ -106,10 +152,14 @@ class BaseAgent[TDeps = None, TResult = str](MessageNode[TDeps, TResult]):
|
|
|
106
152
|
enable_logging=enable_logging,
|
|
107
153
|
event_configs=event_configs,
|
|
108
154
|
)
|
|
155
|
+
self._infinite = False
|
|
156
|
+
self._background_task: asyncio.Task[ChatMessage[Any]] | None = None
|
|
109
157
|
|
|
110
158
|
# Shared infrastructure - previously duplicated in all 4 agents
|
|
111
159
|
self._event_queue: asyncio.Queue[RichAgentStreamEvent[Any]] = asyncio.Queue()
|
|
112
|
-
|
|
160
|
+
# Use storage from agent_pool if available, otherwise memory-only
|
|
161
|
+
storage = agent_pool.storage if agent_pool else None
|
|
162
|
+
self.conversation = MessageHistory(storage=storage)
|
|
113
163
|
self.env = env or LocalExecutionEnvironment()
|
|
114
164
|
self._input_provider = input_provider
|
|
115
165
|
self._output_type: type[TResult] = output_type
|
|
@@ -119,22 +169,17 @@ class BaseAgent[TDeps = None, TResult = str](MessageNode[TDeps, TResult]):
|
|
|
119
169
|
self.event_handler: MultiEventHandler[IndividualEventHandler] = MultiEventHandler(
|
|
120
170
|
resolved_handlers
|
|
121
171
|
)
|
|
122
|
-
|
|
123
|
-
# Cancellation infrastructure
|
|
124
172
|
self._cancelled = False
|
|
125
173
|
self._current_stream_task: asyncio.Task[Any] | None = None
|
|
126
|
-
|
|
174
|
+
# Deferred initialization support - subclasses set True in __aenter__,
|
|
175
|
+
# override ensure_initialized() to do actual connection
|
|
176
|
+
self._connect_pending: bool = False
|
|
127
177
|
# State change signal - emitted when mode/model/commands change
|
|
128
178
|
# Uses union type for different state update kinds
|
|
129
179
|
self.state_updated: BoundSignal[StateUpdate] = BoundSignal()
|
|
130
|
-
|
|
131
|
-
# Command store for slash commands
|
|
132
|
-
from slashed import CommandStore
|
|
133
|
-
|
|
134
|
-
from agentpool_commands import get_commands
|
|
135
|
-
|
|
136
180
|
self._command_store: CommandStore = CommandStore()
|
|
137
|
-
|
|
181
|
+
# Initialize store (registers builtin help/exit commands)
|
|
182
|
+
self._command_store._initialize_sync()
|
|
138
183
|
# Register default agent commands
|
|
139
184
|
for command in get_commands():
|
|
140
185
|
self._command_store.register_command(command)
|
|
@@ -144,11 +189,78 @@ class BaseAgent[TDeps = None, TResult = str](MessageNode[TDeps, TResult]):
|
|
|
144
189
|
for command in commands:
|
|
145
190
|
self._command_store.register_command(command)
|
|
146
191
|
|
|
192
|
+
@overload
|
|
193
|
+
def __and__( # if other doesnt define deps, we take the agents one
|
|
194
|
+
self, other: ProcessorCallback[Any] | Team[TDeps] | Agent[TDeps, Any]
|
|
195
|
+
) -> Team[TDeps]: ...
|
|
196
|
+
|
|
197
|
+
@overload
|
|
198
|
+
def __and__( # otherwise, we dont know and deps is Any
|
|
199
|
+
self, other: ProcessorCallback[Any] | Team[Any] | Agent[Any, Any]
|
|
200
|
+
) -> Team[Any]: ...
|
|
201
|
+
|
|
202
|
+
def __and__(self, other: MessageNode[Any, Any] | ProcessorCallback[Any]) -> Team[Any]:
|
|
203
|
+
"""Create sequential team using & operator.
|
|
204
|
+
|
|
205
|
+
Example:
|
|
206
|
+
group = analyzer & planner & executor # Create group of 3
|
|
207
|
+
group = analyzer & existing_group # Add to existing group
|
|
208
|
+
"""
|
|
209
|
+
from agentpool.agents.agent import Agent
|
|
210
|
+
from agentpool.delegation.team import Team
|
|
211
|
+
|
|
212
|
+
match other:
|
|
213
|
+
case Team():
|
|
214
|
+
return Team([self, *other.nodes])
|
|
215
|
+
case Callable():
|
|
216
|
+
agent_2 = Agent.from_callback(other)
|
|
217
|
+
agent_2.agent_pool = self.agent_pool
|
|
218
|
+
return Team([self, agent_2])
|
|
219
|
+
case MessageNode():
|
|
220
|
+
return Team([self, other])
|
|
221
|
+
case _:
|
|
222
|
+
msg = f"Invalid agent type: {type(other)}"
|
|
223
|
+
raise ValueError(msg)
|
|
224
|
+
|
|
225
|
+
@overload
|
|
226
|
+
def __or__(self, other: MessageNode[TDeps, Any]) -> TeamRun[TDeps, Any]: ...
|
|
227
|
+
|
|
228
|
+
@overload
|
|
229
|
+
def __or__[TOtherDeps](self, other: MessageNode[TOtherDeps, Any]) -> TeamRun[Any, Any]: ...
|
|
230
|
+
|
|
231
|
+
@overload
|
|
232
|
+
def __or__(self, other: ProcessorCallback[Any]) -> TeamRun[Any, Any]: ...
|
|
233
|
+
|
|
234
|
+
def __or__(self, other: MessageNode[Any, Any] | ProcessorCallback[Any]) -> TeamRun[Any, Any]:
|
|
235
|
+
# Create new execution with sequential mode (for piping)
|
|
236
|
+
from agentpool import TeamRun
|
|
237
|
+
from agentpool.agents.agent import Agent
|
|
238
|
+
|
|
239
|
+
if callable(other):
|
|
240
|
+
other = Agent.from_callback(other)
|
|
241
|
+
other.agent_pool = self.agent_pool
|
|
242
|
+
|
|
243
|
+
return TeamRun([self, other])
|
|
244
|
+
|
|
147
245
|
@property
|
|
148
246
|
def command_store(self) -> CommandStore:
|
|
149
247
|
"""Get the command store for slash commands."""
|
|
150
248
|
return self._command_store
|
|
151
249
|
|
|
250
|
+
async def reset(self) -> None:
|
|
251
|
+
"""Reset agent state (conversation history and tool states)."""
|
|
252
|
+
old_tools = await self.tools.list_tools()
|
|
253
|
+
await self.conversation.clear()
|
|
254
|
+
await self.tools.reset_states()
|
|
255
|
+
new_tools = await self.tools.list_tools()
|
|
256
|
+
|
|
257
|
+
event = self.AgentReset(
|
|
258
|
+
agent_name=self.name,
|
|
259
|
+
previous_tools=old_tools,
|
|
260
|
+
new_tools=new_tools,
|
|
261
|
+
)
|
|
262
|
+
await self.agent_reset.emit(event)
|
|
263
|
+
|
|
152
264
|
@abstractmethod
|
|
153
265
|
def get_context(self, data: Any = None) -> AgentContext[Any]:
|
|
154
266
|
"""Create a new context for this agent.
|
|
@@ -176,17 +288,275 @@ class BaseAgent[TDeps = None, TResult = str](MessageNode[TDeps, TResult]):
|
|
|
176
288
|
"""
|
|
177
289
|
...
|
|
178
290
|
|
|
179
|
-
|
|
180
|
-
|
|
291
|
+
async def run_iter(
|
|
292
|
+
self,
|
|
293
|
+
*prompt_groups: Sequence[PromptCompatible],
|
|
294
|
+
store_history: bool = True,
|
|
295
|
+
wait_for_connections: bool | None = None,
|
|
296
|
+
) -> AsyncIterator[ChatMessage[TResult]]:
|
|
297
|
+
"""Run agent sequentially on multiple prompt groups.
|
|
298
|
+
|
|
299
|
+
Args:
|
|
300
|
+
prompt_groups: Groups of prompts to process sequentially
|
|
301
|
+
store_history: Whether to store in conversation history
|
|
302
|
+
wait_for_connections: Whether to wait for connected agents
|
|
303
|
+
|
|
304
|
+
Yields:
|
|
305
|
+
Response messages in sequence
|
|
306
|
+
|
|
307
|
+
Example:
|
|
308
|
+
questions = [
|
|
309
|
+
["What is your name?"],
|
|
310
|
+
["How old are you?", image1],
|
|
311
|
+
["Describe this image", image2],
|
|
312
|
+
]
|
|
313
|
+
async for response in agent.run_iter(*questions):
|
|
314
|
+
print(response.content)
|
|
315
|
+
"""
|
|
316
|
+
for prompts in prompt_groups:
|
|
317
|
+
response = await self.run(
|
|
318
|
+
*prompts,
|
|
319
|
+
store_history=store_history,
|
|
320
|
+
wait_for_connections=wait_for_connections,
|
|
321
|
+
)
|
|
322
|
+
yield response # pyright: ignore
|
|
323
|
+
|
|
324
|
+
async def run_in_background(
|
|
181
325
|
self,
|
|
182
|
-
*prompt:
|
|
326
|
+
*prompt: PromptCompatible,
|
|
327
|
+
max_count: int | None = None,
|
|
328
|
+
interval: float = 1.0,
|
|
183
329
|
**kwargs: Any,
|
|
330
|
+
) -> asyncio.Task[ChatMessage[TResult] | None]:
|
|
331
|
+
"""Run agent continuously in background with prompt or dynamic prompt function.
|
|
332
|
+
|
|
333
|
+
Args:
|
|
334
|
+
prompt: Static prompt or function that generates prompts
|
|
335
|
+
max_count: Maximum number of runs (None = infinite)
|
|
336
|
+
interval: Seconds between runs
|
|
337
|
+
**kwargs: Arguments passed to run()
|
|
338
|
+
"""
|
|
339
|
+
self._infinite = max_count is None
|
|
340
|
+
|
|
341
|
+
async def _continuous() -> ChatMessage[Any]:
|
|
342
|
+
count = 0
|
|
343
|
+
self.log.debug("Starting continuous run", max_count=max_count, interval=interval)
|
|
344
|
+
latest = None
|
|
345
|
+
while (max_count is None or count < max_count) and not self._cancelled:
|
|
346
|
+
try:
|
|
347
|
+
agent_ctx = self.get_context()
|
|
348
|
+
current_prompts = [
|
|
349
|
+
call_with_context(p, agent_ctx, **kwargs) if callable(p) else p
|
|
350
|
+
for p in prompt
|
|
351
|
+
]
|
|
352
|
+
self.log.debug("Generated prompt", iteration=count)
|
|
353
|
+
latest = await self.run(current_prompts, **kwargs)
|
|
354
|
+
self.log.debug("Run continuous result", iteration=count)
|
|
355
|
+
|
|
356
|
+
count += 1
|
|
357
|
+
await anyio.sleep(interval)
|
|
358
|
+
except asyncio.CancelledError:
|
|
359
|
+
self.log.debug("Continuous run cancelled")
|
|
360
|
+
break
|
|
361
|
+
except Exception:
|
|
362
|
+
# Check if we were cancelled (may surface as other exceptions)
|
|
363
|
+
if self._cancelled:
|
|
364
|
+
self.log.debug("Continuous run cancelled via flag")
|
|
365
|
+
break
|
|
366
|
+
count += 1
|
|
367
|
+
self.log.exception("Background run failed")
|
|
368
|
+
await anyio.sleep(interval)
|
|
369
|
+
self.log.debug("Continuous run completed", iterations=count)
|
|
370
|
+
return latest # type: ignore[return-value]
|
|
371
|
+
|
|
372
|
+
await self.stop() # Cancel any existing background task
|
|
373
|
+
self._cancelled = False # Reset cancellation flag for new run
|
|
374
|
+
task = asyncio.create_task(_continuous(), name=f"background_{self.name}")
|
|
375
|
+
self.log.debug("Started background task", task_name=task.get_name())
|
|
376
|
+
self._background_task = task
|
|
377
|
+
return task
|
|
378
|
+
|
|
379
|
+
async def stop(self) -> None:
|
|
380
|
+
"""Stop continuous execution if running."""
|
|
381
|
+
self._cancelled = True # Signal cancellation via flag
|
|
382
|
+
if self._background_task and not self._background_task.done():
|
|
383
|
+
self._background_task.cancel()
|
|
384
|
+
with suppress(asyncio.CancelledError): # Expected when we cancel the task
|
|
385
|
+
await self._background_task
|
|
386
|
+
self._background_task = None
|
|
387
|
+
|
|
388
|
+
def is_busy(self) -> bool:
|
|
389
|
+
"""Check if agent is currently processing tasks."""
|
|
390
|
+
return bool(self.task_manager._pending_tasks or self._background_task)
|
|
391
|
+
|
|
392
|
+
async def wait(self) -> ChatMessage[TResult]:
|
|
393
|
+
"""Wait for background execution to complete."""
|
|
394
|
+
if not self._background_task:
|
|
395
|
+
msg = "No background task running"
|
|
396
|
+
raise RuntimeError(msg)
|
|
397
|
+
if self._infinite:
|
|
398
|
+
msg = "Cannot wait on infinite execution"
|
|
399
|
+
raise RuntimeError(msg)
|
|
400
|
+
try:
|
|
401
|
+
return await self._background_task
|
|
402
|
+
finally:
|
|
403
|
+
self._background_task = None
|
|
404
|
+
|
|
405
|
+
@method_spawner
|
|
406
|
+
async def run_stream(
|
|
407
|
+
self,
|
|
408
|
+
*prompts: PromptCompatible,
|
|
409
|
+
store_history: bool = True,
|
|
410
|
+
message_id: str | None = None,
|
|
411
|
+
conversation_id: str | None = None,
|
|
412
|
+
parent_id: str | None = None,
|
|
413
|
+
message_history: MessageHistory | None = None,
|
|
414
|
+
input_provider: InputProvider | None = None,
|
|
415
|
+
wait_for_connections: bool | None = None,
|
|
416
|
+
deps: TDeps | None = None,
|
|
417
|
+
event_handlers: Sequence[IndividualEventHandler | BuiltinEventHandlerType] | None = None,
|
|
184
418
|
) -> AsyncIterator[RichAgentStreamEvent[TResult]]:
|
|
185
419
|
"""Run agent with streaming output.
|
|
186
420
|
|
|
421
|
+
This method delegates to _stream_events() which must be implemented by subclasses.
|
|
422
|
+
Handles prompt conversion from various formats to UserContent.
|
|
423
|
+
|
|
424
|
+
Args:
|
|
425
|
+
*prompts: Input prompts (various formats supported)
|
|
426
|
+
store_history: Whether to store in history
|
|
427
|
+
message_id: Optional message ID
|
|
428
|
+
conversation_id: Optional conversation ID
|
|
429
|
+
parent_id: Optional parent message ID
|
|
430
|
+
message_history: Optional message history
|
|
431
|
+
input_provider: Optional input provider
|
|
432
|
+
wait_for_connections: Whether to wait for connected agents
|
|
433
|
+
deps: Optional dependencies
|
|
434
|
+
event_handlers: Optional event handlers
|
|
435
|
+
|
|
436
|
+
Yields:
|
|
437
|
+
Stream events during execution
|
|
438
|
+
"""
|
|
439
|
+
from agentpool.messaging import ChatMessage
|
|
440
|
+
from agentpool.utils.identifiers import generate_session_id
|
|
441
|
+
|
|
442
|
+
# Convert prompts to standard UserContent format
|
|
443
|
+
converted_prompts = await convert_prompts(prompts)
|
|
444
|
+
# Get message history (either passed or agent's own)
|
|
445
|
+
conversation = message_history if message_history is not None else self.conversation
|
|
446
|
+
# Determine effective parent_id (from param or last message in history)
|
|
447
|
+
effective_parent_id = parent_id if parent_id else conversation.get_last_message_id()
|
|
448
|
+
# Initialize or adopt conversation_id
|
|
449
|
+
if self.conversation_id is None:
|
|
450
|
+
if conversation_id:
|
|
451
|
+
# Adopt conversation_id (from agent chain or external session like ACP)
|
|
452
|
+
self.conversation_id = conversation_id
|
|
453
|
+
else:
|
|
454
|
+
# Generate new conversation_id
|
|
455
|
+
self.conversation_id = generate_session_id()
|
|
456
|
+
# Always log conversation with initial prompt for title generation
|
|
457
|
+
# StorageManager handles idempotent behavior (skip if already logged)
|
|
458
|
+
# Use last prompt to avoid staged content (staged is prepended, user prompt is last)
|
|
459
|
+
user_prompts = [
|
|
460
|
+
str(p) for p in prompts if isinstance(p, str)
|
|
461
|
+
] # Filter to text prompts only
|
|
462
|
+
initial_prompt = user_prompts[-1] if user_prompts else None
|
|
463
|
+
await self.log_conversation(initial_prompt)
|
|
464
|
+
elif conversation_id and self.conversation_id != conversation_id:
|
|
465
|
+
# Adopt passed conversation_id (for routing chains)
|
|
466
|
+
self.conversation_id = conversation_id
|
|
467
|
+
|
|
468
|
+
user_msg = ChatMessage.user_prompt(
|
|
469
|
+
message=converted_prompts,
|
|
470
|
+
parent_id=effective_parent_id,
|
|
471
|
+
conversation_id=self.conversation_id,
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
# Stream events from implementation
|
|
475
|
+
final_message = None
|
|
476
|
+
self._current_stream_task = asyncio.current_task()
|
|
477
|
+
try:
|
|
478
|
+
async for event in self._stream_events(
|
|
479
|
+
converted_prompts,
|
|
480
|
+
user_msg=user_msg,
|
|
481
|
+
effective_parent_id=effective_parent_id,
|
|
482
|
+
store_history=store_history,
|
|
483
|
+
message_id=message_id,
|
|
484
|
+
conversation_id=conversation_id,
|
|
485
|
+
parent_id=parent_id,
|
|
486
|
+
message_history=message_history,
|
|
487
|
+
input_provider=input_provider,
|
|
488
|
+
wait_for_connections=wait_for_connections,
|
|
489
|
+
deps=deps,
|
|
490
|
+
event_handlers=event_handlers,
|
|
491
|
+
):
|
|
492
|
+
yield event
|
|
493
|
+
# Capture final message from StreamCompleteEvent
|
|
494
|
+
if isinstance(event, StreamCompleteEvent):
|
|
495
|
+
final_message = event.message
|
|
496
|
+
except Exception as e:
|
|
497
|
+
self.log.exception("Agent stream failed")
|
|
498
|
+
failed_event = BaseAgent.RunFailedEvent(
|
|
499
|
+
agent_name=self.name,
|
|
500
|
+
message="Agent stream failed",
|
|
501
|
+
exception=e,
|
|
502
|
+
)
|
|
503
|
+
await self.run_failed.emit(failed_event)
|
|
504
|
+
raise
|
|
505
|
+
finally:
|
|
506
|
+
self._current_stream_task = None
|
|
507
|
+
|
|
508
|
+
# Post-processing after stream completes
|
|
509
|
+
if final_message is not None:
|
|
510
|
+
# Emit signal (always - for event handlers)
|
|
511
|
+
await self.message_sent.emit(final_message)
|
|
512
|
+
# Conditional persistence based on store_history
|
|
513
|
+
# TODO: Verify store_history semantics across all use cases:
|
|
514
|
+
# - Should subagent tool calls set store_history=False?
|
|
515
|
+
# - Should forked/ephemeral runs always skip persistence?
|
|
516
|
+
# - Should signals still fire when store_history=False?
|
|
517
|
+
# Current behavior: store_history controls both DB logging AND conversation context
|
|
518
|
+
if store_history:
|
|
519
|
+
# Log to persistent storage and add to conversation context
|
|
520
|
+
await self.log_message(final_message)
|
|
521
|
+
conversation.add_chat_messages([user_msg, final_message])
|
|
522
|
+
# Route to connected agents (always - they decide what to do with it)
|
|
523
|
+
await self.connections.route_message(final_message, wait=wait_for_connections)
|
|
524
|
+
|
|
525
|
+
@abstractmethod
|
|
526
|
+
def _stream_events(
|
|
527
|
+
self,
|
|
528
|
+
prompts: list[UserContent],
|
|
529
|
+
*,
|
|
530
|
+
user_msg: Any, # ChatMessage but imported in run_stream
|
|
531
|
+
effective_parent_id: str | None,
|
|
532
|
+
message_id: str | None = None,
|
|
533
|
+
conversation_id: str | None = None,
|
|
534
|
+
parent_id: str | None = None,
|
|
535
|
+
input_provider: InputProvider | None = None,
|
|
536
|
+
message_history: MessageHistory | None = None,
|
|
537
|
+
deps: TDeps | None = None,
|
|
538
|
+
event_handlers: Sequence[IndividualEventHandler | BuiltinEventHandlerType] | None = None,
|
|
539
|
+
wait_for_connections: bool | None = None,
|
|
540
|
+
store_history: bool = True,
|
|
541
|
+
) -> AsyncIterator[RichAgentStreamEvent[TResult]]:
|
|
542
|
+
"""Agent-specific streaming implementation.
|
|
543
|
+
|
|
544
|
+
Subclasses must implement this to provide their streaming logic.
|
|
545
|
+
Prompts are pre-converted to UserContent format by run_stream().
|
|
546
|
+
|
|
187
547
|
Args:
|
|
188
|
-
|
|
189
|
-
|
|
548
|
+
prompts: Converted prompts in UserContent format
|
|
549
|
+
user_msg: Pre-created user ChatMessage (from base class)
|
|
550
|
+
effective_parent_id: Resolved parent message ID for threading
|
|
551
|
+
message_id: Optional message ID
|
|
552
|
+
conversation_id: Optional conversation ID
|
|
553
|
+
parent_id: Optional parent message ID
|
|
554
|
+
input_provider: Optional input provider
|
|
555
|
+
message_history: Optional message history
|
|
556
|
+
deps: Optional dependencies
|
|
557
|
+
event_handlers: Optional event handlers
|
|
558
|
+
wait_for_connections: Whether to wait for connected agents
|
|
559
|
+
store_history: Whether to store in history
|
|
190
560
|
|
|
191
561
|
Yields:
|
|
192
562
|
Stream events during execution
|
|
@@ -201,6 +571,25 @@ class BaseAgent[TDeps = None, TResult = str](MessageNode[TDeps, TResult]):
|
|
|
201
571
|
"""
|
|
202
572
|
self.tool_confirmation_mode = mode
|
|
203
573
|
|
|
574
|
+
def is_initializing(self) -> bool:
|
|
575
|
+
"""Check if agent is still initializing.
|
|
576
|
+
|
|
577
|
+
Returns:
|
|
578
|
+
True if deferred initialization is pending
|
|
579
|
+
"""
|
|
580
|
+
return self._connect_pending
|
|
581
|
+
|
|
582
|
+
async def ensure_initialized(self) -> None:
|
|
583
|
+
"""Wait for deferred initialization to complete.
|
|
584
|
+
|
|
585
|
+
Subclasses that use deferred init should:
|
|
586
|
+
1. Set `self._connect_pending = True` in `__aenter__`
|
|
587
|
+
2. Override this method to do actual connection work
|
|
588
|
+
3. Set `self._connect_pending = False` when done
|
|
589
|
+
|
|
590
|
+
The base implementation is a no-op for agents without deferred init.
|
|
591
|
+
"""
|
|
592
|
+
|
|
204
593
|
def is_cancelled(self) -> bool:
|
|
205
594
|
"""Check if the agent has been cancelled.
|
|
206
595
|
|
|
@@ -282,6 +671,70 @@ class BaseAgent[TDeps = None, TResult = str](MessageNode[TDeps, TResult]):
|
|
|
282
671
|
|
|
283
672
|
return result
|
|
284
673
|
|
|
674
|
+
@method_spawner
|
|
675
|
+
async def run(
|
|
676
|
+
self,
|
|
677
|
+
*prompts: PromptCompatible | ChatMessage[Any],
|
|
678
|
+
store_history: bool = True,
|
|
679
|
+
message_id: str | None = None,
|
|
680
|
+
conversation_id: str | None = None,
|
|
681
|
+
parent_id: str | None = None,
|
|
682
|
+
message_history: MessageHistory | None = None,
|
|
683
|
+
deps: TDeps | None = None,
|
|
684
|
+
input_provider: InputProvider | None = None,
|
|
685
|
+
event_handlers: Sequence[IndividualEventHandler | BuiltinEventHandlerType] | None = None,
|
|
686
|
+
wait_for_connections: bool | None = None,
|
|
687
|
+
) -> ChatMessage[TResult]:
|
|
688
|
+
"""Run agent with prompt and get response.
|
|
689
|
+
|
|
690
|
+
This is the standard synchronous run method shared by all agent types.
|
|
691
|
+
It collects all streaming events from run_stream() and returns the final message.
|
|
692
|
+
|
|
693
|
+
Args:
|
|
694
|
+
prompts: User query or instruction
|
|
695
|
+
store_history: Whether the message exchange should be added to the
|
|
696
|
+
context window
|
|
697
|
+
message_id: Optional message id for the returned message.
|
|
698
|
+
Automatically generated if not provided.
|
|
699
|
+
conversation_id: Optional conversation id for the returned message.
|
|
700
|
+
parent_id: Parent message id
|
|
701
|
+
message_history: Optional MessageHistory object to
|
|
702
|
+
use instead of agent's own conversation
|
|
703
|
+
deps: Optional dependencies for the agent
|
|
704
|
+
input_provider: Optional input provider for the agent
|
|
705
|
+
event_handlers: Optional event handlers for this run (overrides agent's handlers)
|
|
706
|
+
wait_for_connections: Whether to wait for connected agents to complete
|
|
707
|
+
|
|
708
|
+
Returns:
|
|
709
|
+
ChatMessage containing response and run information
|
|
710
|
+
|
|
711
|
+
Raises:
|
|
712
|
+
RuntimeError: If no final message received from stream
|
|
713
|
+
UnexpectedModelBehavior: If the model fails or behaves unexpectedly
|
|
714
|
+
"""
|
|
715
|
+
# Collect all events through run_stream
|
|
716
|
+
final_message: ChatMessage[TResult] | None = None
|
|
717
|
+
async for event in self.run_stream(
|
|
718
|
+
*prompts,
|
|
719
|
+
store_history=store_history,
|
|
720
|
+
message_id=message_id,
|
|
721
|
+
conversation_id=conversation_id,
|
|
722
|
+
parent_id=parent_id,
|
|
723
|
+
message_history=message_history,
|
|
724
|
+
deps=deps,
|
|
725
|
+
input_provider=input_provider,
|
|
726
|
+
event_handlers=event_handlers,
|
|
727
|
+
wait_for_connections=wait_for_connections,
|
|
728
|
+
):
|
|
729
|
+
if isinstance(event, StreamCompleteEvent):
|
|
730
|
+
final_message = event.message
|
|
731
|
+
|
|
732
|
+
if final_message is None:
|
|
733
|
+
msg = "No final message received from stream"
|
|
734
|
+
raise RuntimeError(msg)
|
|
735
|
+
|
|
736
|
+
return final_message
|
|
737
|
+
|
|
285
738
|
@abstractmethod
|
|
286
739
|
async def get_available_models(self) -> list[ModelInfo] | None:
|
|
287
740
|
"""Get available models for this agent.
|
|
@@ -298,18 +751,18 @@ class BaseAgent[TDeps = None, TResult = str](MessageNode[TDeps, TResult]):
|
|
|
298
751
|
...
|
|
299
752
|
|
|
300
753
|
@abstractmethod
|
|
301
|
-
def get_modes(self) -> list[ModeCategory]:
|
|
754
|
+
async def get_modes(self) -> list[ModeCategory]:
|
|
302
755
|
"""Get available mode categories for this agent.
|
|
303
756
|
|
|
304
757
|
Returns a list of mode categories that can be switched. Each category
|
|
305
758
|
represents a group of mutually exclusive modes (e.g., permissions,
|
|
306
|
-
behavior presets).
|
|
759
|
+
models, behavior presets).
|
|
307
760
|
|
|
308
761
|
Different agent types expose different modes:
|
|
309
|
-
- Native Agent:
|
|
310
|
-
- ClaudeCodeAgent:
|
|
762
|
+
- Native Agent: permissions + model selection
|
|
763
|
+
- ClaudeCodeAgent: permissions + model selection
|
|
311
764
|
- ACPAgent: Passthrough from remote server
|
|
312
|
-
- AGUIAgent:
|
|
765
|
+
- AGUIAgent: model selection (if applicable)
|
|
313
766
|
|
|
314
767
|
Returns:
|
|
315
768
|
List of ModeCategory, empty list if no modes supported
|