python-codex 0.3.3__tar.gz → 0.4.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 (167) hide show
  1. python_codex-0.3.3/README.md → python_codex-0.4.1/PKG-INFO +141 -36
  2. python_codex-0.3.3/PKG-INFO → python_codex-0.4.1/README.md +121 -59
  3. {python_codex-0.3.3 → python_codex-0.4.1}/README_ZH.md +105 -22
  4. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/__init__.py +3 -27
  5. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/bootstrap.py +5 -45
  6. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/cli.py +1 -14
  7. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/context.py +1 -1
  8. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/events.py +1 -1
  9. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/model.py +2 -2
  10. python_codex-0.4.1/pycodex/portable/__init__.py +45 -0
  11. python_codex-0.3.3/pycodex/portable.py → python_codex-0.4.1/pycodex/portable/client.py +4 -7
  12. python_codex-0.3.3/pycodex/portable_server.py → python_codex-0.4.1/pycodex/portable/server.py +3 -3
  13. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/protocol.py +1 -1
  14. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/runtime.py +2 -2
  15. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/base_tool.py +1 -1
  16. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/close_agent_tool.py +1 -1
  17. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/code_mode_manager.py +1 -1
  18. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/request_permissions_tool.py +1 -1
  19. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/resume_agent_tool.py +1 -1
  20. {python_codex-0.3.3/pycodex → python_codex-0.4.1/pycodex/tools}/runtime_services.py +5 -5
  21. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/send_input_tool.py +1 -1
  22. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/spawn_agent_tool.py +1 -1
  23. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/unified_exec_manager.py +1 -1
  24. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/update_plan_tool.py +1 -1
  25. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/view_image_tool.py +5 -0
  26. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/wait_agent_tool.py +1 -1
  27. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/utils/__init__.py +0 -4
  28. python_codex-0.4.1/pycodex/utils/_aes_gcm/LICENSE +56 -0
  29. python_codex-0.4.1/pycodex/utils/_aes_gcm/README.md +61 -0
  30. python_codex-0.4.1/pycodex/utils/_aes_gcm/__init__.py +1 -0
  31. python_codex-0.4.1/pycodex/utils/_aes_gcm/aes.py +175 -0
  32. python_codex-0.4.1/pycodex/utils/_aes_gcm/gcm.py +73 -0
  33. python_codex-0.4.1/pycodex/utils/_aes_gcm/source-sha256.json +4 -0
  34. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/utils/async_bridge.py +3 -4
  35. {python_codex-0.3.3/pycodex → python_codex-0.4.1/pycodex/utils}/compat.py +2 -0
  36. python_codex-0.4.1/pycodex/utils/crypto.py +35 -0
  37. python_codex-0.4.1/pycodex/utils/debug.py +36 -0
  38. {python_codex-0.3.3/pycodex → python_codex-0.4.1/pycodex/utils}/doctor.py +3 -3
  39. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/utils/image_utils.py +3 -2
  40. {python_codex-0.3.3/pycodex → python_codex-0.4.1/pycodex/utils}/model_metadata.py +2 -2
  41. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/utils/session_persist.py +28 -0
  42. {python_codex-0.3.3 → python_codex-0.4.1}/pyproject.toml +23 -23
  43. {python_codex-0.3.3 → python_codex-0.4.1}/responses_server/app.py +28 -15
  44. python_codex-0.3.3/.gitattributes +0 -2
  45. python_codex-0.3.3/.github/workflows/publish.yml +0 -201
  46. python_codex-0.3.3/.github/workflows/test.yml +0 -87
  47. python_codex-0.3.3/AGENTS.md +0 -85
  48. python_codex-0.3.3/docs/ALIGNMENT.md +0 -777
  49. python_codex-0.3.3/docs/CONTEXT.md +0 -187
  50. python_codex-0.3.3/docs/RELEASE_0.3.0.md +0 -74
  51. python_codex-0.3.3/docs/RELEASE_0.3.2.md +0 -36
  52. python_codex-0.3.3/docs/RELEASE_0.3.3.md +0 -43
  53. python_codex-0.3.3/docs/RUNTIME.md +0 -1001
  54. python_codex-0.3.3/docs/responses_server/README.md +0 -181
  55. python_codex-0.3.3/packages/pycodex-ws/README.md +0 -21
  56. python_codex-0.3.3/packages/pycodex-ws/pycodex_ws_meta/__init__.py +0 -1
  57. python_codex-0.3.3/packages/pycodex-ws/pyproject.toml +0 -16
  58. python_codex-0.3.3/pycodex/feishu_card.py +0 -793
  59. python_codex-0.3.3/pycodex/feishu_link.py +0 -440
  60. python_codex-0.3.3/pycodex/tools/ipython_tool.py +0 -142
  61. python_codex-0.3.3/pycodex/utils/debug.py +0 -12
  62. python_codex-0.3.3/tests/TESTS.md +0 -407
  63. python_codex-0.3.3/tests/__init__.py +0 -2
  64. python_codex-0.3.3/tests/compare_context_requests.py +0 -485
  65. python_codex-0.3.3/tests/compare_steer_request_bodies.py +0 -689
  66. python_codex-0.3.3/tests/compare_tool_schemas.py +0 -499
  67. python_codex-0.3.3/tests/conftest.py +0 -6
  68. python_codex-0.3.3/tests/fake_responses_server.py +0 -402
  69. python_codex-0.3.3/tests/fakes.py +0 -56
  70. python_codex-0.3.3/tests/responses_server/fake_chat_completions_server.py +0 -512
  71. python_codex-0.3.3/tests/responses_server/test_server.py +0 -2819
  72. python_codex-0.3.3/tests/responses_server/test_trajectory_dump.py +0 -127
  73. python_codex-0.3.3/tests/test_agent.py +0 -1776
  74. python_codex-0.3.3/tests/test_agent_state.py +0 -1718
  75. python_codex-0.3.3/tests/test_async_bridge.py +0 -142
  76. python_codex-0.3.3/tests/test_builtin_tools.py +0 -1613
  77. python_codex-0.3.3/tests/test_cli.py +0 -744
  78. python_codex-0.3.3/tests/test_compactor.py +0 -111
  79. python_codex-0.3.3/tests/test_context.py +0 -491
  80. python_codex-0.3.3/tests/test_doctor.py +0 -166
  81. python_codex-0.3.3/tests/test_events.py +0 -792
  82. python_codex-0.3.3/tests/test_fake_responses_server.py +0 -187
  83. python_codex-0.3.3/tests/test_feishu_card.py +0 -732
  84. python_codex-0.3.3/tests/test_feishu_link.py +0 -332
  85. python_codex-0.3.3/tests/test_feishu_oauth.py +0 -61
  86. python_codex-0.3.3/tests/test_ipython_tool.py +0 -100
  87. python_codex-0.3.3/tests/test_model.py +0 -2402
  88. python_codex-0.3.3/tests/test_owned_workspace_server.py +0 -145
  89. python_codex-0.3.3/tests/test_packaging.py +0 -71
  90. python_codex-0.3.3/tests/test_portable.py +0 -194
  91. python_codex-0.3.3/tests/test_py36_syntax.py +0 -31
  92. python_codex-0.3.3/tests/test_runtime_state.py +0 -860
  93. python_codex-0.3.3/tests/test_session_backend.py +0 -726
  94. python_codex-0.3.3/tests/test_workspace_server.py +0 -1307
  95. python_codex-0.3.3/tests/test_workspace_views.py +0 -948
  96. python_codex-0.3.3/tests/workspace_content_views.mjs +0 -919
  97. python_codex-0.3.3/tools/feishu_oauth.py +0 -185
  98. python_codex-0.3.3/tools/sync_model_prompts.py +0 -39
  99. python_codex-0.3.3/workspace_server/__init__.py +0 -39
  100. python_codex-0.3.3/workspace_server/__main__.py +0 -4
  101. python_codex-0.3.3/workspace_server/app.py +0 -1906
  102. python_codex-0.3.3/workspace_server/content_views.LICENSE.txt +0 -470
  103. python_codex-0.3.3/workspace_server/content_views.css +0 -1
  104. python_codex-0.3.3/workspace_server/content_views.mjs +0 -25
  105. python_codex-0.3.3/workspace_server/editor.css +0 -1
  106. python_codex-0.3.3/workspace_server/editor.mjs +0 -1260
  107. python_codex-0.3.3/workspace_server/frontend/.gitignore +0 -1
  108. python_codex-0.3.3/workspace_server/frontend/build.mjs +0 -98
  109. python_codex-0.3.3/workspace_server/frontend/content_views.css +0 -83
  110. python_codex-0.3.3/workspace_server/frontend/content_views.mjs +0 -668
  111. python_codex-0.3.3/workspace_server/frontend/editor.css +0 -50
  112. python_codex-0.3.3/workspace_server/frontend/editor.mjs +0 -342
  113. python_codex-0.3.3/workspace_server/frontend/package-lock.json +0 -541
  114. python_codex-0.3.3/workspace_server/frontend/package.json +0 -17
  115. python_codex-0.3.3/workspace_server/owned_process.py +0 -27
  116. python_codex-0.3.3/workspace_server/workspace.html +0 -2267
  117. python_codex-0.3.3/workspace_server/workspace_views.py +0 -533
  118. python_codex-0.3.3/workspace_server/workspaces.html +0 -550
  119. python_codex-0.3.3/workspace_server/workspaces.py +0 -632
  120. {python_codex-0.3.3 → python_codex-0.4.1}/.gitignore +0 -0
  121. {python_codex-0.3.3 → python_codex-0.4.1}/LICENSE +0 -0
  122. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/__main__.py +0 -0
  123. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/agent.py +0 -0
  124. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/prompts/default_base_instructions.md +0 -0
  125. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/prompts/models.json +0 -0
  126. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/prompts/permissions/approval_policy/never.md +0 -0
  127. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/prompts/permissions/approval_policy/on_failure.md +0 -0
  128. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/prompts/permissions/approval_policy/on_request.md +0 -0
  129. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/prompts/permissions/approval_policy/on_request_rule_request_permission.md +0 -0
  130. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/prompts/permissions/approval_policy/unless_trusted.md +0 -0
  131. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/prompts/permissions/sandbox_mode/danger_full_access.md +0 -0
  132. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/prompts/permissions/sandbox_mode/read_only.md +0 -0
  133. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/prompts/permissions/sandbox_mode/workspace_write.md +0 -0
  134. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/__init__.py +0 -0
  135. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/agent_tool_schemas.py +0 -0
  136. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/apply_patch_tool.py +0 -0
  137. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/clock_tool.py +0 -0
  138. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/exec_command_tool.py +0 -0
  139. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/exec_runtime.js +0 -0
  140. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/exec_tool.py +0 -0
  141. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/grep_files_tool.py +0 -0
  142. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/list_dir_tool.py +0 -0
  143. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/read_file_tool.py +0 -0
  144. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/request_user_input_tool.py +0 -0
  145. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/shell_command_tool.py +0 -0
  146. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/shell_tool.py +0 -0
  147. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/wait_tool.py +0 -0
  148. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/web_search_tool.py +0 -0
  149. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/tools/write_stdin_tool.py +0 -0
  150. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/utils/compactor.py +0 -0
  151. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/utils/dotenv.py +0 -0
  152. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/utils/event_helpers.py +0 -0
  153. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/utils/get_env.py +0 -0
  154. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/utils/random_ids.py +0 -0
  155. {python_codex-0.3.3 → python_codex-0.4.1}/pycodex/utils/truncation.py +0 -0
  156. {python_codex-0.3.3 → python_codex-0.4.1}/responses_server/__init__.py +0 -0
  157. {python_codex-0.3.3 → python_codex-0.4.1}/responses_server/__main__.py +0 -0
  158. {python_codex-0.3.3 → python_codex-0.4.1}/responses_server/config.py +0 -0
  159. {python_codex-0.3.3 → python_codex-0.4.1}/responses_server/messages_api.py +0 -0
  160. {python_codex-0.3.3 → python_codex-0.4.1}/responses_server/payload_processors.py +0 -0
  161. {python_codex-0.3.3 → python_codex-0.4.1}/responses_server/server.py +0 -0
  162. {python_codex-0.3.3 → python_codex-0.4.1}/responses_server/session_store.py +0 -0
  163. {python_codex-0.3.3 → python_codex-0.4.1}/responses_server/stream_router.py +0 -0
  164. {python_codex-0.3.3 → python_codex-0.4.1}/responses_server/tools/__init__.py +0 -0
  165. {python_codex-0.3.3 → python_codex-0.4.1}/responses_server/tools/custom_adapter.py +0 -0
  166. {python_codex-0.3.3 → python_codex-0.4.1}/responses_server/tools/web_search.py +0 -0
  167. {python_codex-0.3.3 → python_codex-0.4.1}/responses_server/trajectory_dump.py +0 -0
