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,896 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""Tool action projection, validation metrics and per-file change verification.
|
|
4
|
+
|
|
5
|
+
Actions are projected from tool lifecycles; action counts, edit/verify
|
|
6
|
+
cycles, validation metrics and file verification states are derived from them
|
|
7
|
+
here. Action classes and tool-outcome predicates come from ``classification``.
|
|
8
|
+
``findings``' unverified-change rule repeats this module's unverified-edit
|
|
9
|
+
selection (``model.verify_covers_edit`` against the last successful verify)
|
|
10
|
+
and takes its precision from the count here, so a change to which edits count
|
|
11
|
+
as verified must change both. File verification states order differently: a
|
|
12
|
+
verify in the file's last-change turn must start after every potential mutator
|
|
13
|
+
in that turn.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from collections import defaultdict
|
|
19
|
+
from collections.abc import Callable
|
|
20
|
+
from dataclasses import replace
|
|
21
|
+
from statistics import median
|
|
22
|
+
from threading import Event
|
|
23
|
+
from typing import Final, cast
|
|
24
|
+
|
|
25
|
+
from chrys.foundation.trajectory.event_types import SourceRefKind, ToolOutcome
|
|
26
|
+
from chrys.foundation.trajectory.ids import is_valid_analytics_id
|
|
27
|
+
from chrys.service.analytics._facts import (
|
|
28
|
+
_active,
|
|
29
|
+
_Endpoint,
|
|
30
|
+
_EventScope,
|
|
31
|
+
_Intermediate,
|
|
32
|
+
_Node,
|
|
33
|
+
_payload_int,
|
|
34
|
+
_payload_str,
|
|
35
|
+
_payload_value,
|
|
36
|
+
_ToolPayloadExtras,
|
|
37
|
+
)
|
|
38
|
+
from chrys.service.analytics._metric_ops import _cap_session_metric, _with_unresolved_precision
|
|
39
|
+
from chrys.service.analytics._session_projection import _SessionProjection
|
|
40
|
+
from chrys.service.analytics._timeline import _tool_context_for_start
|
|
41
|
+
from chrys.service.analytics.classification import (
|
|
42
|
+
KNOWN_TOOL_OUTCOMES,
|
|
43
|
+
classification_evidence_key,
|
|
44
|
+
classify_action,
|
|
45
|
+
parse_verify_commands,
|
|
46
|
+
tool_failed,
|
|
47
|
+
tool_succeeded,
|
|
48
|
+
)
|
|
49
|
+
from chrys.service.analytics.model import (
|
|
50
|
+
ActionClass,
|
|
51
|
+
ActionFunnel,
|
|
52
|
+
ActionOperation,
|
|
53
|
+
ChangeVerification,
|
|
54
|
+
ChangeVerificationRow,
|
|
55
|
+
ChangeVerificationState,
|
|
56
|
+
Metric,
|
|
57
|
+
Precision,
|
|
58
|
+
TurnAnalysis,
|
|
59
|
+
ValidationMetrics,
|
|
60
|
+
least_precision,
|
|
61
|
+
verify_covers_edit,
|
|
62
|
+
)
|
|
63
|
+
from chrys.service.analytics.reader import raise_if_cancelled as _check_cancelled
|
|
64
|
+
from chrys.service.mutations.types import FileHashDiff, parse_skip_reason
|
|
65
|
+
|
|
66
|
+
_WORD_LIST_HEURISTIC_REASON: Final = "one or more shell actions use the verification word-list heuristic"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _resolve_actions(
|
|
70
|
+
intermediate: _Intermediate,
|
|
71
|
+
inactive_ranges: tuple[tuple[int, int], ...],
|
|
72
|
+
turns: list[TurnAnalysis],
|
|
73
|
+
*,
|
|
74
|
+
session_projection: Callable[[], _SessionProjection],
|
|
75
|
+
verify_commands: str,
|
|
76
|
+
cancel_event: Event | None,
|
|
77
|
+
) -> tuple[ActionOperation, ...]:
|
|
78
|
+
turns_by_id = {turn.turn_id: turn for turn in turns}
|
|
79
|
+
commands = parse_verify_commands(verify_commands)
|
|
80
|
+
actions: list[ActionOperation] = []
|
|
81
|
+
for node in intermediate.nodes.get("tool.operation", {}).values():
|
|
82
|
+
_check_cancelled(cancel_event)
|
|
83
|
+
starts = [event for event in node.starts if _active(event.sequence, inactive_ranges) and not event.side_call]
|
|
84
|
+
finishes = [
|
|
85
|
+
event for event in node.finishes if _active(event.sequence, inactive_ranges) and not event.side_call
|
|
86
|
+
]
|
|
87
|
+
if len(starts) != 1 or starts[0].side_call or starts[0].turn_id not in turns_by_id:
|
|
88
|
+
continue
|
|
89
|
+
start = starts[0]
|
|
90
|
+
call_item_id = _payload_str(start.payload, "call_item_id")
|
|
91
|
+
argument_fingerprint = _payload_str(start.payload, "argument_fingerprint")
|
|
92
|
+
tool_kind = _payload_str(start.payload, "tool_kind")
|
|
93
|
+
tool_name = _payload_str(start.payload, "tool_name")
|
|
94
|
+
stable_key = classification_evidence_key(
|
|
95
|
+
call_item_id=call_item_id,
|
|
96
|
+
argument_fingerprint=argument_fingerprint,
|
|
97
|
+
)
|
|
98
|
+
command = session_projection().commands.get(call_item_id or "") if tool_kind == "shell" else None
|
|
99
|
+
classification, classification_precision, classification_reason = classify_action(
|
|
100
|
+
tool_kind,
|
|
101
|
+
command=command,
|
|
102
|
+
verify_commands=commands,
|
|
103
|
+
)
|
|
104
|
+
outcome, outcome_precision, outcome_reason, finish = _action_outcome(start, finishes)
|
|
105
|
+
context = _tool_context_for_start(node, start)
|
|
106
|
+
payload = _tool_payload_for_node(node, inactive_ranges, start.scope)
|
|
107
|
+
turn = turns_by_id[cast("str", start.turn_id)]
|
|
108
|
+
if turn.action_projection_precision is not Precision.EXACT:
|
|
109
|
+
outcome = None
|
|
110
|
+
outcome_precision = Precision.UNRESOLVED
|
|
111
|
+
outcome_reason = turn.action_projection_reason or "tool action projection is incomplete"
|
|
112
|
+
finish = None
|
|
113
|
+
actions.append(
|
|
114
|
+
ActionOperation(
|
|
115
|
+
evidence_key=stable_key,
|
|
116
|
+
occurrence_id=f"tool:{start.sequence}:{node.operation_id}",
|
|
117
|
+
operation_id=node.operation_id,
|
|
118
|
+
turn_id=turn.turn_id,
|
|
119
|
+
turn_number=turn.turn_number,
|
|
120
|
+
tool_name=tool_name,
|
|
121
|
+
tool_kind=tool_kind,
|
|
122
|
+
call_item_id=call_item_id,
|
|
123
|
+
argument_fingerprint=argument_fingerprint,
|
|
124
|
+
classification=classification,
|
|
125
|
+
classification_precision=classification_precision,
|
|
126
|
+
classification_reason=classification_reason,
|
|
127
|
+
outcome=outcome,
|
|
128
|
+
outcome_precision=outcome_precision,
|
|
129
|
+
start_sequence=start.sequence,
|
|
130
|
+
start_ns=start.monotonic_ns,
|
|
131
|
+
end_ns=finish.monotonic_ns if finish is not None and finish.monotonic_measurement else None,
|
|
132
|
+
end_sequence=finish.sequence if finish is not None else None,
|
|
133
|
+
outcome_reason=outcome_reason,
|
|
134
|
+
server_name=context.server_name if context is not None else None,
|
|
135
|
+
remote_name=context.remote_name if context is not None else None,
|
|
136
|
+
skill_name=context.skill_name if context is not None else None,
|
|
137
|
+
skill_revision=context.skill_revision if context is not None else None,
|
|
138
|
+
script_name=context.script_name if context is not None else None,
|
|
139
|
+
resource_name=context.resource_name if context is not None else None,
|
|
140
|
+
exit_code=_payload_int(finish.payload, "exit_code") if finish is not None else None,
|
|
141
|
+
payload_observed=payload is not None,
|
|
142
|
+
payload_bytes=payload.model_visible_bytes if payload is not None else None,
|
|
143
|
+
payload_token_estimate=payload.local_token_estimate if payload is not None else None,
|
|
144
|
+
payload_original_bytes=payload.original_bytes if payload is not None else None,
|
|
145
|
+
payload_truncated=payload.truncated if payload is not None else None,
|
|
146
|
+
payload_spilled=payload.spilled if payload is not None else None,
|
|
147
|
+
)
|
|
148
|
+
)
|
|
149
|
+
return tuple(sorted(actions, key=lambda action: (action.start_sequence, action.operation_id)))
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _action_outcome(
|
|
153
|
+
start: _Endpoint,
|
|
154
|
+
finishes: list[_Endpoint],
|
|
155
|
+
) -> tuple[str | None, Precision, str | None, _Endpoint | None]:
|
|
156
|
+
if not finishes:
|
|
157
|
+
return None, Precision.MISSING, "tool operation has no terminal event", None
|
|
158
|
+
if len(finishes) != 1:
|
|
159
|
+
return None, Precision.UNRESOLVED, "tool operation has more than one terminal event", None
|
|
160
|
+
finish = finishes[0]
|
|
161
|
+
if finish.scope != start.scope:
|
|
162
|
+
return None, Precision.UNRESOLVED, "tool lifecycle endpoints cross scope", None
|
|
163
|
+
if finish.sequence <= start.sequence or finish.monotonic_ns < start.monotonic_ns:
|
|
164
|
+
return None, Precision.UNRESOLVED, "tool lifecycle endpoints are not ordered", None
|
|
165
|
+
outcome = _payload_str(finish.payload, "outcome")
|
|
166
|
+
if outcome is None:
|
|
167
|
+
return None, Precision.MISSING, "tool terminal outcome is missing", finish
|
|
168
|
+
if outcome not in KNOWN_TOOL_OUTCOMES:
|
|
169
|
+
return outcome, Precision.UNRESOLVED, "tool terminal outcome is invalid", finish
|
|
170
|
+
return outcome, Precision.EXACT, None, finish
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _scope_matches(observed_scope: _EventScope, node_scope: _EventScope) -> bool:
|
|
174
|
+
return (
|
|
175
|
+
observed_scope.runtime_id == node_scope.runtime_id
|
|
176
|
+
and observed_scope.branch_id == node_scope.branch_id
|
|
177
|
+
and observed_scope.coverage_id == node_scope.coverage_id
|
|
178
|
+
and observed_scope.actor_id == node_scope.actor_id
|
|
179
|
+
and observed_scope.turn_id == node_scope.turn_id
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _tool_payload_for_node(
|
|
184
|
+
node: _Node,
|
|
185
|
+
inactive_ranges: tuple[tuple[int, int], ...],
|
|
186
|
+
scope: _EventScope,
|
|
187
|
+
) -> _ToolPayloadExtras | None:
|
|
188
|
+
extras = node.extras
|
|
189
|
+
if extras is None:
|
|
190
|
+
return None
|
|
191
|
+
active = [
|
|
192
|
+
item
|
|
193
|
+
for item in extras.payloads
|
|
194
|
+
if _active(item.sequence, inactive_ranges) and _scope_matches(item.scope, scope)
|
|
195
|
+
]
|
|
196
|
+
return active[0] if len(active) == 1 else None
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _action_count_metrics(
|
|
200
|
+
actions: tuple[ActionOperation, ...],
|
|
201
|
+
*,
|
|
202
|
+
projection_precision: Precision = Precision.EXACT,
|
|
203
|
+
projection_reason: str | None = None,
|
|
204
|
+
) -> dict[ActionClass, Metric]:
|
|
205
|
+
degraded_shell = any(
|
|
206
|
+
action.tool_kind == "shell" and action.classification_precision is Precision.UNRESOLVED for action in actions
|
|
207
|
+
)
|
|
208
|
+
estimated_shell = any(
|
|
209
|
+
action.tool_kind == "shell" and action.classification_precision is Precision.ESTIMATED for action in actions
|
|
210
|
+
)
|
|
211
|
+
metrics: dict[ActionClass, Metric] = {}
|
|
212
|
+
for action_class in ActionClass:
|
|
213
|
+
matching = [action for action in actions if action.classification is action_class]
|
|
214
|
+
precision = least_precision((projection_precision, *(action.classification_precision for action in matching)))
|
|
215
|
+
reason = None
|
|
216
|
+
if projection_precision is not Precision.EXACT:
|
|
217
|
+
reason = projection_reason or "tool action projection is incomplete"
|
|
218
|
+
elif action_class in {ActionClass.VERIFY, ActionClass.OTHER} and degraded_shell:
|
|
219
|
+
precision = Precision.UNRESOLVED
|
|
220
|
+
reason = "one or more shell command carriers are unavailable"
|
|
221
|
+
elif (
|
|
222
|
+
action_class in {ActionClass.VERIFY, ActionClass.OTHER} and estimated_shell and precision is Precision.EXACT
|
|
223
|
+
):
|
|
224
|
+
# The word-list heuristic decides between the verify and other
|
|
225
|
+
# buckets, so either count is only as precise as the heuristic
|
|
226
|
+
# even when every shell action landed in the opposite bucket.
|
|
227
|
+
precision = Precision.ESTIMATED
|
|
228
|
+
reason = _WORD_LIST_HEURISTIC_REASON
|
|
229
|
+
elif precision is Precision.ESTIMATED:
|
|
230
|
+
reason = _WORD_LIST_HEURISTIC_REASON
|
|
231
|
+
elif precision is Precision.UNRESOLVED:
|
|
232
|
+
reason = "one or more action classifications are unresolved"
|
|
233
|
+
metrics[action_class] = Metric(len(matching), precision, reason)
|
|
234
|
+
return metrics
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _validation_metrics(
|
|
238
|
+
actions: tuple[ActionOperation, ...],
|
|
239
|
+
turns: list[TurnAnalysis],
|
|
240
|
+
change_verification: ChangeVerification,
|
|
241
|
+
retry_amplification: Metric,
|
|
242
|
+
*,
|
|
243
|
+
cancel_event: Event | None = None,
|
|
244
|
+
) -> ValidationMetrics:
|
|
245
|
+
_check_cancelled(cancel_event)
|
|
246
|
+
projection_precision = least_precision(turn.action_projection_precision for turn in turns)
|
|
247
|
+
projection_reason = next(
|
|
248
|
+
(turn.action_projection_reason for turn in turns if turn.action_projection_reason is not None),
|
|
249
|
+
None,
|
|
250
|
+
)
|
|
251
|
+
counts = _action_count_metrics(
|
|
252
|
+
actions,
|
|
253
|
+
projection_precision=projection_precision,
|
|
254
|
+
projection_reason=projection_reason,
|
|
255
|
+
)
|
|
256
|
+
degraded_shell = any(
|
|
257
|
+
action.tool_kind == "shell" and action.classification_precision is Precision.UNRESOLVED for action in actions
|
|
258
|
+
)
|
|
259
|
+
estimated_shell = any(
|
|
260
|
+
action.tool_kind == "shell" and action.classification_precision is Precision.ESTIMATED for action in actions
|
|
261
|
+
)
|
|
262
|
+
verification_reason = "one or more shell command carriers are unavailable"
|
|
263
|
+
edits = [action for action in actions if action.classification is ActionClass.EDIT]
|
|
264
|
+
verifies = [action for action in actions if action.classification is ActionClass.VERIFY]
|
|
265
|
+
incomplete_outcomes = any(action.outcome_precision is not Precision.EXACT for action in actions)
|
|
266
|
+
unknown_outcomes = any(action.outcome == ToolOutcome.UNKNOWN for action in actions)
|
|
267
|
+
outcome_precision = Precision.UNRESOLVED if incomplete_outcomes or unknown_outcomes else Precision.EXACT
|
|
268
|
+
outcome_reason = (
|
|
269
|
+
next((action.outcome_reason for action in actions if action.outcome_reason is not None), None)
|
|
270
|
+
if incomplete_outcomes
|
|
271
|
+
else "one or more tool outcomes are unknown"
|
|
272
|
+
if unknown_outcomes
|
|
273
|
+
else None
|
|
274
|
+
)
|
|
275
|
+
verify_outcomes_unresolved = any(
|
|
276
|
+
action.outcome_precision is not Precision.EXACT or action.outcome == ToolOutcome.UNKNOWN for action in verifies
|
|
277
|
+
)
|
|
278
|
+
first_edit = edits[0] if edits else None
|
|
279
|
+
time_to_edit = _time_to_action(turns, first_edit)
|
|
280
|
+
first_verify = next(
|
|
281
|
+
(action for action in verifies if first_edit is not None and verify_covers_edit(first_edit, action)),
|
|
282
|
+
None,
|
|
283
|
+
)
|
|
284
|
+
if first_edit is not None and first_verify is not None:
|
|
285
|
+
edit_to_verify = _duration_between_actions(
|
|
286
|
+
turns,
|
|
287
|
+
first_edit,
|
|
288
|
+
first_verify,
|
|
289
|
+
precisions=(first_edit.classification_precision, first_verify.classification_precision),
|
|
290
|
+
)
|
|
291
|
+
if degraded_shell:
|
|
292
|
+
edit_to_verify = _with_unresolved_precision(edit_to_verify, verification_reason)
|
|
293
|
+
elif degraded_shell and first_edit is not None:
|
|
294
|
+
edit_to_verify = Metric(None, Precision.UNRESOLVED, verification_reason)
|
|
295
|
+
elif estimated_shell and first_edit is not None:
|
|
296
|
+
# An estimated other-classified shell action may have been the
|
|
297
|
+
# first verify, so "no verify followed" cannot be stated as fact.
|
|
298
|
+
edit_to_verify = Metric(None, Precision.UNRESOLVED, _WORD_LIST_HEURISTIC_REASON)
|
|
299
|
+
else:
|
|
300
|
+
edit_to_verify = Metric(None, Precision.MISSING, "no verify action followed the first edit")
|
|
301
|
+
cycle_metrics: list[Metric] = []
|
|
302
|
+
pending_edits: list[ActionOperation] = []
|
|
303
|
+
for action in actions:
|
|
304
|
+
_check_cancelled(cancel_event)
|
|
305
|
+
if action.classification is ActionClass.EDIT:
|
|
306
|
+
pending_edits.append(action)
|
|
307
|
+
# A verify closes the open iteration only when it began after every
|
|
308
|
+
# pending edit's terminal landed; one overlapping any of them
|
|
309
|
+
# leaves the whole batch pending for a later ordered verify.
|
|
310
|
+
elif (
|
|
311
|
+
action.classification is ActionClass.VERIFY
|
|
312
|
+
and tool_succeeded(action.outcome)
|
|
313
|
+
and pending_edits
|
|
314
|
+
and all(verify_covers_edit(edit, action) for edit in pending_edits)
|
|
315
|
+
):
|
|
316
|
+
cycle_metrics.append(
|
|
317
|
+
_duration_between_actions(
|
|
318
|
+
turns,
|
|
319
|
+
pending_edits[0],
|
|
320
|
+
action,
|
|
321
|
+
precisions=(
|
|
322
|
+
*(edit.classification_precision for edit in pending_edits),
|
|
323
|
+
action.classification_precision,
|
|
324
|
+
action.outcome_precision,
|
|
325
|
+
),
|
|
326
|
+
)
|
|
327
|
+
)
|
|
328
|
+
pending_edits = []
|
|
329
|
+
cycle_values = [metric.value for metric in cycle_metrics if isinstance(metric.value, int)]
|
|
330
|
+
cycle_precisions = [metric.precision for metric in cycle_metrics]
|
|
331
|
+
if degraded_shell or verify_outcomes_unresolved:
|
|
332
|
+
cycle_precisions.append(Precision.UNRESOLVED)
|
|
333
|
+
elif estimated_shell:
|
|
334
|
+
# An unrecognized shell verify could have opened or closed more
|
|
335
|
+
# cycles than the word list identified.
|
|
336
|
+
cycle_precisions.append(Precision.ESTIMATED)
|
|
337
|
+
cycle_precision = least_precision(cycle_precisions)
|
|
338
|
+
cycle_reason = (
|
|
339
|
+
verification_reason
|
|
340
|
+
if degraded_shell
|
|
341
|
+
else outcome_reason
|
|
342
|
+
if verify_outcomes_unresolved
|
|
343
|
+
else _WORD_LIST_HEURISTIC_REASON
|
|
344
|
+
if estimated_shell
|
|
345
|
+
else next((metric.reason for metric in cycle_metrics if metric.reason is not None), None)
|
|
346
|
+
)
|
|
347
|
+
successful_verifies = [action for action in verifies if tool_succeeded(action.outcome)]
|
|
348
|
+
last_successful_verify = successful_verifies[-1] if successful_verifies else None
|
|
349
|
+
unverified = [
|
|
350
|
+
action
|
|
351
|
+
for action in edits
|
|
352
|
+
if last_successful_verify is None or not verify_covers_edit(action, last_successful_verify)
|
|
353
|
+
]
|
|
354
|
+
unverified_precision = least_precision(action.classification_precision for action in (*edits, *verifies))
|
|
355
|
+
unverified_reason = None
|
|
356
|
+
if degraded_shell:
|
|
357
|
+
unverified_precision = Precision.UNRESOLVED
|
|
358
|
+
unverified_reason = verification_reason
|
|
359
|
+
elif verify_outcomes_unresolved:
|
|
360
|
+
unverified_precision = Precision.UNRESOLVED
|
|
361
|
+
unverified_reason = outcome_reason
|
|
362
|
+
elif estimated_shell and unverified_precision is Precision.EXACT:
|
|
363
|
+
# An estimated other-classified shell action may really have been
|
|
364
|
+
# verification work, so the absence this count rests on is only as
|
|
365
|
+
# good as the word-list heuristic.
|
|
366
|
+
unverified_precision = Precision.ESTIMATED
|
|
367
|
+
unverified_reason = _WORD_LIST_HEURISTIC_REASON
|
|
368
|
+
elif unverified_precision is Precision.ESTIMATED:
|
|
369
|
+
unverified_reason = _WORD_LIST_HEURISTIC_REASON
|
|
370
|
+
failures = [action for action in actions if tool_failed(action.outcome)]
|
|
371
|
+
failure_groups: dict[tuple[str | None, str | None], list[ActionOperation]] = defaultdict(list)
|
|
372
|
+
for action in failures:
|
|
373
|
+
_check_cancelled(cancel_event)
|
|
374
|
+
if action.tool_name is not None and action.argument_fingerprint is not None:
|
|
375
|
+
failure_groups[(action.tool_name, action.argument_fingerprint)].append(action)
|
|
376
|
+
repeated_signatures = sum(len(group) >= 2 for group in failure_groups.values())
|
|
377
|
+
recovery_metrics: list[Metric] = []
|
|
378
|
+
for failure in failures:
|
|
379
|
+
_check_cancelled(cancel_event)
|
|
380
|
+
# A recovery responds to an observed failure, so candidates must
|
|
381
|
+
# begin after the failure's terminal — a concurrent same-tool
|
|
382
|
+
# success is not a retry, and without the terminal nothing orders
|
|
383
|
+
# any later success against the failure. A failure with no tool
|
|
384
|
+
# name cannot be paired at all: matching nameless calls would
|
|
385
|
+
# marry unrelated tools.
|
|
386
|
+
if failure.tool_name is None:
|
|
387
|
+
recovery_metrics.append(Metric(None, Precision.UNRESOLVED, "the failed call lacks a tool identity"))
|
|
388
|
+
continue
|
|
389
|
+
if failure.end_sequence is None:
|
|
390
|
+
recovery_metrics.append(Metric(None, Precision.UNRESOLVED, "the failed call's terminal never landed"))
|
|
391
|
+
continue
|
|
392
|
+
recovered: ActionOperation | None = None
|
|
393
|
+
# A same-tool action with an unknown or imprecise outcome between
|
|
394
|
+
# the failure and the recognized success may itself have been the
|
|
395
|
+
# recovery, so the measured latency cannot claim exactness past it.
|
|
396
|
+
passed_unknown_outcome = False
|
|
397
|
+
for action in actions:
|
|
398
|
+
if action.start_sequence <= failure.end_sequence or action.tool_name != failure.tool_name:
|
|
399
|
+
continue
|
|
400
|
+
if tool_succeeded(action.outcome):
|
|
401
|
+
recovered = action
|
|
402
|
+
break
|
|
403
|
+
if action.outcome == ToolOutcome.UNKNOWN or action.outcome_precision is not Precision.EXACT:
|
|
404
|
+
passed_unknown_outcome = True
|
|
405
|
+
if recovered is not None:
|
|
406
|
+
metric = _duration_between_actions(
|
|
407
|
+
turns,
|
|
408
|
+
failure,
|
|
409
|
+
recovered,
|
|
410
|
+
from_end=True,
|
|
411
|
+
precisions=(failure.outcome_precision, recovered.outcome_precision),
|
|
412
|
+
)
|
|
413
|
+
if passed_unknown_outcome:
|
|
414
|
+
metric = _with_unresolved_precision(
|
|
415
|
+
metric, "a same-tool call with an unknown outcome preceded the recognized recovery"
|
|
416
|
+
)
|
|
417
|
+
recovery_metrics.append(metric)
|
|
418
|
+
if any(action.tool_name is None or action.argument_fingerprint is None for action in failures):
|
|
419
|
+
signature_precision = Precision.UNRESOLVED
|
|
420
|
+
signature_reason = "one or more failed tools lack a name or argument fingerprint"
|
|
421
|
+
else:
|
|
422
|
+
signature_precision = outcome_precision
|
|
423
|
+
signature_reason = outcome_reason
|
|
424
|
+
recovery_values = [metric.value for metric in recovery_metrics if isinstance(metric.value, int)]
|
|
425
|
+
recovery_precision = least_precision(metric.precision for metric in recovery_metrics)
|
|
426
|
+
recovery_reason = next((metric.reason for metric in recovery_metrics if metric.reason is not None), outcome_reason)
|
|
427
|
+
validation = ValidationMetrics(
|
|
428
|
+
funnel=ActionFunnel(
|
|
429
|
+
search=counts[ActionClass.SEARCH],
|
|
430
|
+
read=counts[ActionClass.READ],
|
|
431
|
+
edit=counts[ActionClass.EDIT],
|
|
432
|
+
verify=counts[ActionClass.VERIFY],
|
|
433
|
+
),
|
|
434
|
+
time_to_first_edit_ns=time_to_edit,
|
|
435
|
+
first_edit_to_first_verify_ns=edit_to_verify,
|
|
436
|
+
edit_verify_cycle_count=Metric(len(cycle_metrics), cycle_precision, cycle_reason),
|
|
437
|
+
edit_verify_cycle_median_ns=(
|
|
438
|
+
Metric(int(median(cycle_values)), cycle_precision, cycle_reason)
|
|
439
|
+
if cycle_values and len(cycle_values) == len(cycle_metrics)
|
|
440
|
+
else Metric(None, Precision.UNRESOLVED, cycle_reason)
|
|
441
|
+
if cycle_metrics
|
|
442
|
+
else Metric(None, Precision.UNRESOLVED, verification_reason)
|
|
443
|
+
if degraded_shell
|
|
444
|
+
else Metric(None, Precision.UNRESOLVED, outcome_reason)
|
|
445
|
+
if verify_outcomes_unresolved and first_edit is not None
|
|
446
|
+
else Metric(None, Precision.UNRESOLVED, _WORD_LIST_HEURISTIC_REASON)
|
|
447
|
+
if estimated_shell and first_edit is not None
|
|
448
|
+
else Metric(None, Precision.MISSING, "no completed edit-to-verify cycle was recorded")
|
|
449
|
+
),
|
|
450
|
+
unverified_change_count=Metric(len(unverified), unverified_precision, unverified_reason),
|
|
451
|
+
net_zero_churn_count=change_verification.net_zero,
|
|
452
|
+
repeated_failure_signature_count=Metric(repeated_signatures, signature_precision, signature_reason),
|
|
453
|
+
failure_recovery_median_ns=(
|
|
454
|
+
Metric(int(median(recovery_values)), recovery_precision, recovery_reason)
|
|
455
|
+
if recovery_values and len(recovery_values) == len(recovery_metrics)
|
|
456
|
+
else Metric(None, Precision.UNRESOLVED, recovery_reason)
|
|
457
|
+
if recovery_metrics
|
|
458
|
+
else Metric(None, Precision.UNRESOLVED, outcome_reason)
|
|
459
|
+
if outcome_precision is Precision.UNRESOLVED
|
|
460
|
+
else Metric(None, Precision.MISSING, "no failed tool call was followed by a same-tool success")
|
|
461
|
+
),
|
|
462
|
+
tool_failure_count=Metric(len(failures), outcome_precision, outcome_reason),
|
|
463
|
+
tool_count=Metric(
|
|
464
|
+
len(actions),
|
|
465
|
+
projection_precision,
|
|
466
|
+
None if projection_precision is Precision.EXACT else projection_reason,
|
|
467
|
+
),
|
|
468
|
+
retry_amplification_tokens=retry_amplification,
|
|
469
|
+
)
|
|
470
|
+
if projection_precision is Precision.EXACT:
|
|
471
|
+
return validation
|
|
472
|
+
reason = projection_reason or "tool action projection is incomplete"
|
|
473
|
+
return _degrade_validation_metrics(validation, reason)
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def _time_to_action(turns: list[TurnAnalysis], action: ActionOperation | None) -> Metric:
|
|
477
|
+
if action is None:
|
|
478
|
+
return Metric(None, Precision.MISSING, "no edit action was recorded")
|
|
479
|
+
elapsed = 0
|
|
480
|
+
precisions = [action.classification_precision]
|
|
481
|
+
for turn in turns:
|
|
482
|
+
if turn.turn_id == action.turn_id:
|
|
483
|
+
offset = action.start_ns - turn.axis_start_ns
|
|
484
|
+
if offset < 0:
|
|
485
|
+
return Metric(None, Precision.UNRESOLVED, "action precedes its owning turn")
|
|
486
|
+
return Metric(elapsed + offset, least_precision(precisions), action.classification_reason)
|
|
487
|
+
if not isinstance(turn.elapsed_ns.value, int):
|
|
488
|
+
return Metric(None, Precision.UNRESOLVED, "an earlier turn duration is unresolved")
|
|
489
|
+
elapsed += turn.elapsed_ns.value
|
|
490
|
+
precisions.append(turn.elapsed_ns.precision)
|
|
491
|
+
return Metric(None, Precision.UNRESOLVED, "action turn is absent from the active projection")
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
def _duration_between_actions(
|
|
495
|
+
turns: list[TurnAnalysis],
|
|
496
|
+
first: ActionOperation,
|
|
497
|
+
second: ActionOperation,
|
|
498
|
+
*,
|
|
499
|
+
from_end: bool = False,
|
|
500
|
+
precisions: tuple[Precision, ...],
|
|
501
|
+
) -> Metric:
|
|
502
|
+
start_ns = first.end_ns if from_end else first.start_ns
|
|
503
|
+
if start_ns is None:
|
|
504
|
+
return Metric(None, Precision.MISSING, "the starting tool outcome has no terminal timestamp")
|
|
505
|
+
if first.turn_id == second.turn_id:
|
|
506
|
+
duration = second.start_ns - start_ns
|
|
507
|
+
if duration < 0:
|
|
508
|
+
return Metric(None, Precision.UNRESOLVED, "action order yields a negative duration")
|
|
509
|
+
return Metric(duration, least_precision(precisions))
|
|
510
|
+
turns_by_id = {turn.turn_id: index for index, turn in enumerate(turns)}
|
|
511
|
+
first_index = turns_by_id.get(first.turn_id)
|
|
512
|
+
second_index = turns_by_id.get(second.turn_id)
|
|
513
|
+
if first_index is None or second_index is None or second_index <= first_index:
|
|
514
|
+
return Metric(None, Precision.UNRESOLVED, "action turns are not ordered in the active projection")
|
|
515
|
+
first_turn = turns[first_index]
|
|
516
|
+
second_turn = turns[second_index]
|
|
517
|
+
if not isinstance(first_turn.elapsed_ns.value, int):
|
|
518
|
+
return Metric(None, Precision.UNRESOLVED, "the starting turn duration is unresolved")
|
|
519
|
+
start_offset = start_ns - first_turn.axis_start_ns
|
|
520
|
+
end_offset = second.start_ns - second_turn.axis_start_ns
|
|
521
|
+
duration = first_turn.elapsed_ns.value - start_offset + end_offset
|
|
522
|
+
metric_precisions = [*precisions, first_turn.elapsed_ns.precision]
|
|
523
|
+
for turn in turns[first_index + 1 : second_index]:
|
|
524
|
+
if not isinstance(turn.elapsed_ns.value, int):
|
|
525
|
+
return Metric(None, Precision.UNRESOLVED, "an intervening turn duration is unresolved")
|
|
526
|
+
duration += turn.elapsed_ns.value
|
|
527
|
+
metric_precisions.append(turn.elapsed_ns.precision)
|
|
528
|
+
if duration < 0:
|
|
529
|
+
return Metric(None, Precision.UNRESOLVED, "action order yields a negative duration")
|
|
530
|
+
return Metric(duration, least_precision(metric_precisions))
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
def _degrade_validation_metrics(
|
|
534
|
+
validation: ValidationMetrics,
|
|
535
|
+
reason: str,
|
|
536
|
+
*,
|
|
537
|
+
session_integrity_cap: bool = False,
|
|
538
|
+
) -> ValidationMetrics:
|
|
539
|
+
def degrade(metric: Metric) -> Metric:
|
|
540
|
+
return (
|
|
541
|
+
_cap_session_metric(metric, reason) if session_integrity_cap else _with_unresolved_precision(metric, reason)
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
return ValidationMetrics(
|
|
545
|
+
funnel=ActionFunnel(
|
|
546
|
+
search=degrade(validation.funnel.search),
|
|
547
|
+
read=degrade(validation.funnel.read),
|
|
548
|
+
edit=degrade(validation.funnel.edit),
|
|
549
|
+
verify=degrade(validation.funnel.verify),
|
|
550
|
+
),
|
|
551
|
+
time_to_first_edit_ns=degrade(validation.time_to_first_edit_ns),
|
|
552
|
+
first_edit_to_first_verify_ns=degrade(validation.first_edit_to_first_verify_ns),
|
|
553
|
+
edit_verify_cycle_count=degrade(validation.edit_verify_cycle_count),
|
|
554
|
+
edit_verify_cycle_median_ns=degrade(validation.edit_verify_cycle_median_ns),
|
|
555
|
+
unverified_change_count=degrade(validation.unverified_change_count),
|
|
556
|
+
net_zero_churn_count=degrade(validation.net_zero_churn_count),
|
|
557
|
+
repeated_failure_signature_count=degrade(validation.repeated_failure_signature_count),
|
|
558
|
+
failure_recovery_median_ns=degrade(validation.failure_recovery_median_ns),
|
|
559
|
+
tool_failure_count=degrade(validation.tool_failure_count),
|
|
560
|
+
tool_count=degrade(validation.tool_count),
|
|
561
|
+
retry_amplification_tokens=degrade(validation.retry_amplification_tokens),
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
def _resolve_change_verification(
|
|
566
|
+
intermediate: _Intermediate,
|
|
567
|
+
inactive_ranges: tuple[tuple[int, int], ...],
|
|
568
|
+
turns: list[TurnAnalysis],
|
|
569
|
+
actions: tuple[ActionOperation, ...],
|
|
570
|
+
projection: _SessionProjection,
|
|
571
|
+
*,
|
|
572
|
+
rollback_projection_unresolved: bool,
|
|
573
|
+
cancel_event: Event | None,
|
|
574
|
+
) -> ChangeVerification:
|
|
575
|
+
_check_cancelled(cancel_event)
|
|
576
|
+
active_turn_numbers = {turn.turn_number for turn in turns if turn.turn_number is not None}
|
|
577
|
+
active_turn_ids = {turn.turn_id for turn in turns}
|
|
578
|
+
# The detailed mutation state joins on turn numbers; a missing,
|
|
579
|
+
# damaged, or duplicated number silently drops or misattributes that
|
|
580
|
+
# turn's rows, so the join cannot claim the detailed counts.
|
|
581
|
+
seen_turn_numbers: set[int] = set()
|
|
582
|
+
previous_turn_number: int | None = None
|
|
583
|
+
turn_numbers_joinable = True
|
|
584
|
+
for turn in turns:
|
|
585
|
+
number = turn.turn_number
|
|
586
|
+
repeated_without_contiguous_retry = number in seen_turn_numbers and (
|
|
587
|
+
not turn.attempts[0].is_retry or number != previous_turn_number
|
|
588
|
+
)
|
|
589
|
+
if number is None or number <= 0 or repeated_without_contiguous_retry:
|
|
590
|
+
turn_numbers_joinable = False
|
|
591
|
+
break
|
|
592
|
+
seen_turn_numbers.add(number)
|
|
593
|
+
previous_turn_number = number
|
|
594
|
+
summaries = [
|
|
595
|
+
endpoint
|
|
596
|
+
for endpoint in intermediate.mutation_summaries
|
|
597
|
+
if _active(endpoint.sequence, inactive_ranges) and endpoint.turn_id in active_turn_ids
|
|
598
|
+
]
|
|
599
|
+
# A summary that names no session checkpoint recorded mutations whose
|
|
600
|
+
# save never completed: the readable document predates them, so its
|
|
601
|
+
# detail cannot be read as the current truth. A later successful save
|
|
602
|
+
# re-serializes the whole mutation log, so only the newest summary
|
|
603
|
+
# decides freshness. Only a ref of the session-checkpoint kind carrying
|
|
604
|
+
# an id the save path could have minted vouches for the document; any
|
|
605
|
+
# other shape proves nothing about it.
|
|
606
|
+
latest_summary = max(summaries, key=lambda endpoint: endpoint.sequence, default=None)
|
|
607
|
+
source_ref = None if latest_summary is None else _payload_value(latest_summary.payload, "source_ref")
|
|
608
|
+
detail_current = latest_summary is None or (
|
|
609
|
+
isinstance(source_ref, dict)
|
|
610
|
+
and source_ref.get("kind") == SourceRefKind.SESSION_CHECKPOINT
|
|
611
|
+
and is_valid_analytics_id(source_ref.get("id"))
|
|
612
|
+
)
|
|
613
|
+
if (
|
|
614
|
+
not projection.available
|
|
615
|
+
or not projection.mutation_detail_available
|
|
616
|
+
or not detail_current
|
|
617
|
+
or not turn_numbers_joinable
|
|
618
|
+
):
|
|
619
|
+
# Per-turn summaries carry counts but no file identities, so a file
|
|
620
|
+
# touched in several turns counts once per turn, and a create followed
|
|
621
|
+
# by a later modify never folds into one created file — the sums are
|
|
622
|
+
# honest per-turn totals, never the session-wide folded counts the
|
|
623
|
+
# detailed projection reports.
|
|
624
|
+
precision = Precision.UNRESOLVED if rollback_projection_unresolved else Precision.ESTIMATED
|
|
625
|
+
reason = (
|
|
626
|
+
"recorded summary counts only; rollback projection and session.json detail are unavailable"
|
|
627
|
+
if precision is Precision.UNRESOLVED
|
|
628
|
+
else "summed per-turn summary counts; the active turns' numbers cannot join the session.json file detail"
|
|
629
|
+
if not turn_numbers_joinable
|
|
630
|
+
else "summed per-turn summary counts; usable session.json file detail is unavailable to fold repeat touches"
|
|
631
|
+
)
|
|
632
|
+
|
|
633
|
+
def summary(key: str) -> Metric:
|
|
634
|
+
values = [_payload_int(endpoint.payload, key) for endpoint in summaries]
|
|
635
|
+
if any(value is None or value < 0 for value in values):
|
|
636
|
+
return Metric(None, Precision.MISSING, f"recorded mutation summary is missing or invalid for {key}")
|
|
637
|
+
return Metric(sum(cast("int", value) for value in values), precision, reason)
|
|
638
|
+
|
|
639
|
+
return ChangeVerification(
|
|
640
|
+
detail_available=False,
|
|
641
|
+
detection_truncated=False,
|
|
642
|
+
files_touched=summary("files_touched"),
|
|
643
|
+
created=summary("create"),
|
|
644
|
+
modified=summary("modify"),
|
|
645
|
+
deleted=summary("delete"),
|
|
646
|
+
net_zero=summary("net_zero_count"),
|
|
647
|
+
)
|
|
648
|
+
selected = sorted(
|
|
649
|
+
(
|
|
650
|
+
mutation
|
|
651
|
+
for mutation in projection.mutations
|
|
652
|
+
if mutation.turn_number in active_turn_numbers and mutation.provenance != "foreign"
|
|
653
|
+
),
|
|
654
|
+
key=lambda mutation: mutation.turn_number,
|
|
655
|
+
)
|
|
656
|
+
folded: dict[str, tuple[FileHashDiff, int]] = {}
|
|
657
|
+
|
|
658
|
+
def fold(
|
|
659
|
+
path: str,
|
|
660
|
+
turn_number: int,
|
|
661
|
+
*,
|
|
662
|
+
before_hash: str | None,
|
|
663
|
+
before_skip: str | None,
|
|
664
|
+
after_hash: str | None,
|
|
665
|
+
after_skip: str | None,
|
|
666
|
+
inferred: bool,
|
|
667
|
+
contested: bool,
|
|
668
|
+
) -> None:
|
|
669
|
+
previous = folded.get(path)
|
|
670
|
+
before = previous[0].before if previous is not None else before_hash
|
|
671
|
+
folded_before_skip = previous[0].before_skip if previous is not None else parse_skip_reason(before_skip)
|
|
672
|
+
folded[path] = (
|
|
673
|
+
FileHashDiff(
|
|
674
|
+
before=before,
|
|
675
|
+
after=after_hash,
|
|
676
|
+
before_skip=folded_before_skip,
|
|
677
|
+
after_skip=parse_skip_reason(after_skip),
|
|
678
|
+
contested=(previous is not None and previous[0].contested) or contested,
|
|
679
|
+
inferred=(previous is not None and previous[0].inferred) or inferred,
|
|
680
|
+
),
|
|
681
|
+
turn_number,
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
for mutation in selected:
|
|
685
|
+
_check_cancelled(cancel_event)
|
|
686
|
+
# Only a proven row attests that this session's own write produced
|
|
687
|
+
# the change; anything else folded from a window diff may be a
|
|
688
|
+
# concurrent third party's, so the folded diff keeps the badge.
|
|
689
|
+
inferred = mutation.provenance != "proven"
|
|
690
|
+
fold(
|
|
691
|
+
mutation.path,
|
|
692
|
+
mutation.turn_number,
|
|
693
|
+
before_hash=mutation.before_hash,
|
|
694
|
+
before_skip=mutation.before_skip,
|
|
695
|
+
after_hash=mutation.after_hash,
|
|
696
|
+
after_skip=mutation.after_skip,
|
|
697
|
+
inferred=inferred,
|
|
698
|
+
contested=mutation.contested,
|
|
699
|
+
)
|
|
700
|
+
if mutation.old_path is not None:
|
|
701
|
+
# The move's own before_hash describes the destination; the
|
|
702
|
+
# source folds as a delete from its snapshotted pre-state.
|
|
703
|
+
fold(
|
|
704
|
+
mutation.old_path,
|
|
705
|
+
mutation.turn_number,
|
|
706
|
+
before_hash=mutation.old_before_hash,
|
|
707
|
+
before_skip=mutation.old_before_skip,
|
|
708
|
+
after_hash=None,
|
|
709
|
+
after_skip=None,
|
|
710
|
+
inferred=inferred,
|
|
711
|
+
contested=mutation.contested,
|
|
712
|
+
)
|
|
713
|
+
detection_truncated = bool(projection.detection_truncated_turns & active_turn_numbers)
|
|
714
|
+
metric_precision = Precision.UNRESOLVED if detection_truncated else Precision.EXACT
|
|
715
|
+
metric_reason = "recorded/observed file counts; mutation detection was truncated" if detection_truncated else None
|
|
716
|
+
created = modified = deleted = net_zero = 0
|
|
717
|
+
rows: list[ChangeVerificationRow] = []
|
|
718
|
+
successful_verifies = [
|
|
719
|
+
action for action in actions if action.classification is ActionClass.VERIFY and tool_succeeded(action.outcome)
|
|
720
|
+
]
|
|
721
|
+
# Batched tool calls run concurrently, so an action having *started*
|
|
722
|
+
# before a verify proves nothing about what the verify observed; the
|
|
723
|
+
# verify vouches for a same-turn change only when it began after every
|
|
724
|
+
# action that could have produced the change finished landing. Mutation
|
|
725
|
+
# rows carry no operation attribution, so the ordering must clear every
|
|
726
|
+
# potential mutator — edits, the other-classified actions (shell
|
|
727
|
+
# commands and the like), and the verify-classified peers of the
|
|
728
|
+
# candidate verify (a fixer like `ruff --fix` matches the word list
|
|
729
|
+
# yet mutates) — while the row's evidence and the "orderable at all"
|
|
730
|
+
# question stay with the edit-classified actions alone.
|
|
731
|
+
edit_terminals_by_turn: dict[int, list[int | None]] = {turn_number: [] for turn_number in active_turn_numbers}
|
|
732
|
+
mutator_terminals_by_turn: dict[int, list[int | None]] = {turn_number: [] for turn_number in active_turn_numbers}
|
|
733
|
+
verify_terminals_by_turn: dict[int, list[tuple[str, int | None]]] = {
|
|
734
|
+
turn_number: [] for turn_number in active_turn_numbers
|
|
735
|
+
}
|
|
736
|
+
for action in actions:
|
|
737
|
+
if action.turn_number not in active_turn_numbers:
|
|
738
|
+
continue
|
|
739
|
+
if action.classification is ActionClass.EDIT:
|
|
740
|
+
edit_terminals_by_turn[action.turn_number].append(action.end_sequence)
|
|
741
|
+
if action.classification in (ActionClass.EDIT, ActionClass.OTHER):
|
|
742
|
+
mutator_terminals_by_turn[action.turn_number].append(action.end_sequence)
|
|
743
|
+
if action.classification is ActionClass.VERIFY:
|
|
744
|
+
verify_terminals_by_turn[action.turn_number].append((action.operation_id, action.end_sequence))
|
|
745
|
+
# A shell action whose command carrier is unavailable could have been
|
|
746
|
+
# the verify an unverified row claims never happened, and a recognized
|
|
747
|
+
# verify whose terminal outcome never resolved could have been the
|
|
748
|
+
# success; an estimated shell action may have been verification work
|
|
749
|
+
# the word list missed.
|
|
750
|
+
verify_absence_precision = (
|
|
751
|
+
Precision.UNRESOLVED
|
|
752
|
+
if any(
|
|
753
|
+
(action.tool_kind == "shell" and action.classification_precision is Precision.UNRESOLVED)
|
|
754
|
+
or (
|
|
755
|
+
action.classification is ActionClass.VERIFY
|
|
756
|
+
and (action.outcome_precision is not Precision.EXACT or action.outcome == ToolOutcome.UNKNOWN)
|
|
757
|
+
)
|
|
758
|
+
for action in actions
|
|
759
|
+
)
|
|
760
|
+
else Precision.ESTIMATED
|
|
761
|
+
if any(
|
|
762
|
+
action.tool_kind == "shell" and action.classification_precision is Precision.ESTIMATED for action in actions
|
|
763
|
+
)
|
|
764
|
+
else Precision.EXACT
|
|
765
|
+
)
|
|
766
|
+
edit_evidence_by_turn = {
|
|
767
|
+
turn_number: tuple(
|
|
768
|
+
sorted(
|
|
769
|
+
action.evidence_key
|
|
770
|
+
for action in actions
|
|
771
|
+
if action.turn_number == turn_number
|
|
772
|
+
and action.classification is ActionClass.EDIT
|
|
773
|
+
and action.evidence_key is not None
|
|
774
|
+
)
|
|
775
|
+
)
|
|
776
|
+
for turn_number in active_turn_numbers
|
|
777
|
+
}
|
|
778
|
+
unprovable_net_zero = False
|
|
779
|
+
for path, (diff, last_turn) in folded.items():
|
|
780
|
+
_check_cancelled(cancel_event)
|
|
781
|
+
if not diff.before_exists and diff.after_exists:
|
|
782
|
+
created += 1
|
|
783
|
+
elif diff.before_exists and not diff.after_exists:
|
|
784
|
+
deleted += 1
|
|
785
|
+
else:
|
|
786
|
+
modified += 1
|
|
787
|
+
# A window-inferred or peer-contested fold may describe another
|
|
788
|
+
# writer's change, so no row built from one can claim exactness.
|
|
789
|
+
provenance_precision = Precision.ESTIMATED if diff.inferred or diff.contested else Precision.EXACT
|
|
790
|
+
if diff.is_net_zero:
|
|
791
|
+
net_zero += 1
|
|
792
|
+
state = ChangeVerificationState.NET_ZERO
|
|
793
|
+
if diff.content_unavailable:
|
|
794
|
+
# Both content backups were withheld: existence never
|
|
795
|
+
# flipped, but a return to the original bytes cannot be
|
|
796
|
+
# proven either way.
|
|
797
|
+
unprovable_net_zero = True
|
|
798
|
+
row_precision = Precision.UNRESOLVED
|
|
799
|
+
else:
|
|
800
|
+
row_precision = least_precision((metric_precision, provenance_precision))
|
|
801
|
+
else:
|
|
802
|
+
latest_verify = successful_verifies[-1] if successful_verifies else None
|
|
803
|
+
edit_terminals = edit_terminals_by_turn.get(last_turn, [])
|
|
804
|
+
mutator_terminals = list(mutator_terminals_by_turn.get(last_turn, []))
|
|
805
|
+
if latest_verify is not None:
|
|
806
|
+
mutator_terminals.extend(
|
|
807
|
+
end_sequence
|
|
808
|
+
for operation_id, end_sequence in verify_terminals_by_turn.get(last_turn, [])
|
|
809
|
+
if operation_id != latest_verify.operation_id
|
|
810
|
+
)
|
|
811
|
+
orderable = bool(edit_terminals) and None not in mutator_terminals
|
|
812
|
+
if latest_verify is None or latest_verify.turn_number is None:
|
|
813
|
+
state = ChangeVerificationState.UNVERIFIED
|
|
814
|
+
row_precision = least_precision((metric_precision, provenance_precision, verify_absence_precision))
|
|
815
|
+
elif latest_verify.turn_number == last_turn and not orderable:
|
|
816
|
+
# The turn's changes came from actions that never classify
|
|
817
|
+
# as edits (shell commands classify as verify or other) or
|
|
818
|
+
# from a potential mutator whose terminal never landed, so
|
|
819
|
+
# the verify cannot be ordered against the change it would
|
|
820
|
+
# need to follow.
|
|
821
|
+
state = ChangeVerificationState.UNVERIFIED
|
|
822
|
+
row_precision = Precision.UNRESOLVED
|
|
823
|
+
elif latest_verify.turn_number > last_turn or (
|
|
824
|
+
latest_verify.turn_number == last_turn
|
|
825
|
+
and latest_verify.start_sequence > max(cast("list[int]", mutator_terminals))
|
|
826
|
+
):
|
|
827
|
+
state = ChangeVerificationState.VERIFIED
|
|
828
|
+
row_precision = least_precision(
|
|
829
|
+
(metric_precision, provenance_precision, latest_verify.classification_precision)
|
|
830
|
+
)
|
|
831
|
+
else:
|
|
832
|
+
# The state leans on having identified the candidate as a
|
|
833
|
+
# verify AND on no unclassifiable shell action having been
|
|
834
|
+
# a later verify that would upgrade the row, so both
|
|
835
|
+
# precisions travel with it.
|
|
836
|
+
state = ChangeVerificationState.AFTER_VERIFY
|
|
837
|
+
row_precision = least_precision(
|
|
838
|
+
(
|
|
839
|
+
metric_precision,
|
|
840
|
+
provenance_precision,
|
|
841
|
+
latest_verify.classification_precision,
|
|
842
|
+
verify_absence_precision,
|
|
843
|
+
)
|
|
844
|
+
)
|
|
845
|
+
rows.append(
|
|
846
|
+
ChangeVerificationRow(
|
|
847
|
+
path,
|
|
848
|
+
state,
|
|
849
|
+
last_turn,
|
|
850
|
+
row_precision,
|
|
851
|
+
edit_evidence_by_turn.get(last_turn, ()),
|
|
852
|
+
)
|
|
853
|
+
)
|
|
854
|
+
diagnostics = [metric_reason] if metric_reason is not None else []
|
|
855
|
+
count_precision = metric_precision
|
|
856
|
+
if any(diff.inferred or diff.contested for diff, _ in folded.values()):
|
|
857
|
+
# The counts then include folds that may describe another writer's
|
|
858
|
+
# change, so they cannot pass for exact per-session totals.
|
|
859
|
+
count_precision = least_precision((count_precision, Precision.ESTIMATED))
|
|
860
|
+
diagnostics.append("counts include window-inferred or peer-contested mutations")
|
|
861
|
+
count_reason = "; ".join(diagnostics) if diagnostics else None
|
|
862
|
+
net_zero_precision = count_precision
|
|
863
|
+
net_zero_reason = count_reason
|
|
864
|
+
if unprovable_net_zero:
|
|
865
|
+
net_zero_precision = least_precision((net_zero_precision, Precision.UNRESOLVED))
|
|
866
|
+
net_zero_reason = "; ".join(
|
|
867
|
+
(*diagnostics, "count includes files whose withheld content backups leave the net change unprovable")
|
|
868
|
+
)
|
|
869
|
+
return ChangeVerification(
|
|
870
|
+
detail_available=True,
|
|
871
|
+
detection_truncated=detection_truncated,
|
|
872
|
+
files_touched=Metric(len(folded), count_precision, count_reason),
|
|
873
|
+
created=Metric(created, count_precision, count_reason),
|
|
874
|
+
modified=Metric(modified, count_precision, count_reason),
|
|
875
|
+
deleted=Metric(deleted, count_precision, count_reason),
|
|
876
|
+
net_zero=Metric(net_zero, net_zero_precision, net_zero_reason),
|
|
877
|
+
rows=tuple(sorted(rows, key=lambda row: row.path)),
|
|
878
|
+
)
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
def _degrade_change_verification(change: ChangeVerification, reason: str) -> ChangeVerification:
|
|
882
|
+
"""Cap every session-wide mutation result when the source log is incomplete."""
|
|
883
|
+
|
|
884
|
+
return ChangeVerification(
|
|
885
|
+
detail_available=change.detail_available,
|
|
886
|
+
detection_truncated=change.detection_truncated,
|
|
887
|
+
files_touched=_cap_session_metric(change.files_touched, reason),
|
|
888
|
+
created=_cap_session_metric(change.created, reason),
|
|
889
|
+
modified=_cap_session_metric(change.modified, reason),
|
|
890
|
+
deleted=_cap_session_metric(change.deleted, reason),
|
|
891
|
+
net_zero=_cap_session_metric(change.net_zero, reason),
|
|
892
|
+
rows=tuple(
|
|
893
|
+
row if row.precision is Precision.MISSING else replace(row, precision=Precision.UNRESOLVED)
|
|
894
|
+
for row in change.rows
|
|
895
|
+
),
|
|
896
|
+
)
|