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_toolsets/__init__.py
CHANGED
|
@@ -3,13 +3,11 @@
|
|
|
3
3
|
from agentpool_toolsets.config_creation import ConfigCreationTools
|
|
4
4
|
from agentpool_toolsets.fsspec_toolset import FSSpecTools
|
|
5
5
|
from agentpool_toolsets.notifications import NotificationsTools
|
|
6
|
-
from agentpool_toolsets.semantic_memory_toolset import SemanticMemoryTools
|
|
7
6
|
from agentpool_toolsets.vfs_toolset import VFSTools
|
|
8
7
|
|
|
9
8
|
__all__ = [
|
|
10
9
|
"ConfigCreationTools",
|
|
11
10
|
"FSSpecTools",
|
|
12
11
|
"NotificationsTools",
|
|
13
|
-
"SemanticMemoryTools",
|
|
14
12
|
"VFSTools",
|
|
15
13
|
]
|
|
@@ -6,10 +6,9 @@ from __future__ import annotations
|
|
|
6
6
|
# Import provider classes
|
|
7
7
|
from agentpool_toolsets.builtin.code import CodeTools
|
|
8
8
|
from agentpool_toolsets.builtin.debug import DebugTools
|
|
9
|
-
from agentpool_toolsets.builtin.execution_environment import
|
|
9
|
+
from agentpool_toolsets.builtin.execution_environment import ProcessManagementTools
|
|
10
10
|
from agentpool_toolsets.builtin.skills import SkillsTools
|
|
11
11
|
from agentpool_toolsets.builtin.subagent_tools import SubagentTools
|
|
12
|
-
from agentpool_toolsets.builtin.user_interaction import UserInteractionTools
|
|
13
12
|
from agentpool_toolsets.builtin.workers import WorkersTools
|
|
14
13
|
|
|
15
14
|
|
|
@@ -17,9 +16,8 @@ __all__ = [
|
|
|
17
16
|
# Provider classes
|
|
18
17
|
"CodeTools",
|
|
19
18
|
"DebugTools",
|
|
20
|
-
"
|
|
19
|
+
"ProcessManagementTools",
|
|
21
20
|
"SkillsTools",
|
|
22
21
|
"SubagentTools",
|
|
23
|
-
"UserInteractionTools",
|
|
24
22
|
"WorkersTools",
|
|
25
23
|
]
|
|
@@ -21,13 +21,13 @@ from agentpool_toolsets.fsspec_toolset.diagnostics import (
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
if TYPE_CHECKING:
|
|
24
|
+
from collections.abc import Sequence
|
|
25
|
+
|
|
24
26
|
from exxec.base import ExecutionEnvironment
|
|
25
27
|
from fsspec.asyn import AsyncFileSystem
|
|
26
28
|
|
|
27
29
|
from agentpool.tools.base import Tool
|
|
28
|
-
from agentpool_toolsets.fsspec_toolset.diagnostics import
|
|
29
|
-
DiagnosticsConfig,
|
|
30
|
-
)
|
|
30
|
+
from agentpool_toolsets.fsspec_toolset.diagnostics import DiagnosticsConfig
|
|
31
31
|
|
|
32
32
|
logger = get_logger(__name__)
|
|
33
33
|
|
|
@@ -149,7 +149,7 @@ class CodeTools(ResourceProvider):
|
|
|
149
149
|
return str(Path(cwd) / path)
|
|
150
150
|
return path
|
|
151
151
|
|
|
152
|
-
async def get_tools(self) ->
|
|
152
|
+
async def get_tools(self) -> Sequence[Tool]:
|
|
153
153
|
"""Get code analysis tools."""
|
|
154
154
|
if self._tools is not None:
|
|
155
155
|
return self._tools
|
|
@@ -4,15 +4,23 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from collections import deque
|
|
6
6
|
from dataclasses import dataclass, field
|
|
7
|
+
from datetime import UTC, datetime
|
|
8
|
+
import json
|
|
7
9
|
import logging
|
|
8
|
-
from typing import Any, Literal
|
|
10
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
9
11
|
|
|
12
|
+
from fsspec.implementations.asyn_wrapper import AsyncFileSystemWrapper
|
|
13
|
+
from fsspec.implementations.memory import MemoryFileSystem
|
|
10
14
|
from pydantic_ai import RunContext # noqa: TC002
|
|
11
15
|
|
|
12
16
|
from agentpool.agents.context import AgentContext # noqa: TC001
|
|
13
17
|
from agentpool.resource_providers import StaticResourceProvider
|
|
14
18
|
|
|
15
19
|
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from fsspec.asyn import AsyncFileSystem
|
|
22
|
+
|
|
23
|
+
|
|
16
24
|
LogLevel = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
|
17
25
|
|
|
18
26
|
|
|
@@ -131,6 +139,8 @@ Available in namespace:
|
|
|
131
139
|
- ctx: AgentContext (ctx.agent, ctx.pool, ctx.config, ctx.definition, etc.)
|
|
132
140
|
- run_ctx: pydantic-ai RunContext (current run state)
|
|
133
141
|
- me: Shortcut for ctx.agent (your Agent instance)
|
|
142
|
+
- save(key, value): Save an object to persist between calls
|
|
143
|
+
- state: Dict of saved objects from previous calls
|
|
134
144
|
|
|
135
145
|
You can inspect yourself, the pool, other agents, your tools, and more.
|
|
136
146
|
Write an async main() function that returns the result.
|
|
@@ -159,43 +169,6 @@ async def main():
|
|
|
159
169
|
"""
|
|
160
170
|
|
|
161
171
|
|
|
162
|
-
async def execute_introspection(ctx: AgentContext, run_ctx: RunContext[Any], code: str) -> str: # noqa: D417
|
|
163
|
-
"""Execute Python code with access to your own runtime context.
|
|
164
|
-
|
|
165
|
-
This is a debugging/development tool that gives you full access to
|
|
166
|
-
inspect and interact with your runtime environment.
|
|
167
|
-
|
|
168
|
-
Args:
|
|
169
|
-
code: Python code with async main() function to execute
|
|
170
|
-
|
|
171
|
-
Returns:
|
|
172
|
-
Result of execution or error message
|
|
173
|
-
"""
|
|
174
|
-
# Emit progress with the code being executed
|
|
175
|
-
await ctx.events.tool_call_progress(
|
|
176
|
-
title="Executing introspection code",
|
|
177
|
-
status="in_progress",
|
|
178
|
-
items=[f"```python\n{code}\n```"],
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
# Build namespace with runtime context
|
|
182
|
-
namespace: dict[str, Any] = {"ctx": ctx, "run_ctx": run_ctx, "me": ctx.agent}
|
|
183
|
-
try:
|
|
184
|
-
exec(code, namespace)
|
|
185
|
-
if "main" not in namespace:
|
|
186
|
-
return "Error: Code must define an async main() function"
|
|
187
|
-
result = await namespace["main"]()
|
|
188
|
-
await ctx.events.tool_call_progress(
|
|
189
|
-
title="Executed introspection code successfully",
|
|
190
|
-
status="in_progress",
|
|
191
|
-
items=[f"```python\n{code}\n```\n\n```terminal\n{result}\n```"],
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
return str(result) if result is not None else "Code executed successfully (no return value)"
|
|
195
|
-
except Exception as e: # noqa: BLE001
|
|
196
|
-
return f"Error executing code: {type(e).__name__}: {e}"
|
|
197
|
-
|
|
198
|
-
|
|
199
172
|
# =============================================================================
|
|
200
173
|
# Log Tools
|
|
201
174
|
# =============================================================================
|
|
@@ -269,6 +242,7 @@ class DebugTools(StaticResourceProvider):
|
|
|
269
242
|
- Self-introspection via code execution with runtime context access
|
|
270
243
|
- Log inspection and management
|
|
271
244
|
- Platform path discovery
|
|
245
|
+
- Stateful namespace for persisting objects between introspection calls
|
|
272
246
|
"""
|
|
273
247
|
|
|
274
248
|
def __init__(self, name: str = "debug") -> None:
|
|
@@ -278,9 +252,110 @@ class DebugTools(StaticResourceProvider):
|
|
|
278
252
|
name: Toolset name/namespace
|
|
279
253
|
"""
|
|
280
254
|
super().__init__(name=name)
|
|
255
|
+
self._namespace_storage: dict[str, Any] = {} # Stateful storage for introspection
|
|
256
|
+
# Wrap MemoryFileSystem for async support
|
|
257
|
+
self._memory_fs = MemoryFileSystem()
|
|
258
|
+
self._fs = AsyncFileSystemWrapper(self._memory_fs)
|
|
281
259
|
|
|
282
|
-
desc = (execute_introspection.__doc__ or "") + "\n\n" + INTROSPECTION_USAGE
|
|
260
|
+
desc = (self.execute_introspection.__doc__ or "") + "\n\n" + INTROSPECTION_USAGE
|
|
283
261
|
self._tools = [
|
|
284
|
-
self.create_tool(
|
|
262
|
+
self.create_tool(
|
|
263
|
+
self.execute_introspection, category="other", description_override=desc
|
|
264
|
+
),
|
|
285
265
|
self.create_tool(get_platform_paths, category="other", read_only=True, idempotent=True),
|
|
286
266
|
]
|
|
267
|
+
|
|
268
|
+
def get_fs(self) -> AsyncFileSystem:
|
|
269
|
+
"""Get filesystem view of script history and state.
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
AsyncFileSystem containing:
|
|
273
|
+
- scripts/{timestamp}_{title}.py - Executed scripts
|
|
274
|
+
- scripts/{timestamp}_{title}.json - Execution metadata
|
|
275
|
+
"""
|
|
276
|
+
return self._fs
|
|
277
|
+
|
|
278
|
+
async def execute_introspection( # noqa: D417
|
|
279
|
+
self, ctx: AgentContext, run_ctx: RunContext[Any], code: str, title: str
|
|
280
|
+
) -> str:
|
|
281
|
+
"""Execute Python code with access to your own runtime context.
|
|
282
|
+
|
|
283
|
+
This is a debugging/development tool that gives you full access to
|
|
284
|
+
inspect and interact with your runtime environment.
|
|
285
|
+
|
|
286
|
+
Args:
|
|
287
|
+
code: Python code with async main() function to execute
|
|
288
|
+
title: Short descriptive title for this script (3-4 words)
|
|
289
|
+
|
|
290
|
+
Returns:
|
|
291
|
+
Result of execution or error message
|
|
292
|
+
"""
|
|
293
|
+
# Emit progress with the code being executed
|
|
294
|
+
await ctx.events.tool_call_progress(
|
|
295
|
+
title="Executing introspection code",
|
|
296
|
+
status="in_progress",
|
|
297
|
+
items=[f"```python\n{code}\n```"],
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
# Build namespace with runtime context and stateful storage
|
|
301
|
+
def save(key: str, value: Any) -> None:
|
|
302
|
+
"""Save a value to persist between introspection calls."""
|
|
303
|
+
self._namespace_storage[key] = value
|
|
304
|
+
|
|
305
|
+
state = self._namespace_storage.copy()
|
|
306
|
+
|
|
307
|
+
namespace: dict[str, Any] = {
|
|
308
|
+
"ctx": ctx,
|
|
309
|
+
"run_ctx": run_ctx,
|
|
310
|
+
"me": ctx.agent,
|
|
311
|
+
"save": save,
|
|
312
|
+
"state": state,
|
|
313
|
+
}
|
|
314
|
+
start_time = datetime.now(UTC)
|
|
315
|
+
exit_code = 0
|
|
316
|
+
error_msg = None
|
|
317
|
+
result_str = None
|
|
318
|
+
|
|
319
|
+
try:
|
|
320
|
+
exec(code, namespace)
|
|
321
|
+
if "main" not in namespace:
|
|
322
|
+
return "Error: Code must define an async main() function"
|
|
323
|
+
result = await namespace["main"]()
|
|
324
|
+
result_str = (
|
|
325
|
+
str(result)
|
|
326
|
+
if result is not None
|
|
327
|
+
else "Code executed successfully (no return value)"
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
await ctx.events.tool_call_progress(
|
|
331
|
+
title="Executed introspection code successfully",
|
|
332
|
+
status="in_progress",
|
|
333
|
+
items=[f"```python\n{code}\n```\n\n```terminal\n{result_str}\n```"],
|
|
334
|
+
)
|
|
335
|
+
except Exception as e: # noqa: BLE001
|
|
336
|
+
exit_code = 1
|
|
337
|
+
error_msg = f"{type(e).__name__}: {e}"
|
|
338
|
+
result_str = error_msg
|
|
339
|
+
finally:
|
|
340
|
+
# Save to filesystem
|
|
341
|
+
end_time = datetime.now(UTC)
|
|
342
|
+
duration = (end_time - start_time).total_seconds()
|
|
343
|
+
timestamp = start_time.strftime("%Y%m%d_%H%M%S")
|
|
344
|
+
|
|
345
|
+
# Write script file
|
|
346
|
+
script_path = f"scripts/{timestamp}_{title}.py"
|
|
347
|
+
self._memory_fs.pipe(script_path, code.encode("utf-8"))
|
|
348
|
+
|
|
349
|
+
# Write metadata file
|
|
350
|
+
metadata = {
|
|
351
|
+
"title": title,
|
|
352
|
+
"timestamp": start_time.isoformat(),
|
|
353
|
+
"exit_code": exit_code,
|
|
354
|
+
"duration": duration,
|
|
355
|
+
"result": result_str,
|
|
356
|
+
"error": error_msg,
|
|
357
|
+
}
|
|
358
|
+
metadata_path = f"scripts/{timestamp}_{title}.json"
|
|
359
|
+
self._memory_fs.pipe(metadata_path, json.dumps(metadata, indent=2).encode("utf-8"))
|
|
360
|
+
|
|
361
|
+
return result_str
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
"""Provider for
|
|
1
|
+
"""Provider for process management tools with event emission."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import re
|
|
5
6
|
from typing import TYPE_CHECKING
|
|
6
|
-
import uuid
|
|
7
|
-
|
|
8
|
-
from exxec.events import OutputEvent, ProcessCompletedEvent, ProcessErrorEvent, ProcessStartedEvent
|
|
9
7
|
|
|
10
8
|
from agentpool import log
|
|
11
9
|
from agentpool.agents.context import AgentContext # noqa: TC001
|
|
@@ -16,26 +14,29 @@ logger = log.get_logger(__name__)
|
|
|
16
14
|
|
|
17
15
|
|
|
18
16
|
if TYPE_CHECKING:
|
|
17
|
+
from collections.abc import Sequence
|
|
18
|
+
|
|
19
19
|
from exxec import ExecutionEnvironment
|
|
20
20
|
|
|
21
21
|
from agentpool.tools.base import Tool
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
class
|
|
25
|
-
"""Provider for
|
|
24
|
+
class ProcessManagementTools(ResourceProvider):
|
|
25
|
+
"""Provider for background process management tools.
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
Provides tools for starting, monitoring, and controlling background processes.
|
|
28
|
+
Uses any ExecutionEnvironment backend and emits events via AgentContext.
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
For code execution and bash commands, use the standalone tools:
|
|
31
|
+
- agentpool.tool_impls.bash.BashTool
|
|
32
|
+
- agentpool.tool_impls.execute_code.ExecuteCodeTool
|
|
32
33
|
"""
|
|
33
34
|
|
|
34
|
-
def __init__(self, env: ExecutionEnvironment | None = None, name: str = "
|
|
35
|
-
"""Initialize
|
|
35
|
+
def __init__(self, env: ExecutionEnvironment | None = None, name: str = "process") -> None:
|
|
36
|
+
"""Initialize process management toolset.
|
|
36
37
|
|
|
37
38
|
Args:
|
|
38
|
-
env: Execution environment to use (defaults to
|
|
39
|
+
env: Execution environment to use (defaults to agent.env)
|
|
39
40
|
name: The name of the toolset
|
|
40
41
|
"""
|
|
41
42
|
super().__init__(name=name)
|
|
@@ -51,12 +52,8 @@ class ExecutionEnvironmentTools(ResourceProvider):
|
|
|
51
52
|
return self._env
|
|
52
53
|
return agent_ctx.agent.env
|
|
53
54
|
|
|
54
|
-
async def get_tools(self) ->
|
|
55
|
+
async def get_tools(self) -> Sequence[Tool]:
|
|
55
56
|
return [
|
|
56
|
-
# Code execution tools
|
|
57
|
-
self.create_tool(self.execute_code, category="execute"),
|
|
58
|
-
self.create_tool(self.bash, category="execute", open_world=True),
|
|
59
|
-
# Process management tools
|
|
60
57
|
self.create_tool(self.start_process, category="execute", open_world=True),
|
|
61
58
|
self.create_tool(
|
|
62
59
|
self.get_process_output, category="execute", read_only=True, idempotent=True
|
|
@@ -71,151 +68,6 @@ class ExecutionEnvironmentTools(ResourceProvider):
|
|
|
71
68
|
),
|
|
72
69
|
]
|
|
73
70
|
|
|
74
|
-
async def execute_code(self, agent_ctx: AgentContext, code: str) -> str: # noqa: D417
|
|
75
|
-
"""Execute Python code and return the result.
|
|
76
|
-
|
|
77
|
-
Args:
|
|
78
|
-
code: Python code to execute
|
|
79
|
-
"""
|
|
80
|
-
process_id: str | None = None
|
|
81
|
-
output_parts: list[str] = []
|
|
82
|
-
exit_code: int | None = None
|
|
83
|
-
error_msg: str | None = None
|
|
84
|
-
try:
|
|
85
|
-
async for event in self.get_env(agent_ctx).stream_code(code):
|
|
86
|
-
match event:
|
|
87
|
-
case ProcessStartedEvent(process_id=pid, command=cmd):
|
|
88
|
-
process_id = pid # save for later on.
|
|
89
|
-
await agent_ctx.events.process_started(pid, cmd, success=True)
|
|
90
|
-
case OutputEvent(data=data):
|
|
91
|
-
output_parts.append(data)
|
|
92
|
-
if process_id:
|
|
93
|
-
await agent_ctx.events.process_output(process_id, data)
|
|
94
|
-
case ProcessCompletedEvent(exit_code=code_):
|
|
95
|
-
exit_code = code_
|
|
96
|
-
out = "".join(output_parts)
|
|
97
|
-
if process_id:
|
|
98
|
-
await agent_ctx.events.process_exit(
|
|
99
|
-
process_id, exit_code, final_output=out
|
|
100
|
-
)
|
|
101
|
-
case ProcessErrorEvent(error=err, exit_code=code_):
|
|
102
|
-
error_msg = err
|
|
103
|
-
exit_code = code_
|
|
104
|
-
if process_id:
|
|
105
|
-
await agent_ctx.events.process_exit(
|
|
106
|
-
process_id, exit_code or 1, final_output=err
|
|
107
|
-
)
|
|
108
|
-
|
|
109
|
-
combined_output = "".join(output_parts)
|
|
110
|
-
|
|
111
|
-
# Format as plain text for LLM
|
|
112
|
-
if error_msg:
|
|
113
|
-
return f"{combined_output}\n\nError: {error_msg}\nExit code: {exit_code}"
|
|
114
|
-
|
|
115
|
-
except Exception as e: # noqa: BLE001
|
|
116
|
-
error_id = process_id or f"code_{uuid.uuid4().hex[:8]}"
|
|
117
|
-
await agent_ctx.events.process_started(
|
|
118
|
-
error_id, "execute_code", success=False, error=str(e)
|
|
119
|
-
)
|
|
120
|
-
return f"Error executing code: {e}"
|
|
121
|
-
else:
|
|
122
|
-
# Return just output if success, add exit code only if non-zero
|
|
123
|
-
if exit_code and exit_code != 0:
|
|
124
|
-
return f"{combined_output}\n\nExit code: {exit_code}"
|
|
125
|
-
return combined_output
|
|
126
|
-
|
|
127
|
-
async def bash( # noqa: PLR0915, D417
|
|
128
|
-
self,
|
|
129
|
-
agent_ctx: AgentContext,
|
|
130
|
-
command: str,
|
|
131
|
-
output_limit: int | None = None,
|
|
132
|
-
) -> str:
|
|
133
|
-
"""Execute a shell command and return the output.
|
|
134
|
-
|
|
135
|
-
Args:
|
|
136
|
-
command: Shell command to execute
|
|
137
|
-
output_limit: Maximum bytes of output to return
|
|
138
|
-
"""
|
|
139
|
-
# process_id comes from exxec events (is terminal_id when using ACP)
|
|
140
|
-
process_id: str | None = None
|
|
141
|
-
stdout_parts: list[str] = []
|
|
142
|
-
stderr_parts: list[str] = []
|
|
143
|
-
exit_code: int | None = None
|
|
144
|
-
error_msg: str | None = None
|
|
145
|
-
try:
|
|
146
|
-
async for event in self.get_env(agent_ctx).stream_command(command):
|
|
147
|
-
match event:
|
|
148
|
-
case ProcessStartedEvent(process_id=pid, command=cmd):
|
|
149
|
-
process_id = pid
|
|
150
|
-
if pid:
|
|
151
|
-
await agent_ctx.events.process_started(pid, cmd, success=True)
|
|
152
|
-
else:
|
|
153
|
-
logger.warning("ProcessStartedEvent missing process_id", command=cmd)
|
|
154
|
-
case OutputEvent(process_id=pid, data=data, stream=stream):
|
|
155
|
-
if stream == "stderr":
|
|
156
|
-
stderr_parts.append(data)
|
|
157
|
-
else:
|
|
158
|
-
stdout_parts.append(data)
|
|
159
|
-
if pid:
|
|
160
|
-
await agent_ctx.events.process_output(pid, data)
|
|
161
|
-
else:
|
|
162
|
-
logger.warning("OutputEvent missing process_id", stream=stream)
|
|
163
|
-
case ProcessCompletedEvent(process_id=pid, exit_code=code_):
|
|
164
|
-
exit_code = code_
|
|
165
|
-
combined = "".join(stdout_parts) + "".join(stderr_parts)
|
|
166
|
-
if pid:
|
|
167
|
-
await agent_ctx.events.process_exit(
|
|
168
|
-
pid, exit_code, final_output=combined
|
|
169
|
-
)
|
|
170
|
-
else:
|
|
171
|
-
msg = "ProcessCompletedEvent missing process_id,"
|
|
172
|
-
logger.warning(msg, exit_code=code_)
|
|
173
|
-
case ProcessErrorEvent(process_id=pid, error=err, exit_code=code_):
|
|
174
|
-
error_msg = err
|
|
175
|
-
exit_code = code_
|
|
176
|
-
|
|
177
|
-
stdout = "".join(stdout_parts)
|
|
178
|
-
stderr = "".join(stderr_parts)
|
|
179
|
-
|
|
180
|
-
# Apply output limit if specified
|
|
181
|
-
truncated = False
|
|
182
|
-
if output_limit:
|
|
183
|
-
if len(stdout.encode()) > output_limit:
|
|
184
|
-
out = stdout.encode()[-output_limit:].decode(errors="ignore")
|
|
185
|
-
stdout = "...[truncated]\n" + out
|
|
186
|
-
truncated = True
|
|
187
|
-
if len(stderr.encode()) > output_limit:
|
|
188
|
-
out = stderr.encode()[-output_limit:].decode(errors="ignore")
|
|
189
|
-
stderr = "...[truncated]\n" + out
|
|
190
|
-
truncated = True
|
|
191
|
-
|
|
192
|
-
# Format as plain text for LLM
|
|
193
|
-
if error_msg:
|
|
194
|
-
output = stdout + stderr if stdout or stderr else ""
|
|
195
|
-
return f"{output}\n\nError: {error_msg}\nExit code: {exit_code}"
|
|
196
|
-
|
|
197
|
-
except Exception as e: # noqa: BLE001
|
|
198
|
-
# Use process_id from events if available, otherwise generate fallback
|
|
199
|
-
error_id = process_id or f"cmd_{uuid.uuid4().hex[:8]}"
|
|
200
|
-
await agent_ctx.events.process_started(error_id, command, success=False, error=str(e))
|
|
201
|
-
return f"Error executing command: {e}"
|
|
202
|
-
else:
|
|
203
|
-
# Combine stdout and stderr for output
|
|
204
|
-
output = stdout
|
|
205
|
-
if stderr:
|
|
206
|
-
output = f"{stdout}\n\nSTDERR:\n{stderr}" if stdout else f"STDERR:\n{stderr}"
|
|
207
|
-
|
|
208
|
-
# Add metadata only when relevant
|
|
209
|
-
suffix_parts = []
|
|
210
|
-
if truncated:
|
|
211
|
-
suffix_parts.append("[output truncated]")
|
|
212
|
-
if exit_code and exit_code != 0:
|
|
213
|
-
suffix_parts.append(f"Exit code: {exit_code}")
|
|
214
|
-
|
|
215
|
-
if suffix_parts:
|
|
216
|
-
return f"{output}\n\n{' | '.join(suffix_parts)}"
|
|
217
|
-
return output
|
|
218
|
-
|
|
219
71
|
async def start_process( # noqa: D417
|
|
220
72
|
self,
|
|
221
73
|
agent_ctx: AgentContext,
|
|
@@ -252,11 +104,18 @@ class ExecutionEnvironmentTools(ResourceProvider):
|
|
|
252
104
|
full_cmd = f"{command} {' '.join(args)}" if args else command
|
|
253
105
|
return f"Started background process {process_id}\nCommand: {full_cmd}"
|
|
254
106
|
|
|
255
|
-
async def get_process_output(
|
|
107
|
+
async def get_process_output( # noqa: D417
|
|
108
|
+
self,
|
|
109
|
+
agent_ctx: AgentContext,
|
|
110
|
+
process_id: str,
|
|
111
|
+
filter_lines: str | None = None,
|
|
112
|
+
) -> str:
|
|
256
113
|
"""Get current output from a background process.
|
|
257
114
|
|
|
258
115
|
Args:
|
|
259
116
|
process_id: Process identifier from start_process
|
|
117
|
+
filter_lines: Optional regex pattern to filter output lines
|
|
118
|
+
(only matching lines returned)
|
|
260
119
|
"""
|
|
261
120
|
manager = self.get_env(agent_ctx).process_manager
|
|
262
121
|
try:
|
|
@@ -264,6 +123,18 @@ class ExecutionEnvironmentTools(ResourceProvider):
|
|
|
264
123
|
await agent_ctx.events.process_output(process_id, output.combined or "")
|
|
265
124
|
|
|
266
125
|
combined = output.combined or ""
|
|
126
|
+
|
|
127
|
+
# Apply regex filter if specified
|
|
128
|
+
if filter_lines and combined:
|
|
129
|
+
try:
|
|
130
|
+
pattern = re.compile(filter_lines)
|
|
131
|
+
filtered_lines = [
|
|
132
|
+
line for line in combined.splitlines(keepends=True) if pattern.search(line)
|
|
133
|
+
]
|
|
134
|
+
combined = "".join(filtered_lines)
|
|
135
|
+
except re.error as regex_err:
|
|
136
|
+
return f"Invalid filter regex: {regex_err}"
|
|
137
|
+
|
|
267
138
|
status = "completed" if output.exit_code is not None else "running"
|
|
268
139
|
|
|
269
140
|
# Format as plain text
|
|
@@ -283,11 +154,18 @@ class ExecutionEnvironmentTools(ResourceProvider):
|
|
|
283
154
|
except Exception as e: # noqa: BLE001
|
|
284
155
|
return f"Error getting process output: {e}"
|
|
285
156
|
|
|
286
|
-
async def wait_for_process(
|
|
157
|
+
async def wait_for_process( # noqa: D417
|
|
158
|
+
self,
|
|
159
|
+
agent_ctx: AgentContext,
|
|
160
|
+
process_id: str,
|
|
161
|
+
filter_lines: str | None = None,
|
|
162
|
+
) -> str:
|
|
287
163
|
"""Wait for background process to complete and return final output.
|
|
288
164
|
|
|
289
165
|
Args:
|
|
290
166
|
process_id: Process identifier from start_process
|
|
167
|
+
filter_lines: Optional regex pattern to filter output lines
|
|
168
|
+
(only matching lines returned)
|
|
291
169
|
"""
|
|
292
170
|
manager = self.get_env(agent_ctx).process_manager
|
|
293
171
|
try:
|
|
@@ -301,6 +179,17 @@ class ExecutionEnvironmentTools(ResourceProvider):
|
|
|
301
179
|
else:
|
|
302
180
|
combined = output.combined or ""
|
|
303
181
|
|
|
182
|
+
# Apply regex filter if specified
|
|
183
|
+
if filter_lines and combined:
|
|
184
|
+
try:
|
|
185
|
+
pattern = re.compile(filter_lines)
|
|
186
|
+
filtered_lines = [
|
|
187
|
+
line for line in combined.splitlines(keepends=True) if pattern.search(line)
|
|
188
|
+
]
|
|
189
|
+
combined = "".join(filtered_lines)
|
|
190
|
+
except re.error as regex_err:
|
|
191
|
+
return f"Invalid filter regex: {regex_err}"
|
|
192
|
+
|
|
304
193
|
# Format as plain text
|
|
305
194
|
suffix_parts = []
|
|
306
195
|
if output.truncated:
|
|
@@ -59,77 +59,6 @@ async def list_skills(ctx: AgentContext) -> str:
|
|
|
59
59
|
return "\n".join(lines)
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
class _StringOutputWriter:
|
|
63
|
-
"""Output writer that captures output to a string buffer."""
|
|
64
|
-
|
|
65
|
-
def __init__(self) -> None:
|
|
66
|
-
from io import StringIO
|
|
67
|
-
|
|
68
|
-
self._buffer = StringIO()
|
|
69
|
-
|
|
70
|
-
async def print(self, message: str) -> None:
|
|
71
|
-
"""Write a message to the buffer."""
|
|
72
|
-
self._buffer.write(message)
|
|
73
|
-
self._buffer.write("\n")
|
|
74
|
-
|
|
75
|
-
def getvalue(self) -> str:
|
|
76
|
-
"""Get the captured output."""
|
|
77
|
-
return self._buffer.getvalue()
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
async def run_command(ctx: AgentContext, command: str) -> str: # noqa: D417
|
|
81
|
-
"""Execute an internal command.
|
|
82
|
-
|
|
83
|
-
This provides access to the agent's internal CLI for management operations.
|
|
84
|
-
|
|
85
|
-
IMPORTANT: Before using any command for the first time, call "help <command>" to learn
|
|
86
|
-
the correct syntax and available options. Commands have specific argument orders and
|
|
87
|
-
flags that must be followed exactly.
|
|
88
|
-
|
|
89
|
-
Discovery commands:
|
|
90
|
-
- "help" - list all available commands
|
|
91
|
-
- "help <command>" - get detailed usage for a specific command (ALWAYS do this first!)
|
|
92
|
-
|
|
93
|
-
Command categories:
|
|
94
|
-
- Agent/team management: create-agent, create-team, list-agents
|
|
95
|
-
- Tool management: list-tools, register-tool, enable-tool, disable-tool
|
|
96
|
-
- MCP servers: add-mcp-server, add-remote-mcp-server, list-mcp-servers
|
|
97
|
-
- Connections: connect, disconnect, connections
|
|
98
|
-
- Workers: add-worker, remove-worker, list-workers
|
|
99
|
-
|
|
100
|
-
Args:
|
|
101
|
-
command: The command to execute. Leading slash is optional.
|
|
102
|
-
|
|
103
|
-
Returns:
|
|
104
|
-
Command output or error message
|
|
105
|
-
"""
|
|
106
|
-
from slashed import CommandContext
|
|
107
|
-
|
|
108
|
-
if not ctx.agent.command_store:
|
|
109
|
-
return "No command store available"
|
|
110
|
-
|
|
111
|
-
# Remove leading slash if present (slashed expects command name without /)
|
|
112
|
-
cmd = command.lstrip("/")
|
|
113
|
-
|
|
114
|
-
# Create output capture
|
|
115
|
-
output = _StringOutputWriter()
|
|
116
|
-
|
|
117
|
-
# Create CommandContext with output capture and AgentContext as data
|
|
118
|
-
cmd_ctx = CommandContext(
|
|
119
|
-
output=output,
|
|
120
|
-
data=ctx,
|
|
121
|
-
command_store=ctx.agent.command_store,
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
try:
|
|
125
|
-
await ctx.agent.command_store.execute_command(cmd, cmd_ctx)
|
|
126
|
-
result = output.getvalue()
|
|
127
|
-
except Exception as e: # noqa: BLE001
|
|
128
|
-
return f"Command failed: {e}"
|
|
129
|
-
else:
|
|
130
|
-
return result if result else "Command executed successfully."
|
|
131
|
-
|
|
132
|
-
|
|
133
62
|
class SkillsTools(StaticResourceProvider):
|
|
134
63
|
"""Provider for skills and commands tools.
|
|
135
64
|
|
|
@@ -150,10 +79,4 @@ class SkillsTools(StaticResourceProvider):
|
|
|
150
79
|
self._tools = [
|
|
151
80
|
self.create_tool(load_skill, category="read", read_only=True, idempotent=True),
|
|
152
81
|
self.create_tool(list_skills, category="read", read_only=True, idempotent=True),
|
|
153
|
-
self.create_tool(
|
|
154
|
-
run_command,
|
|
155
|
-
category="other",
|
|
156
|
-
read_only=False,
|
|
157
|
-
idempotent=False,
|
|
158
|
-
),
|
|
159
82
|
]
|