python-codex 0.3.2__tar.gz → 0.3.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- python_codex-0.3.3/.gitattributes +2 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/AGENTS.md +5 -2
- {python_codex-0.3.2 → python_codex-0.3.3}/PKG-INFO +47 -2
- {python_codex-0.3.2 → python_codex-0.3.3}/README.md +46 -1
- {python_codex-0.3.2 → python_codex-0.3.3}/README_ZH.md +29 -1
- python_codex-0.3.3/docs/RELEASE_0.3.3.md +43 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/docs/RUNTIME.md +291 -2
- {python_codex-0.3.2 → python_codex-0.3.3}/docs/responses_server/README.md +21 -3
- {python_codex-0.3.2 → python_codex-0.3.3}/packages/pycodex-ws/pyproject.toml +2 -2
- {python_codex-0.3.2 → python_codex-0.3.3}/pyproject.toml +8 -2
- {python_codex-0.3.2 → python_codex-0.3.3}/responses_server/stream_router.py +17 -13
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/TESTS.md +40 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/responses_server/fake_chat_completions_server.py +14 -1
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/responses_server/test_server.py +39 -2
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/test_model.py +121 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/test_workspace_server.py +2 -2
- python_codex-0.3.3/tests/test_workspace_views.py +948 -0
- python_codex-0.3.3/tests/workspace_content_views.mjs +919 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/workspace_server/app.py +203 -10
- python_codex-0.3.3/workspace_server/content_views.LICENSE.txt +470 -0
- python_codex-0.3.3/workspace_server/content_views.css +1 -0
- python_codex-0.3.3/workspace_server/content_views.mjs +25 -0
- python_codex-0.3.3/workspace_server/editor.css +1 -0
- python_codex-0.3.3/workspace_server/editor.mjs +1260 -0
- python_codex-0.3.3/workspace_server/frontend/.gitignore +1 -0
- python_codex-0.3.3/workspace_server/frontend/build.mjs +98 -0
- python_codex-0.3.3/workspace_server/frontend/content_views.css +83 -0
- python_codex-0.3.3/workspace_server/frontend/content_views.mjs +668 -0
- python_codex-0.3.3/workspace_server/frontend/editor.css +50 -0
- python_codex-0.3.3/workspace_server/frontend/editor.mjs +342 -0
- python_codex-0.3.3/workspace_server/frontend/package-lock.json +541 -0
- python_codex-0.3.3/workspace_server/frontend/package.json +17 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/workspace_server/workspace.html +118 -62
- python_codex-0.3.3/workspace_server/workspace_views.py +533 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/workspace_server/workspaces.html +2 -2
- {python_codex-0.3.2 → python_codex-0.3.3}/workspace_server/workspaces.py +4 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/.github/workflows/publish.yml +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/.github/workflows/test.yml +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/.gitignore +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/LICENSE +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/docs/ALIGNMENT.md +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/docs/CONTEXT.md +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/docs/RELEASE_0.3.0.md +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/docs/RELEASE_0.3.2.md +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/packages/pycodex-ws/README.md +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/packages/pycodex-ws/pycodex_ws_meta/__init__.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/__init__.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/__main__.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/agent.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/bootstrap.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/cli.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/compat.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/context.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/doctor.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/events.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/feishu_card.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/feishu_link.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/model.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/model_metadata.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/portable.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/portable_server.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/prompts/default_base_instructions.md +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/prompts/models.json +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/prompts/permissions/approval_policy/never.md +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/prompts/permissions/approval_policy/on_failure.md +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/prompts/permissions/approval_policy/on_request.md +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/prompts/permissions/approval_policy/on_request_rule_request_permission.md +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/prompts/permissions/approval_policy/unless_trusted.md +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/prompts/permissions/sandbox_mode/danger_full_access.md +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/prompts/permissions/sandbox_mode/read_only.md +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/prompts/permissions/sandbox_mode/workspace_write.md +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/protocol.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/runtime.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/runtime_services.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/__init__.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/agent_tool_schemas.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/apply_patch_tool.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/base_tool.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/clock_tool.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/close_agent_tool.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/code_mode_manager.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/exec_command_tool.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/exec_runtime.js +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/exec_tool.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/grep_files_tool.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/ipython_tool.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/list_dir_tool.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/read_file_tool.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/request_permissions_tool.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/request_user_input_tool.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/resume_agent_tool.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/send_input_tool.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/shell_command_tool.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/shell_tool.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/spawn_agent_tool.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/unified_exec_manager.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/update_plan_tool.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/view_image_tool.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/wait_agent_tool.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/wait_tool.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/web_search_tool.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/tools/write_stdin_tool.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/utils/__init__.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/utils/async_bridge.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/utils/compactor.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/utils/debug.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/utils/dotenv.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/utils/event_helpers.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/utils/get_env.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/utils/image_utils.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/utils/random_ids.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/utils/session_persist.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/pycodex/utils/truncation.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/responses_server/__init__.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/responses_server/__main__.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/responses_server/app.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/responses_server/config.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/responses_server/messages_api.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/responses_server/payload_processors.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/responses_server/server.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/responses_server/session_store.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/responses_server/tools/__init__.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/responses_server/tools/custom_adapter.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/responses_server/tools/web_search.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/responses_server/trajectory_dump.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/__init__.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/compare_context_requests.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/compare_steer_request_bodies.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/compare_tool_schemas.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/conftest.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/fake_responses_server.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/fakes.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/responses_server/test_trajectory_dump.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/test_agent.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/test_agent_state.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/test_async_bridge.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/test_builtin_tools.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/test_cli.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/test_compactor.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/test_context.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/test_doctor.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/test_events.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/test_fake_responses_server.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/test_feishu_card.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/test_feishu_link.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/test_feishu_oauth.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/test_ipython_tool.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/test_owned_workspace_server.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/test_packaging.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/test_portable.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/test_py36_syntax.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/test_runtime_state.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tests/test_session_backend.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tools/feishu_oauth.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/tools/sync_model_prompts.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/workspace_server/__init__.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/workspace_server/__main__.py +0 -0
- {python_codex-0.3.2 → python_codex-0.3.3}/workspace_server/owned_process.py +0 -0
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
- 保持文件结构简洁:会话命令放在 `runtime.py`,CLI 启动、终端 I/O 和输入循环统一放在 `cli.py`;事件、纯文本 `visualize()`、有状态 `render(display)` 及展示状态/提示规则放在 `events.py`,通用无状态文本/颜色/摘要 helpers 放在 `utils/event_helpers.py`,helpers 不反向依赖事件或前端,不维护展示 handler mapping。每个前端实例独占 `EventDisplay`,事件本身保持冻结且不持有展示状态;CLI 只执行 log/status/prompt 回调,飞书卡片复用同一套无色 render,仅保留会话快照和卡片布局适配。飞书灰框按排队/开始/完成边界保留最近完成的回复,新一轮标记 `(*last turn)`、完成后替换;临时活动/问题单独展示,不把事件日志累积进回复正文,不恢复工具/流式/重试等逐事件展示分支。Web/飞书不接收 ANSI 文本。测试按行为归属维护,展示规则在事件层验证,前端只留必要的输入/展示/传输集成回归。
|
|
30
30
|
- Runtime 统一通过 `start()` / `close()` 启停,不恢复公开 `shutdown()` / `run_forever()` 双入口或独立关闭 Future。`Agent.accepts_input` 是本地接纳开关,`runtime.accepts_input` 只读取它;Runtime 关闭时把该开关置为 false,后台唤醒因此停止,已接收请求继续排空。关闭不入队特殊请求,不引入 `begin_shutdown` / `draining`。worker 排空后在命令锁内清理子 Agent 和连接,最后由 `Agent.shutdown()` 一次性清理工具。取消关闭调用方不能取消 worker/turn,Web 线程清理异常也必须 stop/join,不重复关闭底层会话。
|
|
31
31
|
- 交互 CLI 的 Ctrl+C 在 prompt_toolkit 输入边界通过 `c-c` / `<sigint>` 按键绑定以 `EOFError` 正常退出,兼容不支持 `interrupt_exception` 构造参数的 3.0.36,和 `/exit` 共用 `runtime.close()`;不要让输入子 Task 抛出的 `KeyboardInterrupt` 逃到 asyncio runner 后触发全体 Task 清理。单次 Ctrl+C 发起正常退出,接纳关闭事件展示 `[closing]` 等待提示,已有模型/工具/队列继续自然结束,不借此恢复 turn cancellation。关闭等待期间再次 Ctrl+C 是显式强退:CLI 的进程级 SIGINT handler 以状态 130 立即退出,不再进入 asyncio 清理等待;这不改变 Runtime 的正常排空语义,也不保证外部工具进程停止。旧版 prompt_toolkit 会在输入结束时重置 SIGINT,因此关闭边界要重新安装 handler;正常结束或清理报错后恢复原 SIGINT handler。
|
|
32
|
-
- Relative images in board HTML resolve to HTTP siblings such as `/w/<workspace-id>/<image>` (or `/<image>` in single-workspace mode), not to `/board/...`. Keep the image fallback routes after concrete workspace/API routes, resolve files from the board's directory, and reject non-`image/*` files or paths that escape that directory.
|
|
32
|
+
- Relative images in board HTML resolve to HTTP siblings such as `/w/<workspace-id>/<image>` (or `/<image>` in single-workspace mode), not to `/board/...`. Keep the image fallback routes after concrete workspace/API routes, resolve files from the board's directory, and reject non-`image/*` files or paths that escape that directory. Canvas and HTML file previews use ordinary same-origin iframes with the same script permissions; the browser sends login cookies when loading resources. Do not copy cookies into HTML or add image-fetch/data-URL preprocessing.
|
|
33
33
|
- 交互 CLI 的事件流展示优先表达用户可感知的阶段(例如工具开始/完成、模型回看工具结果),不要直接把内部 `iteration` 计数暴露成主要状态文案;`iterations` 应继续保留在 `TurnResult` 等程序化结果里。
|
|
34
34
|
- 在交互 CLI 里,`stream_error` 表示当前 Responses stream attempt 失败且模型客户端可能马上自动重试;不要在这个事件上 `finish_stream()` 输出当前 assistant delta buffer,否则第一次失败 attempt 的文本和重试成功后的最终回复会重复显示。真正 fatal 的失败仍由 `turn_failed` 走通用 flush,保留 partial 输出。
|
|
35
35
|
- prompt/context 相关逻辑统一放在 `pycodex/context.py`:`AgentLoop` 只维护真实会话历史;每轮请求前由 `ContextManager` 注入 base instructions、developer message、`AGENTS.md` 指令和 `<environment_context>`,且这些注入项不写回 history。
|
|
@@ -49,7 +49,10 @@
|
|
|
49
49
|
- Collaboration mode 已按用户要求彻底移除:不保留模式枚举、ContextConfig/ToolContext 模式字段、协作提示模板、`build_agent` 的 `session_mode` 参数或模式门控;不要为了 upstream 对齐重新引入。移除模式不代表启用 `request_user_input`,它的默认不可用返回单独保留。普通 CLI/Web 交互、`update_plan` 和 subagent 与该模式无关,继续保留。
|
|
50
50
|
- `request_user_input` 保留默认工具注册及参数声明,但调用固定返回 `request_user_input is unavailable in Default mode`,不收集答案、不触发 `input_requested`,有交互 handler 也不启用。工具本身无状态,不注入 request manager,不为此恢复模式系统或增加启用开关。通用 runtime 问答/权限服务继续保留。`ToolResult.success` 仅是本地执行状态,不能序列化到 Responses input;恢复旧 rollout 读到 `success` 时也不得重新发送该字段。
|
|
51
51
|
- 当前 `exec` / `wait` 已有一个最小 code-mode 实现:底层通过 Node 子进程运行 JavaScript,自带 `text` / `image` / `store` / `load` / `exit` / `notify` / `yield_control` helper,并允许通过 `tools.<name>(...)` 调回本地已注册工具;`web_search` 当前作为 Responses API provider-native tool declaration 暴露给模型,不经过本地 ToolRegistry 执行。
|
|
52
|
-
- 除明确记录的 pycodex
|
|
52
|
+
- 除明确记录的 pycodex 扩展 `clock` 外,我们支持的 tools 必须和原版 Codex 内置 tools 一一对应:名称、定位、参数形状、交互模型、输出语义都应尽量对齐;不要混合多个原版工具的语义做一个“折中工具”。
|
|
53
|
+
- Workspace 文件/diff 由用户点击 Markdown 链接打开,不再注册 `workspace_display`。普通绝对/相对文件路径兼容 `:行号` / `#L行号`,相对路径基于 workspace cwd;Git 文件用 `git:REV:path`,比较用 `diff:REV:path`、`diff:BASE..HEAD:path`、`diff:REV:path..REV:path` 或 `diff:left..right`,diff 行号用 `#L` / `#R`。Git 路径沿用仓库根目录语义,显式 `./` / `../` 基于 workspace cwd;路径特殊字符编码,比较端点文件名中的字面 `..` 编码成 `%2E%2E`。点击经 `POST api/views/open`,独立于 Agent/Runtime,不产生模型消息或工具结果,空 `--toolset` 仍可使用。同一来源或有序比较复用标签的 id、顺序和阅读状态;身份按规范化路径或 Git 仓库/解析后版本/路径判定,标题保留完整路径和版本。本地文件跟随磁盘,Git 内容固定;来源、最新内容、打开列表及 reveal counter 原子保存在 Board 同名 `.pycodex-views.json`。旧记录无来源时保持固定快照。只有发起点击的页面消费返回的 selection/reveal 并切换焦点,轮询只更新列表与内容,不跟随其他页面打开;关闭不修改源文件或会话。
|
|
54
|
+
- Workspace 代码与 diff 查看器使用只读 Monaco;源码和固定依赖在 `workspace_server/frontend/`,修改后用该目录的 `npm ci` / `npm run build` 更新生成的 JS/CSS 和许可证,不手改构建产物。首屏 `content_views.mjs` / CSS 只含轻量控制层,首次打开源码/diff 才并行加载独立的同源 `editor.mjs` / CSS,等待两者完成后创建编辑器;Canvas/Markdown/HTML/图片预览不加载 Monaco。编辑器保留全部语言高亮及查看器需要的功能,JSON 直接注册 tokenizer 和语言配置,避免语言服务入口间接带回全量功能。diff worker 从内嵌 Blob URL 启动,字体内嵌编辑器 CSS,随 wheel 分发,Python 启动不依赖 Node。静态资源按 Accept-Encoding 协商 gzip,压缩结果按应用实例缓存并随文件变化失效,使用 Vary/ETag/304;不对会话流加压缩,不在 wheel 重复保存压缩副本。`MonacoEnvironment.getWorker` 是库要求的全局 host hook,其余查看器状态按实例维护。只保留活动视图的编辑器和 model,切换时先保存阅读状态再隐藏/销毁;diff 按内容容器宽度调整原实例布局。沿用视图列表的两秒轮询检查已记录路径,按内容 revision 失效缓存;内容更新复用编辑器/model 并保留阅读状态,不切换标签或手机面板。标签按 VS Code 的文件名/次要说明方式展示:优先显示 basename,同名不同路径才补最短可区分目录,Git 版本用 7 位 commit ID 作为次要说明,同一路径比较只显示一次文件名。宽度上限复用会话标签规则,次要说明先截断,完整标题继续用于悬停、存储及 API 回复。内容/会话标签栏将普通竖向鼠标滚轮转换成横向滚动,保留原生横向、Shift/Ctrl 手势。
|
|
55
|
+
- Workspace 整个左侧展示区称为看板,实时 HTML 首标签称为 Canvas;内部 `board` id、配置字段及路由名保持不变。装配提示沿用自然语言介绍 Canvas 文件的位置、修改实时可见及可随时创建/修改,再简要说明回复和 Canvas 中可用的扩展链接、行号后缀和返回 Canvas 的链接;不规定展示策略,也不加入冗长实现细节。`workspace://canvas` 返回现有实时页面;Markdown 和 HTML(含 `.htm`)文件链接默认预览,带行号打开源码,再次点击无行号链接在同一标签回到预览。普通刷新和手动切换保留本页所选模式及各自阅读位置,不重放旧定位;行号和模式不参与标签身份/标题,其他页面的打开不改变本页模式。Canvas 是常驻的同源 HTML iframe,普通 HTML 预览沿用同源权限及浏览器原生资源加载;预览的父页面消息只用于滚动位置恢复并校验 frame 和文档代数,无变化时保留当前实例,切换时释放。Canvas 普通 HTML 导航保持不变,仅接管显式 git/diff/Canvas URI。异步打开/创建/更新/diff 完成时只应用当前请求;Markdown/HTML diff 始终显示代码。窄屏定位 diff 左侧时保留双栏,聚焦/定位右侧恢复自动布局。
|
|
53
56
|
- 代码风格上不要使用 `*` 定义 keyword-only 参数;接口默认允许位置参数。
|
|
54
57
|
- runtime 包和会在 import 阶段执行的测试辅助代码需要保持 Python 3.6.2 语法兼容;不要引入 walrus `:=`、`match` 等仅 3.8+/3.10+ 可解析的新语法,哪怕分支在运行时不会走到。
|
|
55
58
|
- 本仓库使用 `uv`;本地默认没有预装测试依赖,开始工作前先跑 `uv sync --dev`,验证用 `uv run pytest`。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-codex
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: A minimal Python extraction of Codex's main agent loop
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Python: >=3.6.2
|
|
@@ -25,6 +25,8 @@ Description-Content-Type: text/markdown
|
|
|
25
25
|
|
|
26
26
|
English README. Chinese version: `README_ZH.md`
|
|
27
27
|
|
|
28
|
+
0.3.3 workspace viewers and reasoning stream fix: [release notes](docs/RELEASE_0.3.3.md).
|
|
29
|
+
|
|
28
30
|
0.3.2 startup fixes: [release notes](docs/RELEASE_0.3.2.md).
|
|
29
31
|
|
|
30
32
|
0.3.0 release preparation and Python API migration: `docs/RELEASE_0.3.0.md`.
|
|
@@ -264,6 +266,45 @@ Current behavior:
|
|
|
264
266
|
- with an argv prompt or piped stdin, run a single turn
|
|
265
267
|
- `pycodex-ws` starts the standalone browser workspace manager and serves each
|
|
266
268
|
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
|
|
270
|
+
scroll together in one toolbar; a mouse wheel scrolls overflowing tab strips
|
|
271
|
+
horizontally.
|
|
272
|
+
Click Markdown file links such as `[source](src/app.py:42)` or
|
|
273
|
+
`[source](/data/project/app.py#L42)` to open a view. Git-style diff links use
|
|
274
|
+
`[changes](diff:HEAD:src/app.py)` for working changes,
|
|
275
|
+
`[versions](diff:HEAD~1..HEAD:src/app.py)` for two revisions, and
|
|
276
|
+
`[files](diff:old.py..new.py)` for two local files. Append `#L42` or `#R42`
|
|
277
|
+
to a diff link to select a 1-based line on the left or right.
|
|
278
|
+
Views open when the user clicks; model replies alone do not create tabs.
|
|
279
|
+
Reopening the same file or comparison selects its existing tab in that
|
|
280
|
+
browser, including when the line changes. Other pages keep their selection.
|
|
281
|
+
Tabs show filenames first, adding a short directory suffix
|
|
282
|
+
for duplicate names. Git versions use short commit IDs in a secondary label.
|
|
283
|
+
Long labels use the same width limit as session tabs, shortening the secondary
|
|
284
|
+
information before the filename;
|
|
285
|
+
hover titles retain the complete paths and revisions. Text,
|
|
286
|
+
Markdown, HTML, common raster images and text diffs are supported. Monaco
|
|
287
|
+
provides syntax highlighting, find, folding and character-level diffs.
|
|
288
|
+
Its code and styles load only when a code or diff view opens, so Canvas,
|
|
289
|
+
Markdown and HTML previews do not load the editor. Viewer assets use gzip
|
|
290
|
+
when supported and revalidate cached content with ETags.
|
|
291
|
+
Narrow content panes automatically use a unified diff; targeting the left
|
|
292
|
+
side keeps both sides visible. Markdown and HTML links default to previews;
|
|
293
|
+
line targets open their source. Clicking a link without a line returns that
|
|
294
|
+
same tab to preview. Manual tab switches preserve the chosen mode and reading state.
|
|
295
|
+
All views remain
|
|
296
|
+
read-only, with text selection and copying available. Local files update
|
|
297
|
+
automatically, including both sides of file diffs and the working side of
|
|
298
|
+
Git diffs; Git revisions stay fixed. Updates keep the active editor and
|
|
299
|
+
reading state without replaying an earlier line request.
|
|
300
|
+
Canvas is for explanations and custom visuals; source and diffs use links.
|
|
301
|
+
`[Canvas](workspace://canvas)` returns to the live page; an HTML file link opens
|
|
302
|
+
a preview tab, and `page.html:1` opens its source. File links work even with an empty `--toolset`.
|
|
303
|
+
Canvas and HTML previews share the same origin and script permissions;
|
|
304
|
+
the browser loads their resources with the workspace login cookie.
|
|
305
|
+
Open views survive reloads and server restarts. See
|
|
306
|
+
[workspace content views](docs/RUNTIME.md#workspace-content-views) for the
|
|
307
|
+
complete syntax and limits.
|
|
267
308
|
- interactive mode exits through `/exit`, `/quit`, Ctrl+D on an empty prompt,
|
|
268
309
|
or a single Ctrl+C; accepted work finishes before cleanup and normal exit.
|
|
269
310
|
A `[closing]` message explains the wait. Press Ctrl+C again while closing
|
|
@@ -366,7 +407,7 @@ Current behavior:
|
|
|
366
407
|
time when it expires.
|
|
367
408
|
- while a background command or clock is pending, the idle status is
|
|
368
409
|
`idle: sleeping`
|
|
369
|
-
- only the active workspace tab shows its close button; closing it restores
|
|
410
|
+
- only the active workspace session tab shows its close button; closing it restores
|
|
370
411
|
the selected tab's conversation, draft, and scroll position immediately
|
|
371
412
|
after its snapshot loads
|
|
372
413
|
- `--vllm-endpoint http://host:port` automatically launches a local
|
|
@@ -387,6 +428,10 @@ Current behavior:
|
|
|
387
428
|
reasoning, retries the same downstream request once, and only then emits
|
|
388
429
|
`response.failed` with `type = "model_output_invalid"` if the retry is still
|
|
389
430
|
reasoning-only
|
|
431
|
+
- compat reasoning fragments are forwarded immediately as
|
|
432
|
+
`response.reasoning_text.delta`, including thinking from `/v1/messages`.
|
|
433
|
+
Continuous reasoning keeps the client's read idle timeout from expiring
|
|
434
|
+
while it waits for assistant text; only finalized reasoning items enter history
|
|
390
435
|
- malformed JSON in ordinary downstream function-call arguments is detected by
|
|
391
436
|
`responses_server` and returned as `response.failed` with `type = "server_error"`;
|
|
392
437
|
the client's existing `stream_max_retries` policy retries the current request
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
English README. Chinese version: `README_ZH.md`
|
|
4
4
|
|
|
5
|
+
0.3.3 workspace viewers and reasoning stream fix: [release notes](docs/RELEASE_0.3.3.md).
|
|
6
|
+
|
|
5
7
|
0.3.2 startup fixes: [release notes](docs/RELEASE_0.3.2.md).
|
|
6
8
|
|
|
7
9
|
0.3.0 release preparation and Python API migration: `docs/RELEASE_0.3.0.md`.
|
|
@@ -241,6 +243,45 @@ Current behavior:
|
|
|
241
243
|
- with an argv prompt or piped stdin, run a single turn
|
|
242
244
|
- `pycodex-ws` starts the standalone browser workspace manager and serves each
|
|
243
245
|
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
|
|
247
|
+
scroll together in one toolbar; a mouse wheel scrolls overflowing tab strips
|
|
248
|
+
horizontally.
|
|
249
|
+
Click Markdown file links such as `[source](src/app.py:42)` or
|
|
250
|
+
`[source](/data/project/app.py#L42)` to open a view. Git-style diff links use
|
|
251
|
+
`[changes](diff:HEAD:src/app.py)` for working changes,
|
|
252
|
+
`[versions](diff:HEAD~1..HEAD:src/app.py)` for two revisions, and
|
|
253
|
+
`[files](diff:old.py..new.py)` for two local files. Append `#L42` or `#R42`
|
|
254
|
+
to a diff link to select a 1-based line on the left or right.
|
|
255
|
+
Views open when the user clicks; model replies alone do not create tabs.
|
|
256
|
+
Reopening the same file or comparison selects its existing tab in that
|
|
257
|
+
browser, including when the line changes. Other pages keep their selection.
|
|
258
|
+
Tabs show filenames first, adding a short directory suffix
|
|
259
|
+
for duplicate names. Git versions use short commit IDs in a secondary label.
|
|
260
|
+
Long labels use the same width limit as session tabs, shortening the secondary
|
|
261
|
+
information before the filename;
|
|
262
|
+
hover titles retain the complete paths and revisions. Text,
|
|
263
|
+
Markdown, HTML, common raster images and text diffs are supported. Monaco
|
|
264
|
+
provides syntax highlighting, find, folding and character-level diffs.
|
|
265
|
+
Its code and styles load only when a code or diff view opens, so Canvas,
|
|
266
|
+
Markdown and HTML previews do not load the editor. Viewer assets use gzip
|
|
267
|
+
when supported and revalidate cached content with ETags.
|
|
268
|
+
Narrow content panes automatically use a unified diff; targeting the left
|
|
269
|
+
side keeps both sides visible. Markdown and HTML links default to previews;
|
|
270
|
+
line targets open their source. Clicking a link without a line returns that
|
|
271
|
+
same tab to preview. Manual tab switches preserve the chosen mode and reading state.
|
|
272
|
+
All views remain
|
|
273
|
+
read-only, with text selection and copying available. Local files update
|
|
274
|
+
automatically, including both sides of file diffs and the working side of
|
|
275
|
+
Git diffs; Git revisions stay fixed. Updates keep the active editor and
|
|
276
|
+
reading state without replaying an earlier line request.
|
|
277
|
+
Canvas is for explanations and custom visuals; source and diffs use links.
|
|
278
|
+
`[Canvas](workspace://canvas)` returns to the live page; an HTML file link opens
|
|
279
|
+
a preview tab, and `page.html:1` opens its source. File links work even with an empty `--toolset`.
|
|
280
|
+
Canvas and HTML previews share the same origin and script permissions;
|
|
281
|
+
the browser loads their resources with the workspace login cookie.
|
|
282
|
+
Open views survive reloads and server restarts. See
|
|
283
|
+
[workspace content views](docs/RUNTIME.md#workspace-content-views) for the
|
|
284
|
+
complete syntax and limits.
|
|
244
285
|
- interactive mode exits through `/exit`, `/quit`, Ctrl+D on an empty prompt,
|
|
245
286
|
or a single Ctrl+C; accepted work finishes before cleanup and normal exit.
|
|
246
287
|
A `[closing]` message explains the wait. Press Ctrl+C again while closing
|
|
@@ -343,7 +384,7 @@ Current behavior:
|
|
|
343
384
|
time when it expires.
|
|
344
385
|
- while a background command or clock is pending, the idle status is
|
|
345
386
|
`idle: sleeping`
|
|
346
|
-
- only the active workspace tab shows its close button; closing it restores
|
|
387
|
+
- only the active workspace session tab shows its close button; closing it restores
|
|
347
388
|
the selected tab's conversation, draft, and scroll position immediately
|
|
348
389
|
after its snapshot loads
|
|
349
390
|
- `--vllm-endpoint http://host:port` automatically launches a local
|
|
@@ -364,6 +405,10 @@ Current behavior:
|
|
|
364
405
|
reasoning, retries the same downstream request once, and only then emits
|
|
365
406
|
`response.failed` with `type = "model_output_invalid"` if the retry is still
|
|
366
407
|
reasoning-only
|
|
408
|
+
- compat reasoning fragments are forwarded immediately as
|
|
409
|
+
`response.reasoning_text.delta`, including thinking from `/v1/messages`.
|
|
410
|
+
Continuous reasoning keeps the client's read idle timeout from expiring
|
|
411
|
+
while it waits for assistant text; only finalized reasoning items enter history
|
|
367
412
|
- malformed JSON in ordinary downstream function-call arguments is detected by
|
|
368
413
|
`responses_server` and returned as `response.failed` with `type = "server_error"`;
|
|
369
414
|
the client's existing `stream_max_retries` policy retries the current request
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
中文 README。English version: `README.md`
|
|
4
4
|
|
|
5
|
+
0.3.3 看板文件/diff 查看器与推理流修复:[发布说明](docs/RELEASE_0.3.3.md)。
|
|
6
|
+
|
|
5
7
|
0.3.2 启动修复:[发布说明](docs/RELEASE_0.3.2.md)。
|
|
6
8
|
|
|
7
9
|
0.3.0 发版准备与 Python API 迁移说明:`docs/RELEASE_0.3.0.md`。
|
|
@@ -260,8 +262,34 @@ pycodex doctor
|
|
|
260
262
|
- 当前默认工具集由上游对齐子集和 pycodex 的 `clock` 扩展组成:`shell`、`shell_command`、`exec_command`、`write_stdin`、`clock`、`exec`、`wait`、`web_search`、`update_plan`、`request_user_input`、`request_permissions`、`spawn_agent`、`send_input`、`resume_agent`、`wait_agent`、`close_agent`、`apply_patch`、`grep_files`、`read_file`、`list_dir`、`view_image`
|
|
261
263
|
- `clock(period_m)` 为当前 Agent session 设置一个周期计时器,传 `null` 取消;每次回复后重新计时,到期后用包含当前时区时间的 `<clock_tick>` 消息唤醒 Agent
|
|
262
264
|
- 后台命令或 clock 正在等待时,空闲状态统一显示为 `idle: sleeping`
|
|
263
|
-
- workspace
|
|
265
|
+
- workspace 整个左侧展示区称为看板,包含实时 HTML Canvas 首标签和只读文件/diff 标签;
|
|
266
|
+
所有标签在同一行共同滚动,溢出时可用鼠标滚轮横向滚动。点击普通 Markdown 文件链接
|
|
267
|
+
`[源码](src/app.py:42)` 或 `[源码](/data/project/app.py#L42)` 即可打开视图。
|
|
268
|
+
diff 使用 Git 风格链接:`[工作区改动](diff:HEAD:src/app.py)`、
|
|
269
|
+
`[两个版本](diff:HEAD~1..HEAD:src/app.py)`、`[两个文件](diff:old.py..new.py)`。
|
|
270
|
+
只有用户点击后才创建或复用标签,模型输出链接本身不会打开文件。
|
|
271
|
+
再次打开同一文件或比较会切换到已有标签,保持原有顺序;其他页面的焦点不变。
|
|
272
|
+
diff 后加 `#L42` / `#R42` 可居中并选中左/右侧第 42 行,行号从 1 开始。
|
|
273
|
+
行号不参与标签身份,改变行号仍复用已有标签;普通刷新不会重放旧定位。
|
|
274
|
+
标签优先显示文件名,同名文件补充可区分的短目录;Git 版本用短 commit ID 作为次要说明。
|
|
275
|
+
长标题沿用会话标签的宽度上限,优先截断次要说明,悬停标题保留完整路径和 Git revision。
|
|
276
|
+
支持文本、Markdown、HTML、PNG/JPEG/GIF/WebP 和文本 diff。
|
|
277
|
+
代码与 diff 使用 Monaco,支持语法高亮、查找、折叠、字符级差异和差异跳转;
|
|
278
|
+
编辑器代码和样式在首次打开源码或 diff 时才加载,Canvas、Markdown/HTML 预览
|
|
279
|
+
不加载 Monaco。查看器静态资源支持 gzip 传输及 ETag 缓存校验。
|
|
280
|
+
内容区变窄时自动切换单栏 diff,定位左侧行号时保留双栏以显示目标。
|
|
281
|
+
Markdown 和 HTML 不带行号时默认预览,带行号时显示源码;再次点击无行号链接会在
|
|
282
|
+
同一标签回到预览,手动切换标签保留所选模式和阅读位置。用户可以选中复制,不能在查看器中修改源文件。
|
|
283
|
+
本地文件自动更新,包括文件 diff 两侧和 Git diff 的工作区一侧;Git 提交版本保持固定。
|
|
284
|
+
更新复用当前编辑器并保留阅读状态。Canvas 用于导读和自定义图示,源码和 diff 通过链接查看;
|
|
285
|
+
`[Canvas](workspace://canvas)` 返回实时页面,HTML 文件链接打开预览标签,`page.html:1` 查看源码。
|
|
286
|
+
Canvas 与 HTML 预览使用相同的同源权限,资源请求由浏览器自动携带工作区登录 cookie。
|
|
287
|
+
空 `--toolset` 不影响用户点击文件链接。
|
|
288
|
+
用户可关闭标签,打开列表和视图在刷新及服务重启
|
|
289
|
+
后恢复,完整语法和大小限制见 [workspace content views](docs/RUNTIME.md#workspace-content-views)。
|
|
290
|
+
- workspace 右侧会话区只在当前活动 tab 上显示关闭按钮
|
|
264
291
|
- `--vllm-endpoint http://host:port` 会自动拉起一个本地 `responses_server` compat 层;当 path 为空时会内部补 `/v1`,继续把 `/responses` 请求转到下游 `/v1/chat/completions`。这条本地 compat 路径始终使用标准 Responses request shape,即使所选模型的 metadata 打开了 `responses_lite` 也不会切换成 lite wire format;启动时还会读取下游 `/v1/models`,自动使用返回列表中的最后一个 model id。当前对 `model_provider = "vllm"` 已补上 reasoning 兼容:会把 chat chunk 里的 `reasoning` / `reasoning_content` 翻回 Responses `reasoning` item,并把历史里的 `reasoning` item 回放成下游 assistant message 的 `reasoning` 字段;同时会向 vLLM 请求 streaming usage,并在最终 `response.completed.response.usage` 中回传
|
|
292
|
+
- compat 层会立即将 reasoning 分片转发为 `response.reasoning_text.delta`,也适用于 `/v1/messages` 的 thinking 流。持续到达的思考分片可避免等待正文期间触发客户端读空闲超时;只有最终确认的 reasoning item 才进入历史
|
|
265
293
|
- `pycodex doctor` 会检查配置、`.env`、API key、DNS、TCP/TLS,以及可选的 live Responses API 请求
|
|
266
294
|
|
|
267
295
|
它目前主要用于:
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# 0.3.3 release notes
|
|
2
|
+
|
|
3
|
+
Both `python-codex` and `pycodex-ws` use version **0.3.3**. The workspace
|
|
4
|
+
metapackage depends on exactly `python-codex==0.3.3`; publish the core package
|
|
5
|
+
first.
|
|
6
|
+
|
|
7
|
+
## Workspace file and diff viewers
|
|
8
|
+
|
|
9
|
+
- Open file, Git revision, and diff links from replies or Canvas in persistent
|
|
10
|
+
board tabs. Source and diff views use read-only Monaco editors with line
|
|
11
|
+
navigation and syntax highlighting.
|
|
12
|
+
- Preview Markdown, HTML, and images directly. Add a line number to a Markdown
|
|
13
|
+
or HTML link to open its source; click the link without a line number to return
|
|
14
|
+
to the preview in the same tab. `workspace://canvas` returns to Canvas.
|
|
15
|
+
- Local files refresh as they change, while Git views retain their resolved
|
|
16
|
+
revision. Each browser page keeps its own active tab, preview mode, and reading
|
|
17
|
+
position. Opening a view does not submit a message to the agent.
|
|
18
|
+
- Load the editor only when opening source or a diff. The wheel includes editor
|
|
19
|
+
assets, fonts, and the diff worker, so installed workspaces do not need Node.
|
|
20
|
+
Static assets support cached gzip responses and conditional requests.
|
|
21
|
+
|
|
22
|
+
## Reasoning stream fix
|
|
23
|
+
|
|
24
|
+
Chat-completions compatibility mode now forwards `reasoning` and
|
|
25
|
+
`reasoning_content` increments immediately as `response.reasoning_text.delta`
|
|
26
|
+
events. Long reasoning phases continue to send data to the client, avoiding
|
|
27
|
+
stream idle timeouts while the backend is still generating.
|
|
28
|
+
|
|
29
|
+
Repeated reasoning aliases are deduplicated. Final reasoning items and the
|
|
30
|
+
existing retry behavior for responses containing only reasoning are preserved.
|
|
31
|
+
Regression coverage includes a client request whose reasoning phase lasts
|
|
32
|
+
longer than its configured stream idle timeout.
|
|
33
|
+
|
|
34
|
+
## Upgrade
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
python3 -m pip install --upgrade pycodex-ws==0.3.3
|
|
38
|
+
python3 -m pycodex
|
|
39
|
+
python3 -m workspace_server --help
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Installing `pycodex-ws==0.3.3` also installs `python-codex==0.3.3`. The import
|
|
43
|
+
path and both console commands remain unchanged.
|
|
@@ -163,7 +163,7 @@ under `Diagram source`. Loading or syntax errors leave the source visible.
|
|
|
163
163
|
A render that finishes after its conversation view has been replaced is discarded;
|
|
164
164
|
completed diagrams keep the view at the bottom only if it is already there.
|
|
165
165
|
|
|
166
|
-
Closing the active workspace tab uses the same browser transition as selecting
|
|
166
|
+
Closing the active workspace session tab uses the same browser transition as selecting
|
|
167
167
|
another tab: reset that tab's render signature, restore its draft and scroll
|
|
168
168
|
position, and request its snapshot immediately. A pending poll for the previous
|
|
169
169
|
tab is aborted; late responses or errors cannot overwrite the selected tab.
|
|
@@ -249,7 +249,7 @@ own; unlinking a Feishu card only detaches that frontend. Web submits directly
|
|
|
249
249
|
to the backend, including across its worker-thread boundary: it does not run a
|
|
250
250
|
CLI shell or maintain an extra prompt queue.
|
|
251
251
|
|
|
252
|
-
Workspace state stores every open tab in order, starting when the tab is created.
|
|
252
|
+
Workspace state stores every open session tab in order, starting when the tab is created.
|
|
253
253
|
An empty tab stores an empty title and rollout path, so restoring it creates a
|
|
254
254
|
fresh empty session without writing a rollout. Manual titles and first messages
|
|
255
255
|
are not prerequisites for keeping a tab across a server restart. Changes to
|
|
@@ -438,6 +438,295 @@ completed/failed status, but active execution and pending inputs take precedence
|
|
|
438
438
|
when reporting busy. Direct calls and background invokes produce the same events;
|
|
439
439
|
status observers no longer inspect raw Task results.
|
|
440
440
|
|
|
441
|
+
## Workspace content views
|
|
442
|
+
|
|
443
|
+
The entire left workspace pane is the board. It starts with a permanent Canvas
|
|
444
|
+
tab for live HTML, followed by read-only file/diff tabs. All tabs scroll together horizontally.
|
|
445
|
+
The tab strips hide their native scrollbars so overflowing tabs stay vertically
|
|
446
|
+
aligned with the toolbar buttons while remaining scrollable.
|
|
447
|
+
Both content and session tab strips translate ordinary vertical mouse-wheel
|
|
448
|
+
input into horizontal scrolling. Horizontal trackpad gestures, Shift+wheel and
|
|
449
|
+
Ctrl+wheel retain their browser behavior; wheel input is consumed only when
|
|
450
|
+
the strip actually moves.
|
|
451
|
+
The file/diff toolbar beneath the tabs keeps its complete path labels in a
|
|
452
|
+
horizontal scroll area with no visible scrollbar. It uses the same wheel
|
|
453
|
+
behavior as the tab strips; the action buttons stay outside the scrolling area.
|
|
454
|
+
Canvas keeps its existing live HTML and image routes. Clicking a Markdown file
|
|
455
|
+
or diff link selects the existing tab for that target, or appends a new read-only
|
|
456
|
+
tab when the target is not already open. Rendering a link does not open a view.
|
|
457
|
+
Reusing a tab keeps its ID, order and reading state. On mobile, selecting a view reveals the content pane. Switching
|
|
458
|
+
tabs preserves their scroll positions; closing a view removes that tab
|
|
459
|
+
without changing a source file, the Canvas or a conversation.
|
|
460
|
+
|
|
461
|
+
Content tabs share the session tabs' rounded styling and show a close button
|
|
462
|
+
only on the active content tab. They occupy the same 44-pixel toolbar as the
|
|
463
|
+
workspace back link and mobile pane switch. The Canvas tab's hover title labels
|
|
464
|
+
`Workspace`, `Current dir`, and `Canvas file` on separate lines.
|
|
465
|
+
Diff views start with the two file labels; the opening time, read-only status
|
|
466
|
+
and whether the view follows files are available in the tab's hover title.
|
|
467
|
+
Stored titles, API responses and hover titles retain complete paths and both Git revisions, for example
|
|
468
|
+
`COMMIT_A:src/A.py <-> COMMIT_B:src/A.py` for full commit IDs. A symbolic or
|
|
469
|
+
abbreviated revision also includes its resolved ID, such as `HEAD (COMMIT):src/A.py`,
|
|
470
|
+
so comparisons opened before and after a branch moves remain distinguishable.
|
|
471
|
+
Visible labels follow VS Code's filename/description layout: the basename is
|
|
472
|
+
the main label, with no leading directory. When open files share a name but
|
|
473
|
+
have different paths, a secondary label adds the shortest directory suffix that
|
|
474
|
+
distinguishes them. For example, two `src/app.py` files in different packages
|
|
475
|
+
can show `app.py` with `client/src` and `server/src`. Opening or closing a
|
|
476
|
+
duplicate updates these descriptions without changing tab identity or focus.
|
|
477
|
+
Git versions use seven-character commit IDs in the secondary label; a comparison
|
|
478
|
+
of the same path shows its filename once, followed by `abc1234 ↔ def5678` or
|
|
479
|
+
`abc1234 ↔ working tree`. Different comparison paths retain both filenames and
|
|
480
|
+
any needed directory descriptions. This is browser display formatting only;
|
|
481
|
+
source identity, stored titles and API responses retain their full information.
|
|
482
|
+
Content tabs share the session tabs' 180-pixel maximum width. Secondary text
|
|
483
|
+
shrinks and ends in an ellipsis before the filename loses space; a filename
|
|
484
|
+
that alone exceeds the available width also uses an ellipsis. The complete
|
|
485
|
+
title remains available on hover and as the tab's accessible label.
|
|
486
|
+
|
|
487
|
+
Workspace assembly introduces the Canvas file, explains that edits are shown
|
|
488
|
+
in real time and that the model can create or modify it anytime. It then describes
|
|
489
|
+
the extended file/diff links supported in replies and Canvas HTML, including
|
|
490
|
+
optional line anchors and the link back to the live Canvas.
|
|
491
|
+
Detailed syntax and viewer behavior are documented below. There is no `workspace_display` tool.
|
|
492
|
+
Link clicks use the browser's view API, independently of Agent/Runtime, and add
|
|
493
|
+
no messages or tool results to the conversation. They also work with an empty
|
|
494
|
+
`--toolset`. Internal `board` IDs, configuration keys and HTTP routes keep their
|
|
495
|
+
existing names.
|
|
496
|
+
|
|
497
|
+
Ordinary Markdown file links accept absolute or workspace-relative paths.
|
|
498
|
+
Use Git-style expressions for comparisons:
|
|
499
|
+
|
|
500
|
+
| Markdown link | View |
|
|
501
|
+
| --- | --- |
|
|
502
|
+
| `[source](src/app.py)` | Working file |
|
|
503
|
+
| `[line](src/app.py:42)` or `[line](/data/project/app.py#L42)` | File, line 42 |
|
|
504
|
+
| `[document](README.md)` or `[page](report.html)` | Markdown or HTML preview |
|
|
505
|
+
| `[source](README.md:1)` or `[source](report.html#L1)` | Markdown or HTML source, line 1 |
|
|
506
|
+
| `[version](git:HEAD:src/app.py#L42)` | Git file at a fixed revision, line 42 |
|
|
507
|
+
| `[changes](diff:HEAD:src/app.py)` | HEAD versus the working file |
|
|
508
|
+
| `[versions](diff:HEAD~1..HEAD:src/app.py)` | Two revisions of the same file |
|
|
509
|
+
| `[rename](diff:HEAD~1:old.py..HEAD:new.py)` | Two Git files with different paths |
|
|
510
|
+
| `[files](diff:old.py..new.py)` | Two local files |
|
|
511
|
+
| `[left](diff:HEAD:src/app.py#L12)` | Diff, left line 12 |
|
|
512
|
+
| `[right](diff:HEAD:src/app.py#R42)` | Diff, right line 42 |
|
|
513
|
+
| `[Canvas](workspace://canvas)` | The existing live HTML Canvas |
|
|
514
|
+
|
|
515
|
+
`..` compares the two specified snapshots directly, like `git diff A..B`.
|
|
516
|
+
It does not calculate a merge base. Line numbers start at 1 and select the
|
|
517
|
+
whole line. A diff uses `#L` for its left side and `#R` for its right side;
|
|
518
|
+
plain file links use `:line` or `#Lline`. Zero, negative or out-of-range lines,
|
|
519
|
+
images and missing target files are errors and do not change the selection.
|
|
520
|
+
Line targets do not affect a tab's identity or title.
|
|
521
|
+
|
|
522
|
+
These links work in chat Markdown, rendered Markdown file views and, for the
|
|
523
|
+
explicit `git:`, `diff:` and `workspace://canvas` forms, Canvas HTML anchors.
|
|
524
|
+
Canvas's ordinary HTML navigation keeps its existing behavior. Linking the
|
|
525
|
+
Canvas HTML file path opens a preview tab; adding a line number opens its source.
|
|
526
|
+
`workspace://canvas` selects the existing live page. External URLs, `mailto:` links and plain `#anchor` links keep their browser
|
|
527
|
+
behavior. The renderer preserves recognized view destinations through sanitizing
|
|
528
|
+
without permitting executable URL protocols.
|
|
529
|
+
|
|
530
|
+
Percent-encode literal special characters in paths: space as `%20`, `#` as `%23`,
|
|
531
|
+
`?` as `%3F`, `%` as `%25`, and a literal colon as `%3A`. For example,
|
|
532
|
+
`[source](src/a%20%23%25.py)` opens `src/a #%.py`. In a diff endpoint, encode
|
|
533
|
+
a literal `..` within a filename as `%2E%2E` so it is not a comparison separator.
|
|
534
|
+
Parent-directory components such as `diff:../old.py..../new.py` work directly.
|
|
535
|
+
Links are limited to 8192 characters; malformed percent encoding and control
|
|
536
|
+
characters are rejected.
|
|
537
|
+
|
|
538
|
+
File identities use normalized absolute paths, so relative/absolute spellings
|
|
539
|
+
of the same path reuse a tab. Comparisons use the ordered pair of their sources;
|
|
540
|
+
reversing the sides opens a separate view. Git identities include the repository,
|
|
541
|
+
resolved revision and repository-relative path. Equivalent link forms share
|
|
542
|
+
a tab, while distinct commits remain distinct even when their file contents
|
|
543
|
+
match. Without a line target, a missing file can still select its existing tab
|
|
544
|
+
with the last readable contents and an error. A line request reads current content to
|
|
545
|
+
validate the line before changing the selection.
|
|
546
|
+
|
|
547
|
+
Local paths resolve from the workspace working directory; absolute paths and
|
|
548
|
+
percent-encoded paths with spaces are accepted. Backslashes are preserved for
|
|
549
|
+
Windows paths. Git `revision:path` operands follow Git's object syntax and
|
|
550
|
+
identify blobs; their paths start at the repository root, or at the workspace
|
|
551
|
+
directory when explicitly prefixed with `./` or `../`. One revision compares the
|
|
552
|
+
Git file with its working counterpart; two revisions compare the same repository
|
|
553
|
+
path in both revisions. A file missing on just one side is displayed as empty, including
|
|
554
|
+
an explicitly selected new working file. Missing on both sides is an error.
|
|
555
|
+
Other protocols, multiple comparisons and arbitrary Git flags are rejected.
|
|
556
|
+
Links are parsed into internal operations, never passed to a shell; Git
|
|
557
|
+
content is read by object ID without running diff drivers or text conversions.
|
|
558
|
+
|
|
559
|
+
Local file views follow changes on disk. Both sides of `diff:A..B` update, while
|
|
560
|
+
`diff:REV:file` updates only its working file. Git operands are resolved
|
|
561
|
+
once when opening the view, so a later commit or branch move does not change
|
|
562
|
+
the pinned Git contents. This also applies to comparisons between two revisions.
|
|
563
|
+
Opening the moved reference again resolves its new version and creates a separate
|
|
564
|
+
comparison when that version is not already open.
|
|
565
|
+
|
|
566
|
+
Text is decoded as UTF-8 with replacement for invalid bytes. Markdown and HTML
|
|
567
|
+
files default to previews. Markdown uses the existing sanitized renderer; HTML
|
|
568
|
+
(`.html` / `.htm`, case-insensitive) renders its markup, styles and scripts in an
|
|
569
|
+
ordinary same-origin iframe, just like Canvas. Canvas is the persistent iframe
|
|
570
|
+
for the configured live HTML file; other HTML tabs render their current file or
|
|
571
|
+
Git snapshot through `srcdoc`. Both use the browser's normal resource loading
|
|
572
|
+
and automatically send the workspace login cookie. HTML scripts have the same
|
|
573
|
+
access to the parent page and authenticated workspace APIs as Canvas scripts;
|
|
574
|
+
the login cookie remains `HttpOnly` and is never copied into the HTML.
|
|
575
|
+
Preview host messages only save and restore scrolling, and are checked against the
|
|
576
|
+
active frame and current document generation. Unchanged HTML keeps its current
|
|
577
|
+
frame and interactive state; content changes reload it and restore scrolling.
|
|
578
|
+
Switching away destroys the frame, and returning rebuilds it at the saved position.
|
|
579
|
+
Relative asset URLs still use the workspace HTTP base and existing Board image
|
|
580
|
+
routes; filesystem image paths are not implicitly converted into asset URLs.
|
|
581
|
+
Image `src`, `srcset`, CSS resource URLs and HTML `<base>` URLs keep their native
|
|
582
|
+
browser behavior. The viewer does not fetch or convert HTML images to data URLs.
|
|
583
|
+
The existing Board image routes continue to enforce directory and MIME boundaries.
|
|
584
|
+
Code and SVG files are shown as source, and
|
|
585
|
+
PNG/JPEG/GIF/WebP files are stored as image data URLs. Code and text diffs use
|
|
586
|
+
Monaco with filename-based syntax highlighting, line numbers, native
|
|
587
|
+
find, code folding, selection/copying and optional line wrapping. Unknown
|
|
588
|
+
extensions use plain text. Both editor state and DOM editing are read-only;
|
|
589
|
+
find has no replace controls, and diff views disable the editing gutter menu,
|
|
590
|
+
with no accept/reject or revert controls.
|
|
591
|
+
The editor uses textarea input (`editContext: false`) so `domReadOnly` sets the
|
|
592
|
+
native `readonly` attribute and touching code does not open the mobile keyboard.
|
|
593
|
+
The separate find field remains editable.
|
|
594
|
+
Opening Markdown or HTML with a line number switches that tab to its source
|
|
595
|
+
editor, so line numbers refer to the actual file. An explicit link without a
|
|
596
|
+
line switches the same tab back to preview. Source and preview keep separate
|
|
597
|
+
reading positions. Ordinary content refreshes and manual tab switches preserve
|
|
598
|
+
the current presentation and do not replay line requests. Presentation choices
|
|
599
|
+
belong to that browser page; another page's opens do not change them.
|
|
600
|
+
Markdown/HTML comparisons always use the code diff editor.
|
|
601
|
+
|
|
602
|
+
Monaco's diff worker computes line and character changes from the two complete
|
|
603
|
+
documents, including leading and trailing whitespace changes.
|
|
604
|
+
The native diff editor keeps both sides aligned when
|
|
605
|
+
scrolling or opening find. Arrow buttons and `Alt+Up`/`Alt+Down` navigate
|
|
606
|
+
changes. A `ResizeObserver` on the content container selects split mode at
|
|
607
|
+
720 pixels or wider and unified mode below that, including splitter changes
|
|
608
|
+
without a window resize. A line request on the original (left) side keeps split
|
|
609
|
+
mode even below that width so the selected line remains visible. Focusing or
|
|
610
|
+
targeting the modified (right) editor restores automatic layout. This setting
|
|
611
|
+
is preserved when switching tabs. Layout changes reuse the same editors and text models.
|
|
612
|
+
Long documents keep their full searchable text while Monaco renders only the
|
|
613
|
+
visible region.
|
|
614
|
+
|
|
615
|
+
Only the active code view owns an editor (or a pair of editors for a split
|
|
616
|
+
diff). Switching tabs captures Monaco's serializable view state plus find and
|
|
617
|
+
wrapping options, then disposes the editor, text models, event subscriptions and
|
|
618
|
+
observer. Capture happens before hiding the pane so scroll positions remain
|
|
619
|
+
available. Returning restores that reading state; closing the tab also drops its
|
|
620
|
+
caches. Late module/editor completions
|
|
621
|
+
cannot replace a newly selected view. Markdown and image previews retain their
|
|
622
|
+
existing rendering and scroll state.
|
|
623
|
+
Content updates reuse that active editor and its models. Monaco's existing worker
|
|
624
|
+
computes text edits, which are applied without flushing the models so folding and
|
|
625
|
+
find decorations survive; the editor restores its reading state afterward.
|
|
626
|
+
Find stays open and editable. Superseded updates and updates to disposed editors
|
|
627
|
+
are discarded. Updates to
|
|
628
|
+
background tabs invalidate their cached content; selecting them fetches the
|
|
629
|
+
latest version. A refresh keeps the current content visible while loading and
|
|
630
|
+
does not select a tab or switch the mobile pane.
|
|
631
|
+
Explicit line requests wait for current file contents and diff computation,
|
|
632
|
+
then select the line, reveal it and focus the target editor. A newer selection
|
|
633
|
+
or a tab switch invalidates an older pending request. Ordinary content refreshes
|
|
634
|
+
and manual tab switches do not replay an earlier line request; another click
|
|
635
|
+
can explicitly reveal even the same line again.
|
|
636
|
+
|
|
637
|
+
Files are limited to 2 MiB each, text to 20,000 lines and raster images to
|
|
638
|
+
20 megapixels. View storage is limited to 64 MiB per workspace; exceeding a
|
|
639
|
+
limit when opening a view is reported in the browser and does not create or replace a tab.
|
|
640
|
+
If a live file becomes unreadable or invalid, its last readable content remains
|
|
641
|
+
visible with an error. A deleted file view also keeps its last content; a
|
|
642
|
+
deleted side of a diff becomes empty and is marked missing. Recreating or fixing
|
|
643
|
+
the file resumes updates. A storage write or total-budget failure is reported
|
|
644
|
+
to the browser and leaves the previous persisted views intact.
|
|
645
|
+
|
|
646
|
+
`workspace_server/workspace_views.py` owns `WorkspaceViewOpener` and `WorkspaceViewStore`.
|
|
647
|
+
It saves open views, explicit file/Git sources, the latest contents, optional
|
|
648
|
+
line targets and the monotonic reveal counter
|
|
649
|
+
atomically in the Canvas file's sibling `.pycodex-views.json` file, separately from
|
|
650
|
+
conversation state. Sources are checked again after a server restart.
|
|
651
|
+
Earlier saved snapshots without source information remain fixed; reopening
|
|
652
|
+
their file or diff creates a view with explicit sources. Such snapshots are not
|
|
653
|
+
matched by title alone. Saved titles are reconstructed from their full document
|
|
654
|
+
labels without guessing source paths. The store is shared across that workspace's
|
|
655
|
+
session threads; closing a tab is also reflected in other connected browsers.
|
|
656
|
+
Selected-tab state is local to each browser/workspace. A click focuses its target
|
|
657
|
+
only in that page; other pages can discover the tab without changing their
|
|
658
|
+
selection. A new page defaults to Canvas, or restores its saved selection.
|
|
659
|
+
|
|
660
|
+
`POST api/views/open` accepts `{"href": "<file path or URI>"}` and returns
|
|
661
|
+
`{"view": <full snapshot>, "views": <metadata list>}`. Relative local paths use the
|
|
662
|
+
workspace's `work_dir`; single-workspace `create_app(..., work_dir=...)` defaults
|
|
663
|
+
to the process's working directory. The other routes use view IDs:
|
|
664
|
+
`GET api/views` lists metadata, including content/reveal revisions, a live/fixed
|
|
665
|
+
flag and optional `selection` (`document`: zero-based side, `line`: one-based line);
|
|
666
|
+
`GET api/views/<id>` returns the current contents and
|
|
667
|
+
`DELETE api/views/<id>` closes the view. These routes use the existing
|
|
668
|
+
workspace authentication and namespace in both single- and multi-workspace
|
|
669
|
+
apps. The browser polls
|
|
670
|
+
the list every two seconds. The store checks only recorded file paths, using
|
|
671
|
+
file identity, size and modification/change times to detect writes and atomic
|
|
672
|
+
replacements; unchanged files are not reread. No recursive scan or additional
|
|
673
|
+
watcher process is needed. Content/error changes advance the view revision and
|
|
674
|
+
invalidate browser caches. Stale poll/content responses cannot replace a
|
|
675
|
+
newer revision or a different selected view.
|
|
676
|
+
Each successful click advances the target's `reveal_revision`. Only the initiating
|
|
677
|
+
page uses that open response to select a tab and apply its optional line target;
|
|
678
|
+
polling never follows another page's reveal. Newer clicks, manual selection,
|
|
679
|
+
closing a tab or stopping the page invalidate an outstanding open request.
|
|
680
|
+
Late success/failure responses and polls cannot override the user's latest choice.
|
|
681
|
+
Reveals keep the current editor when possible and do not reorder tabs. The
|
|
682
|
+
counter survives closing its latest target and restarting the server. File
|
|
683
|
+
refreshes change only the content `revision` and therefore do not move focus.
|
|
684
|
+
The JS/CSS assets and third-party licenses ship in the wheel and are served
|
|
685
|
+
through concrete routes before the Board image fallback. `content_views.mjs`
|
|
686
|
+
and `content_views.css` contain only the lightweight tabs and preview controller.
|
|
687
|
+
The first code or diff view loads the separate same-origin `editor.mjs` and
|
|
688
|
+
`editor.css` in parallel, and waits for both before creating an editor. Canvas,
|
|
689
|
+
Markdown, HTML and image previews do not request either editor asset. Each
|
|
690
|
+
display retains its loading promise and stylesheet for later code views;
|
|
691
|
+
switching away during loading still invalidates the pending editor creation.
|
|
692
|
+
|
|
693
|
+
The editor bundle includes all language highlighters and its editor/diff worker,
|
|
694
|
+
but registers only the features used by the read-only viewer. JSON uses its
|
|
695
|
+
tokenizer and language configuration directly: the language-service entry point
|
|
696
|
+
would also import the full editor feature set even with diagnostics disabled.
|
|
697
|
+
The worker starts from an embedded Blob URL and the editor CSS embeds the icon
|
|
698
|
+
font. `MonacoEnvironment.getWorker` is the library-required global host hook;
|
|
699
|
+
other viewer state belongs to each content display. No editor, font, worker or
|
|
700
|
+
language package is fetched from a CDN. Python installation and `pycodex-ws`
|
|
701
|
+
startup do not require Node.
|
|
702
|
+
|
|
703
|
+
Only these static viewer assets use negotiated gzip responses. Each application
|
|
704
|
+
instance caches the requested identity/gzip representations, invalidating them
|
|
705
|
+
when the corresponding file's modification time or size changes. Responses use
|
|
706
|
+
`Vary: Accept-Encoding`, representation-specific ETags and `Cache-Control: no-cache`;
|
|
707
|
+
matching conditional requests return 304. HEAD reports the same representation
|
|
708
|
+
headers without a body. Compression does not buffer session traffic, and the
|
|
709
|
+
wheel contains one copy of each asset, without precompressed duplicates. The
|
|
710
|
+
package's Python requirements are unchanged. Monaco does not officially support
|
|
711
|
+
mobile browsers; viewport and touch checks supplement, but do not establish,
|
|
712
|
+
support across real mobile browsers.
|
|
713
|
+
|
|
714
|
+
Frontend sources and exact dependency versions live in
|
|
715
|
+
`workspace_server/frontend/`, with `package-lock.json` fixing transitive
|
|
716
|
+
dependencies. After changing those sources, rebuild the generated assets using
|
|
717
|
+
Node 18+ from the repository root:
|
|
718
|
+
|
|
719
|
+
```bash
|
|
720
|
+
npm --prefix workspace_server/frontend ci
|
|
721
|
+
npm --prefix workspace_server/frontend run build
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
The build collects licenses and third-party notices for bundled packages and stages every output
|
|
725
|
+
before replacing the public JS entry. For an isolated preview, pass a staging
|
|
726
|
+
directory with `npm --prefix workspace_server/frontend run build -- /tmp/workspace-assets`.
|
|
727
|
+
The wheel contains the generated assets, without frontend sources, `node_modules`
|
|
728
|
+
or the build toolchain.
|
|
729
|
+
|
|
441
730
|
## Observation
|
|
442
731
|
|
|
443
732
|
Runtime events are frozen dataclasses defined together in `pycodex/events.py`,
|