python-codex 0.1.1__py3-none-any.whl → 0.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pycodex/__init__.py +5 -1
- pycodex/agent.py +39 -41
- pycodex/cli.py +51 -43
- pycodex/collaboration.py +6 -7
- pycodex/compat.py +99 -0
- pycodex/context.py +87 -87
- pycodex/doctor.py +40 -40
- pycodex/model.py +69 -69
- pycodex/portable.py +33 -33
- pycodex/portable_server.py +22 -21
- pycodex/protocol.py +84 -86
- pycodex/runtime.py +36 -35
- pycodex/runtime_services.py +72 -69
- pycodex/tools/agent_tool_schemas.py +0 -2
- pycodex/tools/apply_patch_tool.py +43 -44
- pycodex/tools/base_tool.py +35 -36
- pycodex/tools/close_agent_tool.py +2 -4
- pycodex/tools/code_mode_manager.py +61 -61
- pycodex/tools/exec_command_tool.py +5 -6
- pycodex/tools/exec_runtime.js +3 -3
- pycodex/tools/exec_tool.py +3 -5
- pycodex/tools/grep_files_tool.py +10 -11
- pycodex/tools/list_dir_tool.py +8 -9
- pycodex/tools/read_file_tool.py +13 -14
- pycodex/tools/request_permissions_tool.py +2 -4
- pycodex/tools/request_user_input_tool.py +13 -14
- pycodex/tools/resume_agent_tool.py +2 -4
- pycodex/tools/send_input_tool.py +8 -9
- pycodex/tools/shell_command_tool.py +5 -6
- pycodex/tools/shell_tool.py +5 -6
- pycodex/tools/spawn_agent_tool.py +4 -5
- pycodex/tools/unified_exec_manager.py +79 -61
- pycodex/tools/update_plan_tool.py +4 -5
- pycodex/tools/view_image_tool.py +4 -5
- pycodex/tools/wait_agent_tool.py +2 -4
- pycodex/tools/wait_tool.py +4 -5
- pycodex/tools/web_search_tool.py +1 -3
- pycodex/tools/write_stdin_tool.py +4 -5
- pycodex/utils/dotenv.py +6 -6
- pycodex/utils/get_env.py +57 -34
- pycodex/utils/random_ids.py +1 -2
- pycodex/utils/visualize.py +79 -79
- {python_codex-0.1.1.dist-info → python_codex-0.1.3.dist-info}/METADATA +15 -9
- python_codex-0.1.3.dist-info/RECORD +74 -0
- {python_codex-0.1.1.dist-info → python_codex-0.1.3.dist-info}/WHEEL +1 -1
- responses_server/__init__.py +17 -0
- responses_server/__main__.py +5 -0
- responses_server/app.py +227 -0
- responses_server/config.py +63 -0
- responses_server/payload_processors.py +86 -0
- responses_server/server.py +63 -0
- responses_server/session_store.py +37 -0
- responses_server/stream_router.py +784 -0
- responses_server/tools/__init__.py +4 -0
- responses_server/tools/custom_adapter.py +235 -0
- responses_server/tools/web_search.py +263 -0
- python_codex-0.1.1.dist-info/RECORD +0 -62
- {python_codex-0.1.1.dist-info → python_codex-0.1.3.dist-info}/entry_points.txt +0 -0
- {python_codex-0.1.1.dist-info → python_codex-0.1.3.dist-info}/licenses/LICENSE +0 -0
pycodex/tools/read_file_tool.py
CHANGED
|
@@ -9,13 +9,12 @@ Expected behavior:
|
|
|
9
9
|
mode used to inspect code structure without shelling out to `sed` or `cat`.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
from __future__ import annotations
|
|
13
|
-
|
|
14
12
|
from collections import deque
|
|
15
13
|
from pathlib import Path
|
|
16
14
|
|
|
17
15
|
from ..protocol import JSONDict, JSONValue
|
|
18
16
|
from .base_tool import BaseTool, ToolContext
|
|
17
|
+
import typing
|
|
19
18
|
|
|
20
19
|
MAX_LINE_LENGTH = 500
|
|
21
20
|
TAB_WIDTH = 4
|
|
@@ -49,7 +48,7 @@ class ReadFileTool(BaseTool):
|
|
|
49
48
|
"required": ["file_path"],
|
|
50
49
|
}
|
|
51
50
|
|
|
52
|
-
async def run(self, context: ToolContext, args: JSONDict) -> JSONValue:
|
|
51
|
+
async def run(self, context: 'ToolContext', args: 'JSONDict') -> 'JSONValue':
|
|
53
52
|
del context
|
|
54
53
|
file_path = Path(str(args.get("file_path", "")))
|
|
55
54
|
offset = int(args.get("offset", 1))
|
|
@@ -72,7 +71,7 @@ class ReadFileTool(BaseTool):
|
|
|
72
71
|
return self._read_indentation(file_path, offset, limit, indentation)
|
|
73
72
|
return self._read_slice(file_path, offset, limit)
|
|
74
73
|
|
|
75
|
-
def _read_slice(self, file_path: Path, offset: int, limit: int) -> str:
|
|
74
|
+
def _read_slice(self, file_path: 'Path', offset: 'int', limit: 'int') -> 'str':
|
|
76
75
|
lines = file_path.read_text(errors="replace").splitlines()
|
|
77
76
|
if offset > len(lines):
|
|
78
77
|
return "Error: `offset` exceeds file length."
|
|
@@ -85,11 +84,11 @@ class ReadFileTool(BaseTool):
|
|
|
85
84
|
|
|
86
85
|
def _read_indentation(
|
|
87
86
|
self,
|
|
88
|
-
file_path: Path,
|
|
89
|
-
offset: int,
|
|
90
|
-
limit: int,
|
|
91
|
-
indentation: JSONDict,
|
|
92
|
-
) -> str:
|
|
87
|
+
file_path: 'Path',
|
|
88
|
+
offset: 'int',
|
|
89
|
+
limit: 'int',
|
|
90
|
+
indentation: 'JSONDict',
|
|
91
|
+
) -> 'str':
|
|
93
92
|
lines = self._collect_line_records(file_path)
|
|
94
93
|
anchor_line = int(indentation.get("anchor_line", offset))
|
|
95
94
|
max_levels = int(indentation.get("max_levels", 0))
|
|
@@ -171,7 +170,7 @@ class ReadFileTool(BaseTool):
|
|
|
171
170
|
f"L{record['number']}: {record['display']}" for record in selected
|
|
172
171
|
)
|
|
173
172
|
|
|
174
|
-
def _collect_line_records(self, file_path: Path) ->
|
|
173
|
+
def _collect_line_records(self, file_path: 'Path') -> 'typing.List[typing.Dict[str, object]]':
|
|
175
174
|
records = []
|
|
176
175
|
for number, raw in enumerate(file_path.read_text(errors="replace").splitlines(), start=1):
|
|
177
176
|
records.append(
|
|
@@ -185,7 +184,7 @@ class ReadFileTool(BaseTool):
|
|
|
185
184
|
)
|
|
186
185
|
return records
|
|
187
186
|
|
|
188
|
-
def _compute_effective_indents(self, records:
|
|
187
|
+
def _compute_effective_indents(self, records: 'typing.List[typing.Dict[str, object]]') -> 'typing.List[int]':
|
|
189
188
|
effective = []
|
|
190
189
|
previous_indent = 0
|
|
191
190
|
for record in records:
|
|
@@ -196,7 +195,7 @@ class ReadFileTool(BaseTool):
|
|
|
196
195
|
effective.append(previous_indent)
|
|
197
196
|
return effective
|
|
198
197
|
|
|
199
|
-
def _measure_indent(self, line: str) -> int:
|
|
198
|
+
def _measure_indent(self, line: 'str') -> 'int':
|
|
200
199
|
total = 0
|
|
201
200
|
for character in line:
|
|
202
201
|
if character == " ":
|
|
@@ -207,10 +206,10 @@ class ReadFileTool(BaseTool):
|
|
|
207
206
|
break
|
|
208
207
|
return total
|
|
209
208
|
|
|
210
|
-
def _format_line(self, text: str) -> str:
|
|
209
|
+
def _format_line(self, text: 'str') -> 'str':
|
|
211
210
|
return text[:MAX_LINE_LENGTH]
|
|
212
211
|
|
|
213
|
-
def _trim_empty_lines(self, records: deque[
|
|
212
|
+
def _trim_empty_lines(self, records: 'deque[typing.Dict[str, object]]') -> 'None':
|
|
214
213
|
while records and not str(records[0]["raw"]).strip():
|
|
215
214
|
records.popleft()
|
|
216
215
|
while records and not str(records[-1]["raw"]).strip():
|
|
@@ -9,8 +9,6 @@ Expected behavior:
|
|
|
9
9
|
- Return the granted permission profile and scope so later tool calls can use it.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
from __future__ import annotations
|
|
13
|
-
|
|
14
12
|
from ..protocol import JSONDict, JSONValue
|
|
15
13
|
from ..runtime_services import RequestPermissionsManager
|
|
16
14
|
from .base_tool import BaseTool, ToolContext
|
|
@@ -73,10 +71,10 @@ class RequestPermissionsTool(BaseTool):
|
|
|
73
71
|
}
|
|
74
72
|
supports_parallel = False
|
|
75
73
|
|
|
76
|
-
def __init__(self, request_manager: RequestPermissionsManager) -> None:
|
|
74
|
+
def __init__(self, request_manager: 'RequestPermissionsManager') -> 'None':
|
|
77
75
|
self._request_manager = request_manager
|
|
78
76
|
|
|
79
|
-
async def run(self, context: ToolContext, args: JSONDict) -> JSONValue:
|
|
77
|
+
async def run(self, context: 'ToolContext', args: 'JSONDict') -> 'JSONValue':
|
|
80
78
|
del context
|
|
81
79
|
permissions = args.get("permissions")
|
|
82
80
|
if not isinstance(permissions, dict):
|
|
@@ -12,14 +12,13 @@ Expected behavior:
|
|
|
12
12
|
`success=true`.
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
from __future__ import annotations
|
|
16
|
-
|
|
17
15
|
import json
|
|
18
16
|
|
|
19
17
|
from ..collaboration import collaboration_mode_display_name
|
|
20
18
|
from ..protocol import JSONDict, JSONValue
|
|
21
19
|
from ..runtime_services import RequestUserInputManager
|
|
22
20
|
from .base_tool import BaseTool, StructuredToolOutput, ToolContext
|
|
21
|
+
import typing
|
|
23
22
|
|
|
24
23
|
REQUEST_USER_INPUT_QUESTION_SCHEMA = {
|
|
25
24
|
"type": "object",
|
|
@@ -67,9 +66,9 @@ REQUEST_USER_INPUT_QUESTION_SCHEMA = {
|
|
|
67
66
|
|
|
68
67
|
|
|
69
68
|
def request_user_input_is_available(
|
|
70
|
-
mode: str,
|
|
71
|
-
default_mode_request_user_input: bool = False,
|
|
72
|
-
) -> bool:
|
|
69
|
+
mode: 'str',
|
|
70
|
+
default_mode_request_user_input: 'bool' = False,
|
|
71
|
+
) -> 'bool':
|
|
73
72
|
normalized = mode.strip().lower()
|
|
74
73
|
return normalized == "plan" or (
|
|
75
74
|
default_mode_request_user_input and normalized == "default"
|
|
@@ -77,9 +76,9 @@ def request_user_input_is_available(
|
|
|
77
76
|
|
|
78
77
|
|
|
79
78
|
def request_user_input_unavailable_message(
|
|
80
|
-
mode: str,
|
|
81
|
-
default_mode_request_user_input: bool = False,
|
|
82
|
-
) -> str
|
|
79
|
+
mode: 'str',
|
|
80
|
+
default_mode_request_user_input: 'bool' = False,
|
|
81
|
+
) -> 'typing.Union[str, None]':
|
|
83
82
|
if request_user_input_is_available(mode, default_mode_request_user_input):
|
|
84
83
|
return None
|
|
85
84
|
return (
|
|
@@ -89,8 +88,8 @@ def request_user_input_unavailable_message(
|
|
|
89
88
|
|
|
90
89
|
|
|
91
90
|
def request_user_input_tool_description(
|
|
92
|
-
default_mode_request_user_input: bool = False,
|
|
93
|
-
) -> str:
|
|
91
|
+
default_mode_request_user_input: 'bool' = False,
|
|
92
|
+
) -> 'str':
|
|
94
93
|
if default_mode_request_user_input:
|
|
95
94
|
allowed_modes = "Default or Plan mode"
|
|
96
95
|
else:
|
|
@@ -120,16 +119,16 @@ class RequestUserInputTool(BaseTool):
|
|
|
120
119
|
|
|
121
120
|
def __init__(
|
|
122
121
|
self,
|
|
123
|
-
request_manager: RequestUserInputManager,
|
|
124
|
-
default_mode_request_user_input: bool = False,
|
|
125
|
-
) -> None:
|
|
122
|
+
request_manager: 'RequestUserInputManager',
|
|
123
|
+
default_mode_request_user_input: 'bool' = False,
|
|
124
|
+
) -> 'None':
|
|
126
125
|
self._request_manager = request_manager
|
|
127
126
|
self._default_mode_request_user_input = default_mode_request_user_input
|
|
128
127
|
self.description = request_user_input_tool_description(
|
|
129
128
|
default_mode_request_user_input
|
|
130
129
|
)
|
|
131
130
|
|
|
132
|
-
async def run(self, context: ToolContext, args: JSONDict) -> JSONValue:
|
|
131
|
+
async def run(self, context: 'ToolContext', args: 'JSONDict') -> 'JSONValue':
|
|
133
132
|
unavailable = request_user_input_unavailable_message(
|
|
134
133
|
context.collaboration_mode,
|
|
135
134
|
self._default_mode_request_user_input,
|
|
@@ -8,8 +8,6 @@ Expected behavior:
|
|
|
8
8
|
- Return the agent's current status payload.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
-
from __future__ import annotations
|
|
12
|
-
|
|
13
11
|
from ..protocol import JSONDict, JSONValue
|
|
14
12
|
from ..runtime_services import SubAgentManager
|
|
15
13
|
from .agent_tool_schemas import AGENT_STATUS_SCHEMA
|
|
@@ -45,10 +43,10 @@ class ResumeAgentTool(BaseTool):
|
|
|
45
43
|
output_schema = RESUME_AGENT_OUTPUT_SCHEMA
|
|
46
44
|
supports_parallel = False
|
|
47
45
|
|
|
48
|
-
def __init__(self, subagent_manager: SubAgentManager) -> None:
|
|
46
|
+
def __init__(self, subagent_manager: 'SubAgentManager') -> 'None':
|
|
49
47
|
self._subagent_manager = subagent_manager
|
|
50
48
|
|
|
51
|
-
async def run(self, context: ToolContext, args: JSONDict) -> JSONValue:
|
|
49
|
+
async def run(self, context: 'ToolContext', args: 'JSONDict') -> 'JSONValue':
|
|
52
50
|
del context
|
|
53
51
|
agent_id = str(args.get("id", "")).strip()
|
|
54
52
|
if not agent_id:
|
pycodex/tools/send_input_tool.py
CHANGED
|
@@ -9,12 +9,11 @@ Expected behavior:
|
|
|
9
9
|
- Return the submission id for the queued input.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
from __future__ import annotations
|
|
13
|
-
|
|
14
12
|
from ..protocol import JSONDict, JSONValue
|
|
15
13
|
from ..runtime_services import SubAgentManager
|
|
16
14
|
from .agent_tool_schemas import COLLAB_INPUT_ITEMS_SCHEMA
|
|
17
15
|
from .base_tool import BaseTool, ToolContext
|
|
16
|
+
import typing
|
|
18
17
|
|
|
19
18
|
SEND_INPUT_OUTPUT_SCHEMA = {
|
|
20
19
|
"type": "object",
|
|
@@ -58,10 +57,10 @@ class SendInputTool(BaseTool):
|
|
|
58
57
|
output_schema = SEND_INPUT_OUTPUT_SCHEMA
|
|
59
58
|
supports_parallel = False
|
|
60
59
|
|
|
61
|
-
def __init__(self, subagent_manager: SubAgentManager) -> None:
|
|
60
|
+
def __init__(self, subagent_manager: 'SubAgentManager') -> 'None':
|
|
62
61
|
self._subagent_manager = subagent_manager
|
|
63
62
|
|
|
64
|
-
async def run(self, context: ToolContext, args: JSONDict) -> JSONValue:
|
|
63
|
+
async def run(self, context: 'ToolContext', args: 'JSONDict') -> 'JSONValue':
|
|
65
64
|
del context
|
|
66
65
|
agent_id = str(args.get("id", "")).strip()
|
|
67
66
|
if not agent_id:
|
|
@@ -83,10 +82,10 @@ class SendInputTool(BaseTool):
|
|
|
83
82
|
|
|
84
83
|
def _compose_prompt(
|
|
85
84
|
self,
|
|
86
|
-
message: str
|
|
87
|
-
items:
|
|
88
|
-
) -> str:
|
|
89
|
-
parts:
|
|
85
|
+
message: 'typing.Union[str, None]',
|
|
86
|
+
items: 'typing.Union[typing.List[typing.Dict[str, object]], None]',
|
|
87
|
+
) -> 'str':
|
|
88
|
+
parts: 'typing.List[str]' = []
|
|
90
89
|
if message:
|
|
91
90
|
parts.append(message.strip())
|
|
92
91
|
for item in items or []:
|
|
@@ -99,7 +98,7 @@ class SendInputTool(BaseTool):
|
|
|
99
98
|
parts.append(str(item))
|
|
100
99
|
return "\n\n".join(part for part in parts if part)
|
|
101
100
|
|
|
102
|
-
def _optional_string(self, args: JSONDict, key: str) -> str
|
|
101
|
+
def _optional_string(self, args: 'JSONDict', key: 'str') -> 'typing.Union[str, None]':
|
|
103
102
|
value = args.get(key)
|
|
104
103
|
if value in (None, ""):
|
|
105
104
|
return None
|
|
@@ -11,13 +11,12 @@ Expected behavior:
|
|
|
11
11
|
stdout, and stderr.
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
from __future__ import annotations
|
|
15
|
-
|
|
16
14
|
import asyncio
|
|
17
15
|
from pathlib import Path
|
|
18
16
|
|
|
19
17
|
from ..protocol import JSONDict, JSONValue
|
|
20
18
|
from .base_tool import BaseTool, ToolContext
|
|
19
|
+
import typing
|
|
21
20
|
|
|
22
21
|
DEFAULT_SHELL_TIMEOUT_MS = 30_000
|
|
23
22
|
MAX_OUTPUT_CHARS = 12_000
|
|
@@ -38,10 +37,10 @@ class ShellCommandTool(BaseTool):
|
|
|
38
37
|
}
|
|
39
38
|
supports_parallel = False
|
|
40
39
|
|
|
41
|
-
def __init__(self, cwd: str
|
|
40
|
+
def __init__(self, cwd: 'typing.Union[typing.Union[str, Path], None]' = None) -> 'None':
|
|
42
41
|
self._working_directory = Path(cwd or Path.cwd()).resolve()
|
|
43
42
|
|
|
44
|
-
async def run(self, context: ToolContext, args: JSONDict) -> JSONValue:
|
|
43
|
+
async def run(self, context: 'ToolContext', args: 'JSONDict') -> 'JSONValue':
|
|
45
44
|
del context
|
|
46
45
|
command = str(args.get("command", "")).strip()
|
|
47
46
|
timeout_ms = int(args.get("timeout_ms", DEFAULT_SHELL_TIMEOUT_MS))
|
|
@@ -93,7 +92,7 @@ class ShellCommandTool(BaseTool):
|
|
|
93
92
|
|
|
94
93
|
return "\n".join(pieces)
|
|
95
94
|
|
|
96
|
-
def _resolve_workdir(self, workdir_arg) -> Path:
|
|
95
|
+
def _resolve_workdir(self, workdir_arg) -> 'Path':
|
|
97
96
|
if workdir_arg in (None, ""):
|
|
98
97
|
return self._working_directory
|
|
99
98
|
workdir = Path(str(workdir_arg))
|
|
@@ -101,7 +100,7 @@ class ShellCommandTool(BaseTool):
|
|
|
101
100
|
workdir = self._working_directory / workdir
|
|
102
101
|
return workdir.resolve()
|
|
103
102
|
|
|
104
|
-
def _clip_output(self, text: str) -> str:
|
|
103
|
+
def _clip_output(self, text: 'str') -> 'str':
|
|
105
104
|
if len(text) <= MAX_OUTPUT_CHARS:
|
|
106
105
|
return text
|
|
107
106
|
return text[:MAX_OUTPUT_CHARS] + "\n...[truncated]..."
|
pycodex/tools/shell_tool.py
CHANGED
|
@@ -11,13 +11,12 @@ Expected behavior:
|
|
|
11
11
|
stdout, and stderr.
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
from __future__ import annotations
|
|
15
|
-
|
|
16
14
|
import asyncio
|
|
17
15
|
from pathlib import Path
|
|
18
16
|
|
|
19
17
|
from ..protocol import JSONDict, JSONValue
|
|
20
18
|
from .base_tool import BaseTool, ToolContext
|
|
19
|
+
import typing
|
|
21
20
|
|
|
22
21
|
DEFAULT_SHELL_TIMEOUT_MS = 30_000
|
|
23
22
|
MAX_OUTPUT_CHARS = 12_000
|
|
@@ -43,10 +42,10 @@ class ShellTool(BaseTool):
|
|
|
43
42
|
}
|
|
44
43
|
supports_parallel = False
|
|
45
44
|
|
|
46
|
-
def __init__(self, cwd: str
|
|
45
|
+
def __init__(self, cwd: 'typing.Union[typing.Union[str, Path], None]' = None) -> 'None':
|
|
47
46
|
self._working_directory = Path(cwd or Path.cwd()).resolve()
|
|
48
47
|
|
|
49
|
-
async def run(self, context: ToolContext, args: JSONDict) -> JSONValue:
|
|
48
|
+
async def run(self, context: 'ToolContext', args: 'JSONDict') -> 'JSONValue':
|
|
50
49
|
del context
|
|
51
50
|
command = args.get("command")
|
|
52
51
|
timeout_ms = int(args.get("timeout_ms", DEFAULT_SHELL_TIMEOUT_MS))
|
|
@@ -98,7 +97,7 @@ class ShellTool(BaseTool):
|
|
|
98
97
|
|
|
99
98
|
return "\n".join(pieces)
|
|
100
99
|
|
|
101
|
-
def _resolve_workdir(self, workdir_arg) -> Path:
|
|
100
|
+
def _resolve_workdir(self, workdir_arg) -> 'Path':
|
|
102
101
|
if workdir_arg in (None, ""):
|
|
103
102
|
return self._working_directory
|
|
104
103
|
workdir = Path(str(workdir_arg))
|
|
@@ -106,7 +105,7 @@ class ShellTool(BaseTool):
|
|
|
106
105
|
workdir = self._working_directory / workdir
|
|
107
106
|
return workdir.resolve()
|
|
108
107
|
|
|
109
|
-
def _clip_output(self, text: str) -> str:
|
|
108
|
+
def _clip_output(self, text: 'str') -> 'str':
|
|
110
109
|
if len(text) <= MAX_OUTPUT_CHARS:
|
|
111
110
|
return text
|
|
112
111
|
return text[:MAX_OUTPUT_CHARS] + "\n...[truncated]..."
|
|
@@ -9,12 +9,11 @@ Expected behavior:
|
|
|
9
9
|
- Return the new agent identifier plus any user-facing nickname.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
from __future__ import annotations
|
|
13
|
-
|
|
14
12
|
from ..protocol import JSONDict, JSONValue
|
|
15
13
|
from ..runtime_services import SubAgentManager
|
|
16
14
|
from .agent_tool_schemas import COLLAB_INPUT_ITEMS_SCHEMA
|
|
17
15
|
from .base_tool import BaseTool, ToolContext
|
|
16
|
+
import typing
|
|
18
17
|
|
|
19
18
|
SPAWN_AGENT_OUTPUT_SCHEMA = {
|
|
20
19
|
"type": "object",
|
|
@@ -70,10 +69,10 @@ class SpawnAgentTool(BaseTool):
|
|
|
70
69
|
output_schema = SPAWN_AGENT_OUTPUT_SCHEMA
|
|
71
70
|
supports_parallel = False
|
|
72
71
|
|
|
73
|
-
def __init__(self, subagent_manager: SubAgentManager) -> None:
|
|
72
|
+
def __init__(self, subagent_manager: 'SubAgentManager') -> 'None':
|
|
74
73
|
self._subagent_manager = subagent_manager
|
|
75
74
|
|
|
76
|
-
async def run(self, context: ToolContext, args: JSONDict) -> JSONValue:
|
|
75
|
+
async def run(self, context: 'ToolContext', args: 'JSONDict') -> 'JSONValue':
|
|
77
76
|
message = self._optional_string(args, "message")
|
|
78
77
|
items = args.get("items")
|
|
79
78
|
if items is not None and not isinstance(items, list):
|
|
@@ -90,7 +89,7 @@ class SpawnAgentTool(BaseTool):
|
|
|
90
89
|
history=context.history,
|
|
91
90
|
)
|
|
92
91
|
|
|
93
|
-
def _optional_string(self, args: JSONDict, key: str) -> str
|
|
92
|
+
def _optional_string(self, args: 'JSONDict', key: 'str') -> 'typing.Union[str, None]':
|
|
94
93
|
value = args.get(key)
|
|
95
94
|
if value in (None, ""):
|
|
96
95
|
return None
|