python-codex 0.4.7__tar.gz → 0.5.0__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.
Files changed (94) hide show
  1. python_codex-0.5.0/PKG-INFO +193 -0
  2. python_codex-0.5.0/README.md +173 -0
  3. python_codex-0.5.0/README_ZH.md +164 -0
  4. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/agent.py +8 -0
  5. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/bootstrap.py +23 -26
  6. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/context.py +10 -0
  7. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/runtime.py +11 -0
  8. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/apply_patch_tool.py +24 -26
  9. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/base_tool.py +5 -0
  10. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/code_mode_manager.py +4 -9
  11. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/exec_command_tool.py +1 -2
  12. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/grep_files_tool.py +2 -16
  13. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/shell_command_tool.py +1 -17
  14. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/shell_tool.py +1 -17
  15. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/unified_exec_manager.py +2 -14
  16. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/view_image_tool.py +1 -12
  17. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/utils/event_helpers.py +2 -0
  18. {python_codex-0.4.7 → python_codex-0.5.0}/pyproject.toml +2 -2
  19. python_codex-0.4.7/PKG-INFO +0 -905
  20. python_codex-0.4.7/README.md +0 -885
  21. python_codex-0.4.7/README_ZH.md +0 -521
  22. {python_codex-0.4.7 → python_codex-0.5.0}/.gitignore +0 -0
  23. {python_codex-0.4.7 → python_codex-0.5.0}/LICENSE +0 -0
  24. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/__init__.py +0 -0
  25. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/__main__.py +0 -0
  26. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/cli.py +0 -0
  27. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/events.py +0 -0
  28. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/model.py +0 -0
  29. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/portable/__init__.py +0 -0
  30. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/portable/client.py +0 -0
  31. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/portable/server.py +0 -0
  32. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/prompts/default_base_instructions.md +0 -0
  33. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/prompts/models.json +0 -0
  34. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/prompts/permissions/approval_policy/never.md +0 -0
  35. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/prompts/permissions/approval_policy/on_failure.md +0 -0
  36. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/prompts/permissions/approval_policy/on_request.md +0 -0
  37. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/prompts/permissions/approval_policy/on_request_rule_request_permission.md +0 -0
  38. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/prompts/permissions/approval_policy/unless_trusted.md +0 -0
  39. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/prompts/permissions/sandbox_mode/danger_full_access.md +0 -0
  40. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/prompts/permissions/sandbox_mode/read_only.md +0 -0
  41. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/prompts/permissions/sandbox_mode/workspace_write.md +0 -0
  42. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/protocol.py +0 -0
  43. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/__init__.py +0 -0
  44. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/agent_tool_schemas.py +0 -0
  45. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/clock_tool.py +0 -0
  46. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/close_agent_tool.py +0 -0
  47. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/exec_runtime.js +0 -0
  48. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/exec_tool.py +0 -0
  49. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/list_dir_tool.py +0 -0
  50. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/read_file_tool.py +0 -0
  51. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/request_permissions_tool.py +0 -0
  52. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/request_user_input_tool.py +0 -0
  53. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/resume_agent_tool.py +0 -0
  54. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/runtime_services.py +0 -0
  55. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/send_input_tool.py +0 -0
  56. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/spawn_agent_tool.py +0 -0
  57. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/update_plan_tool.py +0 -0
  58. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/wait_agent_tool.py +0 -0
  59. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/wait_tool.py +0 -0
  60. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/web_search_tool.py +0 -0
  61. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/tools/write_stdin_tool.py +0 -0
  62. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/utils/__init__.py +0 -0
  63. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/utils/_aes_gcm/LICENSE +0 -0
  64. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/utils/_aes_gcm/README.md +0 -0
  65. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/utils/_aes_gcm/__init__.py +0 -0
  66. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/utils/_aes_gcm/aes.py +0 -0
  67. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/utils/_aes_gcm/gcm.py +0 -0
  68. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/utils/_aes_gcm/source-sha256.json +0 -0
  69. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/utils/async_bridge.py +0 -0
  70. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/utils/compactor.py +0 -0
  71. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/utils/compat.py +0 -0
  72. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/utils/crypto.py +0 -0
  73. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/utils/debug.py +0 -0
  74. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/utils/doctor.py +0 -0
  75. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/utils/dotenv.py +0 -0
  76. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/utils/get_env.py +0 -0
  77. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/utils/image_utils.py +0 -0
  78. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/utils/model_metadata.py +0 -0
  79. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/utils/random_ids.py +0 -0
  80. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/utils/session_persist.py +0 -0
  81. {python_codex-0.4.7 → python_codex-0.5.0}/pycodex/utils/truncation.py +0 -0
  82. {python_codex-0.4.7 → python_codex-0.5.0}/responses_server/__init__.py +0 -0
  83. {python_codex-0.4.7 → python_codex-0.5.0}/responses_server/__main__.py +0 -0
  84. {python_codex-0.4.7 → python_codex-0.5.0}/responses_server/app.py +0 -0
  85. {python_codex-0.4.7 → python_codex-0.5.0}/responses_server/config.py +0 -0
  86. {python_codex-0.4.7 → python_codex-0.5.0}/responses_server/messages_api.py +0 -0
  87. {python_codex-0.4.7 → python_codex-0.5.0}/responses_server/payload_processors.py +0 -0
  88. {python_codex-0.4.7 → python_codex-0.5.0}/responses_server/server.py +0 -0
  89. {python_codex-0.4.7 → python_codex-0.5.0}/responses_server/session_store.py +0 -0
  90. {python_codex-0.4.7 → python_codex-0.5.0}/responses_server/stream_router.py +0 -0
  91. {python_codex-0.4.7 → python_codex-0.5.0}/responses_server/tools/__init__.py +0 -0
  92. {python_codex-0.4.7 → python_codex-0.5.0}/responses_server/tools/custom_adapter.py +0 -0
  93. {python_codex-0.4.7 → python_codex-0.5.0}/responses_server/tools/web_search.py +0 -0
  94. {python_codex-0.4.7 → python_codex-0.5.0}/responses_server/trajectory_dump.py +0 -0
