python-codex 0.1.13__py3-none-any.whl → 0.2.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.
- pycodex/agent.py +71 -11
- pycodex/cli.py +16 -356
- pycodex/context.py +12 -0
- pycodex/feishu_card.py +76 -30
- pycodex/feishu_link.py +131 -11
- pycodex/interactive_session.py +397 -0
- pycodex/model.py +11 -22
- pycodex/protocol.py +0 -5
- pycodex/runtime.py +23 -0
- pycodex/runtime_services.py +2 -2
- pycodex/tools/agent_tool_schemas.py +1 -1
- pycodex/tools/apply_patch_tool.py +1 -1
- pycodex/tools/base_tool.py +1 -27
- pycodex/tools/close_agent_tool.py +11 -4
- pycodex/tools/code_mode_manager.py +1 -1
- pycodex/tools/exec_command_tool.py +40 -16
- pycodex/tools/exec_tool.py +18 -2
- pycodex/tools/grep_files_tool.py +19 -6
- pycodex/tools/ipython_tool.py +3 -2
- pycodex/tools/list_dir_tool.py +19 -6
- pycodex/tools/read_file_tool.py +39 -9
- pycodex/tools/request_permissions_tool.py +12 -1
- pycodex/tools/request_user_input_tool.py +28 -1
- pycodex/tools/send_input_tool.py +4 -2
- pycodex/tools/shell_command_tool.py +23 -6
- pycodex/tools/shell_tool.py +13 -4
- pycodex/tools/spawn_agent_tool.py +31 -8
- pycodex/tools/unified_exec_manager.py +49 -93
- pycodex/tools/update_plan_tool.py +14 -6
- pycodex/tools/view_image_tool.py +17 -16
- pycodex/tools/wait_agent_tool.py +15 -3
- pycodex/tools/wait_tool.py +18 -4
- pycodex/tools/web_search_tool.py +2 -1
- pycodex/tools/write_stdin_tool.py +42 -10
- pycodex/utils/compactor.py +7 -1
- pycodex/utils/session_persist.py +42 -1
- pycodex/utils/truncation.py +206 -0
- pycodex/utils/visualize.py +34 -15
- {python_codex-0.1.13.dist-info → python_codex-0.2.0.dist-info}/METADATA +4 -1
- python_codex-0.2.0.dist-info/RECORD +88 -0
- {python_codex-0.1.13.dist-info → python_codex-0.2.0.dist-info}/entry_points.txt +1 -0
- workspace_server/__init__.py +23 -0
- workspace_server/__main__.py +5 -0
- workspace_server/app.py +1347 -0
- workspace_server/workspace.html +866 -0
- pycodex/prompts/exec_tools.json +0 -411
- pycodex/prompts/subagent_tools.json +0 -163
- python_codex-0.1.13.dist-info/RECORD +0 -84
- {python_codex-0.1.13.dist-info → python_codex-0.2.0.dist-info}/WHEEL +0 -0
- {python_codex-0.1.13.dist-info → python_codex-0.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -21,10 +21,17 @@ from .unified_exec_manager import (
|
|
|
21
21
|
)
|
|
22
22
|
import typing
|
|
23
23
|
|
|
24
|
+
MIN_EXEC_YIELD_TIME_MS = 250
|
|
25
|
+
MAX_EXEC_YIELD_TIME_MS = 30_000
|
|
26
|
+
|
|
24
27
|
|
|
25
28
|
class ExecCommandTool(BaseTool):
|
|
26
29
|
name = "exec_command"
|
|
27
|
-
description =
|
|
30
|
+
description = (
|
|
31
|
+
"Runs a command in a PTY, returning output or a session ID for ongoing interaction. "
|
|
32
|
+
"For long tasks, you can reply first; when the task finishes, you will be "
|
|
33
|
+
"invoked to continue."
|
|
34
|
+
)
|
|
28
35
|
input_schema = {
|
|
29
36
|
"type": "object",
|
|
30
37
|
"properties": {
|
|
@@ -34,27 +41,27 @@ class ExecCommandTool(BaseTool):
|
|
|
34
41
|
},
|
|
35
42
|
"workdir": {
|
|
36
43
|
"type": "string",
|
|
37
|
-
"description": "
|
|
38
|
-
},
|
|
39
|
-
"shell": {
|
|
40
|
-
"type": "string",
|
|
41
|
-
"description": "Shell binary to launch. Defaults to the user's default shell.",
|
|
42
|
-
},
|
|
43
|
-
"login": {
|
|
44
|
-
"type": "boolean",
|
|
45
|
-
"description": "Whether to run the shell with -l/-i semantics. Defaults to true.",
|
|
44
|
+
"description": "Working directory for the command. Defaults to the turn cwd.",
|
|
46
45
|
},
|
|
47
46
|
"tty": {
|
|
48
47
|
"type": "boolean",
|
|
49
|
-
"description": "
|
|
48
|
+
"description": "True allocates a PTY for the command; false or omitted uses plain pipes.",
|
|
50
49
|
},
|
|
51
50
|
"yield_time_ms": {
|
|
52
|
-
"type": "
|
|
53
|
-
"description": "
|
|
51
|
+
"type": "number",
|
|
52
|
+
"description": "Wait before yielding output. Defaults to 10000 ms; effective range is 250-30000 ms.",
|
|
54
53
|
},
|
|
55
54
|
"max_output_tokens": {
|
|
56
|
-
"type": "
|
|
57
|
-
"description": "
|
|
55
|
+
"type": "number",
|
|
56
|
+
"description": "Output token budget. Defaults to 10000 tokens; larger requests may be capped by policy.",
|
|
57
|
+
},
|
|
58
|
+
"shell": {
|
|
59
|
+
"type": "string",
|
|
60
|
+
"description": "Shell binary to launch. Defaults to the user's default shell.",
|
|
61
|
+
},
|
|
62
|
+
"login": {
|
|
63
|
+
"type": "boolean",
|
|
64
|
+
"description": "True runs the shell with -l/-i semantics; false disables them. Defaults to true.",
|
|
58
65
|
},
|
|
59
66
|
},
|
|
60
67
|
"required": ["cmd"],
|
|
@@ -78,7 +85,13 @@ class ExecCommandTool(BaseTool):
|
|
|
78
85
|
shell=self._optional_string(args, "shell"),
|
|
79
86
|
login=bool(args.get("login", DEFAULT_LOGIN)),
|
|
80
87
|
tty=bool(args.get("tty", DEFAULT_TTY)),
|
|
81
|
-
yield_time_ms=
|
|
88
|
+
yield_time_ms=self._bounded_int(
|
|
89
|
+
args,
|
|
90
|
+
"yield_time_ms",
|
|
91
|
+
DEFAULT_EXEC_YIELD_TIME_MS,
|
|
92
|
+
MIN_EXEC_YIELD_TIME_MS,
|
|
93
|
+
MAX_EXEC_YIELD_TIME_MS,
|
|
94
|
+
),
|
|
82
95
|
max_output_tokens=self._optional_int(args, "max_output_tokens"),
|
|
83
96
|
)
|
|
84
97
|
|
|
@@ -93,3 +106,14 @@ class ExecCommandTool(BaseTool):
|
|
|
93
106
|
if value in (None, ""):
|
|
94
107
|
return None
|
|
95
108
|
return int(value)
|
|
109
|
+
|
|
110
|
+
def _bounded_int(
|
|
111
|
+
self,
|
|
112
|
+
args: 'JSONDict',
|
|
113
|
+
key: 'str',
|
|
114
|
+
default: 'int',
|
|
115
|
+
minimum: 'int',
|
|
116
|
+
maximum: 'int',
|
|
117
|
+
) -> 'int':
|
|
118
|
+
value = int(args.get(key, default))
|
|
119
|
+
return min(max(value, minimum), maximum)
|
pycodex/tools/exec_tool.py
CHANGED
|
@@ -28,8 +28,24 @@ SOURCE: /[\s\S]+/
|
|
|
28
28
|
class ExecTool(BaseTool):
|
|
29
29
|
name = "exec"
|
|
30
30
|
description = (
|
|
31
|
-
"
|
|
32
|
-
"
|
|
31
|
+
"Run JavaScript code to orchestrate/compose tool calls\n"
|
|
32
|
+
"- Evaluates the provided JavaScript code in a fresh V8 isolate as an "
|
|
33
|
+
"async module.\n"
|
|
34
|
+
"- All nested tools are available on the global `tools` object, for "
|
|
35
|
+
"example `await tools.exec_command(...)`.\n"
|
|
36
|
+
"- Nested tool methods take either a string or an object as their input "
|
|
37
|
+
"argument.\n"
|
|
38
|
+
"- Runs raw JavaScript -- no Node, no file system, no network access, "
|
|
39
|
+
"no console.\n"
|
|
40
|
+
"- Accepts raw JavaScript source text, not JSON, quoted strings, or "
|
|
41
|
+
"markdown code fences.\n"
|
|
42
|
+
"- You may optionally start the tool input with a first-line pragma "
|
|
43
|
+
"like `// @exec: {\"yield_time_ms\": 10000, "
|
|
44
|
+
"\"max_output_tokens\": 1000}`.\n"
|
|
45
|
+
"- `yield_time_ms` asks `exec` to yield early if the script is still "
|
|
46
|
+
"running. Defaults to 10000 ms.\n"
|
|
47
|
+
"- `max_output_tokens` sets the token budget for direct `exec` results. "
|
|
48
|
+
"Defaults to 10000 tokens."
|
|
33
49
|
)
|
|
34
50
|
tool_type = "custom"
|
|
35
51
|
format = {
|
pycodex/tools/grep_files_tool.py
CHANGED
|
@@ -26,18 +26,31 @@ COMMAND_TIMEOUT_SECONDS = 30
|
|
|
26
26
|
class GrepFilesTool(BaseTool):
|
|
27
27
|
name = "grep_files"
|
|
28
28
|
description = (
|
|
29
|
-
"
|
|
30
|
-
"
|
|
29
|
+
"Search file contents with ripgrep and return matching file paths, "
|
|
30
|
+
"newest first."
|
|
31
31
|
)
|
|
32
32
|
input_schema = {
|
|
33
33
|
"type": "object",
|
|
34
34
|
"properties": {
|
|
35
|
-
"pattern": {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
"pattern": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"description": "Regular expression to search for.",
|
|
38
|
+
},
|
|
39
|
+
"include": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"description": "Optional glob pattern to limit searched file names.",
|
|
42
|
+
},
|
|
43
|
+
"path": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"description": "File or directory to search. Defaults to the tool cwd.",
|
|
46
|
+
},
|
|
47
|
+
"limit": {
|
|
48
|
+
"type": "integer",
|
|
49
|
+
"description": "Maximum number of matching paths to return.",
|
|
50
|
+
},
|
|
39
51
|
},
|
|
40
52
|
"required": ["pattern"],
|
|
53
|
+
"additionalProperties": False,
|
|
41
54
|
}
|
|
42
55
|
|
|
43
56
|
def __init__(self, cwd: 'typing.Union[typing.Union[str, Path], None]' = None) -> 'None':
|
pycodex/tools/ipython_tool.py
CHANGED
|
@@ -7,8 +7,9 @@ from .base_tool import BaseTool, ToolContext
|
|
|
7
7
|
class IPythonTool(BaseTool):
|
|
8
8
|
name = "ipython"
|
|
9
9
|
description = (
|
|
10
|
-
"Execute Python code in the current IPython kernel namespace. "
|
|
11
|
-
"
|
|
10
|
+
"Execute Python code in the current IPython kernel namespace. Use this "
|
|
11
|
+
"only when running inside IPython and live notebook/session variables "
|
|
12
|
+
"need to be inspected."
|
|
12
13
|
)
|
|
13
14
|
input_schema = {
|
|
14
15
|
"type": "object",
|
pycodex/tools/list_dir_tool.py
CHANGED
|
@@ -23,18 +23,31 @@ INDENTATION_SPACES = 2
|
|
|
23
23
|
class ListDirTool(BaseTool):
|
|
24
24
|
name = "list_dir"
|
|
25
25
|
description = (
|
|
26
|
-
"
|
|
27
|
-
"
|
|
26
|
+
"List entries in a local directory as a stable tree slice with simple "
|
|
27
|
+
"type labels."
|
|
28
28
|
)
|
|
29
29
|
input_schema = {
|
|
30
30
|
"type": "object",
|
|
31
31
|
"properties": {
|
|
32
|
-
"dir_path": {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
"dir_path": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"description": "Absolute path to the directory to list.",
|
|
35
|
+
},
|
|
36
|
+
"offset": {
|
|
37
|
+
"type": "integer",
|
|
38
|
+
"description": "1-indexed entry offset for pagination. Defaults to 1.",
|
|
39
|
+
},
|
|
40
|
+
"limit": {
|
|
41
|
+
"type": "integer",
|
|
42
|
+
"description": "Maximum number of entries to return. Defaults to 25.",
|
|
43
|
+
},
|
|
44
|
+
"depth": {
|
|
45
|
+
"type": "integer",
|
|
46
|
+
"description": "Maximum directory depth to include. Defaults to 2.",
|
|
47
|
+
},
|
|
36
48
|
},
|
|
37
49
|
"required": ["dir_path"],
|
|
50
|
+
"additionalProperties": False,
|
|
38
51
|
}
|
|
39
52
|
|
|
40
53
|
async def run(self, context: 'ToolContext', args: 'JSONDict') -> 'JSONValue':
|
pycodex/tools/read_file_tool.py
CHANGED
|
@@ -30,22 +30,52 @@ class ReadFileTool(BaseTool):
|
|
|
30
30
|
input_schema = {
|
|
31
31
|
"type": "object",
|
|
32
32
|
"properties": {
|
|
33
|
-
"file_path": {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
"file_path": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"description": "Absolute path to the file to read.",
|
|
36
|
+
},
|
|
37
|
+
"offset": {
|
|
38
|
+
"type": "integer",
|
|
39
|
+
"description": "1-indexed starting line number. Defaults to 1.",
|
|
40
|
+
},
|
|
41
|
+
"limit": {
|
|
42
|
+
"type": "integer",
|
|
43
|
+
"description": "Maximum number of lines to return. Defaults to 2000.",
|
|
44
|
+
},
|
|
45
|
+
"mode": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"description": "Read mode. Use `slice` for a line range or `indentation` for an indentation-aware block.",
|
|
48
|
+
},
|
|
37
49
|
"indentation": {
|
|
38
50
|
"type": "object",
|
|
51
|
+
"description": "Options for indentation-aware block mode.",
|
|
39
52
|
"properties": {
|
|
40
|
-
"anchor_line": {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"
|
|
53
|
+
"anchor_line": {
|
|
54
|
+
"type": "integer",
|
|
55
|
+
"description": "1-indexed anchor line for block expansion.",
|
|
56
|
+
},
|
|
57
|
+
"max_levels": {
|
|
58
|
+
"type": "integer",
|
|
59
|
+
"description": "How many indentation levels above the anchor to include. Use 0 for file-root scope.",
|
|
60
|
+
},
|
|
61
|
+
"include_siblings": {
|
|
62
|
+
"type": "boolean",
|
|
63
|
+
"description": "Whether to include sibling blocks at the same indentation level.",
|
|
64
|
+
},
|
|
65
|
+
"include_header": {
|
|
66
|
+
"type": "boolean",
|
|
67
|
+
"description": "Whether to include immediately preceding header/comment lines.",
|
|
68
|
+
},
|
|
69
|
+
"max_lines": {
|
|
70
|
+
"type": "integer",
|
|
71
|
+
"description": "Maximum number of lines to return in indentation mode.",
|
|
72
|
+
},
|
|
45
73
|
},
|
|
74
|
+
"additionalProperties": False,
|
|
46
75
|
},
|
|
47
76
|
},
|
|
48
77
|
"required": ["file_path"],
|
|
78
|
+
"additionalProperties": False,
|
|
49
79
|
}
|
|
50
80
|
|
|
51
81
|
async def run(self, context: 'ToolContext', args: 'JSONDict') -> 'JSONValue':
|
|
@@ -55,7 +55,13 @@ class RequestPermissionsTool(BaseTool):
|
|
|
55
55
|
name = "request_permissions"
|
|
56
56
|
description = (
|
|
57
57
|
"Request additional filesystem or network permissions from the user "
|
|
58
|
-
"and wait for a
|
|
58
|
+
"and wait for the client to grant a subset of the requested permission "
|
|
59
|
+
"profile. Use environment_id to target a specific attached "
|
|
60
|
+
"environment; omit it to use the primary environment. Relative "
|
|
61
|
+
"filesystem paths resolve against the selected environment cwd. "
|
|
62
|
+
"Granted permissions apply automatically to later shell-like commands "
|
|
63
|
+
"in the current turn, or for the rest of the session if the client "
|
|
64
|
+
"approves them at session scope."
|
|
59
65
|
)
|
|
60
66
|
input_schema = {
|
|
61
67
|
"type": "object",
|
|
@@ -64,6 +70,10 @@ class RequestPermissionsTool(BaseTool):
|
|
|
64
70
|
"type": "string",
|
|
65
71
|
"description": "Optional short explanation for why additional permissions are needed.",
|
|
66
72
|
},
|
|
73
|
+
"environment_id": {
|
|
74
|
+
"type": "string",
|
|
75
|
+
"description": "Environment id from <environment_context>. Omit to use the primary environment.",
|
|
76
|
+
},
|
|
67
77
|
"permissions": REQUEST_PERMISSION_PROFILE_SCHEMA,
|
|
68
78
|
},
|
|
69
79
|
"required": ["permissions"],
|
|
@@ -85,6 +95,7 @@ class RequestPermissionsTool(BaseTool):
|
|
|
85
95
|
response = await self._request_manager.request(
|
|
86
96
|
{
|
|
87
97
|
"reason": None if args.get("reason") in (None, "") else str(args.get("reason")),
|
|
98
|
+
"environment_id": None if args.get("environment_id") in (None, "") else str(args.get("environment_id")),
|
|
88
99
|
"permissions": permissions,
|
|
89
100
|
}
|
|
90
101
|
)
|
|
@@ -20,6 +20,9 @@ from ..runtime_services import RequestUserInputManager
|
|
|
20
20
|
from .base_tool import BaseTool, StructuredToolOutput, ToolContext
|
|
21
21
|
import typing
|
|
22
22
|
|
|
23
|
+
MIN_AUTO_RESOLUTION_MS = 60_000
|
|
24
|
+
MAX_AUTO_RESOLUTION_MS = 240_000
|
|
25
|
+
|
|
23
26
|
REQUEST_USER_INPUT_QUESTION_SCHEMA = {
|
|
24
27
|
"type": "object",
|
|
25
28
|
"properties": {
|
|
@@ -96,7 +99,11 @@ def request_user_input_tool_description(
|
|
|
96
99
|
allowed_modes = "Plan mode"
|
|
97
100
|
return (
|
|
98
101
|
"Request user input for one to three short questions and wait for the "
|
|
99
|
-
f"response.
|
|
102
|
+
f"response. Set autoResolutionMs, from {MIN_AUTO_RESOLUTION_MS} to "
|
|
103
|
+
f"{MAX_AUTO_RESOLUTION_MS} milliseconds, only when the question is "
|
|
104
|
+
"useful but non-blocking and continuing with best judgment is "
|
|
105
|
+
"acceptable if the user does not answer; omit it when explicit user "
|
|
106
|
+
f"input is required. This tool is only available in {allowed_modes}."
|
|
100
107
|
)
|
|
101
108
|
|
|
102
109
|
|
|
@@ -110,6 +117,20 @@ class RequestUserInputTool(BaseTool):
|
|
|
110
117
|
"type": "array",
|
|
111
118
|
"description": "Questions to show the user. Prefer 1 and do not exceed 3",
|
|
112
119
|
"items": REQUEST_USER_INPUT_QUESTION_SCHEMA,
|
|
120
|
+
},
|
|
121
|
+
"autoResolutionMs": {
|
|
122
|
+
"type": "number",
|
|
123
|
+
"description": (
|
|
124
|
+
"Optional auto-resolution window in milliseconds, from "
|
|
125
|
+
f"{MIN_AUTO_RESOLUTION_MS} to {MAX_AUTO_RESOLUTION_MS}. "
|
|
126
|
+
"Include this only when the question is useful but "
|
|
127
|
+
"non-blocking and continuing with best judgment is "
|
|
128
|
+
"acceptable if the user does not answer; omit it when "
|
|
129
|
+
"explicit user input is required before continuing. Use "
|
|
130
|
+
f"{MIN_AUTO_RESOLUTION_MS} for lightly helpful context and "
|
|
131
|
+
f"up to {MAX_AUTO_RESOLUTION_MS} when the answer would "
|
|
132
|
+
"materially unblock better work."
|
|
133
|
+
),
|
|
113
134
|
}
|
|
114
135
|
},
|
|
115
136
|
"required": ["questions"],
|
|
@@ -157,6 +178,12 @@ class RequestUserInputTool(BaseTool):
|
|
|
157
178
|
for question in questions
|
|
158
179
|
]
|
|
159
180
|
}
|
|
181
|
+
auto_resolution_ms = args.get("autoResolutionMs")
|
|
182
|
+
if auto_resolution_ms not in (None, ""):
|
|
183
|
+
request_payload["autoResolutionMs"] = min(
|
|
184
|
+
max(int(auto_resolution_ms), MIN_AUTO_RESOLUTION_MS),
|
|
185
|
+
MAX_AUTO_RESOLUTION_MS,
|
|
186
|
+
)
|
|
160
187
|
response = await self._request_manager.request(request_payload)
|
|
161
188
|
if response is None:
|
|
162
189
|
return "request_user_input was cancelled before receiving a response"
|
pycodex/tools/send_input_tool.py
CHANGED
|
@@ -32,7 +32,9 @@ class SendInputTool(BaseTool):
|
|
|
32
32
|
name = "send_input"
|
|
33
33
|
description = (
|
|
34
34
|
"Send a message to an existing agent. Use interrupt=true to redirect "
|
|
35
|
-
"work immediately."
|
|
35
|
+
"work immediately. You should reuse the agent by send_input if you "
|
|
36
|
+
"believe your assigned task is highly dependent on the context of a "
|
|
37
|
+
"previous task."
|
|
36
38
|
)
|
|
37
39
|
input_schema = {
|
|
38
40
|
"type": "object",
|
|
@@ -48,7 +50,7 @@ class SendInputTool(BaseTool):
|
|
|
48
50
|
"items": COLLAB_INPUT_ITEMS_SCHEMA,
|
|
49
51
|
"interrupt": {
|
|
50
52
|
"type": "boolean",
|
|
51
|
-
"description": "
|
|
53
|
+
"description": "True interrupts the current task and handles this message immediately; false or omitted queues it.",
|
|
52
54
|
},
|
|
53
55
|
},
|
|
54
56
|
"required": ["id"],
|
|
@@ -18,22 +18,39 @@ from ..protocol import JSONDict, JSONValue
|
|
|
18
18
|
from .base_tool import BaseTool, ToolContext
|
|
19
19
|
import typing
|
|
20
20
|
|
|
21
|
-
DEFAULT_SHELL_TIMEOUT_MS =
|
|
21
|
+
DEFAULT_SHELL_TIMEOUT_MS = 10_000
|
|
22
22
|
MAX_OUTPUT_CHARS = 12_000
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
class ShellCommandTool(BaseTool):
|
|
26
26
|
name = "shell_command"
|
|
27
|
-
description =
|
|
27
|
+
description = (
|
|
28
|
+
"Runs a shell command and returns its output.\n"
|
|
29
|
+
"- Always set the `workdir` param when using the shell_command "
|
|
30
|
+
"function. Do not use `cd` unless absolutely necessary."
|
|
31
|
+
)
|
|
28
32
|
input_schema = {
|
|
29
33
|
"type": "object",
|
|
30
34
|
"properties": {
|
|
31
|
-
"command": {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
"command": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"description": "Shell script to run in the user's default shell.",
|
|
38
|
+
},
|
|
39
|
+
"workdir": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"description": "Working directory for the command. Defaults to the turn cwd.",
|
|
42
|
+
},
|
|
43
|
+
"timeout_ms": {
|
|
44
|
+
"type": "integer",
|
|
45
|
+
"description": "Maximum command runtime. Defaults to 10000 ms.",
|
|
46
|
+
},
|
|
47
|
+
"login": {
|
|
48
|
+
"type": "boolean",
|
|
49
|
+
"description": "True runs with login shell semantics; false disables them. Defaults to true.",
|
|
50
|
+
},
|
|
35
51
|
},
|
|
36
52
|
"required": ["command"],
|
|
53
|
+
"additionalProperties": False,
|
|
37
54
|
}
|
|
38
55
|
supports_parallel = False
|
|
39
56
|
|
pycodex/tools/shell_tool.py
CHANGED
|
@@ -25,8 +25,9 @@ MAX_OUTPUT_CHARS = 12_000
|
|
|
25
25
|
class ShellTool(BaseTool):
|
|
26
26
|
name = "shell"
|
|
27
27
|
description = (
|
|
28
|
-
"Runs a
|
|
29
|
-
"
|
|
28
|
+
"Runs a process from an argv array and returns its output. Use "
|
|
29
|
+
"`shell_command` for shell-script strings; use this tool when the exact "
|
|
30
|
+
"argv vector matters."
|
|
30
31
|
)
|
|
31
32
|
input_schema = {
|
|
32
33
|
"type": "object",
|
|
@@ -34,11 +35,19 @@ class ShellTool(BaseTool):
|
|
|
34
35
|
"command": {
|
|
35
36
|
"type": "array",
|
|
36
37
|
"items": {"type": "string"},
|
|
38
|
+
"description": "Command argv to execute.",
|
|
39
|
+
},
|
|
40
|
+
"workdir": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"description": "Working directory for the command. Defaults to the tool cwd.",
|
|
43
|
+
},
|
|
44
|
+
"timeout_ms": {
|
|
45
|
+
"type": "integer",
|
|
46
|
+
"description": "Maximum command runtime in milliseconds.",
|
|
37
47
|
},
|
|
38
|
-
"workdir": {"type": "string"},
|
|
39
|
-
"timeout_ms": {"type": "integer"},
|
|
40
48
|
},
|
|
41
49
|
"required": ["command"],
|
|
50
|
+
"additionalProperties": False,
|
|
42
51
|
}
|
|
43
52
|
supports_parallel = False
|
|
44
53
|
|
|
@@ -34,11 +34,34 @@ SPAWN_AGENT_OUTPUT_SCHEMA = {
|
|
|
34
34
|
|
|
35
35
|
class SpawnAgentTool(BaseTool):
|
|
36
36
|
name = "spawn_agent"
|
|
37
|
-
description =
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
description = """
|
|
38
|
+
Spawn a sub-agent for a well-scoped task. Returns the spawned agent id plus the user-facing nickname when available. Spawned agents inherit your current model by default. Omit `model` to use that preferred default; set `model` only when an explicit override is needed.
|
|
39
|
+
This spawn_agent tool provides you access to sub-agents that inherit your current model by default. Do not set the `model` field unless the user explicitly asks for a different model or there is a clear task-specific reason. You should follow the rules and guidelines below to use this tool.
|
|
40
|
+
|
|
41
|
+
Do not spawn sub-agents unless the user explicitly asks for sub-agents, delegation, or parallel agent work.
|
|
42
|
+
|
|
43
|
+
### Designing delegated subtasks
|
|
44
|
+
- Subtasks must be concrete, well-defined, and self-contained.
|
|
45
|
+
- Delegated subtasks must materially advance the main task.
|
|
46
|
+
- Do not duplicate work between the main rollout and delegated subtasks.
|
|
47
|
+
- Avoid issuing multiple delegate calls on the same unresolved thread unless the new delegated task is genuinely different and necessary.
|
|
48
|
+
- Narrow the delegated ask to the concrete output you need next.
|
|
49
|
+
- For coding tasks, prefer delegating concrete code-change worker subtasks over read-only explorer analysis when the subagent can make a bounded patch in a clear write scope.
|
|
50
|
+
- When delegating coding work, instruct the submodel to edit files directly in its forked workspace and list the file paths it changed in the final answer.
|
|
51
|
+
- For code-edit subtasks, decompose work so each delegated task has a disjoint write set.
|
|
52
|
+
|
|
53
|
+
### After you delegate
|
|
54
|
+
- Call wait_agent very sparingly. Only call wait_agent when you need the result immediately for the next critical-path step and you are blocked until it returns.
|
|
55
|
+
- Do not redo delegated subagent tasks yourself; focus on integrating results or tackling non-overlapping work.
|
|
56
|
+
- While the subagent is running in the background, do meaningful non-overlapping work immediately.
|
|
57
|
+
- Do not repeatedly wait by reflex.
|
|
58
|
+
- When a delegated coding task returns, quickly review the uploaded changes, then integrate or refine them.
|
|
59
|
+
|
|
60
|
+
### Parallel delegation patterns
|
|
61
|
+
- Run multiple independent information-seeking subtasks in parallel when you have distinct questions that can be answered independently.
|
|
62
|
+
- Split implementation into disjoint codebase slices and spawn multiple agents in parallel when the write scopes do not overlap.
|
|
63
|
+
- Delegate verification only when it can run in parallel with ongoing implementation and is likely to catch a concrete risk before final integration.
|
|
64
|
+
- The key is to find opportunities to spawn multiple independent subtasks in parallel within the same round, while ensuring each subtask is well-defined, self-contained, and materially advances the main task."""
|
|
42
65
|
input_schema = {
|
|
43
66
|
"type": "object",
|
|
44
67
|
"properties": {
|
|
@@ -53,15 +76,15 @@ class SpawnAgentTool(BaseTool):
|
|
|
53
76
|
},
|
|
54
77
|
"fork_context": {
|
|
55
78
|
"type": "boolean",
|
|
56
|
-
"description": "
|
|
79
|
+
"description": "True forks the current thread history into the new agent; false or omitted starts with only the initial prompt.",
|
|
57
80
|
},
|
|
58
81
|
"model": {
|
|
59
82
|
"type": "string",
|
|
60
|
-
"description": "
|
|
83
|
+
"description": "Model override for the new agent. Omit unless an explicit override is needed.",
|
|
61
84
|
},
|
|
62
85
|
"reasoning_effort": {
|
|
63
86
|
"type": "string",
|
|
64
|
-
"description": "
|
|
87
|
+
"description": "Reasoning effort override for the new agent. Omit to inherit the parent effort.",
|
|
65
88
|
},
|
|
66
89
|
},
|
|
67
90
|
"additionalProperties": False,
|