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,224 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""Total, phase-aware ACP client error classification."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
import errno
|
|
9
|
+
from enum import StrEnum
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from acp.exceptions import RequestError
|
|
13
|
+
|
|
14
|
+
from chrys.foundation.platform.files import surrogate_safe_text
|
|
15
|
+
from chrys.foundation.platform.process import MissingWorkingDirectoryError
|
|
16
|
+
|
|
17
|
+
_DETERMINISTIC_REQUEST_CODES = frozenset({-32700, -32600, -32601, -32602, -32002})
|
|
18
|
+
_AUTH_REQUIRED_CODE = -32000
|
|
19
|
+
_INTERNAL_ERROR_CODE = -32603
|
|
20
|
+
# ETXTBSY is deliberately absent: it clears when the concurrent writer of the
|
|
21
|
+
# executable finishes, so the same launch can succeed on retry.
|
|
22
|
+
_DETERMINISTIC_ERRNOS = frozenset(
|
|
23
|
+
{
|
|
24
|
+
errno.E2BIG,
|
|
25
|
+
errno.EACCES,
|
|
26
|
+
errno.EINVAL,
|
|
27
|
+
errno.EISDIR,
|
|
28
|
+
errno.ELOOP,
|
|
29
|
+
errno.ENAMETOOLONG,
|
|
30
|
+
errno.ENOENT,
|
|
31
|
+
errno.ENOEXEC,
|
|
32
|
+
errno.ENOTDIR,
|
|
33
|
+
errno.EPERM,
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
_DETERMINISTIC_WINERRORS = frozenset({2, 3, 5, 10, 87, 123, 193, 203, 206, 216, 267, 740})
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class AcpOperation(StrEnum):
|
|
40
|
+
"""Protocol operation being classified."""
|
|
41
|
+
|
|
42
|
+
SPAWN = "spawn"
|
|
43
|
+
INITIALIZE = "initialize"
|
|
44
|
+
NEW_SESSION = "session/new"
|
|
45
|
+
SET_MODE = "session/set_mode"
|
|
46
|
+
SET_MODEL = "session/set_model"
|
|
47
|
+
SET_CONFIG_OPTION = "session/set_config_option"
|
|
48
|
+
PROMPT = "session/prompt"
|
|
49
|
+
CLOSE_SESSION = "session/close"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class AcpClientError(RuntimeError):
|
|
53
|
+
"""Base for sanitized client failures safe to expose to model-visible code."""
|
|
54
|
+
|
|
55
|
+
def __init__(self, detail: str, *, cause: BaseException | None = None) -> None:
|
|
56
|
+
super().__init__(_sanitize_detail(detail))
|
|
57
|
+
self.detail = _sanitize_detail(detail)
|
|
58
|
+
self.cause = cause
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class AcpConfigError(AcpClientError):
|
|
62
|
+
"""Deterministic configuration or protocol incompatibility."""
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class AcpSpawnError(AcpClientError):
|
|
66
|
+
"""Deterministic process launch failure."""
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class AcpConnectError(AcpClientError):
|
|
70
|
+
"""Transient failure in the provably stateless connect window."""
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class AcpTransportError(AcpClientError):
|
|
74
|
+
"""Ambiguous post-stateful transport or protocol failure."""
|
|
75
|
+
|
|
76
|
+
def __init__(
|
|
77
|
+
self,
|
|
78
|
+
detail: str,
|
|
79
|
+
*,
|
|
80
|
+
cause: BaseException | None = None,
|
|
81
|
+
usage: Any | None = None,
|
|
82
|
+
) -> None:
|
|
83
|
+
super().__init__(detail, cause=cause)
|
|
84
|
+
self.usage = usage
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class AcpIdleTimeoutError(AcpTransportError):
|
|
88
|
+
"""Prompt inactivity timeout while no human decision is outstanding."""
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class AcpAuthRequiredError(AcpClientError):
|
|
92
|
+
"""The remote requires an authentication method Chrys cannot perform."""
|
|
93
|
+
|
|
94
|
+
def __init__(
|
|
95
|
+
self,
|
|
96
|
+
detail: str = "The ACP agent requires authentication.",
|
|
97
|
+
*,
|
|
98
|
+
method_names: tuple[str, ...] = (),
|
|
99
|
+
cause: BaseException | None = None,
|
|
100
|
+
) -> None:
|
|
101
|
+
super().__init__(detail, cause=cause)
|
|
102
|
+
self.method_names = method_names
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class AcpRefusalError(AcpClientError):
|
|
106
|
+
"""Terminal remote refusal, distinct from a transport pause."""
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _sanitize_detail(detail: str) -> str:
|
|
110
|
+
line = str(detail).replace("\r", " ").replace("\n", " ").strip()
|
|
111
|
+
if not line:
|
|
112
|
+
return "ACP operation failed."
|
|
113
|
+
return line[:500]
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _request_auth_methods(error: RequestError) -> tuple[str, ...]:
|
|
117
|
+
data = error.data
|
|
118
|
+
if not isinstance(data, dict):
|
|
119
|
+
return ()
|
|
120
|
+
raw_methods = data.get("authMethods", data.get("auth_methods", data.get("methods", ())))
|
|
121
|
+
if not isinstance(raw_methods, list | tuple):
|
|
122
|
+
return ()
|
|
123
|
+
names: list[str] = []
|
|
124
|
+
for item in raw_methods:
|
|
125
|
+
if isinstance(item, str):
|
|
126
|
+
value = item
|
|
127
|
+
elif isinstance(item, dict):
|
|
128
|
+
value = item.get("name", item.get("id"))
|
|
129
|
+
if not isinstance(value, str):
|
|
130
|
+
continue
|
|
131
|
+
else:
|
|
132
|
+
continue
|
|
133
|
+
sanitized = value.replace("\r", " ").replace("\n", " ").strip()[:100]
|
|
134
|
+
if sanitized:
|
|
135
|
+
names.append(sanitized)
|
|
136
|
+
return tuple(names)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def is_remote_error(error: AcpClientError) -> bool:
|
|
140
|
+
"""Return whether the remote agent itself answered with the JSON-RPC error behind *error*."""
|
|
141
|
+
return isinstance(error.cause, RequestError)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def is_deterministic_request_error(error: RequestError) -> bool:
|
|
145
|
+
"""Return whether retrying the same request cannot change its rejection."""
|
|
146
|
+
return type(error.code) is int and error.code in _DETERMINISTIC_REQUEST_CODES
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def is_best_effort_option_rejection(error: RequestError) -> bool:
|
|
150
|
+
"""Return whether a configured option may be skipped under best-effort policy."""
|
|
151
|
+
return type(error.code) is int and error.code in {-32601, -32602}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def classify_spawn_error(error: BaseException) -> AcpSpawnError | AcpConnectError:
|
|
155
|
+
"""Classify every subprocess-boundary failure as deterministic or transient."""
|
|
156
|
+
if isinstance(error, TypeError | ValueError):
|
|
157
|
+
return AcpSpawnError("The ACP process launch configuration is invalid.", cause=error)
|
|
158
|
+
if isinstance(error, MissingWorkingDirectoryError):
|
|
159
|
+
return AcpSpawnError(
|
|
160
|
+
f"The ACP agent's working directory no longer exists: {surrogate_safe_text(error.path)}", cause=error
|
|
161
|
+
)
|
|
162
|
+
if isinstance(error, OSError):
|
|
163
|
+
winerror = getattr(error, "winerror", None)
|
|
164
|
+
if error.errno in _DETERMINISTIC_ERRNOS or winerror in _DETERMINISTIC_WINERRORS:
|
|
165
|
+
return AcpSpawnError("The ACP process could not be launched with this configuration.", cause=error)
|
|
166
|
+
return AcpConnectError("The ACP process could not be started due to a transient system error.", cause=error)
|
|
167
|
+
return AcpConnectError("The ACP process could not be started.", cause=error)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def classify_protocol_frame_error(
|
|
171
|
+
error: BaseException,
|
|
172
|
+
*,
|
|
173
|
+
stateful: bool,
|
|
174
|
+
complete: bool,
|
|
175
|
+
) -> AcpConfigError | AcpConnectError | AcpTransportError:
|
|
176
|
+
"""Classify a syntactically invalid complete line versus EOF-truncated frame."""
|
|
177
|
+
if stateful:
|
|
178
|
+
return AcpTransportError("The ACP agent sent an invalid protocol frame.", cause=error)
|
|
179
|
+
if complete:
|
|
180
|
+
return AcpConfigError("The ACP executable did not produce valid ACP JSON-RPC.", cause=error)
|
|
181
|
+
return AcpConnectError("The ACP process ended while writing a protocol frame.", cause=error)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def classify_acp_error(
|
|
185
|
+
error: BaseException,
|
|
186
|
+
*,
|
|
187
|
+
operation: AcpOperation,
|
|
188
|
+
stateful: bool,
|
|
189
|
+
) -> AcpClientError:
|
|
190
|
+
"""Map every known SDK/validation/transport failure to one settled policy class."""
|
|
191
|
+
if isinstance(error, AcpClientError):
|
|
192
|
+
return error
|
|
193
|
+
if operation is AcpOperation.SPAWN:
|
|
194
|
+
return classify_spawn_error(error)
|
|
195
|
+
if isinstance(error, RequestError):
|
|
196
|
+
if type(error.code) is int and error.code == _AUTH_REQUIRED_CODE:
|
|
197
|
+
return AcpAuthRequiredError(method_names=_request_auth_methods(error), cause=error)
|
|
198
|
+
if is_deterministic_request_error(error):
|
|
199
|
+
return AcpConfigError(f"The ACP agent rejected {operation.value}.", cause=error)
|
|
200
|
+
if type(error.code) is int and error.code == _INTERNAL_ERROR_CODE:
|
|
201
|
+
if stateful:
|
|
202
|
+
return AcpTransportError(f"The ACP agent failed during {operation.value}.", cause=error)
|
|
203
|
+
return AcpConnectError(f"The ACP agent failed during {operation.value}.", cause=error)
|
|
204
|
+
if stateful:
|
|
205
|
+
return AcpTransportError(f"The ACP agent returned an unknown error during {operation.value}.", cause=error)
|
|
206
|
+
return AcpConnectError(f"The ACP agent returned an unknown error during {operation.value}.", cause=error)
|
|
207
|
+
if _is_validation_error(error):
|
|
208
|
+
if operation is AcpOperation.INITIALIZE:
|
|
209
|
+
return AcpConfigError("The ACP agent returned an incompatible initialize response.", cause=error)
|
|
210
|
+
if stateful:
|
|
211
|
+
return AcpTransportError(f"The ACP agent returned a malformed {operation.value} response.", cause=error)
|
|
212
|
+
return AcpConnectError(f"The ACP agent returned a malformed {operation.value} response.", cause=error)
|
|
213
|
+
if isinstance(error, TimeoutError | ConnectionError | EOFError | OSError | asyncio.CancelledError):
|
|
214
|
+
if stateful:
|
|
215
|
+
return AcpTransportError(f"The ACP transport failed during {operation.value}.", cause=error)
|
|
216
|
+
return AcpConnectError(f"The ACP transport failed during {operation.value}.", cause=error)
|
|
217
|
+
if stateful:
|
|
218
|
+
return AcpTransportError(f"The ACP client failed during {operation.value}.", cause=error)
|
|
219
|
+
return AcpConnectError(f"The ACP client failed during {operation.value}.", cause=error)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _is_validation_error(error: BaseException) -> bool:
|
|
223
|
+
error_type = type(error)
|
|
224
|
+
return error_type.__name__ == "ValidationError" and error_type.__module__.startswith("pydantic")
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""ACP protocol validation and retained payload caps."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import base64
|
|
8
|
+
import binascii
|
|
9
|
+
import json
|
|
10
|
+
import math
|
|
11
|
+
from typing import Any, Literal
|
|
12
|
+
|
|
13
|
+
from chrys.foundation.text.images import MAX_IMAGE_BYTES
|
|
14
|
+
from chrys.foundation.util.unicode_scalars import find_unpaired_surrogate
|
|
15
|
+
|
|
16
|
+
_MAX_PAYLOAD_BYTES = 1024 * 1024
|
|
17
|
+
_MAX_PAYLOAD_DEPTH = 32
|
|
18
|
+
_MAX_COLLECTION_ITEMS = 4_096
|
|
19
|
+
_MAX_STRING_CHARS = 256 * 1024
|
|
20
|
+
_MAX_BASE64_IMAGE_CHARS = ((MAX_IMAGE_BYTES + 2) // 3) * 4
|
|
21
|
+
_MAX_REQUEST_ID_CHARS = 4_096
|
|
22
|
+
_MAX_METHOD_CHARS = 1_024
|
|
23
|
+
|
|
24
|
+
_PERMISSION_METHOD = "session/request_permission"
|
|
25
|
+
_ASK_USER_METHOD = "_chrys/request_input"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def validate_json_scalar_tree(value: Any) -> None:
|
|
29
|
+
"""Validate every JSON key, value, and sequence element recursively."""
|
|
30
|
+
if value is None or type(value) in {bool, int}:
|
|
31
|
+
return
|
|
32
|
+
if type(value) is float:
|
|
33
|
+
if not math.isfinite(value):
|
|
34
|
+
raise ValueError("JSON numbers must be finite.")
|
|
35
|
+
return
|
|
36
|
+
if type(value) is str:
|
|
37
|
+
_validate_surrogates(value)
|
|
38
|
+
return
|
|
39
|
+
if type(value) is dict:
|
|
40
|
+
for key, item in value.items():
|
|
41
|
+
if type(key) is not str:
|
|
42
|
+
raise ValueError("JSON object keys must be strings.")
|
|
43
|
+
_validate_surrogates(key)
|
|
44
|
+
validate_json_scalar_tree(item)
|
|
45
|
+
return
|
|
46
|
+
if type(value) in {list, tuple}:
|
|
47
|
+
for item in value:
|
|
48
|
+
validate_json_scalar_tree(item)
|
|
49
|
+
return
|
|
50
|
+
raise ValueError(f"Unsupported JSON value type: {type(value).__name__}")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _validate_surrogates(value: str) -> None:
|
|
54
|
+
if find_unpaired_surrogate(value) >= 0:
|
|
55
|
+
raise ValueError("JSON strings cannot contain unpaired surrogates.")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def encode_protocol_json(value: Any) -> bytes:
|
|
59
|
+
"""Encode a protocol-bound value after the shared scalar validation pass."""
|
|
60
|
+
validate_json_scalar_tree(value)
|
|
61
|
+
return json.dumps(value, separators=(",", ":"), ensure_ascii=True, allow_nan=False).encode("utf-8")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def parse_protocol_json(raw: bytes) -> Any:
|
|
65
|
+
"""Parse one JSON value while rejecting JavaScript non-finite constants."""
|
|
66
|
+
|
|
67
|
+
def reject_constant(value: str) -> None:
|
|
68
|
+
raise ValueError(f"Invalid JSON numeric constant: {value}")
|
|
69
|
+
|
|
70
|
+
parsed = json.loads(raw, parse_constant=reject_constant)
|
|
71
|
+
validate_json_scalar_tree(parsed)
|
|
72
|
+
return parsed
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
_ENVELOPE_MEMBERS = frozenset({"jsonrpc", "id", "method", "params", "result", "error"})
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def validate_json_rpc_envelope(message: Any) -> Literal["request", "notification", "response"]:
|
|
79
|
+
"""Validate the strict JSON-RPC envelope accepted by the SDK-facing reader."""
|
|
80
|
+
if type(message) is not dict:
|
|
81
|
+
raise ValueError("JSON-RPC frame root must be an object.")
|
|
82
|
+
# The per-kind caps run on params/result only, and the SDK re-parses the
|
|
83
|
+
# complete frame; an unknown top-level member would ride around both.
|
|
84
|
+
if not _ENVELOPE_MEMBERS.issuperset(message):
|
|
85
|
+
raise ValueError("JSON-RPC frame contains unknown envelope members.")
|
|
86
|
+
if message.get("jsonrpc") != "2.0" or type(message.get("jsonrpc")) is not str:
|
|
87
|
+
raise ValueError("JSON-RPC frame must declare version 2.0.")
|
|
88
|
+
|
|
89
|
+
has_id = "id" in message
|
|
90
|
+
has_method = "method" in message
|
|
91
|
+
has_result = "result" in message
|
|
92
|
+
has_error = "error" in message
|
|
93
|
+
|
|
94
|
+
if has_id and type(message["id"]) not in {str, int}:
|
|
95
|
+
raise ValueError("JSON-RPC ids must be exact strings or integers.")
|
|
96
|
+
# Ids and methods are retained (inbound map keys, outgoing records) and
|
|
97
|
+
# echoed into response frames, so they carry their own §7.4-style caps.
|
|
98
|
+
if has_id and type(message["id"]) is str and len(message["id"]) > _MAX_REQUEST_ID_CHARS:
|
|
99
|
+
raise ValueError("JSON-RPC id exceeds the retained-field cap.")
|
|
100
|
+
if has_id and type(message["id"]) is int and not -(1 << 63) <= message["id"] < 1 << 63:
|
|
101
|
+
raise ValueError("JSON-RPC id exceeds the interoperable integer range.")
|
|
102
|
+
if has_method and type(message["method"]) is not str:
|
|
103
|
+
raise ValueError("JSON-RPC methods must be strings.")
|
|
104
|
+
if has_method and len(message["method"]) > _MAX_METHOD_CHARS:
|
|
105
|
+
raise ValueError("JSON-RPC method exceeds the retained-field cap.")
|
|
106
|
+
if "params" in message:
|
|
107
|
+
if not has_method:
|
|
108
|
+
raise ValueError("JSON-RPC responses cannot contain params.")
|
|
109
|
+
if type(message["params"]) is not dict:
|
|
110
|
+
raise ValueError("JSON-RPC params must be an object.")
|
|
111
|
+
|
|
112
|
+
if has_method:
|
|
113
|
+
if has_result or has_error:
|
|
114
|
+
raise ValueError("JSON-RPC requests cannot contain result or error.")
|
|
115
|
+
return "request" if has_id else "notification"
|
|
116
|
+
|
|
117
|
+
if not has_id or has_result == has_error:
|
|
118
|
+
raise ValueError("JSON-RPC responses require an id and exactly one of result or error.")
|
|
119
|
+
if has_error:
|
|
120
|
+
error = message["error"]
|
|
121
|
+
if type(error) is not dict:
|
|
122
|
+
raise ValueError("JSON-RPC error must be an object.")
|
|
123
|
+
if type(error.get("code")) is not int or type(error.get("message")) is not str:
|
|
124
|
+
raise ValueError("JSON-RPC error requires an exact integer code and string message.")
|
|
125
|
+
return "response"
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _measure_payload(value: Any, *, depth: int = 0) -> tuple[int, int]:
|
|
129
|
+
if depth > _MAX_PAYLOAD_DEPTH:
|
|
130
|
+
raise ValueError("ACP payload nesting is too deep.")
|
|
131
|
+
if type(value) is str:
|
|
132
|
+
if len(value) > _MAX_STRING_CHARS:
|
|
133
|
+
raise ValueError("ACP payload string is too large.")
|
|
134
|
+
return len(value.encode("utf-8", errors="surrogatepass")), 1
|
|
135
|
+
if type(value) is dict:
|
|
136
|
+
if len(value) > _MAX_COLLECTION_ITEMS:
|
|
137
|
+
raise ValueError("ACP payload object has too many fields.")
|
|
138
|
+
total = 0
|
|
139
|
+
items = 1
|
|
140
|
+
for key, item in value.items():
|
|
141
|
+
if len(key) > _MAX_STRING_CHARS:
|
|
142
|
+
raise ValueError("ACP payload string is too large.")
|
|
143
|
+
total += len(key.encode("utf-8", errors="surrogatepass"))
|
|
144
|
+
child_bytes, child_items = _measure_payload(item, depth=depth + 1)
|
|
145
|
+
total += child_bytes
|
|
146
|
+
items += child_items
|
|
147
|
+
return total, items
|
|
148
|
+
if type(value) in {list, tuple}:
|
|
149
|
+
if len(value) > _MAX_COLLECTION_ITEMS:
|
|
150
|
+
raise ValueError("ACP payload sequence has too many items.")
|
|
151
|
+
total = 0
|
|
152
|
+
items = 1
|
|
153
|
+
for item in value:
|
|
154
|
+
child_bytes, child_items = _measure_payload(item, depth=depth + 1)
|
|
155
|
+
total += child_bytes
|
|
156
|
+
items += child_items
|
|
157
|
+
return total, items
|
|
158
|
+
return 16, 1
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _validate_payload_caps(value: Any) -> None:
|
|
162
|
+
_total_bytes, total_items = _measure_payload(value)
|
|
163
|
+
# Per-collection checks alone admit 4096-wide children at every level;
|
|
164
|
+
# the aggregate bound is what actually caps the retained object count.
|
|
165
|
+
if total_items > _MAX_COLLECTION_ITEMS:
|
|
166
|
+
raise ValueError("ACP payload has too many items in total.")
|
|
167
|
+
if len(encode_protocol_json(value)) > _MAX_PAYLOAD_BYTES:
|
|
168
|
+
raise ValueError("ACP retained payload exceeds the byte limit.")
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _validate_update_image_data(data: str) -> None:
|
|
172
|
+
"""Validate one image payload before exempting its encoding overhead."""
|
|
173
|
+
if not data:
|
|
174
|
+
raise ValueError("ACP image payload is empty.")
|
|
175
|
+
if len(data) > _MAX_BASE64_IMAGE_CHARS:
|
|
176
|
+
raise ValueError("ACP image payload exceeds the supported size limit.")
|
|
177
|
+
try:
|
|
178
|
+
decoded = base64.b64decode(data, validate=True)
|
|
179
|
+
except (binascii.Error, ValueError) as exc:
|
|
180
|
+
raise ValueError("ACP image payload is not valid base64.") from exc
|
|
181
|
+
if not decoded:
|
|
182
|
+
raise ValueError("ACP image payload is empty.")
|
|
183
|
+
if len(decoded) > MAX_IMAGE_BYTES:
|
|
184
|
+
raise ValueError("ACP image payload exceeds the supported size limit.")
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _payload_without_tool_image_data(value: Any) -> Any:
|
|
188
|
+
"""Replace validated tool-image encodings for retained-cap accounting."""
|
|
189
|
+
if type(value) is not dict:
|
|
190
|
+
return value
|
|
191
|
+
update = value.get("update")
|
|
192
|
+
if type(update) is not dict or update.get("sessionUpdate") not in {"tool_call", "tool_call_update"}:
|
|
193
|
+
return value
|
|
194
|
+
content = update.get("content")
|
|
195
|
+
if type(content) is not list:
|
|
196
|
+
return value
|
|
197
|
+
|
|
198
|
+
bounded_content: list[Any] | None = None
|
|
199
|
+
for index, item in enumerate(content):
|
|
200
|
+
if type(item) is not dict or item.get("type") != "content":
|
|
201
|
+
continue
|
|
202
|
+
block = item.get("content")
|
|
203
|
+
if type(block) is not dict or block.get("type") != "image" or type(block.get("mimeType")) is not str:
|
|
204
|
+
continue
|
|
205
|
+
data = block.get("data")
|
|
206
|
+
if type(data) is not str:
|
|
207
|
+
continue
|
|
208
|
+
_validate_update_image_data(data)
|
|
209
|
+
if bounded_content is None:
|
|
210
|
+
bounded_content = list(content)
|
|
211
|
+
bounded_content[index] = {**item, "content": {**block, "data": ""}}
|
|
212
|
+
|
|
213
|
+
if bounded_content is None:
|
|
214
|
+
return value
|
|
215
|
+
return {**value, "update": {**update, "content": bounded_content}}
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _validate_update_payload_caps(value: Any) -> None:
|
|
219
|
+
"""Apply retained caps while excluding validated tool-image encoding overhead."""
|
|
220
|
+
_validate_payload_caps(_payload_without_tool_image_data(value))
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""Owned ACP process spawning, environment hygiene, and bounded stderr capture."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
import contextlib
|
|
9
|
+
import errno
|
|
10
|
+
import math
|
|
11
|
+
import os
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import BinaryIO
|
|
15
|
+
|
|
16
|
+
from acp import default_environment
|
|
17
|
+
|
|
18
|
+
from chrys.foundation.platform.files import SecureFileError, secure_open_owner_only
|
|
19
|
+
from chrys.foundation.platform.process import ManagedStdioProcess, spawn_managed_stdio_process
|
|
20
|
+
|
|
21
|
+
from .errors import AcpClientError, AcpConfigError, classify_spawn_error
|
|
22
|
+
from .spec import AcpAgentSpec
|
|
23
|
+
|
|
24
|
+
ACP_STDIO_LIMIT_BYTES = 50 * 1024 * 1024
|
|
25
|
+
STDERR_LOG_QUOTA_BYTES = 5 * 1024 * 1024
|
|
26
|
+
STDERR_TAIL_BYTES = 4 * 1024
|
|
27
|
+
_STDERR_DRAIN_TIMEOUT_SECONDS = 2.0
|
|
28
|
+
DEPTH_ENV_VAR = "CHRYS_ACP_SUBAGENT_DEPTH"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _validate_exact_spawn_string(value: object, *, field: str) -> str:
|
|
32
|
+
if type(value) is not str:
|
|
33
|
+
raise AcpConfigError(f"ACP {field} must be a string.")
|
|
34
|
+
if "\x00" in value:
|
|
35
|
+
raise AcpConfigError(f"ACP {field} cannot contain NUL.")
|
|
36
|
+
return value
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def build_child_environment(spec: AcpAgentSpec) -> dict[str, str]:
|
|
40
|
+
"""Build a case-fold-deduplicated trimmed environment for one child."""
|
|
41
|
+
if type(spec.depth) is not int or spec.depth < 0:
|
|
42
|
+
raise AcpConfigError("ACP recursion depth must be a non-negative integer.")
|
|
43
|
+
|
|
44
|
+
base = default_environment()
|
|
45
|
+
canonical_by_fold = {key.casefold(): key for key in base}
|
|
46
|
+
merged: dict[str, str] = {}
|
|
47
|
+
present_by_fold: dict[str, str] = {}
|
|
48
|
+
|
|
49
|
+
def overlay(key: object, value: object, *, preserve_base_case: bool) -> None:
|
|
50
|
+
key_text = _validate_exact_spawn_string(key, field="environment key")
|
|
51
|
+
value_text = _validate_exact_spawn_string(value, field=f"environment value for {key_text!r}")
|
|
52
|
+
if not key_text or "=" in key_text:
|
|
53
|
+
raise AcpConfigError("ACP environment keys must be non-empty and cannot contain '='.")
|
|
54
|
+
folded = key_text.casefold()
|
|
55
|
+
previous = present_by_fold.get(folded)
|
|
56
|
+
if previous is not None:
|
|
57
|
+
merged.pop(previous, None)
|
|
58
|
+
output_key = canonical_by_fold.get(folded, key_text) if preserve_base_case else key_text
|
|
59
|
+
merged[output_key] = value_text
|
|
60
|
+
present_by_fold[folded] = output_key
|
|
61
|
+
|
|
62
|
+
for key, value in base.items():
|
|
63
|
+
overlay(key, value, preserve_base_case=True)
|
|
64
|
+
for key, value in spec.env.items():
|
|
65
|
+
overlay(key, value, preserve_base_case=True)
|
|
66
|
+
overlay(DEPTH_ENV_VAR, str(spec.depth + 1), preserve_base_case=False)
|
|
67
|
+
return merged
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def validate_agent_spec(spec: AcpAgentSpec) -> None:
|
|
71
|
+
"""Fail closed on malformed values that reach the process/protocol boundary."""
|
|
72
|
+
_validate_exact_spawn_string(spec.command, field="command")
|
|
73
|
+
cwd = _validate_exact_spawn_string(spec.cwd, field="cwd")
|
|
74
|
+
if not os.path.isabs(cwd):
|
|
75
|
+
raise AcpConfigError("ACP cwd must be absolute.")
|
|
76
|
+
for arg in spec.args:
|
|
77
|
+
_validate_exact_spawn_string(arg, field="argument")
|
|
78
|
+
for key, value in spec.env.items():
|
|
79
|
+
_validate_exact_spawn_string(key, field="environment key")
|
|
80
|
+
_validate_exact_spawn_string(value, field=f"environment value for {key!r}")
|
|
81
|
+
if type(spec.handshake_timeout_seconds) not in {int, float} or not math.isfinite(spec.handshake_timeout_seconds):
|
|
82
|
+
raise AcpConfigError("ACP handshake timeout must be finite.")
|
|
83
|
+
if spec.handshake_timeout_seconds <= 0:
|
|
84
|
+
raise AcpConfigError("ACP handshake timeout must be greater than zero.")
|
|
85
|
+
if type(spec.idle_timeout_seconds) not in {int, float} or not math.isfinite(spec.idle_timeout_seconds):
|
|
86
|
+
raise AcpConfigError("ACP idle timeout must be finite.")
|
|
87
|
+
if spec.idle_timeout_seconds < 0:
|
|
88
|
+
raise AcpConfigError("ACP idle timeout cannot be negative.")
|
|
89
|
+
if type(spec.best_effort_options) is not bool:
|
|
90
|
+
raise AcpConfigError("ACP best-effort option mode must be a boolean.")
|
|
91
|
+
if type(spec.session_mode) is not str or type(spec.model_id) is not str:
|
|
92
|
+
raise AcpConfigError("ACP session mode and model id must be strings.")
|
|
93
|
+
for root in spec.additional_directories:
|
|
94
|
+
directory = _validate_exact_spawn_string(root, field="additional workspace directory")
|
|
95
|
+
# ACP requires every additional workspace root to be absolute; a
|
|
96
|
+
# relative value would resolve agent-dependently.
|
|
97
|
+
if not os.path.isabs(directory):
|
|
98
|
+
raise AcpConfigError("ACP additional workspace directories must be absolute.")
|
|
99
|
+
for option_id, value in spec.config_options.items():
|
|
100
|
+
if type(option_id) is not str or not option_id:
|
|
101
|
+
raise AcpConfigError("ACP config option ids must be non-empty strings.")
|
|
102
|
+
if type(value) not in {str, bool}:
|
|
103
|
+
raise AcpConfigError("ACP config option values must be exact strings or booleans.")
|
|
104
|
+
if type(spec.depth) is not int or spec.depth < 0:
|
|
105
|
+
raise AcpConfigError("ACP recursion depth must be a non-negative integer.")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class StderrCapture:
|
|
109
|
+
"""Quota-bounded owner-only stderr log plus a small diagnostics tail."""
|
|
110
|
+
|
|
111
|
+
def __init__(self, reader: asyncio.StreamReader, path: Path) -> None:
|
|
112
|
+
self._reader = reader
|
|
113
|
+
self._path = path
|
|
114
|
+
self._tail = bytearray()
|
|
115
|
+
self._written_bytes = 0
|
|
116
|
+
self._dropped_bytes = 0
|
|
117
|
+
self._task: asyncio.Task[None] | None = None
|
|
118
|
+
self._file: BinaryIO | None = None
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def tail(self) -> str:
|
|
122
|
+
return bytes(self._tail).decode("utf-8", errors="replace")
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def dropped_bytes(self) -> int:
|
|
126
|
+
return self._dropped_bytes
|
|
127
|
+
|
|
128
|
+
def start(self) -> None:
|
|
129
|
+
if self._task is not None:
|
|
130
|
+
raise RuntimeError("stderr capture already started")
|
|
131
|
+
self._path.parent.mkdir(parents=True, exist_ok=True)
|
|
132
|
+
try:
|
|
133
|
+
fd = secure_open_owner_only(self._path, write=True, create=True, truncate=True)
|
|
134
|
+
except SecureFileError as exc:
|
|
135
|
+
if exc.errno not in {errno.EEXIST, 80, 183}:
|
|
136
|
+
raise
|
|
137
|
+
fd = secure_open_owner_only(self._path, write=True, truncate=True)
|
|
138
|
+
self._file = os.fdopen(fd, "wb", closefd=True)
|
|
139
|
+
self._task = asyncio.create_task(self._run(), name="chrys.acp.stderr")
|
|
140
|
+
|
|
141
|
+
async def _run(self) -> None:
|
|
142
|
+
try:
|
|
143
|
+
while chunk := await self._reader.read(64 * 1024):
|
|
144
|
+
self._tail.extend(chunk)
|
|
145
|
+
if len(self._tail) > STDERR_TAIL_BYTES:
|
|
146
|
+
del self._tail[:-STDERR_TAIL_BYTES]
|
|
147
|
+
remaining = STDERR_LOG_QUOTA_BYTES - self._written_bytes
|
|
148
|
+
if remaining <= 0 or self._file is None:
|
|
149
|
+
self._dropped_bytes += len(chunk)
|
|
150
|
+
continue
|
|
151
|
+
payload = chunk[:remaining]
|
|
152
|
+
try:
|
|
153
|
+
written = self._file.write(payload)
|
|
154
|
+
except OSError:
|
|
155
|
+
self._dropped_bytes += len(chunk)
|
|
156
|
+
self._close_file()
|
|
157
|
+
continue
|
|
158
|
+
self._written_bytes += written
|
|
159
|
+
self._dropped_bytes += len(chunk) - written
|
|
160
|
+
finally:
|
|
161
|
+
self._close_file()
|
|
162
|
+
|
|
163
|
+
def _close_file(self) -> None:
|
|
164
|
+
if self._file is not None:
|
|
165
|
+
with contextlib.suppress(OSError):
|
|
166
|
+
self._file.flush()
|
|
167
|
+
with contextlib.suppress(OSError):
|
|
168
|
+
os.fsync(self._file.fileno())
|
|
169
|
+
with contextlib.suppress(OSError):
|
|
170
|
+
self._file.close()
|
|
171
|
+
self._file = None
|
|
172
|
+
|
|
173
|
+
async def stop(self) -> None:
|
|
174
|
+
task = self._task
|
|
175
|
+
if task is None:
|
|
176
|
+
return
|
|
177
|
+
# Bounded drain before cancel: the child is already dead or dying when
|
|
178
|
+
# the close ladder gets here, so EOF arrives promptly — cancelling
|
|
179
|
+
# first would discard flushed-but-unread final diagnostics.
|
|
180
|
+
with contextlib.suppress(asyncio.CancelledError, Exception):
|
|
181
|
+
await asyncio.wait({task}, timeout=_STDERR_DRAIN_TIMEOUT_SECONDS)
|
|
182
|
+
if not task.done():
|
|
183
|
+
task.cancel()
|
|
184
|
+
with contextlib.suppress(asyncio.CancelledError, Exception):
|
|
185
|
+
await task
|
|
186
|
+
self._task = None
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@dataclass(slots=True)
|
|
190
|
+
class AcpSpawnResult:
|
|
191
|
+
"""Spawned process and its owned stderr diagnostic capture."""
|
|
192
|
+
|
|
193
|
+
process: ManagedStdioProcess
|
|
194
|
+
stderr: StderrCapture
|
|
195
|
+
|
|
196
|
+
@property
|
|
197
|
+
def stderr_tail(self) -> str:
|
|
198
|
+
return self.stderr.tail
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
async def spawn_acp_process(spec: AcpAgentSpec) -> AcpSpawnResult:
|
|
202
|
+
"""Launch the resolved ACP command with an owned process tree and streams."""
|
|
203
|
+
validate_agent_spec(spec)
|
|
204
|
+
command = _validate_exact_spawn_string(spec.command, field="command")
|
|
205
|
+
cwd = _validate_exact_spawn_string(spec.cwd, field="cwd")
|
|
206
|
+
args = tuple(_validate_exact_spawn_string(arg, field="argument") for arg in spec.args)
|
|
207
|
+
parent_env = dict(os.environ)
|
|
208
|
+
env = build_child_environment(spec)
|
|
209
|
+
try:
|
|
210
|
+
process = await spawn_managed_stdio_process(
|
|
211
|
+
command,
|
|
212
|
+
*args,
|
|
213
|
+
cwd=cwd,
|
|
214
|
+
env=env,
|
|
215
|
+
limit=ACP_STDIO_LIMIT_BYTES,
|
|
216
|
+
parent_env=parent_env,
|
|
217
|
+
)
|
|
218
|
+
except asyncio.CancelledError:
|
|
219
|
+
raise
|
|
220
|
+
except AcpClientError:
|
|
221
|
+
raise
|
|
222
|
+
except BaseException as exc:
|
|
223
|
+
raise classify_spawn_error(exc) from exc
|
|
224
|
+
|
|
225
|
+
capture = StderrCapture(process.stderr, spec.stderr_log_path)
|
|
226
|
+
try:
|
|
227
|
+
capture.start()
|
|
228
|
+
except BaseException as exc:
|
|
229
|
+
process.kill_tree()
|
|
230
|
+
try:
|
|
231
|
+
# Cancellation during this wait must still release the transports:
|
|
232
|
+
# the caller has no AcpSpawnResult yet, so its rollback cannot
|
|
233
|
+
# reach the process handles this function still owns.
|
|
234
|
+
with contextlib.suppress(Exception):
|
|
235
|
+
await asyncio.wait_for(process.wait(), timeout=2)
|
|
236
|
+
finally:
|
|
237
|
+
process.close_transports()
|
|
238
|
+
raise AcpConfigError("The ACP stderr log could not be created securely.", cause=exc) from exc
|
|
239
|
+
return AcpSpawnResult(process=process, stderr=capture)
|