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
|
+
"""Mutation tracking pipeline for tool event middleware.
|
|
4
|
+
|
|
5
|
+
Provides before/after orchestration helpers shared by both
|
|
6
|
+
``ToolEventMiddleware`` and ``SubAgentEventMiddleware``, eliminating
|
|
7
|
+
duplicated mutation-tracking code.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import asyncio
|
|
13
|
+
import logging
|
|
14
|
+
import os
|
|
15
|
+
import threading
|
|
16
|
+
import time
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
from typing import TYPE_CHECKING
|
|
19
|
+
|
|
20
|
+
from chrys.foundation.platform.paths import resolve_workspace_path
|
|
21
|
+
from chrys.service.mutations.coordination import canonical_key, fold_case
|
|
22
|
+
from chrys.service.mutations.tool_names import _FILE_TOOLS, _IMPLICIT_WRITE_TOOLS
|
|
23
|
+
from chrys.service.mutations.trace import (
|
|
24
|
+
allocate_trace_file,
|
|
25
|
+
discard_trace_file,
|
|
26
|
+
parse_trace_write_keys,
|
|
27
|
+
shell_trace_argv,
|
|
28
|
+
)
|
|
29
|
+
from chrys.service.mutations.types import (
|
|
30
|
+
FileHashDiff,
|
|
31
|
+
FileMutationTextSnapshot,
|
|
32
|
+
MutationProvenance,
|
|
33
|
+
MutationSource,
|
|
34
|
+
SnapshotSkipReason,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
if TYPE_CHECKING:
|
|
38
|
+
from contextvars import Token
|
|
39
|
+
|
|
40
|
+
from chrys.service.mutations.coordination import MutationCoordinator
|
|
41
|
+
from chrys.service.mutations.git_calibrator import GitDiffCalibrator
|
|
42
|
+
from chrys.service.mutations.tracker import MutationTracker
|
|
43
|
+
from chrys.service.mutations.types import FileMutation, MutationOp
|
|
44
|
+
|
|
45
|
+
logger = logging.getLogger(__name__)
|
|
46
|
+
|
|
47
|
+
_MAX_SHELL_CHILD_SNAPSHOT_FILES = 500
|
|
48
|
+
_MAX_SHELL_CHILD_SNAPSHOT_BYTES = 64 * 1024 * 1024
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# ---------------------------------------------------------------------------
|
|
52
|
+
# Pipeline dataclasses
|
|
53
|
+
# ---------------------------------------------------------------------------
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class MutationContext:
|
|
58
|
+
"""Pre-execution mutation tracking state captured by ``prepare_mutation_tracking``."""
|
|
59
|
+
|
|
60
|
+
file_mutation: FileMutation | None = None
|
|
61
|
+
# True for calls whose writes only implicit detection can observe
|
|
62
|
+
# (shell / implicit write tools). Every artifact below is optional —
|
|
63
|
+
# coordination off, non-Git cwd, no heuristic targets, fsatrace
|
|
64
|
+
# unavailable can all hold at once — so an abort after execution may
|
|
65
|
+
# have started keys "detection died with the call" off this flag,
|
|
66
|
+
# not off which artifacts happened to arm.
|
|
67
|
+
implicit_detection: bool = False
|
|
68
|
+
shell_scan_before: dict | None = None
|
|
69
|
+
git_calibrator: GitDiffCalibrator | None = None
|
|
70
|
+
# Cross-session coordination: the observation window advertised for
|
|
71
|
+
# this call (shell/implicit only) and when it was opened. The open
|
|
72
|
+
# timestamp doubles as the write-interval lower bound for every
|
|
73
|
+
# window-detected mutation.
|
|
74
|
+
window_id: str | None = None
|
|
75
|
+
window_opened_at: float | None = None
|
|
76
|
+
workspace_cwd: str | None = None
|
|
77
|
+
# Layer 3: per-call fsatrace artifacts. ``trace_token`` restores the
|
|
78
|
+
# shell wrapper contextvar and must be reset in the context that set
|
|
79
|
+
# it — the async finalize on the normal path, the sync abort on the
|
|
80
|
+
# cancel/reject paths (all three run in the middleware's context).
|
|
81
|
+
trace_file: str | None = None
|
|
82
|
+
trace_token: Token[tuple[str, ...] | None] | None = None
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@dataclass
|
|
86
|
+
class MutationResult:
|
|
87
|
+
"""Post-execution mutation snapshots produced by ``finalize_mutation_tracking``."""
|
|
88
|
+
|
|
89
|
+
file_snapshot: tuple[str, str] | None = None
|
|
90
|
+
# Set only when ``file_snapshot`` was captured; consumers use both
|
|
91
|
+
# fields together to preserve create/modify/delete semantics.
|
|
92
|
+
file_operation: str | None = None
|
|
93
|
+
file_bytes_changed: bool | None = None
|
|
94
|
+
file_hashes: FileHashDiff | None = None
|
|
95
|
+
shell_snapshots: dict[str, FileMutationTextSnapshot] | None = None
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@dataclass(frozen=True)
|
|
99
|
+
class _ShellObservation:
|
|
100
|
+
"""One per-command candidate collected before tracker recording."""
|
|
101
|
+
|
|
102
|
+
path: str
|
|
103
|
+
operation: MutationOp
|
|
104
|
+
before_data: bytes | None = None
|
|
105
|
+
calibrated: bool = False
|
|
106
|
+
# Set when the snapshot policy withheld the before content (oversized
|
|
107
|
+
# old blob): the event is still recorded, only the backup is missing.
|
|
108
|
+
before_skip: SnapshotSkipReason | None = None
|
|
109
|
+
before_size: int | None = None
|
|
110
|
+
# The old endpoint was a symlink (Git mode 120000); its blob is the
|
|
111
|
+
# link's target text. before_symlink_dir is its kind proven from
|
|
112
|
+
# the old tree (None = unprovable).
|
|
113
|
+
before_symlink: bool = False
|
|
114
|
+
before_symlink_dir: bool | None = None
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
# ---------------------------------------------------------------------------
|
|
118
|
+
# Low-level helpers (moved from module-level in old middleware.py)
|
|
119
|
+
# ---------------------------------------------------------------------------
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _track_file_mutation_before(
|
|
123
|
+
tracker: MutationTracker,
|
|
124
|
+
tool_name: str,
|
|
125
|
+
args: dict,
|
|
126
|
+
call_id: str,
|
|
127
|
+
workspace_cwd: str | None = None,
|
|
128
|
+
) -> FileMutation | None:
|
|
129
|
+
"""Snapshot file state before write_file/edit_file execution."""
|
|
130
|
+
import os
|
|
131
|
+
|
|
132
|
+
from chrys.service.mutations.types import MutationOp, MutationSource
|
|
133
|
+
|
|
134
|
+
file_path = args.get("path", "")
|
|
135
|
+
if not file_path:
|
|
136
|
+
return None
|
|
137
|
+
resolved = resolve_workspace_path(file_path, base_cwd=workspace_cwd)
|
|
138
|
+
if tool_name == "write_file":
|
|
139
|
+
op = MutationOp.CREATE if not os.path.exists(resolved) else MutationOp.MODIFY
|
|
140
|
+
source = MutationSource.WRITE_FILE
|
|
141
|
+
else:
|
|
142
|
+
op = MutationOp.MODIFY
|
|
143
|
+
source = MutationSource.EDIT_FILE
|
|
144
|
+
return tracker.record(resolved, op, source, call_id)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _track_shell_scan_before(tracker: MutationTracker, args: dict, workspace_cwd: str | None = None) -> dict | None:
|
|
148
|
+
"""Pre-snapshot likely mutation targets before shell command execution."""
|
|
149
|
+
from chrys.service.mutations.detector import ShellMutationDetector
|
|
150
|
+
from chrys.service.mutations.git_state import entry_display_path
|
|
151
|
+
from chrys.service.mutations.scanner import WorkspaceScanner
|
|
152
|
+
|
|
153
|
+
command = args.get("command", "")
|
|
154
|
+
raw_cwd = args.get("working_dir") or args.get("cwd") or workspace_cwd
|
|
155
|
+
cwd = resolve_workspace_path(raw_cwd, base_cwd=workspace_cwd) if raw_cwd else resolve_workspace_path(".")
|
|
156
|
+
|
|
157
|
+
targets = ShellMutationDetector.detect_targets(command, cwd)
|
|
158
|
+
target_paths = [entry_display_path(path) for path, _ in targets]
|
|
159
|
+
|
|
160
|
+
if not target_paths:
|
|
161
|
+
return None
|
|
162
|
+
|
|
163
|
+
# Keep a directory alias's children attached to the directory observed
|
|
164
|
+
# before execution. Unlinking/retargeting the alias does not delete them.
|
|
165
|
+
scan_targets = [os.path.realpath(path) if os.path.isdir(path) else path for path in target_paths]
|
|
166
|
+
scanner = WorkspaceScanner(cwd)
|
|
167
|
+
try:
|
|
168
|
+
scan = scanner.scan_paths(scan_targets)
|
|
169
|
+
except OSError:
|
|
170
|
+
logger.debug("Shell before-scan failed", exc_info=True)
|
|
171
|
+
tracker.mark_detection_truncated()
|
|
172
|
+
return None
|
|
173
|
+
# Explicit file/missing/link targets refresh the hash chain independently
|
|
174
|
+
# of the child budget. save() validates their existence; directory links
|
|
175
|
+
# need their own entry backup as well as their children's stable scan.
|
|
176
|
+
explicit_paths = dict.fromkeys(path for path in target_paths if os.path.islink(path) or not os.path.isdir(path))
|
|
177
|
+
children = {path: entry for path, entry in scan.items() if path not in explicit_paths}
|
|
178
|
+
if (
|
|
179
|
+
len(children) > _MAX_SHELL_CHILD_SNAPSHOT_FILES
|
|
180
|
+
or sum(entry.size for entry in children.values()) > _MAX_SHELL_CHILD_SNAPSHOT_BYTES
|
|
181
|
+
):
|
|
182
|
+
# Never replace a skipped before backup with a post-command read.
|
|
183
|
+
# Drop directory observations at both endpoints; Git calibration can
|
|
184
|
+
# still supply independently verified changes within its own limits.
|
|
185
|
+
tracker.mark_detection_truncated()
|
|
186
|
+
scan_targets = [path for path in scan_targets if path in explicit_paths and not os.path.isdir(path)]
|
|
187
|
+
scan = {path: entry for path, entry in scan.items() if path in scan_targets}
|
|
188
|
+
tracker.pre_snapshot(list(dict.fromkeys([*explicit_paths, *scan])))
|
|
189
|
+
return {"scan": scan, "targets": scan_targets, "cwd": cwd}
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _track_shell_scan_after(before_info: dict) -> list[_ShellObservation]:
|
|
193
|
+
"""Collect targeted file changes after shell command execution."""
|
|
194
|
+
from chrys.service.mutations.scanner import WorkspaceScanner
|
|
195
|
+
|
|
196
|
+
target_paths = before_info["targets"]
|
|
197
|
+
cwd = before_info["cwd"]
|
|
198
|
+
|
|
199
|
+
scanner = WorkspaceScanner(cwd)
|
|
200
|
+
after_scan = scanner.scan_paths(target_paths)
|
|
201
|
+
changes = WorkspaceScanner.diff(before_info["scan"], after_scan)
|
|
202
|
+
|
|
203
|
+
return [_ShellObservation(path=path, operation=operation) for path, operation in changes]
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _git_calibration_scope_paths(cwd: str, workspace_cwd: str | None) -> list[str]:
|
|
207
|
+
"""Scope git calibration to the primary workspace and the command cwd."""
|
|
208
|
+
scope_paths: list[str] = []
|
|
209
|
+
seen: set[str] = set()
|
|
210
|
+
for path in (workspace_cwd, cwd):
|
|
211
|
+
if not path:
|
|
212
|
+
continue
|
|
213
|
+
normalized = os.path.normpath(os.path.abspath(path))
|
|
214
|
+
if normalized in seen:
|
|
215
|
+
continue
|
|
216
|
+
seen.add(normalized)
|
|
217
|
+
scope_paths.append(normalized)
|
|
218
|
+
return scope_paths
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
async def _track_git_capture_before(
|
|
222
|
+
tracker: MutationTracker,
|
|
223
|
+
args: dict,
|
|
224
|
+
workspace_cwd: str | None = None,
|
|
225
|
+
) -> GitDiffCalibrator | None:
|
|
226
|
+
"""Capture git state before a shell command for implicit change detection."""
|
|
227
|
+
from chrys.service.mutations.git_calibrator import GitDiffCalibrator
|
|
228
|
+
|
|
229
|
+
raw_cwd = args.get("working_dir") or args.get("cwd") or workspace_cwd
|
|
230
|
+
cwd = resolve_workspace_path(raw_cwd, base_cwd=workspace_cwd) if raw_cwd else resolve_workspace_path(".")
|
|
231
|
+
|
|
232
|
+
def _capture() -> GitDiffCalibrator | None:
|
|
233
|
+
repo_root = GitDiffCalibrator.find_repo_root(cwd)
|
|
234
|
+
if repo_root is None:
|
|
235
|
+
return None
|
|
236
|
+
cal = GitDiffCalibrator(
|
|
237
|
+
repo_root,
|
|
238
|
+
scope_paths=_git_calibration_scope_paths(cwd, workspace_cwd),
|
|
239
|
+
max_snapshot_file_bytes=tracker.store.policy.max_file_bytes,
|
|
240
|
+
)
|
|
241
|
+
dirty_files = cal.capture_before()
|
|
242
|
+
if dirty_files:
|
|
243
|
+
tracker.pre_snapshot(dirty_files)
|
|
244
|
+
return cal
|
|
245
|
+
|
|
246
|
+
try:
|
|
247
|
+
return await asyncio.get_running_loop().run_in_executor(None, _capture)
|
|
248
|
+
except Exception:
|
|
249
|
+
logger.debug("Git calibration capture_before failed", exc_info=True)
|
|
250
|
+
tracker.mark_detection_truncated()
|
|
251
|
+
return None
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
async def _track_git_calibrate_after(
|
|
255
|
+
calibrator: GitDiffCalibrator,
|
|
256
|
+
same_call_paths: set[str],
|
|
257
|
+
) -> list[_ShellObservation]:
|
|
258
|
+
"""Collect Git candidates not emitted by this call's targeted detector."""
|
|
259
|
+
|
|
260
|
+
def _calibrate() -> list[_ShellObservation]:
|
|
261
|
+
return [
|
|
262
|
+
_ShellObservation(
|
|
263
|
+
change.path,
|
|
264
|
+
change.operation,
|
|
265
|
+
change.before_data,
|
|
266
|
+
calibrated=True,
|
|
267
|
+
before_skip=change.before_skip,
|
|
268
|
+
before_size=change.before_size,
|
|
269
|
+
before_symlink=change.before_symlink,
|
|
270
|
+
before_symlink_dir=change.before_symlink_dir,
|
|
271
|
+
)
|
|
272
|
+
for change in calibrator.detect_implicit_changes(same_call_paths)
|
|
273
|
+
]
|
|
274
|
+
|
|
275
|
+
try:
|
|
276
|
+
return await asyncio.get_running_loop().run_in_executor(None, _calibrate)
|
|
277
|
+
except Exception:
|
|
278
|
+
logger.debug("Git calibration detect_implicit failed", exc_info=True)
|
|
279
|
+
calibrator.mark_detection_truncated()
|
|
280
|
+
return []
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def _observation_key(path: str) -> str:
|
|
284
|
+
"""Merge key that resolves the directory route but not the entry itself.
|
|
285
|
+
|
|
286
|
+
Resolving the directory keeps aliased routes to one file merged
|
|
287
|
+
(``/tmp`` vs ``/private/tmp``, symlinked parents), while the lexical
|
|
288
|
+
final component keeps a symlink and its destination as the distinct
|
|
289
|
+
endpoints they are — ``canonical_key`` would collapse a retargeted
|
|
290
|
+
link onto the file it points at and silently drop one observation.
|
|
291
|
+
"""
|
|
292
|
+
parent, name = os.path.split(os.path.normpath(os.path.abspath(path)))
|
|
293
|
+
try:
|
|
294
|
+
resolved = os.path.realpath(parent)
|
|
295
|
+
except OSError:
|
|
296
|
+
resolved = parent
|
|
297
|
+
return fold_case(os.path.join(resolved, name))
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def _record_shell_observations(
|
|
301
|
+
tracker: MutationTracker,
|
|
302
|
+
observations: list[_ShellObservation],
|
|
303
|
+
call_id: str,
|
|
304
|
+
) -> list[FileMutation]:
|
|
305
|
+
"""Merge one command's observations and record every resulting endpoint."""
|
|
306
|
+
from chrys.service.mutations.types import MutationSource
|
|
307
|
+
|
|
308
|
+
merged: dict[str, _ShellObservation] = {}
|
|
309
|
+
for observation in observations:
|
|
310
|
+
key = _observation_key(observation.path)
|
|
311
|
+
if key not in merged or (not observation.calibrated and merged[key].calibrated):
|
|
312
|
+
merged[key] = observation
|
|
313
|
+
|
|
314
|
+
mutations: list[FileMutation] = []
|
|
315
|
+
for observation in merged.values():
|
|
316
|
+
if observation.calibrated:
|
|
317
|
+
mutation = tracker.record_calibrated(
|
|
318
|
+
observation.path,
|
|
319
|
+
observation.operation,
|
|
320
|
+
observation.before_data,
|
|
321
|
+
call_id,
|
|
322
|
+
before_skip=observation.before_skip,
|
|
323
|
+
before_size=observation.before_size,
|
|
324
|
+
before_symlink=observation.before_symlink,
|
|
325
|
+
before_symlink_dir=observation.before_symlink_dir,
|
|
326
|
+
)
|
|
327
|
+
else:
|
|
328
|
+
mutation = tracker.record(
|
|
329
|
+
observation.path,
|
|
330
|
+
observation.operation,
|
|
331
|
+
MutationSource.SHELL,
|
|
332
|
+
call_id,
|
|
333
|
+
)
|
|
334
|
+
if mutation is not None:
|
|
335
|
+
mutations.append(mutation)
|
|
336
|
+
return mutations
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def _read_file_snapshot(tracker: MutationTracker, mutation: FileMutation) -> tuple[str, str]:
|
|
340
|
+
"""Read before/after blob content for a file mutation.
|
|
341
|
+
|
|
342
|
+
Returns ``(before_text, after_text)``. Called in an executor thread.
|
|
343
|
+
"""
|
|
344
|
+
from chrys.foundation.text.encoding import decode_bytes
|
|
345
|
+
|
|
346
|
+
store = tracker.store
|
|
347
|
+
before_bytes = store.read_blob(mutation.before_hash) if mutation.before_hash else None
|
|
348
|
+
after_bytes = store.read_blob(mutation.after_hash) if mutation.after_hash else None
|
|
349
|
+
before_text = decode_bytes(before_bytes) if before_bytes else ""
|
|
350
|
+
after_text = decode_bytes(after_bytes) if after_bytes else ""
|
|
351
|
+
return (before_text, after_text)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def _content_changed(
|
|
355
|
+
before_hash: str | None,
|
|
356
|
+
after_hash: str | None,
|
|
357
|
+
before_skip: SnapshotSkipReason | None,
|
|
358
|
+
after_skip: SnapshotSkipReason | None,
|
|
359
|
+
) -> bool:
|
|
360
|
+
"""Whether a mutation changed file content, given possibly-withheld backups.
|
|
361
|
+
|
|
362
|
+
Hash inequality proves change; a policy skip on either side means
|
|
363
|
+
change cannot be ruled out (the content was never backed up, so both
|
|
364
|
+
hashes read ``None``) — report changed rather than silently dropping
|
|
365
|
+
a real edit as a no-op.
|
|
366
|
+
"""
|
|
367
|
+
if before_skip is not None or after_skip is not None:
|
|
368
|
+
return True
|
|
369
|
+
return before_hash != after_hash
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def _read_shell_snapshots(
|
|
373
|
+
tracker: MutationTracker, mutations: list[FileMutation]
|
|
374
|
+
) -> dict[str, FileMutationTextSnapshot]:
|
|
375
|
+
"""Read before/after blob content for shell-detected mutations.
|
|
376
|
+
|
|
377
|
+
Returns decoded snapshot payloads keyed by path. Collapses
|
|
378
|
+
multi-mutations per path, preserving the first ``before_text``,
|
|
379
|
+
first ``before_hash``, and first operation. Called in an executor
|
|
380
|
+
thread.
|
|
381
|
+
|
|
382
|
+
Mutations known ``FOREIGN`` at this point are not surfaced as live
|
|
383
|
+
rows (they are a peer session's work), but a surviving local row on
|
|
384
|
+
a foreign-touched path is marked ``contested`` — mixed paths keep
|
|
385
|
+
the badge. ``provenance`` collapses sticky-assumed and
|
|
386
|
+
``contested`` sticky-True across multi-mutations.
|
|
387
|
+
"""
|
|
388
|
+
foreign_touched: set[str] = set()
|
|
389
|
+
for m in mutations:
|
|
390
|
+
if m.provenance is MutationProvenance.FOREIGN:
|
|
391
|
+
foreign_touched.add(m.path)
|
|
392
|
+
if m.old_path:
|
|
393
|
+
foreign_touched.add(m.old_path)
|
|
394
|
+
|
|
395
|
+
result: dict[str, FileMutationTextSnapshot] = {}
|
|
396
|
+
for m in mutations:
|
|
397
|
+
if m.provenance is MutationProvenance.FOREIGN:
|
|
398
|
+
continue
|
|
399
|
+
provenance = m.provenance.value if m.provenance else ""
|
|
400
|
+
contested = m.contested or m.path in foreign_touched
|
|
401
|
+
before_text, after_text = _read_file_snapshot(tracker, m)
|
|
402
|
+
# Preserve the first before_text per path (collapse multi-mutations)
|
|
403
|
+
if m.path in result:
|
|
404
|
+
original = result[m.path]
|
|
405
|
+
source = (
|
|
406
|
+
MutationSource.IMPLICIT.value
|
|
407
|
+
if original.source == MutationSource.IMPLICIT.value or m.source is MutationSource.IMPLICIT
|
|
408
|
+
else original.source or m.source.value
|
|
409
|
+
)
|
|
410
|
+
if MutationProvenance.ASSUMED.value in (original.provenance, provenance):
|
|
411
|
+
provenance = MutationProvenance.ASSUMED.value
|
|
412
|
+
result[m.path] = FileMutationTextSnapshot(
|
|
413
|
+
before_text=original.before_text,
|
|
414
|
+
after_text=after_text,
|
|
415
|
+
operation=original.operation,
|
|
416
|
+
bytes_changed=_content_changed(original.before_hash, m.after_hash, original.before_skip, m.after_skip),
|
|
417
|
+
source=source,
|
|
418
|
+
before_hash=original.before_hash,
|
|
419
|
+
after_hash=m.after_hash,
|
|
420
|
+
before_skip=original.before_skip,
|
|
421
|
+
after_skip=m.after_skip,
|
|
422
|
+
provenance=provenance,
|
|
423
|
+
contested=original.contested or contested,
|
|
424
|
+
)
|
|
425
|
+
else:
|
|
426
|
+
result[m.path] = FileMutationTextSnapshot(
|
|
427
|
+
before_text=before_text,
|
|
428
|
+
after_text=after_text,
|
|
429
|
+
operation=m.operation.value,
|
|
430
|
+
bytes_changed=_content_changed(m.before_hash, m.after_hash, m.before_skip, m.after_skip),
|
|
431
|
+
source=m.source.value,
|
|
432
|
+
before_hash=m.before_hash,
|
|
433
|
+
after_hash=m.after_hash,
|
|
434
|
+
before_skip=m.before_skip,
|
|
435
|
+
after_skip=m.after_skip,
|
|
436
|
+
provenance=provenance,
|
|
437
|
+
contested=contested,
|
|
438
|
+
)
|
|
439
|
+
return result
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
# ---------------------------------------------------------------------------
|
|
443
|
+
# High-level pipeline (shared by ToolEventMiddleware / SubAgentEventMiddleware)
|
|
444
|
+
# ---------------------------------------------------------------------------
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
async def prepare_mutation_tracking(
|
|
448
|
+
tracker: MutationTracker | None,
|
|
449
|
+
tool_name: str,
|
|
450
|
+
args: dict,
|
|
451
|
+
call_id: str,
|
|
452
|
+
is_shell: bool,
|
|
453
|
+
workspace_cwd: str | None = None,
|
|
454
|
+
coordinator: MutationCoordinator | None = None,
|
|
455
|
+
) -> MutationContext:
|
|
456
|
+
"""Capture pre-execution mutation state.
|
|
457
|
+
|
|
458
|
+
Call this before publishing the start event and executing the tool.
|
|
459
|
+
"""
|
|
460
|
+
ctx = MutationContext(workspace_cwd=workspace_cwd)
|
|
461
|
+
if tracker is None:
|
|
462
|
+
return ctx
|
|
463
|
+
|
|
464
|
+
ctx.implicit_detection = is_shell or tool_name in _IMPLICIT_WRITE_TOOLS
|
|
465
|
+
loop = asyncio.get_running_loop()
|
|
466
|
+
|
|
467
|
+
# Advertise the observation window FIRST — before git capture, shell
|
|
468
|
+
# pre-scan, or any snapshot — so the published window is a superset
|
|
469
|
+
# of the observation. A peer write in a pre-scan gap would be
|
|
470
|
+
# attributed by the diff yet never overlap our interval otherwise.
|
|
471
|
+
if coordinator is not None and (is_shell or tool_name in _IMPLICIT_WRITE_TOOLS):
|
|
472
|
+
raw_cwd = args.get("working_dir") or args.get("cwd") or workspace_cwd
|
|
473
|
+
cwd = resolve_workspace_path(raw_cwd, base_cwd=workspace_cwd) if raw_cwd else resolve_workspace_path(".")
|
|
474
|
+
scope = _git_calibration_scope_paths(cwd, workspace_cwd)
|
|
475
|
+
ctx.window_opened_at = time.time()
|
|
476
|
+
# A cancellation landing exactly on this await abandons the
|
|
477
|
+
# executor call, but the thread may still open (or already have
|
|
478
|
+
# opened) the window — with the id lost to every abort path, it
|
|
479
|
+
# would advertise a live command until shutdown. The handshake
|
|
480
|
+
# makes exactly one side close it: the thread publishes the id
|
|
481
|
+
# under the lock unless the await was abandoned first, in which
|
|
482
|
+
# case the thread closes its own window.
|
|
483
|
+
window_lock = threading.Lock()
|
|
484
|
+
window_state: dict[str, str | bool | None] = {"id": None, "abandoned": False}
|
|
485
|
+
|
|
486
|
+
def _open_window() -> str | None:
|
|
487
|
+
wid = coordinator.open_window(scope, fallback_root=workspace_cwd)
|
|
488
|
+
with window_lock:
|
|
489
|
+
if not window_state["abandoned"]:
|
|
490
|
+
window_state["id"] = wid
|
|
491
|
+
return wid
|
|
492
|
+
coordinator.close_window(wid)
|
|
493
|
+
return None
|
|
494
|
+
|
|
495
|
+
try:
|
|
496
|
+
ctx.window_id = await loop.run_in_executor(None, _open_window)
|
|
497
|
+
except BaseException:
|
|
498
|
+
with window_lock:
|
|
499
|
+
window_state["abandoned"] = True
|
|
500
|
+
orphaned = window_state["id"]
|
|
501
|
+
if isinstance(orphaned, str):
|
|
502
|
+
try:
|
|
503
|
+
coordinator.close_window(orphaned)
|
|
504
|
+
except Exception:
|
|
505
|
+
logger.debug("Orphaned window close failed", exc_info=True)
|
|
506
|
+
raise
|
|
507
|
+
|
|
508
|
+
# Everything below the window open runs guarded: a cancellation
|
|
509
|
+
# landing on any of these awaits would propagate before the caller
|
|
510
|
+
# ever owns the MutationContext, so no middleware abort could reach
|
|
511
|
+
# the just-opened window (or the armed trace wrapper) — clean up
|
|
512
|
+
# here and re-raise.
|
|
513
|
+
try:
|
|
514
|
+
# Layer 3: allocate the fsatrace write-trace and hand the wrapper
|
|
515
|
+
# argv to the shell tool via contextvar (tool schema and args stay
|
|
516
|
+
# untouched). ShellTools has no session-dir access — this is the
|
|
517
|
+
# handoff point. The first call pays a one-shot binary probe.
|
|
518
|
+
if is_shell:
|
|
519
|
+
allocation = await loop.run_in_executor(None, allocate_trace_file, tracker.store.mutations_dir)
|
|
520
|
+
if allocation is not None:
|
|
521
|
+
wrapper_prefix, trace_file = allocation
|
|
522
|
+
ctx.trace_file = trace_file
|
|
523
|
+
ctx.trace_token = shell_trace_argv.set(tuple(wrapper_prefix))
|
|
524
|
+
|
|
525
|
+
# File mutation snapshot (write_file / edit_file)
|
|
526
|
+
if tool_name in _FILE_TOOLS:
|
|
527
|
+
ctx.file_mutation = await loop.run_in_executor(
|
|
528
|
+
None, _track_file_mutation_before, tracker, tool_name, args, call_id, workspace_cwd
|
|
529
|
+
)
|
|
530
|
+
if ctx.file_mutation is not None:
|
|
531
|
+
# Tight write-interval lower bound: record() runs pre-write.
|
|
532
|
+
ctx.file_mutation.t_start = ctx.file_mutation.timestamp
|
|
533
|
+
|
|
534
|
+
# Git calibration + shell heuristic scan
|
|
535
|
+
if is_shell or tool_name in _IMPLICIT_WRITE_TOOLS:
|
|
536
|
+
ctx.git_calibrator = await _track_git_capture_before(tracker, args, workspace_cwd)
|
|
537
|
+
if is_shell:
|
|
538
|
+
ctx.shell_scan_before = await loop.run_in_executor(
|
|
539
|
+
None, _track_shell_scan_before, tracker, args, workspace_cwd
|
|
540
|
+
)
|
|
541
|
+
except BaseException:
|
|
542
|
+
abort_mutation_tracking(ctx, coordinator)
|
|
543
|
+
raise
|
|
544
|
+
|
|
545
|
+
return ctx
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
async def finalize_mutation_tracking(
|
|
549
|
+
tracker: MutationTracker | None,
|
|
550
|
+
ctx: MutationContext,
|
|
551
|
+
call_id: str,
|
|
552
|
+
coordinator: MutationCoordinator | None = None,
|
|
553
|
+
tool_errored: bool = False,
|
|
554
|
+
) -> MutationResult:
|
|
555
|
+
"""Run post-execution mutation detection and read snapshots.
|
|
556
|
+
|
|
557
|
+
Call this after tool execution completes (in the ``finally`` block).
|
|
558
|
+
Skipped when the tool was rejected by approval.
|
|
559
|
+
|
|
560
|
+
``tool_errored`` marks a tool whose call raised/failed: the mutation
|
|
561
|
+
record still exists (record-in-prepare + finalize-in-``finally``
|
|
562
|
+
leaves one even for failed calls), but chrys may not have authored
|
|
563
|
+
the observed delta — such write/edit mutations publish no claim.
|
|
564
|
+
"""
|
|
565
|
+
result = MutationResult()
|
|
566
|
+
if tracker is None:
|
|
567
|
+
return result
|
|
568
|
+
|
|
569
|
+
loop = asyncio.get_running_loop()
|
|
570
|
+
|
|
571
|
+
# Capture after-content for file mutations (for DiffView replay)
|
|
572
|
+
if ctx.file_mutation is not None:
|
|
573
|
+
await loop.run_in_executor(None, tracker.record_after, ctx.file_mutation)
|
|
574
|
+
# Tight write-interval upper bound: the disk write happened
|
|
575
|
+
# during tool execution, strictly before this stamp.
|
|
576
|
+
ctx.file_mutation.t_end = time.time()
|
|
577
|
+
snapshot = await loop.run_in_executor(None, _read_file_snapshot, tracker, ctx.file_mutation)
|
|
578
|
+
if snapshot:
|
|
579
|
+
result.file_snapshot = snapshot
|
|
580
|
+
result.file_operation = ctx.file_mutation.operation.value
|
|
581
|
+
result.file_bytes_changed = _content_changed(
|
|
582
|
+
ctx.file_mutation.before_hash,
|
|
583
|
+
ctx.file_mutation.after_hash,
|
|
584
|
+
ctx.file_mutation.before_skip,
|
|
585
|
+
ctx.file_mutation.after_skip,
|
|
586
|
+
)
|
|
587
|
+
result.file_hashes = FileHashDiff(
|
|
588
|
+
before=ctx.file_mutation.before_hash,
|
|
589
|
+
after=ctx.file_mutation.after_hash,
|
|
590
|
+
before_skip=ctx.file_mutation.before_skip,
|
|
591
|
+
after_skip=ctx.file_mutation.after_skip,
|
|
592
|
+
)
|
|
593
|
+
|
|
594
|
+
# Collect per-command observations before recording. Targeted scan rows
|
|
595
|
+
# own the worktree endpoint; Git fills only paths that scan did not see.
|
|
596
|
+
observations: list[_ShellObservation] = []
|
|
597
|
+
if ctx.shell_scan_before is not None:
|
|
598
|
+
try:
|
|
599
|
+
observations = await loop.run_in_executor(None, _track_shell_scan_after, ctx.shell_scan_before)
|
|
600
|
+
except OSError:
|
|
601
|
+
logger.debug("Shell after-scan failed", exc_info=True)
|
|
602
|
+
tracker.mark_detection_truncated()
|
|
603
|
+
|
|
604
|
+
if ctx.git_calibrator is not None:
|
|
605
|
+
same_call_paths = {observation.path for observation in observations}
|
|
606
|
+
observations.extend(await _track_git_calibrate_after(ctx.git_calibrator, same_call_paths))
|
|
607
|
+
if ctx.git_calibrator.detection_truncated:
|
|
608
|
+
tracker.mark_detection_truncated()
|
|
609
|
+
|
|
610
|
+
shell_mutations = await loop.run_in_executor(None, _record_shell_observations, tracker, observations, call_id)
|
|
611
|
+
if shell_mutations:
|
|
612
|
+
logger.debug("Mutation detection: %d file changes from shell command", len(shell_mutations))
|
|
613
|
+
|
|
614
|
+
# Window-detected mutations get the full observation window as their
|
|
615
|
+
# write interval — the point timestamp sits post-window and must not
|
|
616
|
+
# be used for overlap matching.
|
|
617
|
+
if shell_mutations and ctx.window_opened_at is not None:
|
|
618
|
+
stamp = time.time()
|
|
619
|
+
for m in shell_mutations:
|
|
620
|
+
m.t_start = ctx.window_opened_at
|
|
621
|
+
m.t_end = stamp
|
|
622
|
+
|
|
623
|
+
# Layer 3: positive trace evidence upgrades this call's window
|
|
624
|
+
# inferences to PROVEN — before persistence, display, or claims.
|
|
625
|
+
proven_shell: list[FileMutation] = []
|
|
626
|
+
if ctx.trace_file is not None:
|
|
627
|
+
proven_shell = await loop.run_in_executor(None, _apply_trace_upgrades, ctx, shell_mutations)
|
|
628
|
+
_release_trace_wrapper(ctx)
|
|
629
|
+
|
|
630
|
+
if coordinator is not None:
|
|
631
|
+
await loop.run_in_executor(
|
|
632
|
+
None, _coordinate_after_detection, tracker, ctx, coordinator, tool_errored, proven_shell
|
|
633
|
+
)
|
|
634
|
+
|
|
635
|
+
if shell_mutations:
|
|
636
|
+
result.shell_snapshots = await loop.run_in_executor(None, _read_shell_snapshots, tracker, shell_mutations)
|
|
637
|
+
|
|
638
|
+
return result
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
def _coordinate_after_detection(
|
|
642
|
+
tracker: MutationTracker,
|
|
643
|
+
ctx: MutationContext,
|
|
644
|
+
coordinator: MutationCoordinator,
|
|
645
|
+
tool_errored: bool,
|
|
646
|
+
proven_shell: list[FileMutation],
|
|
647
|
+
) -> None:
|
|
648
|
+
"""Registry protocol tail of finalize: close window, claim, reclassify.
|
|
649
|
+
|
|
650
|
+
Post-detection first (the caller just ran it), then the window
|
|
651
|
+
closes and claims for proven mutations publish — write/edit claims
|
|
652
|
+
thus appear within milliseconds of the bytes hitting disk. Errors
|
|
653
|
+
never propagate: coordination must not break tool execution.
|
|
654
|
+
|
|
655
|
+
Trace-proven shell rows publish regardless of ``tool_errored``: the
|
|
656
|
+
positive trace IS the authorship evidence (a failing build still
|
|
657
|
+
wrote what it wrote). The tool-success gate exists only for
|
|
658
|
+
write/edit rows, whose authorship is otherwise assumed from the
|
|
659
|
+
observed delta.
|
|
660
|
+
"""
|
|
661
|
+
try:
|
|
662
|
+
if ctx.file_mutation is not None and not tool_errored:
|
|
663
|
+
coordinator.publish_claims([ctx.file_mutation], fallback_root=ctx.workspace_cwd)
|
|
664
|
+
if proven_shell:
|
|
665
|
+
coordinator.publish_claims(proven_shell, fallback_root=ctx.workspace_cwd)
|
|
666
|
+
coordinator.close_window(ctx.window_id)
|
|
667
|
+
coordinator.reclassify(tracker, fallback_root=ctx.workspace_cwd)
|
|
668
|
+
except Exception:
|
|
669
|
+
logger.debug("Mutation coordination failed in finalize", exc_info=True)
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
def _apply_trace_upgrades(ctx: MutationContext, shell_mutations: list[FileMutation]) -> list[FileMutation]:
|
|
673
|
+
"""Upgrade window inferences positively observed in this call's trace.
|
|
674
|
+
|
|
675
|
+
Evidence-only: matches against the rows detection already produced
|
|
676
|
+
and never adds mutations — out-of-scope writes (gitignored build
|
|
677
|
+
output) stay untracked by policy. A MOVE row matches on either
|
|
678
|
+
side. The trace file is deleted afterwards regardless of outcome,
|
|
679
|
+
and any failure degrades to "no upgrades". Returns the upgraded
|
|
680
|
+
rows (they become Layer-2 claims).
|
|
681
|
+
|
|
682
|
+
Claims ``ctx.trace_file`` (nulls it) at entry: a cancellation can
|
|
683
|
+
leave this executor call racing the middleware's sync abort, and on
|
|
684
|
+
Windows the abort's unlink can lose to our open handle — claiming
|
|
685
|
+
first means the abort skips the discard entirely and this
|
|
686
|
+
``finally`` keeps a path to delete after the handle closes.
|
|
687
|
+
"""
|
|
688
|
+
trace_file = ctx.trace_file
|
|
689
|
+
ctx.trace_file = None
|
|
690
|
+
upgraded: list[FileMutation] = []
|
|
691
|
+
if not trace_file:
|
|
692
|
+
# The abort claimed (and discarded) it first.
|
|
693
|
+
return upgraded
|
|
694
|
+
try:
|
|
695
|
+
candidates: dict[str, list[FileMutation]] = {}
|
|
696
|
+
for m in shell_mutations:
|
|
697
|
+
candidates.setdefault(canonical_key(m.path), []).append(m)
|
|
698
|
+
if m.old_path:
|
|
699
|
+
candidates.setdefault(canonical_key(m.old_path), []).append(m)
|
|
700
|
+
if candidates:
|
|
701
|
+
hit_keys = parse_trace_write_keys(trace_file, candidates.keys())
|
|
702
|
+
seen: set[int] = set()
|
|
703
|
+
for key in hit_keys:
|
|
704
|
+
for m in candidates[key]:
|
|
705
|
+
if id(m) in seen:
|
|
706
|
+
continue
|
|
707
|
+
seen.add(id(m))
|
|
708
|
+
m.provenance = MutationProvenance.PROVEN
|
|
709
|
+
upgraded.append(m)
|
|
710
|
+
except Exception:
|
|
711
|
+
logger.debug("Trace upgrade failed", exc_info=True)
|
|
712
|
+
finally:
|
|
713
|
+
discard_trace_file(trace_file)
|
|
714
|
+
return upgraded
|
|
715
|
+
|
|
716
|
+
|
|
717
|
+
def _release_trace_wrapper(ctx: MutationContext) -> None:
|
|
718
|
+
"""Reset the shell wrapper contextvar set by prepare (idempotent)."""
|
|
719
|
+
if ctx.trace_token is None:
|
|
720
|
+
return
|
|
721
|
+
try:
|
|
722
|
+
shell_trace_argv.reset(ctx.trace_token)
|
|
723
|
+
except ValueError:
|
|
724
|
+
# Defensive: reset from a different context — the var dies with
|
|
725
|
+
# the task's context anyway.
|
|
726
|
+
logger.debug("Trace wrapper token reset outside its context", exc_info=True)
|
|
727
|
+
ctx.trace_token = None
|
|
728
|
+
|
|
729
|
+
|
|
730
|
+
def abort_mutation_tracking(
|
|
731
|
+
ctx: MutationContext,
|
|
732
|
+
coordinator: MutationCoordinator | None,
|
|
733
|
+
*,
|
|
734
|
+
tracker: MutationTracker | None = None,
|
|
735
|
+
) -> None:
|
|
736
|
+
"""Sync cleanup for calls that skip the async finalize (cancel/reject).
|
|
737
|
+
|
|
738
|
+
When the agent task is cancelled (interrupt), the middleware cannot
|
|
739
|
+
run the async finalize — a cancelled task raises ``CancelledError``
|
|
740
|
+
at its next ``await``; a tool rejected at approval never executed and
|
|
741
|
+
deliberately skips finalize — so this must stay synchronous. It
|
|
742
|
+
releases the Layer-3 trace wrapper (contextvar + trace file) and
|
|
743
|
+
closes the observation window; without the latter, a window opened
|
|
744
|
+
in prepare keeps advertising a live command in the registry until
|
|
745
|
+
shutdown, causing false peer-active warnings. The window end stamps
|
|
746
|
+
at the abort instant, which is exactly the upper bound of when an
|
|
747
|
+
interrupted tool could still have written. No claims publish
|
|
748
|
+
(authorship of any delta is unknowable here) and errors never
|
|
749
|
+
propagate.
|
|
750
|
+
|
|
751
|
+
``tracker`` is passed only from abort sites where the command may have
|
|
752
|
+
actually run (cancellation mid-execution or mid-finalize — never
|
|
753
|
+
approval rejection or the prepare phase): implicit detection died with
|
|
754
|
+
the call, so a write it performed before the interrupt would otherwise
|
|
755
|
+
vanish from the notice entirely — no mutation row, and absorbed by the
|
|
756
|
+
baseline the finalizer still captures. Marking the turn's detection
|
|
757
|
+
truncated surfaces that honestly. The gate is the call class
|
|
758
|
+
(``implicit_detection``), not which artifacts armed: an executed
|
|
759
|
+
shell with zero artifacts (coordination off, non-Git cwd, opaque
|
|
760
|
+
command, no fsatrace) has exactly the same blind spot. File tools
|
|
761
|
+
stay unmarked — their explicit mutation row survives the abort.
|
|
762
|
+
"""
|
|
763
|
+
if tracker is not None and ctx.implicit_detection:
|
|
764
|
+
try:
|
|
765
|
+
tracker.mark_detection_truncated()
|
|
766
|
+
except Exception:
|
|
767
|
+
logger.debug("Detection-truncated marking failed during abort", exc_info=True)
|
|
768
|
+
_release_trace_wrapper(ctx)
|
|
769
|
+
# Claim-then-discard, mirroring _apply_trace_upgrades: whichever side
|
|
770
|
+
# claims the path first owns its deletion; the other sees None.
|
|
771
|
+
trace_file = ctx.trace_file
|
|
772
|
+
ctx.trace_file = None
|
|
773
|
+
discard_trace_file(trace_file)
|
|
774
|
+
if coordinator is None or ctx.window_id is None:
|
|
775
|
+
return
|
|
776
|
+
try:
|
|
777
|
+
coordinator.close_window(ctx.window_id)
|
|
778
|
+
except Exception:
|
|
779
|
+
logger.debug("Mutation coordination window abort failed", exc_info=True)
|