@@ -1,7 +1,29 @@
1
+ Metadata-Version: 2.1
2
+ Name: python-codex
3
+ Version: 0.4.1
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
+
1
21
  # pycodex
2
22
 
3
23
  English README. Chinese version: `README_ZH.md`
4
24
 
25
+ 0.4.0 core/workspace package split and dependency cleanup: [release notes](docs/RELEASE_0.4.0.md).
26
+
5
27
  0.3.3 workspace viewers and reasoning stream fix: [release notes](docs/RELEASE_0.3.3.md).
6
28
 
7
29
  0.3.2 startup fixes: [release notes](docs/RELEASE_0.3.2.md).
@@ -10,15 +32,37 @@ English README. Chinese version: `README_ZH.md`
10
32
 
11
33
  PyPI distributions:
12
34
 
13
- - Primary package: `python-codex`
14
- - Workspace install alias: `pycodex-ws`
35
+ - Core package: `python-codex`
36
+ - Workspace and integrations: `pycodex-ws`
15
37
 
16
38
  The import path remains `pycodex`; the CLI commands are `pycodex` and
17
39
  `pycodex-ws`.
18
40
 
19
- `pycodex-ws` is a thin metapackage that depends on the exact matching
20
- `python-codex` version. The implementation and console script remain owned by
21
- `python-codex`, so the two distributions never install duplicate modules.
41
+ `python-codex` contains the Agent, runtime, CLI, standard tools, Responses
42
+ compatibility server and portable storage support. `pycodex-ws` depends on the
43
+ exact matching core version and contains the browser workspace, viewer assets
44
+ and optional Feishu integration. It owns the
45
+ `workspace_server` and `pycodex_ws` modules and the `pycodex-ws` command.
46
+
47
+ The Feishu SDK is not installed automatically. Install it in the workspace's
48
+ Python environment with `python3 -m pip install lark-oapi` to enable `/link`.
49
+ If `lark_oapi` cannot be imported, `/link` reports that it is unavailable.
50
+
51
+ Core does not require Pillow. Without it, `view_image` sends the original image
52
+ bytes with `detail="original"`, including when `high` is requested. With Pillow
53
+ installed, the default path retains resizing to a maximum dimension of 2048.
54
+ `pycodex-ws` installs Pillow for image preview validation.
55
+
56
+ Portable encryption uses a bundled pure Python AES-GCM subset of pyaes and
57
+ tlslite-ng, under MIT and BSD licenses. Core does not require a crypto package
58
+ or compiler and ships a `py3-none-any` wheel. Existing `PCX1` bundles and call
59
+ tokens remain compatible; large bundles take longer to encrypt and decrypt.
60
+ See the [source notes](pycodex/utils/_aes_gcm/README.md).
61
+
62
+ The Responses compatibility server and workspace HTTP server use Starlette
63
+ and Uvicorn directly. Neither package requires FastAPI, Pydantic or
64
+ `pydantic-core`. Python 3.6 uses Starlette 0.19.x; newer interpreters resolve
65
+ a compatible Starlette release through its Python version metadata.
22
66
 
