python-codex 0.3.0__tar.gz → 0.3.1__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 (138) hide show
  1. {python_codex-0.3.0 → python_codex-0.3.1}/PKG-INFO +40 -1
  2. {python_codex-0.3.0 → python_codex-0.3.1}/README.md +39 -0
  3. {python_codex-0.3.0 → python_codex-0.3.1}/README_ZH.md +27 -0
  4. {python_codex-0.3.0 → python_codex-0.3.1}/docs/RUNTIME.md +92 -2
  5. {python_codex-0.3.0 → python_codex-0.3.1}/docs/responses_server/README.md +15 -1
  6. {python_codex-0.3.0 → python_codex-0.3.1}/packages/pycodex-ws/pyproject.toml +2 -2
  7. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/prompts/models.json +176 -0
  8. {python_codex-0.3.0 → python_codex-0.3.1}/pyproject.toml +2 -2
  9. {python_codex-0.3.0 → python_codex-0.3.1}/responses_server/app.py +1 -5
  10. {python_codex-0.3.0 → python_codex-0.3.1}/responses_server/stream_router.py +108 -0
  11. {python_codex-0.3.0 → python_codex-0.3.1}/tests/TESTS.md +36 -0
  12. {python_codex-0.3.0 → python_codex-0.3.1}/tests/responses_server/test_server.py +257 -2
  13. {python_codex-0.3.0 → python_codex-0.3.1}/tests/test_model.py +310 -6
  14. python_codex-0.3.1/tests/test_owned_workspace_server.py +145 -0
  15. {python_codex-0.3.0 → python_codex-0.3.1}/tests/test_workspace_server.py +110 -12
  16. {python_codex-0.3.0 → python_codex-0.3.1}/workspace_server/app.py +4 -2
  17. python_codex-0.3.1/workspace_server/owned_process.py +27 -0
  18. {python_codex-0.3.0 → python_codex-0.3.1}/workspace_server/workspace.html +2 -0
  19. {python_codex-0.3.0 → python_codex-0.3.1}/workspace_server/workspaces.py +27 -28
  20. {python_codex-0.3.0 → python_codex-0.3.1}/.github/workflows/publish.yml +0 -0
  21. {python_codex-0.3.0 → python_codex-0.3.1}/.github/workflows/test.yml +0 -0
  22. {python_codex-0.3.0 → python_codex-0.3.1}/.gitignore +0 -0
  23. {python_codex-0.3.0 → python_codex-0.3.1}/AGENTS.md +0 -0
  24. {python_codex-0.3.0 → python_codex-0.3.1}/LICENSE +0 -0
  25. {python_codex-0.3.0 → python_codex-0.3.1}/docs/ALIGNMENT.md +0 -0
  26. {python_codex-0.3.0 → python_codex-0.3.1}/docs/CONTEXT.md +0 -0
  27. {python_codex-0.3.0 → python_codex-0.3.1}/docs/RELEASE_0.3.0.md +0 -0
  28. {python_codex-0.3.0 → python_codex-0.3.1}/packages/pycodex-ws/README.md +0 -0
  29. {python_codex-0.3.0 → python_codex-0.3.1}/packages/pycodex-ws/pycodex_ws_meta/__init__.py +0 -0
  30. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/__init__.py +0 -0
  31. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/agent.py +0 -0
  32. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/bootstrap.py +0 -0
  33. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/cli.py +0 -0
  34. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/compat.py +0 -0
  35. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/context.py +0 -0
  36. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/doctor.py +0 -0
  37. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/events.py +0 -0
  38. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/feishu_card.py +0 -0
  39. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/feishu_link.py +0 -0
  40. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/model.py +0 -0
  41. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/model_metadata.py +0 -0
  42. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/portable.py +0 -0
  43. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/portable_server.py +0 -0
  44. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/prompts/default_base_instructions.md +0 -0
  45. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/prompts/permissions/approval_policy/never.md +0 -0
  46. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/prompts/permissions/approval_policy/on_failure.md +0 -0
  47. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/prompts/permissions/approval_policy/on_request.md +0 -0
  48. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/prompts/permissions/approval_policy/on_request_rule_request_permission.md +0 -0
  49. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/prompts/permissions/approval_policy/unless_trusted.md +0 -0
  50. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/prompts/permissions/sandbox_mode/danger_full_access.md +0 -0
  51. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/prompts/permissions/sandbox_mode/read_only.md +0 -0
  52. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/prompts/permissions/sandbox_mode/workspace_write.md +0 -0
  53. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/protocol.py +0 -0
  54. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/runtime.py +0 -0
  55. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/runtime_services.py +0 -0
  56. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/__init__.py +0 -0
  57. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/agent_tool_schemas.py +0 -0
  58. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/apply_patch_tool.py +0 -0
  59. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/base_tool.py +0 -0
  60. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/clock_tool.py +0 -0
  61. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/close_agent_tool.py +0 -0
  62. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/code_mode_manager.py +0 -0
  63. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/exec_command_tool.py +0 -0
  64. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/exec_runtime.js +0 -0
  65. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/exec_tool.py +0 -0
  66. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/grep_files_tool.py +0 -0
  67. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/ipython_tool.py +0 -0
  68. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/list_dir_tool.py +0 -0
  69. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/read_file_tool.py +0 -0
  70. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/request_permissions_tool.py +0 -0
  71. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/request_user_input_tool.py +0 -0
  72. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/resume_agent_tool.py +0 -0
  73. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/send_input_tool.py +0 -0
  74. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/shell_command_tool.py +0 -0
  75. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/shell_tool.py +0 -0
  76. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/spawn_agent_tool.py +0 -0
  77. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/unified_exec_manager.py +0 -0
  78. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/update_plan_tool.py +0 -0
  79. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/view_image_tool.py +0 -0
  80. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/wait_agent_tool.py +0 -0
  81. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/wait_tool.py +0 -0
  82. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/web_search_tool.py +0 -0
  83. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/tools/write_stdin_tool.py +0 -0
  84. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/utils/__init__.py +0 -0
  85. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/utils/async_bridge.py +0 -0
  86. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/utils/compactor.py +0 -0
  87. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/utils/debug.py +0 -0
  88. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/utils/dotenv.py +0 -0
  89. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/utils/event_helpers.py +0 -0
  90. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/utils/get_env.py +0 -0
  91. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/utils/image_utils.py +0 -0
  92. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/utils/random_ids.py +0 -0
  93. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/utils/session_persist.py +0 -0
  94. {python_codex-0.3.0 → python_codex-0.3.1}/pycodex/utils/truncation.py +0 -0
  95. {python_codex-0.3.0 → python_codex-0.3.1}/responses_server/__init__.py +0 -0
  96. {python_codex-0.3.0 → python_codex-0.3.1}/responses_server/__main__.py +0 -0
  97. {python_codex-0.3.0 → python_codex-0.3.1}/responses_server/config.py +0 -0
  98. {python_codex-0.3.0 → python_codex-0.3.1}/responses_server/messages_api.py +0 -0
  99. {python_codex-0.3.0 → python_codex-0.3.1}/responses_server/payload_processors.py +0 -0
  100. {python_codex-0.3.0 → python_codex-0.3.1}/responses_server/server.py +0 -0
  101. {python_codex-0.3.0 → python_codex-0.3.1}/responses_server/session_store.py +0 -0
  102. {python_codex-0.3.0 → python_codex-0.3.1}/responses_server/tools/__init__.py +0 -0
  103. {python_codex-0.3.0 → python_codex-0.3.1}/responses_server/tools/custom_adapter.py +0 -0
  104. {python_codex-0.3.0 → python_codex-0.3.1}/responses_server/tools/web_search.py +0 -0
  105. {python_codex-0.3.0 → python_codex-0.3.1}/responses_server/trajectory_dump.py +0 -0
  106. {python_codex-0.3.0 → python_codex-0.3.1}/tests/__init__.py +0 -0
  107. {python_codex-0.3.0 → python_codex-0.3.1}/tests/compare_context_requests.py +0 -0
  108. {python_codex-0.3.0 → python_codex-0.3.1}/tests/compare_steer_request_bodies.py +0 -0
  109. {python_codex-0.3.0 → python_codex-0.3.1}/tests/compare_tool_schemas.py +0 -0
  110. {python_codex-0.3.0 → python_codex-0.3.1}/tests/conftest.py +0 -0
  111. {python_codex-0.3.0 → python_codex-0.3.1}/tests/fake_responses_server.py +0 -0
  112. {python_codex-0.3.0 → python_codex-0.3.1}/tests/fakes.py +0 -0
  113. {python_codex-0.3.0 → python_codex-0.3.1}/tests/responses_server/fake_chat_completions_server.py +0 -0
  114. {python_codex-0.3.0 → python_codex-0.3.1}/tests/responses_server/test_trajectory_dump.py +0 -0
  115. {python_codex-0.3.0 → python_codex-0.3.1}/tests/test_agent.py +0 -0
  116. {python_codex-0.3.0 → python_codex-0.3.1}/tests/test_agent_state.py +0 -0
  117. {python_codex-0.3.0 → python_codex-0.3.1}/tests/test_async_bridge.py +0 -0
  118. {python_codex-0.3.0 → python_codex-0.3.1}/tests/test_builtin_tools.py +0 -0
  119. {python_codex-0.3.0 → python_codex-0.3.1}/tests/test_cli.py +0 -0
  120. {python_codex-0.3.0 → python_codex-0.3.1}/tests/test_compactor.py +0 -0
  121. {python_codex-0.3.0 → python_codex-0.3.1}/tests/test_context.py +0 -0
  122. {python_codex-0.3.0 → python_codex-0.3.1}/tests/test_doctor.py +0 -0
  123. {python_codex-0.3.0 → python_codex-0.3.1}/tests/test_events.py +0 -0
  124. {python_codex-0.3.0 → python_codex-0.3.1}/tests/test_fake_responses_server.py +0 -0
  125. {python_codex-0.3.0 → python_codex-0.3.1}/tests/test_feishu_card.py +0 -0
  126. {python_codex-0.3.0 → python_codex-0.3.1}/tests/test_feishu_link.py +0 -0
  127. {python_codex-0.3.0 → python_codex-0.3.1}/tests/test_feishu_oauth.py +0 -0
  128. {python_codex-0.3.0 → python_codex-0.3.1}/tests/test_ipython_tool.py +0 -0
  129. {python_codex-0.3.0 → python_codex-0.3.1}/tests/test_packaging.py +0 -0
  130. {python_codex-0.3.0 → python_codex-0.3.1}/tests/test_portable.py +0 -0
  131. {python_codex-0.3.0 → python_codex-0.3.1}/tests/test_py36_syntax.py +0 -0
  132. {python_codex-0.3.0 → python_codex-0.3.1}/tests/test_runtime_state.py +0 -0
  133. {python_codex-0.3.0 → python_codex-0.3.1}/tests/test_session_backend.py +0 -0
  134. {python_codex-0.3.0 → python_codex-0.3.1}/tools/feishu_oauth.py +0 -0
  135. {python_codex-0.3.0 → python_codex-0.3.1}/tools/sync_model_prompts.py +0 -0
  136. {python_codex-0.3.0 → python_codex-0.3.1}/workspace_server/__init__.py +0 -0
  137. {python_codex-0.3.0 → python_codex-0.3.1}/workspace_server/__main__.py +0 -0
  138. {python_codex-0.3.0 → python_codex-0.3.1}/workspace_server/workspaces.html +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-codex
