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,769 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""Approval middleware — gates tool execution with user approval."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
import contextlib
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from typing import TYPE_CHECKING, Any, Protocol, cast
|
|
13
|
+
from uuid import uuid4
|
|
14
|
+
|
|
15
|
+
from chrys.foundation.events.types import (
|
|
16
|
+
ApprovalCancelled,
|
|
17
|
+
ApprovalRequest,
|
|
18
|
+
ApprovalResponse,
|
|
19
|
+
Event,
|
|
20
|
+
InvocationToolCallArgsUpdated,
|
|
21
|
+
Warning,
|
|
22
|
+
)
|
|
23
|
+
from chrys.foundation.i18n import msg
|
|
24
|
+
from chrys.foundation.models.invocations import InvocationOrigin
|
|
25
|
+
from chrys.foundation.platform.paths import resolve_workspace_path
|
|
26
|
+
from chrys.foundation.tool_kinds import (
|
|
27
|
+
KIND_FILESYSTEM_READ,
|
|
28
|
+
KIND_FILESYSTEM_WRITE,
|
|
29
|
+
KIND_SHELL,
|
|
30
|
+
KIND_SUB_AGENT,
|
|
31
|
+
get_tool_kind,
|
|
32
|
+
)
|
|
33
|
+
from chrys.foundation.trajectory.context import side_call_scope
|
|
34
|
+
from chrys.foundation.trajectory.envelope import ActorRole
|
|
35
|
+
from chrys.foundation.util.once_close import finish_close
|
|
36
|
+
from chrys.kernel.middleware import FunctionMiddleware
|
|
37
|
+
from chrys.service.agent_middleware._metadata_keys import (
|
|
38
|
+
_APPROVAL_MODIFIED_ARGS_KEY,
|
|
39
|
+
_APPROVAL_REJECTED_KEY,
|
|
40
|
+
_REJECTION_MESSAGE_KEY,
|
|
41
|
+
_REJECTION_SOURCE_KEY,
|
|
42
|
+
_SHORT_ID_LEN,
|
|
43
|
+
)
|
|
44
|
+
from chrys.service.agent_middleware.control.approval_hooks import await_approval_with_hooks
|
|
45
|
+
from chrys.service.agent_middleware.events.hook_dispatch import (
|
|
46
|
+
apply_before_tool_hooks,
|
|
47
|
+
get_call_id,
|
|
48
|
+
get_tool_invocation_order,
|
|
49
|
+
)
|
|
50
|
+
from chrys.service.approval.arbitration import ApprovalDecisionArbiter, ApprovalJudgeInput
|
|
51
|
+
from chrys.service.approval.correlation import OneShotCorrelation
|
|
52
|
+
from chrys.service.approval.policy import ApprovalMode
|
|
53
|
+
from chrys.service.approval.reuse_binding import ApprovalReuseBinding, PreparedReuse
|
|
54
|
+
from chrys.service.approval.safety_classifier import (
|
|
55
|
+
path_arg_may_access_sensitive_data,
|
|
56
|
+
shell_arg_may_access_sensitive_data,
|
|
57
|
+
shell_command_may_access_sensitive_data,
|
|
58
|
+
target_may_access_sensitive_data,
|
|
59
|
+
)
|
|
60
|
+
from chrys.service.tools.approval_targets import approved_targets
|
|
61
|
+
from chrys.service.trajectory.approvals import ApprovalDecider, ApprovalTrace
|
|
62
|
+
from chrys.service.trajectory.tools import tool_operation_id
|
|
63
|
+
|
|
64
|
+
_REMEMBER_FAILED = msg(
|
|
65
|
+
"approval.reuse.save_failed",
|
|
66
|
+
fallback="Allowed once, but the approval could not be remembered. Check storage permissions or manage grants with icode approvals.",
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
_GITDIR_POINTER_PREFIX = "gitdir:"
|
|
70
|
+
|
|
71
|
+
if TYPE_CHECKING:
|
|
72
|
+
from collections.abc import Awaitable, Callable
|
|
73
|
+
from pathlib import Path
|
|
74
|
+
|
|
75
|
+
from chrys.foundation.events.bus import EventBus
|
|
76
|
+
from chrys.foundation.models.approval_reuse import ReuseChoice
|
|
77
|
+
from chrys.kernel.middleware import FunctionInvocationContext
|
|
78
|
+
from chrys.service.approval.judge import ApprovalJudge
|
|
79
|
+
from chrys.service.approval.policy import ApprovalPolicy
|
|
80
|
+
from chrys.service.approval.reuse import Candidate
|
|
81
|
+
from chrys.service.approval.turn_context import TurnContextHolder
|
|
82
|
+
from chrys.service.hooks.manager import HookManager
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _unanswered(future: asyncio.Future[ApprovalResponse]) -> bool:
|
|
86
|
+
"""Whether no response settled *future*; an interrupted wait cancels it."""
|
|
87
|
+
return not future.done() or future.cancelled()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _path_is_at_or_under(path: str, parent: str) -> bool:
|
|
91
|
+
return path == parent or path.startswith(parent + os.sep)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _is_session_archive_read_path(file_path: str, resolved_roots: list[str], *, base_cwd: str | None = None) -> bool:
|
|
95
|
+
"""Check if *file_path* resolves inside a session-owned archive root.
|
|
96
|
+
|
|
97
|
+
*resolved_roots* must already be resolved via ``os.path.realpath``. The
|
|
98
|
+
candidate is fully resolved (symlinks included) before the containment
|
|
99
|
+
check, so a link planted under the archive cannot smuggle an outside
|
|
100
|
+
read through the auto-approval.
|
|
101
|
+
"""
|
|
102
|
+
try:
|
|
103
|
+
resolved = os.path.realpath(resolve_workspace_path(file_path, base_cwd=base_cwd))
|
|
104
|
+
except OSError, ValueError:
|
|
105
|
+
return False
|
|
106
|
+
return any(_path_is_at_or_under(resolved, root) for root in resolved_roots)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _gitdir_pointer_target(git_marker: str) -> str | None:
|
|
110
|
+
"""Return the resolved gitdir target for a worktree/submodule .git file."""
|
|
111
|
+
if not os.path.isfile(git_marker):
|
|
112
|
+
return None
|
|
113
|
+
try:
|
|
114
|
+
with open(git_marker, encoding="utf-8", errors="replace") as f:
|
|
115
|
+
line = f.readline(4096).strip()
|
|
116
|
+
except OSError:
|
|
117
|
+
return None
|
|
118
|
+
if not line.lower().startswith(_GITDIR_POINTER_PREFIX):
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
target = line[len(_GITDIR_POINTER_PREFIX) :].strip()
|
|
122
|
+
if not target:
|
|
123
|
+
return None
|
|
124
|
+
if not os.path.isabs(target):
|
|
125
|
+
target = os.path.join(os.path.dirname(git_marker), target)
|
|
126
|
+
try:
|
|
127
|
+
return os.path.realpath(target)
|
|
128
|
+
except OSError, ValueError:
|
|
129
|
+
return None
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _is_workspace_git_path(file_path: str, resolved_roots: list[str], *, base_cwd: str | None = None) -> bool:
|
|
133
|
+
"""Check if *file_path* is inside a workspace root AND under git version control.
|
|
134
|
+
|
|
135
|
+
*resolved_roots* must already be resolved via ``os.path.realpath``.
|
|
136
|
+
|
|
137
|
+
Returns ``True`` only when BOTH conditions hold:
|
|
138
|
+
1. The resolved absolute path starts with one of the *resolved_roots*.
|
|
139
|
+
2. A ``.git`` directory or file exists in the file's directory or any ancestor.
|
|
140
|
+
Git metadata internals are excluded even when a worktree/submodule ``.git``
|
|
141
|
+
pointer places the real gitdir under the workspace root by another name.
|
|
142
|
+
"""
|
|
143
|
+
try:
|
|
144
|
+
resolved = os.path.realpath(resolve_workspace_path(file_path, base_cwd=base_cwd))
|
|
145
|
+
except OSError, ValueError:
|
|
146
|
+
return False
|
|
147
|
+
|
|
148
|
+
# Check if inside any workspace root (roots are pre-resolved)
|
|
149
|
+
if not any(resolved == root or resolved.startswith(root + os.sep) for root in resolved_roots):
|
|
150
|
+
return False
|
|
151
|
+
|
|
152
|
+
# Walk up from file's directory to check for .git. Worktrees and submodules
|
|
153
|
+
# commonly use a .git file containing a gitdir pointer instead of a directory.
|
|
154
|
+
check_dir = os.path.dirname(resolved) if not os.path.isdir(resolved) else resolved
|
|
155
|
+
while True:
|
|
156
|
+
git_marker = os.path.join(check_dir, ".git")
|
|
157
|
+
if os.path.isdir(git_marker) or os.path.isfile(git_marker):
|
|
158
|
+
resolved_git_marker = os.path.realpath(git_marker)
|
|
159
|
+
if _path_is_at_or_under(resolved, resolved_git_marker):
|
|
160
|
+
return False
|
|
161
|
+
gitdir_target = _gitdir_pointer_target(git_marker)
|
|
162
|
+
return gitdir_target is None or not _path_is_at_or_under(resolved, gitdir_target)
|
|
163
|
+
parent = os.path.dirname(check_dir)
|
|
164
|
+
if parent == check_dir:
|
|
165
|
+
break # reached filesystem root
|
|
166
|
+
check_dir = parent
|
|
167
|
+
|
|
168
|
+
return False
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _decision(
|
|
172
|
+
*,
|
|
173
|
+
request_id: str,
|
|
174
|
+
tool_name: str,
|
|
175
|
+
status: str,
|
|
176
|
+
call_id: str = "",
|
|
177
|
+
tool_order: int | None = None,
|
|
178
|
+
) -> dict[str, str]:
|
|
179
|
+
decision = {"request_id": request_id, "tool_name": tool_name, "status": status}
|
|
180
|
+
if call_id:
|
|
181
|
+
decision["call_id"] = call_id
|
|
182
|
+
if tool_order is not None:
|
|
183
|
+
# Approval decisions are typed as dict[str, str] for persistence handoff.
|
|
184
|
+
decision["tool_order"] = str(tool_order)
|
|
185
|
+
return decision
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def _approval_level(*, policy: bool, dev_sub_agent_review: bool, sensitive: bool) -> str:
|
|
189
|
+
"""Why the dialog was shown, for ``approval.requested.approval_level``."""
|
|
190
|
+
if policy:
|
|
191
|
+
return "policy"
|
|
192
|
+
if dev_sub_agent_review:
|
|
193
|
+
return "dev_sub_agent_review"
|
|
194
|
+
if sensitive:
|
|
195
|
+
return "sensitive_target"
|
|
196
|
+
return "unknown"
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@dataclass(frozen=True, slots=True)
|
|
200
|
+
class ApprovalRetrySnapshot:
|
|
201
|
+
"""Approval decisions that predate one retryable Agent.run attempt."""
|
|
202
|
+
|
|
203
|
+
decisions: tuple[dict[str, str], ...]
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class InvocationPublisher(Protocol):
|
|
207
|
+
"""Bound execution identity supplied by the caller's invocation wiring."""
|
|
208
|
+
|
|
209
|
+
@property
|
|
210
|
+
def origin(self) -> InvocationOrigin: ...
|
|
211
|
+
async def publish(self, event: Event) -> None: ...
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class ApprovalMiddleware(FunctionMiddleware):
|
|
215
|
+
"""Gates tool execution with user approval inside the Chrys tool loop.
|
|
216
|
+
|
|
217
|
+
Replaces the old snapshot-restore approval loop. When a tool needs
|
|
218
|
+
approval, the middleware pauses (``await`` on a future), publishes an
|
|
219
|
+
``ApprovalRequest`` event, and waits for the TUI to respond with an
|
|
220
|
+
``ApprovalResponse``. Approved tools proceed to ``call_next()``; rejected
|
|
221
|
+
tools get an error result returned to the LLM.
|
|
222
|
+
|
|
223
|
+
Because the middleware runs *inside* the Chrys tool loop,
|
|
224
|
+
``agent.run()`` never exits early for approval — no re-submission,
|
|
225
|
+
no snapshot/restore, no loop-message capture for approval iterations.
|
|
226
|
+
"""
|
|
227
|
+
|
|
228
|
+
def __init__(
|
|
229
|
+
self,
|
|
230
|
+
approval_policy: ApprovalPolicy,
|
|
231
|
+
event_bus: EventBus,
|
|
232
|
+
session_id: str | None = None,
|
|
233
|
+
tool_kinds: dict[str, str] | None = None,
|
|
234
|
+
caller_name: str = "",
|
|
235
|
+
workspace_roots: list[str] | None = None,
|
|
236
|
+
approval_mode: ApprovalMode = ApprovalMode.MANUAL,
|
|
237
|
+
approval_judge: ApprovalJudge | None = None,
|
|
238
|
+
approval_log_dir: Path | None = None,
|
|
239
|
+
workspace_cwd: str | None = None,
|
|
240
|
+
dev_mode: bool = False,
|
|
241
|
+
hook_manager: HookManager | None = None,
|
|
242
|
+
profile_name: str = "",
|
|
243
|
+
session_archive_read_roots: list[Path] | None = None,
|
|
244
|
+
turn_context: TurnContextHolder | None = None,
|
|
245
|
+
reuse: ApprovalReuseBinding | None = None,
|
|
246
|
+
) -> None:
|
|
247
|
+
from chrys.service.approval.turn_context import TurnContextHolder
|
|
248
|
+
|
|
249
|
+
self._policy = approval_policy
|
|
250
|
+
self._reuse = reuse
|
|
251
|
+
self._publisher: InvocationPublisher | None = None
|
|
252
|
+
self._bus = event_bus
|
|
253
|
+
self._session_id = session_id
|
|
254
|
+
self._tool_kinds: dict[str, str] = tool_kinds or {}
|
|
255
|
+
self._caller_name = caller_name
|
|
256
|
+
self._decisions: list[dict[str, str]] = []
|
|
257
|
+
self._turn_context = turn_context or TurnContextHolder()
|
|
258
|
+
self._approval_mode = approval_mode
|
|
259
|
+
self._approval_judge = approval_judge
|
|
260
|
+
self._approval_log_dir = approval_log_dir
|
|
261
|
+
self._workspace_cwd = workspace_cwd
|
|
262
|
+
self._dev_mode = dev_mode
|
|
263
|
+
self._hook_manager = hook_manager
|
|
264
|
+
self._profile_name = profile_name
|
|
265
|
+
self._approval_arbiter = ApprovalDecisionArbiter(event_bus, session_id=session_id)
|
|
266
|
+
# Keep the long-standing private test seams as aliases while the race
|
|
267
|
+
# itself is owned by the shared arbiter.
|
|
268
|
+
self._blocked_auto_fulfill_ids = self._approval_arbiter._blocked
|
|
269
|
+
self._on_auto_fulfill_blocked = self._approval_arbiter._on_blocked
|
|
270
|
+
# Pre-resolve workspace roots once (avoids repeated realpath per tool call)
|
|
271
|
+
self._workspace_roots: list[str] = []
|
|
272
|
+
for r in workspace_roots or []:
|
|
273
|
+
with contextlib.suppress(OSError, ValueError):
|
|
274
|
+
self._workspace_roots.append(os.path.realpath(os.path.abspath(r)))
|
|
275
|
+
# Session-owned archive dirs (compaction spill records, superseded
|
|
276
|
+
# LAST_WORDS notes) whose reads are always auto-approved: chrys wrote
|
|
277
|
+
# those files from context the agent already saw.
|
|
278
|
+
self._session_archive_read_roots: list[str] = []
|
|
279
|
+
for root in session_archive_read_roots or []:
|
|
280
|
+
with contextlib.suppress(OSError, ValueError):
|
|
281
|
+
self._session_archive_read_roots.append(os.path.realpath(os.path.abspath(root)))
|
|
282
|
+
|
|
283
|
+
def bind_publisher(self, publisher: InvocationPublisher | None) -> None:
|
|
284
|
+
"""Bind future tool callbacks; in-flight approvals retain their publisher."""
|
|
285
|
+
self._publisher = publisher
|
|
286
|
+
|
|
287
|
+
def set_user_message(self, text: str) -> None:
|
|
288
|
+
"""Store the current user message for inclusion in approval requests."""
|
|
289
|
+
self.set_user_messages([text] if text else [])
|
|
290
|
+
|
|
291
|
+
def set_user_messages(self, messages: list[str]) -> None:
|
|
292
|
+
"""Store current-turn user messages for inclusion in approval judge context."""
|
|
293
|
+
self._turn_context.replace(messages)
|
|
294
|
+
|
|
295
|
+
def append_user_message(self, text: str) -> None:
|
|
296
|
+
"""Append a current-turn user message for later approval judge context."""
|
|
297
|
+
self._turn_context.append(text)
|
|
298
|
+
|
|
299
|
+
def remove_user_message(self, text: str) -> None:
|
|
300
|
+
"""Remove the most recent occurrence of *text* from judge context.
|
|
301
|
+
|
|
302
|
+
Used when a queued injection is cancelled after its text was already
|
|
303
|
+
appended, so later approval requests do not cite withdrawn input.
|
|
304
|
+
"""
|
|
305
|
+
self._turn_context.remove(text)
|
|
306
|
+
|
|
307
|
+
@property
|
|
308
|
+
def _user_message(self) -> str:
|
|
309
|
+
return self._turn_context.user_message
|
|
310
|
+
|
|
311
|
+
@property
|
|
312
|
+
def _user_messages(self) -> list[str]:
|
|
313
|
+
return self._turn_context.user_messages
|
|
314
|
+
|
|
315
|
+
def set_approval_mode(self, mode: ApprovalMode) -> None:
|
|
316
|
+
"""Update the active approval mode.
|
|
317
|
+
|
|
318
|
+
Only affects subsequent tool calls — in-flight requests keep the
|
|
319
|
+
mode they were created with.
|
|
320
|
+
"""
|
|
321
|
+
self._approval_mode = mode
|
|
322
|
+
|
|
323
|
+
@property
|
|
324
|
+
def approval_mode(self) -> ApprovalMode:
|
|
325
|
+
return self._approval_mode
|
|
326
|
+
|
|
327
|
+
async def process(
|
|
328
|
+
self,
|
|
329
|
+
context: FunctionInvocationContext,
|
|
330
|
+
call_next: Callable[[], Awaitable[None]],
|
|
331
|
+
) -> None:
|
|
332
|
+
publisher = self._publisher
|
|
333
|
+
tool_name = context.function.name
|
|
334
|
+
call_id = get_call_id(context)
|
|
335
|
+
tool_order = get_tool_invocation_order(context)
|
|
336
|
+
|
|
337
|
+
tool_kind = get_tool_kind(context.function) or self._tool_kinds.get(tool_name, "")
|
|
338
|
+
dev_sub_agent_review = (
|
|
339
|
+
self._dev_mode and tool_kind == KIND_SUB_AGENT and self._approval_mode != ApprovalMode.BYPASS
|
|
340
|
+
)
|
|
341
|
+
sensitive_shell = tool_kind == KIND_SHELL and shell_arg_may_access_sensitive_data(
|
|
342
|
+
context.arguments,
|
|
343
|
+
shell_name=tool_name,
|
|
344
|
+
)
|
|
345
|
+
sensitive_filesystem_read = tool_kind == KIND_FILESYSTEM_READ and path_arg_may_access_sensitive_data(
|
|
346
|
+
context.arguments
|
|
347
|
+
)
|
|
348
|
+
sensitive_filesystem_write = tool_kind == KIND_FILESYSTEM_WRITE and path_arg_may_access_sensitive_data(
|
|
349
|
+
context.arguments
|
|
350
|
+
)
|
|
351
|
+
policy_requires_approval = self._policy.should_require_approval(tool_name, tool_kind)
|
|
352
|
+
if (
|
|
353
|
+
not policy_requires_approval
|
|
354
|
+
and not dev_sub_agent_review
|
|
355
|
+
and not sensitive_shell
|
|
356
|
+
and not sensitive_filesystem_read
|
|
357
|
+
and not sensitive_filesystem_write
|
|
358
|
+
):
|
|
359
|
+
await call_next()
|
|
360
|
+
return
|
|
361
|
+
|
|
362
|
+
# Auto-approve safe read-only shell commands (e.g. ls, cat, grep)
|
|
363
|
+
# without showing the approval dialog.
|
|
364
|
+
if tool_kind == KIND_SHELL:
|
|
365
|
+
raw = context.arguments
|
|
366
|
+
cmd = raw.get("command", "") if isinstance(raw, dict) else ""
|
|
367
|
+
if cmd:
|
|
368
|
+
from chrys.service.tools.builtins.shell_filter import is_safe_readonly_command
|
|
369
|
+
|
|
370
|
+
if is_safe_readonly_command(cmd, shell_name=tool_name) and not shell_command_may_access_sensitive_data(
|
|
371
|
+
cmd, shell_name=tool_name
|
|
372
|
+
):
|
|
373
|
+
self._decisions.append(
|
|
374
|
+
_decision(
|
|
375
|
+
request_id="",
|
|
376
|
+
tool_name=tool_name,
|
|
377
|
+
status="auto_approved",
|
|
378
|
+
call_id=call_id,
|
|
379
|
+
tool_order=tool_order,
|
|
380
|
+
)
|
|
381
|
+
)
|
|
382
|
+
await call_next()
|
|
383
|
+
return
|
|
384
|
+
|
|
385
|
+
# Auto-approve file writes inside workspace git repos.
|
|
386
|
+
if tool_kind == KIND_FILESYSTEM_WRITE and self._workspace_roots:
|
|
387
|
+
raw = context.arguments
|
|
388
|
+
file_path = raw.get("path", "") if isinstance(raw, dict) else ""
|
|
389
|
+
if (
|
|
390
|
+
file_path
|
|
391
|
+
and _is_workspace_git_path(file_path, self._workspace_roots, base_cwd=self._workspace_cwd)
|
|
392
|
+
and not target_may_access_sensitive_data(file_path)
|
|
393
|
+
):
|
|
394
|
+
self._decisions.append(
|
|
395
|
+
_decision(
|
|
396
|
+
request_id="",
|
|
397
|
+
tool_name=tool_name,
|
|
398
|
+
status="auto_approved",
|
|
399
|
+
call_id=call_id,
|
|
400
|
+
tool_order=tool_order,
|
|
401
|
+
)
|
|
402
|
+
)
|
|
403
|
+
await call_next()
|
|
404
|
+
return
|
|
405
|
+
|
|
406
|
+
# Auto-approve reads of session-owned compaction archives (spilled
|
|
407
|
+
# tool records, superseded LAST_WORDS notes): the agent is reading
|
|
408
|
+
# back content chrys itself archived from the conversation, so the
|
|
409
|
+
# dialog would gate nothing — even under a ``require`` rule or a
|
|
410
|
+
# sensitive-looking archived tool name in the filename.
|
|
411
|
+
if tool_kind == KIND_FILESYSTEM_READ and self._session_archive_read_roots:
|
|
412
|
+
raw = context.arguments
|
|
413
|
+
file_path = raw.get("path", "") if isinstance(raw, dict) else ""
|
|
414
|
+
if (
|
|
415
|
+
isinstance(file_path, str)
|
|
416
|
+
and file_path
|
|
417
|
+
and _is_session_archive_read_path(
|
|
418
|
+
file_path,
|
|
419
|
+
self._session_archive_read_roots,
|
|
420
|
+
base_cwd=self._workspace_cwd,
|
|
421
|
+
)
|
|
422
|
+
):
|
|
423
|
+
self._decisions.append(
|
|
424
|
+
_decision(
|
|
425
|
+
request_id="",
|
|
426
|
+
tool_name=tool_name,
|
|
427
|
+
status="auto_approved",
|
|
428
|
+
call_id=call_id,
|
|
429
|
+
tool_order=tool_order,
|
|
430
|
+
)
|
|
431
|
+
)
|
|
432
|
+
await call_next()
|
|
433
|
+
return
|
|
434
|
+
|
|
435
|
+
# BYPASS — silently auto-approve without ever publishing a request.
|
|
436
|
+
if self._approval_mode == ApprovalMode.BYPASS:
|
|
437
|
+
self._decisions.append(
|
|
438
|
+
_decision(
|
|
439
|
+
request_id="",
|
|
440
|
+
tool_name=tool_name,
|
|
441
|
+
status="bypass_approved",
|
|
442
|
+
call_id=call_id,
|
|
443
|
+
tool_order=tool_order,
|
|
444
|
+
)
|
|
445
|
+
)
|
|
446
|
+
await call_next()
|
|
447
|
+
return
|
|
448
|
+
|
|
449
|
+
# Tool needs approval — parse args and publish request.
|
|
450
|
+
raw_args = context.arguments
|
|
451
|
+
if isinstance(raw_args, dict):
|
|
452
|
+
parsed_args = raw_args
|
|
453
|
+
elif isinstance(raw_args, str):
|
|
454
|
+
try:
|
|
455
|
+
parsed_args = json.loads(raw_args)
|
|
456
|
+
except json.JSONDecodeError, TypeError:
|
|
457
|
+
parsed_args = {}
|
|
458
|
+
else:
|
|
459
|
+
# Pydantic model or other — try dict conversion
|
|
460
|
+
parsed_args = dict(raw_args) if raw_args else {}
|
|
461
|
+
# Kernel tool-call validation guarantees object-shaped, string-keyed
|
|
462
|
+
# arguments before this middleware runs; retain the tolerant parsing
|
|
463
|
+
# branches above without copying or coercing their result.
|
|
464
|
+
parsed_args = cast("dict[str, Any]", parsed_args)
|
|
465
|
+
|
|
466
|
+
binding = self._reuse if self._reuse is not None and self._reuse.supports(context) else None
|
|
467
|
+
reuse: PreparedReuse | None = None
|
|
468
|
+
if binding is not None:
|
|
469
|
+
sensitive = sensitive_shell or sensitive_filesystem_read or sensitive_filesystem_write
|
|
470
|
+
reuse = await asyncio.to_thread(binding.prepare, context, reusable=not sensitive)
|
|
471
|
+
if reuse.grant_ids:
|
|
472
|
+
decision = _decision(
|
|
473
|
+
request_id="",
|
|
474
|
+
tool_name=tool_name,
|
|
475
|
+
status="reuse_approved",
|
|
476
|
+
call_id=call_id,
|
|
477
|
+
tool_order=tool_order,
|
|
478
|
+
)
|
|
479
|
+
decision["grant_ids"] = json.dumps(reuse.grant_ids)
|
|
480
|
+
self._decisions.append(decision)
|
|
481
|
+
with approved_targets(reuse.targets):
|
|
482
|
+
await call_next()
|
|
483
|
+
return
|
|
484
|
+
|
|
485
|
+
request_id = uuid4().hex[:_SHORT_ID_LEN]
|
|
486
|
+
decision = _decision(
|
|
487
|
+
request_id=request_id,
|
|
488
|
+
tool_name=tool_name,
|
|
489
|
+
status="approval_pending",
|
|
490
|
+
call_id=call_id,
|
|
491
|
+
tool_order=tool_order,
|
|
492
|
+
)
|
|
493
|
+
# Keep this dict in _decisions and mutate it after the approval
|
|
494
|
+
# response so list order stays tied to invocation order, not response
|
|
495
|
+
# arrival order.
|
|
496
|
+
self._decisions.append(decision)
|
|
497
|
+
|
|
498
|
+
judge_task: asyncio.Task[None] | None = None
|
|
499
|
+
approval_trace = None
|
|
500
|
+
# Set just before the request goes out: from then on a frontend may hold
|
|
501
|
+
# it (a cancelled publish can stop after some handlers ran), so a wait
|
|
502
|
+
# that ends without an answer must retract it.
|
|
503
|
+
published_future: asyncio.Future[ApprovalResponse] | None = None
|
|
504
|
+
try:
|
|
505
|
+
# Subscribe BEFORE publishing (a frontend may answer synchronously)
|
|
506
|
+
# and hold the subscription until the decision is in, whatever
|
|
507
|
+
# interrupts the wait.
|
|
508
|
+
async with OneShotCorrelation(self._bus, ApprovalResponse, request_id=request_id) as correlation:
|
|
509
|
+
future = correlation.future
|
|
510
|
+
try:
|
|
511
|
+
judging = (
|
|
512
|
+
self._approval_mode == ApprovalMode.AUTO
|
|
513
|
+
and self._approval_judge is not None
|
|
514
|
+
and not dev_sub_agent_review
|
|
515
|
+
)
|
|
516
|
+
if judging:
|
|
517
|
+
# Frontends may synchronously block auto-fulfilment while handling
|
|
518
|
+
# ApprovalRequest, so install the shared arbitration subscription
|
|
519
|
+
# before publishing the request.
|
|
520
|
+
await self._ensure_auto_fulfill_block_subscription()
|
|
521
|
+
|
|
522
|
+
approval_trace = ApprovalTrace.open(context.metadata)
|
|
523
|
+
|
|
524
|
+
published_future = future
|
|
525
|
+
await self._bus.publish(
|
|
526
|
+
ApprovalRequest(
|
|
527
|
+
request_id=request_id,
|
|
528
|
+
call_id=call_id,
|
|
529
|
+
tool_name=tool_name,
|
|
530
|
+
tool_kind=tool_kind,
|
|
531
|
+
args=parsed_args,
|
|
532
|
+
# intent_summary stays empty: the middleware has no real intent
|
|
533
|
+
# to report, and a fabricated "Execute {tool_name}" placeholder
|
|
534
|
+
# would win over the informative title fallbacks downstream
|
|
535
|
+
# (the ACP server titles shell calls by their command).
|
|
536
|
+
session_id=self._session_id,
|
|
537
|
+
caller_name=self._caller_name,
|
|
538
|
+
user_message=self._user_message,
|
|
539
|
+
workspace_roots=list(self._workspace_roots),
|
|
540
|
+
workspace_cwd=self._workspace_cwd or "",
|
|
541
|
+
judging=judging,
|
|
542
|
+
reuse_offer=reuse.candidate.offer() if reuse is not None and reuse.candidate else None,
|
|
543
|
+
)
|
|
544
|
+
)
|
|
545
|
+
|
|
546
|
+
if judging:
|
|
547
|
+
# The judge's model call is a side call of this session: rebind
|
|
548
|
+
# the ambient trajectory actor so its exchange is attributed to
|
|
549
|
+
# the judge, never to the main agent.
|
|
550
|
+
with side_call_scope(ActorRole.APPROVAL_JUDGE):
|
|
551
|
+
judge_task = asyncio.create_task(
|
|
552
|
+
self._approval_arbiter.judge(
|
|
553
|
+
request_id=request_id,
|
|
554
|
+
judge=self._approval_judge,
|
|
555
|
+
judge_input=ApprovalJudgeInput(
|
|
556
|
+
user_message=self._turn_context.user_message,
|
|
557
|
+
user_messages=self._turn_context.user_messages,
|
|
558
|
+
tool_name=tool_name,
|
|
559
|
+
tool_kind=tool_kind,
|
|
560
|
+
args=parsed_args,
|
|
561
|
+
workspace_roots=list(self._workspace_roots),
|
|
562
|
+
),
|
|
563
|
+
decision_future=future,
|
|
564
|
+
approved_value=ApprovalResponse(
|
|
565
|
+
request_id=request_id,
|
|
566
|
+
approved=True,
|
|
567
|
+
session_id=self._session_id,
|
|
568
|
+
),
|
|
569
|
+
log_dir=self._approval_log_dir,
|
|
570
|
+
)
|
|
571
|
+
)
|
|
572
|
+
|
|
573
|
+
# Inside the block that lets the request go: the marker awaits
|
|
574
|
+
# its write ack, and an interrupt landing there would otherwise
|
|
575
|
+
# leave this request's handler subscribed and its judge still
|
|
576
|
+
# calling the model. The judge task cannot run before this
|
|
577
|
+
# either, so its own events still follow the request.
|
|
578
|
+
if approval_trace is not None:
|
|
579
|
+
await approval_trace.requested(
|
|
580
|
+
tool_name=tool_name,
|
|
581
|
+
approval_mode=self._approval_mode.value,
|
|
582
|
+
approval_level=_approval_level(
|
|
583
|
+
policy=policy_requires_approval,
|
|
584
|
+
dev_sub_agent_review=dev_sub_agent_review,
|
|
585
|
+
sensitive=sensitive_shell or sensitive_filesystem_read or sensitive_filesystem_write,
|
|
586
|
+
),
|
|
587
|
+
)
|
|
588
|
+
response = await await_approval_with_hooks(
|
|
589
|
+
future=future,
|
|
590
|
+
judge_task=judge_task,
|
|
591
|
+
manager=self._hook_manager,
|
|
592
|
+
session_id=self._session_id,
|
|
593
|
+
profile_name=self._profile_name,
|
|
594
|
+
workspace_cwd=self._workspace_cwd or "",
|
|
595
|
+
caller_name=self._caller_name,
|
|
596
|
+
request_id=request_id,
|
|
597
|
+
tool_name=tool_name,
|
|
598
|
+
tool_kind=tool_kind,
|
|
599
|
+
call_id=call_id,
|
|
600
|
+
args=parsed_args,
|
|
601
|
+
target_operation_id=approval_trace.request_id if approval_trace is not None else None,
|
|
602
|
+
)
|
|
603
|
+
finally:
|
|
604
|
+
# Cancel the judge if the user decided first (or reason is in — the
|
|
605
|
+
# judge still may have published a verdict that raced in; the dialog
|
|
606
|
+
# is already dismissed by then so the TUI drops it).
|
|
607
|
+
if judge_task is not None and not judge_task.done():
|
|
608
|
+
judge_task.cancel()
|
|
609
|
+
with contextlib.suppress(asyncio.CancelledError, Exception):
|
|
610
|
+
await judge_task
|
|
611
|
+
|
|
612
|
+
approved = response.approved
|
|
613
|
+
# Only a person's own answer can remember an approval, never the judge's.
|
|
614
|
+
remember_choice = response.remember_choice if approved and correlation.resolved_by_event else ""
|
|
615
|
+
reason = response.reason.strip()
|
|
616
|
+
modified_args = response.modified_args
|
|
617
|
+
status = "user_approved" if approved else "user_rejected"
|
|
618
|
+
decision["status"] = status
|
|
619
|
+
if reason:
|
|
620
|
+
decision["reason"] = reason
|
|
621
|
+
else:
|
|
622
|
+
decision.pop("reason", None)
|
|
623
|
+
if approval_trace is not None:
|
|
624
|
+
await approval_trace.resolved(
|
|
625
|
+
approved=approved,
|
|
626
|
+
decider=ApprovalDecider.USER if correlation.resolved_by_event else ApprovalDecider.JUDGE,
|
|
627
|
+
reason_code="user_reason" if reason else "",
|
|
628
|
+
arguments_modified=bool(modified_args),
|
|
629
|
+
)
|
|
630
|
+
except BaseException as exc:
|
|
631
|
+
# Interrupted (or failed) while the dialog was still open: the
|
|
632
|
+
# request is abandoned, and only this path can say so.
|
|
633
|
+
if approval_trace is not None:
|
|
634
|
+
approval_trace.interrupted_soon()
|
|
635
|
+
# A frontend still shows the request (or holds it unseen while the
|
|
636
|
+
# judge reviews it) until told otherwise. The judge is drained by
|
|
637
|
+
# now, so this follows any verdict it published.
|
|
638
|
+
if published_future is not None and _unanswered(published_future):
|
|
639
|
+
await self._publish_abandoned(request_id, ended_by=exc)
|
|
640
|
+
raise
|
|
641
|
+
|
|
642
|
+
if approved:
|
|
643
|
+
targets = reuse.targets if reuse is not None else None
|
|
644
|
+
if modified_args:
|
|
645
|
+
context.arguments = {**parsed_args, **modified_args}
|
|
646
|
+
# Re-dispatch ``before_tool_call`` hooks with the edited args.
|
|
647
|
+
# ``ToolEventMiddleware`` already fired hooks once with the
|
|
648
|
+
# original args before approval ran; without this second pass,
|
|
649
|
+
# a hook that gates on ``args.prompt`` would approve one prompt
|
|
650
|
+
# and let the user's edited prompt execute unchecked.
|
|
651
|
+
hook_blocked = await apply_before_tool_hooks(
|
|
652
|
+
manager=self._hook_manager,
|
|
653
|
+
context=context,
|
|
654
|
+
session_id=self._session_id,
|
|
655
|
+
profile_name=self._profile_name,
|
|
656
|
+
tool_name=tool_name,
|
|
657
|
+
kind=tool_kind,
|
|
658
|
+
call_id=call_id,
|
|
659
|
+
args=context.arguments,
|
|
660
|
+
workspace_cwd=self._workspace_cwd or "",
|
|
661
|
+
# Same target as the first dispatch: a hook that gates the
|
|
662
|
+
# edited arguments belongs to the tool it can still block,
|
|
663
|
+
# not to the exchange the call came in on.
|
|
664
|
+
target_operation_id=tool_operation_id(context.metadata),
|
|
665
|
+
)
|
|
666
|
+
# Hooks may have rewritten args further — capture the final form.
|
|
667
|
+
final_args = context.arguments if isinstance(context.arguments, dict) else modified_args
|
|
668
|
+
context.metadata[_APPROVAL_MODIFIED_ARGS_KEY] = final_args
|
|
669
|
+
if call_id and isinstance(final_args, dict):
|
|
670
|
+
if publisher is None:
|
|
671
|
+
raise ValueError("Cannot route an unbound approval callback")
|
|
672
|
+
await publisher.publish(
|
|
673
|
+
InvocationToolCallArgsUpdated(
|
|
674
|
+
origin=publisher.origin,
|
|
675
|
+
agent_name=self._caller_name,
|
|
676
|
+
tool_name=tool_name,
|
|
677
|
+
tool_kind=tool_kind,
|
|
678
|
+
call_id=call_id,
|
|
679
|
+
args=final_args,
|
|
680
|
+
session_id=self._session_id,
|
|
681
|
+
)
|
|
682
|
+
)
|
|
683
|
+
try:
|
|
684
|
+
decision["modified_args"] = json.dumps(final_args, ensure_ascii=False)
|
|
685
|
+
except TypeError:
|
|
686
|
+
decision["modified_args"] = json.dumps(modified_args, ensure_ascii=False, default=str)
|
|
687
|
+
if hook_blocked:
|
|
688
|
+
# Hook intercepted the edited args. ``apply_before_tool_hooks``
|
|
689
|
+
# already set ``context.result`` and ``_APPROVAL_REJECTED_KEY``;
|
|
690
|
+
# skip ``call_next`` so the tool doesn't run.
|
|
691
|
+
return
|
|
692
|
+
if binding is not None:
|
|
693
|
+
# An edit is approved once, for where the edited call acts now.
|
|
694
|
+
targets = await asyncio.to_thread(binding.targets, context)
|
|
695
|
+
elif remember_choice and binding is not None and reuse is not None and reuse.candidate is not None:
|
|
696
|
+
await self._remember(binding, reuse.candidate, remember_choice)
|
|
697
|
+
with approved_targets(targets):
|
|
698
|
+
await call_next()
|
|
699
|
+
else:
|
|
700
|
+
# Return error result — LLM sees rejection and can respond naturally.
|
|
701
|
+
# Set metadata flag so ToolEventMiddleware can detect rejection
|
|
702
|
+
# without string matching (decoupled from the user-facing message).
|
|
703
|
+
context.metadata[_APPROVAL_REJECTED_KEY] = True
|
|
704
|
+
message = "Tool execution was rejected by user."
|
|
705
|
+
if reason:
|
|
706
|
+
message = f"{message} User reason: {reason}"
|
|
707
|
+
context.metadata[_REJECTION_SOURCE_KEY] = "user"
|
|
708
|
+
context.metadata[_REJECTION_MESSAGE_KEY] = message
|
|
709
|
+
context.result = "Error: Tool execution was rejected by user."
|
|
710
|
+
if reason:
|
|
711
|
+
context.result = f"{context.result}\nUser reason: {reason}"
|
|
712
|
+
|
|
713
|
+
async def _remember(self, binding: ApprovalReuseBinding, candidate: Candidate, choice: ReuseChoice) -> None:
|
|
714
|
+
"""Save the grant for the request the person saw; a failure still allows this call."""
|
|
715
|
+
if await asyncio.to_thread(binding.service.remember, candidate, choice):
|
|
716
|
+
return
|
|
717
|
+
await self._bus.publish(
|
|
718
|
+
Warning(
|
|
719
|
+
session_id=self._session_id,
|
|
720
|
+
code="approval_reuse_save_failed",
|
|
721
|
+
message=_REMEMBER_FAILED.fallback,
|
|
722
|
+
display_message=_REMEMBER_FAILED.bind(),
|
|
723
|
+
)
|
|
724
|
+
)
|
|
725
|
+
|
|
726
|
+
async def _publish_abandoned(self, request_id: str, *, ended_by: BaseException) -> None:
|
|
727
|
+
"""Retract an unanswered request, even when another cancel lands meanwhile.
|
|
728
|
+
|
|
729
|
+
That cancel is absorbed only when a cancel already ended the wait, which
|
|
730
|
+
the caller re-raises; after a failure it propagates instead, so the
|
|
731
|
+
failure never swallows an interrupt.
|
|
732
|
+
"""
|
|
733
|
+
retract = asyncio.create_task(
|
|
734
|
+
self._bus.publish(ApprovalCancelled(request_id=request_id, session_id=self._session_id))
|
|
735
|
+
)
|
|
736
|
+
try:
|
|
737
|
+
await finish_close(retract)
|
|
738
|
+
except asyncio.CancelledError:
|
|
739
|
+
if not isinstance(ended_by, asyncio.CancelledError):
|
|
740
|
+
raise
|
|
741
|
+
|
|
742
|
+
async def _ensure_auto_fulfill_block_subscription(self) -> None:
|
|
743
|
+
"""Subscribe once to frontend blocks for judge auto-fulfilment."""
|
|
744
|
+
await self._approval_arbiter.ensure_subscription()
|
|
745
|
+
|
|
746
|
+
async def close(self) -> None:
|
|
747
|
+
"""Release EventBus subscriptions owned by this middleware."""
|
|
748
|
+
await self._approval_arbiter.close()
|
|
749
|
+
|
|
750
|
+
def drain_decisions(self) -> list[dict[str, str]]:
|
|
751
|
+
"""Return and clear collected approval decisions."""
|
|
752
|
+
decisions = list(self._decisions)
|
|
753
|
+
self._decisions.clear()
|
|
754
|
+
return decisions
|
|
755
|
+
|
|
756
|
+
def snapshot_retry_state(self) -> ApprovalRetrySnapshot:
|
|
757
|
+
"""Capture completed decisions that must survive a later retry."""
|
|
758
|
+
return ApprovalRetrySnapshot(tuple(dict(decision) for decision in self._decisions))
|
|
759
|
+
|
|
760
|
+
def restore_retry_state(self, snapshot: ApprovalRetrySnapshot) -> None:
|
|
761
|
+
"""Drop failed-attempt decisions while preserving the prior baseline."""
|
|
762
|
+
self._decisions = [dict(decision) for decision in snapshot.decisions]
|
|
763
|
+
|
|
764
|
+
def reset(self) -> None:
|
|
765
|
+
"""Clear per-run state."""
|
|
766
|
+
self._decisions.clear()
|
|
767
|
+
# Keep the EventBus subscription alive across runs; close() owns resource teardown.
|
|
768
|
+
self._approval_arbiter.clear()
|
|
769
|
+
self._turn_context.reset()
|