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,1023 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""Headless session host for running Chrys agents outside the TUI."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
import contextlib
|
|
9
|
+
import os
|
|
10
|
+
from collections.abc import AsyncIterator, Callable
|
|
11
|
+
from dataclasses import dataclass, field, replace
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Literal
|
|
14
|
+
|
|
15
|
+
from chrys.foundation.config.settings import Settings
|
|
16
|
+
from chrys.foundation.config.settings_store import LoadedSettings
|
|
17
|
+
from chrys.foundation.events.bus import EventBus
|
|
18
|
+
from chrys.foundation.events.types import (
|
|
19
|
+
AgentLoadFailed,
|
|
20
|
+
AgentLoadFinished,
|
|
21
|
+
AgentLoadProgress,
|
|
22
|
+
AgentLoadStarted,
|
|
23
|
+
AgentRuntimeUpdated,
|
|
24
|
+
AgentThinking,
|
|
25
|
+
ApprovalModeUpdated,
|
|
26
|
+
ApprovalRequest,
|
|
27
|
+
ApprovalReviewed,
|
|
28
|
+
CompactionFinished,
|
|
29
|
+
CompactionStarted,
|
|
30
|
+
ContextCompressed,
|
|
31
|
+
Error,
|
|
32
|
+
Event,
|
|
33
|
+
InvocationAborted,
|
|
34
|
+
InvocationCascadeAborted,
|
|
35
|
+
InvocationCompactionCommitted,
|
|
36
|
+
InvocationCompactionFinished,
|
|
37
|
+
InvocationCompactionStarted,
|
|
38
|
+
InvocationContextPressure,
|
|
39
|
+
InvocationEvent,
|
|
40
|
+
InvocationMessage,
|
|
41
|
+
InvocationPaused,
|
|
42
|
+
InvocationPresentationAttemptAccepted,
|
|
43
|
+
InvocationPresentationAttemptRejected,
|
|
44
|
+
InvocationProgress,
|
|
45
|
+
InvocationResumed,
|
|
46
|
+
InvocationRetryAttempt,
|
|
47
|
+
InvocationStarted,
|
|
48
|
+
InvocationToolCallArgsUpdated,
|
|
49
|
+
InvocationToolCallProgress,
|
|
50
|
+
InvocationToolCallResult,
|
|
51
|
+
InvocationToolCallStart,
|
|
52
|
+
InvocationToolCallStatusUpdated,
|
|
53
|
+
QuestionToUser,
|
|
54
|
+
SessionRestore,
|
|
55
|
+
SessionRestored,
|
|
56
|
+
SessionSaved,
|
|
57
|
+
StateChanged,
|
|
58
|
+
TodoListUpdated,
|
|
59
|
+
ToolCompacted,
|
|
60
|
+
UsageUpdate,
|
|
61
|
+
UserInjectResult,
|
|
62
|
+
UserInterrupt,
|
|
63
|
+
UserMessage,
|
|
64
|
+
Warning,
|
|
65
|
+
WorkflowCancelRequest,
|
|
66
|
+
WorkflowNodeStateChanged,
|
|
67
|
+
WorkflowRunAccepted,
|
|
68
|
+
WorkflowRunFinished,
|
|
69
|
+
WorkflowRunRejected,
|
|
70
|
+
WorkflowRunRequest,
|
|
71
|
+
)
|
|
72
|
+
from chrys.foundation.events.workflow import WORKFLOW_RUN_EVENTS
|
|
73
|
+
from chrys.foundation.i18n import DisplayBlock, DisplaySequence, MessageRef, msg
|
|
74
|
+
from chrys.foundation.models.session_surface import SessionSurface
|
|
75
|
+
from chrys.foundation.models.workflow_session import (
|
|
76
|
+
WorkflowDraft,
|
|
77
|
+
WorkflowSessionSelection,
|
|
78
|
+
WorkflowTarget,
|
|
79
|
+
WorkspaceSnapshot,
|
|
80
|
+
)
|
|
81
|
+
from chrys.foundation.models.workspace import Workspace
|
|
82
|
+
from chrys.foundation.platform import get_platform
|
|
83
|
+
from chrys.foundation.trajectory.ids import new_analytics_id
|
|
84
|
+
from chrys.foundation.util.session_ids import SESSION_SHORT_ID_LEN, session_short_id
|
|
85
|
+
from chrys.orchestration.engine.assembly import assemble_agent_engine
|
|
86
|
+
from chrys.orchestration.engine.engine import AgentEngine
|
|
87
|
+
from chrys.orchestration.workflows.catalog import WorkflowCatalog
|
|
88
|
+
from chrys.orchestration.workflows.preview import PreparedWorkflow
|
|
89
|
+
from chrys.orchestration.workflows.runner import WorkflowRunResult
|
|
90
|
+
from chrys.service.approval.policy import ApprovalMode
|
|
91
|
+
from chrys.service.profiles.agents.registry import AgentProfileRegistry
|
|
92
|
+
from chrys.service.profiles.agents.schema import AgentProfile, MCPServerConfig
|
|
93
|
+
from chrys.service.profiles.models.registry import ModelProfileRegistry
|
|
94
|
+
from chrys.service.state.store import JsonFileStateStore, StateStore
|
|
95
|
+
from chrys.service.workflows.discovery import Discovery
|
|
96
|
+
from chrys.service.workflows.outcomes import REASON_DEADLINE_EXCEEDED
|
|
97
|
+
|
|
98
|
+
_AGENT_PROFILE_NOT_FOUND = msg(
|
|
99
|
+
"session_host.agent_profile_not_found",
|
|
100
|
+
fallback="Agent profile not found: {name}",
|
|
101
|
+
)
|
|
102
|
+
_AGENT_PROFILE_NOT_FOUND_WITH_AVAILABLE = msg(
|
|
103
|
+
"session_host.agent_profile_not_found_with_available",
|
|
104
|
+
fallback="Agent profile not found: {name}. Available profiles: {available}",
|
|
105
|
+
)
|
|
106
|
+
_SESSION_NOT_FOUND = msg(
|
|
107
|
+
"session_host.session_not_found",
|
|
108
|
+
fallback="Session not found: {session_id}",
|
|
109
|
+
)
|
|
110
|
+
_SESSION_NOT_FOUND_WITH_RECENT = msg(
|
|
111
|
+
"session_host.session_not_found_with_recent",
|
|
112
|
+
fallback="Session not found: {session_id}. Recent sessions: {recent}",
|
|
113
|
+
)
|
|
114
|
+
_SESSION_ID_AMBIGUOUS = msg(
|
|
115
|
+
"session_host.session_id_ambiguous",
|
|
116
|
+
fallback="Session id '{session_id}' is ambiguous.",
|
|
117
|
+
)
|
|
118
|
+
_NO_FINAL_RESPONSE = msg(
|
|
119
|
+
"session_host.no_final_response",
|
|
120
|
+
fallback="Agent run ended without a final response.",
|
|
121
|
+
)
|
|
122
|
+
_HEADLESS_INTERACTION_REQUIRED = msg(
|
|
123
|
+
"session_host.headless_interaction_required",
|
|
124
|
+
fallback="Agent requested user input in headless mode: {detail}",
|
|
125
|
+
multiline=True,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
_HEADLESS_RUN_EVENT_TYPES = (
|
|
129
|
+
AgentLoadFailed,
|
|
130
|
+
AgentLoadFinished,
|
|
131
|
+
AgentLoadProgress,
|
|
132
|
+
AgentLoadStarted,
|
|
133
|
+
InvocationMessage,
|
|
134
|
+
AgentRuntimeUpdated,
|
|
135
|
+
AgentThinking,
|
|
136
|
+
ApprovalModeUpdated,
|
|
137
|
+
ApprovalRequest,
|
|
138
|
+
ApprovalReviewed,
|
|
139
|
+
CompactionFinished,
|
|
140
|
+
CompactionStarted,
|
|
141
|
+
ContextCompressed,
|
|
142
|
+
InvocationContextPressure,
|
|
143
|
+
Error,
|
|
144
|
+
QuestionToUser,
|
|
145
|
+
InvocationPresentationAttemptAccepted,
|
|
146
|
+
InvocationPresentationAttemptRejected,
|
|
147
|
+
InvocationRetryAttempt,
|
|
148
|
+
SessionRestored,
|
|
149
|
+
SessionSaved,
|
|
150
|
+
StateChanged,
|
|
151
|
+
InvocationAborted,
|
|
152
|
+
InvocationCascadeAborted,
|
|
153
|
+
InvocationCompactionCommitted,
|
|
154
|
+
InvocationCompactionFinished,
|
|
155
|
+
InvocationCompactionStarted,
|
|
156
|
+
InvocationStarted,
|
|
157
|
+
InvocationPaused,
|
|
158
|
+
InvocationProgress,
|
|
159
|
+
InvocationResumed,
|
|
160
|
+
InvocationToolCallArgsUpdated,
|
|
161
|
+
InvocationToolCallProgress,
|
|
162
|
+
InvocationToolCallResult,
|
|
163
|
+
InvocationToolCallStart,
|
|
164
|
+
InvocationToolCallStatusUpdated,
|
|
165
|
+
TodoListUpdated,
|
|
166
|
+
ToolCompacted,
|
|
167
|
+
UsageUpdate,
|
|
168
|
+
UserInjectResult,
|
|
169
|
+
Warning,
|
|
170
|
+
WorkflowRunAccepted,
|
|
171
|
+
WorkflowRunRejected,
|
|
172
|
+
*WORKFLOW_RUN_EVENTS,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def allows_headless_event(event: Event) -> bool:
|
|
177
|
+
"""Keep the existing headless boundary after execution families converge."""
|
|
178
|
+
if not isinstance(event, _HEADLESS_RUN_EVENT_TYPES):
|
|
179
|
+
return False
|
|
180
|
+
if not isinstance(event, InvocationEvent):
|
|
181
|
+
return True
|
|
182
|
+
if event.origin.root.kind == "workflow_node":
|
|
183
|
+
return False
|
|
184
|
+
return event.origin.kind == "turn" or not isinstance(
|
|
185
|
+
event, InvocationMessage | InvocationPresentationAttemptAccepted | InvocationPresentationAttemptRejected
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def allows_workflow_node_event(event: Event) -> bool:
|
|
190
|
+
"""Whether a workflow agent node's own activity may reach a headless workflow stream that asked for it.
|
|
191
|
+
|
|
192
|
+
The complement of what :func:`allows_headless_event` drops for workflow-rooted invocations: tool calls,
|
|
193
|
+
retries, compaction and sub-agent lifecycle, and the node's own prose. A node's sub-agents keep their prose
|
|
194
|
+
private, as a chat turn's do.
|
|
195
|
+
"""
|
|
196
|
+
if not isinstance(event, InvocationEvent) or not isinstance(event, _HEADLESS_RUN_EVENT_TYPES):
|
|
197
|
+
return False
|
|
198
|
+
if event.origin.root.kind != "workflow_node":
|
|
199
|
+
return False
|
|
200
|
+
return event.origin.kind == "workflow_node" or not isinstance(
|
|
201
|
+
event, InvocationMessage | InvocationPresentationAttemptAccepted | InvocationPresentationAttemptRejected
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
@dataclass(frozen=True)
|
|
206
|
+
class HeadlessRunResult:
|
|
207
|
+
"""Final response from one headless agent turn; ``events`` stays empty unless the caller asked to keep them."""
|
|
208
|
+
|
|
209
|
+
text: str
|
|
210
|
+
session_id: str
|
|
211
|
+
events: list[Event] = field(default_factory=list)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class HeadlessRunError(RuntimeError):
|
|
215
|
+
"""Raised when a headless agent turn ends with an engine error."""
|
|
216
|
+
|
|
217
|
+
def __init__(self, event: Error) -> None:
|
|
218
|
+
self.event = event
|
|
219
|
+
super().__init__(event.message or event.code or "Headless agent run failed")
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
@dataclass(frozen=True)
|
|
223
|
+
class EndTurn:
|
|
224
|
+
"""A turn completed normally."""
|
|
225
|
+
|
|
226
|
+
kind: Literal["end_turn"] = "end_turn"
|
|
227
|
+
final_text: str = ""
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
@dataclass(frozen=True)
|
|
231
|
+
class Cancelled:
|
|
232
|
+
"""A turn was cancelled by the caller."""
|
|
233
|
+
|
|
234
|
+
kind: Literal["cancelled"] = "cancelled"
|
|
235
|
+
reason: str = ""
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
@dataclass(frozen=True)
|
|
239
|
+
class Errored:
|
|
240
|
+
"""A turn ended with an engine error."""
|
|
241
|
+
|
|
242
|
+
kind: Literal["error"] = "error"
|
|
243
|
+
error: Error = field(default_factory=Error)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
TurnOutcome = EndTurn | Cancelled | Errored
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class WorkflowRunRejectedError(RuntimeError):
|
|
250
|
+
"""The engine refused the workflow run request; ``event`` carries the reason code."""
|
|
251
|
+
|
|
252
|
+
def __init__(self, event: WorkflowRunRejected) -> None:
|
|
253
|
+
super().__init__(f"{event.error}: {event.message}")
|
|
254
|
+
self.event = event
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class WorkflowRunTimeoutError(TimeoutError):
|
|
258
|
+
"""The workflow deadline expired before a run could be accepted."""
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class AgentProfileNotFoundError(KeyError):
|
|
262
|
+
"""Raised when an agent profile selector can't be resolved against the registry."""
|
|
263
|
+
|
|
264
|
+
def __init__(self, message: str, *, display_message: MessageRef | None = None) -> None:
|
|
265
|
+
self.display_message = display_message
|
|
266
|
+
super().__init__(message)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class SessionNotFoundError(KeyError):
|
|
270
|
+
"""Raised when a session id (full or short) doesn't match any stored session."""
|
|
271
|
+
|
|
272
|
+
def __init__(self, message: str, *, display_message: MessageRef | None = None) -> None:
|
|
273
|
+
self.display_message = display_message
|
|
274
|
+
super().__init__(message)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
class AmbiguousSessionIdError(ValueError):
|
|
278
|
+
"""Raised when a session-id prefix matches more than one stored session."""
|
|
279
|
+
|
|
280
|
+
def __init__(self, message: str, *, display_message: MessageRef | None = None) -> None:
|
|
281
|
+
self.display_message = display_message
|
|
282
|
+
super().__init__(message)
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def _normalize_cwd(cwd: str | None) -> str:
|
|
286
|
+
raw = (cwd or "").strip()
|
|
287
|
+
if not raw:
|
|
288
|
+
return ""
|
|
289
|
+
return os.path.abspath(os.path.expanduser(raw))
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
class ChrysSessionHost:
|
|
293
|
+
"""Owns one event bus, one engine, and one session for non-TUI callers.
|
|
294
|
+
|
|
295
|
+
``start()`` is an explicit, idempotent lifecycle boundary. Callers that
|
|
296
|
+
need startup/restore events should use ``iter_run_events()``, which
|
|
297
|
+
auto-starts before publishing the user turn. ``run_until_final()`` is only
|
|
298
|
+
a convenience wrapper over that streaming primitive.
|
|
299
|
+
"""
|
|
300
|
+
|
|
301
|
+
def __init__(
|
|
302
|
+
self,
|
|
303
|
+
*,
|
|
304
|
+
profile_name: str = "",
|
|
305
|
+
session_id: str | None = None,
|
|
306
|
+
settings: Settings | None = None,
|
|
307
|
+
loaded_settings: LoadedSettings | None = None,
|
|
308
|
+
event_bus: EventBus | None = None,
|
|
309
|
+
agent_registry: AgentProfileRegistry | None = None,
|
|
310
|
+
model_registry: ModelProfileRegistry | None = None,
|
|
311
|
+
state_store: StateStore | None = None,
|
|
312
|
+
approval_mode: ApprovalMode | None = ApprovalMode.BYPASS,
|
|
313
|
+
cwd: str | None = None,
|
|
314
|
+
workspace: Workspace | None = None,
|
|
315
|
+
mcp_overlay: list[MCPServerConfig] | None = None,
|
|
316
|
+
allow_user_interaction: bool = False,
|
|
317
|
+
on_successful_turn: Callable[[], None] | None = None,
|
|
318
|
+
on_turn_started: Callable[[], None] | None = None,
|
|
319
|
+
surface: SessionSurface | None = None,
|
|
320
|
+
) -> None:
|
|
321
|
+
self._profile_name = profile_name.strip()
|
|
322
|
+
self._restore_session_id = (session_id or "").strip() or None
|
|
323
|
+
if not self._profile_name and not self._restore_session_id:
|
|
324
|
+
error_message = "profile_name is required."
|
|
325
|
+
raise ValueError(error_message)
|
|
326
|
+
self._bus = event_bus or EventBus()
|
|
327
|
+
self._agent_registry = agent_registry or AgentProfileRegistry()
|
|
328
|
+
self._model_registry = model_registry or ModelProfileRegistry()
|
|
329
|
+
self._state_store = state_store or JsonFileStateStore()
|
|
330
|
+
self._cwd = _normalize_cwd(cwd)
|
|
331
|
+
self._workflow_catalog: WorkflowCatalog | None = None
|
|
332
|
+
self._workflow_selection: WorkflowSessionSelection | None = None
|
|
333
|
+
self._workspace = workspace
|
|
334
|
+
self._mcp_overlay = list(mcp_overlay or [])
|
|
335
|
+
self._allow_user_interaction = allow_user_interaction
|
|
336
|
+
self._started = False
|
|
337
|
+
self._run_lock = asyncio.Lock()
|
|
338
|
+
self._cancel_requested = False
|
|
339
|
+
self._last_turn_outcome: TurnOutcome | None = None
|
|
340
|
+
self._engine = assemble_agent_engine(
|
|
341
|
+
self._bus,
|
|
342
|
+
settings,
|
|
343
|
+
loaded_settings=loaded_settings,
|
|
344
|
+
agent_registry=self._agent_registry,
|
|
345
|
+
model_registry=self._model_registry,
|
|
346
|
+
state_store=self._state_store,
|
|
347
|
+
initial_approval_mode=approval_mode,
|
|
348
|
+
mcp_overlay=self._mcp_overlay,
|
|
349
|
+
initial_workspace=workspace or (Workspace.from_cwd(self._cwd) if self._cwd else None),
|
|
350
|
+
on_successful_turn=on_successful_turn,
|
|
351
|
+
on_turn_started=on_turn_started,
|
|
352
|
+
allow_user_interaction=allow_user_interaction,
|
|
353
|
+
surface=surface,
|
|
354
|
+
)
|
|
355
|
+
self._approval_mode = self._engine.session.approval_mode
|
|
356
|
+
|
|
357
|
+
@property
|
|
358
|
+
def event_bus(self) -> EventBus:
|
|
359
|
+
"""Event bus used by this host."""
|
|
360
|
+
return self._bus
|
|
361
|
+
|
|
362
|
+
@property
|
|
363
|
+
def engine(self) -> AgentEngine:
|
|
364
|
+
"""Engine owned by this host."""
|
|
365
|
+
return self._engine
|
|
366
|
+
|
|
367
|
+
@property
|
|
368
|
+
def _settings(self) -> Settings:
|
|
369
|
+
"""The engine's current settings, not a copy taken at construction.
|
|
370
|
+
|
|
371
|
+
Held nowhere here on purpose: the engine owns the handle, and a second
|
|
372
|
+
field would answer with whatever was true when this host was built —
|
|
373
|
+
stale from the first reload or session restore onwards.
|
|
374
|
+
"""
|
|
375
|
+
return self._engine.settings
|
|
376
|
+
|
|
377
|
+
@property
|
|
378
|
+
def session_id(self) -> str | None:
|
|
379
|
+
"""Current canonical session id, or ``None`` before start."""
|
|
380
|
+
return self._engine.session_id
|
|
381
|
+
|
|
382
|
+
@property
|
|
383
|
+
def approval_mode(self) -> ApprovalMode:
|
|
384
|
+
"""Initial approval mode used when the engine was built."""
|
|
385
|
+
return self._approval_mode
|
|
386
|
+
|
|
387
|
+
@property
|
|
388
|
+
def vision_enabled(self) -> bool:
|
|
389
|
+
"""Whether the currently built runtime model accepts image input."""
|
|
390
|
+
return self._engine.runtime_details.model.vision
|
|
391
|
+
|
|
392
|
+
@property
|
|
393
|
+
def last_turn_outcome(self) -> TurnOutcome | None:
|
|
394
|
+
"""Structured outcome for the most recently completed streamed turn."""
|
|
395
|
+
return self._last_turn_outcome
|
|
396
|
+
|
|
397
|
+
async def start(self) -> None:
|
|
398
|
+
"""Start a fresh session or restore the configured existing session.
|
|
399
|
+
|
|
400
|
+
Idempotent. Callers that need to observe startup/restore events through
|
|
401
|
+
``iter_run_events()`` should let the first run drive startup so the
|
|
402
|
+
event stream is already subscribed before load events are published.
|
|
403
|
+
"""
|
|
404
|
+
if self._started:
|
|
405
|
+
return
|
|
406
|
+
self._load_registries_if_needed()
|
|
407
|
+
if self._restore_session_id:
|
|
408
|
+
await self.restore_session(self._restore_session_id)
|
|
409
|
+
else:
|
|
410
|
+
if not self._profile_name:
|
|
411
|
+
error_message = "profile_name is required to start a new session."
|
|
412
|
+
raise ValueError(error_message)
|
|
413
|
+
profile = self._resolve_profile(self._profile_name)
|
|
414
|
+
self._profile_name = profile.name
|
|
415
|
+
await self._engine.start(profile)
|
|
416
|
+
self._started = True
|
|
417
|
+
|
|
418
|
+
async def restore_session(self, session_id: str) -> None:
|
|
419
|
+
"""Restore a saved session into this host."""
|
|
420
|
+
target_session_id = await self._resolve_restore_session_id(session_id)
|
|
421
|
+
if not target_session_id:
|
|
422
|
+
error_message = "session_id is required to restore a session."
|
|
423
|
+
raise ValueError(error_message)
|
|
424
|
+
self._load_registries_if_needed()
|
|
425
|
+
if self._profile_name:
|
|
426
|
+
self._profile_name = self._resolve_profile(self._profile_name).name
|
|
427
|
+
await self._engine.prepare()
|
|
428
|
+
# Promote a requested short id to the canonical id used by restore events and future idempotent starts.
|
|
429
|
+
self._restore_session_id = target_session_id
|
|
430
|
+
loop = asyncio.get_running_loop()
|
|
431
|
+
restored: asyncio.Future[SessionRestored] = loop.create_future()
|
|
432
|
+
|
|
433
|
+
async def _on_restored(event: SessionRestored) -> None:
|
|
434
|
+
if event.session_id == target_session_id and not restored.done():
|
|
435
|
+
restored.set_result(event)
|
|
436
|
+
|
|
437
|
+
async def _on_error(event: Error) -> None:
|
|
438
|
+
if event.session_id == target_session_id and not restored.done():
|
|
439
|
+
restored.set_exception(HeadlessRunError(event))
|
|
440
|
+
|
|
441
|
+
await self._bus.subscribe(SessionRestored, _on_restored)
|
|
442
|
+
await self._bus.subscribe(Error, _on_error)
|
|
443
|
+
try:
|
|
444
|
+
await self._bus.publish(
|
|
445
|
+
SessionRestore(
|
|
446
|
+
session_id=target_session_id,
|
|
447
|
+
primary_cwd=self._cwd,
|
|
448
|
+
profile_name=self._profile_name,
|
|
449
|
+
working_dirs=(
|
|
450
|
+
[d.path for d in self._workspace.working_dirs if not d.is_primary]
|
|
451
|
+
if self._workspace is not None
|
|
452
|
+
else None
|
|
453
|
+
),
|
|
454
|
+
),
|
|
455
|
+
raise_handler_errors=True,
|
|
456
|
+
)
|
|
457
|
+
await restored
|
|
458
|
+
finally:
|
|
459
|
+
await self._bus.unsubscribe(SessionRestored, _on_restored)
|
|
460
|
+
await self._bus.unsubscribe(Error, _on_error)
|
|
461
|
+
if restored.done() and not restored.cancelled():
|
|
462
|
+
# A failing restore publishes its Error *and* re-raises out of
|
|
463
|
+
# the publish above, so the future's copy of the failure goes
|
|
464
|
+
# unawaited; retrieve it or asyncio logs it as never-retrieved.
|
|
465
|
+
restored.exception()
|
|
466
|
+
self._started = True
|
|
467
|
+
|
|
468
|
+
async def iter_run_events(self, message: str | UserMessage) -> AsyncIterator[Event]:
|
|
469
|
+
"""Run one user turn and yield backend events until the engine run finishes."""
|
|
470
|
+
async for event in self._iter_turn_events(message, raise_for_outcome=True):
|
|
471
|
+
yield event
|
|
472
|
+
|
|
473
|
+
async def iter_turn_events(self, message: str | UserMessage) -> AsyncIterator[Event]:
|
|
474
|
+
"""Run one user turn and yield events, recording a structured terminal outcome."""
|
|
475
|
+
async for event in self._iter_turn_events(message, raise_for_outcome=False):
|
|
476
|
+
yield event
|
|
477
|
+
|
|
478
|
+
async def _iter_turn_events(self, message: str | UserMessage, *, raise_for_outcome: bool) -> AsyncIterator[Event]:
|
|
479
|
+
if self._run_lock.locked():
|
|
480
|
+
error_message = "Concurrent turns are not supported for a ChrysSessionHost."
|
|
481
|
+
raise RuntimeError(error_message)
|
|
482
|
+
async with self._run_lock:
|
|
483
|
+
self._last_turn_outcome = None
|
|
484
|
+
self._cancel_requested = False
|
|
485
|
+
async for event in self._iter_run_events_locked(message, raise_for_outcome=raise_for_outcome):
|
|
486
|
+
yield event
|
|
487
|
+
|
|
488
|
+
async def cancel_current_turn(self) -> None:
|
|
489
|
+
"""Request cancellation for the active turn, if any."""
|
|
490
|
+
self._cancel_requested = True
|
|
491
|
+
await self._bus.publish(UserInterrupt(session_id=self.session_id))
|
|
492
|
+
|
|
493
|
+
async def _iter_run_events_locked(
|
|
494
|
+
self,
|
|
495
|
+
message: str | UserMessage,
|
|
496
|
+
*,
|
|
497
|
+
raise_for_outcome: bool,
|
|
498
|
+
) -> AsyncIterator[Event]:
|
|
499
|
+
"""Run one user turn while the host run lock is held."""
|
|
500
|
+
final: InvocationMessage | None = None
|
|
501
|
+
error: Error | None = None
|
|
502
|
+
question: QuestionToUser | None = None
|
|
503
|
+
next_event_task: asyncio.Task[Event] | None = None
|
|
504
|
+
start_task: asyncio.Task[None] | None = None
|
|
505
|
+
run_task: asyncio.Task[None] | None = None
|
|
506
|
+
|
|
507
|
+
def _record_event(event: Event) -> None:
|
|
508
|
+
nonlocal final, error, question
|
|
509
|
+
# Only the terminal state is kept: a caller that wants the turn's
|
|
510
|
+
# events reads them from this iterator as they stream.
|
|
511
|
+
if (
|
|
512
|
+
isinstance(event, InvocationMessage)
|
|
513
|
+
and event.origin.kind == "turn"
|
|
514
|
+
and event.is_final
|
|
515
|
+
and not event.is_intermediate
|
|
516
|
+
):
|
|
517
|
+
final = event
|
|
518
|
+
elif isinstance(event, Error):
|
|
519
|
+
error = event
|
|
520
|
+
elif isinstance(event, QuestionToUser):
|
|
521
|
+
question = event
|
|
522
|
+
|
|
523
|
+
async with self._bus.stream(*_HEADLESS_RUN_EVENT_TYPES) as stream:
|
|
524
|
+
ask_user_interrupted = False
|
|
525
|
+
|
|
526
|
+
async def _publish_user_turn() -> None:
|
|
527
|
+
nonlocal run_task
|
|
528
|
+
user_event = self._coerce_user_message(message)
|
|
529
|
+
await self._bus.publish(user_event)
|
|
530
|
+
run_task = asyncio.create_task(self._engine.wait_for_run_task())
|
|
531
|
+
|
|
532
|
+
async def _event_task_ready_after_checkpoint() -> bool:
|
|
533
|
+
if next_event_task is not None and next_event_task.done():
|
|
534
|
+
return True
|
|
535
|
+
# EventBus.publish queues stream events synchronously; this checkpoint lets those tasks win first.
|
|
536
|
+
# Revisit if the bus later grows asynchronous fan-out.
|
|
537
|
+
await asyncio.sleep(0)
|
|
538
|
+
return next_event_task is not None and next_event_task.done()
|
|
539
|
+
|
|
540
|
+
next_event_task = asyncio.create_task(stream.__anext__())
|
|
541
|
+
if self._started:
|
|
542
|
+
await _publish_user_turn()
|
|
543
|
+
else:
|
|
544
|
+
start_task = asyncio.create_task(self.start())
|
|
545
|
+
|
|
546
|
+
try:
|
|
547
|
+
while True:
|
|
548
|
+
wait_tasks: set[asyncio.Task[object]] = {next_event_task}
|
|
549
|
+
if start_task is not None:
|
|
550
|
+
wait_tasks.add(start_task)
|
|
551
|
+
if run_task is not None:
|
|
552
|
+
wait_tasks.add(run_task)
|
|
553
|
+
done, _pending = await asyncio.wait(wait_tasks, return_when=asyncio.FIRST_COMPLETED)
|
|
554
|
+
|
|
555
|
+
if next_event_task in done:
|
|
556
|
+
event = next_event_task.result()
|
|
557
|
+
next_event_task = asyncio.create_task(stream.__anext__())
|
|
558
|
+
if not self._event_belongs_to_session(event, self.session_id, self._restore_session_id):
|
|
559
|
+
continue
|
|
560
|
+
if not allows_headless_event(event):
|
|
561
|
+
continue
|
|
562
|
+
_record_event(event)
|
|
563
|
+
yield event
|
|
564
|
+
if (
|
|
565
|
+
isinstance(event, QuestionToUser)
|
|
566
|
+
and not self._allow_user_interaction
|
|
567
|
+
and not ask_user_interrupted
|
|
568
|
+
):
|
|
569
|
+
ask_user_interrupted = True
|
|
570
|
+
await self._bus.publish(UserInterrupt(session_id=self.session_id))
|
|
571
|
+
continue
|
|
572
|
+
|
|
573
|
+
if start_task is not None and start_task in done:
|
|
574
|
+
if await _event_task_ready_after_checkpoint():
|
|
575
|
+
continue
|
|
576
|
+
await start_task
|
|
577
|
+
start_task = None
|
|
578
|
+
await _publish_user_turn()
|
|
579
|
+
continue
|
|
580
|
+
|
|
581
|
+
if run_task is not None and run_task in done:
|
|
582
|
+
if await _event_task_ready_after_checkpoint():
|
|
583
|
+
continue
|
|
584
|
+
await run_task
|
|
585
|
+
outcome = self._resolve_run_outcome(final=final, error=error, question=question)
|
|
586
|
+
self._last_turn_outcome = outcome
|
|
587
|
+
if raise_for_outcome:
|
|
588
|
+
self._raise_for_run_outcome(outcome)
|
|
589
|
+
return
|
|
590
|
+
finally:
|
|
591
|
+
if start_task is not None:
|
|
592
|
+
if start_task.done():
|
|
593
|
+
self._observe_task_exception(start_task)
|
|
594
|
+
else:
|
|
595
|
+
start_task.cancel()
|
|
596
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
597
|
+
await start_task
|
|
598
|
+
if run_task is not None:
|
|
599
|
+
if run_task.done():
|
|
600
|
+
self._observe_task_exception(run_task)
|
|
601
|
+
else:
|
|
602
|
+
try:
|
|
603
|
+
self._cancel_requested = True
|
|
604
|
+
await self._bus.publish(UserInterrupt(session_id=self.session_id))
|
|
605
|
+
await asyncio.shield(run_task)
|
|
606
|
+
finally:
|
|
607
|
+
self._observe_or_defer_task_exception(run_task)
|
|
608
|
+
if next_event_task is not None and not next_event_task.done():
|
|
609
|
+
next_event_task.cancel()
|
|
610
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
611
|
+
await next_event_task
|
|
612
|
+
|
|
613
|
+
async def run_until_final(
|
|
614
|
+
self,
|
|
615
|
+
message: str | UserMessage,
|
|
616
|
+
*,
|
|
617
|
+
timeout: float | None = None,
|
|
618
|
+
keep_events: bool = False,
|
|
619
|
+
on_event: Callable[[Event], None] | None = None,
|
|
620
|
+
) -> HeadlessRunResult:
|
|
621
|
+
"""Run one user turn and return the final assistant response.
|
|
622
|
+
|
|
623
|
+
The turn's streamed events are retained in ``HeadlessRunResult.events``
|
|
624
|
+
only with *keep_events*: a long turn streams many deltas nobody reads.
|
|
625
|
+
*on_event* sees every event as it streams, before the next one is read.
|
|
626
|
+
"""
|
|
627
|
+
|
|
628
|
+
async def _run() -> HeadlessRunResult:
|
|
629
|
+
events: list[Event] = []
|
|
630
|
+
final: InvocationMessage | None = None
|
|
631
|
+
async for event in self.iter_run_events(message):
|
|
632
|
+
if on_event is not None:
|
|
633
|
+
on_event(event)
|
|
634
|
+
if keep_events:
|
|
635
|
+
events.append(event)
|
|
636
|
+
if (
|
|
637
|
+
isinstance(event, InvocationMessage)
|
|
638
|
+
and event.origin.kind == "turn"
|
|
639
|
+
and event.is_final
|
|
640
|
+
and not event.is_intermediate
|
|
641
|
+
):
|
|
642
|
+
final = event
|
|
643
|
+
if final is None:
|
|
644
|
+
error_message = "Agent run ended without a final response."
|
|
645
|
+
raise RuntimeError(error_message)
|
|
646
|
+
return HeadlessRunResult(text=final.text, session_id=self.session_id or "", events=events)
|
|
647
|
+
|
|
648
|
+
if timeout is None:
|
|
649
|
+
return await _run()
|
|
650
|
+
return await asyncio.wait_for(_run(), timeout=timeout)
|
|
651
|
+
|
|
652
|
+
# -- workflows -----------------------------------------------------------------------
|
|
653
|
+
|
|
654
|
+
def _project_workspace(self) -> Workspace:
|
|
655
|
+
"""The workspace inherited by a new workflow session, copied at admission."""
|
|
656
|
+
return self._engine.workspace or self._workspace or Workspace.from_cwd(self._cwd)
|
|
657
|
+
|
|
658
|
+
@property
|
|
659
|
+
def workflow_session_id(self) -> str:
|
|
660
|
+
return self._workflow_selection.session_id if self._workflow_selection is not None else ""
|
|
661
|
+
|
|
662
|
+
@property
|
|
663
|
+
def workflow_catalog(self) -> WorkflowCatalog:
|
|
664
|
+
config_dir, project_cwd = (
|
|
665
|
+
get_platform().config_dir,
|
|
666
|
+
Path(
|
|
667
|
+
self._workflow_selection.workspace.primary_cwd
|
|
668
|
+
if self._workflow_selection is not None
|
|
669
|
+
else self._project_workspace().primary_cwd
|
|
670
|
+
),
|
|
671
|
+
)
|
|
672
|
+
catalog = self._workflow_catalog
|
|
673
|
+
if catalog is None or (catalog.config_dir, catalog.project_cwd) != (config_dir, project_cwd):
|
|
674
|
+
catalog = self._workflow_catalog = WorkflowCatalog(config_dir=config_dir, project_cwd=project_cwd)
|
|
675
|
+
return catalog
|
|
676
|
+
|
|
677
|
+
@property
|
|
678
|
+
def workflow_session_dir(self) -> Path | None:
|
|
679
|
+
return self._state_store.session_dir(self.workflow_session_id) if self.workflow_session_id else None
|
|
680
|
+
|
|
681
|
+
async def load_workflow_session(self, session_id: str) -> None:
|
|
682
|
+
"""Select workflow history and workspace without constructing a chat agent."""
|
|
683
|
+
normalized = session_id.strip().replace("-", "").lower()
|
|
684
|
+
meta = await self._state_store.load_session_meta(normalized, strict=True)
|
|
685
|
+
if meta is None or normalized != (
|
|
686
|
+
session_short_id(meta.session_id).lower()
|
|
687
|
+
if len(normalized) <= SESSION_SHORT_ID_LEN
|
|
688
|
+
else meta.session_id.replace("-", "").lower()
|
|
689
|
+
):
|
|
690
|
+
raise ValueError(f"No workflow session {session_id!r} exists.")
|
|
691
|
+
if meta.kind != "workflow":
|
|
692
|
+
raise ValueError(f"Session {session_id!r} is a Chat session, not a Workflow session.")
|
|
693
|
+
if not meta.run_count:
|
|
694
|
+
raise ValueError(f"Workflow session {session_id!r} has no runs.")
|
|
695
|
+
state = await self._state_store.load_workflow_session(meta.session_id)
|
|
696
|
+
if state is None:
|
|
697
|
+
raise ValueError(f"No workflow session {session_id!r} exists.")
|
|
698
|
+
self._workflow_selection = state.selection(meta.session_id)
|
|
699
|
+
|
|
700
|
+
def list_workflows(self) -> Discovery:
|
|
701
|
+
"""Active and shadowed workflows, plus unreadable files."""
|
|
702
|
+
return self.workflow_catalog.discover()
|
|
703
|
+
|
|
704
|
+
def workflow_target(
|
|
705
|
+
self, workflow_id: str, *, new_session: bool = False, workspace: Workspace | None = None
|
|
706
|
+
) -> WorkflowTarget:
|
|
707
|
+
"""Capture a draft or the selected session; subsequent browsing cannot retarget it."""
|
|
708
|
+
if not new_session and self._workflow_selection is not None:
|
|
709
|
+
if workspace is not None or workflow_id != self._workflow_selection.workflow_id:
|
|
710
|
+
raise WorkflowRunRejectedError(
|
|
711
|
+
WorkflowRunRejected(
|
|
712
|
+
error="spec_changed",
|
|
713
|
+
message="Use a new workflow draft to select another definition or workspace.",
|
|
714
|
+
)
|
|
715
|
+
)
|
|
716
|
+
return self._workflow_selection
|
|
717
|
+
return WorkflowDraft(workflow_id, WorkspaceSnapshot.capture(workspace or self._project_workspace()))
|
|
718
|
+
|
|
719
|
+
async def preview_workflow(
|
|
720
|
+
self, target: WorkflowTarget, *, timeout: float | None = None, trust: bool = False
|
|
721
|
+
) -> PreparedWorkflow:
|
|
722
|
+
"""Prepare the exact target; ``trust`` explicitly authorizes execution of unconfirmed source."""
|
|
723
|
+
catalog = WorkflowCatalog(config_dir=get_platform().config_dir, project_cwd=Path(target.workspace.primary_cwd))
|
|
724
|
+
preview = await catalog.preview(
|
|
725
|
+
target.workflow_id,
|
|
726
|
+
timeout=timeout,
|
|
727
|
+
trust=trust,
|
|
728
|
+
expected_identity=target.identity if isinstance(target, WorkflowSessionSelection) else None,
|
|
729
|
+
)
|
|
730
|
+
return PreparedWorkflow(target, preview)
|
|
731
|
+
|
|
732
|
+
def confirm_workflow(self, prepared: PreparedWorkflow) -> None:
|
|
733
|
+
"""Confirm a prepared definition independently of the current browsing selection."""
|
|
734
|
+
catalog = WorkflowCatalog(
|
|
735
|
+
config_dir=get_platform().config_dir, project_cwd=Path(prepared.target.workspace.primary_cwd)
|
|
736
|
+
)
|
|
737
|
+
catalog.confirm(prepared.preview)
|
|
738
|
+
|
|
739
|
+
async def iter_workflow_events(
|
|
740
|
+
self,
|
|
741
|
+
target: WorkflowTarget | PreparedWorkflow,
|
|
742
|
+
*,
|
|
743
|
+
input_text: str = "",
|
|
744
|
+
timeout: float = 0.0,
|
|
745
|
+
include_node_activity: bool = False,
|
|
746
|
+
) -> AsyncIterator[Event]:
|
|
747
|
+
"""Run one workflow and yield backend events until its ``WorkflowRunFinished``.
|
|
748
|
+
|
|
749
|
+
Raises :class:`WorkflowRunRejectedError` when the engine refuses the
|
|
750
|
+
request. Replies to other requests (a retransmitted request id is
|
|
751
|
+
answered with its original reply) are not this run's and are not
|
|
752
|
+
yielded. Closing the iterator early (``contextlib.aclosing``) cancels
|
|
753
|
+
the run and waits for it; so does a cancellation that lands while the
|
|
754
|
+
request is still being published.
|
|
755
|
+
|
|
756
|
+
``timeout`` is in seconds and covers admission and execution. Expiry before acceptance
|
|
757
|
+
raises :class:`WorkflowRunTimeoutError`; an accepted run finishes as
|
|
758
|
+
cancelled with reason ``deadline_exceeded``. Both paths await cleanup.
|
|
759
|
+
|
|
760
|
+
With ``include_node_activity`` the stream also carries this run's agent-node activity
|
|
761
|
+
(:func:`allows_workflow_node_event`), admitted only for invocations one of this run's
|
|
762
|
+
``WorkflowNodeStateChanged`` events announced: the runner binds an activation's invocation id before
|
|
763
|
+
publishing ``running``, so a same-session invocation from any other run is never yielded.
|
|
764
|
+
"""
|
|
765
|
+
if self._run_lock.locked():
|
|
766
|
+
error_message = "Concurrent runs are not supported for a ChrysSessionHost."
|
|
767
|
+
raise RuntimeError(error_message)
|
|
768
|
+
async with self._run_lock, self._bus.stream(*_HEADLESS_RUN_EVENT_TYPES) as stream:
|
|
769
|
+
self._load_registries_if_needed()
|
|
770
|
+
await self._engine.prepare()
|
|
771
|
+
request_id = new_analytics_id()
|
|
772
|
+
request = (
|
|
773
|
+
target.run_request(input_text=input_text, request_id=request_id, timeout=timeout)
|
|
774
|
+
if isinstance(target, PreparedWorkflow)
|
|
775
|
+
else WorkflowRunRequest(target=target, input_text=input_text, request_id=request_id, timeout=timeout)
|
|
776
|
+
)
|
|
777
|
+
run_id: str | None = None
|
|
778
|
+
execution_session_id: str | None = None
|
|
779
|
+
owned_invocations: set[str] = set()
|
|
780
|
+
rejected = False
|
|
781
|
+
finished = False
|
|
782
|
+
timed_out = False
|
|
783
|
+
|
|
784
|
+
async def enforce_deadline() -> None:
|
|
785
|
+
nonlocal timed_out
|
|
786
|
+
await asyncio.sleep(timeout)
|
|
787
|
+
workflows = self._engine.workflows
|
|
788
|
+
if workflows.active_request_id == request_id:
|
|
789
|
+
timed_out = workflows.cancel_active(reason=REASON_DEADLINE_EXCEEDED)
|
|
790
|
+
|
|
791
|
+
# Own a timer independently of iterator consumption: never put a timeout context across yield.
|
|
792
|
+
deadline_task = (
|
|
793
|
+
asyncio.create_task(enforce_deadline(), name=f"chrys.workflow.deadline.{request_id}")
|
|
794
|
+
if timeout > 0
|
|
795
|
+
else None
|
|
796
|
+
)
|
|
797
|
+
try:
|
|
798
|
+
# Published inside the try: a subscriber behind the coordinator can hold the publish open after
|
|
799
|
+
# the run was admitted, and a cancellation landing there must still give the run up below.
|
|
800
|
+
await self._bus.publish(request)
|
|
801
|
+
async for event in stream:
|
|
802
|
+
node_activity = include_node_activity and allows_workflow_node_event(event)
|
|
803
|
+
if not node_activity and not allows_headless_event(event):
|
|
804
|
+
continue
|
|
805
|
+
if isinstance(event, WorkflowRunAccepted | WorkflowRunRejected) and event.request_id != request_id:
|
|
806
|
+
continue # the reply to someone else's request, a replayed acceptance included
|
|
807
|
+
if isinstance(event, WorkflowRunRejected):
|
|
808
|
+
rejected = True
|
|
809
|
+
if timed_out:
|
|
810
|
+
raise WorkflowRunTimeoutError("Workflow run timed out during admission.")
|
|
811
|
+
raise WorkflowRunRejectedError(event)
|
|
812
|
+
if isinstance(event, WorkflowRunAccepted):
|
|
813
|
+
run_id = event.run_id
|
|
814
|
+
execution_session_id = event.session_id
|
|
815
|
+
self._workflow_selection = event.selection
|
|
816
|
+
elif isinstance(event, Warning) and event.request_id:
|
|
817
|
+
if event.request_id != request_id:
|
|
818
|
+
continue
|
|
819
|
+
elif run_id is None or event.session_id != execution_session_id:
|
|
820
|
+
continue
|
|
821
|
+
if (
|
|
822
|
+
isinstance(
|
|
823
|
+
event,
|
|
824
|
+
WORKFLOW_RUN_EVENTS,
|
|
825
|
+
)
|
|
826
|
+
and event.run_id != run_id
|
|
827
|
+
):
|
|
828
|
+
continue
|
|
829
|
+
if isinstance(event, WorkflowNodeStateChanged) and event.invocation_id:
|
|
830
|
+
owned_invocations.add(event.invocation_id)
|
|
831
|
+
if (
|
|
832
|
+
node_activity
|
|
833
|
+
and isinstance(event, InvocationEvent)
|
|
834
|
+
and event.origin.root.invocation_id not in owned_invocations
|
|
835
|
+
):
|
|
836
|
+
continue
|
|
837
|
+
yield event
|
|
838
|
+
if isinstance(event, WorkflowRunFinished):
|
|
839
|
+
finished = True
|
|
840
|
+
return
|
|
841
|
+
finally:
|
|
842
|
+
if deadline_task is not None:
|
|
843
|
+
deadline_task.cancel()
|
|
844
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
845
|
+
await deadline_task
|
|
846
|
+
# Only this request's run is ours to give up; a refused request leaves the active run alone.
|
|
847
|
+
workflows = self._engine.workflows
|
|
848
|
+
if workflows.active_request_id == request_id:
|
|
849
|
+
if not finished and not rejected:
|
|
850
|
+
await asyncio.shield(workflows.abandon_active())
|
|
851
|
+
await asyncio.shield(workflows.wait_idle())
|
|
852
|
+
|
|
853
|
+
async def run_workflow_until_final(
|
|
854
|
+
self,
|
|
855
|
+
target: WorkflowTarget | PreparedWorkflow,
|
|
856
|
+
*,
|
|
857
|
+
input_text: str = "",
|
|
858
|
+
timeout: float = 0.0,
|
|
859
|
+
) -> WorkflowRunResult:
|
|
860
|
+
"""Run one workflow to its terminal outcome and return the full result (outputs included)."""
|
|
861
|
+
run_id = ""
|
|
862
|
+
async for event in self.iter_workflow_events(
|
|
863
|
+
target,
|
|
864
|
+
input_text=input_text,
|
|
865
|
+
timeout=timeout,
|
|
866
|
+
):
|
|
867
|
+
if isinstance(event, WorkflowRunAccepted):
|
|
868
|
+
run_id = event.run_id
|
|
869
|
+
result = self._engine.workflows.result(run_id)
|
|
870
|
+
if result is None:
|
|
871
|
+
error_message = "Workflow run ended without a recorded result."
|
|
872
|
+
raise RuntimeError(error_message)
|
|
873
|
+
return result
|
|
874
|
+
|
|
875
|
+
async def cancel_workflow(self) -> None:
|
|
876
|
+
"""Request cancellation of the active workflow run, if any."""
|
|
877
|
+
run_id = self._engine.workflows.active_run_id
|
|
878
|
+
if run_id is not None:
|
|
879
|
+
await self._bus.publish(WorkflowCancelRequest(run_id=run_id, session_id=self.workflow_session_id or None))
|
|
880
|
+
|
|
881
|
+
async def shutdown(self) -> None:
|
|
882
|
+
"""Shutdown the owned engine and release session resources."""
|
|
883
|
+
await self._engine.shutdown()
|
|
884
|
+
self._started = False
|
|
885
|
+
|
|
886
|
+
def _load_registries_if_needed(self) -> None:
|
|
887
|
+
if not self._agent_registry.list_names():
|
|
888
|
+
self._agent_registry.load_all()
|
|
889
|
+
if not self._model_registry.list_ids():
|
|
890
|
+
self._model_registry.load_all()
|
|
891
|
+
|
|
892
|
+
def _resolve_profile(self, name: str) -> AgentProfile:
|
|
893
|
+
profile = self._agent_registry.resolve_selector(name)
|
|
894
|
+
if profile is not None:
|
|
895
|
+
return profile
|
|
896
|
+
available_names = sorted(self._agent_registry.list_names())
|
|
897
|
+
available = ", ".join(available_names)
|
|
898
|
+
error_message = f"Agent profile not found: {name}"
|
|
899
|
+
if available_names:
|
|
900
|
+
error_message = f"{error_message}. Available profiles: {available}"
|
|
901
|
+
display_message = _AGENT_PROFILE_NOT_FOUND_WITH_AVAILABLE.bind(
|
|
902
|
+
name=name,
|
|
903
|
+
available=DisplaySequence(available_names),
|
|
904
|
+
)
|
|
905
|
+
else:
|
|
906
|
+
display_message = _AGENT_PROFILE_NOT_FOUND.bind(name=name)
|
|
907
|
+
raise AgentProfileNotFoundError(error_message, display_message=display_message)
|
|
908
|
+
|
|
909
|
+
async def _resolve_restore_session_id(self, session_id: str) -> str:
|
|
910
|
+
raw = (session_id or "").strip()
|
|
911
|
+
if not raw:
|
|
912
|
+
return ""
|
|
913
|
+
sessions = await self._state_store.list_sessions()
|
|
914
|
+
normalized = raw.replace("-", "").lower()
|
|
915
|
+
if len(normalized) <= SESSION_SHORT_ID_LEN:
|
|
916
|
+
matches = [meta.session_id for meta in sessions if session_short_id(meta.session_id).lower() == normalized]
|
|
917
|
+
else:
|
|
918
|
+
matches = [
|
|
919
|
+
meta.session_id
|
|
920
|
+
for meta in sessions
|
|
921
|
+
if meta.session_id == raw or meta.session_id.replace("-", "").lower() == normalized
|
|
922
|
+
]
|
|
923
|
+
if len(matches) == 1:
|
|
924
|
+
return matches[0]
|
|
925
|
+
if len(matches) > 1:
|
|
926
|
+
error_message = f"Session id '{raw}' is ambiguous."
|
|
927
|
+
raise AmbiguousSessionIdError(
|
|
928
|
+
error_message,
|
|
929
|
+
display_message=_SESSION_ID_AMBIGUOUS.bind(session_id=raw),
|
|
930
|
+
)
|
|
931
|
+
recent_ids = [session_short_id(meta.session_id) for meta in sessions[:5]]
|
|
932
|
+
recent = ", ".join(recent_ids)
|
|
933
|
+
error_message = f"Session not found: {raw}"
|
|
934
|
+
if recent_ids:
|
|
935
|
+
error_message = f"{error_message}. Recent sessions: {recent}"
|
|
936
|
+
display_message = _SESSION_NOT_FOUND_WITH_RECENT.bind(
|
|
937
|
+
session_id=raw,
|
|
938
|
+
recent=DisplaySequence(recent_ids),
|
|
939
|
+
)
|
|
940
|
+
else:
|
|
941
|
+
display_message = _SESSION_NOT_FOUND.bind(session_id=raw)
|
|
942
|
+
raise SessionNotFoundError(error_message, display_message=display_message)
|
|
943
|
+
|
|
944
|
+
@staticmethod
|
|
945
|
+
def _event_belongs_to_session(
|
|
946
|
+
event: Event,
|
|
947
|
+
session_id: str | None,
|
|
948
|
+
restore_session_id: str | None = None,
|
|
949
|
+
) -> bool:
|
|
950
|
+
if not event.session_id:
|
|
951
|
+
# Headless streams ignore global/sessionless events so unrelated errors cannot terminate this run.
|
|
952
|
+
return False
|
|
953
|
+
return event.session_id == session_id or (
|
|
954
|
+
restore_session_id is not None and event.session_id == restore_session_id
|
|
955
|
+
)
|
|
956
|
+
|
|
957
|
+
@staticmethod
|
|
958
|
+
def _observe_task_exception(task: asyncio.Task[object]) -> None:
|
|
959
|
+
"""Mark a completed helper task's exception as retrieved."""
|
|
960
|
+
if task.done() and not task.cancelled():
|
|
961
|
+
task.exception()
|
|
962
|
+
|
|
963
|
+
@classmethod
|
|
964
|
+
def _observe_or_defer_task_exception(cls, task: asyncio.Task[object]) -> None:
|
|
965
|
+
"""Observe a helper task now, or once it finishes after caller cancellation."""
|
|
966
|
+
if task.done():
|
|
967
|
+
cls._observe_task_exception(task)
|
|
968
|
+
return
|
|
969
|
+
task.add_done_callback(cls._observe_task_exception)
|
|
970
|
+
|
|
971
|
+
def _raise_for_run_outcome(self, outcome: TurnOutcome) -> None:
|
|
972
|
+
if isinstance(outcome, Errored):
|
|
973
|
+
raise HeadlessRunError(outcome.error)
|
|
974
|
+
if isinstance(outcome, Cancelled):
|
|
975
|
+
error_message = outcome.reason or "Agent run was cancelled."
|
|
976
|
+
raise RuntimeError(error_message)
|
|
977
|
+
|
|
978
|
+
def _resolve_run_outcome(
|
|
979
|
+
self,
|
|
980
|
+
*,
|
|
981
|
+
final: InvocationMessage | None,
|
|
982
|
+
error: Error | None,
|
|
983
|
+
question: QuestionToUser | None,
|
|
984
|
+
) -> TurnOutcome:
|
|
985
|
+
if question is not None and not self._allow_user_interaction:
|
|
986
|
+
# Headless runs have no one to answer, so a question terminates the turn.
|
|
987
|
+
# Interactive hosts (e.g. ACP) answer ask_user inline, so the run continues
|
|
988
|
+
# to a real final/error outcome and the question must not fail the turn.
|
|
989
|
+
return Errored(error=self._question_to_error(question))
|
|
990
|
+
if self._cancel_requested:
|
|
991
|
+
return Cancelled(reason="Agent run was cancelled.")
|
|
992
|
+
if error is not None:
|
|
993
|
+
return Errored(error=error)
|
|
994
|
+
if final is not None:
|
|
995
|
+
return EndTurn(final_text=final.text)
|
|
996
|
+
return Errored(
|
|
997
|
+
error=Error(
|
|
998
|
+
code="no_final_response",
|
|
999
|
+
message="Agent run ended without a final response.",
|
|
1000
|
+
display_message=_NO_FINAL_RESPONSE.bind(),
|
|
1001
|
+
session_id=self.session_id,
|
|
1002
|
+
)
|
|
1003
|
+
)
|
|
1004
|
+
|
|
1005
|
+
def _coerce_user_message(self, message: str | UserMessage) -> UserMessage:
|
|
1006
|
+
if isinstance(message, UserMessage):
|
|
1007
|
+
return replace(message, session_id=self.session_id)
|
|
1008
|
+
return UserMessage(text=message, session_id=self.session_id)
|
|
1009
|
+
|
|
1010
|
+
@staticmethod
|
|
1011
|
+
def _question_to_error(event: QuestionToUser) -> Error:
|
|
1012
|
+
details = [question.question.strip() for question in event.questions if question.question.strip()]
|
|
1013
|
+
detail = (
|
|
1014
|
+
" ".join(f"{index}. {question}" for index, question in enumerate(details, start=1))
|
|
1015
|
+
if details
|
|
1016
|
+
else "The agent requested user input."
|
|
1017
|
+
)
|
|
1018
|
+
return Error(
|
|
1019
|
+
code="headless_interaction_required",
|
|
1020
|
+
message=f"Agent requested user input in headless mode: {detail}",
|
|
1021
|
+
display_message=_HEADLESS_INTERACTION_REQUIRED.bind(detail=DisplayBlock(detail)),
|
|
1022
|
+
session_id=event.session_id,
|
|
1023
|
+
)
|