3
- Version: 0.3.0
3
+ Version: 0.3.1
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
@@ -77,6 +77,32 @@ uv run pycodex "Reply with exactly OK."
77
77
  uv run pycodex
78
78
  ```
79
79
 
80
+ A VS Code frontend is available in [`extensions/vscode`](extensions/vscode/README.md).
81
+ It reuses an existing `python-codex>=0.3.1` installation. On first use, missing
82
+ or older backends are installed/upgraded with pip in the selected Python
83
+ environment on the workspace host; compatible versions need no network check.
84
+ It starts a dedicated `pycodex-ws` workspace on the project's host and opens a
85
+ chat in the left sidebar using VS Code's stable WebviewView API. Each chat
86
+ creates a workspace session with no code context attached. You can insert code
87
+ from editors, diffs or file-tree selections as editable tokens among your words.
88
+ Each token expands at its position when you send the message.
89
+ Previous chats fold into live status entries; each can be
90
+ reopened or closed, and models can be changed with `/model`.
91
+ Remote workspaces run Python on the remote host. A stdin pipe ties each backend
92
+ to its extension host; normal closure or host failure triggers draining and exit.
93
+ `pycodex-ws` always shuts down on stdin EOF, after accepted work finishes.
94
+ Keep stdin open while running the server; redirecting it from `/dev/null`
95
+ causes the server to shut down.
96
+ Workspace configuration uses a temporary JSON file passed to `--workspace-config`;
97
+ normal backend exit removes the temporary directory.
98
+ The extension uses that host's `~/.codex/config.toml` by default.
99
+ The chat settings menu selects a project venv or another Python interpreter
100
+ and edits the Codex profile. Changes offer a window reload when a backend is
101
+ already running.
102
+ Its context ring shows the same remaining budget and usage details as the
103
+ workspace web frontend.
104
+ See the extension README for building a VSIX, server setup and Remote SSH usage.
105
+
80
106
  ## Design Tradeoffs
81
107
 
82
108
  This is not a 1:1 port of the Rust implementation. The current goal is a
@@ -111,6 +137,7 @@ nailing the core tool-augmented reasoning loop first.
111
137
  - `pycodex/tools/`: concrete tool implementations
112
138
  - `pycodex/agent.py`: inner turn loop
113
139
  - `pycodex/runtime.py`: session commands, submission queues and frontend event subscriptions
140
+ - `extensions/vscode/`: VS Code chat frontend using the workspace HTTP API
114
141
  - `tests/test_agent.py`: core behavior tests
115
142
 
116
143
  ## Current Alignment Status
@@ -158,6 +185,9 @@ Chained failures also retain `stream_error_cause_type` and, for HTTP errors,
158
185
  `stream_error_http_status`; exception messages, headers and URLs are not
159
186
  copied into these diagnostic fields.
160
187
 
188
+ Downstream errors include elapsed time, timeout, failure stage, response-header
189
+ and first-chunk timing, and received chunk count directly in the error message.
190
+
161
191
  ## Live Model Integration
162
192
 
163
193
  If this machine already has a Codex CLI configuration, `pycodex` can reuse the
@@ -330,6 +360,15 @@ Current behavior:
330
360
  reasoning, retries the same downstream request once, and only then emits
331
361
  `response.failed` with `type = "model_output_invalid"` if the retry is still
332
362
  reasoning-only
363
+ - malformed JSON in ordinary downstream function-call arguments is detected by
364
+ `responses_server` and returned as `response.failed` with `type = "server_error"`;
365
+ the client's existing `stream_max_retries` policy retries the current request
366
+ before its local tools execute
367
+ - downstream connection failures before HTTP response headers or during SSE
368
+ reading are converted to `response.failed` for provider retries. Downstream
369
+ requests honor environment proxies; see the
370
+ [connection troubleshooting notes](docs/responses_server/README.md#连接排查)
371
+ for `RemoteDisconnected` and proxy bypass configuration
333
372
  - standalone `responses_server` now also supports downstream `/v1/messages`
334
373
  backends via `--outcomming-api messages`, while keeping the internal
335
374
  canonical request/route logic in chat-completions shape
@@ -54,6 +54,32 @@ uv run pycodex "Reply with exactly OK."
54
54
  uv run pycodex
55
55
  ```
