iCode-TUI 0.29.1__py3-none-any.whl
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.
- chrys/__init__.py +16 -0
- chrys/app/__init__.py +3 -0
- chrys/app/acp/__init__.py +3 -0
- chrys/app/acp/bridge.py +664 -0
- chrys/app/acp/content.py +132 -0
- chrys/app/acp/doc/frontend-api.md +425 -0
- chrys/app/acp/history.py +585 -0
- chrys/app/acp/server.py +1796 -0
- chrys/app/acp/session_manager.py +1815 -0
- chrys/app/acp/tool_status.py +45 -0
- chrys/app/cli/__init__.py +3 -0
- chrys/app/cli/acp.py +178 -0
- chrys/app/cli/app.py +165 -0
- chrys/app/cli/approvals.py +101 -0
- chrys/app/cli/headless.py +223 -0
- chrys/app/cli/launch_cwd.py +42 -0
- chrys/app/cli/profiles.py +298 -0
- chrys/app/cli/progress.py +857 -0
- chrys/app/cli/run.py +425 -0
- chrys/app/cli/serve.py +1593 -0
- chrys/app/cli/tool_summary.py +225 -0
- chrys/app/cli/trajectory.py +201 -0
- chrys/app/cli/workflow.py +601 -0
- chrys/app/features/__init__.py +3 -0
- chrys/app/features/buddy/__init__.py +3 -0
- chrys/app/features/buddy/actions.py +68 -0
- chrys/app/features/buddy/animation.py +76 -0
- chrys/app/features/buddy/commands.py +196 -0
- chrys/app/features/buddy/docs/design.md +187 -0
- chrys/app/features/buddy/hatchery.py +80 -0
- chrys/app/features/buddy/lifecycle.py +41 -0
- chrys/app/features/buddy/model.py +265 -0
- chrys/app/features/buddy/pixel_renderer.py +113 -0
- chrys/app/features/buddy/pixel_sprites.py +279 -0
- chrys/app/features/buddy/portrait.py +76 -0
- chrys/app/features/buddy/progression.py +58 -0
- chrys/app/features/buddy/replies.py +191 -0
- chrys/app/features/buddy/sprites/alien.toml +70 -0
- chrys/app/features/buddy/sprites/axolotl.toml +71 -0
- chrys/app/features/buddy/sprites/bat.toml +69 -0
- chrys/app/features/buddy/sprites/bee.toml +69 -0
- chrys/app/features/buddy/sprites/cactus.toml +73 -0
- chrys/app/features/buddy/sprites/capybara.toml +70 -0
- chrys/app/features/buddy/sprites/cat.toml +69 -0
- chrys/app/features/buddy/sprites/crab.toml +70 -0
- chrys/app/features/buddy/sprites/dragon.toml +71 -0
- chrys/app/features/buddy/sprites/duck.toml +69 -0
- chrys/app/features/buddy/sprites/fox.toml +69 -0
- chrys/app/features/buddy/sprites/frog.toml +70 -0
- chrys/app/features/buddy/sprites/ghost.toml +70 -0
- chrys/app/features/buddy/sprites/goose.toml +69 -0
- chrys/app/features/buddy/sprites/jelly.toml +70 -0
- chrys/app/features/buddy/sprites/llama.toml +70 -0
- chrys/app/features/buddy/sprites/mushroom.toml +69 -0
- chrys/app/features/buddy/sprites/octopus.toml +70 -0
- chrys/app/features/buddy/sprites/owl.toml +70 -0
- chrys/app/features/buddy/sprites/panda.toml +71 -0
- chrys/app/features/buddy/sprites/penguin.toml +69 -0
- chrys/app/features/buddy/sprites/rabbit.toml +70 -0
- chrys/app/features/buddy/sprites/robot.toml +72 -0
- chrys/app/features/buddy/sprites/shark.toml +70 -0
- chrys/app/features/buddy/sprites/snail.toml +69 -0
- chrys/app/features/buddy/sprites/snake.toml +69 -0
- chrys/app/features/buddy/sprites/tabby.toml +69 -0
- chrys/app/features/buddy/sprites/turtle.toml +69 -0
- chrys/app/features/buddy/store.py +143 -0
- chrys/app/features/session_title/__init__.py +7 -0
- chrys/app/features/session_title/updater.py +582 -0
- chrys/app/installer.py +651 -0
- chrys/app/parsing.py +17 -0
- chrys/app/tui/__init__.py +3 -0
- chrys/app/tui/app.py +1287 -0
- chrys/app/tui/behaviors/__init__.py +3 -0
- chrys/app/tui/behaviors/click_outside_dismiss.py +90 -0
- chrys/app/tui/behaviors/insert_clipboard.py +73 -0
- chrys/app/tui/behaviors/right_click_copy.py +146 -0
- chrys/app/tui/binding_display.py +139 -0
- chrys/app/tui/buddy_messages.py +17 -0
- chrys/app/tui/buddy_reply.py +86 -0
- chrys/app/tui/chrys.tcss +549 -0
- chrys/app/tui/clipboard.py +109 -0
- chrys/app/tui/copy_messages.py +10 -0
- chrys/app/tui/i18n.py +230 -0
- chrys/app/tui/language.py +58 -0
- chrys/app/tui/notifications/__init__.py +12 -0
- chrys/app/tui/notifications/drivers.py +180 -0
- chrys/app/tui/notifications/service.py +126 -0
- chrys/app/tui/notifications/settings.py +141 -0
- chrys/app/tui/screens/__init__.py +3 -0
- chrys/app/tui/screens/agents/__init__.py +8 -0
- chrys/app/tui/screens/agents/agent_draft_store.py +937 -0
- chrys/app/tui/screens/agents/config.py +2142 -0
- chrys/app/tui/screens/agents/config.tcss +169 -0
- chrys/app/tui/screens/agents/panels/__init__.py +3 -0
- chrys/app/tui/screens/agents/panels/acp.py +1176 -0
- chrys/app/tui/screens/agents/panels/basic.py +497 -0
- chrys/app/tui/screens/agents/panels/compaction.py +231 -0
- chrys/app/tui/screens/agents/panels/config_card.py +218 -0
- chrys/app/tui/screens/agents/panels/instructions.py +91 -0
- chrys/app/tui/screens/agents/panels/markdown_report.py +76 -0
- chrys/app/tui/screens/agents/panels/mcp.py +2262 -0
- chrys/app/tui/screens/agents/panels/memory.py +684 -0
- chrys/app/tui/screens/agents/panels/path_entry.py +227 -0
- chrys/app/tui/screens/agents/panels/skills.py +806 -0
- chrys/app/tui/screens/agents/panels/subagents.py +652 -0
- chrys/app/tui/screens/agents/panels/tools.py +205 -0
- chrys/app/tui/screens/agents/picker.py +103 -0
- chrys/app/tui/screens/agents/skill_paths.py +38 -0
- chrys/app/tui/screens/agents/validation_messages.py +452 -0
- chrys/app/tui/screens/dialogs/__init__.py +35 -0
- chrys/app/tui/screens/dialogs/agent_load.py +834 -0
- chrys/app/tui/screens/dialogs/agent_load.tcss +78 -0
- chrys/app/tui/screens/dialogs/app_mode.py +55 -0
- chrys/app/tui/screens/dialogs/approval/__init__.py +15 -0
- chrys/app/tui/screens/dialogs/approval/approval.tcss +311 -0
- chrys/app/tui/screens/dialogs/approval/bodies/__init__.py +3 -0
- chrys/app/tui/screens/dialogs/approval/bodies/file_edit.py +210 -0
- chrys/app/tui/screens/dialogs/approval/bodies/sub_agent.py +82 -0
- chrys/app/tui/screens/dialogs/approval/body.py +110 -0
- chrys/app/tui/screens/dialogs/approval/dialog.py +565 -0
- chrys/app/tui/screens/dialogs/approval/mode.py +105 -0
- chrys/app/tui/screens/dialogs/ask_user.py +148 -0
- chrys/app/tui/screens/dialogs/ask_user.tcss +62 -0
- chrys/app/tui/screens/dialogs/base.py +84 -0
- chrys/app/tui/screens/dialogs/confirm.py +184 -0
- chrys/app/tui/screens/dialogs/confirm.tcss +46 -0
- chrys/app/tui/screens/dialogs/connection_test.py +183 -0
- chrys/app/tui/screens/dialogs/connection_test.tcss +92 -0
- chrys/app/tui/screens/dialogs/diagram.py +102 -0
- chrys/app/tui/screens/dialogs/diagram.tcss +24 -0
- chrys/app/tui/screens/dialogs/editor.py +378 -0
- chrys/app/tui/screens/dialogs/editor.tcss +132 -0
- chrys/app/tui/screens/dialogs/file_picker.py +582 -0
- chrys/app/tui/screens/dialogs/file_picker.tcss +78 -0
- chrys/app/tui/screens/dialogs/fork_session.py +219 -0
- chrys/app/tui/screens/dialogs/fork_session.tcss +75 -0
- chrys/app/tui/screens/dialogs/image_compression.py +102 -0
- chrys/app/tui/screens/dialogs/image_compression.tcss +28 -0
- chrys/app/tui/screens/dialogs/man_page.py +172 -0
- chrys/app/tui/screens/dialogs/man_page.tcss +39 -0
- chrys/app/tui/screens/dialogs/new_folder.py +277 -0
- chrys/app/tui/screens/dialogs/new_folder.tcss +51 -0
- chrys/app/tui/screens/dialogs/notice.tcss +14 -0
- chrys/app/tui/screens/dialogs/prompt_cache.py +144 -0
- chrys/app/tui/screens/dialogs/prompt_cache.tcss +38 -0
- chrys/app/tui/screens/dialogs/runtime_details.py +371 -0
- chrys/app/tui/screens/dialogs/runtime_details.tcss +49 -0
- chrys/app/tui/screens/dialogs/session_title.py +110 -0
- chrys/app/tui/screens/dialogs/session_title.tcss +43 -0
- chrys/app/tui/screens/dialogs/tool_view.py +207 -0
- chrys/app/tui/screens/dialogs/tool_view.tcss +43 -0
- chrys/app/tui/screens/dialogs/vision_unsupported.py +122 -0
- chrys/app/tui/screens/dialogs/vision_unsupported.tcss +35 -0
- chrys/app/tui/screens/dialogs/workflow_confirm.py +91 -0
- chrys/app/tui/screens/dialogs/workflow_confirm.tcss +17 -0
- chrys/app/tui/screens/dialogs/workflow_input.py +258 -0
- chrys/app/tui/screens/dialogs/workflow_input.tcss +73 -0
- chrys/app/tui/screens/dialogs/workflow_node.py +462 -0
- chrys/app/tui/screens/dialogs/workflow_node.tcss +41 -0
- chrys/app/tui/screens/dialogs/workflow_picker.py +46 -0
- chrys/app/tui/screens/dialogs/workflow_result.py +136 -0
- chrys/app/tui/screens/dialogs/workflow_result.tcss +14 -0
- chrys/app/tui/screens/diff/__init__.py +22 -0
- chrys/app/tui/screens/diff/rollback_modal.py +1381 -0
- chrys/app/tui/screens/diff/rollback_modal.tcss +248 -0
- chrys/app/tui/screens/diff/screen.py +1532 -0
- chrys/app/tui/screens/diff/screen.tcss +104 -0
- chrys/app/tui/screens/guides/__init__.py +7 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/agents.md +178 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/approval.md +117 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/chat-options.md +276 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/compaction.md +42 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/memory.md +48 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/models.md +158 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/settings.md +210 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/themes.md +61 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/tools.md +60 -0
- chrys/app/tui/screens/guides/_docs/en/guides/configuration/web-tools.md +286 -0
- chrys/app/tui/screens/guides/_docs/en/guides/daily-use/sessions.md +77 -0
- chrys/app/tui/screens/guides/_docs/en/guides/daily-use/workspaces.md +51 -0
- chrys/app/tui/screens/guides/_docs/en/guides/extensions/external-acp-agents.md +126 -0
- chrys/app/tui/screens/guides/_docs/en/guides/extensions/hooks.md +167 -0
- chrys/app/tui/screens/guides/_docs/en/guides/extensions/mcp.md +95 -0
- chrys/app/tui/screens/guides/_docs/en/guides/extensions/skills.md +175 -0
- chrys/app/tui/screens/guides/_docs/en/guides/running/icode-acp.md +61 -0
- chrys/app/tui/screens/guides/_docs/en/guides/running/icode-run.md +190 -0
- chrys/app/tui/screens/guides/_docs/en/guides/running/icode-serve.md +239 -0
- chrys/app/tui/screens/guides/_docs/en/guides/running/workflows.md +664 -0
- chrys/app/tui/screens/guides/_docs/en/reference/agent-profile.md +548 -0
- chrys/app/tui/screens/guides/_docs/en/reference/hooks.md +602 -0
- chrys/app/tui/screens/guides/_docs/en/reference/opentelemetry.md +177 -0
- chrys/app/tui/screens/guides/_docs/en/reference/settings.md +245 -0
- chrys/app/tui/screens/guides/_docs/en/reference/themes.md +149 -0
- chrys/app/tui/screens/guides/_docs/en/reference/tool-kinds-and-names.md +58 -0
- chrys/app/tui/screens/guides/_docs/en/reference/tui-slash-commands.md +76 -0
- chrys/app/tui/screens/guides/_docs/en/reference/workflows.md +1006 -0
- chrys/app/tui/screens/guides/_docs/en/start/getting-started.md +170 -0
- chrys/app/tui/screens/guides/_docs/en/start/what-is-icode.md +34 -0
- chrys/app/tui/screens/guides/_docs/index.yaml +100 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/agents.md +178 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/approval.md +117 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/chat-options.md +276 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/compaction.md +42 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/memory.md +48 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/models.md +158 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/settings.md +210 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/themes.md +61 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/tools.md +60 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/web-tools.md +286 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/daily-use/sessions.md +77 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/daily-use/workspaces.md +51 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/external-acp-agents.md +126 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/hooks.md +167 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/mcp.md +95 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/skills.md +175 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/icode-acp.md +61 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/icode-run.md +190 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/icode-serve.md +239 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/workflows.md +664 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/agent-profile.md +548 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/hooks.md +602 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/opentelemetry.md +177 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/settings.md +245 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/themes.md +149 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/tool-kinds-and-names.md +58 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/tui-slash-commands.md +76 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/reference/workflows.md +1005 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/start/getting-started.md +170 -0
- chrys/app/tui/screens/guides/_docs/zh-Hans/start/what-is-icode.md +34 -0
- chrys/app/tui/screens/guides/index.py +215 -0
- chrys/app/tui/screens/guides/reader.py +173 -0
- chrys/app/tui/screens/guides/screen.py +442 -0
- chrys/app/tui/screens/guides/screen.tcss +52 -0
- chrys/app/tui/screens/logs/__init__.py +7 -0
- chrys/app/tui/screens/logs/screen.py +504 -0
- chrys/app/tui/screens/logs/screen.tcss +45 -0
- chrys/app/tui/screens/main/__init__.py +7 -0
- chrys/app/tui/screens/main/action_availability.py +30 -0
- chrys/app/tui/screens/main/buddy_command.py +143 -0
- chrys/app/tui/screens/main/commands.py +1073 -0
- chrys/app/tui/screens/main/config_actions.py +464 -0
- chrys/app/tui/screens/main/copy_actions.py +117 -0
- chrys/app/tui/screens/main/dialog_controllers.py +826 -0
- chrys/app/tui/screens/main/dialog_gateway.py +166 -0
- chrys/app/tui/screens/main/diff_controller.py +478 -0
- chrys/app/tui/screens/main/engine_read_model.py +184 -0
- chrys/app/tui/screens/main/event_handlers.py +2014 -0
- chrys/app/tui/screens/main/input_flow.py +241 -0
- chrys/app/tui/screens/main/live_diff.py +244 -0
- chrys/app/tui/screens/main/main_screen_presenter.py +295 -0
- chrys/app/tui/screens/main/model_indicator.py +217 -0
- chrys/app/tui/screens/main/navigation.py +343 -0
- chrys/app/tui/screens/main/ports.py +571 -0
- chrys/app/tui/screens/main/recent_dirs.py +123 -0
- chrys/app/tui/screens/main/rollback_controller.py +586 -0
- chrys/app/tui/screens/main/runtime_info.py +71 -0
- chrys/app/tui/screens/main/screen.py +2265 -0
- chrys/app/tui/screens/main/screen.tcss +21 -0
- chrys/app/tui/screens/main/session_handlers.py +1127 -0
- chrys/app/tui/screens/main/session_title.py +239 -0
- chrys/app/tui/screens/main/settings_coordinator.py +365 -0
- chrys/app/tui/screens/main/settings_persistence.py +184 -0
- chrys/app/tui/screens/main/shell_mode.py +112 -0
- chrys/app/tui/screens/main/state.py +289 -0
- chrys/app/tui/screens/main/submit_state.py +11 -0
- chrys/app/tui/screens/main/subscriptions.py +177 -0
- chrys/app/tui/screens/main/suggestions.py +982 -0
- chrys/app/tui/screens/main/tool_action_bridge.py +75 -0
- chrys/app/tui/screens/main/view_adapter.py +1567 -0
- chrys/app/tui/screens/main/workflow_browser.py +747 -0
- chrys/app/tui/screens/main/workflow_content.py +248 -0
- chrys/app/tui/screens/main/workflow_controller.py +240 -0
- chrys/app/tui/screens/main/workflow_feedback.py +137 -0
- chrys/app/tui/screens/main/workflow_flow.py +49 -0
- chrys/app/tui/screens/main/workflow_run_control.py +456 -0
- chrys/app/tui/screens/main/workflow_session_view.py +316 -0
- chrys/app/tui/screens/main/workspace_actions.py +272 -0
- chrys/app/tui/screens/main/workspace_branch.py +194 -0
- chrys/app/tui/screens/menus/__init__.py +8 -0
- chrys/app/tui/screens/menus/languages.py +114 -0
- chrys/app/tui/screens/models/__init__.py +7 -0
- chrys/app/tui/screens/models/picker.py +147 -0
- chrys/app/tui/screens/models/screen.py +2043 -0
- chrys/app/tui/screens/models/screen.tcss +255 -0
- chrys/app/tui/screens/sessions/__init__.py +8 -0
- chrys/app/tui/screens/sessions/presenter.py +586 -0
- chrys/app/tui/screens/sessions/screen.py +779 -0
- chrys/app/tui/screens/sessions/screen.tcss +127 -0
- chrys/app/tui/screens/settings/__init__.py +29 -0
- chrys/app/tui/screens/settings/danger.py +26 -0
- chrys/app/tui/screens/settings/dialog.py +281 -0
- chrys/app/tui/screens/settings/layout.py +519 -0
- chrys/app/tui/screens/settings/migrate_dialog.py +253 -0
- chrys/app/tui/screens/settings/panes/__init__.py +3 -0
- chrys/app/tui/screens/settings/panes/notifications.py +248 -0
- chrys/app/tui/screens/settings/panes/sessions.py +148 -0
- chrys/app/tui/screens/settings/ports.py +55 -0
- chrys/app/tui/screens/settings/provenance.py +102 -0
- chrys/app/tui/screens/settings/rows.py +546 -0
- chrys/app/tui/screens/settings/settings.tcss +406 -0
- chrys/app/tui/screens/themes/__init__.py +8 -0
- chrys/app/tui/screens/themes/dialogs.py +330 -0
- chrys/app/tui/screens/themes/editor.py +632 -0
- chrys/app/tui/screens/themes/editor.tcss +268 -0
- chrys/app/tui/screens/themes/messages.py +95 -0
- chrys/app/tui/screens/themes/palette.py +694 -0
- chrys/app/tui/screens/themes/panel.py +113 -0
- chrys/app/tui/screens/themes/picker.py +203 -0
- chrys/app/tui/screens/themes/save.py +191 -0
- chrys/app/tui/support/__init__.py +3 -0
- chrys/app/tui/support/file_manager.py +64 -0
- chrys/app/tui/support/gc_freeze.py +1114 -0
- chrys/app/tui/support/path_shortcuts.py +50 -0
- chrys/app/tui/support/prompt_history.py +298 -0
- chrys/app/tui/support/workspace_mru.py +476 -0
- chrys/app/tui/terminal/__init__.py +6 -0
- chrys/app/tui/terminal/_pty_backend.py +114 -0
- chrys/app/tui/terminal/emulator/__init__.py +24 -0
- chrys/app/tui/terminal/emulator/buffer.py +456 -0
- chrys/app/tui/terminal/emulator/charsets.py +113 -0
- chrys/app/tui/terminal/emulator/core.py +963 -0
- chrys/app/tui/terminal/emulator/input.py +342 -0
- chrys/app/tui/terminal/emulator/parser.py +227 -0
- chrys/app/tui/terminal/emulator/pen.py +158 -0
- chrys/app/tui/terminal/launcher.py +455 -0
- chrys/app/tui/terminal/leaked_reports.py +44 -0
- chrys/app/tui/terminal/panel.py +323 -0
- chrys/app/tui/terminal/pty_process.py +405 -0
- chrys/app/tui/terminal/rendering.py +151 -0
- chrys/app/tui/terminal/shell.py +273 -0
- chrys/app/tui/terminal/shell_integration.py +199 -0
- chrys/app/tui/terminal/title.py +224 -0
- chrys/app/tui/terminal/widget.py +654 -0
- chrys/app/tui/theme.py +422 -0
- chrys/app/tui/theme_loader.py +201 -0
- chrys/app/tui/theme_stylesheet.py +49 -0
- chrys/app/tui/themes/__init__.py +3 -0
- chrys/app/tui/themes/document.py +120 -0
- chrys/app/tui/themes/preview.py +170 -0
- chrys/app/tui/themes/store.py +176 -0
- chrys/app/tui/toasts.py +69 -0
- chrys/app/tui/util/__init__.py +3 -0
- chrys/app/tui/util/context_pressure.py +146 -0
- chrys/app/tui/util/debug_log.py +292 -0
- chrys/app/tui/util/diff_entries.py +64 -0
- chrys/app/tui/util/formatting.py +53 -0
- chrys/app/tui/util/git_branch.py +326 -0
- chrys/app/tui/util/invocation_progress.py +57 -0
- chrys/app/tui/util/logo.py +32 -0
- chrys/app/tui/util/message_gate.py +35 -0
- chrys/app/tui/util/mutation_sources.py +14 -0
- chrys/app/tui/util/picker_text.py +27 -0
- chrys/app/tui/util/removal.py +47 -0
- chrys/app/tui/util/rich_style.py +36 -0
- chrys/app/tui/util/source_text.py +12 -0
- chrys/app/tui/util/static_update.py +88 -0
- chrys/app/tui/util/todo_format.py +51 -0
- chrys/app/tui/util/visibility.py +214 -0
- chrys/app/tui/widgets/__init__.py +60 -0
- chrys/app/tui/widgets/ask_user_controls.py +652 -0
- chrys/app/tui/widgets/ask_user_markdown.py +17 -0
- chrys/app/tui/widgets/ask_user_prompt.py +606 -0
- chrys/app/tui/widgets/buttons.py +52 -0
- chrys/app/tui/widgets/chat/__init__.py +3 -0
- chrys/app/tui/widgets/chat/agent_transcript_surface.py +818 -0
- chrys/app/tui/widgets/chat/chrome.py +296 -0
- chrys/app/tui/widgets/chat/compaction_card.py +353 -0
- chrys/app/tui/widgets/chat/context_fold.py +77 -0
- chrys/app/tui/widgets/chat/file_snapshot.py +91 -0
- chrys/app/tui/widgets/chat/image_preview.py +214 -0
- chrys/app/tui/widgets/chat/live_renderer.py +383 -0
- chrys/app/tui/widgets/chat/messages.py +1039 -0
- chrys/app/tui/widgets/chat/panel.py +1769 -0
- chrys/app/tui/widgets/chat/ports.py +245 -0
- chrys/app/tui/widgets/chat/renderers/__init__.py +7 -0
- chrys/app/tui/widgets/chat/renderers/ask_user.py +747 -0
- chrys/app/tui/widgets/chat/renderers/execute.py +431 -0
- chrys/app/tui/widgets/chat/renderers/file_edit.py +925 -0
- chrys/app/tui/widgets/chat/renderers/hosted_code.py +83 -0
- chrys/app/tui/widgets/chat/renderers/hosted_generic.py +330 -0
- chrys/app/tui/widgets/chat/renderers/hosted_image.py +107 -0
- chrys/app/tui/widgets/chat/renderers/hosted_mcp.py +43 -0
- chrys/app/tui/widgets/chat/renderers/hosted_search.py +167 -0
- chrys/app/tui/widgets/chat/renderers/hosted_shell.py +132 -0
- chrys/app/tui/widgets/chat/renderers/read_file.py +399 -0
- chrys/app/tui/widgets/chat/renderers/search.py +348 -0
- chrys/app/tui/widgets/chat/renderers/skill.py +564 -0
- chrys/app/tui/widgets/chat/renderers/sleep.py +287 -0
- chrys/app/tui/widgets/chat/renderers/sub_agent.py +1385 -0
- chrys/app/tui/widgets/chat/renderers/todo.py +114 -0
- chrys/app/tui/widgets/chat/renderers/web.py +69 -0
- chrys/app/tui/widgets/chat/replay.py +1605 -0
- chrys/app/tui/widgets/chat/replay_mount.py +322 -0
- chrys/app/tui/widgets/chat/scroll_controller.py +579 -0
- chrys/app/tui/widgets/chat/selection_controller.py +435 -0
- chrys/app/tui/widgets/chat/session_json.py +508 -0
- chrys/app/tui/widgets/chat/toc_model.py +156 -0
- chrys/app/tui/widgets/chat/tool_call.py +2478 -0
- chrys/app/tui/widgets/chat/tool_registry.py +753 -0
- chrys/app/tui/widgets/chat/tool_renderers.py +247 -0
- chrys/app/tui/widgets/chat/tool_view_builders.py +260 -0
- chrys/app/tui/widgets/chat/transcript_reader.py +57 -0
- chrys/app/tui/widgets/checkbox.py +24 -0
- chrys/app/tui/widgets/chrome/__init__.py +3 -0
- chrys/app/tui/widgets/chrome/app_header.py +283 -0
- chrys/app/tui/widgets/chrome/commands.py +95 -0
- chrys/app/tui/widgets/chrome/file_index.py +68 -0
- chrys/app/tui/widgets/chrome/file_scanner.py +670 -0
- chrys/app/tui/widgets/chrome/footer.py +284 -0
- chrys/app/tui/widgets/chrome/image_paste.py +351 -0
- chrys/app/tui/widgets/chrome/input_bar.py +1299 -0
- chrys/app/tui/widgets/chrome/status_bar.py +911 -0
- chrys/app/tui/widgets/chrome/suggestion_list.py +652 -0
- chrys/app/tui/widgets/click_affordance.py +24 -0
- chrys/app/tui/widgets/color_picker/__init__.py +254 -0
- chrys/app/tui/widgets/color_picker/controls.py +171 -0
- chrys/app/tui/widgets/color_picker/model.py +89 -0
- chrys/app/tui/widgets/dialog_buttons.py +58 -0
- chrys/app/tui/widgets/dialog_scroll.py +24 -0
- chrys/app/tui/widgets/diff_view/__init__.py +20 -0
- chrys/app/tui/widgets/diff_view/cells.py +59 -0
- chrys/app/tui/widgets/diff_view/code.py +280 -0
- chrys/app/tui/widgets/diff_view/compute.py +286 -0
- chrys/app/tui/widgets/diff_view/gutter.py +161 -0
- chrys/app/tui/widgets/diff_view/inline.py +77 -0
- chrys/app/tui/widgets/diff_view/palette.py +87 -0
- chrys/app/tui/widgets/diff_view/protocols.py +24 -0
- chrys/app/tui/widgets/diff_view/rows.py +138 -0
- chrys/app/tui/widgets/diff_view/unified.py +267 -0
- chrys/app/tui/widgets/diff_view/widget.py +209 -0
- chrys/app/tui/widgets/editor/__init__.py +79 -0
- chrys/app/tui/widgets/editor/coordinates.py +30 -0
- chrys/app/tui/widgets/editor/highlighter.py +139 -0
- chrys/app/tui/widgets/editor/keymaps.py +357 -0
- chrys/app/tui/widgets/editor/types.py +216 -0
- chrys/app/tui/widgets/editor/vim.py +1195 -0
- chrys/app/tui/widgets/editor/widget.py +401 -0
- chrys/app/tui/widgets/file_preview.py +58 -0
- chrys/app/tui/widgets/hatch.py +138 -0
- chrys/app/tui/widgets/input.py +66 -0
- chrys/app/tui/widgets/loading.py +125 -0
- chrys/app/tui/widgets/markdown/__init__.py +25 -0
- chrys/app/tui/widgets/markdown/_utils.py +21 -0
- chrys/app/tui/widgets/markdown/anchors.py +71 -0
- chrys/app/tui/widgets/markdown/blocks.py +115 -0
- chrys/app/tui/widgets/markdown/copyable.py +163 -0
- chrys/app/tui/widgets/markdown/diagram/__init__.py +35 -0
- chrys/app/tui/widgets/markdown/diagram/canvas.py +409 -0
- chrys/app/tui/widgets/markdown/diagram/charts.py +52 -0
- chrys/app/tui/widgets/markdown/diagram/diagnostics.py +101 -0
- chrys/app/tui/widgets/markdown/diagram/geometry.py +46 -0
- chrys/app/tui/widgets/markdown/diagram/layout.py +494 -0
- chrys/app/tui/widgets/markdown/diagram/messages.py +18 -0
- chrys/app/tui/widgets/markdown/diagram/model.py +275 -0
- chrys/app/tui/widgets/markdown/diagram/parser.py +173 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/__init__.py +3 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/charts.py +349 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/common.py +403 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/graphs.py +1072 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/planning.py +317 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/schedule.py +298 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/schemas.py +241 -0
- chrys/app/tui/widgets/markdown/diagram/parsers/structure.py +221 -0
- chrys/app/tui/widgets/markdown/diagram/presentation.py +32 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/__init__.py +3 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/charts.py +275 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/common.py +73 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/planning.py +157 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/schedule.py +202 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/structure.py +77 -0
- chrys/app/tui/widgets/markdown/diagram/renderers/xy.py +385 -0
- chrys/app/tui/widgets/markdown/diagram/router.py +651 -0
- chrys/app/tui/widgets/markdown/diagram/specs/__init__.py +3 -0
- chrys/app/tui/widgets/markdown/diagram/specs/charts.py +94 -0
- chrys/app/tui/widgets/markdown/diagram/specs/planning.py +68 -0
- chrys/app/tui/widgets/markdown/diagram/specs/schedule.py +65 -0
- chrys/app/tui/widgets/markdown/diagram/specs/structure.py +23 -0
- chrys/app/tui/widgets/markdown/diagram/viewer.py +92 -0
- chrys/app/tui/widgets/markdown/links.py +47 -0
- chrys/app/tui/widgets/markdown/math/__init__.py +36 -0
- chrys/app/tui/widgets/markdown/math/layout.py +739 -0
- chrys/app/tui/widgets/markdown/math/markdown.py +291 -0
- chrys/app/tui/widgets/markdown/math/parser.py +325 -0
- chrys/app/tui/widgets/markdown/math/symbols.py +325 -0
- chrys/app/tui/widgets/markdown/navigator.py +80 -0
- chrys/app/tui/widgets/markdown/parser.py +1057 -0
- chrys/app/tui/widgets/markdown/stream.py +73 -0
- chrys/app/tui/widgets/markdown/toc.py +98 -0
- chrys/app/tui/widgets/markdown/viewer.py +146 -0
- chrys/app/tui/widgets/markdown/widget.py +2046 -0
- chrys/app/tui/widgets/marquee.py +101 -0
- chrys/app/tui/widgets/option_menu.py +82 -0
- chrys/app/tui/widgets/page_navigator.py +118 -0
- chrys/app/tui/widgets/select.py +196 -0
- chrys/app/tui/widgets/selection.py +59 -0
- chrys/app/tui/widgets/sidebar/__init__.py +3 -0
- chrys/app/tui/widgets/sidebar/buddy.py +367 -0
- chrys/app/tui/widgets/sidebar/context.py +482 -0
- chrys/app/tui/widgets/sidebar/debug.py +195 -0
- chrys/app/tui/widgets/sidebar/empty_state.py +11 -0
- chrys/app/tui/widgets/sidebar/panel.py +198 -0
- chrys/app/tui/widgets/sidebar/tasks.py +198 -0
- chrys/app/tui/widgets/sidebar/toc.py +166 -0
- chrys/app/tui/widgets/sidebar/toc_types.py +30 -0
- chrys/app/tui/widgets/syntax_theme.py +85 -0
- chrys/app/tui/widgets/text_area.py +258 -0
- chrys/app/tui/widgets/trajectory/__init__.py +8 -0
- chrys/app/tui/widgets/trajectory/chartkit.py +264 -0
- chrys/app/tui/widgets/trajectory/insights.py +1266 -0
- chrys/app/tui/widgets/trajectory/overview.py +1101 -0
- chrys/app/tui/widgets/trajectory/panel.py +983 -0
- chrys/app/tui/widgets/trajectory/presentation.py +311 -0
- chrys/app/tui/widgets/trajectory/session_info.py +124 -0
- chrys/app/tui/widgets/trajectory/text_view.py +124 -0
- chrys/app/tui/widgets/trajectory/timeline.py +380 -0
- chrys/app/tui/widgets/welcome.py +126 -0
- chrys/app/tui/widgets/workflow/__init__.py +5 -0
- chrys/app/tui/widgets/workflow/edges.py +89 -0
- chrys/app/tui/widgets/workflow/graph.py +869 -0
- chrys/app/tui/widgets/workflow/info.py +310 -0
- chrys/app/tui/widgets/workflow/node_view.py +27 -0
- chrys/app/tui/widgets/workflow/output.py +225 -0
- chrys/app/tui/widgets/workflow/panel.py +658 -0
- chrys/app/tui/widgets/workflow/projector.py +324 -0
- chrys/app/tui/widgets/workflow/records.py +69 -0
- chrys/app/tui/widgets/workflow/scrollbar.py +82 -0
- chrys/app/tui/widgets/workflow/selection.py +151 -0
- chrys/app/tui/widgets/workflow/source.py +65 -0
- chrys/app/tui/widgets/workflow/text.py +257 -0
- chrys/app/tui/widgets/workflow/values.py +317 -0
- chrys/foundation/__init__.py +0 -0
- chrys/foundation/branding.py +15 -0
- chrys/foundation/config/__init__.py +7 -0
- chrys/foundation/config/coercion.py +374 -0
- chrys/foundation/config/context.py +34 -0
- chrys/foundation/config/env_file.py +141 -0
- chrys/foundation/config/env_layers.py +370 -0
- chrys/foundation/config/migrations.py +521 -0
- chrys/foundation/config/notification_events.py +22 -0
- chrys/foundation/config/process_settings.py +332 -0
- chrys/foundation/config/runtime_pointer.py +133 -0
- chrys/foundation/config/settings.py +1952 -0
- chrys/foundation/config/settings_store.py +996 -0
- chrys/foundation/config/spec.py +396 -0
- chrys/foundation/config/user_settings.py +140 -0
- chrys/foundation/config/warnings.py +262 -0
- chrys/foundation/config/web_values.py +135 -0
- chrys/foundation/config/yaml_store.py +487 -0
- chrys/foundation/errors/__init__.py +68 -0
- chrys/foundation/errors/_legacy.py +117 -0
- chrys/foundation/errors/_text.py +13 -0
- chrys/foundation/errors/_walk.py +85 -0
- chrys/foundation/errors/classify.py +278 -0
- chrys/foundation/errors/display.py +274 -0
- chrys/foundation/errors/formatting.py +283 -0
- chrys/foundation/errors/kinds.py +67 -0
- chrys/foundation/errors/network.py +497 -0
- chrys/foundation/errors/provider.py +445 -0
- chrys/foundation/errors/route.py +85 -0
- chrys/foundation/events/__init__.py +8 -0
- chrys/foundation/events/bus.py +143 -0
- chrys/foundation/events/types.py +1629 -0
- chrys/foundation/events/workflow.py +38 -0
- chrys/foundation/hosted_tools.py +142 -0
- chrys/foundation/i18n/__init__.py +32 -0
- chrys/foundation/i18n/_catalogs/zh-Hans/LC_MESSAGES/chrys.mo +0 -0
- chrys/foundation/i18n/catalogs.py +42 -0
- chrys/foundation/i18n/formatting.py +202 -0
- chrys/foundation/i18n/locale.py +92 -0
- chrys/foundation/i18n/localizer.py +187 -0
- chrys/foundation/i18n/messages.py +268 -0
- chrys/foundation/io/__init__.py +3 -0
- chrys/foundation/io/result_content.py +237 -0
- chrys/foundation/models/__init__.py +3 -0
- chrys/foundation/models/approval_reuse.py +23 -0
- chrys/foundation/models/ask_user.py +468 -0
- chrys/foundation/models/execution.py +18 -0
- chrys/foundation/models/history_markers.py +115 -0
- chrys/foundation/models/invocations.py +63 -0
- chrys/foundation/models/mutation_scope.py +28 -0
- chrys/foundation/models/session_env.py +87 -0
- chrys/foundation/models/session_surface.py +29 -0
- chrys/foundation/models/todos.py +74 -0
- chrys/foundation/models/turns.py +125 -0
- chrys/foundation/models/workflow_session.py +89 -0
- chrys/foundation/models/workspace.py +59 -0
- chrys/foundation/net/__init__.py +3 -0
- chrys/foundation/net/address_class.py +57 -0
- chrys/foundation/net/route_probe.py +94 -0
- chrys/foundation/net/url.py +107 -0
- chrys/foundation/observability/__init__.py +3 -0
- chrys/foundation/observability/exporters.py +174 -0
- chrys/foundation/observability/gate.py +31 -0
- chrys/foundation/observability/setup.py +497 -0
- chrys/foundation/observability/sink.py +228 -0
- chrys/foundation/patches/__init__.py +19 -0
- chrys/foundation/patches/mdurl_cache.py +59 -0
- chrys/foundation/patches/patcher.py +287 -0
- chrys/foundation/patches/staged_members.py +205 -0
- chrys/foundation/patches/textual_block_border.py +105 -0
- chrys/foundation/patches/textual_button_ansi.py +65 -0
- chrys/foundation/patches/textual_button_ansi_old.txt +68 -0
- chrys/foundation/patches/textual_callback_cache.py +248 -0
- chrys/foundation/patches/textual_callback_dispatch.py +109 -0
- chrys/foundation/patches/textual_compositor_cjk.py +566 -0
- chrys/foundation/patches/textual_dispatch_cache.py +150 -0
- chrys/foundation/patches/textual_ime_cursor_anchor.py +92 -0
- chrys/foundation/patches/textual_kitty_keyboard.py +268 -0
- chrys/foundation/patches/textual_lru_acyclic.py +241 -0
- chrys/foundation/patches/textual_message_pump.py +136 -0
- chrys/foundation/patches/textual_node_diet.py +875 -0
- chrys/foundation/patches/textual_one_shot_timer.py +48 -0
- chrys/foundation/patches/textual_option_list.py +90 -0
- chrys/foundation/patches/textual_precompose.py +89 -0
- chrys/foundation/patches/textual_pruned_tabs.py +69 -0
- chrys/foundation/patches/textual_reflow_reuse.py +1753 -0
- chrys/foundation/patches/textual_removed_node_caches.py +86 -0
- chrys/foundation/patches/textual_removed_screen_callbacks.py +55 -0
- chrys/foundation/patches/textual_selection_extract.py +48 -0
- chrys/foundation/patches/textual_strip_cycles.py +119 -0
- chrys/foundation/patches/textual_tab_selection.py +1611 -0
- chrys/foundation/patches/textual_timer_skip.py +70 -0
- chrys/foundation/patches/textual_utf8_decoder.py +106 -0
- chrys/foundation/patches/textual_win_sleep.py +167 -0
- chrys/foundation/patches/watchdog_fsevents.py +70 -0
- chrys/foundation/patches/watchdog_windows.py +52 -0
- chrys/foundation/platform/__init__.py +556 -0
- chrys/foundation/platform/c_api.py +32 -0
- chrys/foundation/platform/child_reap.py +107 -0
- chrys/foundation/platform/command_line.py +95 -0
- chrys/foundation/platform/files.py +1470 -0
- chrys/foundation/platform/output_capture.py +113 -0
- chrys/foundation/platform/paths.py +193 -0
- chrys/foundation/platform/process.py +1468 -0
- chrys/foundation/platform/pty_output.py +19 -0
- chrys/foundation/platform/runtime_paths.py +281 -0
- chrys/foundation/platform/win_console.py +129 -0
- chrys/foundation/reasoning_origin.py +67 -0
- chrys/foundation/recovery.py +16 -0
- chrys/foundation/retry.py +278 -0
- chrys/foundation/text/__init__.py +3 -0
- chrys/foundation/text/encoding.py +603 -0
- chrys/foundation/text/images.py +304 -0
- chrys/foundation/text/lines.py +24 -0
- chrys/foundation/text/mentions.py +90 -0
- chrys/foundation/text/model_json.py +25 -0
- chrys/foundation/text/reading_order.py +20 -0
- chrys/foundation/text/tokenizer.py +25 -0
- chrys/foundation/text/tool_output.py +287 -0
- chrys/foundation/text/yaml_io.py +62 -0
- chrys/foundation/tool_call_context.py +202 -0
- chrys/foundation/tool_execution_stamp.py +184 -0
- chrys/foundation/tool_invocation_order.py +48 -0
- chrys/foundation/tool_kinds.py +102 -0
- chrys/foundation/tool_result_metadata.py +287 -0
- chrys/foundation/trajectory/__init__.py +10 -0
- chrys/foundation/trajectory/context.py +481 -0
- chrys/foundation/trajectory/envelope.py +571 -0
- chrys/foundation/trajectory/event_types.py +294 -0
- chrys/foundation/trajectory/finalizers.py +46 -0
- chrys/foundation/trajectory/fingerprint.py +62 -0
- chrys/foundation/trajectory/ids.py +155 -0
- chrys/foundation/trajectory/keys.py +99 -0
- chrys/foundation/trajectory/lease.py +173 -0
- chrys/foundation/trajectory/metadata.py +72 -0
- chrys/foundation/trajectory/reader.py +271 -0
- chrys/foundation/trajectory/recovery.py +225 -0
- chrys/foundation/trajectory/revisions.py +152 -0
- chrys/foundation/trajectory/segments.py +225 -0
- chrys/foundation/trajectory/tools.py +117 -0
- chrys/foundation/trajectory/usage.py +151 -0
- chrys/foundation/trajectory/writer.py +972 -0
- chrys/foundation/trajectory_timing.py +97 -0
- chrys/foundation/util/__init__.py +3 -0
- chrys/foundation/util/async_tasks.py +29 -0
- chrys/foundation/util/chrys_headers.py +23 -0
- chrys/foundation/util/env_templates.py +104 -0
- chrys/foundation/util/filenames.py +96 -0
- chrys/foundation/util/header_charset.py +138 -0
- chrys/foundation/util/httpx_helpers.py +12 -0
- chrys/foundation/util/lock.py +236 -0
- chrys/foundation/util/once_close.py +46 -0
- chrys/foundation/util/session_ids.py +29 -0
- chrys/foundation/util/shell_tokens.py +88 -0
- chrys/foundation/util/sub_agent_context.py +38 -0
- chrys/foundation/util/time.py +35 -0
- chrys/foundation/util/unicode_scalars.py +36 -0
- chrys/foundation/vendor/__init__.py +43 -0
- chrys/foundation/vendor/ripgrep/rg-aarch64-apple-darwin +0 -0
- chrys/foundation/vendor/ripgrep/rg-aarch64-pc-windows-msvc.exe +0 -0
- chrys/foundation/vendor/ripgrep/rg-aarch64-unknown-linux-gnu +0 -0
- chrys/foundation/vendor/ripgrep/rg-x86_64-apple-darwin +0 -0
- chrys/foundation/vendor/ripgrep/rg-x86_64-pc-windows-msvc.exe +0 -0
- chrys/foundation/vendor/ripgrep/rg-x86_64-unknown-linux-musl +0 -0
- chrys/kernel/__init__.py +256 -0
- chrys/kernel/_content.py +1866 -0
- chrys/kernel/_loop_recorder.py +631 -0
- chrys/kernel/_null_overlay.py +1026 -0
- chrys/kernel/_result_ceiling.py +216 -0
- chrys/kernel/_serialization.py +431 -0
- chrys/kernel/_tool_arg_errors.py +1629 -0
- chrys/kernel/_tool_execution.py +1172 -0
- chrys/kernel/_tool_expansion.py +26 -0
- chrys/kernel/_types.py +2598 -0
- chrys/kernel/agent.py +994 -0
- chrys/kernel/client.py +1084 -0
- chrys/kernel/compaction.py +1315 -0
- chrys/kernel/exceptions.py +211 -0
- chrys/kernel/exchanges.py +683 -0
- chrys/kernel/identity.py +95 -0
- chrys/kernel/images.py +36 -0
- chrys/kernel/instrumentation.py +1618 -0
- chrys/kernel/loop.py +2308 -0
- chrys/kernel/middleware.py +616 -0
- chrys/kernel/sessions.py +604 -0
- chrys/kernel/tools.py +1061 -0
- chrys/kernel/types.py +126 -0
- chrys/orchestration/__init__.py +3 -0
- chrys/orchestration/engine/__init__.py +3 -0
- chrys/orchestration/engine/assembly.py +236 -0
- chrys/orchestration/engine/build/__init__.py +3 -0
- chrys/orchestration/engine/build/builder.py +1061 -0
- chrys/orchestration/engine/build/construction.py +408 -0
- chrys/orchestration/engine/build/loaded.py +139 -0
- chrys/orchestration/engine/engine.py +789 -0
- chrys/orchestration/engine/execution.py +771 -0
- chrys/orchestration/engine/loader.py +865 -0
- chrys/orchestration/engine/rollback.py +918 -0
- chrys/orchestration/engine/run/__init__.py +3 -0
- chrys/orchestration/engine/run/active_injection.py +419 -0
- chrys/orchestration/engine/run/attachments.py +519 -0
- chrys/orchestration/engine/run/bindings.py +1095 -0
- chrys/orchestration/engine/run/coordinator.py +1062 -0
- chrys/orchestration/engine/run/finalizer.py +417 -0
- chrys/orchestration/engine/run/input_refs.py +60 -0
- chrys/orchestration/engine/run/prompt_content.py +457 -0
- chrys/orchestration/engine/run/resume.py +299 -0
- chrys/orchestration/engine/run/retry.py +948 -0
- chrys/orchestration/engine/run/runner.py +625 -0
- chrys/orchestration/engine/run/runtime_skills.py +146 -0
- chrys/orchestration/engine/run/sub_agent_coordination.py +126 -0
- chrys/orchestration/engine/run/turn_hooks.py +258 -0
- chrys/orchestration/engine/run/turn_state.py +149 -0
- chrys/orchestration/engine/run/working_dir.py +50 -0
- chrys/orchestration/engine/session_lifecycle.py +2013 -0
- chrys/orchestration/engine/state/__init__.py +3 -0
- chrys/orchestration/engine/state/active_session.py +183 -0
- chrys/orchestration/engine/state/controls.py +639 -0
- chrys/orchestration/engine/state/current_agent.py +26 -0
- chrys/orchestration/engine/state/lifecycle_permits.py +395 -0
- chrys/orchestration/engine/state/machine.py +193 -0
- chrys/orchestration/engine/state/session_writer.py +434 -0
- chrys/orchestration/engine/trajectory.py +714 -0
- chrys/orchestration/engine/usage.py +118 -0
- chrys/orchestration/invoker/__init__.py +3 -0
- chrys/orchestration/invoker/acp.py +727 -0
- chrys/orchestration/invoker/acp_protocol.py +1115 -0
- chrys/orchestration/invoker/acp_spec.py +79 -0
- chrys/orchestration/invoker/attempts.py +994 -0
- chrys/orchestration/invoker/child_compaction.py +131 -0
- chrys/orchestration/invoker/child_history.py +99 -0
- chrys/orchestration/invoker/contracts.py +253 -0
- chrys/orchestration/invoker/evidence.py +94 -0
- chrys/orchestration/invoker/kernel.py +343 -0
- chrys/orchestration/invoker/origin.py +79 -0
- chrys/orchestration/invoker/resources.py +343 -0
- chrys/orchestration/invoker/runtime.py +299 -0
- chrys/orchestration/invoker/usage.py +80 -0
- chrys/orchestration/session_hooks.py +117 -0
- chrys/orchestration/session_host.py +1023 -0
- chrys/orchestration/session_resources.py +95 -0
- chrys/orchestration/session_usage.py +219 -0
- chrys/orchestration/startup.py +255 -0
- chrys/orchestration/sub_agents/__init__.py +3 -0
- chrys/orchestration/sub_agents/acp_policy.py +269 -0
- chrys/orchestration/sub_agents/kernel_policy.py +848 -0
- chrys/orchestration/sub_agents/registration.py +44 -0
- chrys/orchestration/sub_agents/shell.py +337 -0
- chrys/orchestration/sub_agents/tools.py +2228 -0
- chrys/orchestration/workflows/__init__.py +3 -0
- chrys/orchestration/workflows/agent_archive.py +156 -0
- chrys/orchestration/workflows/agent_node.py +894 -0
- chrys/orchestration/workflows/agent_node_build.py +687 -0
- chrys/orchestration/workflows/catalog.py +332 -0
- chrys/orchestration/workflows/coordinator.py +771 -0
- chrys/orchestration/workflows/hooks.py +176 -0
- chrys/orchestration/workflows/preview.py +322 -0
- chrys/orchestration/workflows/rollback.py +151 -0
- chrys/orchestration/workflows/runner.py +986 -0
- chrys/orchestration/workflows/session.py +390 -0
- chrys/orchestration/workflows/settings.py +97 -0
- chrys/orchestration/workflows/validation.py +674 -0
- chrys/orchestration/workflows/worker_client.py +1002 -0
- chrys/service/__init__.py +3 -0
- chrys/service/acp_client/__init__.py +65 -0
- chrys/service/acp_client/_transport.py +922 -0
- chrys/service/acp_client/client.py +1311 -0
- chrys/service/acp_client/errors.py +224 -0
- chrys/service/acp_client/protocol.py +220 -0
- chrys/service/acp_client/spawn.py +239 -0
- chrys/service/acp_client/spec.py +100 -0
- chrys/service/agent_middleware/__init__.py +60 -0
- chrys/service/agent_middleware/_metadata_keys.py +52 -0
- chrys/service/agent_middleware/control/__init__.py +3 -0
- chrys/service/agent_middleware/control/approval.py +769 -0
- chrys/service/agent_middleware/control/approval_hooks.py +62 -0
- chrys/service/agent_middleware/control/ask_user.py +207 -0
- chrys/service/agent_middleware/control/interrupt.py +50 -0
- chrys/service/agent_middleware/control/sleep.py +118 -0
- chrys/service/agent_middleware/control/todo.py +117 -0
- chrys/service/agent_middleware/events/__init__.py +3 -0
- chrys/service/agent_middleware/events/hook_dispatch.py +250 -0
- chrys/service/agent_middleware/events/hosted_tools.py +1762 -0
- chrys/service/agent_middleware/events/intermediate_text.py +128 -0
- chrys/service/agent_middleware/events/rejection_metadata.py +50 -0
- chrys/service/agent_middleware/events/result_persistence.py +98 -0
- chrys/service/agent_middleware/events/sub_agent_events.py +951 -0
- chrys/service/agent_middleware/events/tool_error_text.py +22 -0
- chrys/service/agent_middleware/events/tool_events.py +779 -0
- chrys/service/agent_middleware/injection.py +510 -0
- chrys/service/agent_middleware/reminders/__init__.py +91 -0
- chrys/service/agent_middleware/reminders/archive_pointer.py +154 -0
- chrys/service/agent_middleware/reminders/base.py +25 -0
- chrys/service/agent_middleware/reminders/context_usage.py +74 -0
- chrys/service/agent_middleware/reminders/file_change.py +63 -0
- chrys/service/agent_middleware/reminders/mcp.py +39 -0
- chrys/service/agent_middleware/reminders/profile_switch.py +106 -0
- chrys/service/agent_middleware/reminders/runtime_env.py +190 -0
- chrys/service/agent_middleware/reminders/skills.py +44 -0
- chrys/service/agent_middleware/reminders/sub_agents.py +35 -0
- chrys/service/agent_middleware/reminders/todo.py +35 -0
- chrys/service/agent_middleware/reminders/turn_line.py +48 -0
- chrys/service/agent_middleware/response_validation.py +1380 -0
- chrys/service/agent_middleware/system_reminder.py +1107 -0
- chrys/service/agent_middleware/validators.py +396 -0
- chrys/service/analytics/__init__.py +124 -0
- chrys/service/analytics/_actions.py +896 -0
- chrys/service/analytics/_context_evidence.py +357 -0
- chrys/service/analytics/_critical_path.py +179 -0
- chrys/service/analytics/_facts.py +919 -0
- chrys/service/analytics/_insights.py +769 -0
- chrys/service/analytics/_metric_ops.py +83 -0
- chrys/service/analytics/_session_projection.py +460 -0
- chrys/service/analytics/_timeline.py +434 -0
- chrys/service/analytics/_turn_graph.py +1237 -0
- chrys/service/analytics/_turns.py +1377 -0
- chrys/service/analytics/_workflows.py +171 -0
- chrys/service/analytics/aggregation.py +1474 -0
- chrys/service/analytics/classification.py +128 -0
- chrys/service/analytics/export.py +803 -0
- chrys/service/analytics/findings.py +282 -0
- chrys/service/analytics/math.py +79 -0
- chrys/service/analytics/model.py +886 -0
- chrys/service/analytics/reader.py +321 -0
- chrys/service/approval/__init__.py +3 -0
- chrys/service/approval/arbitration.py +102 -0
- chrys/service/approval/command_identity.py +109 -0
- chrys/service/approval/correlation.py +57 -0
- chrys/service/approval/grant_store.py +108 -0
- chrys/service/approval/judge.py +589 -0
- chrys/service/approval/level.py +15 -0
- chrys/service/approval/policy.py +209 -0
- chrys/service/approval/reuse.py +223 -0
- chrys/service/approval/reuse_binding.py +133 -0
- chrys/service/approval/safety_classifier.py +536 -0
- chrys/service/approval/turn_context.py +49 -0
- chrys/service/context/__init__.py +3 -0
- chrys/service/context/compaction/__init__.py +38 -0
- chrys/service/context/compaction/breaker.py +156 -0
- chrys/service/context/compaction/budgets.py +93 -0
- chrys/service/context/compaction/calibration.py +77 -0
- chrys/service/context/compaction/compression.py +690 -0
- chrys/service/context/compaction/current_turn_drop.py +458 -0
- chrys/service/context/compaction/events.py +201 -0
- chrys/service/context/compaction/exclusions.py +261 -0
- chrys/service/context/compaction/groups.py +361 -0
- chrys/service/context/compaction/last_words.py +1996 -0
- chrys/service/context/compaction/last_words_state.py +752 -0
- chrys/service/context/compaction/scoped.py +472 -0
- chrys/service/context/compaction/spill.py +1947 -0
- chrys/service/context/compaction/strategy.py +1116 -0
- chrys/service/context/compaction/summaries.py +319 -0
- chrys/service/context/compaction/turns.py +375 -0
- chrys/service/context/manager.py +174 -0
- chrys/service/context/memory_loader.py +792 -0
- chrys/service/context/middleware/__init__.py +3 -0
- chrys/service/context/middleware/usage.py +332 -0
- chrys/service/context/providers/__init__.py +3 -0
- chrys/service/context/providers/context_management.py +432 -0
- chrys/service/context/providers/history.py +647 -0
- chrys/service/context/providers/memory.py +50 -0
- chrys/service/context/tool_content.py +22 -0
- chrys/service/hooks/__init__.py +37 -0
- chrys/service/hooks/detached_worker.py +118 -0
- chrys/service/hooks/docs/README.md +14 -0
- chrys/service/hooks/docs/authoring.md +684 -0
- chrys/service/hooks/docs/configuration.md +499 -0
- chrys/service/hooks/docs/design.md +359 -0
- chrys/service/hooks/docs/project-hooks.md +348 -0
- chrys/service/hooks/events.py +157 -0
- chrys/service/hooks/loader.py +665 -0
- chrys/service/hooks/manager.py +807 -0
- chrys/service/hooks/matcher.py +111 -0
- chrys/service/hooks/outbox.py +294 -0
- chrys/service/hooks/runner.py +572 -0
- chrys/service/hooks/schema.py +407 -0
- chrys/service/llm/__init__.py +7 -0
- chrys/service/llm/_structured_outputs.py +192 -0
- chrys/service/llm/anthropic_messages/__init__.py +9 -0
- chrys/service/llm/anthropic_messages/client.py +254 -0
- chrys/service/llm/anthropic_messages/decode.py +331 -0
- chrys/service/llm/anthropic_messages/history.py +279 -0
- chrys/service/llm/anthropic_messages/request.py +323 -0
- chrys/service/llm/anthropic_messages/server_tools.py +319 -0
- chrys/service/llm/anthropic_messages/stream.py +297 -0
- chrys/service/llm/anthropic_messages/thinking_binding.py +156 -0
- chrys/service/llm/chat_completions/__init__.py +9 -0
- chrys/service/llm/chat_completions/client.py +369 -0
- chrys/service/llm/chat_completions/decode.py +338 -0
- chrys/service/llm/chat_completions/history.py +657 -0
- chrys/service/llm/chat_completions/reasoning.py +244 -0
- chrys/service/llm/chat_completions/request.py +192 -0
- chrys/service/llm/chat_completions/stream.py +546 -0
- chrys/service/llm/chat_completions/validation.py +308 -0
- chrys/service/llm/clients.py +613 -0
- chrys/service/llm/images.py +65 -0
- chrys/service/llm/mock.py +413 -0
- chrys/service/llm/observer.py +446 -0
- chrys/service/llm/one_shot.py +106 -0
- chrys/service/llm/openai_exceptions.py +93 -0
- chrys/service/llm/openai_responses/__init__.py +10 -0
- chrys/service/llm/openai_responses/client.py +323 -0
- chrys/service/llm/openai_responses/decode.py +432 -0
- chrys/service/llm/openai_responses/history.py +519 -0
- chrys/service/llm/openai_responses/hosted.py +625 -0
- chrys/service/llm/openai_responses/replay.py +629 -0
- chrys/service/llm/openai_responses/request.py +342 -0
- chrys/service/llm/openai_responses/stream.py +847 -0
- chrys/service/llm/openai_timestamps.py +57 -0
- chrys/service/llm/providers.py +107 -0
- chrys/service/llm/proxy_route.py +54 -0
- chrys/service/llm/raw_http_log.py +309 -0
- chrys/service/llm/route_facts.py +63 -0
- chrys/service/llm/route_sessions.py +49 -0
- chrys/service/llm/sdk_anthropic.py +18 -0
- chrys/service/llm/sdk_headers.py +47 -0
- chrys/service/llm/sdk_openai.py +18 -0
- chrys/service/llm/sdk_retry.py +59 -0
- chrys/service/llm/wire_client.py +246 -0
- chrys/service/mcp/__init__.py +3 -0
- chrys/service/mcp/_connection.py +235 -0
- chrys/service/mcp/_http_transport.py +350 -0
- chrys/service/mcp/_progressive.py +459 -0
- chrys/service/mcp/_stdio_transport.py +614 -0
- chrys/service/mcp/_tool_mixins.py +96 -0
- chrys/service/mcp/adapter.py +1031 -0
- chrys/service/mcp/cache.py +476 -0
- chrys/service/mcp/content.py +32 -0
- chrys/service/mcp/errors.py +169 -0
- chrys/service/mcp/owned.py +1330 -0
- chrys/service/mcp/result_limits.py +168 -0
- chrys/service/mcp/thinking_warning.py +95 -0
- chrys/service/mcp/validation.py +74 -0
- chrys/service/mutations/__init__.py +84 -0
- chrys/service/mutations/coordination.py +894 -0
- chrys/service/mutations/detector.py +458 -0
- chrys/service/mutations/git_calibrator.py +397 -0
- chrys/service/mutations/git_state.py +647 -0
- chrys/service/mutations/pipeline.py +779 -0
- chrys/service/mutations/scanner.py +466 -0
- chrys/service/mutations/snapshot_files.py +233 -0
- chrys/service/mutations/store.py +625 -0
- chrys/service/mutations/tool_names.py +15 -0
- chrys/service/mutations/trace.py +246 -0
- chrys/service/mutations/tracker.py +1289 -0
- chrys/service/mutations/types.py +559 -0
- chrys/service/mutations/workspace_changes.py +1583 -0
- chrys/service/profiles/__init__.py +2 -0
- chrys/service/profiles/agents/__init__.py +3 -0
- chrys/service/profiles/agents/builtins/Code.yaml +199 -0
- chrys/service/profiles/agents/builtins/Explore.yaml +62 -0
- chrys/service/profiles/agents/builtins/General.yaml +114 -0
- chrys/service/profiles/agents/builtins/QA.yaml +128 -0
- chrys/service/profiles/agents/loader.py +808 -0
- chrys/service/profiles/agents/registry.py +727 -0
- chrys/service/profiles/agents/schema.py +371 -0
- chrys/service/profiles/agents/serializer.py +196 -0
- chrys/service/profiles/coercion.py +22 -0
- chrys/service/profiles/models/__init__.py +3 -0
- chrys/service/profiles/models/env_bridge.py +157 -0
- chrys/service/profiles/models/loader.py +282 -0
- chrys/service/profiles/models/options.py +438 -0
- chrys/service/profiles/models/registry.py +104 -0
- chrys/service/profiles/models/resolver.py +286 -0
- chrys/service/profiles/models/schema.py +91 -0
- chrys/service/profiles/models/serializer.py +76 -0
- chrys/service/session/__init__.py +3 -0
- chrys/service/session/checkpoint.py +156 -0
- chrys/service/session/history.py +1616 -0
- chrys/service/session/message_metadata.py +151 -0
- chrys/service/session/persistence.py +435 -0
- chrys/service/session/runtime_metadata.py +210 -0
- chrys/service/session/sub_agent_logs.py +1069 -0
- chrys/service/session/sub_agent_transcript.py +654 -0
- chrys/service/skills/__init__.py +3 -0
- chrys/service/skills/adapter.py +408 -0
- chrys/service/skills/constants.py +8 -0
- chrys/service/skills/loader.py +575 -0
- chrys/service/skills/model.py +130 -0
- chrys/service/skills/provider.py +841 -0
- chrys/service/skills/runner.py +390 -0
- chrys/service/state/__init__.py +3 -0
- chrys/service/state/_fork.py +710 -0
- chrys/service/state/_session_files.py +175 -0
- chrys/service/state/_session_meta.py +552 -0
- chrys/service/state/locks.py +154 -0
- chrys/service/state/serializers.py +156 -0
- chrys/service/state/session_catalog.py +361 -0
- chrys/service/state/session_listing.py +84 -0
- chrys/service/state/session_migration.py +446 -0
- chrys/service/state/session_mru.py +315 -0
- chrys/service/state/store.py +2132 -0
- chrys/service/state/workflow.py +158 -0
- chrys/service/text_blocks.py +47 -0
- chrys/service/todos/__init__.py +3 -0
- chrys/service/todos/reminder.py +31 -0
- chrys/service/todos/tracker.py +58 -0
- chrys/service/tools/__init__.py +3 -0
- chrys/service/tools/approval_targets.py +92 -0
- chrys/service/tools/builtins/__init__.py +3 -0
- chrys/service/tools/builtins/ask_user.py +326 -0
- chrys/service/tools/builtins/doc_converter/__init__.py +494 -0
- chrys/service/tools/builtins/doc_converter/artifacts.py +392 -0
- chrys/service/tools/builtins/doc_converter/parsers/__init__.py +3 -0
- chrys/service/tools/builtins/doc_converter/parsers/_table.py +34 -0
- chrys/service/tools/builtins/doc_converter/parsers/base.py +101 -0
- chrys/service/tools/builtins/doc_converter/parsers/docx.py +88 -0
- chrys/service/tools/builtins/doc_converter/parsers/pdf.py +144 -0
- chrys/service/tools/builtins/doc_converter/parsers/pptx.py +103 -0
- chrys/service/tools/builtins/doc_converter/parsers/xls.py +32 -0
- chrys/service/tools/builtins/doc_converter/parsers/xlsx.py +51 -0
- chrys/service/tools/builtins/doc_converter/registry.py +55 -0
- chrys/service/tools/builtins/filesystem.py +1071 -0
- chrys/service/tools/builtins/search.py +1265 -0
- chrys/service/tools/builtins/shell.py +967 -0
- chrys/service/tools/builtins/shell_command_filtering/__init__.py +5 -0
- chrys/service/tools/builtins/shell_command_filtering/policy.py +60 -0
- chrys/service/tools/builtins/shell_command_filtering/presets.py +152 -0
- chrys/service/tools/builtins/shell_command_filtering/readonly/__init__.py +358 -0
- chrys/service/tools/builtins/shell_command_filtering/readonly/commands.py +416 -0
- chrys/service/tools/builtins/shell_command_filtering/readonly/git.py +408 -0
- chrys/service/tools/builtins/shell_command_filtering/readonly/syntax.py +87 -0
- chrys/service/tools/builtins/shell_command_filtering/tokenizer.py +214 -0
- chrys/service/tools/builtins/shell_filter.py +24 -0
- chrys/service/tools/builtins/sleep.py +91 -0
- chrys/service/tools/builtins/todo.py +64 -0
- chrys/service/tools/builtins/web/__init__.py +6 -0
- chrys/service/tools/builtins/web/build.py +268 -0
- chrys/service/tools/builtins/web/config.py +338 -0
- chrys/service/tools/builtins/web/credentials.py +37 -0
- chrys/service/tools/builtins/web/fetch/__init__.py +3 -0
- chrys/service/tools/builtins/web/fetch/cache.py +68 -0
- chrys/service/tools/builtins/web/fetch/convert.py +56 -0
- chrys/service/tools/builtins/web/fetch/tool.py +223 -0
- chrys/service/tools/builtins/web/hosted.py +61 -0
- chrys/service/tools/builtins/web/http.py +330 -0
- chrys/service/tools/builtins/web/progress.py +18 -0
- chrys/service/tools/builtins/web/search/__init__.py +3 -0
- chrys/service/tools/builtins/web/search/exa_mcp.py +146 -0
- chrys/service/tools/builtins/web/search/html.py +152 -0
- chrys/service/tools/builtins/web/search/json_api.py +227 -0
- chrys/service/tools/builtins/web/search/providers.py +77 -0
- chrys/service/tools/builtins/web/search/tool.py +218 -0
- chrys/service/tools/builtins/web/search/types.py +28 -0
- chrys/service/tools/builtins/web/tls_identity.py +66 -0
- chrys/service/tools/kinds.py +115 -0
- chrys/service/tools/names.py +47 -0
- chrys/service/tools/registry.py +242 -0
- chrys/service/tools/result_metadata.py +103 -0
- chrys/service/tools/session_artifacts.py +196 -0
- chrys/service/tools/spill.py +218 -0
- chrys/service/tools/workspace_paths.py +81 -0
- chrys/service/trajectory/__init__.py +11 -0
- chrys/service/trajectory/approvals.py +201 -0
- chrys/service/trajectory/compaction.py +420 -0
- chrys/service/trajectory/fork.py +75 -0
- chrys/service/trajectory/hooks.py +217 -0
- chrys/service/trajectory/items.py +41 -0
- chrys/service/trajectory/preparation.py +374 -0
- chrys/service/trajectory/retries.py +95 -0
- chrys/service/trajectory/revisions.py +129 -0
- chrys/service/trajectory/segmented.py +163 -0
- chrys/service/trajectory/session.py +762 -0
- chrys/service/trajectory/state.py +83 -0
- chrys/service/trajectory/sub_agents.py +169 -0
- chrys/service/trajectory/tombstone.py +262 -0
- chrys/service/trajectory/tools.py +323 -0
- chrys/service/trajectory/validation.py +130 -0
- chrys/service/trajectory/waits.py +161 -0
- chrys/service/trajectory/workflow.py +148 -0
- chrys/service/vision.py +128 -0
- chrys/service/workflows/__init__.py +3 -0
- chrys/service/workflows/admission.py +151 -0
- chrys/service/workflows/artifacts.py +109 -0
- chrys/service/workflows/asks.py +130 -0
- chrys/service/workflows/builtins/__init__.py +12 -0
- chrys/service/workflows/builtins/demo-workflow.manifest.json +635 -0
- chrys/service/workflows/builtins/demo-workflow.py +756 -0
- chrys/service/workflows/discovery.py +554 -0
- chrys/service/workflows/environment.py +284 -0
- chrys/service/workflows/graph.py +430 -0
- chrys/service/workflows/history.py +82 -0
- chrys/service/workflows/interpreter.py +107 -0
- chrys/service/workflows/journal.py +334 -0
- chrys/service/workflows/layout.py +56 -0
- chrys/service/workflows/ledger.py +149 -0
- chrys/service/workflows/model_selection.py +18 -0
- chrys/service/workflows/orphans.py +209 -0
- chrys/service/workflows/outcomes.py +46 -0
- chrys/service/workflows/protocol.py +210 -0
- chrys/service/workflows/records.py +142 -0
- chrys/service/workflows/scheduler.py +770 -0
- chrys/service/workflows/sdk/__init__.py +51 -0
- chrys/service/workflows/sdk/_ask.py +167 -0
- chrys/service/workflows/sdk/_builder.py +781 -0
- chrys/service/workflows/sdk/_values.py +92 -0
- chrys/service/workflows/sdk_artifact.py +74 -0
- chrys/service/workflows/store.py +494 -0
- chrys/service/workflows/transcript.py +102 -0
- chrys/service/workflows/values.py +73 -0
- chrys/service/workflows/worker_host.py +1820 -0
- chrys/workflows.py +38 -0
- icode_tui-0.29.1.dist-info/METADATA +104 -0
- icode_tui-0.29.1.dist-info/RECORD +1138 -0
- icode_tui-0.29.1.dist-info/WHEEL +4 -0
- icode_tui-0.29.1.dist-info/entry_points.txt +2 -0
- icode_tui-0.29.1.dist-info/licenses/LICENSE +202 -0
- icode_tui-0.29.1.dist-info/licenses/NOTICE +1955 -0
|
@@ -0,0 +1,710 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""Session fork operations for the JSON file state store."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import contextlib
|
|
8
|
+
import json
|
|
9
|
+
import logging
|
|
10
|
+
import os
|
|
11
|
+
import shutil
|
|
12
|
+
from collections.abc import Callable
|
|
13
|
+
from datetime import UTC, datetime
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import TYPE_CHECKING, Any
|
|
16
|
+
from uuid import uuid4
|
|
17
|
+
|
|
18
|
+
from chrys.foundation.models.session_surface import SessionSurface
|
|
19
|
+
from chrys.foundation.platform.files import (
|
|
20
|
+
atomic_write_owner_only_bytes,
|
|
21
|
+
atomic_write_owner_only_text,
|
|
22
|
+
fsync_directory,
|
|
23
|
+
read_owner_verified_bounded,
|
|
24
|
+
)
|
|
25
|
+
from chrys.foundation.text.mentions import format_file_mention, iter_mention_tokens
|
|
26
|
+
from chrys.foundation.trajectory.ids import new_analytics_id
|
|
27
|
+
from chrys.service.mutations.snapshot_files import rollback_snapshot_paths
|
|
28
|
+
from chrys.service.session.sub_agent_logs import (
|
|
29
|
+
MAX_AUDIT_SCAN_TOTAL_BYTES,
|
|
30
|
+
MAX_SCANNED_AUDIT_FILES,
|
|
31
|
+
MAX_SUB_AGENT_AUDIT_BYTES,
|
|
32
|
+
collect_sub_agent_log_file_references,
|
|
33
|
+
is_safe_sub_agent_log_basename,
|
|
34
|
+
scan_bounded_json_files,
|
|
35
|
+
validate_acp_state_file_references,
|
|
36
|
+
)
|
|
37
|
+
from chrys.service.state._session_files import (
|
|
38
|
+
RAW_HTTP_LOG_FILE_NAME,
|
|
39
|
+
SESSION_BACKUP_FILE_NAME,
|
|
40
|
+
SESSION_CHECKPOINT_ID_KEY,
|
|
41
|
+
SESSION_FILE_NAME,
|
|
42
|
+
SESSION_FORK_MAX_ID_ATTEMPTS,
|
|
43
|
+
SESSION_RECOVERY_FILE_NAME,
|
|
44
|
+
SESSION_WRITE_LOCK_TIMEOUT_SECONDS,
|
|
45
|
+
FileLock,
|
|
46
|
+
SessionForkError,
|
|
47
|
+
SessionNotFoundError,
|
|
48
|
+
_atomic_write_text,
|
|
49
|
+
_is_string_keyed_dict,
|
|
50
|
+
_session_short_id,
|
|
51
|
+
atomic_copy_file,
|
|
52
|
+
make_junction_dropping_ignore,
|
|
53
|
+
session_active_lock_path,
|
|
54
|
+
session_active_owner_path,
|
|
55
|
+
session_write_lock_path,
|
|
56
|
+
)
|
|
57
|
+
from chrys.service.state._session_meta import resolve_session_kind
|
|
58
|
+
from chrys.service.tools.session_artifacts import reharden_document_image_artifacts
|
|
59
|
+
from chrys.service.trajectory.state import TRAJECTORY_STATE_KEY
|
|
60
|
+
|
|
61
|
+
logger = logging.getLogger(__name__)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class SessionForkMixin:
|
|
65
|
+
"""Session-fork operations for JSON state stores."""
|
|
66
|
+
|
|
67
|
+
if TYPE_CHECKING:
|
|
68
|
+
_dir: Path
|
|
69
|
+
|
|
70
|
+
def _session_dir(self, session_id: str) -> Path: ...
|
|
71
|
+
def _session_file(self, session_id: str) -> Path: ...
|
|
72
|
+
def _write_lock_path(self, session_id: str) -> Path: ...
|
|
73
|
+
def _legacy_path(self, session_id: str) -> Path: ...
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
def _read_json_file(path: Path) -> dict[str, Any] | None: ...
|
|
77
|
+
|
|
78
|
+
def _record_mru(self, session_id: str, updated_at: datetime | None) -> None: ...
|
|
79
|
+
def _migrate_if_needed_unlocked(self, session_id: str) -> None: ...
|
|
80
|
+
|
|
81
|
+
def fork_session(self, parent_session_id: str, *, last_surface: SessionSurface | None = None) -> str:
|
|
82
|
+
"""Create an independent copy of *parent_session_id* with a new canonical id.
|
|
83
|
+
|
|
84
|
+
*last_surface* names the surface that forked it; ``None`` keeps the parent's.
|
|
85
|
+
"""
|
|
86
|
+
try:
|
|
87
|
+
return self._fork_session_sync(parent_session_id, last_surface=last_surface)
|
|
88
|
+
except SessionNotFoundError:
|
|
89
|
+
raise
|
|
90
|
+
except SessionForkError:
|
|
91
|
+
raise
|
|
92
|
+
except Exception as exc:
|
|
93
|
+
raise SessionForkError(f"Failed to fork session {parent_session_id}") from exc
|
|
94
|
+
|
|
95
|
+
def _fork_session_sync(self, parent_session_id: str, *, last_surface: SessionSurface | None = None) -> str:
|
|
96
|
+
"""Sync implementation of session fork."""
|
|
97
|
+
with FileLock(self._write_lock_path(parent_session_id), timeout=SESSION_WRITE_LOCK_TIMEOUT_SECONDS):
|
|
98
|
+
self._migrate_if_needed_unlocked(parent_session_id)
|
|
99
|
+
parent_dir = self._session_dir(parent_session_id)
|
|
100
|
+
if not parent_dir.is_dir() or not self._session_file(parent_session_id).exists():
|
|
101
|
+
raise SessionNotFoundError(f"Session '{parent_session_id}' not found")
|
|
102
|
+
|
|
103
|
+
parent = self._read_json_file(self._session_file(parent_session_id))
|
|
104
|
+
if parent is not None and resolve_session_kind(parent.get("meta", {})) != "chat":
|
|
105
|
+
raise SessionForkError("Only Chat sessions can be forked. Start a new Workflow session instead.")
|
|
106
|
+
|
|
107
|
+
last_collision: SessionForkError | None = None
|
|
108
|
+
for _attempt in range(SESSION_FORK_MAX_ID_ATTEMPTS):
|
|
109
|
+
new_session_id = self._new_fork_session_id()
|
|
110
|
+
dest_dir = self._session_dir(new_session_id)
|
|
111
|
+
with FileLock(self._write_lock_path(new_session_id), timeout=SESSION_WRITE_LOCK_TIMEOUT_SECONDS):
|
|
112
|
+
try:
|
|
113
|
+
self._assert_fork_destination_available(new_session_id, include_write_lock=False)
|
|
114
|
+
except SessionForkError as exc:
|
|
115
|
+
last_collision = exc
|
|
116
|
+
continue
|
|
117
|
+
tmp_dir = self._new_fork_temp_dir(new_session_id)
|
|
118
|
+
try:
|
|
119
|
+
shutil.copytree(
|
|
120
|
+
parent_dir, tmp_dir, symlinks=True, ignore=self._make_fork_copy_ignore(parent_dir)
|
|
121
|
+
)
|
|
122
|
+
reharden_document_image_artifacts(parent_dir, tmp_dir)
|
|
123
|
+
self._sanitize_fork_sub_agent_layout(tmp_dir)
|
|
124
|
+
copied_sub_agent_artifacts = self._secure_copy_sub_agent_artifacts(parent_dir, tmp_dir)
|
|
125
|
+
fork_updated_at = self._prepare_fork_directory(
|
|
126
|
+
tmp_dir,
|
|
127
|
+
dest_dir=dest_dir,
|
|
128
|
+
parent_session_id=parent_session_id,
|
|
129
|
+
new_session_id=new_session_id,
|
|
130
|
+
copied_sub_agent_artifacts=copied_sub_agent_artifacts,
|
|
131
|
+
last_surface=last_surface,
|
|
132
|
+
)
|
|
133
|
+
# Index before the rename commits the fork.
|
|
134
|
+
self._record_mru(new_session_id, fork_updated_at)
|
|
135
|
+
os.replace(tmp_dir, dest_dir)
|
|
136
|
+
fsync_directory(self._dir)
|
|
137
|
+
tmp_dir = None
|
|
138
|
+
return new_session_id
|
|
139
|
+
finally:
|
|
140
|
+
if tmp_dir is not None:
|
|
141
|
+
self._remove_fork_temp_dir(tmp_dir)
|
|
142
|
+
raise SessionForkError("Could not allocate a collision-free fork session id") from last_collision
|
|
143
|
+
|
|
144
|
+
def _new_fork_session_id(self) -> str:
|
|
145
|
+
"""Return a collision-free canonical session id for a fork."""
|
|
146
|
+
for _attempt in range(SESSION_FORK_MAX_ID_ATTEMPTS):
|
|
147
|
+
candidate = str(uuid4())
|
|
148
|
+
if self._fork_destination_available(candidate):
|
|
149
|
+
return candidate
|
|
150
|
+
raise SessionForkError("Could not allocate a collision-free fork session id")
|
|
151
|
+
|
|
152
|
+
def _fork_destination_available(self, session_id: str) -> bool:
|
|
153
|
+
try:
|
|
154
|
+
self._assert_fork_destination_available(session_id)
|
|
155
|
+
except SessionForkError:
|
|
156
|
+
return False
|
|
157
|
+
return True
|
|
158
|
+
|
|
159
|
+
def _assert_fork_destination_available(self, session_id: str, *, include_write_lock: bool = True) -> None:
|
|
160
|
+
paths = [
|
|
161
|
+
self._session_dir(session_id),
|
|
162
|
+
self._legacy_path(session_id),
|
|
163
|
+
session_active_lock_path(self._dir, session_id),
|
|
164
|
+
session_active_owner_path(self._dir, session_id),
|
|
165
|
+
]
|
|
166
|
+
if include_write_lock:
|
|
167
|
+
paths.append(session_write_lock_path(self._dir, session_id))
|
|
168
|
+
for path in paths:
|
|
169
|
+
if path.exists():
|
|
170
|
+
raise SessionForkError(f"Fork destination collision at {path}")
|
|
171
|
+
|
|
172
|
+
def _new_fork_temp_dir(self, session_id: str) -> Path:
|
|
173
|
+
for _attempt in range(SESSION_FORK_MAX_ID_ATTEMPTS):
|
|
174
|
+
path = self._dir / f".{_session_short_id(session_id)}.fork.{uuid4().hex}.tmp"
|
|
175
|
+
if not path.exists():
|
|
176
|
+
return path
|
|
177
|
+
raise SessionForkError("Could not allocate a temporary fork directory")
|
|
178
|
+
|
|
179
|
+
@staticmethod
|
|
180
|
+
def _remove_fork_temp_dir(path: Path) -> None:
|
|
181
|
+
try:
|
|
182
|
+
shutil.rmtree(path)
|
|
183
|
+
except FileNotFoundError:
|
|
184
|
+
return
|
|
185
|
+
except OSError:
|
|
186
|
+
logger.warning("Failed to remove temporary fork directory %s", path, exc_info=True)
|
|
187
|
+
|
|
188
|
+
def _prepare_fork_directory(
|
|
189
|
+
self,
|
|
190
|
+
tmp_dir: Path,
|
|
191
|
+
*,
|
|
192
|
+
dest_dir: Path,
|
|
193
|
+
parent_session_id: str,
|
|
194
|
+
new_session_id: str,
|
|
195
|
+
copied_sub_agent_artifacts: list[str],
|
|
196
|
+
last_surface: SessionSurface | None = None,
|
|
197
|
+
) -> datetime:
|
|
198
|
+
"""Delete non-copyable files and rewrite fork-local session identity.
|
|
199
|
+
|
|
200
|
+
Returns the ``updated_at`` stamped into the fork's envelope so the
|
|
201
|
+
caller can index the new session with the value actually on disk.
|
|
202
|
+
"""
|
|
203
|
+
with contextlib.suppress(FileNotFoundError):
|
|
204
|
+
(tmp_dir / SESSION_RECOVERY_FILE_NAME).unlink()
|
|
205
|
+
with contextlib.suppress(FileNotFoundError):
|
|
206
|
+
(tmp_dir / RAW_HTTP_LOG_FILE_NAME).unlink()
|
|
207
|
+
self._prepare_fork_sub_agent_artifacts(tmp_dir)
|
|
208
|
+
|
|
209
|
+
fork_updated_at = datetime.now(UTC)
|
|
210
|
+
updated_at = fork_updated_at.isoformat()
|
|
211
|
+
parent_clipboard_root = (self._session_dir(parent_session_id) / "attachments" / "clipboard").resolve(
|
|
212
|
+
strict=False
|
|
213
|
+
)
|
|
214
|
+
fork_clipboard_root = (dest_dir / "attachments" / "clipboard").resolve(strict=False)
|
|
215
|
+
primary = tmp_dir / SESSION_FILE_NAME
|
|
216
|
+
self._rewrite_fork_envelope_file(
|
|
217
|
+
primary,
|
|
218
|
+
parent_session_id=parent_session_id,
|
|
219
|
+
new_session_id=new_session_id,
|
|
220
|
+
updated_at=updated_at,
|
|
221
|
+
parent_clipboard_root=parent_clipboard_root,
|
|
222
|
+
fork_clipboard_root=fork_clipboard_root,
|
|
223
|
+
last_surface=last_surface,
|
|
224
|
+
)
|
|
225
|
+
backup = tmp_dir / SESSION_BACKUP_FILE_NAME
|
|
226
|
+
if backup.exists():
|
|
227
|
+
rewritten = self._try_rewrite_auxiliary_fork_envelope_file(
|
|
228
|
+
backup,
|
|
229
|
+
parent_session_id=parent_session_id,
|
|
230
|
+
new_session_id=new_session_id,
|
|
231
|
+
updated_at=updated_at,
|
|
232
|
+
parent_clipboard_root=parent_clipboard_root,
|
|
233
|
+
fork_clipboard_root=fork_clipboard_root,
|
|
234
|
+
last_surface=last_surface,
|
|
235
|
+
)
|
|
236
|
+
if not rewritten:
|
|
237
|
+
atomic_copy_file(primary, backup)
|
|
238
|
+
|
|
239
|
+
for snapshot in rollback_snapshot_paths(tmp_dir):
|
|
240
|
+
rewritten = self._try_rewrite_auxiliary_fork_envelope_file(
|
|
241
|
+
snapshot,
|
|
242
|
+
parent_session_id=parent_session_id,
|
|
243
|
+
new_session_id=new_session_id,
|
|
244
|
+
updated_at=updated_at,
|
|
245
|
+
parent_clipboard_root=parent_clipboard_root,
|
|
246
|
+
fork_clipboard_root=fork_clipboard_root,
|
|
247
|
+
last_surface=last_surface,
|
|
248
|
+
)
|
|
249
|
+
if not rewritten:
|
|
250
|
+
snapshot.unlink()
|
|
251
|
+
|
|
252
|
+
self._rewrite_fork_sub_agent_logs(
|
|
253
|
+
tmp_dir,
|
|
254
|
+
new_session_id=new_session_id,
|
|
255
|
+
parent_clipboard_root=parent_clipboard_root,
|
|
256
|
+
fork_clipboard_root=fork_clipboard_root,
|
|
257
|
+
artifact_names=copied_sub_agent_artifacts,
|
|
258
|
+
)
|
|
259
|
+
return fork_updated_at
|
|
260
|
+
|
|
261
|
+
@staticmethod
|
|
262
|
+
def _make_fork_copy_ignore(parent_root: Path) -> Callable[[str, list[str]], set[str]]:
|
|
263
|
+
# Exclude the SESSION-ROOT sub_agents subtree from the generic tree
|
|
264
|
+
# copy. copytree(symlinks=True) copies symlinks verbatim but still
|
|
265
|
+
# FOLLOWS NT directory junctions (os.path.islink() is False for them),
|
|
266
|
+
# so a same-uid untrusted ACP process could plant a junction here that
|
|
267
|
+
# gets materialized as a real directory and evades every post-copy link
|
|
268
|
+
# check — a path back to the parent recurses to disk exhaustion. The
|
|
269
|
+
# only artifacts that carry forward (sessions/*.json + *.stderr.log)
|
|
270
|
+
# are re-created afterward by _secure_copy_sub_agent_artifacts, whose
|
|
271
|
+
# owner-verified writes validate a link-free parent chain.
|
|
272
|
+
parent_key = os.path.normcase(os.path.abspath(parent_root))
|
|
273
|
+
drop_junctions = make_junction_dropping_ignore()
|
|
274
|
+
|
|
275
|
+
def _ignore(path: str, names: list[str]) -> set[str]:
|
|
276
|
+
# Drop NT directory junctions at EVERY level, not just the root —
|
|
277
|
+
# a junction planted anywhere the copy reaches, including a nested
|
|
278
|
+
# ``compactions/sub_agents/<tool>/<invocation>/…`` spill dir the
|
|
279
|
+
# same-uid untrusted ACP process can write, would be materialized
|
|
280
|
+
# through plain CopyFile2 (bypassing owner-only recreation).
|
|
281
|
+
excluded = drop_junctions(path, names)
|
|
282
|
+
# Match the session ROOT only for the by-name sub_agents exclusion:
|
|
283
|
+
# nested directories that merely share the name — kernel compaction
|
|
284
|
+
# spill lives under ``compactions/sub_agents/<tool>/<invocation>/…``
|
|
285
|
+
# — must copy through untouched, or the fork loses dropped-turn
|
|
286
|
+
# records while keeping audit/catalog references to them.
|
|
287
|
+
if os.path.normcase(os.path.abspath(path)) == parent_key:
|
|
288
|
+
# Case-insensitive on the name: NTFS is case-insensitive, so a
|
|
289
|
+
# planted ``SUB_AGENTS`` dir would slip past an exact-case check
|
|
290
|
+
# yet copytree would still copy it. Exclude every case variant
|
|
291
|
+
# at the root; sessions/ is rebuilt afterward regardless.
|
|
292
|
+
excluded.update(name for name in names if name.casefold() == "sub_agents")
|
|
293
|
+
# The trajectory log is per-session execution history: the
|
|
294
|
+
# fork opens its own (see service.trajectory.fork), and the
|
|
295
|
+
# parent's writer may hold this one open right now.
|
|
296
|
+
excluded.update(name for name in names if name.casefold() == "trajectory")
|
|
297
|
+
# Remembered session permissions belong only to the original session.
|
|
298
|
+
excluded.update(
|
|
299
|
+
name for name in names if name.casefold() in {"approval-grants.json", "approval-grants.json.lock"}
|
|
300
|
+
)
|
|
301
|
+
return excluded
|
|
302
|
+
|
|
303
|
+
return _ignore
|
|
304
|
+
|
|
305
|
+
@staticmethod
|
|
306
|
+
def _sanitize_fork_sub_agent_layout(tmp_dir: Path) -> None:
|
|
307
|
+
"""Drop symlinked sub_agents levels from the fork copy before touching them.
|
|
308
|
+
|
|
309
|
+
The parent session's sub_agents tree is writable by the same-uid
|
|
310
|
+
UNTRUSTED ACP process, and ``copytree(symlinks=True)`` preserves a
|
|
311
|
+
planted directory symlink verbatim (its children never reach the
|
|
312
|
+
ignore callback). Every later cleanup/rewrite in the fork copy would
|
|
313
|
+
then resolve THROUGH the link and rmtree/unlink data outside the
|
|
314
|
+
session. Links are removed, never followed.
|
|
315
|
+
"""
|
|
316
|
+
sub_agents = tmp_dir / "sub_agents"
|
|
317
|
+
for candidate in (sub_agents, sub_agents / "sessions", sub_agents / "pending"):
|
|
318
|
+
try:
|
|
319
|
+
if candidate.is_symlink() or candidate.is_junction():
|
|
320
|
+
candidate.unlink()
|
|
321
|
+
logger.warning("Removed linked sub-agent layout entry from fork copy: %s", candidate.name)
|
|
322
|
+
except OSError:
|
|
323
|
+
logger.warning("Failed to sanitize fork sub-agent layout entry: %s", candidate.name, exc_info=True)
|
|
324
|
+
|
|
325
|
+
@classmethod
|
|
326
|
+
def _secure_copy_sub_agent_artifacts(cls, parent_dir: Path, tmp_dir: Path) -> list[str]:
|
|
327
|
+
"""Rebuild the fork's sub_agents/sessions dir; return the copied names.
|
|
328
|
+
|
|
329
|
+
The returned basenames are the EXACT set the rewrite pass must process
|
|
330
|
+
(see _rewrite_fork_sub_agent_logs): the reference-driven pass below can
|
|
331
|
+
copy audits past the anti-flood scan cap, so the rewrite must iterate
|
|
332
|
+
this concrete set rather than re-scanning under the same cap (which would
|
|
333
|
+
leave a referenced-but-uncapped audit copied yet never id-rewritten).
|
|
334
|
+
"""
|
|
335
|
+
source_root = parent_dir / "sub_agents"
|
|
336
|
+
source_dir = source_root / "sessions"
|
|
337
|
+
# Same trust boundary as the fork-copy sanitizer: never traverse a
|
|
338
|
+
# linked layout the ACP process may have planted in the live session.
|
|
339
|
+
if source_root.is_symlink() or source_root.is_junction() or source_dir.is_symlink() or source_dir.is_junction():
|
|
340
|
+
logger.warning("Skipping fork copy of linked sub-agent layout: %s", source_root.name)
|
|
341
|
+
return []
|
|
342
|
+
if not source_dir.is_dir():
|
|
343
|
+
return []
|
|
344
|
+
destination_dir = tmp_dir / "sub_agents" / "sessions"
|
|
345
|
+
copied: set[str] = set()
|
|
346
|
+
|
|
347
|
+
def _copy_artifact(name: str) -> None:
|
|
348
|
+
try:
|
|
349
|
+
# Owner-verified, not owner-only: parent sessions written by
|
|
350
|
+
# releases predating the owner-only writers must still fork.
|
|
351
|
+
# Bounded: a same-uid untrusted ACP process could plant an
|
|
352
|
+
# over-sized/sparse artifact; over-cap files are skipped.
|
|
353
|
+
payload = read_owner_verified_bounded(source_dir / name, max_bytes=MAX_SUB_AGENT_AUDIT_BYTES)
|
|
354
|
+
atomic_write_owner_only_bytes(destination_dir / name, payload)
|
|
355
|
+
except OSError, ValueError:
|
|
356
|
+
logger.warning("Omitting insecure fork sub-agent artifact: %s", name, exc_info=True)
|
|
357
|
+
copied.add(name)
|
|
358
|
+
|
|
359
|
+
# The parent audit dir is same-uid ACP-writable: bound BOTH enumeration
|
|
360
|
+
# (Path.iterdir is eager on 3.14 → a planted flood OOMs the fork during
|
|
361
|
+
# listing) and cumulative read (4096 x 128 MiB would be a huge copy) with
|
|
362
|
+
# the shared scandir scanner. This best-effort pass carries UNREFERENCED
|
|
363
|
+
# artifacts (e.g. reconcile orphans) forward up to the caps; the
|
|
364
|
+
# referenced pass below guarantees the ones the forked history links.
|
|
365
|
+
sources, truncated = scan_bounded_json_files(
|
|
366
|
+
source_dir,
|
|
367
|
+
max_files=MAX_SCANNED_AUDIT_FILES,
|
|
368
|
+
max_total_bytes=MAX_AUDIT_SCAN_TOTAL_BYTES,
|
|
369
|
+
suffixes=(".json", ".stderr.log"),
|
|
370
|
+
)
|
|
371
|
+
if truncated:
|
|
372
|
+
logger.warning("Fork sub-agent artifact scan truncated; unreferenced artifacts beyond the cap are omitted")
|
|
373
|
+
for source in sources:
|
|
374
|
+
_copy_artifact(source.name)
|
|
375
|
+
|
|
376
|
+
# Guarantee every audit the forked history references is carried forward
|
|
377
|
+
# even when the bounded scan truncated before reaching it. The reference
|
|
378
|
+
# set comes from the already-copied forked history (session.json), so it
|
|
379
|
+
# is bounded by history size — NOT by the attacker-writable file count —
|
|
380
|
+
# and a legitimately large session can never fork with a dangling
|
|
381
|
+
# sub_agent_log_file link. The generic copytree already placed
|
|
382
|
+
# session.json in tmp_dir; the later fork envelope rewrite leaves these
|
|
383
|
+
# references untouched. A corrupt/over-huge session.json simply yields no
|
|
384
|
+
# referenced set (the bounded scan above still ran).
|
|
385
|
+
try:
|
|
386
|
+
envelope = cls._read_json_file(tmp_dir / SESSION_FILE_NAME)
|
|
387
|
+
except ValueError, RecursionError:
|
|
388
|
+
envelope = None
|
|
389
|
+
if not isinstance(envelope, dict):
|
|
390
|
+
return sorted(copied)
|
|
391
|
+
for name in sorted(collect_sub_agent_log_file_references(envelope)):
|
|
392
|
+
if not is_safe_sub_agent_log_basename(name):
|
|
393
|
+
continue
|
|
394
|
+
if name not in copied:
|
|
395
|
+
_copy_artifact(name)
|
|
396
|
+
# Carry the audit's stderr sibling too (deterministic name); a
|
|
397
|
+
# missing one is normal (the sub-agent may not have written stderr),
|
|
398
|
+
# so only attempt a copy when it is present to avoid log noise.
|
|
399
|
+
stderr_name = f"{name}.stderr.log"
|
|
400
|
+
if stderr_name not in copied and (source_dir / stderr_name).is_file():
|
|
401
|
+
_copy_artifact(stderr_name)
|
|
402
|
+
return sorted(copied)
|
|
403
|
+
|
|
404
|
+
@staticmethod
|
|
405
|
+
def _prepare_fork_sub_agent_artifacts(tmp_dir: Path) -> None:
|
|
406
|
+
sub_agents = tmp_dir / "sub_agents"
|
|
407
|
+
# Defense in depth behind _sanitize_fork_sub_agent_layout: if the
|
|
408
|
+
# link removal failed, refusing to traverse beats deleting through it.
|
|
409
|
+
if sub_agents.is_symlink() or sub_agents.is_junction() or not sub_agents.is_dir():
|
|
410
|
+
return
|
|
411
|
+
shutil.rmtree(sub_agents / "pending", ignore_errors=True)
|
|
412
|
+
for legacy_pending in sub_agents.glob("*.json"):
|
|
413
|
+
with contextlib.suppress(OSError):
|
|
414
|
+
legacy_pending.unlink()
|
|
415
|
+
for temp_file in sub_agents.rglob("*.tmp"):
|
|
416
|
+
with contextlib.suppress(OSError):
|
|
417
|
+
temp_file.unlink()
|
|
418
|
+
|
|
419
|
+
@classmethod
|
|
420
|
+
def _rewrite_fork_sub_agent_logs(
|
|
421
|
+
cls,
|
|
422
|
+
tmp_dir: Path,
|
|
423
|
+
*,
|
|
424
|
+
new_session_id: str,
|
|
425
|
+
parent_clipboard_root: Path,
|
|
426
|
+
fork_clipboard_root: Path,
|
|
427
|
+
artifact_names: list[str] | None = None,
|
|
428
|
+
) -> None:
|
|
429
|
+
sessions_dir = tmp_dir / "sub_agents" / "sessions"
|
|
430
|
+
# Defense in depth behind _sanitize_fork_sub_agent_layout: rewritten
|
|
431
|
+
# envelopes must never be written through a linked layout.
|
|
432
|
+
if (
|
|
433
|
+
(tmp_dir / "sub_agents").is_symlink()
|
|
434
|
+
or (tmp_dir / "sub_agents").is_junction()
|
|
435
|
+
or sessions_dir.is_symlink()
|
|
436
|
+
or sessions_dir.is_junction()
|
|
437
|
+
or not sessions_dir.is_dir()
|
|
438
|
+
):
|
|
439
|
+
return
|
|
440
|
+
if artifact_names is not None:
|
|
441
|
+
# Rewrite EXACTLY the set _secure_copy_sub_agent_artifacts wrote. Its
|
|
442
|
+
# reference-driven pass can copy audits past the anti-flood scan cap,
|
|
443
|
+
# so re-scanning here under the same cap would leave those referenced
|
|
444
|
+
# audits copied yet never id-rewritten (stale parent_session_id). The
|
|
445
|
+
# rebuilt dir is chrys-owned inside the unguessable fork tmp, so this
|
|
446
|
+
# concrete list — bounded by the copier — is the authoritative set.
|
|
447
|
+
rewrite_paths = sorted(sessions_dir / name for name in artifact_names if name.endswith(".json"))
|
|
448
|
+
else:
|
|
449
|
+
# Direct callers (no prior copy) fall back to the bounded scanner so
|
|
450
|
+
# no fork audit scan relies on an eager glob over an ACP-influenced
|
|
451
|
+
# tree.
|
|
452
|
+
rewrite_paths, _truncated = scan_bounded_json_files(
|
|
453
|
+
sessions_dir,
|
|
454
|
+
max_files=MAX_SCANNED_AUDIT_FILES,
|
|
455
|
+
max_total_bytes=MAX_AUDIT_SCAN_TOTAL_BYTES,
|
|
456
|
+
)
|
|
457
|
+
for path in rewrite_paths:
|
|
458
|
+
try:
|
|
459
|
+
envelope = json.loads(
|
|
460
|
+
read_owner_verified_bounded(path, max_bytes=MAX_SUB_AGENT_AUDIT_BYTES).decode("utf-8")
|
|
461
|
+
)
|
|
462
|
+
except OSError, UnicodeError, json.JSONDecodeError, ValueError, RecursionError:
|
|
463
|
+
# RecursionError (not a ValueError): json.loads recurses per
|
|
464
|
+
# nesting level, so a deeply nested but byte-bounded artifact
|
|
465
|
+
# blows the stack. Skip that one file rather than abort the fork.
|
|
466
|
+
logger.warning("Skipping malformed fork sub-agent audit log: %s", path, exc_info=True)
|
|
467
|
+
continue
|
|
468
|
+
if not isinstance(envelope, dict):
|
|
469
|
+
logger.warning("Leaving opaque fork sub-agent artifact untouched: %s", path)
|
|
470
|
+
continue
|
|
471
|
+
meta = envelope.get("meta")
|
|
472
|
+
if not isinstance(meta, dict):
|
|
473
|
+
logger.warning("Leaving metadata-free fork sub-agent artifact untouched: %s", path)
|
|
474
|
+
continue
|
|
475
|
+
runner = meta.get("runner", "kernel")
|
|
476
|
+
if runner not in {"kernel", "acp"}:
|
|
477
|
+
continue
|
|
478
|
+
if runner == "acp":
|
|
479
|
+
acp_state = envelope.get("acp_state")
|
|
480
|
+
try:
|
|
481
|
+
if not isinstance(acp_state, dict):
|
|
482
|
+
raise ValueError("ACP sub-agent log is missing ACP state")
|
|
483
|
+
validate_acp_state_file_references(acp_state, parent_session_dir=tmp_dir)
|
|
484
|
+
except ValueError:
|
|
485
|
+
logger.warning("Omitting unsafe ACP fork sub-agent audit log: %s", path, exc_info=True)
|
|
486
|
+
path.unlink()
|
|
487
|
+
continue
|
|
488
|
+
meta["parent_session_id"] = new_session_id
|
|
489
|
+
state = envelope.get("state")
|
|
490
|
+
if runner == "kernel" and isinstance(state, dict):
|
|
491
|
+
cls._rewrite_clipboard_mentions_in_state(
|
|
492
|
+
state,
|
|
493
|
+
parent_clipboard_root=parent_clipboard_root,
|
|
494
|
+
fork_clipboard_root=fork_clipboard_root,
|
|
495
|
+
)
|
|
496
|
+
try:
|
|
497
|
+
serialized = json.dumps(envelope, indent=2, ensure_ascii=False, allow_nan=False)
|
|
498
|
+
except ValueError:
|
|
499
|
+
# A legacy artifact written with the default allow_nan=True can
|
|
500
|
+
# carry NaN/Infinity that json.loads accepts but the strict
|
|
501
|
+
# re-encode rejects. Skip this one file (leave the original,
|
|
502
|
+
# parent-scoped copy) rather than aborting the entire fork.
|
|
503
|
+
logger.warning("Leaving non-encodable fork sub-agent artifact untouched: %s", path, exc_info=True)
|
|
504
|
+
continue
|
|
505
|
+
# A lone surrogate a same-uid child may have planted in the audit is
|
|
506
|
+
# neutralized at the sink (atomic_write_owner_only_text encodes with
|
|
507
|
+
# backslashreplace), so this write cannot abort the fork.
|
|
508
|
+
atomic_write_owner_only_text(path, serialized)
|
|
509
|
+
|
|
510
|
+
def _rewrite_fork_envelope_file(
|
|
511
|
+
self,
|
|
512
|
+
path: Path,
|
|
513
|
+
*,
|
|
514
|
+
parent_session_id: str,
|
|
515
|
+
new_session_id: str,
|
|
516
|
+
updated_at: str,
|
|
517
|
+
parent_clipboard_root: Path,
|
|
518
|
+
fork_clipboard_root: Path,
|
|
519
|
+
last_surface: SessionSurface | None = None,
|
|
520
|
+
) -> None:
|
|
521
|
+
envelope = self._read_json_file(path)
|
|
522
|
+
if envelope is None:
|
|
523
|
+
raise SessionForkError(f"Cannot rewrite invalid session envelope: {path}")
|
|
524
|
+
self._rewrite_fork_envelope(
|
|
525
|
+
envelope,
|
|
526
|
+
parent_session_id=parent_session_id,
|
|
527
|
+
new_session_id=new_session_id,
|
|
528
|
+
updated_at=updated_at,
|
|
529
|
+
parent_clipboard_root=parent_clipboard_root,
|
|
530
|
+
fork_clipboard_root=fork_clipboard_root,
|
|
531
|
+
last_surface=last_surface,
|
|
532
|
+
)
|
|
533
|
+
_atomic_write_text(path, json.dumps(envelope, indent=2, ensure_ascii=False))
|
|
534
|
+
|
|
535
|
+
def _try_rewrite_auxiliary_fork_envelope_file(
|
|
536
|
+
self,
|
|
537
|
+
path: Path,
|
|
538
|
+
*,
|
|
539
|
+
parent_session_id: str,
|
|
540
|
+
new_session_id: str,
|
|
541
|
+
updated_at: str,
|
|
542
|
+
parent_clipboard_root: Path,
|
|
543
|
+
fork_clipboard_root: Path,
|
|
544
|
+
last_surface: SessionSurface | None = None,
|
|
545
|
+
) -> bool:
|
|
546
|
+
envelope = self._read_json_file(path)
|
|
547
|
+
if envelope is None:
|
|
548
|
+
logger.warning("Skipping invalid fork auxiliary session envelope: %s", path)
|
|
549
|
+
return False
|
|
550
|
+
try:
|
|
551
|
+
self._rewrite_fork_envelope(
|
|
552
|
+
envelope,
|
|
553
|
+
parent_session_id=parent_session_id,
|
|
554
|
+
new_session_id=new_session_id,
|
|
555
|
+
updated_at=updated_at,
|
|
556
|
+
parent_clipboard_root=parent_clipboard_root,
|
|
557
|
+
fork_clipboard_root=fork_clipboard_root,
|
|
558
|
+
last_surface=last_surface,
|
|
559
|
+
)
|
|
560
|
+
except SessionForkError:
|
|
561
|
+
logger.warning("Skipping malformed fork auxiliary session envelope: %s", path, exc_info=True)
|
|
562
|
+
return False
|
|
563
|
+
_atomic_write_text(path, json.dumps(envelope, indent=2, ensure_ascii=False))
|
|
564
|
+
return True
|
|
565
|
+
|
|
566
|
+
@classmethod
|
|
567
|
+
def _rewrite_fork_envelope(
|
|
568
|
+
cls,
|
|
569
|
+
envelope: dict[str, Any],
|
|
570
|
+
*,
|
|
571
|
+
parent_session_id: str,
|
|
572
|
+
new_session_id: str,
|
|
573
|
+
updated_at: str,
|
|
574
|
+
parent_clipboard_root: Path,
|
|
575
|
+
fork_clipboard_root: Path,
|
|
576
|
+
last_surface: SessionSurface | None = None,
|
|
577
|
+
) -> None:
|
|
578
|
+
meta = envelope.get("meta")
|
|
579
|
+
if not isinstance(meta, dict):
|
|
580
|
+
raise SessionForkError("Session envelope is missing metadata")
|
|
581
|
+
meta["session_id"] = new_session_id
|
|
582
|
+
meta["parent_session_id"] = parent_session_id
|
|
583
|
+
meta["updated_at"] = updated_at
|
|
584
|
+
meta["service_session_id"] = ""
|
|
585
|
+
if last_surface is not None:
|
|
586
|
+
meta["last_surface"] = last_surface.value
|
|
587
|
+
# The fork is a new on-disk revision: derived summaries must not be
|
|
588
|
+
# able to claim they were computed from it via the parent's id.
|
|
589
|
+
envelope[SESSION_CHECKPOINT_ID_KEY] = new_analytics_id()
|
|
590
|
+
# A user-pinned custom_title carries over verbatim (losing the rename
|
|
591
|
+
# reads as data loss). The carried pin keeps auto-generated refreshes
|
|
592
|
+
# disabled on the fork, exactly as on the parent, until renamed.
|
|
593
|
+
|
|
594
|
+
state = envelope.get("state")
|
|
595
|
+
if isinstance(state, dict):
|
|
596
|
+
# The fork records into a log of its own, numbered from one: the
|
|
597
|
+
# turn registry's sequences point into the parent's log and would
|
|
598
|
+
# make a rollback here name a range that never existed.
|
|
599
|
+
state.pop(TRAJECTORY_STATE_KEY, None)
|
|
600
|
+
cls._rewrite_clipboard_mentions_in_state(
|
|
601
|
+
state,
|
|
602
|
+
parent_clipboard_root=parent_clipboard_root,
|
|
603
|
+
fork_clipboard_root=fork_clipboard_root,
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
@classmethod
|
|
607
|
+
def _rewrite_clipboard_mentions_in_state(
|
|
608
|
+
cls,
|
|
609
|
+
state: dict[str, Any],
|
|
610
|
+
*,
|
|
611
|
+
parent_clipboard_root: Path,
|
|
612
|
+
fork_clipboard_root: Path,
|
|
613
|
+
) -> None:
|
|
614
|
+
messages = state.get("messages")
|
|
615
|
+
if isinstance(messages, list):
|
|
616
|
+
cls._rewrite_clipboard_mentions_in_messages(
|
|
617
|
+
messages,
|
|
618
|
+
parent_clipboard_root=parent_clipboard_root,
|
|
619
|
+
fork_clipboard_root=fork_clipboard_root,
|
|
620
|
+
)
|
|
621
|
+
compressed_blocks = state.get("compressed_msgs")
|
|
622
|
+
if not isinstance(compressed_blocks, list):
|
|
623
|
+
return
|
|
624
|
+
for block in compressed_blocks:
|
|
625
|
+
if not isinstance(block, dict):
|
|
626
|
+
continue
|
|
627
|
+
block_messages = block.get("messages")
|
|
628
|
+
if isinstance(block_messages, list):
|
|
629
|
+
cls._rewrite_clipboard_mentions_in_messages(
|
|
630
|
+
block_messages,
|
|
631
|
+
parent_clipboard_root=parent_clipboard_root,
|
|
632
|
+
fork_clipboard_root=fork_clipboard_root,
|
|
633
|
+
)
|
|
634
|
+
|
|
635
|
+
@classmethod
|
|
636
|
+
def _rewrite_clipboard_mentions_in_messages(
|
|
637
|
+
cls,
|
|
638
|
+
messages: list[Any],
|
|
639
|
+
*,
|
|
640
|
+
parent_clipboard_root: Path,
|
|
641
|
+
fork_clipboard_root: Path,
|
|
642
|
+
) -> None:
|
|
643
|
+
for message in messages:
|
|
644
|
+
if not isinstance(message, dict) or message.get("role") != "user":
|
|
645
|
+
continue
|
|
646
|
+
contents = message.get("contents")
|
|
647
|
+
if not isinstance(contents, list):
|
|
648
|
+
continue
|
|
649
|
+
for index, content in enumerate(contents):
|
|
650
|
+
if isinstance(content, str):
|
|
651
|
+
contents[index] = cls._rewrite_clipboard_mentions_in_text(
|
|
652
|
+
content,
|
|
653
|
+
parent_clipboard_root=parent_clipboard_root,
|
|
654
|
+
fork_clipboard_root=fork_clipboard_root,
|
|
655
|
+
)
|
|
656
|
+
continue
|
|
657
|
+
if not _is_string_keyed_dict(content) or content.get("type") != "text":
|
|
658
|
+
continue
|
|
659
|
+
text = content.get("text")
|
|
660
|
+
if isinstance(text, str):
|
|
661
|
+
content["text"] = cls._rewrite_clipboard_mentions_in_text(
|
|
662
|
+
text,
|
|
663
|
+
parent_clipboard_root=parent_clipboard_root,
|
|
664
|
+
fork_clipboard_root=fork_clipboard_root,
|
|
665
|
+
)
|
|
666
|
+
|
|
667
|
+
@staticmethod
|
|
668
|
+
def _rewrite_clipboard_mentions_in_text(
|
|
669
|
+
text: str,
|
|
670
|
+
*,
|
|
671
|
+
parent_clipboard_root: Path,
|
|
672
|
+
fork_clipboard_root: Path,
|
|
673
|
+
) -> str:
|
|
674
|
+
tokens = iter_mention_tokens(text)
|
|
675
|
+
if not tokens:
|
|
676
|
+
return text
|
|
677
|
+
|
|
678
|
+
pieces: list[str] = []
|
|
679
|
+
last = 0
|
|
680
|
+
changed = False
|
|
681
|
+
for token in tokens:
|
|
682
|
+
# Clipboard mentions written by the TUI are absolute paths. Relative
|
|
683
|
+
# hand-authored mentions resolve against the process cwd here and
|
|
684
|
+
# normally will not match the session-local clipboard directory.
|
|
685
|
+
resolved = SessionForkMixin._resolve_mention_path(token.value)
|
|
686
|
+
if resolved is None:
|
|
687
|
+
continue
|
|
688
|
+
try:
|
|
689
|
+
relative = resolved.relative_to(parent_clipboard_root)
|
|
690
|
+
except ValueError:
|
|
691
|
+
continue
|
|
692
|
+
pieces.append(text[last : token.start])
|
|
693
|
+
pieces.append(format_file_mention(fork_clipboard_root / relative))
|
|
694
|
+
last = token.end
|
|
695
|
+
changed = True
|
|
696
|
+
if not changed:
|
|
697
|
+
return text
|
|
698
|
+
pieces.append(text[last:])
|
|
699
|
+
return "".join(pieces)
|
|
700
|
+
|
|
701
|
+
@staticmethod
|
|
702
|
+
def _resolve_mention_path(value: str) -> Path | None:
|
|
703
|
+
try:
|
|
704
|
+
return Path(value).expanduser().resolve(strict=False)
|
|
705
|
+
except OSError:
|
|
706
|
+
return None
|
|
707
|
+
except RuntimeError:
|
|
708
|
+
return None
|
|
709
|
+
except ValueError:
|
|
710
|
+
return None
|