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