56
56
 
57
+ A VS Code frontend is available in [`extensions/vscode`](extensions/vscode/README.md).
58
+ It reuses an existing `python-codex>=0.3.1` installation. On first use, missing
59
+ or older backends are installed/upgraded with pip in the selected Python
60
+ environment on the workspace host; compatible versions need no network check.
61
+ It starts a dedicated `pycodex-ws` workspace on the project's host and opens a
62
+ chat in the left sidebar using VS Code's stable WebviewView API. Each chat
63
+ creates a workspace session with no code context attached. You can insert code
64
+ from editors, diffs or file-tree selections as editable tokens among your words.
65
+ Each token expands at its position when you send the message.
66
+ Previous chats fold into live status entries; each can be
67
+ reopened or closed, and models can be changed with `/model`.
68
+ Remote workspaces run Python on the remote host. A stdin pipe ties each backend
69
+ to its extension host; normal closure or host failure triggers draining and exit.
70
+ `pycodex-ws` always shuts down on stdin EOF, after accepted work finishes.
71
+ Keep stdin open while running the server; redirecting it from `/dev/null`
72
+ causes the server to shut down.
73
+ Workspace configuration uses a temporary JSON file passed to `--workspace-config`;
74
+ normal backend exit removes the temporary directory.
75
+ The extension uses that host's `~/.codex/config.toml` by default.
76
+ The chat settings menu selects a project venv or another Python interpreter
77
+ and edits the Codex profile. Changes offer a window reload when a backend is
78
+ already running.
79
+ Its context ring shows the same remaining budget and usage details as the
80
+ workspace web frontend.
81
+ See the extension README for building a VSIX, server setup and Remote SSH usage.
82
+
57
83
  ## Design Tradeoffs