23
67
  This repository extracts the core Codex agent loop from upstream Codex
24
68
  (`https://github.com/openai/codex`) into a deliberately small Python version,
@@ -38,16 +82,17 @@ Relevant Rust reference points:
38
82
 
39
83
  ## Quick Start
40
84
 
41
- Install the full package or the workspace-oriented alias:
85
+ Install the core CLI, or install the workspace package for the browser frontend:
42
86
 
43
87
  ```bash
44
88
  python3 -m pip install python-codex
45
89
  python3 -m pip install pycodex-ws
46
90
  ```
47
91
 
48
- Use Python 3.6.2 or newer. Both console commands are installed into that
49
- interpreter's scripts directory. On a machine with multiple Python installations,
50
- you can select the same interpreter explicitly for startup:
92
+ The core supports Python 3.6.2 or newer. The workspace package requires Python
93
+ 3.7 or newer. Commands are installed into
94
+ that interpreter's scripts directory. On a machine with multiple Python
95
+ installations, select the same interpreter explicitly for startup:
51
96
 
52
97
  ```bash
53
98
  python3 -m pycodex
@@ -58,10 +103,14 @@ If `pycodex` is missing from `PATH` or resolves to an older installation, use th
58
103
  module entry points. `python3 -m pip --version` shows which Python environment
59
104
  received the installation.
60
105
 
106
+ For an existing Web installation, use `python3 -m pip install --upgrade pycodex-ws`
107
+ to upgrade the workspace and its matching core together. Installing only
108
+ `python-codex` provides the CLI and core API.
109
+
61
110
  For development from this repository, install dependencies first:
62
111
 
63
112
  ```bash
