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,466 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""WorkspaceScanner — before/after directory comparison for shell commands.
|
|
4
|
+
|
|
5
|
+
Detects file changes by comparing directory state (mtime + size) before
|
|
6
|
+
and after shell command execution. Supports filtering via:
|
|
7
|
+
|
|
8
|
+
1. A built-in set of well-known directories to skip (node_modules, .git, ...)
|
|
9
|
+
2. Caller-supplied gitignore rules (via ``pathspec.GitIgnoreSpec``)
|
|
10
|
+
|
|
11
|
+
**Scalability**: full-tree scanning is O(n) in files. For large repos
|
|
12
|
+
(100K+ files), use :meth:`WorkspaceScanner.scan_paths` to scan only
|
|
13
|
+
specific files/directories identified by :class:`ShellMutationDetector`,
|
|
14
|
+
or set ``max_files`` to cap the walk.
|
|
15
|
+
|
|
16
|
+
Cross-platform: uses ``os.walk`` + ``os.lstat`` which work identically
|
|
17
|
+
on Windows, macOS, and Linux. Path separators are normalized internally.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import errno
|
|
23
|
+
import logging
|
|
24
|
+
import os
|
|
25
|
+
import stat
|
|
26
|
+
import time
|
|
27
|
+
from dataclasses import dataclass
|
|
28
|
+
from pathlib import PurePath
|
|
29
|
+
|
|
30
|
+
import pathspec
|
|
31
|
+
|
|
32
|
+
from chrys.service.mutations.types import MutationOp
|
|
33
|
+
|
|
34
|
+
logger = logging.getLogger(__name__)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _raise_scan_error(error: OSError) -> None:
|
|
38
|
+
"""Only a missing entry is absence; discard scans with unreadable coverage."""
|
|
39
|
+
if error.errno not in (errno.ENOENT, errno.ENOTDIR):
|
|
40
|
+
raise error
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
# Default exclude directories — well-known generated / VCS / dependency dirs
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
DEFAULT_EXCLUDES: frozenset[str] = frozenset(
|
|
48
|
+
{
|
|
49
|
+
# Version control
|
|
50
|
+
".git",
|
|
51
|
+
".svn",
|
|
52
|
+
".hg",
|
|
53
|
+
".bzr",
|
|
54
|
+
# JavaScript / Node
|
|
55
|
+
"node_modules",
|
|
56
|
+
"bower_components",
|
|
57
|
+
".next",
|
|
58
|
+
".nuxt",
|
|
59
|
+
".turbo",
|
|
60
|
+
".parcel-cache",
|
|
61
|
+
# Python
|
|
62
|
+
"__pycache__",
|
|
63
|
+
".venv",
|
|
64
|
+
"venv",
|
|
65
|
+
"env",
|
|
66
|
+
".eggs",
|
|
67
|
+
"*.egg-info", # handled via _matches_any_pattern
|
|
68
|
+
".tox",
|
|
69
|
+
".nox",
|
|
70
|
+
".pytest_cache",
|
|
71
|
+
".mypy_cache",
|
|
72
|
+
".ruff_cache",
|
|
73
|
+
".pytype",
|
|
74
|
+
"htmlcov",
|
|
75
|
+
".coverage",
|
|
76
|
+
# Rust / Java / .NET
|
|
77
|
+
"target",
|
|
78
|
+
".gradle",
|
|
79
|
+
".mvn",
|
|
80
|
+
"bin",
|
|
81
|
+
"obj",
|
|
82
|
+
# Go / PHP
|
|
83
|
+
"vendor",
|
|
84
|
+
# Ruby
|
|
85
|
+
".bundle",
|
|
86
|
+
# General build output
|
|
87
|
+
"build",
|
|
88
|
+
"dist",
|
|
89
|
+
"out",
|
|
90
|
+
# IDE / editor
|
|
91
|
+
".idea",
|
|
92
|
+
".vscode",
|
|
93
|
+
".vs",
|
|
94
|
+
".eclipse",
|
|
95
|
+
# Caches
|
|
96
|
+
".cache",
|
|
97
|
+
".sass-cache",
|
|
98
|
+
".eslintcache",
|
|
99
|
+
".stylelintcache",
|
|
100
|
+
# OS artifacts
|
|
101
|
+
".DS_Store",
|
|
102
|
+
"Thumbs.db",
|
|
103
|
+
# Containers / infra
|
|
104
|
+
".terraform",
|
|
105
|
+
".serverless",
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# ---------------------------------------------------------------------------
|
|
111
|
+
# Gitignore rule matcher (powered by pathspec)
|
|
112
|
+
# ---------------------------------------------------------------------------
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class GitignoreFilter:
|
|
116
|
+
"""Gitignore-style path filter using ``pathspec.GitIgnoreSpec``.
|
|
117
|
+
|
|
118
|
+
Supports the full gitignore specification including:
|
|
119
|
+
|
|
120
|
+
- ``*.ext`` -- match by extension
|
|
121
|
+
- ``dirname/`` -- match directories by name
|
|
122
|
+
- ``path/to/file`` -- match specific relative paths
|
|
123
|
+
- ``**/pattern`` -- match in any subdirectory
|
|
124
|
+
- ``a/**/b`` -- match across directory levels
|
|
125
|
+
- ``[charset]`` character ranges
|
|
126
|
+
- ``!pattern`` -- negate a previous rule
|
|
127
|
+
- ``#`` comments and blank lines are ignored
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
def __init__(self, rules: list[str] | None = None) -> None:
|
|
131
|
+
self._spec = pathspec.GitIgnoreSpec.from_lines(rules or [])
|
|
132
|
+
|
|
133
|
+
@classmethod
|
|
134
|
+
def from_file(cls, path: str) -> GitignoreFilter:
|
|
135
|
+
"""Load rules from a ``.gitignore`` file."""
|
|
136
|
+
try:
|
|
137
|
+
with open(path, encoding="utf-8", errors="replace") as f:
|
|
138
|
+
lines = f.read().splitlines()
|
|
139
|
+
except OSError:
|
|
140
|
+
return cls()
|
|
141
|
+
return cls(lines)
|
|
142
|
+
|
|
143
|
+
@classmethod
|
|
144
|
+
def from_text(cls, text: str) -> GitignoreFilter:
|
|
145
|
+
"""Load rules from a gitignore-formatted string."""
|
|
146
|
+
return cls(text.splitlines())
|
|
147
|
+
|
|
148
|
+
def is_ignored(self, rel_path: str, *, is_dir: bool = False) -> bool:
|
|
149
|
+
"""Test whether a relative path should be ignored.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
rel_path: Path relative to the scan root, using ``/`` separators.
|
|
153
|
+
is_dir: Whether the path is a directory.
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
True if the path matches an ignore rule (and is not negated).
|
|
157
|
+
"""
|
|
158
|
+
# ``pathspec`` expects POSIX separators; ``PurePath.as_posix()`` is
|
|
159
|
+
# platform-aware (converts ``\\`` on Windows, no-op on POSIX).
|
|
160
|
+
rel_path = PurePath(rel_path).as_posix().strip("/")
|
|
161
|
+
# pathspec needs trailing slash to identify directories
|
|
162
|
+
if is_dir:
|
|
163
|
+
rel_path = rel_path.rstrip("/") + "/"
|
|
164
|
+
return self._spec.match_file(rel_path)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
# ---------------------------------------------------------------------------
|
|
168
|
+
# File stat snapshot
|
|
169
|
+
# ---------------------------------------------------------------------------
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
@dataclass(frozen=True)
|
|
173
|
+
class FileStat:
|
|
174
|
+
"""Lightweight file stat for change detection."""
|
|
175
|
+
|
|
176
|
+
mtime_ns: int
|
|
177
|
+
size: int
|
|
178
|
+
mode: int = 0
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
# ---------------------------------------------------------------------------
|
|
182
|
+
# Workspace scanner
|
|
183
|
+
# ---------------------------------------------------------------------------
|
|
184
|
+
|
|
185
|
+
# Safety cap: if the tree walk encounters more files than this,
|
|
186
|
+
# stop early and log a warning. Prevents unbounded memory/time
|
|
187
|
+
# on giant monorepos. Callers should prefer scan_paths() for
|
|
188
|
+
# targeted scanning when the command's targets are known.
|
|
189
|
+
_DEFAULT_MAX_FILES = 50_000
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class WorkspaceScanner:
|
|
193
|
+
"""Detects file changes by comparing directory state before/after execution.
|
|
194
|
+
|
|
195
|
+
Two scanning modes:
|
|
196
|
+
|
|
197
|
+
1. **Full scan** (:meth:`scan`): walks the workspace tree. Best for
|
|
198
|
+
small-to-medium repos. Capped by ``max_files`` to avoid runaway
|
|
199
|
+
on giant trees.
|
|
200
|
+
|
|
201
|
+
2. **Targeted scan** (:meth:`scan_paths`): stats only specific files
|
|
202
|
+
or shallow directories. O(targets) regardless of repo size. Use
|
|
203
|
+
this when :class:`ShellMutationDetector` identifies the likely
|
|
204
|
+
targets.
|
|
205
|
+
|
|
206
|
+
Typical usage::
|
|
207
|
+
|
|
208
|
+
# Option A — targeted (preferred for large repos)
|
|
209
|
+
targets = ShellMutationDetector.detect_targets(command, cwd)
|
|
210
|
+
target_paths = [p for p, _ in targets]
|
|
211
|
+
before = scanner.scan_paths(target_paths)
|
|
212
|
+
# ... execute ...
|
|
213
|
+
after = scanner.scan_paths(target_paths)
|
|
214
|
+
|
|
215
|
+
# Option B — full workspace scan (small repos only)
|
|
216
|
+
before = scanner.scan()
|
|
217
|
+
# ... execute ...
|
|
218
|
+
after = scanner.scan()
|
|
219
|
+
|
|
220
|
+
changed = WorkspaceScanner.diff(before, after)
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
def __init__(
|
|
224
|
+
self,
|
|
225
|
+
root: str,
|
|
226
|
+
*,
|
|
227
|
+
excludes: frozenset[str] | None = None,
|
|
228
|
+
gitignore: GitignoreFilter | None = None,
|
|
229
|
+
max_depth: int = 10,
|
|
230
|
+
max_files: int = _DEFAULT_MAX_FILES,
|
|
231
|
+
exclude_roots: frozenset[str] | set[str] | None = None,
|
|
232
|
+
deadline: float | None = None,
|
|
233
|
+
) -> None:
|
|
234
|
+
"""
|
|
235
|
+
Args:
|
|
236
|
+
root: Workspace root directory to scan.
|
|
237
|
+
excludes: Directory / file names to skip. Defaults to
|
|
238
|
+
:data:`DEFAULT_EXCLUDES`.
|
|
239
|
+
gitignore: Optional gitignore filter for additional exclusion.
|
|
240
|
+
max_depth: Maximum directory depth to walk (0 = root only).
|
|
241
|
+
max_files: Safety cap on the number of files to stat. If
|
|
242
|
+
exceeded, the scan stops early and logs a warning.
|
|
243
|
+
Set to 0 for unlimited (not recommended for unknown repos).
|
|
244
|
+
"""
|
|
245
|
+
self._root = os.path.normpath(os.path.abspath(root))
|
|
246
|
+
self._excludes = excludes if excludes is not None else DEFAULT_EXCLUDES
|
|
247
|
+
self._gitignore = gitignore
|
|
248
|
+
self._max_depth = max_depth
|
|
249
|
+
self._max_files = max_files
|
|
250
|
+
self._exclude_roots = frozenset(
|
|
251
|
+
os.path.normcase(os.path.realpath(os.path.abspath(path))) for path in (exclude_roots or ())
|
|
252
|
+
)
|
|
253
|
+
self._deadline = deadline
|
|
254
|
+
|
|
255
|
+
def scan(self, *, since_ns: int = 0) -> dict[str, FileStat]:
|
|
256
|
+
"""Walk the workspace and return ``{abs_path: stat}`` for every file.
|
|
257
|
+
|
|
258
|
+
Skips directories in the exclude set, respects max_depth and
|
|
259
|
+
max_files, and applies gitignore filtering. Symlinks are not
|
|
260
|
+
followed to avoid cycles.
|
|
261
|
+
|
|
262
|
+
If the file count exceeds ``max_files``, the scan stops early
|
|
263
|
+
and a warning is logged. Use :meth:`scan_paths` for targeted
|
|
264
|
+
scanning when this happens.
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
since_ns: If nonzero, only include files whose ``mtime_ns``
|
|
268
|
+
is >= this value. Useful for "after" scans where only
|
|
269
|
+
recently-touched files matter. Files from the "before"
|
|
270
|
+
scan that were NOT modified will be absent from the
|
|
271
|
+
result — ``diff()`` treats these as unchanged (they
|
|
272
|
+
appear in both ``before`` and ``after`` as-is via the
|
|
273
|
+
``before`` dict passed to ``diff()``).
|
|
274
|
+
"""
|
|
275
|
+
result: dict[str, FileStat] = {}
|
|
276
|
+
root = self._root
|
|
277
|
+
root_len = len(root) + 1 # +1 for the separator
|
|
278
|
+
cap = self._max_files
|
|
279
|
+
|
|
280
|
+
for dirpath, dirnames, filenames in os.walk(root, followlinks=False, onerror=_raise_scan_error):
|
|
281
|
+
if self._deadline is not None and time.monotonic() >= self._deadline:
|
|
282
|
+
return result
|
|
283
|
+
# Enforce max depth
|
|
284
|
+
depth = dirpath[len(root) :].count(os.sep)
|
|
285
|
+
if depth >= self._max_depth:
|
|
286
|
+
dirnames.clear()
|
|
287
|
+
continue
|
|
288
|
+
|
|
289
|
+
# Prune excluded directories (in-place to prevent os.walk descent)
|
|
290
|
+
dirnames[:] = [
|
|
291
|
+
d
|
|
292
|
+
for d in dirnames
|
|
293
|
+
if not self._is_excluded_dir(d, dirpath[root_len:] + os.sep + d if dirpath != root else d)
|
|
294
|
+
and os.path.normcase(os.path.realpath(os.path.join(dirpath, d))) not in self._exclude_roots
|
|
295
|
+
]
|
|
296
|
+
|
|
297
|
+
for fname in filenames:
|
|
298
|
+
if self._deadline is not None and time.monotonic() >= self._deadline:
|
|
299
|
+
dirnames.clear()
|
|
300
|
+
return result
|
|
301
|
+
# Budget check
|
|
302
|
+
if cap and len(result) >= cap:
|
|
303
|
+
logger.warning(
|
|
304
|
+
"WorkspaceScanner: hit max_files cap (%d) — scan truncated. "
|
|
305
|
+
"Use scan_paths() for targeted scanning on large repos.",
|
|
306
|
+
cap,
|
|
307
|
+
)
|
|
308
|
+
# Stop the entire walk
|
|
309
|
+
dirnames.clear()
|
|
310
|
+
return result
|
|
311
|
+
|
|
312
|
+
fpath = os.path.join(dirpath, fname)
|
|
313
|
+
rel = fpath[root_len:] if len(fpath) > root_len else fname
|
|
314
|
+
|
|
315
|
+
# Check gitignore
|
|
316
|
+
if self._gitignore and self._gitignore.is_ignored(rel, is_dir=False):
|
|
317
|
+
continue
|
|
318
|
+
|
|
319
|
+
# Check filename against excludes (e.g. .DS_Store, Thumbs.db)
|
|
320
|
+
if fname in self._excludes:
|
|
321
|
+
continue
|
|
322
|
+
|
|
323
|
+
try:
|
|
324
|
+
st = os.lstat(fpath)
|
|
325
|
+
if not stat.S_ISREG(st.st_mode):
|
|
326
|
+
continue
|
|
327
|
+
if since_ns and st.st_mtime_ns < since_ns:
|
|
328
|
+
continue
|
|
329
|
+
result[fpath] = FileStat(
|
|
330
|
+
mtime_ns=st.st_mtime_ns,
|
|
331
|
+
size=st.st_size,
|
|
332
|
+
mode=stat.S_IMODE(st.st_mode),
|
|
333
|
+
)
|
|
334
|
+
except OSError as exc:
|
|
335
|
+
_raise_scan_error(exc)
|
|
336
|
+
|
|
337
|
+
return result
|
|
338
|
+
|
|
339
|
+
def scan_paths(self, paths: list[str], *, shallow_depth: int = 1) -> dict[str, FileStat]:
|
|
340
|
+
"""Stat specific files or shallow-scan specific directories.
|
|
341
|
+
|
|
342
|
+
Unlike :meth:`scan`, this does NOT walk the full workspace tree.
|
|
343
|
+
It only examines the given *paths*:
|
|
344
|
+
|
|
345
|
+
- If a path is a file, stat it directly.
|
|
346
|
+
- If a path is a directory, walk it up to *shallow_depth* levels.
|
|
347
|
+
- If a path doesn't exist, skip it (it may be a deletion target).
|
|
348
|
+
|
|
349
|
+
This is O(len(paths)) for files, making it safe for any repo size.
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
paths: Absolute or relative paths to scan. Relative paths
|
|
353
|
+
are resolved against the scanner's root.
|
|
354
|
+
shallow_depth: When a path is a directory, how many levels
|
|
355
|
+
deep to walk (default 1 = immediate children only).
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
``{abs_path: stat}`` for all discovered regular files.
|
|
359
|
+
"""
|
|
360
|
+
result: dict[str, FileStat] = {}
|
|
361
|
+
|
|
362
|
+
for raw_path in paths:
|
|
363
|
+
abs_path = os.path.normpath(raw_path if os.path.isabs(raw_path) else os.path.join(self._root, raw_path))
|
|
364
|
+
|
|
365
|
+
try:
|
|
366
|
+
entry = os.lstat(abs_path)
|
|
367
|
+
except ValueError:
|
|
368
|
+
# An embedded NUL is invalid, not an unreadable live endpoint.
|
|
369
|
+
continue
|
|
370
|
+
except OSError as exc:
|
|
371
|
+
_raise_scan_error(exc)
|
|
372
|
+
continue
|
|
373
|
+
if stat.S_ISLNK(entry.st_mode):
|
|
374
|
+
# Preserve explicit directory aliases as scan roots. A file
|
|
375
|
+
# link is still excluded by _stat_file; nested directory
|
|
376
|
+
# links remain excluded by walk(followlinks=False).
|
|
377
|
+
try:
|
|
378
|
+
entry = os.stat(abs_path)
|
|
379
|
+
except OSError as exc:
|
|
380
|
+
_raise_scan_error(exc)
|
|
381
|
+
continue
|
|
382
|
+
if stat.S_ISREG(entry.st_mode):
|
|
383
|
+
fs = self._stat_file(abs_path)
|
|
384
|
+
if fs is not None:
|
|
385
|
+
result[abs_path] = fs
|
|
386
|
+
|
|
387
|
+
elif stat.S_ISDIR(entry.st_mode):
|
|
388
|
+
for dirpath, dirnames, filenames in os.walk(abs_path, followlinks=False, onerror=_raise_scan_error):
|
|
389
|
+
depth = dirpath[len(abs_path) :].count(os.sep)
|
|
390
|
+
if depth >= shallow_depth:
|
|
391
|
+
dirnames.clear()
|
|
392
|
+
continue
|
|
393
|
+
# Still respect excludes within the shallow walk
|
|
394
|
+
dirnames[:] = [d for d in dirnames if d not in self._excludes]
|
|
395
|
+
for fname in filenames:
|
|
396
|
+
if fname in self._excludes:
|
|
397
|
+
continue
|
|
398
|
+
fpath = os.path.join(dirpath, fname)
|
|
399
|
+
fs = self._stat_file(fpath)
|
|
400
|
+
if fs is not None:
|
|
401
|
+
result[fpath] = fs
|
|
402
|
+
|
|
403
|
+
# else: path doesn't exist — skip (may be a deletion target)
|
|
404
|
+
|
|
405
|
+
return result
|
|
406
|
+
|
|
407
|
+
@staticmethod
|
|
408
|
+
def _stat_file(fpath: str) -> FileStat | None:
|
|
409
|
+
"""Stat a regular file; return None only for absent or excluded entries."""
|
|
410
|
+
try:
|
|
411
|
+
st = os.lstat(fpath)
|
|
412
|
+
if not stat.S_ISREG(st.st_mode):
|
|
413
|
+
return None
|
|
414
|
+
return FileStat(
|
|
415
|
+
mtime_ns=st.st_mtime_ns,
|
|
416
|
+
size=st.st_size,
|
|
417
|
+
mode=stat.S_IMODE(st.st_mode),
|
|
418
|
+
)
|
|
419
|
+
except OSError as exc:
|
|
420
|
+
_raise_scan_error(exc)
|
|
421
|
+
return None
|
|
422
|
+
|
|
423
|
+
def _is_excluded_dir(self, dirname: str, rel_path: str) -> bool:
|
|
424
|
+
"""Check if a directory should be skipped."""
|
|
425
|
+
# Exact name match (e.g. ".git", "node_modules")
|
|
426
|
+
if dirname in self._excludes:
|
|
427
|
+
return True
|
|
428
|
+
# Pattern match for entries like "*.egg-info"
|
|
429
|
+
if any(dirname.endswith(pat.removeprefix("*")) for pat in self._excludes if pat.startswith("*")):
|
|
430
|
+
return True
|
|
431
|
+
# Gitignore check — ``is_ignored`` normalises separators internally.
|
|
432
|
+
return bool(self._gitignore and self._gitignore.is_ignored(rel_path, is_dir=True))
|
|
433
|
+
|
|
434
|
+
@staticmethod
|
|
435
|
+
def diff(
|
|
436
|
+
before: dict[str, FileStat],
|
|
437
|
+
after: dict[str, FileStat],
|
|
438
|
+
) -> list[tuple[str, MutationOp]]:
|
|
439
|
+
"""Compare two scan results and return detected changes.
|
|
440
|
+
|
|
441
|
+
Returns a list of ``(abs_path, operation)`` tuples:
|
|
442
|
+
|
|
443
|
+
- Files in *after* but not *before* -> ``CREATE``
|
|
444
|
+
- Files in *before* but not *after* -> ``DELETE``
|
|
445
|
+
- Files in both but with changed mtime or size -> ``MODIFY``
|
|
446
|
+
|
|
447
|
+
Note: MOVE is not directly detectable from stat comparison.
|
|
448
|
+
A move appears as ``DELETE`` + ``CREATE``. Higher-level logic
|
|
449
|
+
can correlate these using content hashes if needed.
|
|
450
|
+
"""
|
|
451
|
+
changes: list[tuple[str, MutationOp]] = []
|
|
452
|
+
|
|
453
|
+
for path, after_stat in after.items():
|
|
454
|
+
before_stat = before.get(path)
|
|
455
|
+
if before_stat is None:
|
|
456
|
+
changes.append((path, MutationOp.CREATE))
|
|
457
|
+
elif (
|
|
458
|
+
before_stat.mtime_ns != after_stat.mtime_ns
|
|
459
|
+
or before_stat.size != after_stat.size
|
|
460
|
+
or before_stat.mode != after_stat.mode
|
|
461
|
+
):
|
|
462
|
+
changes.append((path, MutationOp.MODIFY))
|
|
463
|
+
|
|
464
|
+
changes.extend((path, MutationOp.DELETE) for path in before if path not in after)
|
|
465
|
+
|
|
466
|
+
return changes
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""Helpers for rollback snapshot files and turn prompt previews."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import logging
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
from chrys.foundation.models.history_markers import HistoryMarkerKind
|
|
13
|
+
from chrys.foundation.models.turns import is_continuation_message
|
|
14
|
+
from chrys.foundation.util.lock import FileLock
|
|
15
|
+
from chrys.service.state._session_files import (
|
|
16
|
+
SESSION_WRITE_LOCK_TIMEOUT_SECONDS,
|
|
17
|
+
atomic_copy_file,
|
|
18
|
+
parse_snapshot_turn,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from collections.abc import Iterable, Sequence
|
|
23
|
+
|
|
24
|
+
from chrys.kernel import Message
|
|
25
|
+
from chrys.service.context.providers.history import CompressedBlock
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def rollback_snapshot_paths(session_dir: Path | None) -> list[Path]:
|
|
31
|
+
"""Return rollback snapshot files sorted by their filename turn."""
|
|
32
|
+
if session_dir is None:
|
|
33
|
+
return []
|
|
34
|
+
snap_dir = session_dir / "snapshots"
|
|
35
|
+
if not snap_dir.is_dir():
|
|
36
|
+
return []
|
|
37
|
+
return sorted(
|
|
38
|
+
(p for p in snap_dir.glob("*.json") if parse_snapshot_turn(p) >= 1),
|
|
39
|
+
key=parse_snapshot_turn,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def rollback_snapshot_path(session_dir: Path | None, turn: int) -> Path | None:
|
|
44
|
+
"""Return the rollback snapshot path for *turn*."""
|
|
45
|
+
if session_dir is None:
|
|
46
|
+
return None
|
|
47
|
+
return session_dir / "snapshots" / f"turn_{turn}.json"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def prune_rollback_snapshots(session_dir: Path | None, keep: int) -> None:
|
|
51
|
+
"""Drop rollback snapshots older than the newest *keep* entries."""
|
|
52
|
+
if session_dir is None:
|
|
53
|
+
return
|
|
54
|
+
snap_dir = session_dir / "snapshots"
|
|
55
|
+
if not snap_dir.is_dir():
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
effective_keep = max(1, keep)
|
|
59
|
+
snaps = rollback_snapshot_paths(session_dir)
|
|
60
|
+
if len(snaps) <= effective_keep:
|
|
61
|
+
return
|
|
62
|
+
for old in snaps[:-effective_keep]:
|
|
63
|
+
try:
|
|
64
|
+
old.unlink()
|
|
65
|
+
except OSError:
|
|
66
|
+
logger.debug("Failed to prune rollback snapshot %s", old, exc_info=True)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def write_rollback_snapshot(
|
|
70
|
+
*,
|
|
71
|
+
session_dir: Path | None,
|
|
72
|
+
session_id: str | None,
|
|
73
|
+
turn_number: int,
|
|
74
|
+
keep: int,
|
|
75
|
+
lock_path: Path | None,
|
|
76
|
+
) -> None:
|
|
77
|
+
"""Atomically copy ``session.json`` to the current turn snapshot."""
|
|
78
|
+
if session_dir is None or session_id is None:
|
|
79
|
+
return
|
|
80
|
+
session_file = session_dir / "session.json"
|
|
81
|
+
if not session_file.exists():
|
|
82
|
+
return
|
|
83
|
+
snapshot_path = rollback_snapshot_path(session_dir, turn_number)
|
|
84
|
+
if snapshot_path is None or lock_path is None:
|
|
85
|
+
return
|
|
86
|
+
try:
|
|
87
|
+
with FileLock(lock_path, timeout=SESSION_WRITE_LOCK_TIMEOUT_SECONDS):
|
|
88
|
+
if not session_file.exists():
|
|
89
|
+
return
|
|
90
|
+
atomic_copy_file(session_file, snapshot_path)
|
|
91
|
+
except TimeoutError:
|
|
92
|
+
logger.warning("Timed out acquiring session lock for rollback snapshot %s", snapshot_path, exc_info=True)
|
|
93
|
+
return
|
|
94
|
+
except OSError:
|
|
95
|
+
logger.warning("Failed to write rollback snapshot %s", snapshot_path, exc_info=True)
|
|
96
|
+
return
|
|
97
|
+
prune_rollback_snapshots(session_dir, keep)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def snapshot_target_turns(session_dir: Path | None) -> set[int]:
|
|
101
|
+
"""Return rollback target turns implied by on-disk snapshots."""
|
|
102
|
+
turns: set[int] = set()
|
|
103
|
+
for path in rollback_snapshot_paths(session_dir):
|
|
104
|
+
turn = snapshot_target_turn(path)
|
|
105
|
+
if turn is not None and turn >= 1:
|
|
106
|
+
turns.add(turn)
|
|
107
|
+
return turns
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def rollback_snapshot_for_target(session_dir: Path | None, target_turn: int) -> Path | None:
|
|
111
|
+
"""Return the newest snapshot whose payload represents *target_turn*."""
|
|
112
|
+
matches = [path for path in rollback_snapshot_paths(session_dir) if snapshot_target_turn(path) == target_turn]
|
|
113
|
+
if not matches:
|
|
114
|
+
return None
|
|
115
|
+
return max(matches, key=parse_snapshot_turn)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def snapshot_target_turn(path: Path) -> int | None:
|
|
119
|
+
"""Return the turn count represented by a rollback snapshot.
|
|
120
|
+
|
|
121
|
+
Modern snapshots are taken at the start of turn ``N`` and therefore
|
|
122
|
+
contain ``state.turn_counter == N - 1``. Older snapshots were named
|
|
123
|
+
after the completed turn and contain ``state.turn_counter == N``.
|
|
124
|
+
Reading the payload first keeps both layouts usable. Malformed
|
|
125
|
+
prefixed snapshots fall back to the modern filename convention;
|
|
126
|
+
malformed bare numeric snapshots fall back to the legacy after-turn
|
|
127
|
+
convention (``1.json`` means turn 1).
|
|
128
|
+
"""
|
|
129
|
+
turn = _snapshot_payload_turn_counter(path)
|
|
130
|
+
if turn is not None:
|
|
131
|
+
return turn
|
|
132
|
+
snapshot_turn = parse_snapshot_turn(path)
|
|
133
|
+
if snapshot_turn >= 1:
|
|
134
|
+
return snapshot_turn if _is_legacy_numeric_snapshot_name(path) else snapshot_turn - 1
|
|
135
|
+
return None
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _is_legacy_numeric_snapshot_name(path: Path) -> bool:
|
|
139
|
+
stem = path.stem
|
|
140
|
+
return bool(stem) and stem.isdecimal()
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _snapshot_payload_turn_counter(path: Path) -> int | None:
|
|
144
|
+
try:
|
|
145
|
+
raw = json.loads(path.read_text(encoding="utf-8"))
|
|
146
|
+
except json.JSONDecodeError, UnicodeDecodeError, OSError:
|
|
147
|
+
return None
|
|
148
|
+
if not isinstance(raw, dict):
|
|
149
|
+
return None
|
|
150
|
+
state = raw.get("state")
|
|
151
|
+
if not isinstance(state, dict):
|
|
152
|
+
state = raw
|
|
153
|
+
counter = _nonnegative_int(state.get("turn_counter"))
|
|
154
|
+
if counter is not None:
|
|
155
|
+
if counter > 0 or not _state_has_messages(state):
|
|
156
|
+
return counter
|
|
157
|
+
return _max_marker_turn(state)
|
|
158
|
+
return _max_marker_turn(state)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _nonnegative_int(value: object) -> int | None:
|
|
162
|
+
if isinstance(value, bool):
|
|
163
|
+
return None
|
|
164
|
+
if isinstance(value, int):
|
|
165
|
+
return value if value >= 0 else None
|
|
166
|
+
if isinstance(value, str):
|
|
167
|
+
try:
|
|
168
|
+
parsed = int(value)
|
|
169
|
+
except ValueError:
|
|
170
|
+
return None
|
|
171
|
+
return parsed if parsed >= 0 else None
|
|
172
|
+
return None
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _state_has_messages(state: dict[str, object]) -> bool:
|
|
176
|
+
messages = state.get("messages")
|
|
177
|
+
return isinstance(messages, list) and bool(messages)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _max_marker_turn(state: dict[str, object]) -> int | None:
|
|
181
|
+
messages = state.get("messages")
|
|
182
|
+
if not isinstance(messages, list):
|
|
183
|
+
return None
|
|
184
|
+
max_turn: int | None = None
|
|
185
|
+
for message in messages:
|
|
186
|
+
if not isinstance(message, dict):
|
|
187
|
+
continue
|
|
188
|
+
additional = message.get("additional_properties")
|
|
189
|
+
if not isinstance(additional, dict):
|
|
190
|
+
continue
|
|
191
|
+
turn = _nonnegative_int(additional.get("_turn"))
|
|
192
|
+
if turn is not None and (max_turn is None or turn > max_turn):
|
|
193
|
+
max_turn = turn
|
|
194
|
+
return max_turn
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def scan_turn_prompts(messages: Sequence[Message], out: dict[int, str]) -> None:
|
|
198
|
+
"""Walk messages and fill ``out[turn] = first_user_prompt``.
|
|
199
|
+
|
|
200
|
+
Synthetic ``continue`` nudges are skipped — a crash-leftover nudge
|
|
201
|
+
would otherwise label a rollback preview "continue".
|
|
202
|
+
"""
|
|
203
|
+
pending: list[str] = []
|
|
204
|
+
for message in messages:
|
|
205
|
+
ap = message.additional_properties or {}
|
|
206
|
+
kind = ap.get(HistoryMarkerKind.KEY)
|
|
207
|
+
if kind == HistoryMarkerKind.TURN:
|
|
208
|
+
idx = ap.get("_turn")
|
|
209
|
+
if isinstance(idx, int) and pending and idx not in out:
|
|
210
|
+
out[idx] = pending[0]
|
|
211
|
+
pending = []
|
|
212
|
+
continue
|
|
213
|
+
if kind is not None:
|
|
214
|
+
continue
|
|
215
|
+
if message.role == "user" and not is_continuation_message(message):
|
|
216
|
+
text = (message.text or "").strip()
|
|
217
|
+
if text:
|
|
218
|
+
pending.append(text)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def turn_prompt_previews(
|
|
222
|
+
messages: Sequence[Message],
|
|
223
|
+
compressed_blocks: Iterable[CompressedBlock],
|
|
224
|
+
) -> dict[int, str]:
|
|
225
|
+
"""Return ``{turn_number: first_user_prompt}`` from live and compressed history."""
|
|
226
|
+
result: dict[int, str] = {}
|
|
227
|
+
try:
|
|
228
|
+
scan_turn_prompts(messages, result)
|
|
229
|
+
for block in compressed_blocks:
|
|
230
|
+
scan_turn_prompts(list(block.messages or []), result)
|
|
231
|
+
except Exception:
|
|
232
|
+
return {}
|
|
233
|
+
return result
|