58
84
 
59
85
  This is not a 1:1 port of the Rust implementation. The current goal is a
@@ -88,6 +114,7 @@ nailing the core tool-augmented reasoning loop first.
88
114
  - `pycodex/tools/`: concrete tool implementations
89
115
  - `pycodex/agent.py`: inner turn loop
90
116
  - `pycodex/runtime.py`: session commands, submission queues and frontend event subscriptions
117
+ - `extensions/vscode/`: VS Code chat frontend using the workspace HTTP API
91
118
  - `tests/test_agent.py`: core behavior tests
92
119
 
93
120
  ## Current Alignment Status
@@ -135,6 +162,9 @@ Chained failures also retain `stream_error_cause_type` and, for HTTP errors,
135
162
  `stream_error_http_status`; exception messages, headers and URLs are not
136
163
  copied into these diagnostic fields.
137
164
 
165
+ Downstream errors include elapsed time, timeout, failure stage, response-header
166
+ and first-chunk timing, and received chunk count directly in the error message.
167
+
138
168
  ## Live Model Integration
139
169
 
140
170
  If this machine already has a Codex CLI configuration, `pycodex` can reuse the
@@ -307,6 +337,15 @@ Current behavior:
307
337
  reasoning, retries the same downstream request once, and only then emits
308
338
  `response.failed` with `type = "model_output_invalid"` if the retry is still