64
- uv sync
113
+ uv sync --dev
65
114
  ```
66
115
 
67
116
  Try the real entry points:
@@ -71,8 +120,22 @@ uv run pycodex "Reply with exactly OK."
71
120
  uv run pycodex
72
121
  ```
73
122
 
74
- A VS Code frontend is available in [`extensions/vscode`](extensions/vscode/README.md).
75
- It reuses an existing `python-codex>=0.3.1` installation. On first use, missing
123
+ Tests are split by package. `uv run --dev pytest` runs both suites;
124
+ `uv run --dev pytest packages/pycodex-ws/tests` runs workspace and integration
125
+ tests. To check core without installing workspace, use a separate
126
+ environment:
127
+
128
+ ```bash
129
+ UV_PROJECT_ENVIRONMENT=.tmp/core-tests uv sync --group test
130
+ UV_PROJECT_ENVIRONMENT=.tmp/core-tests uv run --group test pytest tests
131
+ ```
132
+
133
+ See [test environments and coverage](tests/TESTS.md). Core tests support Python
134
+ 3.6.2+; workspace tests require Python 3.7+.
135
+
136
+ A VS Code frontend is available in [`packages/pycodex-ws/extensions/vscode`](packages/pycodex-ws/extensions/vscode/README.md).
137
+ It belongs to the workspace frontend and is packaged separately as a VSIX.
138
+ It reuses an existing `pycodex-ws>=0.4.0` installation. On first use, missing
76
139
  or older backends are installed/upgraded with pip in the selected Python
77
140
  environment on the workspace host; compatible versions need no network check.
78
141
  It starts a dedicated `pycodex-ws` workspace on the project's host and opens a
@@ -120,19 +183,35 @@ nailing the core tool-augmented reasoning loop first.
120
183
 
121
184
  ## Layout
122
185
 
186
+ The top level of `pycodex/` contains the core components and package entry points.
187
+ Supporting services live in `tools/`, `portable/` and `utils/`.
188
+
189
+ - `pycodex/agent.py`: inner turn loop
190
+ - `pycodex/runtime.py`: session commands, submission queues and frontend event subscriptions
191
+ - `pycodex/model.py`: model client protocol and Responses API adapter
192
+ - `pycodex/context.py`: configuration, prompt assembly and instruction loading
123
193
  - `pycodex/protocol.py`: minimal conversation item / prompt protocol
124
194
  - `pycodex/events.py`: typed events, plain-text views and stateful presentation
125
- - `pycodex/utils/event_helpers.py`: stateless text, color, summary and result-formatting helpers
126
- - `pycodex/utils/image_utils.py`: image loading, resizing and data-URL preparation
127
- - `pycodex/model.py`: model client protocol and Responses API adapter
128
- - `pycodex/cli.py`: single-turn/interactive entry points, terminal I/O executor and input loop
129
195
  - `pycodex/bootstrap.py`: frontend-independent model, tools, Agent and session assembly
196
+ - `pycodex/cli.py`: single-turn/interactive entry points, terminal I/O executor and input loop
130
197
  - `pycodex/tools/base_tool.py`: `BaseTool`, `ToolRegistry`, `ToolContext`
