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,779 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""SessionsScreen — modal for browsing and restoring persisted sessions.
|
|
4
|
+
|
|
5
|
+
Opens as a centered modal overlay (Ctrl+S). Sessions list newest first, in
|
|
6
|
+
pages of at most 100, filtered by where each was last used (TUI, CLI, ACP;
|
|
7
|
+
TUI only by default). The store snapshots the listing once, so paging never
|
|
8
|
+
reshuffles, and each page loads fresh. Within the page shown: click a column
|
|
9
|
+
header to sort (click again to flip direction), type in the bottom search box
|
|
10
|
+
to filter across every column plus each session's user prompts (rows found
|
|
11
|
+
only through prompt text render in italics, list after the column matches,
|
|
12
|
+
and carry the matched context in their tooltip), and forked sessions nest
|
|
13
|
+
under their parent as a tree. Resume to load, Delete to remove.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Literal
|
|
19
|
+
|
|
20
|
+
from rich.cells import cell_len
|
|
21
|
+
from rich.style import Style
|
|
22
|
+
from rich.text import Text
|
|
23
|
+
from textual import on, work
|
|
24
|
+
from textual.containers import HorizontalGroup, VerticalGroup
|
|
25
|
+
from textual.message import Message
|
|
26
|
+
from textual.widgets import Button, DataTable, Input
|
|
27
|
+
|
|
28
|
+
from chrys.app.tui.binding_display import CLOSE_BINDING, DELETE_BINDING, localized_binding
|
|
29
|
+
from chrys.app.tui.i18n import LocaleController, render_str, widget_localizer
|
|
30
|
+
from chrys.app.tui.screens.dialogs.base import BaseDialog
|
|
31
|
+
from chrys.app.tui.screens.sessions.presenter import (
|
|
32
|
+
COLUMNS,
|
|
33
|
+
SURFACE_LABELS,
|
|
34
|
+
WORKFLOW_COLUMNS,
|
|
35
|
+
SessionRow,
|
|
36
|
+
WorkflowSessionPick,
|
|
37
|
+
build_session_rows,
|
|
38
|
+
build_workflow_rows,
|
|
39
|
+
column_by_key,
|
|
40
|
+
format_tokens,
|
|
41
|
+
last_interaction_display,
|
|
42
|
+
profile_display,
|
|
43
|
+
surface_label,
|
|
44
|
+
title_display,
|
|
45
|
+
)
|
|
46
|
+
from chrys.app.tui.util.rich_style import rich_style_from_textual_color
|
|
47
|
+
from chrys.app.tui.widgets import (
|
|
48
|
+
Checkbox,
|
|
49
|
+
ChrysLoadingIndicator,
|
|
50
|
+
DialogButtonRow,
|
|
51
|
+
DialogButtonSpec,
|
|
52
|
+
HatchedEmptyState,
|
|
53
|
+
PageNavigator,
|
|
54
|
+
)
|
|
55
|
+
from chrys.app.tui.widgets.input import EnhancedInput
|
|
56
|
+
from chrys.foundation.branding import APP_DISPLAY_NAME
|
|
57
|
+
from chrys.foundation.i18n import DisplayPath, MessageRef, msg
|
|
58
|
+
from chrys.foundation.i18n.formatting import sanitize_legacy_scalar
|
|
59
|
+
from chrys.foundation.models.session_surface import SessionSurface
|
|
60
|
+
from chrys.foundation.platform.files import surrogate_safe_text
|
|
61
|
+
from chrys.foundation.util.session_ids import session_short_id
|
|
62
|
+
|
|
63
|
+
if TYPE_CHECKING:
|
|
64
|
+
from collections.abc import Callable, Collection
|
|
65
|
+
|
|
66
|
+
from textual import events
|
|
67
|
+
from textual.app import ComposeResult
|
|
68
|
+
from textual.timer import Timer
|
|
69
|
+
|
|
70
|
+
from chrys.service.state.session_listing import SessionListing
|
|
71
|
+
from chrys.service.state.store import SessionMeta, StateStore
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
_SEARCH_DEBOUNCE_SECONDS = 0.15
|
|
75
|
+
"""Delay between the last search keystroke and the table re-render."""
|
|
76
|
+
|
|
77
|
+
_TREE_LEVEL_WIDTH = 2
|
|
78
|
+
"""Cells one fork level adds to the Session ID column ('└ ' / '│ ')."""
|
|
79
|
+
|
|
80
|
+
DEFAULT_SURFACES = frozenset({SessionSurface.TUI})
|
|
81
|
+
"""Surfaces listed until the user picks others; sessions from before surfaces were recorded count as TUI."""
|
|
82
|
+
WORKFLOW_SURFACES = frozenset({SessionSurface.TUI, SessionSurface.CLI})
|
|
83
|
+
"""Where workflow runs start: the TUI and ``icode workflow run``; ACP clients start none."""
|
|
84
|
+
|
|
85
|
+
_DELETE_SESSION_TITLE = msg("tui.sessions.title.delete", fallback="Delete Session")
|
|
86
|
+
_SESSION_OPEN_ELSEWHERE = msg(
|
|
87
|
+
"tui.sessions.delete.open_elsewhere",
|
|
88
|
+
fallback="Session is open in another {app_name} instance.",
|
|
89
|
+
)
|
|
90
|
+
_SESSIONS_TITLE = msg("tui.sessions.title", fallback="Chat Sessions")
|
|
91
|
+
_WORKFLOW_SESSIONS_TITLE = msg("tui.sessions.title.workflow", fallback="Workflow Sessions")
|
|
92
|
+
_TOOLTIP_WORKFLOW = msg("tui.sessions.tooltip.workflow", fallback="Workflow: {workflow}")
|
|
93
|
+
_TOOLTIP_STATUS = msg("tui.sessions.tooltip.status", fallback="Status: {status}")
|
|
94
|
+
_TOOLTIP_RUN = msg("tui.sessions.tooltip.run", fallback="Run: {run_id}")
|
|
95
|
+
_NO_SAVED_SESSIONS = msg("tui.sessions.empty", fallback="No saved sessions.")
|
|
96
|
+
_NO_FILTERED_SESSIONS = msg("tui.sessions.empty.filtered", fallback="No sessions match the selected filters.")
|
|
97
|
+
_NO_SEARCH_MATCHES = msg("tui.sessions.empty.search", fallback="No sessions on this page match your search.")
|
|
98
|
+
_SEARCH_PLACEHOLDER = msg(
|
|
99
|
+
"tui.sessions.search_placeholder",
|
|
100
|
+
fallback="Search this page… (matches any column & your prompts)",
|
|
101
|
+
)
|
|
102
|
+
_RESUME = msg("tui.sessions.button.resume", fallback="Resume")
|
|
103
|
+
_DELETE = msg("tui.sessions.button.delete", fallback="Delete")
|
|
104
|
+
_CLOSE = msg("tui.sessions.button.close", fallback="Close")
|
|
105
|
+
_LOADING_SESSIONS = msg("tui.sessions.loading", fallback="Loading sessions")
|
|
106
|
+
_SESSION_COUNT = msg("tui.sessions.count", fallback="{count_text} sessions")
|
|
107
|
+
_WORKFLOW_SESSION_COUNT = msg("tui.sessions.workflow_count", fallback="{count_text} workflow sessions")
|
|
108
|
+
_TOOLTIP_TITLE = msg("tui.sessions.tooltip.title", fallback="Title: {title}")
|
|
109
|
+
_TOOLTIP_AGENT = msg("tui.sessions.tooltip.agent", fallback="Agent: {agent}")
|
|
110
|
+
_TOOLTIP_PROMPT_MATCH = msg("tui.sessions.tooltip.prompt_match", fallback="Prompt match: {prompt}")
|
|
111
|
+
_TOOLTIP_DIRECTORY = msg("tui.sessions.tooltip.directory", fallback="Directory: {path}")
|
|
112
|
+
_TOOLTIP_TURNS = msg("tui.sessions.tooltip.turns", fallback="Turns: {turns}")
|
|
113
|
+
_TOOLTIP_TOTAL_TOKENS = msg("tui.sessions.tooltip.total_tokens", fallback="Total tokens: {tokens}")
|
|
114
|
+
_TOOLTIP_LAST_INTERACTION = msg(
|
|
115
|
+
"tui.sessions.tooltip.last_interaction",
|
|
116
|
+
fallback="Last interaction: {interaction}",
|
|
117
|
+
)
|
|
118
|
+
_TOOLTIP_FORKED_FROM = msg("tui.sessions.tooltip.forked_from", fallback="Forked from: {session_id}")
|
|
119
|
+
_TOOLTIP_SURFACE = msg("tui.sessions.tooltip.surface", fallback="Last used in: {surface}")
|
|
120
|
+
_DELETE_SESSION_MESSAGE = msg(
|
|
121
|
+
"tui.sessions.delete.confirm_message",
|
|
122
|
+
fallback='Delete session\n"{session_id}"?\n\nThis cannot be undone.',
|
|
123
|
+
multiline=True,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class _SessionTable(DataTable):
|
|
128
|
+
"""DataTable subclass that shows per-row tooltips on hover.
|
|
129
|
+
|
|
130
|
+
Textual's tooltip system is per-widget: when the mouse moves within the
|
|
131
|
+
same widget and the tooltip is already visible, the screen hides it
|
|
132
|
+
(screen.py ``_handle_mouse_move`` line ~1657). To support per-ROW
|
|
133
|
+
tooltips we defer the tooltip update via ``call_later`` so it runs
|
|
134
|
+
*after* the screen's hide logic, then immediately trigger the tooltip
|
|
135
|
+
display.
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
def __init__(self, **kwargs: Any) -> None:
|
|
139
|
+
super().__init__(**kwargs)
|
|
140
|
+
self._row_tooltips: dict[int, Text] = {}
|
|
141
|
+
self._last_hover_row: int = -1
|
|
142
|
+
|
|
143
|
+
def set_row_tooltips(self, tooltips: dict[int, Text]) -> None:
|
|
144
|
+
"""Set tooltip text for each row index."""
|
|
145
|
+
self._row_tooltips = tooltips
|
|
146
|
+
|
|
147
|
+
def _on_mouse_move(self, event: events.MouseMove) -> None:
|
|
148
|
+
# Textual dispatches DataTable._on_mouse_move after this method; do
|
|
149
|
+
# not call super() here or hover bookkeeping runs twice.
|
|
150
|
+
meta = event.style.meta
|
|
151
|
+
if meta and "row" in meta:
|
|
152
|
+
row_idx = meta["row"]
|
|
153
|
+
if row_idx != self._last_hover_row:
|
|
154
|
+
self._last_hover_row = row_idx
|
|
155
|
+
tip = self._row_tooltips.get(row_idx)
|
|
156
|
+
# Defer so it runs after screen._handle_mouse_move hides tooltip
|
|
157
|
+
self.call_later(self._apply_row_tooltip, tip)
|
|
158
|
+
else:
|
|
159
|
+
self._last_hover_row = -1
|
|
160
|
+
self.tooltip = None
|
|
161
|
+
|
|
162
|
+
def _apply_row_tooltip(self, tip: Text | None) -> None:
|
|
163
|
+
"""Apply the tooltip and force immediate display."""
|
|
164
|
+
self.tooltip = tip
|
|
165
|
+
if tip is not None:
|
|
166
|
+
import contextlib
|
|
167
|
+
|
|
168
|
+
with contextlib.suppress(Exception):
|
|
169
|
+
self.screen._handle_tooltip_timer(self)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class _SearchInput(EnhancedInput):
|
|
173
|
+
"""Filter box: Escape clears the query, then hands focus back."""
|
|
174
|
+
|
|
175
|
+
class Escaped(Message):
|
|
176
|
+
"""Posted when Escape is pressed on an already-empty search box."""
|
|
177
|
+
|
|
178
|
+
async def _on_key(self, event: events.Key) -> None:
|
|
179
|
+
# No super() call: Textual dispatches the EnhancedInput/Input base
|
|
180
|
+
# handlers through the MRO on its own.
|
|
181
|
+
if event.key != "escape":
|
|
182
|
+
return
|
|
183
|
+
event.stop()
|
|
184
|
+
event.prevent_default()
|
|
185
|
+
if self.value:
|
|
186
|
+
self.value = ""
|
|
187
|
+
else:
|
|
188
|
+
self.post_message(self.Escaped())
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _next_cursor_row_after_delete(deleted_row: int, remaining_count: int) -> int | None:
|
|
192
|
+
"""Return the row to highlight after deleting one row from a table."""
|
|
193
|
+
if remaining_count <= 0:
|
|
194
|
+
return None
|
|
195
|
+
return min(deleted_row, remaining_count - 1)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _next_session_id_after_delete(session_ids: list[str], deleted_row: int) -> str | None:
|
|
199
|
+
"""Return the visible neighbor to highlight after deleting one row."""
|
|
200
|
+
if not 0 <= deleted_row < len(session_ids):
|
|
201
|
+
return None
|
|
202
|
+
next_row = deleted_row + 1
|
|
203
|
+
if next_row < len(session_ids):
|
|
204
|
+
return session_ids[next_row]
|
|
205
|
+
previous_row = deleted_row - 1
|
|
206
|
+
if previous_row >= 0:
|
|
207
|
+
return session_ids[previous_row]
|
|
208
|
+
return None
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class SessionsScreen(BaseDialog[str | WorkflowSessionPick | None]):
|
|
212
|
+
"""Modal for browsing persisted sessions.
|
|
213
|
+
|
|
214
|
+
Returns a chat session id or a specific workflow run to open; None closes the modal.
|
|
215
|
+
"""
|
|
216
|
+
|
|
217
|
+
BINDINGS: ClassVar[list] = [
|
|
218
|
+
localized_binding("escape", "dismiss", CLOSE_BINDING),
|
|
219
|
+
localized_binding("delete", "delete_session", DELETE_BINDING),
|
|
220
|
+
]
|
|
221
|
+
|
|
222
|
+
CSS_PATH = "screen.tcss"
|
|
223
|
+
|
|
224
|
+
#: Sentinel prefix: tells caller to delete this session and start fresh.
|
|
225
|
+
DELETE_AND_NEW_PREFIX = "__delete_and_new__:"
|
|
226
|
+
|
|
227
|
+
def __init__(
|
|
228
|
+
self,
|
|
229
|
+
state_store: StateStore,
|
|
230
|
+
current_session_id: str = "",
|
|
231
|
+
*,
|
|
232
|
+
locale_controller: LocaleController | None = None,
|
|
233
|
+
workflow_mode: bool = False,
|
|
234
|
+
surfaces: Collection[SessionSurface] | None = None,
|
|
235
|
+
on_surfaces_changed: Callable[[frozenset[SessionSurface]], None] | None = None,
|
|
236
|
+
) -> None:
|
|
237
|
+
self._state_store = state_store
|
|
238
|
+
self._current_session_id = current_session_id
|
|
239
|
+
self._locale_controller = locale_controller
|
|
240
|
+
self._workflow_mode = workflow_mode
|
|
241
|
+
self._kind: Literal["chat", "workflow"] = "workflow" if workflow_mode else "chat"
|
|
242
|
+
self._columns = WORKFLOW_COLUMNS if workflow_mode else COLUMNS
|
|
243
|
+
self._offered_surfaces = tuple(
|
|
244
|
+
surface for surface in SURFACE_LABELS if not workflow_mode or surface in WORKFLOW_SURFACES
|
|
245
|
+
)
|
|
246
|
+
# A surface the browser offers no checkbox for could never be unchecked.
|
|
247
|
+
chosen = DEFAULT_SURFACES if surfaces is None else surfaces
|
|
248
|
+
self._surfaces = frozenset(surface for surface in chosen if surface in self._offered_surfaces)
|
|
249
|
+
self._on_surfaces_changed = on_surfaces_changed
|
|
250
|
+
# Taken once, on the first load: pages keep their order however sessions change meanwhile.
|
|
251
|
+
self._listing: SessionListing | None = None
|
|
252
|
+
self._page = 1
|
|
253
|
+
self._page_count = 1
|
|
254
|
+
self._filtered_total = 0
|
|
255
|
+
"""Sessions of the selected surfaces, across every page."""
|
|
256
|
+
self._page_loaded = False
|
|
257
|
+
# The page's sessions: what sorting and searching work on.
|
|
258
|
+
self._rendered_sessions: list[SessionMeta] = []
|
|
259
|
+
self._session_ids: list[str] = []
|
|
260
|
+
self._rows: list[SessionRow] = []
|
|
261
|
+
self._sort_column: str = column_by_key("").key
|
|
262
|
+
self._sort_reverse: bool = column_by_key("").default_reverse
|
|
263
|
+
self._loading = True
|
|
264
|
+
self._loading_page: int | None = None
|
|
265
|
+
"""The page the load in flight asked for (the store may clamp it); None once a load finishes."""
|
|
266
|
+
# Becomes True once the cursor has jumped to the currently loaded
|
|
267
|
+
# session (or the initial scan finished without finding it).
|
|
268
|
+
self._cursor_initialized = False
|
|
269
|
+
self._search_timer: Timer | None = None
|
|
270
|
+
super().__init__()
|
|
271
|
+
|
|
272
|
+
def compose(self) -> ComposeResult:
|
|
273
|
+
localizer = widget_localizer(self)
|
|
274
|
+
with VerticalGroup(id="container") as container:
|
|
275
|
+
title = _WORKFLOW_SESSIONS_TITLE if self._workflow_mode else _SESSIONS_TITLE
|
|
276
|
+
container.border_title = Text(render_str(localizer, title.bind()))
|
|
277
|
+
with VerticalGroup(id="sessions-loading-state"):
|
|
278
|
+
yield ChrysLoadingIndicator(id="sessions-loading")
|
|
279
|
+
yield HatchedEmptyState(render_str(localizer, _NO_SAVED_SESSIONS.bind()), id="empty-note")
|
|
280
|
+
table = _SessionTable(id="sessions", cursor_type="row")
|
|
281
|
+
table.display = False
|
|
282
|
+
yield table
|
|
283
|
+
with HorizontalGroup(id="filters") as filters:
|
|
284
|
+
filters.display = False
|
|
285
|
+
with HorizontalGroup(id="surface-filters"):
|
|
286
|
+
for surface in self._offered_surfaces:
|
|
287
|
+
yield Checkbox(
|
|
288
|
+
render_str(localizer, SURFACE_LABELS[surface].bind()),
|
|
289
|
+
value=surface in self._surfaces,
|
|
290
|
+
id=f"surface-{surface.value}",
|
|
291
|
+
)
|
|
292
|
+
yield PageNavigator(self._locale_controller, id="session-pages")
|
|
293
|
+
with HorizontalGroup(id="footer") as footer:
|
|
294
|
+
footer.display = False
|
|
295
|
+
# The themed border lives on the wrapper so the input's
|
|
296
|
+
# background stays inside it (a border directly on the Input
|
|
297
|
+
# would share its background; compact=True would strip the
|
|
298
|
+
# border entirely with `border: none !important`).
|
|
299
|
+
with HorizontalGroup(id="search-box"):
|
|
300
|
+
yield _SearchInput(
|
|
301
|
+
placeholder=render_str(localizer, _SEARCH_PLACEHOLDER.bind()),
|
|
302
|
+
id="search",
|
|
303
|
+
compact=True,
|
|
304
|
+
)
|
|
305
|
+
yield DialogButtonRow(
|
|
306
|
+
DialogButtonSpec(
|
|
307
|
+
Text(render_str(localizer, _RESUME.bind())), id="resume", variant="primary", disabled=True
|
|
308
|
+
),
|
|
309
|
+
DialogButtonSpec(
|
|
310
|
+
Text(render_str(localizer, _DELETE.bind())), id="delete", variant="error", disabled=True
|
|
311
|
+
),
|
|
312
|
+
DialogButtonSpec(Text(render_str(localizer, _CLOSE.bind())), id="cancel", variant="warning"),
|
|
313
|
+
id="buttons",
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
def on_mount(self) -> None:
|
|
317
|
+
self._request_page(1)
|
|
318
|
+
|
|
319
|
+
# ------------------------------------------------------------------
|
|
320
|
+
# Loading & rendering
|
|
321
|
+
# ------------------------------------------------------------------
|
|
322
|
+
|
|
323
|
+
def _request_page(
|
|
324
|
+
self, page: int, *, preferred_cursor_row: int | None = None, preferred_session_id: str | None = None
|
|
325
|
+
) -> None:
|
|
326
|
+
"""Load and show *page*, replacing any load in flight."""
|
|
327
|
+
self._loading_page = page
|
|
328
|
+
self._load_sessions(
|
|
329
|
+
page=page, preferred_cursor_row=preferred_cursor_row, preferred_session_id=preferred_session_id
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
@work(exclusive=True, group="load-sessions")
|
|
333
|
+
async def _load_sessions(
|
|
334
|
+
self,
|
|
335
|
+
page: int = 1,
|
|
336
|
+
preferred_cursor_row: int | None = None,
|
|
337
|
+
preferred_session_id: str | None = None,
|
|
338
|
+
) -> None:
|
|
339
|
+
"""Load one page of the listing, then render it.
|
|
340
|
+
|
|
341
|
+
The listing is snapshotted by the first load; later loads (paging,
|
|
342
|
+
filtering, reloading after a delete) page through that snapshot.
|
|
343
|
+
Loads are exclusive: a newer request cancels the one in flight, so
|
|
344
|
+
the page shown is always the last one asked for.
|
|
345
|
+
"""
|
|
346
|
+
self._loading = True
|
|
347
|
+
self._update_chrome()
|
|
348
|
+
if self._listing is None:
|
|
349
|
+
self._listing = await self._state_store.open_session_listing(kind=self._kind)
|
|
350
|
+
loaded = await self._state_store.load_session_page(self._listing, surfaces=self._surfaces, page=page)
|
|
351
|
+
self._loading = False
|
|
352
|
+
self._loading_page = None
|
|
353
|
+
self._page, self._page_count, self._filtered_total = loaded.page, loaded.page_count, loaded.total
|
|
354
|
+
self._page_loaded = True
|
|
355
|
+
self._render_table(
|
|
356
|
+
preferred_cursor_row=preferred_cursor_row,
|
|
357
|
+
preferred_session_id=preferred_session_id,
|
|
358
|
+
source_sessions=list(loaded.metas),
|
|
359
|
+
)
|
|
360
|
+
self._cursor_initialized = True
|
|
361
|
+
|
|
362
|
+
def _render_table(
|
|
363
|
+
self,
|
|
364
|
+
preferred_cursor_row: int | None = None,
|
|
365
|
+
preferred_session_id: str | None = None,
|
|
366
|
+
*,
|
|
367
|
+
prefer_live_selection: bool = True,
|
|
368
|
+
source_sessions: list[SessionMeta] | None = None,
|
|
369
|
+
) -> None:
|
|
370
|
+
"""Rebuild the table from the in-memory metas (sort + filter + tree)."""
|
|
371
|
+
table = self.query_one("#sessions", _SessionTable)
|
|
372
|
+
query = self.query_one("#search", _SearchInput).value
|
|
373
|
+
previous_selected = self._get_selected_session_id()
|
|
374
|
+
|
|
375
|
+
render_source = source_sessions
|
|
376
|
+
if render_source is None:
|
|
377
|
+
render_source = self._rendered_sessions
|
|
378
|
+
|
|
379
|
+
build_rows = build_workflow_rows if self._workflow_mode else build_session_rows
|
|
380
|
+
rows = build_rows(
|
|
381
|
+
render_source,
|
|
382
|
+
sort_column=self._sort_column,
|
|
383
|
+
sort_reverse=self._sort_reverse,
|
|
384
|
+
query=query,
|
|
385
|
+
render_message=self._render_message,
|
|
386
|
+
)
|
|
387
|
+
self._rows = rows
|
|
388
|
+
self._session_ids = [row.key for row in rows]
|
|
389
|
+
|
|
390
|
+
table.clear(columns=True)
|
|
391
|
+
max_depth = max((row.depth for row in rows), default=0)
|
|
392
|
+
for column in self._columns:
|
|
393
|
+
label = self._render_message(column.label.bind())
|
|
394
|
+
if column.key == self._sort_column:
|
|
395
|
+
label += " ↓" if self._sort_reverse else " ↑"
|
|
396
|
+
width = column.width
|
|
397
|
+
if width is not None:
|
|
398
|
+
if column.key == "id":
|
|
399
|
+
width += _TREE_LEVEL_WIDTH * max_depth
|
|
400
|
+
width = max(width, cell_len(label))
|
|
401
|
+
table.add_column(Text(label), width=width, key=column.key)
|
|
402
|
+
|
|
403
|
+
highlight = query.strip()
|
|
404
|
+
match_style = self._match_style()
|
|
405
|
+
row_tooltips: dict[int, Text] = {}
|
|
406
|
+
for row_index, row in enumerate(rows):
|
|
407
|
+
table.add_row(*self._row_texts(row, highlight, match_style), key=row.key)
|
|
408
|
+
row_tooltips[row_index] = self._row_tooltip(row)
|
|
409
|
+
table.set_row_tooltips(row_tooltips)
|
|
410
|
+
|
|
411
|
+
self._restore_cursor(
|
|
412
|
+
previous_selected,
|
|
413
|
+
preferred_cursor_row,
|
|
414
|
+
preferred_session_id,
|
|
415
|
+
prefer_live_selection=prefer_live_selection,
|
|
416
|
+
)
|
|
417
|
+
self._rendered_sessions = list(render_source)
|
|
418
|
+
has_rows = bool(rows)
|
|
419
|
+
self.query_one("#resume", Button).disabled = not has_rows
|
|
420
|
+
self.query_one("#delete", Button).disabled = not has_rows
|
|
421
|
+
self._update_chrome()
|
|
422
|
+
|
|
423
|
+
def _match_style(self) -> Style:
|
|
424
|
+
"""Search-match highlight style, themed to the warning color."""
|
|
425
|
+
warning = self.app.theme_variables.get("warning", "yellow")
|
|
426
|
+
return rich_style_from_textual_color(warning, reverse=True)
|
|
427
|
+
|
|
428
|
+
def _row_texts(self, row: SessionRow, highlight: str, match_style: Style) -> list[Text]:
|
|
429
|
+
"""Cell renderables for one row, with tree guides and match marks."""
|
|
430
|
+
texts: list[Text] = []
|
|
431
|
+
for column in self._columns:
|
|
432
|
+
cell = sanitize_legacy_scalar(surrogate_safe_text(row.cells[column.key]))
|
|
433
|
+
value = Text(cell, justify="right" if column.numeric else "left")
|
|
434
|
+
if highlight:
|
|
435
|
+
value.highlight_words([highlight], match_style, case_sensitive=False)
|
|
436
|
+
if not row.matched:
|
|
437
|
+
# Ancestor kept only to anchor a matching fork subtree.
|
|
438
|
+
value.stylize("dim")
|
|
439
|
+
elif row.prompt_only:
|
|
440
|
+
# Match lives in prompt text — nothing in the row
|
|
441
|
+
# highlights, so italics flag it (tooltip has the
|
|
442
|
+
# matched context).
|
|
443
|
+
value.stylize("italic")
|
|
444
|
+
if column.key == "id" and row.tree_prefix:
|
|
445
|
+
value = Text(row.tree_prefix, style="dim").append_text(value)
|
|
446
|
+
texts.append(value)
|
|
447
|
+
return texts
|
|
448
|
+
|
|
449
|
+
def _row_tooltip(self, row: SessionRow) -> Text:
|
|
450
|
+
"""Hover tooltip, one labelled line per fact.
|
|
451
|
+
|
|
452
|
+
The agent profile lives only here since its column was dropped
|
|
453
|
+
(rarely distinguishing between rows).
|
|
454
|
+
"""
|
|
455
|
+
meta = row.meta
|
|
456
|
+
if row.workflow is not None:
|
|
457
|
+
parts = [
|
|
458
|
+
self._render_message(_TOOLTIP_WORKFLOW.bind(workflow=row.workflow.title)),
|
|
459
|
+
self._render_message(_TOOLTIP_STATUS.bind(status=row.cells["status"])),
|
|
460
|
+
self._render_message(_TOOLTIP_RUN.bind(run_id=row.workflow.run_id)),
|
|
461
|
+
]
|
|
462
|
+
else:
|
|
463
|
+
if meta.kind != "chat":
|
|
464
|
+
raise ValueError("Unsupported session kind for chat tooltip.")
|
|
465
|
+
parts = [
|
|
466
|
+
self._render_message(_TOOLTIP_TITLE.bind(title=title_display(meta))),
|
|
467
|
+
self._render_message(_TOOLTIP_AGENT.bind(agent=profile_display(meta))),
|
|
468
|
+
]
|
|
469
|
+
if row.prompt_snippet:
|
|
470
|
+
parts.insert(1, self._render_message(_TOOLTIP_PROMPT_MATCH.bind(prompt=row.prompt_snippet)))
|
|
471
|
+
if meta.primary_cwd:
|
|
472
|
+
parts.append(self._render_message(_TOOLTIP_DIRECTORY.bind(path=DisplayPath(meta.primary_cwd))))
|
|
473
|
+
if meta.kind == "chat":
|
|
474
|
+
parts.append(self._render_message(_TOOLTIP_TURNS.bind(turns=meta.turn_count)))
|
|
475
|
+
parts.append(self._render_message(_TOOLTIP_TOTAL_TOKENS.bind(tokens=format_tokens(meta.total_tokens))))
|
|
476
|
+
updated = row.workflow.updated_at if row.workflow is not None else meta.updated_at
|
|
477
|
+
parts.append(
|
|
478
|
+
self._render_message(_TOOLTIP_LAST_INTERACTION.bind(interaction=last_interaction_display(updated)))
|
|
479
|
+
)
|
|
480
|
+
surface = self._render_message(surface_label(meta.last_surface))
|
|
481
|
+
parts.append(self._render_message(_TOOLTIP_SURFACE.bind(surface=surface)))
|
|
482
|
+
if meta.kind == "chat" and meta.parent_session_id:
|
|
483
|
+
parts.append(
|
|
484
|
+
self._render_message(_TOOLTIP_FORKED_FROM.bind(session_id=session_short_id(meta.parent_session_id)))
|
|
485
|
+
)
|
|
486
|
+
return Text("\n".join(parts))
|
|
487
|
+
|
|
488
|
+
def _restore_cursor(
|
|
489
|
+
self,
|
|
490
|
+
previous_selected: str | None,
|
|
491
|
+
preferred_cursor_row: int | None,
|
|
492
|
+
preferred_session_id: str | None,
|
|
493
|
+
*,
|
|
494
|
+
prefer_live_selection: bool,
|
|
495
|
+
) -> None:
|
|
496
|
+
"""Keep the highlighted session stable across re-renders.
|
|
497
|
+
|
|
498
|
+
The jump to the currently loaded session happens at most once, on
|
|
499
|
+
the initial open (``_cursor_initialized``) — a later reload (e.g.
|
|
500
|
+
after a delete) must not yank the cursor back to it. Delete refreshes
|
|
501
|
+
prefer a concrete neighbor session id, with row number only as a last
|
|
502
|
+
fallback if that neighbor disappeared during the reload. Final
|
|
503
|
+
reload renders prefer live user selection; optimistic delete renders
|
|
504
|
+
force the precomputed neighbor for immediate visual stability.
|
|
505
|
+
"""
|
|
506
|
+
table = self.query_one("#sessions", _SessionTable)
|
|
507
|
+
if table.row_count == 0:
|
|
508
|
+
return
|
|
509
|
+
target_id = previous_selected if previous_selected in self._session_ids else None
|
|
510
|
+
if preferred_session_id is not None:
|
|
511
|
+
if (not prefer_live_selection or target_id is None) and preferred_session_id in self._session_ids:
|
|
512
|
+
target_id = preferred_session_id
|
|
513
|
+
elif not prefer_live_selection:
|
|
514
|
+
target_id = None
|
|
515
|
+
if target_id is not None:
|
|
516
|
+
table.move_cursor(row=self._session_ids.index(target_id))
|
|
517
|
+
elif not self._loading and preferred_cursor_row is not None:
|
|
518
|
+
table.move_cursor(row=min(preferred_cursor_row, table.row_count - 1))
|
|
519
|
+
else:
|
|
520
|
+
table.move_cursor(row=0)
|
|
521
|
+
return
|
|
522
|
+
|
|
523
|
+
if target_id is None and not self._cursor_initialized:
|
|
524
|
+
target_id = next((row.key for row in self._rows if row.meta.session_id == self._current_session_id), None)
|
|
525
|
+
self._cursor_initialized = True
|
|
526
|
+
if target_id is not None:
|
|
527
|
+
table.move_cursor(row=self._session_ids.index(target_id))
|
|
528
|
+
elif preferred_cursor_row is not None:
|
|
529
|
+
table.move_cursor(row=min(preferred_cursor_row, table.row_count - 1))
|
|
530
|
+
else:
|
|
531
|
+
table.move_cursor(row=0)
|
|
532
|
+
|
|
533
|
+
def _update_chrome(self) -> None:
|
|
534
|
+
"""Which of loading, empty note, table and controls show, plus the count in the border.
|
|
535
|
+
|
|
536
|
+
With no sessions at all the dialog shrinks to its empty note. Once
|
|
537
|
+
any exist, the filters, pager and footer stay so every filter can be
|
|
538
|
+
undone, and a note replaces the table when the selected surfaces or
|
|
539
|
+
the search leave nothing to show.
|
|
540
|
+
"""
|
|
541
|
+
show_loading = not self._page_loaded
|
|
542
|
+
has_sessions = self._listing is not None and bool(self._listing.entries)
|
|
543
|
+
visible_rows = len(self._rows)
|
|
544
|
+
search = self.query_one("#search", _SearchInput)
|
|
545
|
+
searching = bool(search.value.strip())
|
|
546
|
+
container = self.query_one("#container")
|
|
547
|
+
if has_sessions or show_loading:
|
|
548
|
+
container.remove_class("-empty")
|
|
549
|
+
else:
|
|
550
|
+
container.add_class("-empty")
|
|
551
|
+
show_table = not show_loading and visible_rows > 0
|
|
552
|
+
table = self.query_one("#sessions", _SessionTable)
|
|
553
|
+
self.query_one("#sessions-loading-state").display = show_loading
|
|
554
|
+
table.display = show_table
|
|
555
|
+
note = self.query_one("#empty-note", HatchedEmptyState)
|
|
556
|
+
note.display = not show_loading and not show_table
|
|
557
|
+
if not has_sessions:
|
|
558
|
+
reason = _NO_SAVED_SESSIONS
|
|
559
|
+
elif searching and self._rendered_sessions:
|
|
560
|
+
reason = _NO_SEARCH_MATCHES
|
|
561
|
+
else:
|
|
562
|
+
reason = _NO_FILTERED_SESSIONS
|
|
563
|
+
if (label := self._render_message(reason.bind())) != note.label:
|
|
564
|
+
note.update_label(label)
|
|
565
|
+
show_controls = not show_loading and has_sessions
|
|
566
|
+
self.query_one("#filters").display = show_controls
|
|
567
|
+
self.query_one("#footer").display = show_controls
|
|
568
|
+
navigator = self.query_one(PageNavigator)
|
|
569
|
+
# Focus must not stay on a pager button this disables (a page turned while the search hid
|
|
570
|
+
# the table) or on the table once it hides (a page where the search matches nothing).
|
|
571
|
+
if (show_table or show_controls) and (
|
|
572
|
+
navigator.disables_focused(self._page, self._page_count) or (self.focused is table and not show_table)
|
|
573
|
+
):
|
|
574
|
+
self.set_focus(table if show_table else search, scroll_visible=False)
|
|
575
|
+
navigator.show(self._page, self._page_count)
|
|
576
|
+
|
|
577
|
+
if show_loading:
|
|
578
|
+
container.border_subtitle = Text(self._render_message(_LOADING_SESSIONS.bind()))
|
|
579
|
+
elif has_sessions:
|
|
580
|
+
count = f"{visible_rows}/{len(self._rendered_sessions)}" if searching else f"{self._filtered_total}"
|
|
581
|
+
label = _WORKFLOW_SESSION_COUNT if self._workflow_mode else _SESSION_COUNT
|
|
582
|
+
container.border_subtitle = Text(self._render_message(label.bind(count_text=count)))
|
|
583
|
+
else:
|
|
584
|
+
container.border_subtitle = ""
|
|
585
|
+
|
|
586
|
+
# ------------------------------------------------------------------
|
|
587
|
+
# Surface filters & paging
|
|
588
|
+
# ------------------------------------------------------------------
|
|
589
|
+
|
|
590
|
+
@on(Checkbox.Changed, "#surface-filters Checkbox")
|
|
591
|
+
def _on_surface_toggled(self, event: Checkbox.Changed) -> None:
|
|
592
|
+
"""A surface was checked or unchecked: list the first page of the new selection."""
|
|
593
|
+
event.stop()
|
|
594
|
+
surfaces = frozenset(
|
|
595
|
+
surface for surface in self._offered_surfaces if self.query_one(f"#surface-{surface.value}", Checkbox).value
|
|
596
|
+
)
|
|
597
|
+
if surfaces == self._surfaces:
|
|
598
|
+
return
|
|
599
|
+
self._surfaces = surfaces
|
|
600
|
+
if self._on_surfaces_changed is not None:
|
|
601
|
+
self._on_surfaces_changed(surfaces)
|
|
602
|
+
self._request_page(1)
|
|
603
|
+
|
|
604
|
+
@on(PageNavigator.Changed, "#session-pages")
|
|
605
|
+
def _on_page_changed(self, event: PageNavigator.Changed) -> None:
|
|
606
|
+
event.stop()
|
|
607
|
+
table = self.query_one("#sessions", _SessionTable)
|
|
608
|
+
if table.display:
|
|
609
|
+
self.set_focus(table, scroll_visible=False)
|
|
610
|
+
# The pager still shows the old page while a slow load runs: asking again would only
|
|
611
|
+
# restart it, and the cancelled read would go on in its thread.
|
|
612
|
+
if event.page != self._loading_page:
|
|
613
|
+
self._request_page(event.page)
|
|
614
|
+
|
|
615
|
+
# ------------------------------------------------------------------
|
|
616
|
+
# Sorting & searching
|
|
617
|
+
# ------------------------------------------------------------------
|
|
618
|
+
|
|
619
|
+
@on(DataTable.HeaderSelected, "#sessions")
|
|
620
|
+
def _on_header_selected(self, event: DataTable.HeaderSelected) -> None:
|
|
621
|
+
"""Click a column header — sort by it; click again to flip direction."""
|
|
622
|
+
key = event.column_key.value
|
|
623
|
+
if key is None:
|
|
624
|
+
return
|
|
625
|
+
column = column_by_key(str(key), workflow=self._workflow_mode)
|
|
626
|
+
if self._sort_column == column.key:
|
|
627
|
+
self._sort_reverse = not self._sort_reverse
|
|
628
|
+
else:
|
|
629
|
+
self._sort_column = column.key
|
|
630
|
+
self._sort_reverse = column.default_reverse
|
|
631
|
+
self._render_table()
|
|
632
|
+
|
|
633
|
+
@on(Input.Changed, "#search")
|
|
634
|
+
def _on_search_changed(self, event: Input.Changed) -> None:
|
|
635
|
+
"""Debounce filter re-renders while the user is still typing."""
|
|
636
|
+
if self._search_timer is not None:
|
|
637
|
+
self._search_timer.stop()
|
|
638
|
+
self._search_timer = self.set_timer(_SEARCH_DEBOUNCE_SECONDS, self._render_table)
|
|
639
|
+
|
|
640
|
+
@on(Input.Submitted, "#search")
|
|
641
|
+
def _on_search_submitted(self, event: Input.Submitted) -> None:
|
|
642
|
+
"""Enter in the search box — jump to the filtered results."""
|
|
643
|
+
self._focus_table()
|
|
644
|
+
|
|
645
|
+
@on(_SearchInput.Escaped)
|
|
646
|
+
def _on_search_escaped(self, event: _SearchInput.Escaped) -> None:
|
|
647
|
+
"""Escape on an empty search box — return focus to the table."""
|
|
648
|
+
self._focus_table()
|
|
649
|
+
|
|
650
|
+
def _focus_table(self) -> None:
|
|
651
|
+
# A hidden table can still take focus, which would leave the keyboard nowhere visible.
|
|
652
|
+
table = self.query_one("#sessions", _SessionTable)
|
|
653
|
+
if table.display:
|
|
654
|
+
table.focus()
|
|
655
|
+
|
|
656
|
+
# ------------------------------------------------------------------
|
|
657
|
+
# Selection & dismissal
|
|
658
|
+
# ------------------------------------------------------------------
|
|
659
|
+
|
|
660
|
+
def _get_selected_session_id(self) -> str | None:
|
|
661
|
+
"""Return the session ID stored as the highlighted row key."""
|
|
662
|
+
table = self.query_one("#sessions", DataTable)
|
|
663
|
+
if table.row_count == 0:
|
|
664
|
+
return None
|
|
665
|
+
row_key, _ = table.coordinate_to_cell_key(table.cursor_coordinate)
|
|
666
|
+
if row_key is not None and row_key.value is not None:
|
|
667
|
+
return str(row_key.value)
|
|
668
|
+
return None
|
|
669
|
+
|
|
670
|
+
def _resume_selected(self) -> None:
|
|
671
|
+
key = self._get_selected_session_id()
|
|
672
|
+
row = next((row for row in self._rows if row.key == key), None)
|
|
673
|
+
if row is not None:
|
|
674
|
+
self.dismiss(WorkflowSessionPick(row.meta.session_id) if row.workflow is not None else row.meta.session_id)
|
|
675
|
+
|
|
676
|
+
@on(DataTable.RowHighlighted)
|
|
677
|
+
def _on_row_highlighted(self, event: DataTable.RowHighlighted) -> None:
|
|
678
|
+
"""Enable buttons when a row is highlighted."""
|
|
679
|
+
self.query_one("#resume", Button).disabled = False
|
|
680
|
+
self.query_one("#delete", Button).disabled = False
|
|
681
|
+
|
|
682
|
+
@on(DataTable.RowSelected)
|
|
683
|
+
def _on_row_selected(self, event: DataTable.RowSelected) -> None:
|
|
684
|
+
"""Double-click or Enter on a row — resume that session."""
|
|
685
|
+
self._resume_selected()
|
|
686
|
+
|
|
687
|
+
@on(Button.Pressed, "#resume")
|
|
688
|
+
def _on_resume(self, event: Button.Pressed) -> None:
|
|
689
|
+
self._resume_selected()
|
|
690
|
+
|
|
691
|
+
@on(Button.Pressed, "#cancel")
|
|
692
|
+
def _on_cancel(self, event: Button.Pressed) -> None:
|
|
693
|
+
self.dismiss(None)
|
|
694
|
+
|
|
695
|
+
@on(Button.Pressed, "#delete")
|
|
696
|
+
def _on_delete_button(self, event: Button.Pressed) -> None:
|
|
697
|
+
self.action_delete_session()
|
|
698
|
+
|
|
699
|
+
def action_delete_session(self) -> None:
|
|
700
|
+
"""Delete key or Delete button — confirm removing the highlighted session."""
|
|
701
|
+
session_id = self._get_selected_session_id()
|
|
702
|
+
if session_id:
|
|
703
|
+
table = self.query_one("#sessions", DataTable)
|
|
704
|
+
selected_row = table.cursor_coordinate.row
|
|
705
|
+
session_ids = list(self._session_ids)
|
|
706
|
+
preferred_session_id = _next_session_id_after_delete(session_ids, selected_row)
|
|
707
|
+
preferred_cursor_row = _next_cursor_row_after_delete(selected_row, len(session_ids) - 1)
|
|
708
|
+
|
|
709
|
+
from chrys.app.tui.screens.dialogs.confirm import ConfirmDialog
|
|
710
|
+
|
|
711
|
+
dialog = ConfirmDialog(
|
|
712
|
+
title=self._render_message(_DELETE_SESSION_TITLE.bind()),
|
|
713
|
+
message=self._render_message(_DELETE_SESSION_MESSAGE.bind(session_id=session_short_id(session_id))),
|
|
714
|
+
confirm_label=self._render_message(_DELETE.bind()),
|
|
715
|
+
confirm_variant="error",
|
|
716
|
+
locale_controller=self._locale_controller,
|
|
717
|
+
)
|
|
718
|
+
self.app.push_screen( # ty: ignore[no-matching-overload] # Textual's callback overload rejects the work-decorated handler result.
|
|
719
|
+
dialog,
|
|
720
|
+
callback=lambda confirmed: (
|
|
721
|
+
self._do_delete(
|
|
722
|
+
session_id,
|
|
723
|
+
preferred_cursor_row=preferred_cursor_row,
|
|
724
|
+
preferred_session_id=preferred_session_id,
|
|
725
|
+
)
|
|
726
|
+
if confirmed
|
|
727
|
+
else None
|
|
728
|
+
),
|
|
729
|
+
)
|
|
730
|
+
|
|
731
|
+
def _render_message(self, reference: MessageRef) -> str:
|
|
732
|
+
return render_str(widget_localizer(self), reference)
|
|
733
|
+
|
|
734
|
+
@work(thread=False)
|
|
735
|
+
async def _do_delete(
|
|
736
|
+
self,
|
|
737
|
+
session_id: str,
|
|
738
|
+
*,
|
|
739
|
+
preferred_cursor_row: int | None,
|
|
740
|
+
preferred_session_id: str | None,
|
|
741
|
+
) -> None:
|
|
742
|
+
"""Delete a session from the store and refresh the table."""
|
|
743
|
+
if session_id == self._current_session_id:
|
|
744
|
+
# Don't delete here — let MainScreen handle it via the engine
|
|
745
|
+
# so shutdown() won't re-save the file.
|
|
746
|
+
self.dismiss(f"{self.DELETE_AND_NEW_PREFIX}{session_id}")
|
|
747
|
+
else:
|
|
748
|
+
try:
|
|
749
|
+
await self._state_store.delete_session(session_id)
|
|
750
|
+
except TimeoutError:
|
|
751
|
+
self.notify(
|
|
752
|
+
render_str(
|
|
753
|
+
widget_localizer(self),
|
|
754
|
+
_SESSION_OPEN_ELSEWHERE.bind(app_name=APP_DISPLAY_NAME),
|
|
755
|
+
),
|
|
756
|
+
title=render_str(
|
|
757
|
+
widget_localizer(self),
|
|
758
|
+
_DELETE_SESSION_TITLE.bind(),
|
|
759
|
+
),
|
|
760
|
+
severity="warning",
|
|
761
|
+
timeout=4,
|
|
762
|
+
markup=False,
|
|
763
|
+
)
|
|
764
|
+
return
|
|
765
|
+
if self._listing is not None:
|
|
766
|
+
self._listing = self._listing.without(session_id)
|
|
767
|
+
optimistic_sessions = [meta for meta in self._rendered_sessions if meta.session_id != session_id]
|
|
768
|
+
if len(optimistic_sessions) < len(self._rendered_sessions):
|
|
769
|
+
self._filtered_total = max(0, self._filtered_total - 1)
|
|
770
|
+
self._render_table(
|
|
771
|
+
preferred_cursor_row=preferred_cursor_row,
|
|
772
|
+
preferred_session_id=preferred_session_id,
|
|
773
|
+
prefer_live_selection=False,
|
|
774
|
+
source_sessions=optimistic_sessions,
|
|
775
|
+
)
|
|
776
|
+
# The next page's first session moves up into this one.
|
|
777
|
+
self._request_page(
|
|
778
|
+
self._page, preferred_cursor_row=preferred_cursor_row, preferred_session_id=preferred_session_id
|
|
779
|
+
)
|