python-codex 0.4.6__py3-none-any.whl → 0.4.8__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 +8 -0
- pycodex/bootstrap.py +23 -26
- pycodex/context.py +10 -0
- pycodex/runtime.py +11 -0
- pycodex/tools/apply_patch_tool.py +24 -26
- pycodex/tools/base_tool.py +5 -0
- pycodex/tools/code_mode_manager.py +4 -9
- pycodex/tools/exec_command_tool.py +1 -2
- pycodex/tools/grep_files_tool.py +2 -16
- pycodex/tools/shell_command_tool.py +1 -17
- pycodex/tools/shell_tool.py +1 -17
- pycodex/tools/unified_exec_manager.py +2 -14
- pycodex/tools/view_image_tool.py +1 -12
- pycodex/utils/event_helpers.py +2 -0
- pycodex/utils/session_persist.py +11 -3
- {python_codex-0.4.6.dist-info → python_codex-0.4.8.dist-info}/METADATA +17 -4
- {python_codex-0.4.6.dist-info → python_codex-0.4.8.dist-info}/RECORD +20 -20
- {python_codex-0.4.6.dist-info → python_codex-0.4.8.dist-info}/WHEEL +0 -0
- {python_codex-0.4.6.dist-info → python_codex-0.4.8.dist-info}/entry_points.txt +0 -0
- {python_codex-0.4.6.dist-info → python_codex-0.4.8.dist-info}/licenses/LICENSE +0 -0
pycodex/agent.py
CHANGED
|
@@ -152,6 +152,13 @@ class Agent:
|
|
|
152
152
|
self.context_manager.set_model(model)
|
|
153
153
|
self._last_total_usage_tokens = None
|
|
154
154
|
|
|
155
|
+
def set_cwd(self, cwd: "typing.Union[str, Path]") -> "None":
|
|
156
|
+
if self.is_running:
|
|
157
|
+
raise RuntimeError("cannot change directory while agent is running")
|
|
158
|
+
if not self.accepts_input:
|
|
159
|
+
raise RuntimeError("cannot change directory while agent is closing or closed")
|
|
160
|
+
self.context_manager.set_cwd(cwd)
|
|
161
|
+
|
|
155
162
|
def resume(
|
|
156
163
|
self, session_file_path: "typing.Union[str, Path, None]" = None
|
|
157
164
|
) -> "typing.Union[typing.Dict[str, object], None]":
|
|
@@ -400,6 +407,7 @@ class Agent:
|
|
|
400
407
|
context = ToolContext(
|
|
401
408
|
turn_id=turn_id,
|
|
402
409
|
history=self.history,
|
|
410
|
+
cwd=self.context_manager.cwd,
|
|
403
411
|
)
|
|
404
412
|
if len(batch) == 1:
|
|
405
413
|
results.append(await self._run_single_tool(turn_id, batch[0], context))
|
pycodex/bootstrap.py
CHANGED
|
@@ -86,7 +86,6 @@ def configure_loguru() -> "None":
|
|
|
86
86
|
def get_tools(
|
|
87
87
|
runtime_environment: "typing.Union[AgentRuntimeEnvironment, None]" = None,
|
|
88
88
|
exec_mode: "bool" = False,
|
|
89
|
-
cwd: "typing.Union[str, Path, None]" = None,
|
|
90
89
|
toolset: "typing.Union[typing.Iterable[str], None]" = None,
|
|
91
90
|
):
|
|
92
91
|
from .tools import (
|
|
@@ -119,8 +118,8 @@ def get_tools(
|
|
|
119
118
|
|
|
120
119
|
runtime_environment = runtime_environment or create_agent_runtime_environment()
|
|
121
120
|
registry = Registry(runtime_environment)
|
|
122
|
-
code_mode_manager = CodeModeManager(registry
|
|
123
|
-
unified_exec_manager = UnifiedExecManager(
|
|
121
|
+
code_mode_manager = CodeModeManager(registry)
|
|
122
|
+
unified_exec_manager = UnifiedExecManager()
|
|
124
123
|
clock_manager = ClockManager()
|
|
125
124
|
exec_tool = ExecTool(code_mode_manager)
|
|
126
125
|
wait_tool = WaitTool(code_mode_manager)
|
|
@@ -135,16 +134,16 @@ def get_tools(
|
|
|
135
134
|
resume_agent_tool = ResumeAgentTool(runtime_environment.subagent_manager)
|
|
136
135
|
wait_agent_tool = WaitAgentTool(runtime_environment.subagent_manager)
|
|
137
136
|
close_agent_tool = CloseAgentTool(runtime_environment.subagent_manager)
|
|
138
|
-
apply_patch_tool = ApplyPatchTool(
|
|
139
|
-
shell_tool = ShellTool(
|
|
140
|
-
shell_command_tool = ShellCommandTool(
|
|
137
|
+
apply_patch_tool = ApplyPatchTool()
|
|
138
|
+
shell_tool = ShellTool()
|
|
139
|
+
shell_command_tool = ShellCommandTool()
|
|
141
140
|
exec_command_tool = ExecCommandTool(unified_exec_manager)
|
|
142
141
|
write_stdin_tool = WriteStdinTool(unified_exec_manager)
|
|
143
142
|
clock_tool = ClockTool(clock_manager)
|
|
144
|
-
grep_files_tool = GrepFilesTool(
|
|
143
|
+
grep_files_tool = GrepFilesTool()
|
|
145
144
|
read_file_tool = ReadFileTool()
|
|
146
145
|
list_dir_tool = ListDirTool()
|
|
147
|
-
view_image_tool = ViewImageTool(
|
|
146
|
+
view_image_tool = ViewImageTool()
|
|
148
147
|
tools = (
|
|
149
148
|
shell_tool,
|
|
150
149
|
shell_command_tool,
|
|
@@ -200,7 +199,6 @@ def get_tools(
|
|
|
200
199
|
|
|
201
200
|
def get_subagent_tools(
|
|
202
201
|
runtime_environment: "typing.Union[AgentRuntimeEnvironment, None]" = None,
|
|
203
|
-
cwd: "typing.Union[str, Path, None]" = None,
|
|
204
202
|
):
|
|
205
203
|
from .tools import (
|
|
206
204
|
ApplyPatchTool,
|
|
@@ -215,13 +213,13 @@ def get_subagent_tools(
|
|
|
215
213
|
|
|
216
214
|
runtime_environment = runtime_environment or create_agent_runtime_environment()
|
|
217
215
|
registry = Registry(runtime_environment)
|
|
218
|
-
unified_exec_manager = UnifiedExecManager(
|
|
216
|
+
unified_exec_manager = UnifiedExecManager()
|
|
219
217
|
registry.register(ExecCommandTool(unified_exec_manager))
|
|
220
218
|
registry.register(WriteStdinTool(unified_exec_manager))
|
|
221
219
|
registry.register(UpdatePlanTool(runtime_environment.plan_store))
|
|
222
|
-
registry.register(ApplyPatchTool(
|
|
220
|
+
registry.register(ApplyPatchTool())
|
|
223
221
|
registry.register(WebSearchTool())
|
|
224
|
-
registry.register(ViewImageTool(
|
|
222
|
+
registry.register(ViewImageTool())
|
|
225
223
|
return registry
|
|
226
224
|
|
|
227
225
|
|
|
@@ -244,46 +242,41 @@ def build_agent(
|
|
|
244
242
|
)
|
|
245
243
|
runtime_environment = create_agent_runtime_environment()
|
|
246
244
|
|
|
247
|
-
def make_subagent_runtime_builder(
|
|
245
|
+
def make_subagent_runtime_builder(parent_agent):
|
|
248
246
|
def build_subagent_runtime(
|
|
249
247
|
model_override: "typing.Union[str, None]",
|
|
250
248
|
reasoning_effort_override: "typing.Union[str, None]",
|
|
251
249
|
initial_history=(),
|
|
252
250
|
session_id: "typing.Union[str, None]" = None,
|
|
253
251
|
) -> "AgentRuntime":
|
|
254
|
-
nested_client =
|
|
252
|
+
nested_client = parent_agent.model_client.with_overrides(
|
|
255
253
|
model_override,
|
|
256
254
|
reasoning_effort_override,
|
|
257
255
|
session_id=session_id,
|
|
258
256
|
openai_subagent="collab_spawn",
|
|
259
257
|
)
|
|
260
258
|
subagent_agent_runtime_environment = create_agent_runtime_environment()
|
|
261
|
-
|
|
262
|
-
make_subagent_runtime_builder(nested_client)
|
|
263
|
-
)
|
|
259
|
+
inherited_cwd = parent_agent.context_manager.cwd
|
|
264
260
|
sub_agent = Agent(
|
|
265
261
|
nested_client,
|
|
266
|
-
get_subagent_tools(
|
|
267
|
-
|
|
268
|
-
),
|
|
269
|
-
context_config,
|
|
262
|
+
get_subagent_tools(subagent_agent_runtime_environment),
|
|
263
|
+
replace(context_config, cwd=inherited_cwd),
|
|
270
264
|
initial_history=tuple(initial_history),
|
|
271
265
|
session_id=session_id,
|
|
272
266
|
)
|
|
267
|
+
subagent_agent_runtime_environment.subagent_manager.set_runtime_builder(
|
|
268
|
+
make_subagent_runtime_builder(sub_agent)
|
|
269
|
+
)
|
|
273
270
|
return AgentRuntime(sub_agent)
|
|
274
271
|
|
|
275
272
|
return build_subagent_runtime
|
|
276
273
|
|
|
277
|
-
runtime_environment.subagent_manager.set_runtime_builder(
|
|
278
|
-
make_subagent_runtime_builder(client)
|
|
279
|
-
)
|
|
280
274
|
session_id = uuid7_string()
|
|
281
|
-
|
|
275
|
+
agent = Agent(
|
|
282
276
|
client,
|
|
283
277
|
get_tools(
|
|
284
278
|
runtime_environment,
|
|
285
279
|
exec_mode=True,
|
|
286
|
-
cwd=resolved_cwd,
|
|
287
280
|
toolset=toolset,
|
|
288
281
|
),
|
|
289
282
|
context_config,
|
|
@@ -292,6 +285,10 @@ def build_agent(
|
|
|
292
285
|
),
|
|
293
286
|
session_id=session_id,
|
|
294
287
|
)
|
|
288
|
+
runtime_environment.subagent_manager.set_runtime_builder(
|
|
289
|
+
make_subagent_runtime_builder(agent)
|
|
290
|
+
)
|
|
291
|
+
return agent
|
|
295
292
|
|
|
296
293
|
|
|
297
294
|
def build_model(
|
pycodex/context.py
CHANGED
|
@@ -244,6 +244,16 @@ class ContextManager:
|
|
|
244
244
|
def set_model(self, model: "str") -> "None":
|
|
245
245
|
self._config = replace(self._config, model=model)
|
|
246
246
|
|
|
247
|
+
def set_cwd(self, cwd: "typing.Union[str, Path]") -> "None":
|
|
248
|
+
path = Path(cwd).expanduser()
|
|
249
|
+
path = (self.cwd / path).resolve(strict=True)
|
|
250
|
+
if not path.is_dir():
|
|
251
|
+
raise NotADirectoryError(str(path))
|
|
252
|
+
self.cwd = path
|
|
253
|
+
self._config = replace(self._config, cwd=path)
|
|
254
|
+
self._workspace_metadata_turn_id = None
|
|
255
|
+
self._workspace_metadata_cache = None
|
|
256
|
+
|
|
247
257
|
def resolve_auto_compact_token_limit(self) -> "typing.Union[int, None]":
|
|
248
258
|
if self._config.model_auto_compact_token_limit is not None:
|
|
249
259
|
return self._config.model_auto_compact_token_limit
|
pycodex/runtime.py
CHANGED
|
@@ -124,6 +124,7 @@ class AgentRuntime:
|
|
|
124
124
|
"help",
|
|
125
125
|
"history",
|
|
126
126
|
"title",
|
|
127
|
+
"cd",
|
|
127
128
|
"model",
|
|
128
129
|
"effort",
|
|
129
130
|
"resume",
|
|
@@ -139,6 +140,7 @@ class AgentRuntime:
|
|
|
139
140
|
recorded_path = self.agent.recorded_session_file_path
|
|
140
141
|
return {
|
|
141
142
|
"session_id": self.agent.session_id,
|
|
143
|
+
"cwd": str(self.agent.context_manager.cwd),
|
|
142
144
|
"rollout_path": str(rollout_path) if rollout_path is not None else None,
|
|
143
145
|
"recorded_rollout_path": (
|
|
144
146
|
str(recorded_path) if recorded_path is not None else None
|
|
@@ -187,6 +189,11 @@ class AgentRuntime:
|
|
|
187
189
|
self.agent.set_model(model)
|
|
188
190
|
self.publish_state("model")
|
|
189
191
|
|
|
192
|
+
def set_cwd(self, cwd):
|
|
193
|
+
self.require_idle("change directory")
|
|
194
|
+
self.agent.set_cwd(cwd)
|
|
195
|
+
self.publish_state("cwd")
|
|
196
|
+
|
|
190
197
|
def resume(self, path=None, title=""):
|
|
191
198
|
self.require_idle("resume")
|
|
192
199
|
resumed = self.agent.resume(path)
|
|
@@ -275,6 +282,10 @@ class AgentRuntime:
|
|
|
275
282
|
return {"kind": "help", "commands": self.commands()}
|
|
276
283
|
if command == "history":
|
|
277
284
|
return {"kind": "history", "state": self.snapshot()}
|
|
285
|
+
if command == "cd":
|
|
286
|
+
if argument:
|
|
287
|
+
self.set_cwd(argument)
|
|
288
|
+
return {"kind": "cwd", "cwd": str(self.agent.context_manager.cwd)}
|
|
278
289
|
if command == "title":
|
|
279
290
|
if argument:
|
|
280
291
|
self.set_title(argument)
|
|
@@ -92,20 +92,15 @@ class ApplyPatchTool(BaseTool):
|
|
|
92
92
|
}
|
|
93
93
|
supports_parallel = False
|
|
94
94
|
|
|
95
|
-
def __init__(
|
|
96
|
-
self, cwd: "typing.Union[typing.Union[str, Path], None]" = None
|
|
97
|
-
) -> "None":
|
|
98
|
-
self._workspace_root = Path(cwd or Path.cwd()).resolve()
|
|
99
|
-
|
|
100
95
|
async def run(self, context: "ToolContext", args: "JSONValue") -> "JSONValue":
|
|
101
|
-
|
|
96
|
+
cwd = context.cwd
|
|
102
97
|
patch_text = str(args)
|
|
103
98
|
logger.debug(
|
|
104
|
-
"apply_patch workspace={} bytes={}",
|
|
99
|
+
"apply_patch workspace={} bytes={}", cwd, len(patch_text)
|
|
105
100
|
)
|
|
106
101
|
try:
|
|
107
102
|
operations = self._parse_patch(patch_text)
|
|
108
|
-
return self._format_result(self._apply_operations(operations), exit_code=0)
|
|
103
|
+
return self._format_result(self._apply_operations(operations, cwd), exit_code=0)
|
|
109
104
|
except ApplyPatchError as exc:
|
|
110
105
|
return self._format_result(str(exc), exit_code=1)
|
|
111
106
|
|
|
@@ -239,52 +234,54 @@ class ApplyPatchTool(BaseTool):
|
|
|
239
234
|
def _apply_operations(
|
|
240
235
|
self,
|
|
241
236
|
operations: "typing.List[typing.Union[typing.Union[_AddFileOp, _DeleteFileOp], _UpdateFileOp]]",
|
|
237
|
+
cwd: "Path",
|
|
242
238
|
) -> "str":
|
|
243
239
|
preview: "typing.Dict[Path, typing.Union[str, None]]" = {}
|
|
244
240
|
summaries: "typing.Dict[Path, str]" = {}
|
|
245
241
|
|
|
246
242
|
for operation in operations:
|
|
247
243
|
if isinstance(operation, _AddFileOp):
|
|
248
|
-
path = self._resolve_workspace_path(operation.path)
|
|
244
|
+
path = self._resolve_workspace_path(operation.path, cwd)
|
|
249
245
|
preview[path] = operation.content
|
|
250
246
|
summaries[path] = "A"
|
|
251
247
|
continue
|
|
252
248
|
|
|
253
249
|
if isinstance(operation, _DeleteFileOp):
|
|
254
|
-
path = self._resolve_workspace_path(operation.path)
|
|
255
|
-
self._read_preview_file(path, preview)
|
|
250
|
+
path = self._resolve_workspace_path(operation.path, cwd)
|
|
251
|
+
self._read_preview_file(path, preview, cwd)
|
|
256
252
|
preview[path] = None
|
|
257
253
|
summaries[path] = "D"
|
|
258
254
|
continue
|
|
259
255
|
|
|
260
|
-
path = self._resolve_workspace_path(operation.path)
|
|
261
|
-
original = self._read_preview_file(path, preview)
|
|
262
|
-
updated = self._apply_update(path, original, operation.sections)
|
|
256
|
+
path = self._resolve_workspace_path(operation.path, cwd)
|
|
257
|
+
original = self._read_preview_file(path, preview, cwd)
|
|
258
|
+
updated = self._apply_update(path, original, operation.sections, cwd)
|
|
263
259
|
destination = path
|
|
264
260
|
if operation.move_to is not None:
|
|
265
|
-
destination = self._resolve_workspace_path(operation.move_to)
|
|
261
|
+
destination = self._resolve_workspace_path(operation.move_to, cwd)
|
|
266
262
|
preview[path] = None
|
|
267
263
|
summaries.pop(path, None)
|
|
268
264
|
preview[destination] = updated
|
|
269
265
|
summaries[destination] = "M"
|
|
270
266
|
|
|
271
267
|
self._write_preview(preview)
|
|
272
|
-
return self._format_success(summaries)
|
|
268
|
+
return self._format_success(summaries, cwd)
|
|
273
269
|
|
|
274
270
|
def _read_preview_file(
|
|
275
|
-
self, path: "Path", preview: "typing.Dict[Path, typing.Union[str, None]]"
|
|
271
|
+
self, path: "Path", preview: "typing.Dict[Path, typing.Union[str, None]]",
|
|
272
|
+
cwd: "Path",
|
|
276
273
|
) -> "str":
|
|
277
274
|
if path in preview:
|
|
278
275
|
content = preview[path]
|
|
279
276
|
if content is None:
|
|
280
277
|
raise ApplyPatchError(
|
|
281
|
-
f"apply_patch verification failed: Failed to read {self._display_path(path)}"
|
|
278
|
+
f"apply_patch verification failed: Failed to read {self._display_path(path, cwd)}"
|
|
282
279
|
)
|
|
283
280
|
return content
|
|
284
281
|
|
|
285
282
|
if not path.exists() or not path.is_file():
|
|
286
283
|
raise ApplyPatchError(
|
|
287
|
-
f"apply_patch verification failed: Failed to read {self._display_path(path)}"
|
|
284
|
+
f"apply_patch verification failed: Failed to read {self._display_path(path, cwd)}"
|
|
288
285
|
)
|
|
289
286
|
return path.read_text(encoding="utf-8", errors="replace")
|
|
290
287
|
|
|
@@ -293,6 +290,7 @@ class ApplyPatchTool(BaseTool):
|
|
|
293
290
|
path: "Path",
|
|
294
291
|
original_text: "str",
|
|
295
292
|
sections: "typing.Tuple[_UpdateSection, ...]",
|
|
293
|
+
cwd: "Path",
|
|
296
294
|
) -> "str":
|
|
297
295
|
lines = original_text.splitlines()
|
|
298
296
|
cursor = 0
|
|
@@ -308,7 +306,7 @@ class ApplyPatchTool(BaseTool):
|
|
|
308
306
|
if match_index is None:
|
|
309
307
|
raise ApplyPatchError(
|
|
310
308
|
"apply_patch verification failed: Failed to find expected lines in "
|
|
311
|
-
f"{self._display_path(path)}"
|
|
309
|
+
f"{self._display_path(path, cwd)}"
|
|
312
310
|
)
|
|
313
311
|
lines[match_index : match_index + len(old_block)] = new_block
|
|
314
312
|
cursor = match_index + len(new_block)
|
|
@@ -350,10 +348,10 @@ class ApplyPatchTool(BaseTool):
|
|
|
350
348
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
351
349
|
path.write_text(content, encoding="utf-8")
|
|
352
350
|
|
|
353
|
-
def _format_success(self, summaries: "typing.Dict[Path, str]") -> "str":
|
|
351
|
+
def _format_success(self, summaries: "typing.Dict[Path, str]", cwd: "Path") -> "str":
|
|
354
352
|
buckets = {"A": [], "M": [], "D": []}
|
|
355
353
|
for path, status in summaries.items():
|
|
356
|
-
buckets[status].append(self._display_path(path))
|
|
354
|
+
buckets[status].append(self._display_path(path, cwd))
|
|
357
355
|
lines = ["Success:"]
|
|
358
356
|
for status in ("A", "M", "D"):
|
|
359
357
|
for rel_path in sorted(buckets[status]):
|
|
@@ -365,14 +363,14 @@ class ApplyPatchTool(BaseTool):
|
|
|
365
363
|
f"Exit code: {exit_code}\n" "Wall time: 0 seconds\n" "Output:\n" f"{output}"
|
|
366
364
|
)
|
|
367
365
|
|
|
368
|
-
def _resolve_workspace_path(self, path_text: "str") -> "Path":
|
|
366
|
+
def _resolve_workspace_path(self, path_text: "str", cwd: "Path") -> "Path":
|
|
369
367
|
path = Path(path_text).expanduser()
|
|
370
|
-
resolved = path if path.is_absolute() else
|
|
368
|
+
resolved = path if path.is_absolute() else cwd / path
|
|
371
369
|
return resolved.resolve()
|
|
372
370
|
|
|
373
|
-
def _display_path(self, path: "Path") -> "str":
|
|
371
|
+
def _display_path(self, path: "Path", cwd: "Path") -> "str":
|
|
374
372
|
try:
|
|
375
|
-
return path.relative_to(
|
|
373
|
+
return path.relative_to(cwd).as_posix()
|
|
376
374
|
except ValueError:
|
|
377
375
|
return str(path)
|
|
378
376
|
|
pycodex/tools/base_tool.py
CHANGED
|
@@ -16,6 +16,7 @@ import traceback
|
|
|
16
16
|
import typing
|
|
17
17
|
from abc import ABC, abstractmethod
|
|
18
18
|
from dataclasses import dataclass
|
|
19
|
+
from pathlib import Path
|
|
19
20
|
|
|
20
21
|
from ..events import Event
|
|
21
22
|
from ..protocol import (
|
|
@@ -40,6 +41,10 @@ if typing.TYPE_CHECKING:
|
|
|
40
41
|
class ToolContext:
|
|
41
42
|
turn_id: "str"
|
|
42
43
|
history: "typing.Tuple[ConversationItem, ...]"
|
|
44
|
+
cwd: "Path"
|
|
45
|
+
|
|
46
|
+
def resolve_path(self, path=None) -> "Path":
|
|
47
|
+
return (self.cwd / Path("." if path is None else path).expanduser()).resolve()
|
|
43
48
|
|
|
44
49
|
|
|
45
50
|
class StructuredToolOutput:
|
|
@@ -60,13 +60,8 @@ class ParsedExecSource:
|
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
class CodeModeManager:
|
|
63
|
-
def __init__(
|
|
64
|
-
self,
|
|
65
|
-
registry: "ToolRegistry",
|
|
66
|
-
cwd: "typing.Union[typing.Union[str, Path], None]" = None,
|
|
67
|
-
) -> "None":
|
|
63
|
+
def __init__(self, registry: "ToolRegistry") -> "None":
|
|
68
64
|
self._registry = registry
|
|
69
|
-
self._default_cwd = Path(cwd or Path.cwd()).resolve()
|
|
70
65
|
self._runtime_script = Path(__file__).with_name("exec_runtime.js")
|
|
71
66
|
self._stored_values: "typing.Dict[str, JSONValue]" = {}
|
|
72
67
|
self._cells: "typing.Dict[str, ExecCell]" = {}
|
|
@@ -130,7 +125,7 @@ class CodeModeManager:
|
|
|
130
125
|
process = await asyncio.create_subprocess_exec(
|
|
131
126
|
"node",
|
|
132
127
|
str(self._runtime_script),
|
|
133
|
-
cwd=str(
|
|
128
|
+
cwd=str(context.cwd),
|
|
134
129
|
stdin=asyncio.subprocess.PIPE,
|
|
135
130
|
stdout=asyncio.subprocess.PIPE,
|
|
136
131
|
stderr=asyncio.subprocess.PIPE,
|
|
@@ -153,7 +148,7 @@ class CodeModeManager:
|
|
|
153
148
|
"tools": self.enabled_tools(),
|
|
154
149
|
},
|
|
155
150
|
)
|
|
156
|
-
logger.debug("exec start cell_id={} cwd={}", cell_id,
|
|
151
|
+
logger.debug("exec start cell_id={} cwd={}", cell_id, context.cwd)
|
|
157
152
|
return cell
|
|
158
153
|
|
|
159
154
|
async def _read_stdout(self, cell: "ExecCell", context: "ToolContext") -> "None":
|
|
@@ -278,7 +273,7 @@ class CodeModeManager:
|
|
|
278
273
|
arguments=message.get("arguments"),
|
|
279
274
|
tool_type=tool.tool_type,
|
|
280
275
|
),
|
|
281
|
-
|
|
276
|
+
context,
|
|
282
277
|
)
|
|
283
278
|
if result.is_error:
|
|
284
279
|
payload = {
|
|
@@ -87,14 +87,13 @@ class ExecCommandTool(BaseTool):
|
|
|
87
87
|
return self._manager.running_session_count()
|
|
88
88
|
|
|
89
89
|
async def run(self, context: "ToolContext", args: "JSONDict") -> "JSONValue":
|
|
90
|
-
del context
|
|
91
90
|
cmd = str(args.get("cmd", "")).strip()
|
|
92
91
|
if not cmd:
|
|
93
92
|
return "Error: `cmd` is required."
|
|
94
93
|
|
|
95
94
|
return await self._manager.exec_command(
|
|
96
95
|
cmd=cmd,
|
|
97
|
-
|
|
96
|
+
cwd=context.resolve_path(args.get("workdir")),
|
|
98
97
|
shell=self._optional_string(args, "shell"),
|
|
99
98
|
login=bool(args.get("login", DEFAULT_LOGIN)),
|
|
100
99
|
tty=bool(args.get("tty", DEFAULT_TTY)),
|
pycodex/tools/grep_files_tool.py
CHANGED
|
@@ -53,18 +53,12 @@ class GrepFilesTool(BaseTool):
|
|
|
53
53
|
"additionalProperties": False,
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
def __init__(
|
|
57
|
-
self, cwd: "typing.Union[typing.Union[str, Path], None]" = None
|
|
58
|
-
) -> "None":
|
|
59
|
-
self._working_directory = Path(cwd or Path.cwd()).resolve()
|
|
60
|
-
|
|
61
56
|
async def run(self, context: "ToolContext", args: "JSONDict") -> "JSONValue":
|
|
62
|
-
del context
|
|
63
57
|
pattern = str(args.get("pattern", "")).strip()
|
|
64
58
|
include = str(args.get("include", "")).strip() or None
|
|
65
59
|
limit = min(int(args.get("limit", DEFAULT_LIMIT)), MAX_LIMIT)
|
|
66
60
|
path_arg = args.get("path")
|
|
67
|
-
search_path =
|
|
61
|
+
search_path = context.resolve_path(path_arg)
|
|
68
62
|
|
|
69
63
|
if not pattern:
|
|
70
64
|
return "Error: `pattern` must not be empty."
|
|
@@ -88,7 +82,7 @@ class GrepFilesTool(BaseTool):
|
|
|
88
82
|
try:
|
|
89
83
|
process = await asyncio.create_subprocess_exec(
|
|
90
84
|
*command,
|
|
91
|
-
cwd=str(
|
|
85
|
+
cwd=str(context.cwd),
|
|
92
86
|
stdout=asyncio.subprocess.PIPE,
|
|
93
87
|
stderr=asyncio.subprocess.PIPE,
|
|
94
88
|
)
|
|
@@ -154,11 +148,3 @@ class GrepFilesTool(BaseTool):
|
|
|
154
148
|
|
|
155
149
|
matches.sort(key=lambda path: path.stat().st_mtime, reverse=True)
|
|
156
150
|
return [str(path) for path in matches[:limit]]
|
|
157
|
-
|
|
158
|
-
def _resolve_path(self, path_arg) -> "Path":
|
|
159
|
-
if path_arg in (None, ""):
|
|
160
|
-
return self._working_directory
|
|
161
|
-
path = Path(str(path_arg))
|
|
162
|
-
if not path.is_absolute():
|
|
163
|
-
path = self._working_directory / path
|
|
164
|
-
return path.resolve()
|
|
@@ -12,8 +12,6 @@ Expected behavior:
|
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
14
|
import asyncio
|
|
15
|
-
import typing
|
|
16
|
-
from pathlib import Path
|
|
17
15
|
|
|
18
16
|
from ..protocol import JSONDict, JSONValue
|
|
19
17
|
from .base_tool import BaseTool, ToolContext
|
|
@@ -54,13 +52,7 @@ class ShellCommandTool(BaseTool):
|
|
|
54
52
|
}
|
|
55
53
|
supports_parallel = False
|
|
56
54
|
|
|
57
|
-
def __init__(
|
|
58
|
-
self, cwd: "typing.Union[typing.Union[str, Path], None]" = None
|
|
59
|
-
) -> "None":
|
|
60
|
-
self._working_directory = Path(cwd or Path.cwd()).resolve()
|
|
61
|
-
|
|
62
55
|
async def run(self, context: "ToolContext", args: "JSONDict") -> "JSONValue":
|
|
63
|
-
del context
|
|
64
56
|
command = str(args.get("command", "")).strip()
|
|
65
57
|
timeout_ms = int(args.get("timeout_ms", DEFAULT_SHELL_TIMEOUT_MS))
|
|
66
58
|
if not command:
|
|
@@ -68,7 +60,7 @@ class ShellCommandTool(BaseTool):
|
|
|
68
60
|
|
|
69
61
|
login = bool(args.get("login", True))
|
|
70
62
|
workdir_arg = args.get("workdir")
|
|
71
|
-
working_directory =
|
|
63
|
+
working_directory = context.resolve_path(workdir_arg)
|
|
72
64
|
shell_args = ["bash", "-lc" if login else "-c", command]
|
|
73
65
|
|
|
74
66
|
process = await asyncio.create_subprocess_exec(
|
|
@@ -111,14 +103,6 @@ class ShellCommandTool(BaseTool):
|
|
|
111
103
|
|
|
112
104
|
return "\n".join(pieces)
|
|
113
105
|
|
|
114
|
-
def _resolve_workdir(self, workdir_arg) -> "Path":
|
|
115
|
-
if workdir_arg in (None, ""):
|
|
116
|
-
return self._working_directory
|
|
117
|
-
workdir = Path(str(workdir_arg))
|
|
118
|
-
if not workdir.is_absolute():
|
|
119
|
-
workdir = self._working_directory / workdir
|
|
120
|
-
return workdir.resolve()
|
|
121
|
-
|
|
122
106
|
def _clip_output(self, text: "str") -> "str":
|
|
123
107
|
if len(text) <= MAX_OUTPUT_CHARS:
|
|
124
108
|
return text
|
pycodex/tools/shell_tool.py
CHANGED
|
@@ -12,8 +12,6 @@ Expected behavior:
|
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
14
|
import asyncio
|
|
15
|
-
import typing
|
|
16
|
-
from pathlib import Path
|
|
17
15
|
|
|
18
16
|
from ..protocol import JSONDict, JSONValue
|
|
19
17
|
from .base_tool import BaseTool, ToolContext
|
|
@@ -51,13 +49,7 @@ class ShellTool(BaseTool):
|
|
|
51
49
|
}
|
|
52
50
|
supports_parallel = False
|
|
53
51
|
|
|
54
|
-
def __init__(
|
|
55
|
-
self, cwd: "typing.Union[typing.Union[str, Path], None]" = None
|
|
56
|
-
) -> "None":
|
|
57
|
-
self._working_directory = Path(cwd or Path.cwd()).resolve()
|
|
58
|
-
|
|
59
52
|
async def run(self, context: "ToolContext", args: "JSONDict") -> "JSONValue":
|
|
60
|
-
del context
|
|
61
53
|
command = args.get("command")
|
|
62
54
|
timeout_ms = int(args.get("timeout_ms", DEFAULT_SHELL_TIMEOUT_MS))
|
|
63
55
|
if not isinstance(command, list) or not command:
|
|
@@ -66,7 +58,7 @@ class ShellTool(BaseTool):
|
|
|
66
58
|
return "Error: each `command` entry must be a non-empty string."
|
|
67
59
|
|
|
68
60
|
workdir_arg = args.get("workdir")
|
|
69
|
-
working_directory =
|
|
61
|
+
working_directory = context.resolve_path(workdir_arg)
|
|
70
62
|
|
|
71
63
|
process = await asyncio.create_subprocess_exec(
|
|
72
64
|
*command,
|
|
@@ -108,14 +100,6 @@ class ShellTool(BaseTool):
|
|
|
108
100
|
|
|
109
101
|
return "\n".join(pieces)
|
|
110
102
|
|
|
111
|
-
def _resolve_workdir(self, workdir_arg) -> "Path":
|
|
112
|
-
if workdir_arg in (None, ""):
|
|
113
|
-
return self._working_directory
|
|
114
|
-
workdir = Path(str(workdir_arg))
|
|
115
|
-
if not workdir.is_absolute():
|
|
116
|
-
workdir = self._working_directory / workdir
|
|
117
|
-
return workdir.resolve()
|
|
118
|
-
|
|
119
103
|
def _clip_output(self, text: "str") -> "str":
|
|
120
104
|
if len(text) <= MAX_OUTPUT_CHARS:
|
|
121
105
|
return text
|
|
@@ -121,10 +121,7 @@ class UnifiedExecSession:
|
|
|
121
121
|
|
|
122
122
|
|
|
123
123
|
class UnifiedExecManager:
|
|
124
|
-
def __init__(
|
|
125
|
-
self, cwd: "typing.Union[typing.Union[str, Path], None]" = None
|
|
126
|
-
) -> "None":
|
|
127
|
-
self._default_cwd = Path(cwd or Path.cwd()).resolve()
|
|
124
|
+
def __init__(self) -> "None":
|
|
128
125
|
self._next_session_id = DEFAULT_SESSION_ID_START
|
|
129
126
|
self._sessions: "typing.Dict[int, UnifiedExecSession]" = {}
|
|
130
127
|
self._lock = threading.Lock()
|
|
@@ -146,7 +143,7 @@ class UnifiedExecManager:
|
|
|
146
143
|
async def exec_command(
|
|
147
144
|
self,
|
|
148
145
|
cmd: "str",
|
|
149
|
-
|
|
146
|
+
cwd: "Path",
|
|
150
147
|
shell: "typing.Union[str, None]" = None,
|
|
151
148
|
login: "bool" = DEFAULT_LOGIN,
|
|
152
149
|
tty: "bool" = DEFAULT_TTY,
|
|
@@ -155,7 +152,6 @@ class UnifiedExecManager:
|
|
|
155
152
|
) -> "str":
|
|
156
153
|
session_id = await self._allocate_session_id()
|
|
157
154
|
command = self._build_shell_command(cmd, shell, login)
|
|
158
|
-
cwd = self._resolve_workdir(workdir)
|
|
159
155
|
logger.debug(
|
|
160
156
|
"exec_command start session_id={} shell_command={} cwd={}",
|
|
161
157
|
session_id,
|
|
@@ -381,14 +377,6 @@ class UnifiedExecManager:
|
|
|
381
377
|
}
|
|
382
378
|
)
|
|
383
379
|
|
|
384
|
-
def _resolve_workdir(self, workdir: "typing.Union[str, None]") -> "Path":
|
|
385
|
-
if not workdir:
|
|
386
|
-
return self._default_cwd
|
|
387
|
-
path = Path(workdir)
|
|
388
|
-
if not path.is_absolute():
|
|
389
|
-
path = self._default_cwd / path
|
|
390
|
-
return path.resolve()
|
|
391
|
-
|
|
392
380
|
def _build_shell_command(
|
|
393
381
|
self,
|
|
394
382
|
cmd: "str",
|
pycodex/tools/view_image_tool.py
CHANGED
|
@@ -15,9 +15,7 @@ Expected behavior:
|
|
|
15
15
|
item that Codex uses when feeding image tool output back to the model.
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
-
import typing
|
|
19
18
|
from importlib.util import find_spec
|
|
20
|
-
from pathlib import Path
|
|
21
19
|
|
|
22
20
|
from ..protocol import JSONDict, JSONValue
|
|
23
21
|
from ..utils.image_utils import ImageProcessingError, load_image_data_url
|
|
@@ -65,13 +63,7 @@ class ViewImageTool(BaseTool):
|
|
|
65
63
|
}
|
|
66
64
|
output_schema = VIEW_IMAGE_OUTPUT_SCHEMA
|
|
67
65
|
|
|
68
|
-
def __init__(
|
|
69
|
-
self, cwd: "typing.Union[typing.Union[str, Path], None]" = None
|
|
70
|
-
) -> "None":
|
|
71
|
-
self._workspace_root = Path(cwd or Path.cwd()).resolve()
|
|
72
|
-
|
|
73
66
|
async def run(self, context: "ToolContext", args: "JSONDict") -> "JSONValue":
|
|
74
|
-
del context
|
|
75
67
|
path_value = str(args.get("path", "")).strip()
|
|
76
68
|
if not path_value:
|
|
77
69
|
return "Error: `path` is required."
|
|
@@ -88,10 +80,7 @@ class ViewImageTool(BaseTool):
|
|
|
88
80
|
f"`{detail_value}`."
|
|
89
81
|
)
|
|
90
82
|
|
|
91
|
-
path =
|
|
92
|
-
if not path.is_absolute():
|
|
93
|
-
path = self._workspace_root / path
|
|
94
|
-
path = path.resolve()
|
|
83
|
+
path = context.resolve_path(path_value)
|
|
95
84
|
if not path.exists():
|
|
96
85
|
return f"Error: unable to locate image at `{path}`."
|
|
97
86
|
if not path.is_file():
|
pycodex/utils/event_helpers.py
CHANGED
|
@@ -26,6 +26,8 @@ def format_command_result(result: "typing.Dict[str, object]") -> "str":
|
|
|
26
26
|
return "Extra commands: " + ", ".join("/" + name for name in result["commands"])
|
|
27
27
|
if kind in {"title", "title_changed"}:
|
|
28
28
|
return "Session: " + (result["title"] or "untitled")
|
|
29
|
+
if kind == "cwd":
|
|
30
|
+
return "Current directory: " + result["cwd"]
|
|
29
31
|
if kind == "models":
|
|
30
32
|
return (
|
|
31
33
|
"Current model: "
|
pycodex/utils/session_persist.py
CHANGED
|
@@ -163,6 +163,7 @@ class SessionRolloutRecorder:
|
|
|
163
163
|
def list_resumable_sessions(
|
|
164
164
|
codex_home: "Path",
|
|
165
165
|
limit: "int" = 20,
|
|
166
|
+
session_id: "typing.Union[str, None]" = None,
|
|
166
167
|
) -> "typing.Tuple[typing.Dict[str, str], ...]":
|
|
167
168
|
latest_rollouts_by_id: "typing.Dict[str, Path]" = {}
|
|
168
169
|
for dirname in ROLLUP_SESSION_DIRNAMES:
|
|
@@ -173,6 +174,8 @@ def list_resumable_sessions(
|
|
|
173
174
|
thread_id = _thread_id_from_rollout_path(path)
|
|
174
175
|
if thread_id is None:
|
|
175
176
|
continue
|
|
177
|
+
if session_id is not None and thread_id.lower() != session_id.lower():
|
|
178
|
+
continue
|
|
176
179
|
previous = latest_rollouts_by_id.get(thread_id)
|
|
177
180
|
if previous is None or path.stat().st_mtime > previous.stat().st_mtime:
|
|
178
181
|
latest_rollouts_by_id[thread_id] = path
|
|
@@ -202,11 +205,16 @@ def list_resumable_sessions(
|
|
|
202
205
|
|
|
203
206
|
def select_resumable_session(
|
|
204
207
|
codex_home: "Path",
|
|
205
|
-
|
|
208
|
+
resume_target: "str",
|
|
206
209
|
) -> "typing.Dict[str, str]":
|
|
207
|
-
normalized_target =
|
|
210
|
+
normalized_target = resume_target.strip()
|
|
211
|
+
if UUID_PATTERN.fullmatch(normalized_target):
|
|
212
|
+
sessions = list_resumable_sessions(codex_home, 1, normalized_target)
|
|
213
|
+
if not sessions:
|
|
214
|
+
raise ValueError(f"Session not found: {normalized_target}")
|
|
215
|
+
return sessions[0]
|
|
208
216
|
if not normalized_target.isdigit():
|
|
209
|
-
raise ValueError("Usage: /resume <number>")
|
|
217
|
+
raise ValueError("Usage: /resume <number|session_id>")
|
|
210
218
|
|
|
211
219
|
sessions = list_resumable_sessions(codex_home)
|
|
212
220
|
resume_index = int(normalized_target)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-codex
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.8
|
|
4
4
|
Summary: A minimal Python extraction of Codex's main agent loop
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Python: >=3.6.2
|
|
@@ -378,17 +378,22 @@ Current behavior:
|
|
|
378
378
|
tool execution and model follow-up after tool results
|
|
379
379
|
- startup shows a welcome, model, effort, endpoint and available commands.
|
|
380
380
|
- assistant text is printed from streaming deltas directly
|
|
381
|
-
- interactive mode supports `/history`, `/title`, `/model`, `/effort`, `/resume`,
|
|
381
|
+
- interactive mode supports `/history`, `/title`, `/cd`, `/model`, `/effort`, `/resume`,
|
|
382
382
|
`/compact`, and `/fork`
|
|
383
383
|
- `/model <name>` switches the model used by later turns in the current
|
|
384
384
|
interactive session; `/model` shows the current model and available choices
|
|
385
|
+
- `/cd <directory>` changes the current session's working directory while idle;
|
|
386
|
+
`/cd` shows it. Relative paths and `~` are supported; paths containing spaces
|
|
387
|
+
can be entered directly without quotes. Later tools and context use the new directory.
|
|
385
388
|
- `/effort low` switches effort while idle; `/effort` shows the current value,
|
|
386
389
|
and `/effort default` restores the model default. Frontend labels show
|
|
387
390
|
`model:effort`.
|
|
388
391
|
- `/resume` with no argument lists the currently resumable sessions by their
|
|
389
392
|
first user-message preview; `/resume 1` resumes the first listed session
|
|
390
|
-
- `/resume <
|
|
391
|
-
|
|
393
|
+
- `/resume <session_id>` accepts a full session UUID, including sessions older
|
|
394
|
+
than the displayed list. Both forms search `CODEX_HOME/sessions` and
|
|
395
|
+
`CODEX_HOME/archived_sessions` and replace the in-memory history with the
|
|
396
|
+
selected recorded Codex rollout. Tool calls without recorded results
|
|
392
397
|
are omitted during restore; later saved messages and completed tools remain
|
|
393
398
|
available, and the source file is left intact.
|
|
394
399
|
- `/compact` synthesizes a local handoff summary, replaces the in-memory
|
|
@@ -452,6 +457,11 @@ Current behavior:
|
|
|
452
457
|
original state paths and are saved in the new format. See the
|
|
453
458
|
[workspace configuration example](packages/pycodex-ws/README.md).
|
|
454
459
|
Add/delete actions and later session-state saves refresh the JSON file.
|
|
460
|
+
Each session starts in `work_dir` and can switch independently with `/cd`.
|
|
461
|
+
A small folder indicator appears inside the composer only when the session
|
|
462
|
+
directory differs from `work_dir`; hover for the full path. Directories persist
|
|
463
|
+
across workspace restarts, including for empty sessions. Relative chat/Canvas file links use the selected
|
|
464
|
+
session's directory; Canvas and workspace state files stay at their configured paths.
|
|
455
465
|
Board HTML can reference local
|
|
456
466
|
images beside the board (including nested paths) with relative URLs; only
|
|
457
467
|
`image/*` files contained by the board directory are served.
|
|
@@ -811,6 +821,9 @@ event JSON format.
|
|
|
811
821
|
uses `agent.tool_registry.runtime_environment`, not a forwarding Agent property.
|
|
812
822
|
The global `get_agent_runtime_environment()` and
|
|
813
823
|
separate `Agent(..., runtime_environment=...)` entry points are removed.
|
|
824
|
+
- Configure cwd once with `ContextConfig(cwd=...)` or `build_agent(cwd=...)`.
|
|
825
|
+
Tools use the required `ToolContext.cwd` for each call; tool constructors
|
|
826
|
+
and `get_tools` do not take a separate default directory.
|
|
814
827
|
- Each sub-agent gets its own context manager and runtime services. The sub-agent
|
|
815
828
|
service retains terminal event status and queues one notification per terminal
|
|
816
829
|
event. The parent consumes these notifications before its next normal model
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
pycodex/__init__.py,sha256=fHEyhylULeRo-5xEnl-UxT5ZpBzbFzRW7vLwdy_7oxU,2660
|
|
2
2
|
pycodex/__main__.py,sha256=MHKZ_ae3fSLGTLUUMOx15fWdeOnJSHhq-zslRP5F5Lc,79
|
|
3
|
-
pycodex/agent.py,sha256=
|
|
4
|
-
pycodex/bootstrap.py,sha256=
|
|
3
|
+
pycodex/agent.py,sha256=4YtHLDL0lKpU_KWnbKTNyI5UEfBPgkI4f8lndn8yjGA,21807
|
|
4
|
+
pycodex/bootstrap.py,sha256=wUNipqvIPzJlcP3PTY0cCyPH_QTeIbSR8xtyw4AEP6Y,12282
|
|
5
5
|
pycodex/cli.py,sha256=uCrRARI2IypIU-1aC0Y2DLzhzYGWQta1f7zYWsyaNnU,12797
|
|
6
|
-
pycodex/context.py,sha256=
|
|
6
|
+
pycodex/context.py,sha256=8P3fBXbwzTKmoXGDdkzTsAghfOkAZstJnMTqr-cxHyI,27019
|
|
7
7
|
pycodex/events.py,sha256=L7RkZ3EtFQOfWg1FIG2Y0mLpDKTKV6HGmXMy9HCoWX0,29794
|
|
8
8
|
pycodex/model.py,sha256=VwPN9GEdoL8N8V4hFdMmJ1rGxL_tom3Nq7ZBonCum2k,45648
|
|
9
9
|
pycodex/protocol.py,sha256=uzUz_06iA4m9vAoqZeCGiJ4SocKZDzOfCJUU4Bpt5dI,12033
|
|
10
|
-
pycodex/runtime.py,sha256=
|
|
10
|
+
pycodex/runtime.py,sha256=kEW4dLGcTX0nRArDpWhwm62c1il_1O-AgRHvsQqSYBY,24661
|
|
11
11
|
pycodex/portable/__init__.py,sha256=FmspRPmblYuZIVXMpXaxE1uosCaz2hZDpkpOzxDJQic,1084
|
|
12
12
|
pycodex/portable/client.py,sha256=DOxzZljVfN1ynmhqrChX7NsT1IFSFVKzEn1J3wvV3XQ,16038
|
|
13
13
|
pycodex/portable/server.py,sha256=w4YD71Sxb7MD5q57GPuIgx7AoicTv0R9xzX2kAaibPA,7503
|
|
@@ -23,15 +23,15 @@ pycodex/prompts/permissions/sandbox_mode/read_only.md,sha256=2rAPEXsBYCcuttI5j3e
|
|
|
23
23
|
pycodex/prompts/permissions/sandbox_mode/workspace_write.md,sha256=lVN-LwrBbHqlv5yVjcd_mU8tzZW8jfKpTatJKIZu9HI,277
|
|
24
24
|
pycodex/tools/__init__.py,sha256=UYwmsX_Ckw_YaYQ6TYhUU6w5pxi2qzZoTrq6BAgNErM,1857
|
|
25
25
|
pycodex/tools/agent_tool_schemas.py,sha256=8cL9ml4So0H6IIeK0ZzjAo9bk3VeprDOSYPpIamOqEU,2048
|
|
26
|
-
pycodex/tools/apply_patch_tool.py,sha256=
|
|
27
|
-
pycodex/tools/base_tool.py,sha256=
|
|
26
|
+
pycodex/tools/apply_patch_tool.py,sha256=n30M-ofZlOkDCC18nX10MPF5FLROiURACFLG1hd4FK8,13906
|
|
27
|
+
pycodex/tools/base_tool.py,sha256=NtzDTCudWIdwjhfgk6kd98C5KVl3PrruymOOURLApSA,6655
|
|
28
28
|
pycodex/tools/clock_tool.py,sha256=2_RYbBEw4V9_f1L-qZl3hhJ6XNPM_gBDmOlCh1cCAjo,6575
|
|
29
29
|
pycodex/tools/close_agent_tool.py,sha256=uIbCjsQx9MLkjVYheS8ZVf5VVPbC6Iviwox4pneJpmI,2030
|
|
30
|
-
pycodex/tools/code_mode_manager.py,sha256=
|
|
31
|
-
pycodex/tools/exec_command_tool.py,sha256=
|
|
30
|
+
pycodex/tools/code_mode_manager.py,sha256=oEzYkebFdgX993NBC0A4R0mthg4qdhJ-dD0fgeE3III,18948
|
|
31
|
+
pycodex/tools/exec_command_tool.py,sha256=EvlV355-i9trrKTAXDFiZyYMviTxL_FehhKlQmjuDpw,4443
|
|
32
32
|
pycodex/tools/exec_runtime.js,sha256=DR1uocKailTqNWAcJNFJuQgFFMSUzTpT_uQsRaneg2s,3643
|
|
33
33
|
pycodex/tools/exec_tool.py,sha256=tuC21rlhP8CPSYml4XnSDSRV0UPGkQ6aHj0DLIoOOvM,2274
|
|
34
|
-
pycodex/tools/grep_files_tool.py,sha256=
|
|
34
|
+
pycodex/tools/grep_files_tool.py,sha256=KCBVNaSTU45ND-iAhh14YawMyjQnMRZnGI1NGKL3zgg,4809
|
|
35
35
|
pycodex/tools/list_dir_tool.py,sha256=_U6ouRcVbGHT5mChVyUsiMgDGrFK7EFMFaspPLAHtH8,5337
|
|
36
36
|
pycodex/tools/read_file_tool.py,sha256=YK__uhIVldBjAQega5S_ZuZU5jgP_kLOwr-YhjAmRo4,9735
|
|
37
37
|
pycodex/tools/request_permissions_tool.py,sha256=nEt3_uw3lmFXyydqcN3PxuKwrEvaRPtfjA_U8nikCtU,3978
|
|
@@ -39,12 +39,12 @@ pycodex/tools/request_user_input_tool.py,sha256=_7siHF2p3KiNDFwsTqKKIUw2kPCXMzLa
|
|
|
39
39
|
pycodex/tools/resume_agent_tool.py,sha256=bZvXEUNdc1RVD2N5Msjv63Rni62FQk4gj9LAXK1Vsy8,1613
|
|
40
40
|
pycodex/tools/runtime_services.py,sha256=KPLtB1e7jmqEN9rhXV70rUMlhYNFsXja4CypIunzXLU,15962
|
|
41
41
|
pycodex/tools/send_input_tool.py,sha256=w63SKM05_e3oXvdaBtL-hk941TvkWKuTUDEqTSyWEMQ,3761
|
|
42
|
-
pycodex/tools/shell_command_tool.py,sha256=
|
|
43
|
-
pycodex/tools/shell_tool.py,sha256=
|
|
42
|
+
pycodex/tools/shell_command_tool.py,sha256=bJKlTdIs2i28vH5f4GZfpunezr6JNicRzsL_6Vc3HvU,3675
|
|
43
|
+
pycodex/tools/shell_tool.py,sha256=I6lYLABSmLddRrNmpk0Li83AV55MI8x0bckWaRkFGI0,3527
|
|
44
44
|
pycodex/tools/spawn_agent_tool.py,sha256=Jx5jYpxsH_CQ3BLkXYPZuIINW5deO_zY8tyw8kZpreY,6208
|
|
45
|
-
pycodex/tools/unified_exec_manager.py,sha256=
|
|
45
|
+
pycodex/tools/unified_exec_manager.py,sha256=n6N9Sf125Tt4Vdl8C0lqng8tQbS7Dj04uZnDVqgGkxw,14056
|
|
46
46
|
pycodex/tools/update_plan_tool.py,sha256=xwrSrerw5qfkbFc9Ltx1OZEmD__CWtryOy3UhL0-Jp4,3198
|
|
47
|
-
pycodex/tools/view_image_tool.py,sha256=
|
|
47
|
+
pycodex/tools/view_image_tool.py,sha256=H7OP-QZEU5Gf-E4YnQbXfu2hDkL5-dJ3Apla2YYjErQ,3754
|
|
48
48
|
pycodex/tools/wait_agent_tool.py,sha256=pLsnk9TxSt4v7krC7s7vxi91pLisSNU0lATt9s-P_3E,3134
|
|
49
49
|
pycodex/tools/wait_tool.py,sha256=0_OO_DvMsBhG3oDKZePTOjZTBq1lmWntXAzqIqvebdA,3206
|
|
50
50
|
pycodex/tools/web_search_tool.py,sha256=3ao1IvJw7MwcP35UKLbqrM-nlm5Z-UUFVdEzkJYOV1o,988
|
|
@@ -57,12 +57,12 @@ pycodex/utils/crypto.py,sha256=HRCXF26Ft3S8CG2X0YpZwa8GMU9YJ0Loo8Q9PMPMcNw,1249
|
|
|
57
57
|
pycodex/utils/debug.py,sha256=5XMpWpwFSm6woyx6RGXvN2qL0afRtTQbwlOAv-LotTw,913
|
|
58
58
|
pycodex/utils/doctor.py,sha256=fI66oQqB5zNL7aEpFbiHnzhELyp9XydwMyAGznw5YSE,10616
|
|
59
59
|
pycodex/utils/dotenv.py,sha256=h7ZgvqpEswetqjhtwfiaHyokAHwNKPakizTAHPHz5Ds,1865
|
|
60
|
-
pycodex/utils/event_helpers.py,sha256=
|
|
60
|
+
pycodex/utils/event_helpers.py,sha256=ar4WoIkpiShoayN_GZlnDijy74CC2Z8_5p76EryduNk,6517
|
|
61
61
|
pycodex/utils/get_env.py,sha256=L4S8h84tkgxZKvrxY7ezTgePbXepG-yIigUTKeY6uuE,6094
|
|
62
62
|
pycodex/utils/image_utils.py,sha256=OUzUs0Cow4L8Z2np0EBeEUotAqqsCmRmhrV8AxZUXRw,2460
|
|
63
63
|
pycodex/utils/model_metadata.py,sha256=uK3iI4yC7Ge7yN4I1_gQYalQ4MYKddz2qMwsmQb-P80,1337
|
|
64
64
|
pycodex/utils/random_ids.py,sha256=jLTxZZ0MBbRdQOFDFWGVRjWzIo6FKSQ5ADXbQSLPp1s,379
|
|
65
|
-
pycodex/utils/session_persist.py,sha256=
|
|
65
|
+
pycodex/utils/session_persist.py,sha256=P-tsYdb1r0Ca_mVgTcK24bZM09zR-tNFgoFXlVIn5Q0,24012
|
|
66
66
|
pycodex/utils/truncation.py,sha256=5D6vAorT8EgXgjd7ASwtraAyIZdlCR-w5jKZqJxDzho,5446
|
|
67
67
|
pycodex/utils/_aes_gcm/LICENSE,sha256=KvdHagY3sYIK34GldJaRi34sNnGwMVJIIoDhbC9MRu8,2805
|
|
68
68
|
pycodex/utils/_aes_gcm/README.md,sha256=D9XGeewCgU2PVqJCO6WusUs0Mw2FbMnABCqaVktqCnE,3045
|
|
@@ -83,8 +83,8 @@ responses_server/trajectory_dump.py,sha256=5_Wy6tslszrqx3j1JeP_boyTbzIicKy8No84P
|
|
|
83
83
|
responses_server/tools/__init__.py,sha256=ivsBSEy0SBUhY-Uea5v1XMLXShkwHdCVl0id-1FwdZg,150
|
|
84
84
|
responses_server/tools/custom_adapter.py,sha256=SnIiNUdomff3APtTriURAC9YCeEdhCDjzGMEYusfUdk,8340
|
|
85
85
|
responses_server/tools/web_search.py,sha256=pnKH9JyTScRehWpZS3HpXRvjWsQvC0QZqxjEOCT4ndg,8936
|
|
86
|
-
python_codex-0.4.
|
|
87
|
-
python_codex-0.4.
|
|
88
|
-
python_codex-0.4.
|
|
89
|
-
python_codex-0.4.
|
|
90
|
-
python_codex-0.4.
|
|
86
|
+
python_codex-0.4.8.dist-info/METADATA,sha256=OXHvFKPrz7ORC-G8Z-2VkiAzoiSFAyzgvE5ifGVO67E,48951
|
|
87
|
+
python_codex-0.4.8.dist-info/WHEEL,sha256=KGYbc1zXlYddvwxnNty23BeaKzh7YuoSIvIMO4jEhvw,87
|
|
88
|
+
python_codex-0.4.8.dist-info/entry_points.txt,sha256=sNUVakoVuTrzJH505ZgRTQxmtRRPUHV_EH0i6EbYTyM,45
|
|
89
|
+
python_codex-0.4.8.dist-info/licenses/LICENSE,sha256=0X8ifk312hYAORM4hlzg8wVSEXYKNmiPgWlB1YIy2Nw,10926
|
|
90
|
+
python_codex-0.4.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|