131
- - `pycodex/tools/`: concrete tool implementations
132
- - `pycodex/agent.py`: inner turn loop
133
- - `pycodex/runtime.py`: session commands, submission queues and frontend event subscriptions
134
- - `extensions/vscode/`: VS Code chat frontend using the workspace HTTP API
135
- - `tests/test_agent.py`: core behavior tests
198
+ - `pycodex/tools/runtime_services.py`: plan, sub-agent, user-input and permissions services
199
+ - `pycodex/tools/`: concrete tools and their execution managers
200
+ - `pycodex/portable/`: Codex home storage client and server, both included in core
201
+ - `pycodex/utils/compat.py`: supported-Python compatibility helpers
202
+ - `pycodex/utils/doctor.py`: configuration and connectivity diagnostics
203
+ - `pycodex/utils/model_metadata.py`: vendored model metadata lookup
204
+ - `pycodex/utils/debug.py`: debug paths and the `pycodex.debug()` implementation
205
+ - `pycodex/utils/event_helpers.py`: stateless text, color, summary and result-formatting helpers
206
+ - `pycodex/utils/image_utils.py`: image loading, resizing and data-URL preparation
207
+ - `pycodex/prompts/`: vendored prompts and model metadata
208
+ - `packages/pycodex-ws/workspace_server/`: Web board, API and file/diff viewers
209
+ - `packages/pycodex-ws/pycodex_ws/`: Feishu and workspace assembly
210
+ - `packages/pycodex-ws/extensions/vscode/`: workspace's VS Code frontend, built separately as a VSIX
211
+ - `tests/`: core tests
212
+ - `packages/pycodex-ws/tests/`: workspace and cross-frontend integration tests
213
+
214
+ See [package layout and import paths](docs/RUNTIME.md#package-layout).
136
215
 
137
216
  ## Current Alignment Status
138
217
 
@@ -243,7 +322,7 @@ Current behavior:
243
322
  - with an argv prompt or piped stdin, run a single turn
244
323
  - `pycodex-ws` starts the standalone browser workspace manager and serves each
245
324
  workspace with a board pane and a pycodex session pane
246
- - the left pane is the board, containing a live HTML Canvas tab and closable read-only file/diff tabs that
325
+ - the left pane is the board, containing an optional live HTML Canvas tab and closable read-only file/diff tabs that
247
326
  scroll together in one toolbar; a mouse wheel scrolls overflowing tab strips
248
327
  horizontally.
249
328
  Click Markdown file links such as `[source](src/app.py:42)` or
@@ -262,6 +341,14 @@ Current behavior:
262
341
  hover titles retain the complete paths and revisions. Text,
263
342
  Markdown, HTML, common raster images and text diffs are supported. Monaco
264
343
  provides syntax highlighting, find, folding and character-level diffs.
344
+ Right-click code and choose **Insert into chat**, or select text and use the
345
+ toolbar button, to insert context at the current chat draft's cursor.
346
+ Context appears as a separate block among your words. Click it to edit or
347
+ remove it; Backspace and Delete also remove a block as one element.
348
+ Context identifies the full view label, including both revisions of a diff.
349
+ Selections include only the selected text and range; otherwise it records the
350
+ cursor location. Diff context also identifies the selected side.
351
+ The draft stays editable until you send it.
265
352
  Its code and styles load only when a code or diff view opens, so Canvas,
266
353
  Markdown and HTML previews do not load the editor. Viewer assets use gzip
267
354
  when supported and revalidate cached content with ETags.
@@ -330,28 +417,38 @@ Current behavior:
330
417
  non-persisted follow-up session and submits the file contents as the next
331
418
  user instruction; this is intended for side-effect follow-ups such as
332
419
  Feishu notifications
333
- - `/link <feishu-email|open_id|chat_id>` attaches the current interactive
334
- session to a Feishu card; multiple sessions in the same pycodex process share
420
+ - `/link <feishu-email|open_id|chat_id>` in a workspace session attaches it
421
+ to a Feishu card when the optional `lark-oapi` SDK can be imported;
422
+ multiple sessions in the same workspace process share
335
423
  one Feishu long-connection listener and route card actions by message id
336
424
  - Feishu user OAuth stores its refresh token only in
337
425
  `~/.codex/.feishu_refresh_token` (mode `0600`). Run
338
- `env -u VIRTUAL_ENV uv run python tools/feishu_oauth.py` from this repository
339
- to authorize; app credentials can stay in `~/.codex/.env`. Each refresh reads
426
+ `python3 -m pycodex_ws.feishu_oauth` after installing `pycodex-ws` to authorize.
427
+ From this repository, use `env -u VIRTUAL_ENV uv run --dev python -m pycodex_ws.feishu_oauth`.
428
+ App credentials can stay in `~/.codex/.env`. Each refresh reads
340
429
  the latest token from the dedicated file and saves its replacement there.
341
430
  `FEISHU_REFRESH_TOKEN` in `.env` or the environment is no longer used and
342
431
  can be removed. Refresh is triggered by API calls, with no idle keepalive.
343
432
  - `pycodex-ws --workspace-config workspaces.json` serves workspace boards from
344
433
  one process. The JSON file can be either a list or
345
- `{"workspaces": [...]}`; each entry uses `board` and `work_dir`, with optional
346
- display/URL `id`, and is exposed at `/w/<id>/`. Internally the resolved
347
- `board` path is the workspace identity, so adding the same board path twice is
348
- rejected even with a different name. Relative `board` and `work_dir` paths
434
+ `{"workspaces": [...]}`; each entry uses `state_path` and `work_dir`, with optional
435
+ display/URL `id` and `enable_board` (default `true`), and is exposed at `/w/<id>/`.
436
+ `state_path` is a filename prefix: `/data/demo` uses
437
+ `/data/demo.pycodex-ws.json` for the session index,
438
+ `/data/demo.pycodex-views.json` for file views and `/data/demo.html` for Canvas.
439
+ Suffixes are appended, so prefixes containing dots remain distinct.
440
+ The resolved `state_path` is the workspace identity; duplicates are rejected
441
+ regardless of name or Canvas setting. Relative `state_path` and `work_dir` paths
349
442
  resolve from the config file directory. The root path `/` shows a workspaces
350
- management page with `add_workspace(name=None, dir="./", board=None)` and
351
- `delete(name)` controls; omitted names become `workspace-1`, `workspace-2`,
352
- etc. If `board` is omitted when adding a workspace, pycodex assigns a random
353
- writable `/tmp/pcws-*.html` board path. Add/delete actions and later
354
- session-state saves refresh the JSON file. Board HTML can reference local
443
+ management page with create/delete controls; omitted names become `workspace-1`,
444
+ `workspace-2`, etc. Omitted `state_path` when creating a workspace generates a
445
+ writable `/tmp/pcws-*` prefix. `enable_board: false` hides Canvas and disables its
446
+ routes, polling and context injection; sessions and file views still persist.
447
+ Existing `board: "demo.html"` and `board: false` configurations load with their
448
+ original state paths and are saved in the new format. See the
449
+ [workspace configuration example](packages/pycodex-ws/README.md).
450
+ Add/delete actions and later session-state saves refresh the JSON file.
451
+ Board HTML can reference local
355
452
  images beside the board (including nested paths) with relative URLs; only
356
453
  `image/*` files contained by the board directory are served.
357
454
  Assistant Markdown supports KaTeX formulas with `$...$`, `$$...$$`,
@@ -443,6 +540,13 @@ pycodex doctor --json
443
540
  `Portable Mode` is the quickest way to bring your usual `pycodex` setup into a
444
541
  fresh machine, container, or debug image.
445
542
 
543
+ The storage client and server are both included in `python-codex`. To start a
544
+ local storage service, run this in a separate terminal:
545
+
546
+ ```bash
547
+ python -m pycodex.portable.server --root .tmp/pycodex_storage --host 127.0.0.1 --port 5577
548
+ ```
549
+
446
550
  Use it like this:
447
551
 
448
552
  ```bash
@@ -636,7 +740,8 @@ CLI, Web and Feishu all use `AgentRuntime.submit_input`: not just `/model`
636
740
  and `/resume`, but every session command, steer/queue admission, interactive
637
741
  questions and permissions, state notifications and shutdown. Frontends only
638
742
  adapt input and render events. Web no longer imports or runs the CLI shell.
639
- IPython is the deliberate exception: `ipython_agent()` still returns a bare Agent.
743
+ Workspace assembly uses `pycodex_ws.build_runtime()` to add
744
+ `/link` and `/unlink`; `pycodex.bootstrap.build_runtime()` provides core commands.
640
745
 
641
746
  ```python
642
747
  from pycodex.bootstrap import build_agent, build_model, build_runtime
@@ -1,30 +1,9 @@
1
- Metadata-Version: 2.1
2
- Name: python-codex
3
- Version: 0.3.3
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: fastapi<0.84,>=0.83.0; python_version < '3.7'
9
- Requires-Dist: fastapi>=0.83.0; python_version >= '3.7'
10
- Requires-Dist: importlib-metadata>=4.8.3; python_version < '3.8'
11
- Requires-Dist: loguru>=0.7.3
12
- Requires-Dist: pillow>=8.4.0; python_version < '3.7'
13
- Requires-Dist: pillow>=9.0.0; python_version >= '3.7'
14
- Requires-Dist: prompt-toolkit>=3.0.36
15
- Requires-Dist: pycryptodomex>=3.20
16
- Requires-Dist: pyyaml>=6.0
17
- Requires-Dist: requests>=2.27.1
18
- Requires-Dist: tomli<2,>=1.2.3; python_version < '3.11'
19
- Requires-Dist: typing-extensions>=4.1.1; python_version < '3.8'
20
- Requires-Dist: uvicorn<0.17,>=0.16.0; python_version < '3.7'
21
- Requires-Dist: uvicorn>=0.16.0; python_version >= '3.7'
22
- Description-Content-Type: text/markdown
23
-
24
1
  # pycodex
25
2
 
26
3
  English README. Chinese version: `README_ZH.md`
27
4
 
5
+ 0.4.0 core/workspace package split and dependency cleanup: [release notes](docs/RELEASE_0.4.0.md).
6
+
28
7
  0.3.3 workspace viewers and reasoning stream fix: [release notes](docs/RELEASE_0.3.3.md).
29
8
 
30
9
  0.3.2 startup fixes: [release notes](docs/RELEASE_0.3.2.md).
@@ -33,15 +12,37 @@ English README. Chinese version: `README_ZH.md`
33
12
 
34
13
  PyPI distributions:
35
14
 
36
- - Primary package: `python-codex`
37
- - Workspace install alias: `pycodex-ws`
15
+ - Core package: `python-codex`
16
+ - Workspace and integrations: `pycodex-ws`
38
17
 
39
18
  The import path remains `pycodex`; the CLI commands are `pycodex` and
40
19
  `pycodex-ws`.
41
20
 
42
- `pycodex-ws` is a thin metapackage that depends on the exact matching
43
- `python-codex` version. The implementation and console script remain owned by
44
- `python-codex`, so the two distributions never install duplicate modules.
21
+ `python-codex` contains the Agent, runtime, CLI, standard tools, Responses
22
+ compatibility server and portable storage support. `pycodex-ws` depends on the
23
+ exact matching core version and contains the browser workspace, viewer assets
24
+ and optional Feishu integration. It owns the
25
+ `workspace_server` and `pycodex_ws` modules and the `pycodex-ws` command.
26
+
27
+ The Feishu SDK is not installed automatically. Install it in the workspace's
28
+ Python environment with `python3 -m pip install lark-oapi` to enable `/link`.
29
+ If `lark_oapi` cannot be imported, `/link` reports that it is unavailable.
30
+
31
+ Core does not require Pillow. Without it, `view_image` sends the original image
32
+ bytes with `detail="original"`, including when `high` is requested. With Pillow
33
+ installed, the default path retains resizing to a maximum dimension of 2048.
34
+ `pycodex-ws` installs Pillow for image preview validation.
35
+
36
+ Portable encryption uses a bundled pure Python AES-GCM subset of pyaes and
37
+ tlslite-ng, under MIT and BSD licenses. Core does not require a crypto package
38
+ or compiler and ships a `py3-none-any` wheel. Existing `PCX1` bundles and call
39
+ tokens remain compatible; large bundles take longer to encrypt and decrypt.
40
+ See the [source notes](pycodex/utils/_aes_gcm/README.md).
41
+
42
+ The Responses compatibility server and workspace HTTP server use Starlette
43
+ and Uvicorn directly. Neither package requires FastAPI, Pydantic or
44
+ `pydantic-core`. Python 3.6 uses Starlette 0.19.x; newer interpreters resolve
45
+ a compatible Starlette release through its Python version metadata.
45
46
 
46
47
  This repository extracts the core Codex agent loop from upstream Codex
47
48
  (`https://github.com/openai/codex`) into a deliberately small Python version,
@@ -61,16 +62,17 @@ Relevant Rust reference points:
61
62
 
62
63
  ## Quick Start
63
64
 
64
- Install the full package or the workspace-oriented alias:
65
+ Install the core CLI, or install the workspace package for the browser frontend:
65
66
 
66
67
  ```bash
67
68
  python3 -m pip install python-codex
68
69
  python3 -m pip install pycodex-ws
69
70
  ```
70
71
 
71
- Use Python 3.6.2 or newer. Both console commands are installed into that
72
- interpreter's scripts directory. On a machine with multiple Python installations,
73
- you can select the same interpreter explicitly for startup:
72
+ The core supports Python 3.6.2 or newer. The workspace package requires Python
73
+ 3.7 or newer. Commands are installed into
74
+ that interpreter's scripts directory. On a machine with multiple Python
75
+ installations, select the same interpreter explicitly for startup:
74
76
 
75
77
  ```bash
76
78
  python3 -m pycodex
@@ -81,10 +83,14 @@ If `pycodex` is missing from `PATH` or resolves to an older installation, use th
81
83
  module entry points. `python3 -m pip --version` shows which Python environment
82
84
  received the installation.
83
85
 
86
+ For an existing Web installation, use `python3 -m pip install --upgrade pycodex-ws`
87
+ to upgrade the workspace and its matching core together. Installing only
88
+ `python-codex` provides the CLI and core API.
89
+
84
90
  For development from this repository, install dependencies first:
85
91
 
86
92
  ```bash
87
- uv sync
93
+ uv sync --dev
88
94
  ```
89
95
 
90
96
  Try the real entry points:
@@ -94,8 +100,22 @@ uv run pycodex "Reply with exactly OK."
94
100
  uv run pycodex
95
101
  ```
96
102
 
97
- A VS Code frontend is available in [`extensions/vscode`](extensions/vscode/README.md).
98
- It reuses an existing `python-codex>=0.3.1` installation. On first use, missing
103
+ Tests are split by package. `uv run --dev pytest` runs both suites;
104
+ `uv run --dev pytest packages/pycodex-ws/tests` runs workspace and integration
105
+ tests. To check core without installing workspace, use a separate
106
+ environment:
107
+
108
+ ```bash
109
+ UV_PROJECT_ENVIRONMENT=.tmp/core-tests uv sync --group test
110
+ UV_PROJECT_ENVIRONMENT=.tmp/core-tests uv run --group test pytest tests
111
+ ```
112
+
113
+ See [test environments and coverage](tests/TESTS.md). Core tests support Python
114
+ 3.6.2+; workspace tests require Python 3.7+.
115
+
116
+ A VS Code frontend is available in [`packages/pycodex-ws/extensions/vscode`](packages/pycodex-ws/extensions/vscode/README.md).
117
+ It belongs to the workspace frontend and is packaged separately as a VSIX.
118
+ It reuses an existing `pycodex-ws>=0.4.0` installation. On first use, missing
99
119
  or older backends are installed/upgraded with pip in the selected Python
100
120
  environment on the workspace host; compatible versions need no network check.
101
121
  It starts a dedicated `pycodex-ws` workspace on the project's host and opens a
@@ -143,19 +163,35 @@ nailing the core tool-augmented reasoning loop first.
143
163
 
144
164
  ## Layout
145
165
 
166
+ The top level of `pycodex/` contains the core components and package entry points.
167
+ Supporting services live in `tools/`, `portable/` and `utils/`.
168
+
169
+ - `pycodex/agent.py`: inner turn loop
170
+ - `pycodex/runtime.py`: session commands, submission queues and frontend event subscriptions
171
+ - `pycodex/model.py`: model client protocol and Responses API adapter
172
+ - `pycodex/context.py`: configuration, prompt assembly and instruction loading
146
173
  - `pycodex/protocol.py`: minimal conversation item / prompt protocol
147
174
  - `pycodex/events.py`: typed events, plain-text views and stateful presentation
148
- - `pycodex/utils/event_helpers.py`: stateless text, color, summary and result-formatting helpers
149
- - `pycodex/utils/image_utils.py`: image loading, resizing and data-URL preparation
150
- - `pycodex/model.py`: model client protocol and Responses API adapter
151
- - `pycodex/cli.py`: single-turn/interactive entry points, terminal I/O executor and input loop
152
175
  - `pycodex/bootstrap.py`: frontend-independent model, tools, Agent and session assembly
176
+ - `pycodex/cli.py`: single-turn/interactive entry points, terminal I/O executor and input loop
153
177
  - `pycodex/tools/base_tool.py`: `BaseTool`, `ToolRegistry`, `ToolContext`
154
- - `pycodex/tools/`: concrete tool implementations
155
- - `pycodex/agent.py`: inner turn loop
156
- - `pycodex/runtime.py`: session commands, submission queues and frontend event subscriptions
157
- - `extensions/vscode/`: VS Code chat frontend using the workspace HTTP API
158
- - `tests/test_agent.py`: core behavior tests
178
+ - `pycodex/tools/runtime_services.py`: plan, sub-agent, user-input and permissions services
179
+ - `pycodex/tools/`: concrete tools and their execution managers
180
+ - `pycodex/portable/`: Codex home storage client and server, both included in core
181
+ - `pycodex/utils/compat.py`: supported-Python compatibility helpers
182
+ - `pycodex/utils/doctor.py`: configuration and connectivity diagnostics
183
+ - `pycodex/utils/model_metadata.py`: vendored model metadata lookup
184
+ - `pycodex/utils/debug.py`: debug paths and the `pycodex.debug()` implementation
185
+ - `pycodex/utils/event_helpers.py`: stateless text, color, summary and result-formatting helpers
186
+ - `pycodex/utils/image_utils.py`: image loading, resizing and data-URL preparation
187
+ - `pycodex/prompts/`: vendored prompts and model metadata
188
+ - `packages/pycodex-ws/workspace_server/`: Web board, API and file/diff viewers
189
+ - `packages/pycodex-ws/pycodex_ws/`: Feishu and workspace assembly
190
+ - `packages/pycodex-ws/extensions/vscode/`: workspace's VS Code frontend, built separately as a VSIX
191
+ - `tests/`: core tests
192
+ - `packages/pycodex-ws/tests/`: workspace and cross-frontend integration tests
193
+
194
+ See [package layout and import paths](docs/RUNTIME.md#package-layout).
159
195
 
160
196
  ## Current Alignment Status
161
197
 
@@ -266,7 +302,7 @@ Current behavior:
266
302
  - with an argv prompt or piped stdin, run a single turn
267
303
  - `pycodex-ws` starts the standalone browser workspace manager and serves each
268
304
  workspace with a board pane and a pycodex session pane
269
- - the left pane is the board, containing a live HTML Canvas tab and closable read-only file/diff tabs that
305
+ - the left pane is the board, containing an optional live HTML Canvas tab and closable read-only file/diff tabs that
270
306
  scroll together in one toolbar; a mouse wheel scrolls overflowing tab strips
271
307
  horizontally.
272
308
  Click Markdown file links such as `[source](src/app.py:42)` or
@@ -285,6 +321,14 @@ Current behavior:
285
321
  hover titles retain the complete paths and revisions. Text,
286
322
  Markdown, HTML, common raster images and text diffs are supported. Monaco
287
323
  provides syntax highlighting, find, folding and character-level diffs.
324
+ Right-click code and choose **Insert into chat**, or select text and use the
325
+ toolbar button, to insert context at the current chat draft's cursor.
326
+ Context appears as a separate block among your words. Click it to edit or
327
+ remove it; Backspace and Delete also remove a block as one element.
328
+ Context identifies the full view label, including both revisions of a diff.
329
+ Selections include only the selected text and range; otherwise it records the
330
+ cursor location. Diff context also identifies the selected side.
331
+ The draft stays editable until you send it.
288
332
  Its code and styles load only when a code or diff view opens, so Canvas,
289
333
  Markdown and HTML previews do not load the editor. Viewer assets use gzip
290
334
  when supported and revalidate cached content with ETags.
@@ -353,28 +397,38 @@ Current behavior:
353
397
  non-persisted follow-up session and submits the file contents as the next
354
398
  user instruction; this is intended for side-effect follow-ups such as
355
399
  Feishu notifications
356
- - `/link <feishu-email|open_id|chat_id>` attaches the current interactive
357
- session to a Feishu card; multiple sessions in the same pycodex process share
400
+ - `/link <feishu-email|open_id|chat_id>` in a workspace session attaches it
401
+ to a Feishu card when the optional `lark-oapi` SDK can be imported;
402
+ multiple sessions in the same workspace process share
358
403
  one Feishu long-connection listener and route card actions by message id
359
404
  - Feishu user OAuth stores its refresh token only in
360
405
  `~/.codex/.feishu_refresh_token` (mode `0600`). Run
361
- `env -u VIRTUAL_ENV uv run python tools/feishu_oauth.py` from this repository
362
- to authorize; app credentials can stay in `~/.codex/.env`. Each refresh reads
406
+ `python3 -m pycodex_ws.feishu_oauth` after installing `pycodex-ws` to authorize.
407
+ From this repository, use `env -u VIRTUAL_ENV uv run --dev python -m pycodex_ws.feishu_oauth`.
408
+ App credentials can stay in `~/.codex/.env`. Each refresh reads
363
409
  the latest token from the dedicated file and saves its replacement there.
364
410
  `FEISHU_REFRESH_TOKEN` in `.env` or the environment is no longer used and
365
411
  can be removed. Refresh is triggered by API calls, with no idle keepalive.
366
412
  - `pycodex-ws --workspace-config workspaces.json` serves workspace boards from
367
413
  one process. The JSON file can be either a list or
368
- `{"workspaces": [...]}`; each entry uses `board` and `work_dir`, with optional
369
- display/URL `id`, and is exposed at `/w/<id>/`. Internally the resolved
370
- `board` path is the workspace identity, so adding the same board path twice is
371
- rejected even with a different name. Relative `board` and `work_dir` paths
414
+ `{"workspaces": [...]}`; each entry uses `state_path` and `work_dir`, with optional
415
+ display/URL `id` and `enable_board` (default `true`), and is exposed at `/w/<id>/`.
416
+ `state_path` is a filename prefix: `/data/demo` uses
417
+ `/data/demo.pycodex-ws.json` for the session index,
418
+ `/data/demo.pycodex-views.json` for file views and `/data/demo.html` for Canvas.
419
+ Suffixes are appended, so prefixes containing dots remain distinct.
420
+ The resolved `state_path` is the workspace identity; duplicates are rejected
421
+ regardless of name or Canvas setting. Relative `state_path` and `work_dir` paths
372
422
  resolve from the config file directory. The root path `/` shows a workspaces
373
- management page with `add_workspace(name=None, dir="./", board=None)` and
374
- `delete(name)` controls; omitted names become `workspace-1`, `workspace-2`,
375
- etc. If `board` is omitted when adding a workspace, pycodex assigns a random
376
- writable `/tmp/pcws-*.html` board path. Add/delete actions and later
377
- session-state saves refresh the JSON file. Board HTML can reference local
423
+ management page with create/delete controls; omitted names become `workspace-1`,
424
+ `workspace-2`, etc. Omitted `state_path` when creating a workspace generates a
425
+ writable `/tmp/pcws-*` prefix. `enable_board: false` hides Canvas and disables its
426
+ routes, polling and context injection; sessions and file views still persist.
427
+ Existing `board: "demo.html"` and `board: false` configurations load with their
428
+ original state paths and are saved in the new format. See the
429
+ [workspace configuration example](packages/pycodex-ws/README.md).
430
+ Add/delete actions and later session-state saves refresh the JSON file.
431
+ Board HTML can reference local
378
432
  images beside the board (including nested paths) with relative URLs; only
379
433
  `image/*` files contained by the board directory are served.
380
434
  Assistant Markdown supports KaTeX formulas with `$...$`, `$$...$$`,
@@ -466,6 +520,13 @@ pycodex doctor --json
466
520
  `Portable Mode` is the quickest way to bring your usual `pycodex` setup into a
467
521
  fresh machine, container, or debug image.
468
522
 
523
+ The storage client and server are both included in `python-codex`. To start a
524
+ local storage service, run this in a separate terminal:
525
+
526
+ ```bash
527
+ python -m pycodex.portable.server --root .tmp/pycodex_storage --host 127.0.0.1 --port 5577
528
+ ```
529
+
469
530
  Use it like this:
470
531
 
471
532
  ```bash
@@ -659,7 +720,8 @@ CLI, Web and Feishu all use `AgentRuntime.submit_input`: not just `/model`
659
720
  and `/resume`, but every session command, steer/queue admission, interactive
660
721
  questions and permissions, state notifications and shutdown. Frontends only
661
722
  adapt input and render events. Web no longer imports or runs the CLI shell.
662
- IPython is the deliberate exception: `ipython_agent()` still returns a bare Agent.
723
+ Workspace assembly uses `pycodex_ws.build_runtime()` to add
724
+ `/link` and `/unlink`; `pycodex.bootstrap.build_runtime()` provides core commands.
663
725
 
664
726
  ```python
665
727
  from pycodex.bootstrap import build_agent, build_model, build_runtime