309
339
  reasoning-only
340
+ - malformed JSON in ordinary downstream function-call arguments is detected by
341
+ `responses_server` and returned as `response.failed` with `type = "server_error"`;
342
+ the client's existing `stream_max_retries` policy retries the current request
343
+ before its local tools execute
344
+ - downstream connection failures before HTTP response headers or during SSE
345
+ reading are converted to `response.failed` for provider retries. Downstream
346
+ requests honor environment proxies; see the
347
+ [connection troubleshooting notes](docs/responses_server/README.md#连接排查)
348
+ for `RemoteDisconnected` and proxy bypass configuration
310
349
  - standalone `responses_server` now also supports downstream `/v1/messages`
311
350
  backends via `--outcomming-api messages`, while keeping the internal
312
351
  canonical request/route logic in chat-completions shape
@@ -82,6 +82,7 @@ uv run pycodex
82
82
  - `pycodex/tools/`:具体工具实现
83
83
  - `pycodex/agent.py`:主循环
84
84
  - `pycodex/runtime.py`:统一会话命令、提交队列和前端事件订阅
85
+ - `extensions/vscode/`:通过 workspace HTTP API 接入的 VS Code 聊天前端
85
86
  - `tests/test_agent.py`:核心行为测试
86
87
 
87
88
  ## 当前对齐状态
@@ -92,6 +93,32 @@ worker 启停及连接清理都由后端统一处理,前端只做输入和展
92
93
  Web 不再导入或运行 CLI shell,飞书运行中也能提交 steer 和回答问题。
93
94
  IPython 是明确的例外:`ipython_agent()` 仍返回裸 Agent。
94
95
 
96
+ VS Code 前端为每个项目在项目所在机器上启动独立的 workspace 后端,通过稳定
97
+ WebviewView 在左侧活动栏的 Pycodex 入口显示聊天。首次使用优先复用已有
98
+ `python-codex>=0.3.1`,版本足够时不联网;缺失或版本太旧时,在对应的 Python
99
+ 环境中通过 pip 安装或升级。检查、安装和启动都在工作区所在主机完成。
100
+ 新建 session 会展开新会话、
101
+ 折叠上一个会话,保留后台任务和草稿;折叠条目继续更新状态,可直接点击展开或关闭。
102
+ 每个会话提供 Markdown 回复和固定输入框,新会话默认没有代码上下文,也不绑定源文件。
103
+ 需要引用代码时,可从编辑器、Diff(含只读一侧)或文件树/工作树右键插入上下文。
104
+ 片段像 emoji 一样插在文字光标处,可点击编辑,也可整体删除或复制粘贴。
105
+ 发送时按照文字和片段的原始顺序展开为普通 prompt,后续消息不会自动附加。
106
+ 模型名称仅作展示,通过 `/model <name>` 切换模型,
107
+ `/model` 列出可用模型;关闭会话会等待后端正常收尾。隐藏侧边栏只解除订阅。
108
+ 后端通过既有 `--workspace-config <path>` 读取临时 JSON 配置文件,正常退出后清理临时目录。
109
+ `pycodex-ws` 默认在 stdin EOF 时排空已接收任务并退出;运行期间须保持 stdin 打开,
110
+ 重定向到 `/dev/null` 会触发关闭。扩展通过保持管道打开管理后端生命周期;
111
+ 关闭项目或宿主崩溃使管道关闭,插件正常停用会等待退出确认。
112
+ Remote SSH 等远程工作区的 Python 后端在远程服务器上启动;
113
+ 短暂断联但远端宿主仍在运行时,会话继续保留。
114
+ 默认读取后端所在主机的 `~/.codex/config.toml`,欢迎页会说明这一点。
115
+ 聊天栏设置菜单可选择项目 `.venv`、`venv` 或浏览其他 Python 解释器,
116
+ 也可修改 Profile;例如 `step` 等价于 `--profile step`。
117
+ 已有后端运行时会提示重新加载窗口后生效。输入框底部的 context meter 与 workspace 前端一致,
118
+ 可查看剩余比例、最近一次模型上报的用量、压缩阈值和上下文上限。
119
+ 构建 VSIX、Python 环境配置及 Remote SSH 用法见
120
+ [`extensions/vscode/README.md`](extensions/vscode/README.md)。
121
+
95
122
  外部程序使用 `pycodex.bootstrap.build_model/build_agent/build_runtime` 装配,
96
123
  通过 `await runtime.start()`、`runtime.attach(handler)`、
97
124
  `await runtime.submit_input(text, sender)` 和 `await receipt.future` 交互。
@@ -63,6 +63,7 @@ Session wrapper. Queue data remains internal to Runtime.
63
63
 
64
64
  ```text
65
65
  CLI / Web / Feishu -> AgentRuntime -> Agent
66
+ VS Code -> workspace_server HTTP -> AgentRuntime
66
67
  IPython ---------------------------> Agent
67
68
  ```
68
69
 
@@ -89,6 +90,69 @@ commands and invalid arguments produce explicit command errors. Destructive
89
90
  commands reject active or queued work; a command lock serializes asynchronous
90
91
  commands and lets close wait for accepted command work.
91
92
 
93
+ The [VS Code frontend](../extensions/vscode/README.md) starts a workspace backend
94
+ per project using the existing CLI. Before startup it reads package metadata
95
+ from the selected Python interpreter. Automatic detection resolves an existing
96
+ console script's interpreter or finds Python on the workspace host. A compatible
97
+ `python-codex>=0.3.1` is reused without a network check; missing or older packages
98
+ are installed/upgraded through that interpreter's pip, including the optional
99
+ `pycodex-ws` metapackage when present. It launches `python -m workspace_server`
100
+ with the same interpreter, including when a selected venv initially has no Pycodex.
101
+ Installation finishes before the backend startup deadline starts. Pending
102
+ installs are shared per Python environment, and closing during preparation waits
103
+ for pip before discarding the launch. No private environment is created.
104
+ It uses the backend host's
105
+ `~/.codex/config.toml` by default; the welcome screen explains the local/remote
106
+ configuration source. The header settings menu selects project `.venv`/`venv`
107
+ interpreters or browses for another Python, and edits the Profile name.
108
+ Selections are validated without installing packages and saved to the project's
109
+ `pycodex.pythonPath` and `pycodex.profile` settings. A profile adds
110
+ `--profile <name>` to the launch. Changing either setting through the menu
111
+ offers a window reload if the backend is running; existing sessions keep their
112
+ startup settings until it restarts.
113
+ Sessions have no source-file binding or
114
+ automatically attached editor context. On request, the frontend captures the
115
+ source file, one-based cursor/selection and a bounded editor-buffer excerpt.
116
+ Read-only Git diff documents and Explorer/SCM file selections use VS Code's
117
+ document API. Context appears as atomic tokens interleaved with ordinary text in
118
+ the composer. Insertion uses the saved cursor/selection; clicking a token edits
119
+ its content. Sending expands text and tokens in their original order into the
120
+ ordinary `prompt` field.
121
+ Commands and pending answers use
122
+ the same existing HTTP API.
123
+ Connections are keyed by workspace endpoint and session id.
124
+ Each new chat creates a session; clicking a collapsed entry
125
+ activates that chat. A stable WebviewView in the Pycodex activity-bar container
126
+ displays one expanded chat and collapsed entries for the others, preserving each
127
+ chat's draft. All opened entries retain live status while the
128
+ sidebar is visible; hiding it stops observation without closing sessions.
129
+ Model changes use typed `/model` commands; the model label is read-only.
130
+ Closing an entry uses the existing session DELETE endpoint and waits for normal
131
+ backend draining before removing the entry. HTTP snapshot polling uses the
132
+ existing Web projection. In remote workspaces, the extension host starts Python
133
+ and makes HTTP requests on the remote machine; the webview only exchanges
134
+ messages with the extension host.
135
+ The input footer's context ring uses the existing snapshot fields
136
+ `context_remaining_percent`, `usage_tokens`, `auto_compact_token_limit` and
137
+ `max_context_window`. It shares the Web meter semantics below, including compact
138
+ thresholds, unknown usage and reset after compaction. Hover/focus shows exact
139
+ values, click pins them, and Escape, outside click or session switching closes
140
+ the details. The frontend does not estimate draft tokens or recalculate limits.
141
+ The launcher writes UTF-8 workspace JSON into a private temporary directory
142
+ and passes the filename through the existing `--workspace-config` option.
143
+ It removes that directory after backend exit or failed startup; an abrupt host
144
+ crash can leave the temporary files behind. The `pycodex-ws` CLI always
145
+ shuts down on stdin EOF. Stdin stays open for the backend's lifetime.
146
+ EOF on folder removal, deactivation,
147
+ or extension-host death starts normal server shutdown, including when EOF arrives
148
+ before startup finishes. Accepted turns and queues drain through existing Runtime
149
+ closure; no turn timeout or forced cancellation is added. Deactivation waits for
150
+ all owned processes to exit. A temporary remote disconnection leaves sessions
151
+ running if their extension host remains alive.
152
+ Other CLI launchers must also keep stdin open; closed stdin (including
153
+ `/dev/null`) requests shutdown. Programmatic FastAPI app construction does not
154
+ read stdin.
155
+
92
156
  Web command output preserves the event's plain text, including resume-list
93
157
  numbering and line breaks.
94
158
  Assistant replies render fenced `mermaid` blocks as diagrams. The browser loads
@@ -111,6 +175,9 @@ updating other tab state. A save targets the edited session even if another
111
175
  tab is selected before its response arrives.
112
176
  Selected tabs use a tinted background without a click-focus border; keyboard
113
177
  focus on the title uses an underline.
178
+ The browser page title gains a `●` prefix while any session in the workspace
179
+ is running, using the same session summaries as the tab activity dots. It returns
180
+ to the original workspace title when all sessions are idle.
114
181
 
115
182
  The workspace presents assistant replies as open text and user messages as
116
183
  right-aligned bubbles. Commands and thinking use quieter secondary styling;
@@ -182,7 +249,13 @@ own; unlinking a Feishu card only detaches that frontend. Web submits directly
182
249
  to the backend, including across its worker-thread boundary: it does not run a
183
250
  CLI shell or maintain an extra prompt queue.
184
251
 
185
- Workspace tab persistence follows recording changes as well as explicit titles.
252
+ Workspace state stores every open tab in order, starting when the tab is created.
253
+ An empty tab stores an empty title and rollout path, so restoring it creates a
254
+ fresh empty session without writing a rollout. Manual titles and first messages
255
+ are not prerequisites for keeping a tab across a server restart. Changes to
256
+ automatic or explicit titles and recorded paths update the same saved tab.
257
+ Startup restores the full tab list before saving it; closing a tab removes its
258
+ entry.
186
259
  Once a fork's first turn or successful compact creates its rollout, the saved tab
187
260
  points to that file, including when the model subsequently fails. Before the
188
261
  file exists, the tab stores the recorded ancestor's path with an explicit
@@ -532,7 +605,8 @@ context; `ResponsesModelClient.model` uses the same provider config as metadata.
532
605
 
533
606
  Only the Responses provider classifies external error text:
534
607
 
535
- - Transient/disconnected streams use `ResponsesRetryableError`.
608
+ - Transient/disconnected streams and retryable `response.failed` events
609
+ use `ResponsesRetryableError`.
536
610
  - Context overflow uses `ContextLengthExceeded`, with optional usage and limit
537
611
  parsed from the provider's error details.
538
612
  - A genuine terminal incomplete response uses `ResponsesIncompleteError` and
@@ -540,6 +614,22 @@ Only the Responses provider classifies external error text:
540
614
 
541
615
  Custom clients must raise the corresponding exception rather than rely on
542
616
  arbitrary `RuntimeError` text being reinterpreted by the Agent or compactor.
617
+
618
+ The local `responses_server` validates ordinary function-call arguments after
619
+ assembling the downstream stream. Malformed JSON produces `response.failed` with
620
+ `type = "server_error"` before any final output items are emitted. The error names
621
+ the tool and JSON parsing failure without including raw arguments.
622
+ `ResponsesModelClient` handles this through its generic provider error path and
623
+ retries only the current request under `stream_max_retries`; it does not classify
624
+ tool argument parse failures as retryable locally. Earlier completed tools stay
625
+ in history and retries do not restart the turn. Valid JSON that decodes to a
626
+ non-object still raises a terminal API error; freeform/custom tool inputs remain
627
+ plain text.
628
+
629
+ Assistant message text remains text, including content that resembles a tool
630
+ call. Tool parse errors require a structured tool item or an explicit provider
631
+ error; the client does not infer them from patterns in assistant prose.
632
+
543
633
  For max-output incomplete responses, the Agent persists done assistant/reasoning
544
634
  items for continuation, but not bare text deltas or tool calls without results.
545
635
 
@@ -35,8 +35,9 @@ prompt 渲染路径一致,也保留 provider 自定义 effort 值(例如 `ma
35
35
  - vLLM 历史 `reasoning` item -> assistant message `reasoning` 字段回放
36
36
  - vLLM streaming `usage` -> final `response.completed.response.usage`
37
37
  - 当环境变量 `PYCODEX_DUMP` 存在时,为每条 outcomming 请求附加 `return_token_ids = true`,并把实际下发的 request body、下游 usage/cache、`prompt_token_ids` / `token_ids` 以 JSONL 追加到 `{PYCODEX_DUMP}/dump.jsonl`
38
- - 下游 chat stream 如果半路断开,会转成上游可解析的 `response.failed` 事件,而不是直接截断 HTTP body
38
+ - 下游请求在等待响应头或读流时失败,会返回 `response.failed`,错误直接包含耗时、timeout、失败阶段、响应头/首个 chunk 的时间和 chunk 数量
39
39
  - 普通 function tools
40
+ - 普通 function-call 参数拼接完成后校验 JSON;解析失败返回 `response.failed(type=server_error)`,由客户端按现有策略重试
40
41
  - custom tools 的 function-wrapper 兼容适配
41
42
  - mock `web_search` 接口对齐(返回空结果)
42
43
  - function / custom tool follow-up history 重建
@@ -123,6 +124,14 @@ template 不消费 `developer` role;使用该模板的 provider 应显式配
123
124
  length-stopped thinking 被持久化成 terminal reasoning-only history,并在下一轮转换成
124
125
  下游 chat 后端不接受的裸 assistant message。
125
126
 
127
+ 普通 function-call 的 `arguments` 在拼接完成后由 server 校验 JSON。解析失败时,
128
+ server 在发出本次最终 output items 之前返回 `response.failed(type=server_error)`;
129
+ 错误只包含工具名和解析位置,不包含原始参数。`ResponsesModelClient` 通过通用
130
+ `response.failed` 处理按 `stream_max_retries` 重试当前请求,默认最多重试 5 次。
131
+ 这条路径不增加 server 内部重试,也不把本次失败响应里的工具交给 Agent 执行。
132
+ 合法 JSON 的内容保持原样;非 object 的参数仍由客户端按既有契约报错。
133
+ custom/freeform 工具继续使用原有适配流程,assistant 正文也继续作为文本处理。
134
+
126
135
  `messages` compat 则故意不改这层 canonical request:仍然先构造 chat 风格
127
136
  `outcomming_request`,只有在真正发请求和读 SSE 时,才在边界把它翻译成
128
137
  messages request / event。这样 tool hydration、mock `web_search`
@@ -132,6 +141,11 @@ follow-up、provider payload post-process 仍然复用同一套主逻辑。
132
141
  `max_tokens`,则优先透传上游请求里的 `max_output_tokens` / `max_tokens`;
133
142
  如果上游没给,当前默认补 `32000`,避免直接被下游 `400` 拒绝。
134
143
 
144
+ 下游失败无需开启调试模式。正常错误里的 `phase=waiting_response_headers`
145
+ 表示尚未收到响应头,`waiting_first_chunk` 表示响应头已到但尚无完整 chunk,
146
+ `reading_stream` 表示流已开始;`elapsed` 是本次请求耗时,`idle` 是距离
147
+ 最近一个完整 chunk 的时间。SSE 心跳不计入 chunk 数。
148
+
135
149
  ## 验证
136
150
 
137
151
  当前独立测试:
@@ -4,12 +4,12 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "pycodex-ws"
7
- version = "0.3.0"
7
+ version = "0.3.1"
8
8
  description = "Browser workspace install alias for python-codex"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.6.2"
11
11
  dependencies = [
12
- "python-codex==0.3.0",
12
+ "python-codex==0.3.1",
13
13
  ]
14
14
 
15
15
  [tool.hatch.build.targets.wheel]