python-codex 0.4.5__tar.gz → 0.4.7__tar.gz
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.
- {python_codex-0.4.5 → python_codex-0.4.7}/PKG-INFO +13 -7
- {python_codex-0.4.5 → python_codex-0.4.7}/README.md +12 -6
- {python_codex-0.4.5 → python_codex-0.4.7}/README_ZH.md +7 -3
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/cli.py +5 -2
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/events.py +6 -3
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/model.py +26 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/runtime.py +22 -15
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/utils/event_helpers.py +4 -2
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/utils/session_persist.py +11 -3
- {python_codex-0.4.5 → python_codex-0.4.7}/pyproject.toml +2 -2
- {python_codex-0.4.5 → python_codex-0.4.7}/.gitignore +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/LICENSE +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/__init__.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/__main__.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/agent.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/bootstrap.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/context.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/portable/__init__.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/portable/client.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/portable/server.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/prompts/default_base_instructions.md +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/prompts/models.json +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/prompts/permissions/approval_policy/never.md +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/prompts/permissions/approval_policy/on_failure.md +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/prompts/permissions/approval_policy/on_request.md +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/prompts/permissions/approval_policy/on_request_rule_request_permission.md +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/prompts/permissions/approval_policy/unless_trusted.md +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/prompts/permissions/sandbox_mode/danger_full_access.md +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/prompts/permissions/sandbox_mode/read_only.md +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/prompts/permissions/sandbox_mode/workspace_write.md +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/protocol.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/__init__.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/agent_tool_schemas.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/apply_patch_tool.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/base_tool.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/clock_tool.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/close_agent_tool.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/code_mode_manager.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/exec_command_tool.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/exec_runtime.js +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/exec_tool.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/grep_files_tool.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/list_dir_tool.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/read_file_tool.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/request_permissions_tool.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/request_user_input_tool.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/resume_agent_tool.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/runtime_services.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/send_input_tool.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/shell_command_tool.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/shell_tool.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/spawn_agent_tool.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/unified_exec_manager.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/update_plan_tool.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/view_image_tool.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/wait_agent_tool.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/wait_tool.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/web_search_tool.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/tools/write_stdin_tool.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/utils/__init__.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/utils/_aes_gcm/LICENSE +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/utils/_aes_gcm/README.md +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/utils/_aes_gcm/__init__.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/utils/_aes_gcm/aes.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/utils/_aes_gcm/gcm.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/utils/_aes_gcm/source-sha256.json +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/utils/async_bridge.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/utils/compactor.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/utils/compat.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/utils/crypto.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/utils/debug.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/utils/doctor.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/utils/dotenv.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/utils/get_env.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/utils/image_utils.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/utils/model_metadata.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/utils/random_ids.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/pycodex/utils/truncation.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/responses_server/__init__.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/responses_server/__main__.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/responses_server/app.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/responses_server/config.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/responses_server/messages_api.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/responses_server/payload_processors.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/responses_server/server.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/responses_server/session_store.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/responses_server/stream_router.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/responses_server/tools/__init__.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/responses_server/tools/custom_adapter.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/responses_server/tools/web_search.py +0 -0
- {python_codex-0.4.5 → python_codex-0.4.7}/responses_server/trajectory_dump.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-codex
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.7
|
|
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
|
|
@@ -369,22 +369,28 @@ Current behavior:
|
|
|
369
369
|
Open views survive reloads and server restarts. See
|
|
370
370
|
[workspace content views](docs/RUNTIME.md#workspace-content-views) for the
|
|
371
371
|
complete syntax and limits.
|
|
372
|
-
- interactive mode exits through
|
|
373
|
-
|
|
372
|
+
- interactive mode exits through Ctrl+D on an empty prompt or a single Ctrl+C;
|
|
373
|
+
accepted work finishes before cleanup and normal exit.
|
|
374
374
|
A `[closing]` message explains the wait. Press Ctrl+C again while closing
|
|
375
375
|
to immediately exit the CLI with status 130. This skips remaining cleanup;
|
|
376
376
|
external tool processes may keep running.
|
|
377
377
|
- interactive mode shows a compact event stream for user-visible phases such as
|
|
378
378
|
tool execution and model follow-up after tool results
|
|
379
|
+
- startup shows a welcome, model, effort, endpoint and available commands.
|
|
379
380
|
- assistant text is printed from streaming deltas directly
|
|
380
|
-
- interactive mode supports `/history`, `/title`, `/model`, `/
|
|
381
|
-
and `/fork`
|
|
381
|
+
- interactive mode supports `/history`, `/title`, `/model`, `/effort`, `/resume`,
|
|
382
|
+
`/compact`, and `/fork`
|
|
382
383
|
- `/model <name>` switches the model used by later turns in the current
|
|
383
384
|
interactive session; `/model` shows the current model and available choices
|
|
385
|
+
- `/effort low` switches effort while idle; `/effort` shows the current value,
|
|
386
|
+
and `/effort default` restores the model default. Frontend labels show
|
|
387
|
+
`model:effort`.
|
|
384
388
|
- `/resume` with no argument lists the currently resumable sessions by their
|
|
385
389
|
first user-message preview; `/resume 1` resumes the first listed session
|
|
386
|
-
- `/resume <
|
|
387
|
-
|
|
390
|
+
- `/resume <session_id>` accepts a full session UUID, including sessions older
|
|
391
|
+
than the displayed list. Both forms search `CODEX_HOME/sessions` and
|
|
392
|
+
`CODEX_HOME/archived_sessions` and replace the in-memory history with the
|
|
393
|
+
selected recorded Codex rollout. Tool calls without recorded results
|
|
388
394
|
are omitted during restore; later saved messages and completed tools remain
|
|
389
395
|
available, and the source file is left intact.
|
|
390
396
|
- `/compact` synthesizes a local handoff summary, replaces the in-memory
|
|
@@ -349,22 +349,28 @@ Current behavior:
|
|
|
349
349
|
Open views survive reloads and server restarts. See
|
|
350
350
|
[workspace content views](docs/RUNTIME.md#workspace-content-views) for the
|
|
351
351
|
complete syntax and limits.
|
|
352
|
-
- interactive mode exits through
|
|
353
|
-
|
|
352
|
+
- interactive mode exits through Ctrl+D on an empty prompt or a single Ctrl+C;
|
|
353
|
+
accepted work finishes before cleanup and normal exit.
|
|
354
354
|
A `[closing]` message explains the wait. Press Ctrl+C again while closing
|
|
355
355
|
to immediately exit the CLI with status 130. This skips remaining cleanup;
|
|
356
356
|
external tool processes may keep running.
|
|
357
357
|
- interactive mode shows a compact event stream for user-visible phases such as
|
|
358
358
|
tool execution and model follow-up after tool results
|
|
359
|
+
- startup shows a welcome, model, effort, endpoint and available commands.
|
|
359
360
|
- assistant text is printed from streaming deltas directly
|
|
360
|
-
- interactive mode supports `/history`, `/title`, `/model`, `/
|
|
361
|
-
and `/fork`
|
|
361
|
+
- interactive mode supports `/history`, `/title`, `/model`, `/effort`, `/resume`,
|
|
362
|
+
`/compact`, and `/fork`
|
|
362
363
|
- `/model <name>` switches the model used by later turns in the current
|
|
363
364
|
interactive session; `/model` shows the current model and available choices
|
|
365
|
+
- `/effort low` switches effort while idle; `/effort` shows the current value,
|
|
366
|
+
and `/effort default` restores the model default. Frontend labels show
|
|
367
|
+
`model:effort`.
|
|
364
368
|
- `/resume` with no argument lists the currently resumable sessions by their
|
|
365
369
|
first user-message preview; `/resume 1` resumes the first listed session
|
|
366
|
-
- `/resume <
|
|
367
|
-
|
|
370
|
+
- `/resume <session_id>` accepts a full session UUID, including sessions older
|
|
371
|
+
than the displayed list. Both forms search `CODEX_HOME/sessions` and
|
|
372
|
+
`CODEX_HOME/archived_sessions` and replace the in-memory history with the
|
|
373
|
+
selected recorded Codex rollout. Tool calls without recorded results
|
|
368
374
|
are omitted during restore; later saved messages and completed tools remain
|
|
369
375
|
available, and the source file is left intact.
|
|
370
376
|
- `/compact` synthesizes a local handoff summary, replaces the in-memory
|
|
@@ -304,15 +304,19 @@ pycodex doctor
|
|
|
304
304
|
|
|
305
305
|
- 没有 argv prompt 且 stdin 是 TTY 时,进入交互模式
|
|
306
306
|
- 有 argv prompt 或 stdin 管道输入时,执行单轮请求
|
|
307
|
-
-
|
|
307
|
+
- 交互模式通过空提示符上的 Ctrl+D 或一次 Ctrl+C 正常退出;
|
|
308
308
|
`[closing]` 提示表示正在等待已接收的工作结束并清理资源,不取消模型或工具调用。
|
|
309
309
|
- 交互模式下会显示简洁阶段事件流,例如工具执行状态和模型回看工具结果
|
|
310
|
+
- 开场保留欢迎语,显示 model、effort、endpoint 和命令列表。
|
|
310
311
|
- assistant 文本会按流式 delta 直接打印
|
|
311
|
-
- 交互模式下支持 `/history`、`/title`、`/model` 和 `/resume`
|
|
312
|
+
- 交互模式下支持 `/history`、`/title`、`/model`、`/effort` 和 `/resume`
|
|
312
313
|
- `/model <name>` 会切换当前交互会话后续请求使用的模型;`/model` 会显示当前模型和可选模型
|
|
314
|
+
- `/effort low` 在空闲时切换 effort,`/effort` 查看当前值,`/effort default` 恢复模型默认值;前端模型标签显示 `model:effort`。
|
|
313
315
|
- `/resume` 不带参数时会按首条用户消息预览列出当前可恢复的 session;`/resume 1`
|
|
314
316
|
会恢复列表里的第 1 个 session
|
|
315
|
-
- `/resume
|
|
317
|
+
- `/resume <session_id>` 支持完整的会话 UUID,不受当前列表条数限制。
|
|
318
|
+
编号和 UUID 两种方式都会查找 `CODEX_HOME/sessions` 与
|
|
319
|
+
`CODEX_HOME/archived_sessions`,读取选中的已记录 Codex rollout,
|
|
316
320
|
并直接替换当前内存里的会话 history
|
|
317
321
|
- 新 session 现在会自动保存到 `CODEX_HOME/sessions/.../rollout-*.jsonl`,
|
|
318
322
|
使用稳定的 session/thread id,并按 item 级别 append + flush,和 `/resume`
|
|
@@ -19,7 +19,7 @@ from prompt_toolkit.patch_stdout import patch_stdout
|
|
|
19
19
|
|
|
20
20
|
from .bootstrap import build_agent, build_model, build_runtime, configure_loguru
|
|
21
21
|
from .events import DEFAULT_MAIN_PROMPT, EventDisplay
|
|
22
|
-
from .model import DEFAULT_CODEX_CONFIG_PATH
|
|
22
|
+
from .model import DEFAULT_CODEX_CONFIG_PATH, ModelControl
|
|
23
23
|
from .portable import bootstrap_called_home, upload_codex_home
|
|
24
24
|
from .utils import get_debug_dir
|
|
25
25
|
from .utils.event_helpers import format_error, render_result
|
|
@@ -216,7 +216,10 @@ async def run_interactive_session(runtime, json_mode, config_path=None, view=Non
|
|
|
216
216
|
if not future.cancelled() and future.exception() is None:
|
|
217
217
|
render_result("turn", future.result(), True, view.write_line)
|
|
218
218
|
|
|
219
|
-
|
|
219
|
+
client = typing.cast(ModelControl, runtime.agent.model_client)
|
|
220
|
+
view.display.start(
|
|
221
|
+
client.model, client.reasoning_effort, client.endpoint, runtime.commands()
|
|
222
|
+
)
|
|
220
223
|
with _cli_sigint_handler(runtime) as install_sigint_handler:
|
|
221
224
|
try:
|
|
222
225
|
while not view.display.closed:
|
|
@@ -48,11 +48,14 @@ class EventDisplay:
|
|
|
48
48
|
self.closed = False
|
|
49
49
|
self.set_context_window_tokens(context_window_tokens)
|
|
50
50
|
|
|
51
|
-
def start(self, commands):
|
|
51
|
+
def start(self, model, reasoning_effort, endpoint, commands):
|
|
52
|
+
self.log("Welcome to PyCodex")
|
|
52
53
|
self.log(
|
|
53
|
-
"
|
|
54
|
-
|
|
54
|
+
"model: {0} | effort: {1}".format(
|
|
55
|
+
model, reasoning_effort or "provider default"
|
|
56
|
+
)
|
|
55
57
|
)
|
|
58
|
+
self.log("endpoint: " + endpoint)
|
|
56
59
|
self.log(format_command_result({"kind": "help", "commands": commands}))
|
|
57
60
|
|
|
58
61
|
def write(self, text, kind=""):
|
|
@@ -67,6 +67,11 @@ class ModelClient(Protocol):
|
|
|
67
67
|
|
|
68
68
|
class ModelControl(Protocol):
|
|
69
69
|
model: "str"
|
|
70
|
+
reasoning_effort: "typing.Union[str, None]"
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def endpoint(self) -> "str":
|
|
74
|
+
"""Return the active API base URL, safe for display."""
|
|
70
75
|
|
|
71
76
|
async def list_models(self) -> "typing.List[str]":
|
|
72
77
|
"""List the models available to an interactive session."""
|
|
@@ -398,6 +403,27 @@ class ResponsesModelClient:
|
|
|
398
403
|
def model(self, model: "str") -> "None":
|
|
399
404
|
self._config = replace(self._config, model=model)
|
|
400
405
|
|
|
406
|
+
@property
|
|
407
|
+
def reasoning_effort(self) -> "typing.Union[str, None]":
|
|
408
|
+
"""Return the effective effort; assign None to restore the model default."""
|
|
409
|
+
return self._config.effective_reasoning_effort()
|
|
410
|
+
|
|
411
|
+
@reasoning_effort.setter
|
|
412
|
+
def reasoning_effort(self, effort: "typing.Union[str, None]") -> "None":
|
|
413
|
+
if effort is not None and (
|
|
414
|
+
not effort or any(char.isspace() for char in effort)
|
|
415
|
+
):
|
|
416
|
+
raise ValueError("reasoning effort must be a single non-empty value")
|
|
417
|
+
self._config = replace(self._config, reasoning_effort=effort)
|
|
418
|
+
|
|
419
|
+
@property
|
|
420
|
+
def endpoint(self) -> "str":
|
|
421
|
+
"""Return the active API base URL without credentials, query or fragment."""
|
|
422
|
+
url = urllib.parse.urlsplit(self._config.base_url)
|
|
423
|
+
return urllib.parse.urlunsplit(
|
|
424
|
+
(url.scheme, url.netloc.rsplit("@", 1)[-1], url.path, "", "")
|
|
425
|
+
)
|
|
426
|
+
|
|
401
427
|
@classmethod
|
|
402
428
|
def from_codex_config(
|
|
403
429
|
cls,
|
|
@@ -25,6 +25,7 @@ from .events import (
|
|
|
25
25
|
TurnInterruptedEvent,
|
|
26
26
|
TurnStartedEvent,
|
|
27
27
|
)
|
|
28
|
+
from .model import ModelControl
|
|
28
29
|
from .protocol import TurnResult
|
|
29
30
|
from .utils import uuid7_string
|
|
30
31
|
from .utils.event_helpers import shorten_title
|
|
@@ -124,12 +125,13 @@ class AgentRuntime:
|
|
|
124
125
|
"history",
|
|
125
126
|
"title",
|
|
126
127
|
"model",
|
|
128
|
+
"effort",
|
|
127
129
|
"resume",
|
|
128
130
|
"compact",
|
|
129
131
|
"fork",
|
|
130
132
|
)
|
|
131
133
|
+ tuple(self.command_handlers)
|
|
132
|
-
+ ("queue",
|
|
134
|
+
+ ("queue",)
|
|
133
135
|
)
|
|
134
136
|
|
|
135
137
|
def snapshot(self):
|
|
@@ -141,7 +143,10 @@ class AgentRuntime:
|
|
|
141
143
|
"recorded_rollout_path": (
|
|
142
144
|
str(recorded_path) if recorded_path is not None else None
|
|
143
145
|
),
|
|
144
|
-
"model":
|
|
146
|
+
"model": "{0}:{1}".format(
|
|
147
|
+
self.agent.model_name,
|
|
148
|
+
getattr(self.agent.model_client, "reasoning_effort", None) or "default",
|
|
149
|
+
),
|
|
145
150
|
"title": self.title,
|
|
146
151
|
"history": conversation_history_to_turns(self.agent.history),
|
|
147
152
|
"busy": self.is_busy,
|
|
@@ -250,13 +255,10 @@ class AgentRuntime:
|
|
|
250
255
|
submission_id = uuid7_string()
|
|
251
256
|
future = asyncio.get_running_loop().create_future()
|
|
252
257
|
try:
|
|
253
|
-
|
|
258
|
+
async with self._get_command_lock():
|
|
259
|
+
if not self.agent.accepts_input:
|
|
260
|
+
raise RuntimeError("agent is shutting down")
|
|
254
261
|
result = await self._execute_command(command[1:], argument.strip())
|
|
255
|
-
else:
|
|
256
|
-
async with self._get_command_lock():
|
|
257
|
-
if not self.agent.accepts_input:
|
|
258
|
-
raise RuntimeError("agent is shutting down")
|
|
259
|
-
result = await self._execute_command(command[1:], argument.strip())
|
|
260
262
|
except Exception as exc:
|
|
261
263
|
future.set_exception(exc)
|
|
262
264
|
self._publish(CommandFailedEvent(submission_id, command, str(exc), sender))
|
|
@@ -267,10 +269,7 @@ class AgentRuntime:
|
|
|
267
269
|
return SubmittedInput(submission_id, "command", future)
|
|
268
270
|
|
|
269
271
|
async def _execute_command(self, command, argument):
|
|
270
|
-
if
|
|
271
|
-
command in {"help", "history", "compact", "fork", "exit", "quit"}
|
|
272
|
-
and argument
|
|
273
|
-
):
|
|
272
|
+
if command in {"help", "history", "compact", "fork"} and argument:
|
|
274
273
|
raise ValueError("Usage: /{0}".format(command))
|
|
275
274
|
if command == "help":
|
|
276
275
|
return {"kind": "help", "commands": self.commands()}
|
|
@@ -292,6 +291,17 @@ class AgentRuntime:
|
|
|
292
291
|
"model": self.agent.model_name,
|
|
293
292
|
"models": await self.agent.model_client.list_models(),
|
|
294
293
|
}
|
|
294
|
+
if command == "effort":
|
|
295
|
+
client = typing.cast(ModelControl, self.agent.model_client)
|
|
296
|
+
effort = client.reasoning_effort
|
|
297
|
+
if argument:
|
|
298
|
+
if len(argument.split()) != 1:
|
|
299
|
+
raise ValueError("Usage: /effort [<value>|default]")
|
|
300
|
+
self.require_idle("change reasoning effort")
|
|
301
|
+
client.reasoning_effort = None if argument == "default" else argument
|
|
302
|
+
effort = client.reasoning_effort
|
|
303
|
+
self.publish_state("effort")
|
|
304
|
+
return {"kind": "effort", "reasoning_effort": effort}
|
|
295
305
|
if command == "resume":
|
|
296
306
|
if not argument:
|
|
297
307
|
return {
|
|
@@ -317,9 +327,6 @@ class AgentRuntime:
|
|
|
317
327
|
if command == "fork":
|
|
318
328
|
self.fork()
|
|
319
329
|
return {"kind": "forked", "session_id": self.agent.session_id}
|
|
320
|
-
if command in {"exit", "quit"}:
|
|
321
|
-
await self.close()
|
|
322
|
-
return {"kind": "closed"}
|
|
323
330
|
handler = self.command_handlers.get(command)
|
|
324
331
|
if handler is None:
|
|
325
332
|
raise ValueError("Unknown command: /{0}".format(command))
|
|
@@ -35,6 +35,10 @@ def format_command_result(result: "typing.Dict[str, object]") -> "str":
|
|
|
35
35
|
)
|
|
36
36
|
if kind == "model_changed":
|
|
37
37
|
return "Switched model to {0}.".format(result["model"])
|
|
38
|
+
if kind == "effort":
|
|
39
|
+
return "Reasoning effort: {0}".format(
|
|
40
|
+
result["reasoning_effort"] or "provider default"
|
|
41
|
+
)
|
|
38
42
|
if kind == "sessions":
|
|
39
43
|
if not result["sessions"]:
|
|
40
44
|
return "No resumable sessions found."
|
|
@@ -67,8 +71,6 @@ def format_command_result(result: "typing.Dict[str, object]") -> "str":
|
|
|
67
71
|
)
|
|
68
72
|
if kind == "forked":
|
|
69
73
|
return "Forked session: " + result["session_id"]
|
|
70
|
-
if kind == "closed":
|
|
71
|
-
return ""
|
|
72
74
|
return "\n".join(result["lines"])
|
|
73
75
|
|
|
74
76
|
|
|
@@ -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)
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-codex"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.7"
|
|
8
8
|
description = "A minimal Python extraction of Codex's main agent loop"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.6.2"
|
|
@@ -40,7 +40,7 @@ test = [
|
|
|
40
40
|
]
|
|
41
41
|
dev = [
|
|
42
42
|
{ include-group = "test" },
|
|
43
|
-
"pycodex-ws==0.4.
|
|
43
|
+
"pycodex-ws==0.4.7",
|
|
44
44
|
"black>=22.8.0",
|
|
45
45
|
"debugpy>=1.8.20; python_version >= '3.8'",
|
|
46
46
|
"isort>=5.10.1",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_codex-0.4.5 → python_codex-0.4.7}/pycodex/prompts/permissions/approval_policy/never.md
RENAMED
|
File without changes
|
{python_codex-0.4.5 → python_codex-0.4.7}/pycodex/prompts/permissions/approval_policy/on_failure.md
RENAMED
|
File without changes
|
{python_codex-0.4.5 → python_codex-0.4.7}/pycodex/prompts/permissions/approval_policy/on_request.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_codex-0.4.5 → python_codex-0.4.7}/pycodex/prompts/permissions/sandbox_mode/read_only.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|