@@ -0,0 +1,193 @@
1
+ Metadata-Version: 2.1
2
+ Name: python-codex
3
+ Version: 0.5.0
4
+ Summary: A minimal Python extraction of Codex's main agent loop
5
+ License-File: LICENSE
6
+ Requires-Python: >=3.6.2
7
+ Requires-Dist: dataclasses>=0.8; python_version < '3.7'
8
+ Requires-Dist: importlib-metadata>=4.8.3; python_version < '3.8'
9
+ Requires-Dist: loguru>=0.7.3
10
+ Requires-Dist: prompt-toolkit>=3.0.36
11
+ Requires-Dist: pyyaml>=6.0
12
+ Requires-Dist: requests>=2.27.1
13
+ Requires-Dist: starlette<0.20,>=0.19.1; python_version < '3.7'
14
+ Requires-Dist: starlette>=0.19.1; python_version >= '3.7'
15
+ Requires-Dist: tomli<2,>=1.2.3; python_version < '3.11'
16
+ Requires-Dist: typing-extensions>=4.1.1; python_version < '3.8'
17
+ Requires-Dist: uvicorn<0.17,>=0.16.0; python_version < '3.7'
18
+ Requires-Dist: uvicorn>=0.16.0; python_version >= '3.7'
19
+ Description-Content-Type: text/markdown
20
+
21
+ # pycodex
22
+
23
+ [中文](README_ZH.md)
24
+
25
+ pycodex brings the [Codex](https://github.com/openai/codex) agent loop to Python,
26
+ with a CLI, reusable Agent API and browser workspace. It aims to follow upstream
27
+ behavior; supported features and deliberate differences are documented in
28
+ [Alignment](docs/ALIGNMENT.md).
29
+
30
+ | Package | Provides | Python |
31
+ | --- | --- | --- |
32
+ | `python-codex` | Agent, runtime, CLI, tools, Responses compatibility server and portable storage | 3.6.2+ |
33
+ | `pycodex-ws` | Browser workspace, Canvas, file/diff viewers and optional Feishu integration | 3.7+ |
34
+
35
+ The workspace package installs the matching core version. Python imports use
36
+ `pycodex`; the commands are `pycodex` and `pycodex-ws`.
37
+
38
+ ## Install and run
39
+
40
+ Install the core CLI:
41
+
42
+ ```bash
43
+ python3 -m pip install python-codex
44
+ pycodex
45
+ ```
46
+
47
+ Or install the browser workspace, which includes core:
48
+
49
+ ```bash
50
+ python3 -m pip install pycodex-ws
51
+ pycodex-ws --help
52
+ ```
53
+
54
+ Use `python3 -m pip install --upgrade pycodex-ws` to upgrade both packages
55
+ together. If a command is missing from `PATH`, use `python3 -m pycodex` or
56
+ `python3 -m workspace_server` with the interpreter used for installation.
57
+
58
+ ## Model configuration
59
+
60
+ pycodex reads `~/.codex/config.toml` and the adjacent `.env`. Configure `model`,
61
+ `model_provider`, and the provider's `base_url` and `env_key` as needed.
62
+ The built-in `openai` provider uses `OPENAI_API_KEY`. Authentication uses API
63
+ keys; ChatGPT sign-in credentials from `auth.json` or the OS keychain are not
64
+ loaded.
65
+
66
+ ```bash
67
+ pycodex --profile my_profile "Summarize this repository."
68
+ pycodex --config /path/to/config.toml
69
+ pycodex doctor
70
+ ```
71
+
72
+ Models use the streaming Responses API. For a Chat Completions backend, set
73
+ `use_chat_completion = true` in its provider table. Other backend options include:
74
+
75
+ ```bash
76
+ pycodex --vllm-endpoint http://127.0.0.1:18000
77
+ pycodex --profile my_profile --use-messages
78
+ ```
79
+
80
+ See [context and configuration](docs/CONTEXT.md) and the
81
+ [Responses compatibility server](docs/responses_server/README.md).
82
+
83
+ ## CLI
84
+
85
+ Run `pycodex` without a prompt for an interactive session, or supply a prompt
86
+ for a single turn:
87
+
88
+ ```bash
89
+ pycodex "Summarize this repository in one sentence."
90
+ printf 'Reply with exactly OK.' | pycodex
91
+ pycodex --json "Reply with exactly OK."
92
+ ```
93
+
94
+ | Command | Purpose |
95
+ | --- | --- |
96
+ | `/model`, `/effort` | Show or change the model and reasoning effort |
97
+ | `/cd <directory>` | Change the session's working directory while idle |
98
+ | `/title`, `/history` | Manage the title and inspect conversation history |
99
+ | `/resume` | List saved sessions; pass an index or session UUID to resume |
100
+ | `/compact` | Summarize history to free context |
101
+ | `/fork` | Continue the current history under a new session ID |
102
+ | `/queue <message>` | Wait for the active turn to finish before submitting |
103
+
104
+ Ordinary input during a turn steers at the next safe sampling boundary.
105
+ Sessions are saved under `CODEX_HOME/sessions`. Set
106
+ `model_auto_compact_token_limit` in the configuration to compact automatically.
107
+
108
+ Ctrl+D or Ctrl+C closes the CLI after accepted work finishes. A second Ctrl+C
109
+ during closing exits immediately. Use `pycodex --help` for startup options.
110
+
111
+ ## Browser workspace
112
+
113
+ Create `workspaces.json`:
114
+
115
+ ```json
116
+ {
117
+ "workspaces": [
118
+ {"id": "demo", "work_dir": ".", "state_path": "demo", "enable_board": true}
119
+ ]
120
+ }
121
+ ```
122
+
123
+ ```bash
124
+ pycodex-ws --listen 127.0.0.1:6007 --workspace-config workspaces.json
125
+ ```
126
+
127
+ Open `http://127.0.0.1:6007`. The workspace combines conversations with a live
128
+ HTML Canvas and read-only file/diff viewers. Click file links in replies to open
129
+ them, and use **Insert into chat** to add selected code to an editable draft.
130
+ Keep the server's stdin open: EOF initiates shutdown.
131
+ The gear button starts an Agent conversation for customizing the interface
132
+ through files in `~/.pycodex/frontend/`.
133
+
134
+ See the [workspace guide](packages/pycodex-ws/README.md) for session controls,
135
+ storage and Feishu setup. The separately distributed
136
+ [VS Code extension](packages/pycodex-ws/extensions/vscode/README.md) uses the same
137
+ workspace backend, including on Remote SSH hosts.
138
+
139
+ ## Python API
140
+
141
+ Use the standard model and tool setup from Python:
142
+
143
+ ```python
144
+ from pycodex.bootstrap import build_agent, build_model
145
+
146
+ agent = build_agent(build_model())
147
+ try:
148
+ result = agent.ask("Summarize this repository in one sentence.")
149
+ print(result.output_text)
150
+ finally:
151
+ agent.shutdown()
152
+ ```
153
+
154
+ For custom integrations, `Agent` accepts a model client, `ToolRegistry` and
155
+ `ContextConfig`. `await agent.run_turn([...])` runs an asynchronous turn;
156
+ `AgentRuntime` adds input queues, commands and frontend subscriptions.
157
+ See [runtime contracts and examples](docs/RUNTIME.md).
158
+
159
+ ## Portable configuration
160
+
161
+ Portable mode transfers an encrypted bundle of your configuration, instructions
162
+ and skills to another machine. Start a storage service in a separate terminal:
163
+
164
+ ```bash
165
+ python3 -m pycodex.portable.server --root .tmp/pycodex_storage --host 127.0.0.1 --port 5577
166
+ ```
167
+
168
+ Upload the default Codex home, or specify another directory:
169
+
170
+ ```bash
171
+ pycodex --put @127.0.0.1:5577
172
+ pycodex --put /path/to/.codex/@127.0.0.1:5577
173
+ ```
174
+
175
+ After verifying a download round trip, the command prints a reusable
176
+ `pycodex --call ...` command. Run it on the target machine using a storage
177
+ address reachable from that machine.
178
+
179
+ ## Development and reference
180
+
181
+ ```bash
182
+ uv sync --dev
183
+ uv run pycodex
184
+ uv run --dev pytest
185
+ ```
186
+
187
+ - [Test environments and coverage](tests/TESTS.md)
188
+ - [Architecture, package layout and runtime contracts](docs/RUNTIME.md)
189
+ - [Prompts, configuration and context](docs/CONTEXT.md)
190
+ - [Upstream alignment](docs/ALIGNMENT.md)
191
+ - [Responses compatibility server](docs/responses_server/README.md)
192
+
193
+ Release notes and implementation details live in `docs/`.
@@ -0,0 +1,173 @@
1
+ # pycodex
2
+
3
+ [中文](README_ZH.md)
4
+
5
+ pycodex brings the [Codex](https://github.com/openai/codex) agent loop to Python,
6
+ with a CLI, reusable Agent API and browser workspace. It aims to follow upstream
7
+ behavior; supported features and deliberate differences are documented in
8
+ [Alignment](docs/ALIGNMENT.md).
9
+
10
+ | Package | Provides | Python |
11
+ | --- | --- | --- |
12
+ | `python-codex` | Agent, runtime, CLI, tools, Responses compatibility server and portable storage | 3.6.2+ |
13
+ | `pycodex-ws` | Browser workspace, Canvas, file/diff viewers and optional Feishu integration | 3.7+ |
14
+
15
+ The workspace package installs the matching core version. Python imports use
16
+ `pycodex`; the commands are `pycodex` and `pycodex-ws`.
17
+
18
+ ## Install and run
19
+
20
+ Install the core CLI:
21
+
22
+ ```bash
23
+ python3 -m pip install python-codex
24
+ pycodex
25
+ ```
26
+
27
+ Or install the browser workspace, which includes core:
28
+
29
+ ```bash
30
+ python3 -m pip install pycodex-ws
31
+ pycodex-ws --help
32
+ ```
33
+
34
+ Use `python3 -m pip install --upgrade pycodex-ws` to upgrade both packages
35
+ together. If a command is missing from `PATH`, use `python3 -m pycodex` or
36
+ `python3 -m workspace_server` with the interpreter used for installation.
37
+
38
+ ## Model configuration
39
+
40
+ pycodex reads `~/.codex/config.toml` and the adjacent `.env`. Configure `model`,
41
+ `model_provider`, and the provider's `base_url` and `env_key` as needed.
42
+ The built-in `openai` provider uses `OPENAI_API_KEY`. Authentication uses API
43
+ keys; ChatGPT sign-in credentials from `auth.json` or the OS keychain are not
44
+ loaded.
45
+
46
+ ```bash
47
+ pycodex --profile my_profile "Summarize this repository."
48
+ pycodex --config /path/to/config.toml
49
+ pycodex doctor
50
+ ```
51
+
52
+ Models use the streaming Responses API. For a Chat Completions backend, set
53
+ `use_chat_completion = true` in its provider table. Other backend options include:
54
+
55
+ ```bash
56
+ pycodex --vllm-endpoint http://127.0.0.1:18000
57
+ pycodex --profile my_profile --use-messages
58
+ ```
59
+
60
+ See [context and configuration](docs/CONTEXT.md) and the
61
+ [Responses compatibility server](docs/responses_server/README.md).
62
+
63
+ ## CLI
64
+
65
+ Run `pycodex` without a prompt for an interactive session, or supply a prompt
66
+ for a single turn:
67
+
68
+ ```bash
69
+ pycodex "Summarize this repository in one sentence."
70
+ printf 'Reply with exactly OK.' | pycodex
71
+ pycodex --json "Reply with exactly OK."
72
+ ```
73
+
74
+ | Command | Purpose |
75
+ | --- | --- |
76
+ | `/model`, `/effort` | Show or change the model and reasoning effort |
77
+ | `/cd <directory>` | Change the session's working directory while idle |
78
+ | `/title`, `/history` | Manage the title and inspect conversation history |
79
+ | `/resume` | List saved sessions; pass an index or session UUID to resume |
80
+ | `/compact` | Summarize history to free context |
81
+ | `/fork` | Continue the current history under a new session ID |
82
+ | `/queue <message>` | Wait for the active turn to finish before submitting |
83
+
84
+ Ordinary input during a turn steers at the next safe sampling boundary.
85
+ Sessions are saved under `CODEX_HOME/sessions`. Set
86
+ `model_auto_compact_token_limit` in the configuration to compact automatically.
87
+
88
+ Ctrl+D or Ctrl+C closes the CLI after accepted work finishes. A second Ctrl+C
89
+ during closing exits immediately. Use `pycodex --help` for startup options.
90
+
91
+ ## Browser workspace
92
+
93
+ Create `workspaces.json`:
94
+
95
+ ```json
96
+ {
97
+ "workspaces": [
98
+ {"id": "demo", "work_dir": ".", "state_path": "demo", "enable_board": true}
99
+ ]
100
+ }
101
+ ```
102
+
103
+ ```bash
104
+ pycodex-ws --listen 127.0.0.1:6007 --workspace-config workspaces.json
105
+ ```
106
+
107
+ Open `http://127.0.0.1:6007`. The workspace combines conversations with a live
108
+ HTML Canvas and read-only file/diff viewers. Click file links in replies to open
109
+ them, and use **Insert into chat** to add selected code to an editable draft.
110
+ Keep the server's stdin open: EOF initiates shutdown.
111
+ The gear button starts an Agent conversation for customizing the interface
112
+ through files in `~/.pycodex/frontend/`.
113
+
114
+ See the [workspace guide](packages/pycodex-ws/README.md) for session controls,
115
+ storage and Feishu setup. The separately distributed
116
+ [VS Code extension](packages/pycodex-ws/extensions/vscode/README.md) uses the same
117
+ workspace backend, including on Remote SSH hosts.
118
+
119
+ ## Python API
120
+
121
+ Use the standard model and tool setup from Python:
122
+
123
+ ```python
124
+ from pycodex.bootstrap import build_agent, build_model
125
+
126
+ agent = build_agent(build_model())
127
+ try:
128
+ result = agent.ask("Summarize this repository in one sentence.")
129
+ print(result.output_text)
130
+ finally:
131
+ agent.shutdown()
132
+ ```
133
+
134
+ For custom integrations, `Agent` accepts a model client, `ToolRegistry` and
135
+ `ContextConfig`. `await agent.run_turn([...])` runs an asynchronous turn;
136
+ `AgentRuntime` adds input queues, commands and frontend subscriptions.
137
+ See [runtime contracts and examples](docs/RUNTIME.md).
138
+
139
+ ## Portable configuration
140
+
141
+ Portable mode transfers an encrypted bundle of your configuration, instructions
142
+ and skills to another machine. Start a storage service in a separate terminal:
143
+
144
+ ```bash
145
+ python3 -m pycodex.portable.server --root .tmp/pycodex_storage --host 127.0.0.1 --port 5577
146
+ ```
147
+
148
+ Upload the default Codex home, or specify another directory:
149
+
150
+ ```bash
151
+ pycodex --put @127.0.0.1:5577
152
+ pycodex --put /path/to/.codex/@127.0.0.1:5577
153
+ ```
154
+
155
+ After verifying a download round trip, the command prints a reusable
156
+ `pycodex --call ...` command. Run it on the target machine using a storage
157
+ address reachable from that machine.
158
+
159
+ ## Development and reference
160
+
161
+ ```bash
162
+ uv sync --dev
163
+ uv run pycodex
164
+ uv run --dev pytest
165
+ ```
166
+
167
+ - [Test environments and coverage](tests/TESTS.md)
168
+ - [Architecture, package layout and runtime contracts](docs/RUNTIME.md)
169
+ - [Prompts, configuration and context](docs/CONTEXT.md)
170
+ - [Upstream alignment](docs/ALIGNMENT.md)
171
+ - [Responses compatibility server](docs/responses_server/README.md)
172
+
173
+ Release notes and implementation details live in `docs/`.
@@ -0,0 +1,164 @@
1
+ # pycodex
2
+
3
+ [English](README.md)
4
+
5
+ pycodex 用 Python 实现 [Codex](https://github.com/openai/codex) 的 Agent 主循环,
6
+ 提供 CLI、可复用的 Agent API 和浏览器工作区。项目以对齐上游行为为目标;
7
+ 支持范围和刻意保留的差异见[对齐文档](docs/ALIGNMENT.md)。
8
+
9
+ | 包 | 内容 | Python |
10
+ | --- | --- | --- |
11
+ | `python-codex` | Agent、Runtime、CLI、工具、Responses 兼容服务和 portable 存储 | 3.6.2+ |
12
+ | `pycodex-ws` | 浏览器工作区、Canvas、文件/diff 查看器和可选飞书集成 | 3.7+ |
13
+
14
+ workspace 包会安装匹配版本的 core。Python 导入路径为 `pycodex`,
15
+ 命令为 `pycodex` 和 `pycodex-ws`。
16
+
17
+ ## 安装与启动
18
+
19
+ 安装核心 CLI:
20
+
21
+ ```bash
22
+ python3 -m pip install python-codex
23
+ pycodex
24
+ ```
25
+
26
+ 也可以安装包含 core 的浏览器工作区:
27
+
28
+ ```bash
29
+ python3 -m pip install pycodex-ws
30
+ pycodex-ws --help
31
+ ```
32
+
33
+ 使用 `python3 -m pip install --upgrade pycodex-ws` 一起升级两个包。
34
+ 如果命令不在 `PATH` 中,用安装时的解释器运行 `python3 -m pycodex`
35
+ 或 `python3 -m workspace_server`。
36
+
37
+ ## 模型配置
38
+
39
+ 默认读取 `~/.codex/config.toml` 和同目录的 `.env`。根据需要配置 `model`、
40
+ `model_provider` 及 provider 的 `base_url`、`env_key`。
41
+ 内置 `openai` provider 使用 `OPENAI_API_KEY`。认证使用 API key,
42
+ 不读取 `auth.json` 或系统钥匙串中的 ChatGPT 登录凭据。
43
+
44
+ ```bash
45
+ pycodex --profile my_profile "概括这个仓库。"
46
+ pycodex --config /path/to/config.toml
47
+ pycodex doctor
48
+ ```
49
+
50
+ 默认使用流式 Responses API。接入 Chat Completions 后端时,在对应 provider
51
+ 配置中设置 `use_chat_completion = true`。其他后端入口包括:
52
+
53
+ ```bash
54
+ pycodex --vllm-endpoint http://127.0.0.1:18000
55
+ pycodex --profile my_profile --use-messages
56
+ ```
57
+
58
+ 详见[配置与上下文](docs/CONTEXT.md)和
59
+ [Responses 兼容服务](docs/responses_server/README.md)。
60
+
61
+ ## CLI
62
+
63
+ 不带 prompt 时进入交互会话,也可以直接运行单轮任务:
64
+
65
+ ```bash
66
+ pycodex "用一句话概括这个仓库。"
67
+ printf 'Reply with exactly OK.' | pycodex
68
+ pycodex --json "Reply with exactly OK."
69
+ ```
70
+
71
+ | 命令 | 用途 |
72
+ | --- | --- |
73
+ | `/model`、`/effort` | 查看或切换模型及推理强度 |
74
+ | `/cd <directory>` | 空闲时切换当前会话的工作目录 |
75
+ | `/title`、`/history` | 管理标题、查看历史 |
76
+ | `/resume` | 列出已保存会话;传入序号或 UUID 恢复 |
77
+ | `/compact` | 压缩历史,释放上下文空间 |
78
+ | `/fork` | 保留当前历史,以新会话 ID 继续 |
79
+ | `/queue <message>` | 等当前 turn 完成后再执行 |
80
+
81
+ 运行中提交普通输入,会在下一个安全采样边界插入。会话保存在
82
+ `CODEX_HOME/sessions`;配置 `model_auto_compact_token_limit` 可启用自动压缩。
83
+
84
+ Ctrl+D 或 Ctrl+C 发起退出,等待已接收任务完成;关闭等待期间再次 Ctrl+C
85
+ 立即退出。完整启动参数见 `pycodex --help`。
86
+
87
+ ## 浏览器工作区
88
+
89
+ 创建 `workspaces.json`:
90
+
91
+ ```json
92
+ {
93
+ "workspaces": [
94
+ {"id": "demo", "work_dir": ".", "state_path": "demo", "enable_board": true}
95
+ ]
96
+ }
97
+ ```
98
+
99
+ ```bash
100
+ pycodex-ws --listen 127.0.0.1:6007 --workspace-config workspaces.json
101
+ ```
102
+
103
+ 访问 `http://127.0.0.1:6007`。工作区同时提供对话、实时 HTML Canvas 和只读
104
+ 文件/diff 查看器。点击回复中的文件链接打开视图,通过 **Insert into chat**
105
+ 把选中的代码插入可编辑草稿。运行期间保持服务器 stdin 打开,EOF 会触发关闭。
106
+ 点击齿轮按钮可开启界面定制会话,让 Agent 修改 `~/.pycodex/frontend/` 中的文件。
107
+
108
+ 会话操作、存储和飞书配置见[工作区说明](packages/pycodex-ws/README.md)。
109
+ 独立分发的 [VS Code 扩展](packages/pycodex-ws/extensions/vscode/README.md)
110
+ 复用同一后端,支持 Remote SSH 工作区。
111
+
112
+ ## Python API
113
+
114
+ 在 Python 中复用标准模型和工具装配:
115
+
116
+ ```python
117
+ from pycodex.bootstrap import build_agent, build_model
118
+
119
+ agent = build_agent(build_model())
120
+ try:
121
+ result = agent.ask("用一句话概括这个仓库。")
122
+ print(result.output_text)
123
+ finally:
124
+ agent.shutdown()
125
+ ```
126
+
127
+ 自定义集成时,`Agent` 接收 model client、`ToolRegistry` 和 `ContextConfig`。
128
+ 通过 `await agent.run_turn([...])` 异步运行 turn;`AgentRuntime` 提供输入队列、
129
+ 会话命令和前端订阅。详见[运行时契约与示例](docs/RUNTIME.md)。
130
+
131
+ ## Portable 配置
132
+
133
+ Portable mode 将配置、指令和 skills 加密打包,供其他机器恢复使用。
134
+ 先在单独的终端启动存储服务:
135
+
136
+ ```bash
137
+ python3 -m pycodex.portable.server --root .tmp/pycodex_storage --host 127.0.0.1 --port 5577
138
+ ```
139
+
140
+ 上传默认 Codex home,或指定其他目录:
141
+
142
+ ```bash
143
+ pycodex --put @127.0.0.1:5577
144
+ pycodex --put /path/to/.codex/@127.0.0.1:5577
145
+ ```
146
+
147
+ 上传并验证下载成功后,命令会打印可复用的 `pycodex --call ...` 启动命令。
148
+ 在目标机器运行时,使用该机器能够访问的存储服务地址。
149
+
150
+ ## 开发与参考
151
+
152
+ ```bash
153
+ uv sync --dev
154
+ uv run pycodex
155
+ uv run --dev pytest
156
+ ```
157
+
158
+ - [测试环境与覆盖范围](tests/TESTS.md)
159
+ - [架构、包布局与运行时契约](docs/RUNTIME.md)
160
+ - [提示、配置与上下文](docs/CONTEXT.md)
161
+ - [上游对齐](docs/ALIGNMENT.md)
162
+ - [Responses 兼容服务](docs/responses_server/README.md)
163
+
164
+ 发布记录和实现细节放在 `docs/`。
@@ -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))
@@ -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, cwd=cwd)
123
- unified_exec_manager = UnifiedExecManager(cwd=cwd)
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(cwd=cwd)
139
- shell_tool = ShellTool(cwd=cwd)
140
- shell_command_tool = ShellCommandTool(cwd=cwd)
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(cwd=cwd)
143
+ grep_files_tool = GrepFilesTool()
145
144
  read_file_tool = ReadFileTool()
146
145
  list_dir_tool = ListDirTool()
147
- view_image_tool = ViewImageTool(cwd=cwd)
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(cwd=cwd)
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(cwd=cwd))
220
+ registry.register(ApplyPatchTool())
223
221
  registry.register(WebSearchTool())
224
- registry.register(ViewImageTool(cwd=cwd))
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(base_client):
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 = base_client.with_overrides(
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
- subagent_agent_runtime_environment.subagent_manager.set_runtime_builder(
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
- subagent_agent_runtime_environment, cwd=resolved_cwd
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
- return Agent(
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(