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,1567 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""Textual view adapter for the main screen."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import contextlib
|
|
8
|
+
import inspect
|
|
9
|
+
import logging
|
|
10
|
+
from collections.abc import Awaitable, Callable, Sequence
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from typing import TYPE_CHECKING, Any, TypedDict, cast
|
|
13
|
+
|
|
14
|
+
from textual.css.query import NoMatches
|
|
15
|
+
from textual.widgets import Footer
|
|
16
|
+
|
|
17
|
+
from chrys.app.tui.buddy_messages import BUDDY_TITLE
|
|
18
|
+
from chrys.app.tui.clipboard import copy_text_to_clipboards
|
|
19
|
+
from chrys.app.tui.notifications import NotificationEvent
|
|
20
|
+
from chrys.app.tui.screens.main.dialog_controllers import (
|
|
21
|
+
AgentLoadDialogHandle,
|
|
22
|
+
AnswerQuestionDialogResult,
|
|
23
|
+
ApprovalBypassDecision,
|
|
24
|
+
ApprovalDialogHandle,
|
|
25
|
+
ImageCompressionDialogHandle,
|
|
26
|
+
InlineQuestionDialogResult,
|
|
27
|
+
QuestionDialogHandle,
|
|
28
|
+
QuestionDialogResult,
|
|
29
|
+
)
|
|
30
|
+
from chrys.app.tui.screens.main.ports import (
|
|
31
|
+
ConfirmMessage,
|
|
32
|
+
InputLabel,
|
|
33
|
+
InputRestore,
|
|
34
|
+
NotificationSeverity,
|
|
35
|
+
StatusMessage,
|
|
36
|
+
StatusTrail,
|
|
37
|
+
SuggestionEntry,
|
|
38
|
+
)
|
|
39
|
+
from chrys.app.tui.terminal.panel import ShellPanel
|
|
40
|
+
from chrys.app.tui.terminal.widget import Terminal
|
|
41
|
+
from chrys.app.tui.widgets import PromptDraft
|
|
42
|
+
from chrys.app.tui.widgets.chat.file_snapshot import FileSnapshotPayload
|
|
43
|
+
from chrys.app.tui.widgets.chat.panel import ChatPanel
|
|
44
|
+
from chrys.app.tui.widgets.chat.session_json import SessionJsonPanel
|
|
45
|
+
from chrys.app.tui.widgets.chrome.app_header import AppHeader
|
|
46
|
+
from chrys.app.tui.widgets.chrome.image_paste import clipboard_image_dir_for_session
|
|
47
|
+
from chrys.app.tui.widgets.chrome.input_bar import InputBar
|
|
48
|
+
from chrys.app.tui.widgets.chrome.status_bar import (
|
|
49
|
+
STATUS_AGENT_LOAD_FAILED,
|
|
50
|
+
STATUS_INTERACTIVE_MODE,
|
|
51
|
+
STATUS_INTERRUPTED,
|
|
52
|
+
STATUS_RETRYING,
|
|
53
|
+
STATUS_RUNNING_TOOL,
|
|
54
|
+
STATUS_SHELL_MODE,
|
|
55
|
+
StatusBar,
|
|
56
|
+
)
|
|
57
|
+
from chrys.app.tui.widgets.sidebar.context import ContextUsageState
|
|
58
|
+
from chrys.app.tui.widgets.sidebar.panel import SidebarPanel
|
|
59
|
+
from chrys.app.tui.widgets.sidebar.tasks import TodoListState
|
|
60
|
+
from chrys.app.tui.widgets.sidebar.toc import ConversationToc
|
|
61
|
+
from chrys.app.tui.widgets.trajectory import TrajectoryDashboard
|
|
62
|
+
from chrys.foundation.events.types import (
|
|
63
|
+
AgentRuntimeDetails,
|
|
64
|
+
ApprovalRequest,
|
|
65
|
+
ApprovalReviewed,
|
|
66
|
+
QuestionToUser,
|
|
67
|
+
)
|
|
68
|
+
from chrys.foundation.i18n.formatting import format_message
|
|
69
|
+
from chrys.foundation.models.ask_user import AskUserAnswer
|
|
70
|
+
from chrys.service.approval.policy import ApprovalMode
|
|
71
|
+
from chrys.service.profiles.models.schema import DEFAULT_MAX_CONTEXT_TOKENS
|
|
72
|
+
from chrys.service.trajectory.session import trajectory_events_path
|
|
73
|
+
|
|
74
|
+
if TYPE_CHECKING:
|
|
75
|
+
from textual.worker import Worker
|
|
76
|
+
|
|
77
|
+
from chrys.app.tui.app import ChrysApp
|
|
78
|
+
from chrys.app.tui.i18n import LocaleController
|
|
79
|
+
from chrys.app.tui.screens.diff import RollbackProgressModal
|
|
80
|
+
from chrys.app.tui.screens.diff.rollback_modal import RollbackModalState
|
|
81
|
+
from chrys.app.tui.screens.main.engine_read_model import RollbackReadState
|
|
82
|
+
from chrys.app.tui.screens.main.screen import MainScreen
|
|
83
|
+
from chrys.app.tui.screens.main.state import MainScreenState
|
|
84
|
+
from chrys.app.tui.util.diff_entries import DiffFileEntry, DiffLoadResult
|
|
85
|
+
from chrys.app.tui.widgets.chrome.commands import ManPageSpec
|
|
86
|
+
from chrys.foundation.events.types import ProvisionalPresentation
|
|
87
|
+
from chrys.foundation.models.session_surface import SessionSurface
|
|
88
|
+
from chrys.foundation.models.todos import TodoItem
|
|
89
|
+
from chrys.service.approval.judge import JudgeVerdict
|
|
90
|
+
from chrys.service.context.providers.history import CompressedBlock
|
|
91
|
+
from chrys.service.state.store import StateStore
|
|
92
|
+
|
|
93
|
+
logger = logging.getLogger(__name__)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _push_screen_untyped(app: Any, screen: object, callback: object | None = None) -> object:
|
|
97
|
+
"""Push adapter-typed screens or callbacks that Textual's push_screen overloads cannot model."""
|
|
98
|
+
return app.push_screen(screen) if callback is None else app.push_screen(screen, callback)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _judge_verdict(event: ApprovalReviewed) -> JudgeVerdict:
|
|
102
|
+
from chrys.service.approval.judge import JudgeVerdict
|
|
103
|
+
|
|
104
|
+
return JudgeVerdict(approved=event.approved, reason=event.reason)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class _StatusFlashKwargs(TypedDict, total=False):
|
|
108
|
+
"""Keyword subset forwarded only when requested by the presenter."""
|
|
109
|
+
|
|
110
|
+
error: bool
|
|
111
|
+
caution: bool
|
|
112
|
+
trail: StatusTrail
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class MainScreenViewAdapter:
|
|
116
|
+
"""Own Textual widget lookup and screen side effects for main-screen controllers."""
|
|
117
|
+
|
|
118
|
+
def __init__(
|
|
119
|
+
self,
|
|
120
|
+
screen: MainScreen,
|
|
121
|
+
*,
|
|
122
|
+
state: MainScreenState,
|
|
123
|
+
state_store: StateStore | None = None,
|
|
124
|
+
locale_controller: LocaleController | None = None,
|
|
125
|
+
) -> None:
|
|
126
|
+
self._screen = screen
|
|
127
|
+
self._state = state
|
|
128
|
+
self._state_store = state_store
|
|
129
|
+
self._locale_controller = locale_controller
|
|
130
|
+
self._rollback_progress_modal: RollbackProgressModal | None = None
|
|
131
|
+
self._input_restore_generation = 0
|
|
132
|
+
# The sessions browser's surface filter per mode (workflow mode or not), kept while the app runs.
|
|
133
|
+
self._session_surfaces: dict[bool, frozenset[SessionSurface]] = {}
|
|
134
|
+
# The status bar as shell mode found it, restored when shell mode exits.
|
|
135
|
+
self._shell_status_snapshot: dict = {}
|
|
136
|
+
|
|
137
|
+
# -------------------------------------------------------------- #
|
|
138
|
+
# Generic screen effects
|
|
139
|
+
# -------------------------------------------------------------- #
|
|
140
|
+
|
|
141
|
+
def debug(self, key: str, message: str = "") -> None:
|
|
142
|
+
self._screen._debug(key, message)
|
|
143
|
+
|
|
144
|
+
def notify(
|
|
145
|
+
self,
|
|
146
|
+
message: StatusMessage,
|
|
147
|
+
*,
|
|
148
|
+
title: StatusMessage,
|
|
149
|
+
severity: NotificationSeverity = "information",
|
|
150
|
+
timeout: float | None = 3,
|
|
151
|
+
) -> None:
|
|
152
|
+
self._screen.notify(
|
|
153
|
+
self._render_status_message(message),
|
|
154
|
+
title=self._render_status_message(title),
|
|
155
|
+
severity=severity,
|
|
156
|
+
timeout=timeout,
|
|
157
|
+
markup=False,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
def notify_event(self, event: NotificationEvent) -> None:
|
|
161
|
+
try:
|
|
162
|
+
cast("ChrysApp", self._screen.app).notification_service.notify(event)
|
|
163
|
+
except Exception:
|
|
164
|
+
logger.debug("Notification dispatch failed", exc_info=True)
|
|
165
|
+
|
|
166
|
+
def notify_buddy(
|
|
167
|
+
self,
|
|
168
|
+
message: StatusMessage,
|
|
169
|
+
*,
|
|
170
|
+
severity: NotificationSeverity = "information",
|
|
171
|
+
timeout: float = 10,
|
|
172
|
+
) -> None:
|
|
173
|
+
self._screen.notify(
|
|
174
|
+
self._render_status_message(message),
|
|
175
|
+
title=self._render_status_message(BUDDY_TITLE.bind()),
|
|
176
|
+
severity=severity,
|
|
177
|
+
timeout=timeout,
|
|
178
|
+
markup=False,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
def _render_status_message(self, message: StatusMessage) -> str:
|
|
182
|
+
if isinstance(message, str):
|
|
183
|
+
return message
|
|
184
|
+
controller = self._locale_controller
|
|
185
|
+
if controller is None:
|
|
186
|
+
return format_message(message)
|
|
187
|
+
from chrys.app.tui.i18n import render_str
|
|
188
|
+
|
|
189
|
+
return render_str(controller.localizer, message)
|
|
190
|
+
|
|
191
|
+
def refresh_buddy_panel(self, *, focus_tab: bool) -> None:
|
|
192
|
+
try:
|
|
193
|
+
sidebar = self._screen.query_one(SidebarPanel)
|
|
194
|
+
except NoMatches: # the screen is going away, or never had a sidebar
|
|
195
|
+
return
|
|
196
|
+
if focus_tab:
|
|
197
|
+
sidebar.focus_tab("tab-buddy")
|
|
198
|
+
sidebar.buddy_panel.reload()
|
|
199
|
+
|
|
200
|
+
def push_screen(self, screen: object, callback: object | None = None) -> object:
|
|
201
|
+
return _push_screen_untyped(self._screen.app, screen, callback)
|
|
202
|
+
|
|
203
|
+
def open_guide(self, locale_controller: LocaleController | None) -> None:
|
|
204
|
+
from chrys.app.tui.screens.guides import GuideDialog
|
|
205
|
+
|
|
206
|
+
self._screen.app.push_screen(GuideDialog(locale_controller=locale_controller))
|
|
207
|
+
|
|
208
|
+
def show_man_pages(self, pages: list[ManPageSpec], *, start_index: int = 0) -> None:
|
|
209
|
+
from chrys.app.tui.screens.dialogs.man_page import ManPageDialog
|
|
210
|
+
|
|
211
|
+
self._screen.app.push_screen(
|
|
212
|
+
ManPageDialog(
|
|
213
|
+
pages,
|
|
214
|
+
start_index=start_index,
|
|
215
|
+
locale_controller=self._locale_controller,
|
|
216
|
+
)
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
def open_runtime_details(self, details: AgentRuntimeDetails) -> None:
|
|
220
|
+
from chrys.app.tui.screens.dialogs.runtime_details import RuntimeDetailsDialog
|
|
221
|
+
|
|
222
|
+
self._screen.app.push_screen(RuntimeDetailsDialog(details))
|
|
223
|
+
|
|
224
|
+
def run_worker(self, awaitable: Awaitable[Any], *, group: str) -> None:
|
|
225
|
+
self._screen.run_worker(awaitable, exclusive=False, group=group)
|
|
226
|
+
|
|
227
|
+
def call_after_refresh(self, callback: Callable[[], None]) -> None:
|
|
228
|
+
self._screen.call_after_refresh(callback)
|
|
229
|
+
|
|
230
|
+
def stop_shell(self) -> None:
|
|
231
|
+
self._screen.query_one(ShellPanel).stop()
|
|
232
|
+
|
|
233
|
+
def exit_app(self) -> None:
|
|
234
|
+
self._screen.app.exit()
|
|
235
|
+
|
|
236
|
+
# -------------------------------------------------------------- #
|
|
237
|
+
# Input flow
|
|
238
|
+
# -------------------------------------------------------------- #
|
|
239
|
+
|
|
240
|
+
def set_terminal_title_for_user_message(self, text: str) -> None:
|
|
241
|
+
self._screen._session_title.set_terminal_title_for_user_message(text)
|
|
242
|
+
|
|
243
|
+
def clear_terminal_title_result(self) -> None:
|
|
244
|
+
self._screen._session_title.clear_terminal_title_result()
|
|
245
|
+
|
|
246
|
+
def set_terminal_title_for_cwd(self, cwd: str | None = None) -> None:
|
|
247
|
+
self._screen._session_title.set_terminal_title_for_cwd(cwd)
|
|
248
|
+
|
|
249
|
+
def current_chat_session_id(self) -> str:
|
|
250
|
+
return self._screen.query_one(ChatPanel).session_id
|
|
251
|
+
|
|
252
|
+
def current_session_id(self) -> str:
|
|
253
|
+
workflow = self._screen._workflow
|
|
254
|
+
return workflow.session_id if workflow.workflow_mode else self.current_chat_session_id()
|
|
255
|
+
|
|
256
|
+
def chat_session_id(self) -> str:
|
|
257
|
+
return self.current_chat_session_id()
|
|
258
|
+
|
|
259
|
+
def add_input_history(self, text: str, *, session_id: str | None) -> None:
|
|
260
|
+
self._screen.query_one(InputBar).add_to_history(text, session_id=session_id)
|
|
261
|
+
|
|
262
|
+
def set_retry_mode(self, enabled: bool, *, label: InputLabel = "Retry") -> None:
|
|
263
|
+
input_bar = self._screen.query_one(InputBar)
|
|
264
|
+
input_bar._retry_label = label
|
|
265
|
+
# retry_mode is always_update: assignment refreshes even when the
|
|
266
|
+
# flag is unchanged and only the label above differs.
|
|
267
|
+
input_bar.retry_mode = enabled
|
|
268
|
+
|
|
269
|
+
def set_input_retry_mode(self, value: bool) -> None:
|
|
270
|
+
self._screen.query_one(InputBar).retry_mode = value
|
|
271
|
+
|
|
272
|
+
def set_input_retry(self, label: InputLabel, value: bool = True) -> None:
|
|
273
|
+
self.set_retry_mode(value, label=label)
|
|
274
|
+
|
|
275
|
+
def start_run_status(self, label: StatusMessage) -> None:
|
|
276
|
+
status = self._screen.query_one(StatusBar)
|
|
277
|
+
status.start_run()
|
|
278
|
+
status.show(label)
|
|
279
|
+
|
|
280
|
+
async def render_user_message(self, text: str, *, created_at: datetime, contents: list[Any] | None) -> None:
|
|
281
|
+
await self._screen.query_one(ChatPanel).add_user_message(text, created_at=created_at, contents=contents)
|
|
282
|
+
|
|
283
|
+
async def render_user_retry_note(self, text: str, *, created_at: datetime) -> None:
|
|
284
|
+
await self._screen.query_one(ChatPanel).add_user_message(text, is_injection=True, created_at=created_at)
|
|
285
|
+
|
|
286
|
+
def hide_trailing_status_action(self) -> None:
|
|
287
|
+
self._screen.query_one(ChatPanel).hide_trailing_status_action()
|
|
288
|
+
|
|
289
|
+
def set_retry_pending(self, pending: bool) -> None:
|
|
290
|
+
self._screen.query_one(InputBar).retry_pending = pending
|
|
291
|
+
self._screen.query_one(ChatPanel).set_trailing_status_action_disabled(pending)
|
|
292
|
+
|
|
293
|
+
def clear_ask_user_inline_prompts(self) -> None:
|
|
294
|
+
self._screen.query_one(ChatPanel).clear_ask_user_inline_prompts()
|
|
295
|
+
|
|
296
|
+
def lock_input_with_text(self) -> None:
|
|
297
|
+
self._screen.query_one(InputBar).lock_with_text()
|
|
298
|
+
|
|
299
|
+
def unlock_input_keep_if_locked(self) -> None:
|
|
300
|
+
input_bar = self._screen.query_one(InputBar)
|
|
301
|
+
if input_bar.locked:
|
|
302
|
+
input_bar.unlock_and_keep()
|
|
303
|
+
|
|
304
|
+
def flash_interrupted(self) -> None:
|
|
305
|
+
self._screen.query_one(StatusBar).flash(STATUS_INTERRUPTED.bind(), caution=True)
|
|
306
|
+
|
|
307
|
+
async def render_interrupted(self) -> None:
|
|
308
|
+
await self._screen.query_one(ChatPanel).add_interrupted()
|
|
309
|
+
|
|
310
|
+
def update_toc(self) -> None:
|
|
311
|
+
with contextlib.suppress(Exception):
|
|
312
|
+
chat = self._screen.query_one(ChatPanel)
|
|
313
|
+
toc = self._screen.query_one(ConversationToc)
|
|
314
|
+
toc.update_items(chat.toc_items)
|
|
315
|
+
|
|
316
|
+
# -------------------------------------------------------------- #
|
|
317
|
+
# Status/header/chat presentation
|
|
318
|
+
# -------------------------------------------------------------- #
|
|
319
|
+
|
|
320
|
+
def set_tool_info(self, trail: StatusTrail) -> None:
|
|
321
|
+
self._screen.query_one(StatusBar).set_tool_info(trail)
|
|
322
|
+
|
|
323
|
+
def show_status(self, text: StatusMessage) -> None:
|
|
324
|
+
self._screen.query_one(StatusBar).show(text)
|
|
325
|
+
|
|
326
|
+
def clear_status(self) -> None:
|
|
327
|
+
self._screen.query_one(StatusBar).clear_status()
|
|
328
|
+
|
|
329
|
+
def flash_status(
|
|
330
|
+
self,
|
|
331
|
+
text: StatusMessage,
|
|
332
|
+
*,
|
|
333
|
+
error: bool = False,
|
|
334
|
+
caution: bool = False,
|
|
335
|
+
trail: StatusTrail | None = None,
|
|
336
|
+
) -> None:
|
|
337
|
+
kwargs = _StatusFlashKwargs()
|
|
338
|
+
if error:
|
|
339
|
+
kwargs["error"] = True
|
|
340
|
+
if caution:
|
|
341
|
+
kwargs["caution"] = True
|
|
342
|
+
if trail is not None:
|
|
343
|
+
kwargs["trail"] = trail
|
|
344
|
+
self._screen.query_one(StatusBar).flash(text, **kwargs)
|
|
345
|
+
|
|
346
|
+
def flash_turn_complete(self) -> None:
|
|
347
|
+
self._screen.query_one(StatusBar).flash_completed()
|
|
348
|
+
|
|
349
|
+
def mark_terminal_title_completed(self) -> None:
|
|
350
|
+
self._screen._session_title.mark_terminal_title_completed()
|
|
351
|
+
|
|
352
|
+
def mark_terminal_title_failed(self) -> None:
|
|
353
|
+
self._screen._session_title.mark_terminal_title_failed()
|
|
354
|
+
|
|
355
|
+
def start_tool_status(self, tool_name: str) -> None:
|
|
356
|
+
status = self._screen.query_one(StatusBar)
|
|
357
|
+
status.add_tool_call()
|
|
358
|
+
status.show(STATUS_RUNNING_TOOL.bind(tool_name=tool_name))
|
|
359
|
+
|
|
360
|
+
def set_chat_profile(self, profile: str) -> None:
|
|
361
|
+
self._screen.chat_profile_name = profile
|
|
362
|
+
self._screen.query_one(ChatPanel).set_profile(profile)
|
|
363
|
+
|
|
364
|
+
def set_chat_tool_kinds(self, tool_kinds: dict[str, str]) -> None:
|
|
365
|
+
self._screen.query_one(ChatPanel).set_tool_kinds(tool_kinds)
|
|
366
|
+
|
|
367
|
+
async def clear_chat(self) -> None:
|
|
368
|
+
await self._screen.query_one(ChatPanel).clear()
|
|
369
|
+
|
|
370
|
+
def set_chat_workspace_cwd(self, cwd: str) -> None:
|
|
371
|
+
self._screen.chat_workspace_cwd = cwd
|
|
372
|
+
self._screen.query_one(ChatPanel).set_workspace_cwd(cwd)
|
|
373
|
+
|
|
374
|
+
def update_welcome(self, *, profile: str = "", cwd: str = "") -> None:
|
|
375
|
+
self._screen.query_one(ChatPanel).update_welcome(profile=profile, cwd=cwd)
|
|
376
|
+
|
|
377
|
+
def set_chat_session_id(self, session_id: str) -> None:
|
|
378
|
+
dashboard = self._screen.query_one(TrajectoryDashboard)
|
|
379
|
+
if dashboard.foreground and session_id != self._screen.chat_session_id:
|
|
380
|
+
self.hide_trajectory_dashboard()
|
|
381
|
+
self._screen.chat_session_id = session_id
|
|
382
|
+
self._screen.query_one(ChatPanel).set_session_id(session_id)
|
|
383
|
+
|
|
384
|
+
def set_session_title_state(
|
|
385
|
+
self,
|
|
386
|
+
*,
|
|
387
|
+
custom: str | None = None,
|
|
388
|
+
generated: str | None = None,
|
|
389
|
+
fallback: str | None = None,
|
|
390
|
+
) -> None:
|
|
391
|
+
self._screen._session_title.set_session_title_state(custom=custom, generated=generated, fallback=fallback)
|
|
392
|
+
|
|
393
|
+
def reset_session_title_state(self) -> None:
|
|
394
|
+
self._screen._session_title.reset_session_title_state()
|
|
395
|
+
|
|
396
|
+
def session_custom_title(self) -> str:
|
|
397
|
+
return self._screen._session_title.custom_title
|
|
398
|
+
|
|
399
|
+
def set_context_usage_state(self, state: ContextUsageState | None) -> None:
|
|
400
|
+
self._screen.context_usage_state = state
|
|
401
|
+
|
|
402
|
+
@property
|
|
403
|
+
def context_usage_state(self) -> ContextUsageState | None:
|
|
404
|
+
return self._screen.context_usage_state
|
|
405
|
+
|
|
406
|
+
def set_todo_state(self, items: tuple[TodoItem, ...]) -> None:
|
|
407
|
+
self._screen.todo_state = TodoListState(items=tuple(items))
|
|
408
|
+
|
|
409
|
+
def clear_todos(self) -> None:
|
|
410
|
+
self._screen.todo_state = TodoListState()
|
|
411
|
+
|
|
412
|
+
def set_header_approval_mode(self, mode: ApprovalMode) -> None:
|
|
413
|
+
self._screen.header_approval_mode = mode
|
|
414
|
+
|
|
415
|
+
def set_status_profile(self, profile: str, *, description: str = "") -> None:
|
|
416
|
+
self._screen.query_one(StatusBar).set_profile(profile, description=description)
|
|
417
|
+
|
|
418
|
+
def set_input_clipboard_dir(self, session_id: str | None) -> None:
|
|
419
|
+
self._screen.query_one(InputBar).set_clipboard_image_dir(
|
|
420
|
+
clipboard_image_dir_for_session(self._state_store, session_id)
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
def set_input_paste_cwd(self, cwd: str) -> None:
|
|
424
|
+
self._screen.query_one(InputBar).set_paste_cwd(cwd)
|
|
425
|
+
|
|
426
|
+
def restore_input_text(self, text: str) -> InputRestore | None:
|
|
427
|
+
"""Hand a consumed prompt back to an empty composer.
|
|
428
|
+
|
|
429
|
+
Returns proof of that restore for a caller that has to rewrite the same
|
|
430
|
+
text afterwards, or ``None`` when nothing was written — the composer
|
|
431
|
+
already held a draft, which is the user's and never ours to replace.
|
|
432
|
+
|
|
433
|
+
Every attempt consumes a generation, declined ones included, so an
|
|
434
|
+
outstanding credential dies the moment anything else tries to restore.
|
|
435
|
+
"""
|
|
436
|
+
self._input_restore_generation += 1
|
|
437
|
+
input_bar = self._screen.query_one(InputBar)
|
|
438
|
+
if input_bar.locked:
|
|
439
|
+
input_bar.unlock_and_keep()
|
|
440
|
+
if not input_bar.restore_draft(text):
|
|
441
|
+
return None
|
|
442
|
+
return InputRestore(generation=self._input_restore_generation, text=text)
|
|
443
|
+
|
|
444
|
+
def rewrite_restored_input(self, restore: InputRestore | None, text: str) -> bool:
|
|
445
|
+
"""Rewrite the text *restore* put there, while that restore still stands.
|
|
446
|
+
|
|
447
|
+
Authority comes from the credential, never from what the composer reads:
|
|
448
|
+
it buys nothing once the restore never happened, or any later restore —
|
|
449
|
+
successful or declined — superseded it. Byte identity is then required
|
|
450
|
+
on top, so an edit made under the credential's own restore hands the
|
|
451
|
+
draft back to the user.
|
|
452
|
+
|
|
453
|
+
The credential is spent either way, so it can never be replayed.
|
|
454
|
+
"""
|
|
455
|
+
if restore is None or restore.generation != self._input_restore_generation:
|
|
456
|
+
return False
|
|
457
|
+
self._input_restore_generation += 1
|
|
458
|
+
input_bar = self._screen.query_one(InputBar)
|
|
459
|
+
if input_bar.value != restore.text:
|
|
460
|
+
return False
|
|
461
|
+
# Rewriting identical text would still reset the cursor and break an
|
|
462
|
+
# in-progress IME composition, so leave the composer alone.
|
|
463
|
+
if text != restore.text:
|
|
464
|
+
input_bar.value = text
|
|
465
|
+
return True
|
|
466
|
+
|
|
467
|
+
async def change_shell_directory(self, cwd: str) -> None:
|
|
468
|
+
await self._screen.query_one(ShellPanel).change_directory(cwd)
|
|
469
|
+
|
|
470
|
+
def reset_context_usage(self, max_context_tokens: int = 0) -> None:
|
|
471
|
+
self._screen.query_one(SidebarPanel).context_panel.reset(max_context_tokens)
|
|
472
|
+
|
|
473
|
+
def clear_compressed_blocks(self) -> None:
|
|
474
|
+
self._screen.query_one(SidebarPanel).context_panel.clear_blocks()
|
|
475
|
+
|
|
476
|
+
def restore_session_chrome(self, *, session_id: str, profile: str, cwd: str) -> None:
|
|
477
|
+
self.set_chat_session_id(session_id)
|
|
478
|
+
self.set_chat_profile(profile)
|
|
479
|
+
self.set_chat_workspace_cwd(cwd)
|
|
480
|
+
self.update_welcome(profile=profile, cwd=cwd)
|
|
481
|
+
|
|
482
|
+
# -------------------------------------------------------------- #
|
|
483
|
+
# Copy and fold actions
|
|
484
|
+
# -------------------------------------------------------------- #
|
|
485
|
+
|
|
486
|
+
def chat_copy_messages(self, target: str) -> list[tuple[str, str]]:
|
|
487
|
+
panel = self._screen.query_one(ChatPanel)
|
|
488
|
+
if target == "all":
|
|
489
|
+
return list(panel.get_all_messages())
|
|
490
|
+
if target == "user":
|
|
491
|
+
return list(panel.get_user_messages())
|
|
492
|
+
return list(panel.get_agent_responses())
|
|
493
|
+
|
|
494
|
+
def copy_text(self, text: str) -> None:
|
|
495
|
+
copy_text_to_clipboards(self._screen.app, text)
|
|
496
|
+
|
|
497
|
+
def toggle_chat_fold_all(self) -> bool:
|
|
498
|
+
return self._screen.query_one(ChatPanel).toggle_fold_all()
|
|
499
|
+
|
|
500
|
+
# -------------------------------------------------------------- #
|
|
501
|
+
# Navigation and overlays
|
|
502
|
+
# -------------------------------------------------------------- #
|
|
503
|
+
|
|
504
|
+
def open_log_viewer(self) -> None:
|
|
505
|
+
from chrys.app.tui.screens.logs.screen import LogViewerScreen
|
|
506
|
+
|
|
507
|
+
self._screen.app.push_screen(LogViewerScreen())
|
|
508
|
+
|
|
509
|
+
def open_sessions_screen(self, state_store: StateStore, *, current_session_id: str, on_result: object) -> None:
|
|
510
|
+
from chrys.app.tui.screens.sessions import SessionsScreen
|
|
511
|
+
|
|
512
|
+
workflow_mode = self._screen._workflow.workflow_mode
|
|
513
|
+
|
|
514
|
+
def remember(surfaces: frozenset[SessionSurface]) -> None:
|
|
515
|
+
self._session_surfaces[workflow_mode] = surfaces
|
|
516
|
+
|
|
517
|
+
_push_screen_untyped(
|
|
518
|
+
self._screen.app,
|
|
519
|
+
SessionsScreen(
|
|
520
|
+
state_store,
|
|
521
|
+
current_session_id=current_session_id,
|
|
522
|
+
locale_controller=self._locale_controller,
|
|
523
|
+
workflow_mode=workflow_mode,
|
|
524
|
+
surfaces=self._session_surfaces.get(workflow_mode),
|
|
525
|
+
on_surfaces_changed=remember,
|
|
526
|
+
),
|
|
527
|
+
on_result,
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
async def restore_workflow_session(self, session_id: str) -> None:
|
|
531
|
+
if self._screen._workflow.workflow_mode:
|
|
532
|
+
await self._screen._workflow.session_view.restore_session(session_id)
|
|
533
|
+
|
|
534
|
+
def toggle_sidebar(self) -> None:
|
|
535
|
+
self._screen.query_one(SidebarPanel).toggle()
|
|
536
|
+
|
|
537
|
+
def open_theme_picker(self, on_result: object) -> None:
|
|
538
|
+
from chrys.app.tui.screens.themes import ThemesScreen
|
|
539
|
+
|
|
540
|
+
_push_screen_untyped(self._screen.app, ThemesScreen(self._screen.app.theme), on_result)
|
|
541
|
+
|
|
542
|
+
def apply_theme_setting(self, name: str) -> bool:
|
|
543
|
+
return cast("ChrysApp", self._screen.app).apply_theme_setting(name)
|
|
544
|
+
|
|
545
|
+
def open_language_picker(self, current_locale: str, on_result: object) -> None:
|
|
546
|
+
from chrys.app.tui.screens.menus.languages import LanguagesScreen
|
|
547
|
+
|
|
548
|
+
_push_screen_untyped(self._screen.app, LanguagesScreen(current_locale), on_result)
|
|
549
|
+
|
|
550
|
+
def dashboard_visible(self) -> bool:
|
|
551
|
+
return self._screen.query_one(TrajectoryDashboard).foreground
|
|
552
|
+
|
|
553
|
+
def show_trajectory_dashboard(self) -> None:
|
|
554
|
+
session_id = self.current_chat_session_id()
|
|
555
|
+
path = (
|
|
556
|
+
trajectory_events_path(self._state_store.session_dir(session_id))
|
|
557
|
+
if session_id and self._state_store is not None
|
|
558
|
+
else None
|
|
559
|
+
)
|
|
560
|
+
self._screen.query_one(TrajectoryDashboard).show_session(session_id, path)
|
|
561
|
+
self.sync_main_surface()
|
|
562
|
+
|
|
563
|
+
def hide_trajectory_dashboard(self) -> None:
|
|
564
|
+
self._screen.query_one(TrajectoryDashboard).hide_dashboard()
|
|
565
|
+
self.sync_main_surface()
|
|
566
|
+
self._screen.query_one(InputBar).focus_input()
|
|
567
|
+
|
|
568
|
+
def select_trajectory_turn(self, turn_id: str) -> None:
|
|
569
|
+
dashboard = self._screen.query_one(TrajectoryDashboard)
|
|
570
|
+
dashboard.select_turn(turn_id)
|
|
571
|
+
self.sync_main_surface()
|
|
572
|
+
|
|
573
|
+
def sync_main_surface(self) -> None:
|
|
574
|
+
dashboard = self._screen.query_one(TrajectoryDashboard)
|
|
575
|
+
shell = self._state.shell.active
|
|
576
|
+
workflow = self._screen._workflow.workflow_mode
|
|
577
|
+
dashboard.display = dashboard.foreground and not shell
|
|
578
|
+
self._screen._workflow_panel.display = workflow and not dashboard.foreground and not shell
|
|
579
|
+
chat = not workflow and not dashboard.foreground and not shell
|
|
580
|
+
self._screen.query_one(ChatPanel).display = chat
|
|
581
|
+
self._screen.query_one(StatusBar).display = shell or chat
|
|
582
|
+
self._screen.query_one(InputBar).display = chat
|
|
583
|
+
self._screen.query_one(Footer).display = not shell
|
|
584
|
+
self._screen._sync_workflow_timer()
|
|
585
|
+
|
|
586
|
+
def open_confirm_dialog(
|
|
587
|
+
self,
|
|
588
|
+
*,
|
|
589
|
+
title: StatusMessage,
|
|
590
|
+
message: ConfirmMessage,
|
|
591
|
+
confirm_label: StatusMessage,
|
|
592
|
+
confirm_variant: str,
|
|
593
|
+
on_result: object,
|
|
594
|
+
) -> None:
|
|
595
|
+
from chrys.app.tui.screens.dialogs.confirm import ConfirmDialog
|
|
596
|
+
|
|
597
|
+
_push_screen_untyped(
|
|
598
|
+
self._screen.app,
|
|
599
|
+
ConfirmDialog(
|
|
600
|
+
title=title,
|
|
601
|
+
message=message,
|
|
602
|
+
confirm_label=confirm_label,
|
|
603
|
+
confirm_variant=confirm_variant,
|
|
604
|
+
locale_controller=self._locale_controller,
|
|
605
|
+
),
|
|
606
|
+
on_result,
|
|
607
|
+
)
|
|
608
|
+
|
|
609
|
+
def dismiss_interrupt_confirm(self) -> None:
|
|
610
|
+
from chrys.app.tui.screens.dialogs.confirm import ConfirmDialog
|
|
611
|
+
|
|
612
|
+
if isinstance(self._screen.app.screen, ConfirmDialog):
|
|
613
|
+
self._screen.app.screen.dismiss(False)
|
|
614
|
+
|
|
615
|
+
def scroll_chat_to_turn(self, turn_id: str) -> None:
|
|
616
|
+
self._screen.query_one(ChatPanel).scroll_to_turn(turn_id)
|
|
617
|
+
|
|
618
|
+
# -------------------------------------------------------------- #
|
|
619
|
+
# Diff and rollback
|
|
620
|
+
# -------------------------------------------------------------- #
|
|
621
|
+
|
|
622
|
+
def open_diff_screen(
|
|
623
|
+
self,
|
|
624
|
+
periods_data: dict[int, list[DiffFileEntry]],
|
|
625
|
+
*,
|
|
626
|
+
cwd: str,
|
|
627
|
+
subtitle_parts: tuple[str, ...],
|
|
628
|
+
session_id: str,
|
|
629
|
+
all_entries: list[DiffFileEntry],
|
|
630
|
+
load_data: Callable[[], Awaitable[DiffLoadResult]] | None = None,
|
|
631
|
+
) -> None:
|
|
632
|
+
from chrys.app.tui.screens.diff import DiffScreen
|
|
633
|
+
|
|
634
|
+
self._screen.app.push_screen(
|
|
635
|
+
DiffScreen(
|
|
636
|
+
periods_data,
|
|
637
|
+
cwd=cwd,
|
|
638
|
+
subtitle_parts=subtitle_parts,
|
|
639
|
+
session_id=session_id,
|
|
640
|
+
all_entries=all_entries,
|
|
641
|
+
load_data=load_data,
|
|
642
|
+
locale_controller=self._locale_controller,
|
|
643
|
+
)
|
|
644
|
+
)
|
|
645
|
+
|
|
646
|
+
def open_rollback_modal(
|
|
647
|
+
self,
|
|
648
|
+
*,
|
|
649
|
+
cwd: str,
|
|
650
|
+
load_state: Callable[[], Awaitable[RollbackReadState | None]],
|
|
651
|
+
on_result: object,
|
|
652
|
+
) -> None:
|
|
653
|
+
from chrys.app.tui.screens.diff import RollbackModal
|
|
654
|
+
|
|
655
|
+
# RollbackReadState explicitly provides every RollbackModalState member.
|
|
656
|
+
modal_load_state = cast("Callable[[], Awaitable[RollbackModalState | None]]", load_state)
|
|
657
|
+
modal = RollbackModal(
|
|
658
|
+
tracker=None,
|
|
659
|
+
available_turns=[],
|
|
660
|
+
cwd=cwd,
|
|
661
|
+
load_state=modal_load_state,
|
|
662
|
+
locale_controller=self._locale_controller,
|
|
663
|
+
)
|
|
664
|
+
_push_screen_untyped(self._screen.app, modal, on_result)
|
|
665
|
+
|
|
666
|
+
def open_rollback_progress_modal(self, operation: Callable[[], Awaitable[None]]) -> None:
|
|
667
|
+
from chrys.app.tui.screens.diff import RollbackProgressModal
|
|
668
|
+
|
|
669
|
+
def _start_worker(awaitable: Awaitable[None]) -> object:
|
|
670
|
+
# A restore handoff can leave this operation finishing behind its
|
|
671
|
+
# dismissed modal. A later rollback must not cancel that tail.
|
|
672
|
+
return self._screen.run_worker(awaitable, exclusive=False, group="rollback-progress")
|
|
673
|
+
|
|
674
|
+
modal = RollbackProgressModal(
|
|
675
|
+
operation,
|
|
676
|
+
start_worker=_start_worker,
|
|
677
|
+
locale_controller=self._locale_controller,
|
|
678
|
+
)
|
|
679
|
+
self._rollback_progress_modal = modal
|
|
680
|
+
|
|
681
|
+
def _clear_modal(_result: None = None) -> None:
|
|
682
|
+
if self._rollback_progress_modal is modal:
|
|
683
|
+
self._rollback_progress_modal = None
|
|
684
|
+
|
|
685
|
+
self._screen.app.push_screen(modal, _clear_modal)
|
|
686
|
+
|
|
687
|
+
async def restore_welcome_rollback(self, *, session_id: str | None, profile: str, cwd: str) -> None:
|
|
688
|
+
self._screen.query_one(InputBar).retry_mode = False
|
|
689
|
+
panel = self._screen.query_one(ChatPanel)
|
|
690
|
+
await panel.clear()
|
|
691
|
+
# The rolled-back session's file is gone; stale title state would
|
|
692
|
+
# otherwise keep suppressing future auto-title updates (a custom
|
|
693
|
+
# title in UI state blocks them) and pin a stale terminal title.
|
|
694
|
+
self.reset_session_title_state()
|
|
695
|
+
if session_id:
|
|
696
|
+
self._screen.chat_session_id = session_id
|
|
697
|
+
panel.set_session_id(session_id)
|
|
698
|
+
self._screen.chat_workspace_cwd = cwd
|
|
699
|
+
max_context_tokens = (
|
|
700
|
+
self._screen.context_usage_state.max_context_tokens
|
|
701
|
+
if self._screen.context_usage_state
|
|
702
|
+
else DEFAULT_MAX_CONTEXT_TOKENS
|
|
703
|
+
)
|
|
704
|
+
self._screen.context_usage_state = ContextUsageState.with_window(
|
|
705
|
+
used_tokens=0,
|
|
706
|
+
max_context_tokens=max_context_tokens,
|
|
707
|
+
)
|
|
708
|
+
panel.set_workspace_cwd(cwd)
|
|
709
|
+
panel.update_welcome(profile=profile, cwd=cwd)
|
|
710
|
+
self.update_toc()
|
|
711
|
+
self.clear_todos()
|
|
712
|
+
with contextlib.suppress(Exception):
|
|
713
|
+
self._screen.query_one(SidebarPanel).context_panel.reset()
|
|
714
|
+
|
|
715
|
+
# -------------------------------------------------------------- #
|
|
716
|
+
# Chat transcript rendering
|
|
717
|
+
# -------------------------------------------------------------- #
|
|
718
|
+
|
|
719
|
+
async def add_error(self, message: str, *, action_label: str | None = "Retry") -> None:
|
|
720
|
+
await self._screen.query_one(ChatPanel).add_error(message, action_label=action_label)
|
|
721
|
+
|
|
722
|
+
async def add_system(self, text: str, *, key: str | None = None) -> None:
|
|
723
|
+
await self._screen.query_one(ChatPanel).add_system(text, key=key)
|
|
724
|
+
|
|
725
|
+
async def update_system(self, key: str, text: str) -> None:
|
|
726
|
+
await self._screen.query_one(ChatPanel).update_system(key, text)
|
|
727
|
+
|
|
728
|
+
async def remove_system(self, key: str) -> None:
|
|
729
|
+
await self._screen.query_one(ChatPanel).remove_system(key)
|
|
730
|
+
|
|
731
|
+
async def replay_history(
|
|
732
|
+
self,
|
|
733
|
+
messages: list[dict[str, Any]],
|
|
734
|
+
*,
|
|
735
|
+
initial_profile: str,
|
|
736
|
+
file_snapshots: dict[str, list[FileSnapshotPayload]] | None,
|
|
737
|
+
compressed_blocks: dict[str, CompressedBlock] | None,
|
|
738
|
+
) -> None:
|
|
739
|
+
await self._screen.query_one(ChatPanel).replay_history(
|
|
740
|
+
messages,
|
|
741
|
+
initial_profile=initial_profile,
|
|
742
|
+
file_snapshots=file_snapshots,
|
|
743
|
+
compressed_blocks=compressed_blocks,
|
|
744
|
+
)
|
|
745
|
+
|
|
746
|
+
async def add_agent_message(
|
|
747
|
+
self,
|
|
748
|
+
text: str,
|
|
749
|
+
*,
|
|
750
|
+
is_final: bool,
|
|
751
|
+
is_intermediate: bool = False,
|
|
752
|
+
structured_output_completed: bool = False,
|
|
753
|
+
presentation: ProvisionalPresentation | None = None,
|
|
754
|
+
created_at: datetime | None = None,
|
|
755
|
+
) -> None:
|
|
756
|
+
await self._screen.query_one(ChatPanel).add_agent_message(
|
|
757
|
+
text,
|
|
758
|
+
is_final=is_final,
|
|
759
|
+
is_intermediate=is_intermediate,
|
|
760
|
+
structured_output_completed=structured_output_completed,
|
|
761
|
+
presentation=presentation,
|
|
762
|
+
created_at=created_at,
|
|
763
|
+
)
|
|
764
|
+
|
|
765
|
+
async def accept_presentation_attempt(self, attempt_id: str, segment_ids: tuple[str, ...]) -> None:
|
|
766
|
+
await self._screen.query_one(ChatPanel).accept_presentation_attempt(attempt_id, segment_ids)
|
|
767
|
+
|
|
768
|
+
async def reject_presentation_attempt(self, attempt_id: str) -> None:
|
|
769
|
+
await self._screen.query_one(ChatPanel).reject_presentation_attempt(attempt_id)
|
|
770
|
+
|
|
771
|
+
async def add_user_injection(self, text: str, *, created_at: datetime) -> None:
|
|
772
|
+
await self._screen.query_one(ChatPanel).add_user_message(text, is_injection=True, created_at=created_at)
|
|
773
|
+
|
|
774
|
+
def unlock_input_after_consumed_injection(self) -> None:
|
|
775
|
+
self._screen.query_one(InputBar).unlock_and_clear()
|
|
776
|
+
|
|
777
|
+
def unlock_input_after_abandoned_injection(self) -> None:
|
|
778
|
+
self._screen.query_one(InputBar).unlock_and_keep()
|
|
779
|
+
|
|
780
|
+
async def add_tool_start(
|
|
781
|
+
self,
|
|
782
|
+
call_id: str,
|
|
783
|
+
tool_name: str,
|
|
784
|
+
tool_kind: str,
|
|
785
|
+
args_json: str,
|
|
786
|
+
*,
|
|
787
|
+
args: dict[str, Any] | None,
|
|
788
|
+
provider_hosted: bool = False,
|
|
789
|
+
hosted_family: str = "",
|
|
790
|
+
provider: str = "",
|
|
791
|
+
provider_item_type: str = "",
|
|
792
|
+
provider_status: str = "",
|
|
793
|
+
provider_call_id: str = "",
|
|
794
|
+
canonical_status: str = "running",
|
|
795
|
+
) -> None:
|
|
796
|
+
await self._screen.query_one(ChatPanel).add_tool_start(
|
|
797
|
+
call_id,
|
|
798
|
+
tool_name,
|
|
799
|
+
tool_kind,
|
|
800
|
+
args_json,
|
|
801
|
+
args=args,
|
|
802
|
+
provider_hosted=provider_hosted,
|
|
803
|
+
hosted_family=hosted_family,
|
|
804
|
+
provider=provider,
|
|
805
|
+
provider_item_type=provider_item_type,
|
|
806
|
+
provider_status=provider_status,
|
|
807
|
+
provider_call_id=provider_call_id,
|
|
808
|
+
canonical_status=canonical_status,
|
|
809
|
+
)
|
|
810
|
+
|
|
811
|
+
def update_tool_progress(
|
|
812
|
+
self,
|
|
813
|
+
call_id: str,
|
|
814
|
+
lines: list[str],
|
|
815
|
+
*,
|
|
816
|
+
image_contents: list[object] | None = None,
|
|
817
|
+
snapshot_metadata: dict[str, Any] | None = None,
|
|
818
|
+
provider_status: str = "",
|
|
819
|
+
) -> None:
|
|
820
|
+
self._screen.query_one(ChatPanel).update_tool_progress(
|
|
821
|
+
call_id,
|
|
822
|
+
lines,
|
|
823
|
+
image_contents=image_contents,
|
|
824
|
+
snapshot_metadata=snapshot_metadata,
|
|
825
|
+
provider_status=provider_status,
|
|
826
|
+
)
|
|
827
|
+
|
|
828
|
+
def update_tool_args(self, call_id: str, args: dict[str, Any]) -> None:
|
|
829
|
+
with contextlib.suppress(Exception):
|
|
830
|
+
self._screen.query_one(ChatPanel).update_tool_args(call_id, args)
|
|
831
|
+
|
|
832
|
+
def update_tool_status(
|
|
833
|
+
self,
|
|
834
|
+
call_id: str,
|
|
835
|
+
status: str,
|
|
836
|
+
*,
|
|
837
|
+
provider_status: str = "",
|
|
838
|
+
metadata: dict[str, Any] | None = None,
|
|
839
|
+
) -> None:
|
|
840
|
+
self._screen.query_one(ChatPanel).update_tool_status(
|
|
841
|
+
call_id,
|
|
842
|
+
status,
|
|
843
|
+
provider_status=provider_status,
|
|
844
|
+
metadata=metadata,
|
|
845
|
+
)
|
|
846
|
+
|
|
847
|
+
async def add_tool_result(
|
|
848
|
+
self,
|
|
849
|
+
call_id: str,
|
|
850
|
+
tool_name: str,
|
|
851
|
+
result: str,
|
|
852
|
+
duration_ms: int,
|
|
853
|
+
*,
|
|
854
|
+
image_contents: list[object] | None,
|
|
855
|
+
file_snapshot: FileSnapshotPayload | None,
|
|
856
|
+
approval: Any,
|
|
857
|
+
metadata: dict[str, Any],
|
|
858
|
+
artifacts: list[dict[str, Any]] | None = None,
|
|
859
|
+
provider_status: str = "",
|
|
860
|
+
canonical_status: str = "completed",
|
|
861
|
+
) -> None:
|
|
862
|
+
await self._screen.query_one(ChatPanel).add_tool_result(
|
|
863
|
+
call_id,
|
|
864
|
+
tool_name,
|
|
865
|
+
result,
|
|
866
|
+
duration_ms,
|
|
867
|
+
image_contents=image_contents,
|
|
868
|
+
file_snapshot=file_snapshot,
|
|
869
|
+
approval=approval,
|
|
870
|
+
metadata=metadata,
|
|
871
|
+
artifacts=artifacts,
|
|
872
|
+
provider_status=provider_status,
|
|
873
|
+
canonical_status=canonical_status,
|
|
874
|
+
)
|
|
875
|
+
|
|
876
|
+
async def add_context_fold(
|
|
877
|
+
self, context_id: str, summary: str, freed_messages: int, turn_range: tuple[int, int]
|
|
878
|
+
) -> bool:
|
|
879
|
+
panel = self._screen.query_one(ChatPanel)
|
|
880
|
+
await panel.add_context_fold(context_id, summary, freed_messages, turn_range)
|
|
881
|
+
return panel.mark_turn_range_compressed(turn_range)
|
|
882
|
+
|
|
883
|
+
def add_compressed_block(
|
|
884
|
+
self,
|
|
885
|
+
context_id: str,
|
|
886
|
+
summary: str,
|
|
887
|
+
freed_messages: int = 0,
|
|
888
|
+
turn_range: tuple[int, int] = (0, 0),
|
|
889
|
+
) -> None:
|
|
890
|
+
context_panel = self._screen.query_one(SidebarPanel).context_panel
|
|
891
|
+
context_panel.add_compressed_block(context_id, summary, freed_messages, turn_range)
|
|
892
|
+
|
|
893
|
+
async def add_compaction_start(self, compaction_id: str) -> None:
|
|
894
|
+
await self._screen.query_one(ChatPanel).add_compaction_start(compaction_id)
|
|
895
|
+
|
|
896
|
+
def complete_compaction(
|
|
897
|
+
self,
|
|
898
|
+
compaction_id: str,
|
|
899
|
+
*,
|
|
900
|
+
outcome: str,
|
|
901
|
+
duration_ms: int = 0,
|
|
902
|
+
last_words: str = "",
|
|
903
|
+
format_violation: str = "",
|
|
904
|
+
failure_reason: str = "",
|
|
905
|
+
) -> None:
|
|
906
|
+
self._screen.query_one(ChatPanel).complete_compaction(
|
|
907
|
+
compaction_id,
|
|
908
|
+
outcome=outcome,
|
|
909
|
+
duration_ms=duration_ms,
|
|
910
|
+
last_words=last_words,
|
|
911
|
+
format_violation=format_violation,
|
|
912
|
+
failure_reason=failure_reason,
|
|
913
|
+
)
|
|
914
|
+
|
|
915
|
+
def show_compaction_retry(self, message: str, attempt: int, max_attempts: int, delay_seconds: int) -> None:
|
|
916
|
+
self._screen.query_one(ChatPanel).show_compaction_retry(message, attempt, max_attempts, delay_seconds)
|
|
917
|
+
|
|
918
|
+
# -------------------------------------------------------------- #
|
|
919
|
+
# Sub-agent transcript rendering
|
|
920
|
+
# -------------------------------------------------------------- #
|
|
921
|
+
|
|
922
|
+
def link_sub_agent_invocation(
|
|
923
|
+
self,
|
|
924
|
+
parent_call_id: str,
|
|
925
|
+
invocation_id: str,
|
|
926
|
+
agent_name: str,
|
|
927
|
+
sub_agent_log_file: str = "",
|
|
928
|
+
*,
|
|
929
|
+
tool_name: str = "",
|
|
930
|
+
) -> None:
|
|
931
|
+
self._screen.query_one(ChatPanel).link_sub_agent_invocation(
|
|
932
|
+
parent_call_id,
|
|
933
|
+
invocation_id,
|
|
934
|
+
agent_name,
|
|
935
|
+
sub_agent_log_file,
|
|
936
|
+
tool_name=tool_name,
|
|
937
|
+
)
|
|
938
|
+
|
|
939
|
+
def add_sub_agent_message(
|
|
940
|
+
self,
|
|
941
|
+
agent_name: str,
|
|
942
|
+
invocation_id: str,
|
|
943
|
+
text: str,
|
|
944
|
+
*,
|
|
945
|
+
presentation: ProvisionalPresentation | None = None,
|
|
946
|
+
) -> None:
|
|
947
|
+
self._screen.query_one(ChatPanel).add_sub_agent_message(
|
|
948
|
+
agent_name,
|
|
949
|
+
invocation_id,
|
|
950
|
+
text,
|
|
951
|
+
presentation=presentation,
|
|
952
|
+
)
|
|
953
|
+
|
|
954
|
+
def accept_sub_agent_presentation(
|
|
955
|
+
self,
|
|
956
|
+
invocation_id: str,
|
|
957
|
+
attempt_id: str,
|
|
958
|
+
segment_ids: tuple[str, ...],
|
|
959
|
+
) -> None:
|
|
960
|
+
self._screen.query_one(ChatPanel).accept_sub_agent_presentation(
|
|
961
|
+
invocation_id,
|
|
962
|
+
attempt_id,
|
|
963
|
+
segment_ids,
|
|
964
|
+
)
|
|
965
|
+
|
|
966
|
+
def reject_sub_agent_presentation(self, invocation_id: str, attempt_id: str) -> None:
|
|
967
|
+
self._screen.query_one(ChatPanel).reject_sub_agent_presentation(invocation_id, attempt_id)
|
|
968
|
+
|
|
969
|
+
async def add_sub_agent_tool_start(
|
|
970
|
+
self,
|
|
971
|
+
agent_name: str,
|
|
972
|
+
invocation_id: str,
|
|
973
|
+
tool_name: str,
|
|
974
|
+
args: dict[str, Any],
|
|
975
|
+
call_id: str,
|
|
976
|
+
*,
|
|
977
|
+
tool_kind: str,
|
|
978
|
+
provider_hosted: bool = False,
|
|
979
|
+
hosted_family: str = "",
|
|
980
|
+
provider: str = "",
|
|
981
|
+
provider_item_type: str = "",
|
|
982
|
+
provider_status: str = "",
|
|
983
|
+
provider_call_id: str = "",
|
|
984
|
+
) -> None:
|
|
985
|
+
await self._screen.query_one(ChatPanel).add_sub_agent_tool_start(
|
|
986
|
+
agent_name,
|
|
987
|
+
invocation_id,
|
|
988
|
+
tool_name,
|
|
989
|
+
args,
|
|
990
|
+
call_id,
|
|
991
|
+
tool_kind=tool_kind,
|
|
992
|
+
provider_hosted=provider_hosted,
|
|
993
|
+
hosted_family=hosted_family,
|
|
994
|
+
provider=provider,
|
|
995
|
+
provider_item_type=provider_item_type,
|
|
996
|
+
provider_status=provider_status,
|
|
997
|
+
provider_call_id=provider_call_id,
|
|
998
|
+
)
|
|
999
|
+
|
|
1000
|
+
def update_sub_agent_tool_args(self, invocation_id: str, call_id: str, args: dict[str, Any]) -> None:
|
|
1001
|
+
self._screen.query_one(ChatPanel).update_sub_agent_tool_args(invocation_id, call_id, args)
|
|
1002
|
+
|
|
1003
|
+
def update_sub_agent_tool_status(
|
|
1004
|
+
self,
|
|
1005
|
+
invocation_id: str,
|
|
1006
|
+
call_id: str,
|
|
1007
|
+
status: str,
|
|
1008
|
+
*,
|
|
1009
|
+
metadata: dict[str, Any] | None = None,
|
|
1010
|
+
provider_status: str = "",
|
|
1011
|
+
) -> None:
|
|
1012
|
+
self._screen.query_one(ChatPanel).update_sub_agent_tool_status(
|
|
1013
|
+
invocation_id,
|
|
1014
|
+
call_id,
|
|
1015
|
+
status,
|
|
1016
|
+
metadata=metadata,
|
|
1017
|
+
provider_status=provider_status,
|
|
1018
|
+
)
|
|
1019
|
+
|
|
1020
|
+
def update_sub_agent_tool_progress(
|
|
1021
|
+
self,
|
|
1022
|
+
invocation_id: str,
|
|
1023
|
+
call_id: str,
|
|
1024
|
+
lines: list[str],
|
|
1025
|
+
*,
|
|
1026
|
+
image_contents: list[object] | None = None,
|
|
1027
|
+
snapshot_metadata: dict[str, Any] | None = None,
|
|
1028
|
+
provider_status: str = "",
|
|
1029
|
+
) -> None:
|
|
1030
|
+
self._screen.query_one(ChatPanel).update_sub_agent_tool_progress(
|
|
1031
|
+
invocation_id,
|
|
1032
|
+
call_id,
|
|
1033
|
+
lines,
|
|
1034
|
+
image_contents=image_contents,
|
|
1035
|
+
snapshot_metadata=snapshot_metadata,
|
|
1036
|
+
provider_status=provider_status,
|
|
1037
|
+
)
|
|
1038
|
+
|
|
1039
|
+
def complete_sub_agent_tool(
|
|
1040
|
+
self,
|
|
1041
|
+
agent_name: str,
|
|
1042
|
+
invocation_id: str,
|
|
1043
|
+
call_id: str,
|
|
1044
|
+
result: str,
|
|
1045
|
+
duration_ms: int,
|
|
1046
|
+
*,
|
|
1047
|
+
image_contents: list[object] | None,
|
|
1048
|
+
artifacts: list[dict[str, Any]] | None,
|
|
1049
|
+
approval: Any,
|
|
1050
|
+
metadata: dict[str, Any],
|
|
1051
|
+
provider_status: str = "",
|
|
1052
|
+
canonical_status: str = "completed",
|
|
1053
|
+
) -> None:
|
|
1054
|
+
self._screen.query_one(ChatPanel).complete_sub_agent_tool(
|
|
1055
|
+
agent_name,
|
|
1056
|
+
invocation_id,
|
|
1057
|
+
call_id,
|
|
1058
|
+
result,
|
|
1059
|
+
duration_ms,
|
|
1060
|
+
image_contents=image_contents,
|
|
1061
|
+
artifacts=artifacts,
|
|
1062
|
+
approval=approval,
|
|
1063
|
+
metadata=metadata,
|
|
1064
|
+
provider_status=provider_status,
|
|
1065
|
+
canonical_status=canonical_status,
|
|
1066
|
+
)
|
|
1067
|
+
|
|
1068
|
+
def update_sub_agent_progress(
|
|
1069
|
+
self,
|
|
1070
|
+
invocation_id: str,
|
|
1071
|
+
tool_call_count: int,
|
|
1072
|
+
total_tokens: int,
|
|
1073
|
+
total_usage_tokens: int,
|
|
1074
|
+
usage_unreported_attempts: int = 0,
|
|
1075
|
+
) -> None:
|
|
1076
|
+
self._screen.query_one(ChatPanel).update_sub_agent_progress(
|
|
1077
|
+
invocation_id,
|
|
1078
|
+
tool_call_count,
|
|
1079
|
+
total_tokens,
|
|
1080
|
+
total_usage_tokens,
|
|
1081
|
+
usage_unreported_attempts,
|
|
1082
|
+
)
|
|
1083
|
+
|
|
1084
|
+
def add_sub_agent_compaction_start(self, agent_name: str, invocation_id: str, compaction_id: str) -> None:
|
|
1085
|
+
self._screen.query_one(ChatPanel).add_sub_agent_compaction_start(agent_name, invocation_id, compaction_id)
|
|
1086
|
+
|
|
1087
|
+
def complete_sub_agent_compaction(
|
|
1088
|
+
self,
|
|
1089
|
+
invocation_id: str,
|
|
1090
|
+
compaction_id: str,
|
|
1091
|
+
*,
|
|
1092
|
+
outcome: str,
|
|
1093
|
+
duration_ms: int = 0,
|
|
1094
|
+
format_violation: str = "",
|
|
1095
|
+
failure_reason: str = "",
|
|
1096
|
+
) -> None:
|
|
1097
|
+
self._screen.query_one(ChatPanel).complete_sub_agent_compaction(
|
|
1098
|
+
invocation_id,
|
|
1099
|
+
compaction_id,
|
|
1100
|
+
outcome=outcome,
|
|
1101
|
+
duration_ms=duration_ms,
|
|
1102
|
+
format_violation=format_violation,
|
|
1103
|
+
failure_reason=failure_reason,
|
|
1104
|
+
)
|
|
1105
|
+
|
|
1106
|
+
def record_sub_agent_compaction_committed(self, invocation_id: str, compaction_id: str) -> None:
|
|
1107
|
+
self._screen.query_one(ChatPanel).record_sub_agent_compaction_committed(
|
|
1108
|
+
invocation_id,
|
|
1109
|
+
compaction_id,
|
|
1110
|
+
)
|
|
1111
|
+
|
|
1112
|
+
def sub_agent_retry_attempt(
|
|
1113
|
+
self,
|
|
1114
|
+
invocation_id: str,
|
|
1115
|
+
message: str,
|
|
1116
|
+
attempt: int,
|
|
1117
|
+
max_attempts: int,
|
|
1118
|
+
delay_seconds: int,
|
|
1119
|
+
) -> None:
|
|
1120
|
+
self._screen.query_one(ChatPanel).sub_agent_retry_attempt(
|
|
1121
|
+
invocation_id,
|
|
1122
|
+
message,
|
|
1123
|
+
attempt,
|
|
1124
|
+
max_attempts,
|
|
1125
|
+
delay_seconds,
|
|
1126
|
+
)
|
|
1127
|
+
|
|
1128
|
+
def sub_agent_paused(
|
|
1129
|
+
self,
|
|
1130
|
+
invocation_id: str,
|
|
1131
|
+
reason: str,
|
|
1132
|
+
last_error: str,
|
|
1133
|
+
retry_attempts: int,
|
|
1134
|
+
diagnostic_path: str | None = None,
|
|
1135
|
+
last_error_display: str | None = None,
|
|
1136
|
+
) -> None:
|
|
1137
|
+
self._screen.query_one(ChatPanel).sub_agent_paused(
|
|
1138
|
+
invocation_id,
|
|
1139
|
+
reason,
|
|
1140
|
+
last_error,
|
|
1141
|
+
retry_attempts,
|
|
1142
|
+
diagnostic_path,
|
|
1143
|
+
last_error_display,
|
|
1144
|
+
)
|
|
1145
|
+
|
|
1146
|
+
def sub_agent_resumed_after_pause(self, invocation_id: str) -> None:
|
|
1147
|
+
self._screen.query_one(ChatPanel).sub_agent_resumed_after_pause(invocation_id)
|
|
1148
|
+
|
|
1149
|
+
def sub_agent_cascade_aborted(self, invocation_id: str) -> None:
|
|
1150
|
+
self._screen.query_one(ChatPanel).sub_agent_cascade_aborted(invocation_id)
|
|
1151
|
+
|
|
1152
|
+
def sub_agent_aborted(self, invocation_id: str, last_error: str) -> None:
|
|
1153
|
+
self._screen.query_one(ChatPanel).sub_agent_aborted(invocation_id, last_error)
|
|
1154
|
+
|
|
1155
|
+
async def show_retry_attempt(
|
|
1156
|
+
self,
|
|
1157
|
+
message: str,
|
|
1158
|
+
attempt: int,
|
|
1159
|
+
max_attempts: int,
|
|
1160
|
+
delay_seconds: int,
|
|
1161
|
+
) -> None:
|
|
1162
|
+
panel = self._screen.query_one(ChatPanel)
|
|
1163
|
+
await panel.prepare_retry()
|
|
1164
|
+
await panel.add_retry(message, attempt, max_attempts, delay_seconds)
|
|
1165
|
+
self.show_status(STATUS_RETRYING.bind(attempt=attempt, max_attempts=max_attempts))
|
|
1166
|
+
|
|
1167
|
+
# -------------------------------------------------------------- #
|
|
1168
|
+
# Approval dialogs
|
|
1169
|
+
# -------------------------------------------------------------- #
|
|
1170
|
+
|
|
1171
|
+
async def build_approval_body(self, event: ApprovalRequest) -> object | None:
|
|
1172
|
+
from chrys.app.tui.screens.dialogs.approval import create_approval_body
|
|
1173
|
+
|
|
1174
|
+
return await create_approval_body(
|
|
1175
|
+
event.tool_name,
|
|
1176
|
+
event.tool_kind,
|
|
1177
|
+
event.args,
|
|
1178
|
+
workspace_cwd=event.workspace_cwd,
|
|
1179
|
+
)
|
|
1180
|
+
|
|
1181
|
+
def approval_body_bypass(self, body: object | None) -> ApprovalBypassDecision | None:
|
|
1182
|
+
from chrys.app.tui.screens.dialogs.approval import ApprovalBody
|
|
1183
|
+
|
|
1184
|
+
if body is None:
|
|
1185
|
+
return None
|
|
1186
|
+
if not isinstance(body, ApprovalBody):
|
|
1187
|
+
return None
|
|
1188
|
+
bypass = body.bypass
|
|
1189
|
+
if bypass is None:
|
|
1190
|
+
return None
|
|
1191
|
+
return ApprovalBypassDecision(
|
|
1192
|
+
approved=bypass.approved,
|
|
1193
|
+
reason=bypass.reason,
|
|
1194
|
+
debug_reason=bypass.debug_reason,
|
|
1195
|
+
)
|
|
1196
|
+
|
|
1197
|
+
def show_approval_dialog(
|
|
1198
|
+
self,
|
|
1199
|
+
event: ApprovalRequest,
|
|
1200
|
+
approval_body: object | None,
|
|
1201
|
+
on_result: Callable[[tuple[bool, str, dict[str, Any] | None] | None], None],
|
|
1202
|
+
*,
|
|
1203
|
+
verdict: ApprovalReviewed | None,
|
|
1204
|
+
) -> ApprovalDialogHandle:
|
|
1205
|
+
from chrys.app.tui.screens.dialogs.approval import ApprovalBody, ApprovalDialog
|
|
1206
|
+
|
|
1207
|
+
body = approval_body if isinstance(approval_body, ApprovalBody) else None
|
|
1208
|
+
dialog = ApprovalDialog(
|
|
1209
|
+
caller_name=event.caller_name,
|
|
1210
|
+
tool_name=event.tool_name,
|
|
1211
|
+
tool_kind=event.tool_kind,
|
|
1212
|
+
args=event.args,
|
|
1213
|
+
judging=event.judging,
|
|
1214
|
+
approval_body=body,
|
|
1215
|
+
presentation_kind=event.presentation_kind,
|
|
1216
|
+
reuse_offer=event.reuse_offer,
|
|
1217
|
+
verdict=_judge_verdict(verdict) if verdict is not None else None,
|
|
1218
|
+
)
|
|
1219
|
+
self._screen.app.push_screen(dialog, on_result)
|
|
1220
|
+
return dialog
|
|
1221
|
+
|
|
1222
|
+
def deliver_approval_verdict(self, dialog: ApprovalDialogHandle, event: ApprovalReviewed) -> None:
|
|
1223
|
+
from chrys.app.tui.screens.dialogs.approval import ApprovalDialog
|
|
1224
|
+
|
|
1225
|
+
if isinstance(dialog, ApprovalDialog):
|
|
1226
|
+
# The dialog keeps a verdict that beats its mount and shows it then.
|
|
1227
|
+
dialog.receive_verdict(_judge_verdict(event))
|
|
1228
|
+
|
|
1229
|
+
def dismiss_approval_dialog(self, dialog: ApprovalDialogHandle) -> None:
|
|
1230
|
+
"""Close a cancelled approval through its cancellation-only path."""
|
|
1231
|
+
from chrys.app.tui.screens.dialogs.approval import ApprovalDialog
|
|
1232
|
+
|
|
1233
|
+
if isinstance(dialog, ApprovalDialog):
|
|
1234
|
+
dialog.dismiss_due_to_cancellation()
|
|
1235
|
+
|
|
1236
|
+
def approval_dialog_tool_name(self, dialog: ApprovalDialogHandle) -> str:
|
|
1237
|
+
from chrys.app.tui.screens.dialogs.approval import ApprovalDialog
|
|
1238
|
+
|
|
1239
|
+
if isinstance(dialog, ApprovalDialog):
|
|
1240
|
+
return dialog.tool_name
|
|
1241
|
+
return ""
|
|
1242
|
+
|
|
1243
|
+
def set_auto_review_count(self, count: int) -> None:
|
|
1244
|
+
self._screen.query_one(AppHeader).set_auto_review_count(count)
|
|
1245
|
+
|
|
1246
|
+
def notify_approval_required(self) -> None:
|
|
1247
|
+
self.notify_event(NotificationEvent.APPROVAL_REQUIRED)
|
|
1248
|
+
|
|
1249
|
+
# -------------------------------------------------------------- #
|
|
1250
|
+
# Ask-user dialogs
|
|
1251
|
+
# -------------------------------------------------------------- #
|
|
1252
|
+
|
|
1253
|
+
def show_question_dialog(
|
|
1254
|
+
self,
|
|
1255
|
+
event: QuestionToUser,
|
|
1256
|
+
draft: PromptDraft | None,
|
|
1257
|
+
on_result: Callable[[object], None],
|
|
1258
|
+
) -> QuestionDialogHandle:
|
|
1259
|
+
from chrys.app.tui.screens.dialogs.ask_user import AskUserDialog
|
|
1260
|
+
|
|
1261
|
+
# Session identity determines whether the question belongs to the Chat transcript.
|
|
1262
|
+
dialog = AskUserDialog(
|
|
1263
|
+
request_id=event.request_id,
|
|
1264
|
+
questions=event.questions,
|
|
1265
|
+
caller_name=event.caller_name,
|
|
1266
|
+
draft=draft,
|
|
1267
|
+
allow_inline=bool(event.call_id)
|
|
1268
|
+
and (event.session_id is None or event.session_id == self.current_chat_session_id()),
|
|
1269
|
+
)
|
|
1270
|
+
self._screen.app.push_screen(dialog, on_result)
|
|
1271
|
+
return dialog
|
|
1272
|
+
|
|
1273
|
+
def parse_question_dialog_result(self, result: object) -> QuestionDialogResult:
|
|
1274
|
+
from chrys.app.tui.screens.dialogs.ask_user import AskUserInlineResult
|
|
1275
|
+
|
|
1276
|
+
if result is None:
|
|
1277
|
+
return None
|
|
1278
|
+
if isinstance(result, AskUserInlineResult):
|
|
1279
|
+
return InlineQuestionDialogResult(draft=result.draft)
|
|
1280
|
+
if isinstance(result, tuple) and len(result) == 2:
|
|
1281
|
+
request_id, answers = result
|
|
1282
|
+
if (
|
|
1283
|
+
isinstance(request_id, str)
|
|
1284
|
+
and isinstance(answers, tuple)
|
|
1285
|
+
and all(isinstance(answer, AskUserAnswer) for answer in answers)
|
|
1286
|
+
):
|
|
1287
|
+
return AnswerQuestionDialogResult(request_id=request_id, answers=answers)
|
|
1288
|
+
return None
|
|
1289
|
+
|
|
1290
|
+
def show_question_inline(self, event: QuestionToUser, draft: PromptDraft | None = None) -> bool:
|
|
1291
|
+
if not event.call_id:
|
|
1292
|
+
return False
|
|
1293
|
+
try:
|
|
1294
|
+
panel = self._screen.query_one(ChatPanel)
|
|
1295
|
+
except Exception:
|
|
1296
|
+
return False
|
|
1297
|
+
return panel.show_ask_user_inline(
|
|
1298
|
+
event.call_id,
|
|
1299
|
+
event.request_id,
|
|
1300
|
+
event.questions,
|
|
1301
|
+
draft=draft,
|
|
1302
|
+
)
|
|
1303
|
+
|
|
1304
|
+
def clear_question_inline(self, call_id: str, request_id: str) -> bool:
|
|
1305
|
+
"""Clear a timed-out inline prompt if its request still matches."""
|
|
1306
|
+
try:
|
|
1307
|
+
panel = self._screen.query_one(ChatPanel)
|
|
1308
|
+
except Exception:
|
|
1309
|
+
return False
|
|
1310
|
+
return panel.clear_ask_user_inline(call_id, request_id)
|
|
1311
|
+
|
|
1312
|
+
def question_can_reopen_modal(self, event: QuestionToUser) -> bool:
|
|
1313
|
+
if not event.call_id:
|
|
1314
|
+
return True
|
|
1315
|
+
try:
|
|
1316
|
+
panel = self._screen.query_one(ChatPanel)
|
|
1317
|
+
except Exception:
|
|
1318
|
+
return True
|
|
1319
|
+
return panel.is_tool_running(event.call_id)
|
|
1320
|
+
|
|
1321
|
+
def notify_ask_user(self) -> None:
|
|
1322
|
+
self.notify_event(NotificationEvent.ASK_USER)
|
|
1323
|
+
|
|
1324
|
+
def focus_input(self) -> None:
|
|
1325
|
+
with contextlib.suppress(Exception):
|
|
1326
|
+
self._screen.query_one(InputBar).focus_input()
|
|
1327
|
+
|
|
1328
|
+
# -------------------------------------------------------------- #
|
|
1329
|
+
# Suggestions
|
|
1330
|
+
# -------------------------------------------------------------- #
|
|
1331
|
+
|
|
1332
|
+
@property
|
|
1333
|
+
def is_attached(self) -> bool:
|
|
1334
|
+
return self._screen.is_attached
|
|
1335
|
+
|
|
1336
|
+
@property
|
|
1337
|
+
def suggestions_active(self) -> bool:
|
|
1338
|
+
with contextlib.suppress(Exception):
|
|
1339
|
+
return bool(self._screen.query_one(InputBar).suggestions_active)
|
|
1340
|
+
return False
|
|
1341
|
+
|
|
1342
|
+
def show_suggestions(
|
|
1343
|
+
self,
|
|
1344
|
+
mode: str,
|
|
1345
|
+
items: Sequence[SuggestionEntry],
|
|
1346
|
+
*,
|
|
1347
|
+
disabled: set[str] | None = None,
|
|
1348
|
+
initial: str | None = None,
|
|
1349
|
+
title: str = "",
|
|
1350
|
+
) -> None:
|
|
1351
|
+
from chrys.app.tui.widgets.chrome.suggestion_list import SuggestionList
|
|
1352
|
+
|
|
1353
|
+
with contextlib.suppress(NoMatches):
|
|
1354
|
+
self._screen.query_one(SuggestionList).show(
|
|
1355
|
+
mode, list(items), disabled=disabled, initial=initial, title=title
|
|
1356
|
+
)
|
|
1357
|
+
self._screen.query_one(InputBar).set_suggestions_active(True, mode=mode)
|
|
1358
|
+
|
|
1359
|
+
def show_suggestions_loading(self, mode: str, *, title: str = "") -> None:
|
|
1360
|
+
"""Open a suggestion popup immediately with only its loading indicator."""
|
|
1361
|
+
from chrys.app.tui.widgets.chrome.suggestion_list import SuggestionList
|
|
1362
|
+
|
|
1363
|
+
with contextlib.suppress(NoMatches):
|
|
1364
|
+
self._screen.query_one(SuggestionList).show_loading(mode, title=title)
|
|
1365
|
+
self._screen.query_one(InputBar).set_suggestions_active(True, mode=mode)
|
|
1366
|
+
|
|
1367
|
+
def update_suggestions(
|
|
1368
|
+
self,
|
|
1369
|
+
items: Sequence[SuggestionEntry],
|
|
1370
|
+
*,
|
|
1371
|
+
disabled: set[str] | None = None,
|
|
1372
|
+
title: str | None = None,
|
|
1373
|
+
) -> None:
|
|
1374
|
+
from chrys.app.tui.widgets.chrome.suggestion_list import SuggestionList
|
|
1375
|
+
|
|
1376
|
+
with contextlib.suppress(NoMatches):
|
|
1377
|
+
self._screen.query_one(SuggestionList).update(list(items), disabled=disabled, title=title)
|
|
1378
|
+
|
|
1379
|
+
def hide_suggestions(self) -> None:
|
|
1380
|
+
from chrys.app.tui.widgets.chrome.suggestion_list import SuggestionList
|
|
1381
|
+
|
|
1382
|
+
with contextlib.suppress(NoMatches):
|
|
1383
|
+
self._screen.query_one(SuggestionList).hide()
|
|
1384
|
+
input_bar = self._screen.query_one(InputBar)
|
|
1385
|
+
input_bar.set_suggestions_active(False)
|
|
1386
|
+
input_bar.focus_input()
|
|
1387
|
+
|
|
1388
|
+
async def load_prompt_history(self, *, max_entries: int) -> list[str]:
|
|
1389
|
+
"""Load cross-session prompt history without blocking the TUI loop."""
|
|
1390
|
+
return await self._screen.query_one(InputBar).load_prompt_history(max_entries=max_entries)
|
|
1391
|
+
|
|
1392
|
+
def run_file_suggestion_worker(self, awaitable: Awaitable[None], *, name: str, group: str) -> Worker[None]:
|
|
1393
|
+
return self._screen.run_worker(awaitable, name=name, group=group)
|
|
1394
|
+
|
|
1395
|
+
def move_suggestion_cursor(self, direction: str) -> None:
|
|
1396
|
+
from chrys.app.tui.widgets.chrome.suggestion_list import SuggestionList
|
|
1397
|
+
|
|
1398
|
+
suggestions = self._screen.query_one(SuggestionList)
|
|
1399
|
+
if direction == "up":
|
|
1400
|
+
suggestions.move_cursor_up()
|
|
1401
|
+
return
|
|
1402
|
+
suggestions.move_cursor_down()
|
|
1403
|
+
|
|
1404
|
+
def select_highlighted_suggestion(self, *, execute: bool) -> bool:
|
|
1405
|
+
from chrys.app.tui.widgets.chrome.suggestion_list import SuggestionList
|
|
1406
|
+
|
|
1407
|
+
return bool(self._screen.query_one(SuggestionList).select_highlighted(execute=execute))
|
|
1408
|
+
|
|
1409
|
+
def show_subcommand_suggestions(
|
|
1410
|
+
self,
|
|
1411
|
+
items: Sequence[SuggestionEntry],
|
|
1412
|
+
*,
|
|
1413
|
+
initial: str | None = None,
|
|
1414
|
+
) -> None:
|
|
1415
|
+
from chrys.app.tui.widgets.chrome.suggestion_list import SuggestionList
|
|
1416
|
+
|
|
1417
|
+
self._screen.query_one(SuggestionList).show("subcommands", list(items), initial=initial)
|
|
1418
|
+
|
|
1419
|
+
def input_value(self) -> str:
|
|
1420
|
+
return str(self._screen.query_one(InputBar).value)
|
|
1421
|
+
|
|
1422
|
+
def set_input_value(self, value: str) -> None:
|
|
1423
|
+
self._screen.query_one(InputBar).value = value
|
|
1424
|
+
|
|
1425
|
+
def replace_input_trigger_text(self, trigger: str, replacement: str) -> None:
|
|
1426
|
+
self._screen.query_one(InputBar).replace_trigger_text(trigger, replacement)
|
|
1427
|
+
|
|
1428
|
+
# -------------------------------------------------------------- #
|
|
1429
|
+
# Shell mode, focus, and paste
|
|
1430
|
+
# -------------------------------------------------------------- #
|
|
1431
|
+
|
|
1432
|
+
def enter_shell_mode(self) -> None:
|
|
1433
|
+
dashboard = self._screen.query_one(TrajectoryDashboard)
|
|
1434
|
+
session_json = self._screen.query_one(SessionJsonPanel)
|
|
1435
|
+
shell = self._screen.query_one(ShellPanel)
|
|
1436
|
+
status = self._screen.query_one(StatusBar)
|
|
1437
|
+
sidebar = self._screen.query_one(SidebarPanel)
|
|
1438
|
+
self._shell_status_snapshot = status.snapshot()
|
|
1439
|
+
if dashboard.suspend_for_shell_mode():
|
|
1440
|
+
session_json.suspend_for_shell_mode()
|
|
1441
|
+
else:
|
|
1442
|
+
session_json.hide_session_json()
|
|
1443
|
+
# The dashboard hides the status bar while foreground; shell mode needs it.
|
|
1444
|
+
self.sync_main_surface()
|
|
1445
|
+
sidebar.add_class("-shell-active")
|
|
1446
|
+
shell.show()
|
|
1447
|
+
# Flash last: its layout-free face flip rebuilds the compositor map
|
|
1448
|
+
# synchronously, and any widget repaint that lands before the screen's
|
|
1449
|
+
# deferred relayout paints from that map — it must already describe
|
|
1450
|
+
# the final shell layout or the half-applied one flashes on screen.
|
|
1451
|
+
status.flash(STATUS_SHELL_MODE.bind(), warn=True)
|
|
1452
|
+
shell.query_one(Terminal).focus()
|
|
1453
|
+
|
|
1454
|
+
def exit_shell_mode(self) -> None:
|
|
1455
|
+
dashboard = self._screen.query_one(TrajectoryDashboard)
|
|
1456
|
+
session_json = self._screen.query_one(SessionJsonPanel)
|
|
1457
|
+
shell = self._screen.query_one(ShellPanel)
|
|
1458
|
+
status = self._screen.query_one(StatusBar)
|
|
1459
|
+
input_bar = self._screen.query_one(InputBar)
|
|
1460
|
+
sidebar = self._screen.query_one(SidebarPanel)
|
|
1461
|
+
shell.hide()
|
|
1462
|
+
dashboard_restored = dashboard.finish_shell_mode()
|
|
1463
|
+
session_json_visible = dashboard.session_json_visible
|
|
1464
|
+
session_json.finish_shell_mode(restore=session_json_visible)
|
|
1465
|
+
self.sync_main_surface()
|
|
1466
|
+
sidebar.remove_class("-shell-active")
|
|
1467
|
+
# Restore last for the same reason flash runs last in enter_shell_mode:
|
|
1468
|
+
# its map rebuild must see the fully restored layout.
|
|
1469
|
+
status.restore(self._shell_status_snapshot)
|
|
1470
|
+
if not dashboard_restored and input_bar.display:
|
|
1471
|
+
input_bar.focus_input()
|
|
1472
|
+
|
|
1473
|
+
def set_alternate_screen_active(self, active: bool) -> None:
|
|
1474
|
+
status = self._screen.query_one(StatusBar)
|
|
1475
|
+
footer = self._screen.query_one(Footer)
|
|
1476
|
+
sidebar = self._screen.query_one(SidebarPanel)
|
|
1477
|
+
sidebar.suppress_visibility("alternate-screen", active)
|
|
1478
|
+
if active:
|
|
1479
|
+
footer.display = False
|
|
1480
|
+
status.flash(STATUS_INTERACTIVE_MODE.bind(), warn=True)
|
|
1481
|
+
self._screen.query_one(ShellPanel).query_one(Terminal).focus()
|
|
1482
|
+
return
|
|
1483
|
+
if self._state.shell.active:
|
|
1484
|
+
status.flash(STATUS_SHELL_MODE.bind(), warn=True)
|
|
1485
|
+
self._screen.query_one(ShellPanel).query_one(Terminal).focus()
|
|
1486
|
+
else:
|
|
1487
|
+
footer.display = True
|
|
1488
|
+
status.clear_status()
|
|
1489
|
+
|
|
1490
|
+
def insert_paste_payload(self, text: str) -> bool:
|
|
1491
|
+
try:
|
|
1492
|
+
input_bar = self._screen.query_one(InputBar)
|
|
1493
|
+
except Exception:
|
|
1494
|
+
return False
|
|
1495
|
+
if not text:
|
|
1496
|
+
return input_bar.insert_clipboard_image_paste()
|
|
1497
|
+
return input_bar.insert_image_paste_payload(text)
|
|
1498
|
+
|
|
1499
|
+
# -------------------------------------------------------------- #
|
|
1500
|
+
# Agent-load dialogs
|
|
1501
|
+
# -------------------------------------------------------------- #
|
|
1502
|
+
|
|
1503
|
+
def create_agent_load_dialog(self, *, title: StatusMessage, subtitle: str) -> AgentLoadDialogHandle:
|
|
1504
|
+
from chrys.app.tui.screens.dialogs.agent_load import AgentLoadDialog
|
|
1505
|
+
|
|
1506
|
+
return AgentLoadDialog(title=title, subtitle=subtitle, locale_controller=self._locale_controller)
|
|
1507
|
+
|
|
1508
|
+
def prepare_agent_load_ui(
|
|
1509
|
+
self,
|
|
1510
|
+
*,
|
|
1511
|
+
title: StatusMessage,
|
|
1512
|
+
session_id: str | None,
|
|
1513
|
+
update_clipboard_dir: bool,
|
|
1514
|
+
capture_status_snapshot: bool,
|
|
1515
|
+
) -> dict | None:
|
|
1516
|
+
status = self._screen.query_one(StatusBar)
|
|
1517
|
+
snapshot = status.snapshot() if capture_status_snapshot else None
|
|
1518
|
+
status.start_run()
|
|
1519
|
+
status.show(title)
|
|
1520
|
+
|
|
1521
|
+
input_bar = self._screen.query_one(InputBar)
|
|
1522
|
+
if update_clipboard_dir:
|
|
1523
|
+
input_bar.set_clipboard_image_dir(clipboard_image_dir_for_session(self._state_store, session_id))
|
|
1524
|
+
return snapshot
|
|
1525
|
+
|
|
1526
|
+
async def push_agent_load_dialog(self, dialog: AgentLoadDialogHandle) -> None:
|
|
1527
|
+
rollback_modal = self._rollback_progress_modal
|
|
1528
|
+
if rollback_modal is not None:
|
|
1529
|
+
self._rollback_progress_modal = None
|
|
1530
|
+
rollback_modal.handoff_to_session_restore()
|
|
1531
|
+
|
|
1532
|
+
def _restore_input_focus(_result: None = None) -> None:
|
|
1533
|
+
if self._state.shell.active or self._state.shell.fullscreen_terminal:
|
|
1534
|
+
return
|
|
1535
|
+
with contextlib.suppress(Exception):
|
|
1536
|
+
self._screen.query_one(InputBar).focus_input()
|
|
1537
|
+
|
|
1538
|
+
pushed = _push_screen_untyped(self._screen.app, dialog, _restore_input_focus)
|
|
1539
|
+
if inspect.isawaitable(pushed):
|
|
1540
|
+
await pushed
|
|
1541
|
+
|
|
1542
|
+
def restore_agent_load_status(self, snapshot: dict) -> None:
|
|
1543
|
+
with contextlib.suppress(Exception):
|
|
1544
|
+
self._screen.query_one(StatusBar).restore(snapshot)
|
|
1545
|
+
|
|
1546
|
+
def show_load_status(self, message: StatusMessage) -> None:
|
|
1547
|
+
self._screen.query_one(StatusBar).show(message)
|
|
1548
|
+
|
|
1549
|
+
def render_status_message(self, message: StatusMessage) -> str:
|
|
1550
|
+
return self._render_status_message(message)
|
|
1551
|
+
|
|
1552
|
+
def flash_agent_load_failed(self, message: str) -> None:
|
|
1553
|
+
self._screen.query_one(StatusBar).flash(STATUS_AGENT_LOAD_FAILED.bind(message=message), error=True)
|
|
1554
|
+
|
|
1555
|
+
# -------------------------------------------------------------- #
|
|
1556
|
+
# Image-compression dialogs
|
|
1557
|
+
# -------------------------------------------------------------- #
|
|
1558
|
+
|
|
1559
|
+
def create_image_compression_dialog(self, *, title: StatusMessage) -> ImageCompressionDialogHandle:
|
|
1560
|
+
from chrys.app.tui.screens.dialogs.image_compression import ImageCompressionDialog
|
|
1561
|
+
|
|
1562
|
+
return ImageCompressionDialog(title=title, locale_controller=self._locale_controller)
|
|
1563
|
+
|
|
1564
|
+
async def push_image_compression_dialog(self, dialog: ImageCompressionDialogHandle) -> None:
|
|
1565
|
+
pushed = _push_screen_untyped(self._screen.app, dialog)
|
|
1566
|
+
if inspect.isawaitable(pushed):
|
|
1567
|
+
await pushed
|