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,1095 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""Main Turn middleware, presentation callbacks, and backend construction."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
import logging
|
|
9
|
+
import traceback
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from typing import TYPE_CHECKING, Any, Literal, TypedDict
|
|
13
|
+
|
|
14
|
+
from chrys.foundation.errors import clean_error_message
|
|
15
|
+
from chrys.foundation.errors.display import display_fields
|
|
16
|
+
from chrys.foundation.events.types import (
|
|
17
|
+
AgentThinking,
|
|
18
|
+
Error,
|
|
19
|
+
InvocationMessage,
|
|
20
|
+
InvocationPresentationAttemptAccepted,
|
|
21
|
+
InvocationPresentationAttemptRejected,
|
|
22
|
+
InvocationRetryAttempt,
|
|
23
|
+
InvocationToolCallArgsUpdated,
|
|
24
|
+
InvocationToolCallProgress,
|
|
25
|
+
InvocationToolCallResult,
|
|
26
|
+
InvocationToolCallStart,
|
|
27
|
+
InvocationToolCallStatusUpdated,
|
|
28
|
+
ProvisionalPresentation,
|
|
29
|
+
)
|
|
30
|
+
from chrys.foundation.hosted_tools import HOSTED_TOOL_DEFAULT_KIND_BY_FAMILY, HostedToolStatus
|
|
31
|
+
from chrys.foundation.retry import (
|
|
32
|
+
TRANSIENT_RETRY_BACKOFF_SECONDS,
|
|
33
|
+
StreamStall,
|
|
34
|
+
)
|
|
35
|
+
from chrys.foundation.trajectory.envelope import Link, LinkRelation
|
|
36
|
+
from chrys.foundation.util.time import parse_created_at
|
|
37
|
+
from chrys.kernel import (
|
|
38
|
+
Agent,
|
|
39
|
+
AgentSession,
|
|
40
|
+
ConsumedInjectionMessageProbe,
|
|
41
|
+
LoopRecorderSnapshot,
|
|
42
|
+
StallExhaustedAction,
|
|
43
|
+
WireRetryPolicy,
|
|
44
|
+
resolve_storage_mode_and_handles,
|
|
45
|
+
)
|
|
46
|
+
from chrys.orchestration.engine.run.resume import TurnPassState, TurnResumePolicy
|
|
47
|
+
from chrys.orchestration.invoker.attempts import (
|
|
48
|
+
AgentRunKwargs,
|
|
49
|
+
AttemptRecipe,
|
|
50
|
+
AttemptRunner,
|
|
51
|
+
AttemptTaskHandle,
|
|
52
|
+
BlockingCallTiming,
|
|
53
|
+
HistoryRollback,
|
|
54
|
+
ModelRunTrace,
|
|
55
|
+
RestoreAndFallback,
|
|
56
|
+
RetryBoundaryPolicy,
|
|
57
|
+
WireRecipe,
|
|
58
|
+
continuation_token_observer_for,
|
|
59
|
+
is_string_keyed_dict,
|
|
60
|
+
)
|
|
61
|
+
from chrys.orchestration.invoker.contracts import (
|
|
62
|
+
AbortCause,
|
|
63
|
+
FailureDisposition,
|
|
64
|
+
InvocationOutcome,
|
|
65
|
+
RunIntent,
|
|
66
|
+
RunRequest,
|
|
67
|
+
)
|
|
68
|
+
from chrys.orchestration.invoker.kernel import KernelConversation
|
|
69
|
+
from chrys.orchestration.invoker.origin import BoundEmitter, InvocationPublishers
|
|
70
|
+
from chrys.orchestration.invoker.resources import Conversation, PassResources
|
|
71
|
+
from chrys.service.agent_middleware import (
|
|
72
|
+
ApprovalMiddleware,
|
|
73
|
+
AskUserMiddleware,
|
|
74
|
+
IntermediateTextBuffer,
|
|
75
|
+
InterruptMiddleware,
|
|
76
|
+
SleepMiddleware,
|
|
77
|
+
ToolEventMiddleware,
|
|
78
|
+
)
|
|
79
|
+
from chrys.service.agent_middleware.control.approval import ApprovalRetrySnapshot
|
|
80
|
+
from chrys.service.agent_middleware.events.hosted_tools import (
|
|
81
|
+
FinalTextOp,
|
|
82
|
+
HostedPresentationBridge,
|
|
83
|
+
HostedToolArgsOp,
|
|
84
|
+
HostedToolProgressOp,
|
|
85
|
+
HostedToolResultOp,
|
|
86
|
+
HostedToolStartOp,
|
|
87
|
+
HostedToolStatusOp,
|
|
88
|
+
IntermediateTextOp,
|
|
89
|
+
PresentationAttemptAcceptedOp,
|
|
90
|
+
PresentationAttemptRejectedOp,
|
|
91
|
+
PresentationSinkOperation,
|
|
92
|
+
next_hosted_run_generation,
|
|
93
|
+
)
|
|
94
|
+
from chrys.service.agent_middleware.events.tool_events import ToolEventRetrySnapshot
|
|
95
|
+
from chrys.service.agent_middleware.response_validation import (
|
|
96
|
+
ResponseValidationMiddleware,
|
|
97
|
+
)
|
|
98
|
+
from chrys.service.context.providers.history import PRE_OUTPUT_HISTORY_LEN_STATE_KEY
|
|
99
|
+
from chrys.service.session.message_metadata import (
|
|
100
|
+
LAST_ASSISTANT_CREATED_AT_STATE_KEY,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
if TYPE_CHECKING:
|
|
104
|
+
from collections.abc import Awaitable, Callable, Sequence
|
|
105
|
+
|
|
106
|
+
from chrys.foundation.events.bus import EventBus
|
|
107
|
+
from chrys.foundation.trajectory.context import TrajectoryContext
|
|
108
|
+
from chrys.kernel import AgentResponse, AgentResponseUpdate, LoopRecorder
|
|
109
|
+
from chrys.kernel.middleware import FunctionMiddleware
|
|
110
|
+
from chrys.service.agent_middleware.injection import InjectionMiddleware, QueuedInjection
|
|
111
|
+
from chrys.service.context.compaction import UnifiedContextStrategy
|
|
112
|
+
from chrys.service.context.compaction.strategy import CompactionRetrySnapshot
|
|
113
|
+
from chrys.service.hooks.manager import HookManager
|
|
114
|
+
from chrys.service.mutations.coordination import MutationCoordinator
|
|
115
|
+
from chrys.service.mutations.tracker import MutationTracker
|
|
116
|
+
from chrys.service.trajectory.preparation import PreparationTrace
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
logger = logging.getLogger(__name__)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class _AssistantMessageEventKwargs(TypedDict, total=False):
|
|
123
|
+
"""Optional timestamp forwarded to ``InvocationMessage``."""
|
|
124
|
+
|
|
125
|
+
timestamp: datetime
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@dataclass(frozen=True, slots=True)
|
|
129
|
+
class _TurnRetryState:
|
|
130
|
+
"""Auxiliary per-run buffers rolled back with framework history."""
|
|
131
|
+
|
|
132
|
+
loop_recorder: LoopRecorderSnapshot | None
|
|
133
|
+
tool_events: ToolEventRetrySnapshot
|
|
134
|
+
approval: ApprovalRetrySnapshot
|
|
135
|
+
compaction: CompactionRetrySnapshot | None
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class TurnBindings:
|
|
139
|
+
"""Compose main-pass callbacks and middleware; owns no execution facade.
|
|
140
|
+
|
|
141
|
+
KernelConversation runs the pass; TurnRunner owns the user turn.
|
|
142
|
+
Resource references here are borrowed from the installed PreparedAgent owner.
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
# Resilience: retry on transient network errors (proxy drops,
|
|
146
|
+
# connection resets, timeouts). Applies to both blocking and
|
|
147
|
+
# streaming paths. The classifier lives in :mod:`chrys.foundation.errors`
|
|
148
|
+
# and the loop lives in :mod:`chrys.foundation.retry` so sub-agents share
|
|
149
|
+
# the same policy without reaching back into this class.
|
|
150
|
+
_MAX_RETRIES = 5
|
|
151
|
+
_BACKOFF_SCHEDULE = TRANSIENT_RETRY_BACKOFF_SECONDS
|
|
152
|
+
# Fallback when no per-run stream_attempt_timeout is supplied (e.g. tests).
|
|
153
|
+
# Production callers pass ModelProfile.http_read_timeout so stall detection
|
|
154
|
+
# matches the HTTP client's read timeout.
|
|
155
|
+
_DEFAULT_STREAM_ATTEMPT_TIMEOUT = 300.0 # seconds
|
|
156
|
+
# Class-level fallbacks so partially-constructed bindings (tests build
|
|
157
|
+
# them via ``object.__new__``) read "no probe wired" instead of raising.
|
|
158
|
+
_hosted_commits_probe: Callable[[], tuple[str, ...]] | None = None
|
|
159
|
+
_hosted_commits_in_flight_probe: Callable[[], tuple[str, ...]] | None = None
|
|
160
|
+
_max_retries_override: int | None = None
|
|
161
|
+
_hosted_bridge: HostedPresentationBridge | None = None
|
|
162
|
+
|
|
163
|
+
def __init__(
|
|
164
|
+
self,
|
|
165
|
+
*,
|
|
166
|
+
conversation: Conversation,
|
|
167
|
+
agent: Agent,
|
|
168
|
+
session: AgentSession,
|
|
169
|
+
event_bus: EventBus,
|
|
170
|
+
approval_middleware: ApprovalMiddleware,
|
|
171
|
+
ask_user_middleware: AskUserMiddleware,
|
|
172
|
+
injection_middleware: InjectionMiddleware,
|
|
173
|
+
loop_recorder: LoopRecorder | None = None,
|
|
174
|
+
session_id: str | None = None,
|
|
175
|
+
compaction_strategy: UnifiedContextStrategy | None = None,
|
|
176
|
+
stream: bool = False,
|
|
177
|
+
intermediate_buffer: IntermediateTextBuffer | None = None,
|
|
178
|
+
chat_options: dict[str, Any] | None = None,
|
|
179
|
+
mutation_tracker: MutationTracker | None = None,
|
|
180
|
+
mutation_coordinator: MutationCoordinator | None = None,
|
|
181
|
+
stream_attempt_timeout: float | None = None,
|
|
182
|
+
max_transient_retries: int | None = None,
|
|
183
|
+
hook_manager: HookManager | None = None,
|
|
184
|
+
profile_name: str = "",
|
|
185
|
+
workspace_cwd: str = "",
|
|
186
|
+
serialize_implicit_windows: bool = False,
|
|
187
|
+
extra_function_middleware: Sequence[FunctionMiddleware] = (),
|
|
188
|
+
run_cycle_start_hooks: Sequence[Callable[[], None]] = (),
|
|
189
|
+
hosted_commits_probe: Callable[[], tuple[str, ...]] | None = None,
|
|
190
|
+
hosted_commits_in_flight_probe: Callable[[], tuple[str, ...]] | None = None,
|
|
191
|
+
response_validation_middleware: ResponseValidationMiddleware | None = None,
|
|
192
|
+
publish_intermediate_text: Callable[[str], Awaitable[None]] | None = None,
|
|
193
|
+
invocation_publishers: InvocationPublishers | None = None,
|
|
194
|
+
commit_intermediate_text: Callable[[str, int], None] | None = None,
|
|
195
|
+
tool_result_ceiling_tokens: int | None = None,
|
|
196
|
+
) -> None:
|
|
197
|
+
self.resource_scope = conversation
|
|
198
|
+
self._agent = agent
|
|
199
|
+
self._session = session
|
|
200
|
+
self._bus = event_bus
|
|
201
|
+
self.approval = approval_middleware
|
|
202
|
+
self._ask_user = ask_user_middleware
|
|
203
|
+
self._sleep = SleepMiddleware(event_bus, session_id=session_id)
|
|
204
|
+
self._injection = injection_middleware
|
|
205
|
+
self._loop_recorder = loop_recorder
|
|
206
|
+
self._session_id = session_id
|
|
207
|
+
self._compaction_strategy = compaction_strategy
|
|
208
|
+
self._stream = stream
|
|
209
|
+
self._chat_options = chat_options
|
|
210
|
+
self._stream_attempt_timeout = (
|
|
211
|
+
stream_attempt_timeout if stream_attempt_timeout is not None else self._DEFAULT_STREAM_ATTEMPT_TIMEOUT
|
|
212
|
+
)
|
|
213
|
+
self._max_retries_override = max_transient_retries
|
|
214
|
+
|
|
215
|
+
self._intermediate_buffer = intermediate_buffer
|
|
216
|
+
self._response_validation = response_validation_middleware
|
|
217
|
+
self._publish_intermediate_text = publish_intermediate_text
|
|
218
|
+
self._commit_intermediate_text = commit_intermediate_text
|
|
219
|
+
self._provisional_intermediate_batches: dict[tuple[str, str], int] = {}
|
|
220
|
+
self._hosted_run_generation = 0
|
|
221
|
+
self._hosted_bridge = None
|
|
222
|
+
self._interrupt = InterruptMiddleware()
|
|
223
|
+
self._extra_function_middleware = tuple(extra_function_middleware)
|
|
224
|
+
# Fired once at the start of every user-initiated run cycle, before
|
|
225
|
+
# the first attempt. Components that carry state across the cycle's
|
|
226
|
+
# whole-run retry attempts (e.g. the validation middleware's retry
|
|
227
|
+
# budget) register here so an aborted cycle — interrupt during outer
|
|
228
|
+
# backoff, unrelated exception — cannot leak state into the next
|
|
229
|
+
# independent run.
|
|
230
|
+
self._attempt_handle = AttemptTaskHandle()
|
|
231
|
+
self.state = TurnPassState()
|
|
232
|
+
# Validation-middleware probes for provider-hosted tool executions the
|
|
233
|
+
# loop recorder cannot see (the rejected/aborted exchange never reaches
|
|
234
|
+
# it): run-scoped for the whole-run retry gate, wire-attempt-scoped for
|
|
235
|
+
# the kernel's per-wire replay veto.
|
|
236
|
+
self._hosted_commits_probe = hosted_commits_probe
|
|
237
|
+
self._hosted_commits_in_flight_probe = hosted_commits_in_flight_probe
|
|
238
|
+
# Last continuation token announced by the kernel and not yet
|
|
239
|
+
# resolved to a terminal response. Run kwargs are rebuilt per run,
|
|
240
|
+
# so without this carry-over a user retry after a gate-blocked poll
|
|
241
|
+
# failure would CREATE a second background response while the
|
|
242
|
+
# announced one keeps running remotely (duplicate hosted work).
|
|
243
|
+
# Crash-recovery channel to the orchestration turn state, wired by the
|
|
244
|
+
# engine after build (``TurnRuntimeState.set_current_input``). The
|
|
245
|
+
# bare-resume replay branch pops the anchor user message out of state
|
|
246
|
+
# and must register it as recovery current input at pop time — the
|
|
247
|
+
# component that destructively reads is the component that makes it
|
|
248
|
+
# durable.
|
|
249
|
+
# Trajectory recording for the next pass, set by the turn runner:
|
|
250
|
+
# the ambient context every model run of the pass binds (None = not
|
|
251
|
+
# recording) and the pre-minted item id of the message opening it.
|
|
252
|
+
self._attempt_trace = ModelRunTrace(
|
|
253
|
+
interrupted=lambda: self._interrupt.is_interrupted,
|
|
254
|
+
service_side=lambda: self.backend.service_session_storage_enabled,
|
|
255
|
+
committed=lambda: self._loop_recorder is not None and self._loop_recorder.committed_count > 0,
|
|
256
|
+
)
|
|
257
|
+
attempt_recipe = AttemptRecipe(
|
|
258
|
+
stall_exhaustion=RestoreAndFallback(self._reject_stream),
|
|
259
|
+
stall_error=lambda _timeout: StreamStall(0),
|
|
260
|
+
retry_boundary=RetryBoundaryPolicy.SKIP,
|
|
261
|
+
blocking_call_timing=BlockingCallTiming.IN_ATTEMPT_TASK,
|
|
262
|
+
history_state=lambda: session.state.get("chrys_history", {}),
|
|
263
|
+
before_attempt=None,
|
|
264
|
+
)
|
|
265
|
+
self._rollback = HistoryRollback(
|
|
266
|
+
session,
|
|
267
|
+
snapshot_caller=self._snapshot_retry_state,
|
|
268
|
+
restore_caller=self._restore_retry_state,
|
|
269
|
+
history_state=attempt_recipe.history_state,
|
|
270
|
+
)
|
|
271
|
+
self._attempts = AttemptRunner(
|
|
272
|
+
agent=agent,
|
|
273
|
+
session=session,
|
|
274
|
+
handle=self._attempt_handle,
|
|
275
|
+
rollback=self._rollback,
|
|
276
|
+
retry_participant=self._injection,
|
|
277
|
+
interrupt=self._interrupt,
|
|
278
|
+
trace=self._attempt_trace,
|
|
279
|
+
stream_observer=self._make_stream_observer,
|
|
280
|
+
publish_retry=self._publish_retry_notice,
|
|
281
|
+
interruptible_sleep=self._interruptible_sleep,
|
|
282
|
+
max_retries=self._effective_max_retries,
|
|
283
|
+
backoff_schedule=lambda: self._BACKOFF_SCHEDULE,
|
|
284
|
+
stream_timeout=lambda: self._stream_attempt_timeout,
|
|
285
|
+
committed_count=lambda: self._loop_recorder.committed_count if self._loop_recorder is not None else 0,
|
|
286
|
+
hosted_commits=lambda: self._hosted_commits_probe() if self._hosted_commits_probe is not None else (),
|
|
287
|
+
recipe=attempt_recipe,
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
self.backend = KernelConversation(
|
|
291
|
+
owner=conversation,
|
|
292
|
+
session=session,
|
|
293
|
+
attempts=self._attempts,
|
|
294
|
+
attempt_handle=self._attempt_handle,
|
|
295
|
+
observer=self,
|
|
296
|
+
run_kwargs=lambda: self._build_run_kwargs(
|
|
297
|
+
None if self.backend.service_session_storage_enabled else self._build_wire_retry_policy()
|
|
298
|
+
),
|
|
299
|
+
stream=lambda: self._stream,
|
|
300
|
+
service_side=self._resolve_service_storage,
|
|
301
|
+
compaction_strategy=compaction_strategy,
|
|
302
|
+
recorder=loop_recorder,
|
|
303
|
+
hosted_observed=hosted_commits_probe,
|
|
304
|
+
start_hooks=(*run_cycle_start_hooks, self._begin_hosted_baseline),
|
|
305
|
+
failure_disposition=FailureDisposition.CALLER_DECISION,
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
self.inputs = TurnResumePolicy(self.backend, session_id, self.state)
|
|
309
|
+
self._invocation_publishers = invocation_publishers or InvocationPublishers(event_bus)
|
|
310
|
+
self._bound_emitter: BoundEmitter | None = None
|
|
311
|
+
self.tool_events = ToolEventMiddleware(
|
|
312
|
+
event_bus,
|
|
313
|
+
origin=self.inputs.origin,
|
|
314
|
+
session_id=session_id,
|
|
315
|
+
intermediate_buffer=intermediate_buffer,
|
|
316
|
+
mutation_tracker=mutation_tracker,
|
|
317
|
+
hook_manager=hook_manager,
|
|
318
|
+
profile_name=profile_name,
|
|
319
|
+
workspace_cwd=workspace_cwd,
|
|
320
|
+
serialize_implicit_windows=serialize_implicit_windows,
|
|
321
|
+
mutation_coordinator=mutation_coordinator,
|
|
322
|
+
on_start_published=self._local_call_start_published,
|
|
323
|
+
tool_result_ceiling_tokens=tool_result_ceiling_tokens,
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
@property
|
|
327
|
+
def trajectory_context(self) -> TrajectoryContext | None:
|
|
328
|
+
return self._attempt_trace.context
|
|
329
|
+
|
|
330
|
+
@trajectory_context.setter
|
|
331
|
+
def trajectory_context(self, context: TrajectoryContext | None) -> None:
|
|
332
|
+
self._attempt_trace.context = context
|
|
333
|
+
self._attempt_trace.first_run_links = (
|
|
334
|
+
(Link(relation=LinkRelation.CAUSED_BY, target_operation_id=context.turn_preamble_operation_id),)
|
|
335
|
+
if context is not None and context.turn_preamble_operation_id is not None
|
|
336
|
+
else ()
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
def record_pre_run_interrupt(self) -> None:
|
|
340
|
+
"""Record a task-scoped interrupt that arrived before execution began."""
|
|
341
|
+
self.state.was_interrupted = True
|
|
342
|
+
self.state.run_failed = False
|
|
343
|
+
self.state.last_error = ""
|
|
344
|
+
# A cancelled Turn pass abandons any incomplete provider-side
|
|
345
|
+
# response even when Stop prevents ``run()``/``resume()`` from
|
|
346
|
+
# reaching their normal reset paths.
|
|
347
|
+
self.inputs.pending_continuation_token = None
|
|
348
|
+
self._interrupt.reset()
|
|
349
|
+
|
|
350
|
+
def _begin_hosted_baseline(self) -> None:
|
|
351
|
+
if self._response_validation is not None:
|
|
352
|
+
self._response_validation.begin_pass_hosted_baseline(
|
|
353
|
+
resumes_background_response=self.inputs.pending_continuation_token is not None
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
def _resolve_service_storage(self) -> bool:
|
|
357
|
+
"""Return whether the first request uses provider-side history."""
|
|
358
|
+
try:
|
|
359
|
+
stores_by_default = bool(self._agent.client.STORES_BY_DEFAULT)
|
|
360
|
+
except AttributeError:
|
|
361
|
+
stores_by_default = False
|
|
362
|
+
try:
|
|
363
|
+
force_stateless = bool(self._agent.client.FORCES_STATELESS)
|
|
364
|
+
except AttributeError:
|
|
365
|
+
force_stateless = False
|
|
366
|
+
return resolve_storage_mode_and_handles(
|
|
367
|
+
self._chat_options,
|
|
368
|
+
stores_by_default=stores_by_default,
|
|
369
|
+
force_stateless=force_stateless,
|
|
370
|
+
).service_side
|
|
371
|
+
|
|
372
|
+
async def _local_call_start_published(self, provider_call_id: str) -> None:
|
|
373
|
+
"""Release hosted presentation ordered behind a local tool call."""
|
|
374
|
+
if self._hosted_bridge is not None:
|
|
375
|
+
await self._hosted_bridge.local_call_start_published(provider_call_id)
|
|
376
|
+
|
|
377
|
+
@staticmethod
|
|
378
|
+
def _artifact_descriptors(operation: HostedToolResultOp) -> list[dict[str, Any]]:
|
|
379
|
+
"""Build bounded JSON-safe descriptors for hosted result artifacts."""
|
|
380
|
+
descriptors: list[dict[str, Any]] = []
|
|
381
|
+
for artifact in operation.view.artifacts:
|
|
382
|
+
# "path" must stay a real URI: consumers turn it into links, and a
|
|
383
|
+
# bare hosted filename (OpenAI hosted_file) is not addressable.
|
|
384
|
+
descriptor = {
|
|
385
|
+
"id": artifact.file_id or artifact.vector_store_id or artifact.id or "",
|
|
386
|
+
"name": artifact.name or "",
|
|
387
|
+
"path": artifact.uri or "",
|
|
388
|
+
"mime": artifact.media_type or "",
|
|
389
|
+
}
|
|
390
|
+
size = artifact.additional_properties.get("size")
|
|
391
|
+
if isinstance(size, int) and not isinstance(size, bool):
|
|
392
|
+
descriptor["size"] = size
|
|
393
|
+
descriptors.append({key: value for key, value in descriptor.items() if value != ""})
|
|
394
|
+
return descriptors
|
|
395
|
+
|
|
396
|
+
async def _publish_hosted_operation(self, operation: PresentationSinkOperation, *, emitter: BoundEmitter) -> None:
|
|
397
|
+
"""Map one provider-neutral presentation operation onto EventBus events."""
|
|
398
|
+
if isinstance(operation, IntermediateTextOp):
|
|
399
|
+
if operation.provisional:
|
|
400
|
+
segment_id = operation.segment_ids[0] if operation.segment_ids else ""
|
|
401
|
+
if self._intermediate_buffer is not None:
|
|
402
|
+
self._intermediate_buffer.new_batch()
|
|
403
|
+
self._provisional_intermediate_batches[(operation.attempt_id, segment_id)] = (
|
|
404
|
+
self._intermediate_buffer.batch_id
|
|
405
|
+
)
|
|
406
|
+
await emitter.publish(
|
|
407
|
+
InvocationMessage(
|
|
408
|
+
origin=emitter.origin,
|
|
409
|
+
text=operation.text,
|
|
410
|
+
is_final=False,
|
|
411
|
+
is_intermediate=True,
|
|
412
|
+
presentation=ProvisionalPresentation(operation.attempt_id, segment_id),
|
|
413
|
+
session_id=self._session_id,
|
|
414
|
+
)
|
|
415
|
+
)
|
|
416
|
+
return
|
|
417
|
+
if self._publish_intermediate_text is not None:
|
|
418
|
+
await self._publish_intermediate_text(operation.text)
|
|
419
|
+
else:
|
|
420
|
+
await emitter.publish(
|
|
421
|
+
InvocationMessage(
|
|
422
|
+
origin=emitter.origin,
|
|
423
|
+
text=operation.text,
|
|
424
|
+
is_final=False,
|
|
425
|
+
is_intermediate=True,
|
|
426
|
+
session_id=self._session_id,
|
|
427
|
+
)
|
|
428
|
+
)
|
|
429
|
+
return
|
|
430
|
+
if isinstance(operation, PresentationAttemptAcceptedOp):
|
|
431
|
+
accepted_ids = tuple(segment_id for segment in operation.segments for segment_id in segment.segment_ids)
|
|
432
|
+
if self._commit_intermediate_text is not None:
|
|
433
|
+
for segment in operation.segments:
|
|
434
|
+
segment_id = segment.segment_ids[0] if segment.segment_ids else ""
|
|
435
|
+
batch_id = self._provisional_intermediate_batches.get((operation.attempt_id, segment_id))
|
|
436
|
+
if batch_id is not None:
|
|
437
|
+
self._commit_intermediate_text(segment.text, batch_id)
|
|
438
|
+
self._drop_provisional_batches(operation.attempt_id)
|
|
439
|
+
await emitter.publish(
|
|
440
|
+
InvocationPresentationAttemptAccepted(
|
|
441
|
+
origin=emitter.origin,
|
|
442
|
+
attempt_id=operation.attempt_id,
|
|
443
|
+
segment_ids=accepted_ids,
|
|
444
|
+
session_id=self._session_id,
|
|
445
|
+
)
|
|
446
|
+
)
|
|
447
|
+
return
|
|
448
|
+
if isinstance(operation, PresentationAttemptRejectedOp):
|
|
449
|
+
self._drop_provisional_batches(operation.attempt_id)
|
|
450
|
+
await emitter.publish(
|
|
451
|
+
InvocationPresentationAttemptRejected(
|
|
452
|
+
origin=emitter.origin, attempt_id=operation.attempt_id, session_id=self._session_id
|
|
453
|
+
)
|
|
454
|
+
)
|
|
455
|
+
return
|
|
456
|
+
if isinstance(operation, FinalTextOp):
|
|
457
|
+
await emitter.publish(
|
|
458
|
+
InvocationMessage(
|
|
459
|
+
origin=emitter.origin,
|
|
460
|
+
text=operation.text,
|
|
461
|
+
is_final=True,
|
|
462
|
+
structured_output_completed=operation.structured_output_completed,
|
|
463
|
+
**self._assistant_message_event_kwargs(),
|
|
464
|
+
session_id=self._session_id,
|
|
465
|
+
)
|
|
466
|
+
)
|
|
467
|
+
return
|
|
468
|
+
|
|
469
|
+
view = operation.view
|
|
470
|
+
if isinstance(operation, HostedToolStartOp):
|
|
471
|
+
await emitter.publish(
|
|
472
|
+
InvocationToolCallStart(
|
|
473
|
+
origin=emitter.origin,
|
|
474
|
+
tool_name=view.tool_name,
|
|
475
|
+
call_id=operation.presentation_id,
|
|
476
|
+
provider_hosted=True,
|
|
477
|
+
hosted_family=view.family,
|
|
478
|
+
provider=view.provider,
|
|
479
|
+
provider_item_type=view.provider_item_type,
|
|
480
|
+
provider_call_id=view.provider_call_id,
|
|
481
|
+
provider_status=view.provider_status,
|
|
482
|
+
tool_kind=HOSTED_TOOL_DEFAULT_KIND_BY_FAMILY.get(view.family, ""),
|
|
483
|
+
args=view.arguments,
|
|
484
|
+
session_id=self._session_id,
|
|
485
|
+
)
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
elif isinstance(operation, HostedToolArgsOp):
|
|
489
|
+
await emitter.publish(
|
|
490
|
+
InvocationToolCallArgsUpdated(
|
|
491
|
+
origin=emitter.origin,
|
|
492
|
+
tool_name=view.tool_name,
|
|
493
|
+
call_id=operation.presentation_id,
|
|
494
|
+
provider_hosted=True,
|
|
495
|
+
hosted_family=view.family,
|
|
496
|
+
provider=view.provider,
|
|
497
|
+
provider_item_type=view.provider_item_type,
|
|
498
|
+
provider_call_id=view.provider_call_id,
|
|
499
|
+
provider_status=view.provider_status,
|
|
500
|
+
tool_kind=HOSTED_TOOL_DEFAULT_KIND_BY_FAMILY.get(view.family, ""),
|
|
501
|
+
args=view.arguments,
|
|
502
|
+
session_id=self._session_id,
|
|
503
|
+
)
|
|
504
|
+
)
|
|
505
|
+
elif isinstance(operation, HostedToolProgressOp):
|
|
506
|
+
await emitter.publish(
|
|
507
|
+
InvocationToolCallProgress(
|
|
508
|
+
origin=emitter.origin,
|
|
509
|
+
tool_name=view.tool_name,
|
|
510
|
+
call_id=operation.presentation_id,
|
|
511
|
+
provider_hosted=True,
|
|
512
|
+
hosted_family=view.family,
|
|
513
|
+
provider=view.provider,
|
|
514
|
+
provider_item_type=view.provider_item_type,
|
|
515
|
+
provider_call_id=view.provider_call_id,
|
|
516
|
+
provider_status=view.provider_status,
|
|
517
|
+
lines=view.result_text.splitlines(),
|
|
518
|
+
image_contents=view.image_contents,
|
|
519
|
+
snapshot_metadata=view.metadata,
|
|
520
|
+
session_id=self._session_id,
|
|
521
|
+
)
|
|
522
|
+
)
|
|
523
|
+
elif isinstance(operation, HostedToolStatusOp):
|
|
524
|
+
metadata = dict(view.metadata)
|
|
525
|
+
if view.result_text:
|
|
526
|
+
metadata["result_text"] = view.result_text
|
|
527
|
+
if view.provider_item_type:
|
|
528
|
+
metadata["provider_item_type"] = view.provider_item_type
|
|
529
|
+
if view.provider_call_id:
|
|
530
|
+
metadata["provider_call_id"] = view.provider_call_id
|
|
531
|
+
await emitter.publish(
|
|
532
|
+
InvocationToolCallStatusUpdated(
|
|
533
|
+
origin=emitter.origin,
|
|
534
|
+
tool_name=view.tool_name,
|
|
535
|
+
call_id=operation.presentation_id,
|
|
536
|
+
provider_hosted=True,
|
|
537
|
+
hosted_family=view.family,
|
|
538
|
+
provider=view.provider,
|
|
539
|
+
status=view.status,
|
|
540
|
+
provider_status=view.provider_status,
|
|
541
|
+
metadata=metadata,
|
|
542
|
+
session_id=self._session_id,
|
|
543
|
+
)
|
|
544
|
+
)
|
|
545
|
+
elif isinstance(operation, HostedToolResultOp):
|
|
546
|
+
await emitter.publish(
|
|
547
|
+
InvocationToolCallResult(
|
|
548
|
+
origin=emitter.origin,
|
|
549
|
+
tool_name=view.tool_name,
|
|
550
|
+
call_id=operation.presentation_id,
|
|
551
|
+
provider_hosted=True,
|
|
552
|
+
hosted_family=view.family,
|
|
553
|
+
provider=view.provider,
|
|
554
|
+
provider_item_type=view.provider_item_type,
|
|
555
|
+
provider_call_id=view.provider_call_id,
|
|
556
|
+
provider_status=view.provider_status,
|
|
557
|
+
result=view.result_text,
|
|
558
|
+
image_contents=view.image_contents,
|
|
559
|
+
metadata=view.metadata,
|
|
560
|
+
artifacts=self._artifact_descriptors(operation),
|
|
561
|
+
session_id=self._session_id,
|
|
562
|
+
)
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
def _drop_provisional_batches(self, attempt_id: str) -> None:
|
|
566
|
+
stale = [key for key in self._provisional_intermediate_batches if key[0] == attempt_id]
|
|
567
|
+
for key in stale:
|
|
568
|
+
self._provisional_intermediate_batches.pop(key, None)
|
|
569
|
+
|
|
570
|
+
def _build_run_kwargs(self, wire_retry_policy: WireRetryPolicy | None = None) -> AgentRunKwargs:
|
|
571
|
+
"""Build common kwargs for agent.run()."""
|
|
572
|
+
# ToolEventMiddleware owns hook dispatch too, so hook-modified args
|
|
573
|
+
# are reflected consistently in UI events, mutation tracking, and
|
|
574
|
+
# approval requests.
|
|
575
|
+
# Per-run channel consumed by the chrys ToolLoopLayer (fresh dict per
|
|
576
|
+
# call; the agent merges the session into the same mapping). A None
|
|
577
|
+
# recorder is not injected — absent key and None mean the same thing
|
|
578
|
+
# to the loop, and an absent key keeps the mapping honest.
|
|
579
|
+
client_kwargs: dict[str, object] = {}
|
|
580
|
+
if self._loop_recorder is not None:
|
|
581
|
+
client_kwargs["loop_recorder"] = self._loop_recorder
|
|
582
|
+
if wire_retry_policy is not None:
|
|
583
|
+
client_kwargs["wire_retry_policy"] = wire_retry_policy
|
|
584
|
+
client_kwargs["consumed_injection_message_probe"] = ConsumedInjectionMessageProbe(
|
|
585
|
+
drain_consumed_injection_messages=self._injection.drain_consumed_injection_messages,
|
|
586
|
+
commit_consumed_injections=self._injection.commit_logical_call,
|
|
587
|
+
)
|
|
588
|
+
# Chain order is pinned (first = outermost): extras sit INSIDE
|
|
589
|
+
# tool_events (a short-circuiting extra must still publish
|
|
590
|
+
# InvocationToolCallStart/InvocationToolCallResult) and INSIDE approval (no extra can
|
|
591
|
+
# bypass approval on gated kinds).
|
|
592
|
+
kwargs: AgentRunKwargs = {
|
|
593
|
+
"session": self._session,
|
|
594
|
+
"middleware": [
|
|
595
|
+
self.tool_events,
|
|
596
|
+
self._ask_user,
|
|
597
|
+
self.approval,
|
|
598
|
+
*self._extra_function_middleware,
|
|
599
|
+
self._sleep,
|
|
600
|
+
self._interrupt,
|
|
601
|
+
],
|
|
602
|
+
"client_kwargs": client_kwargs,
|
|
603
|
+
}
|
|
604
|
+
if self._compaction_strategy is not None:
|
|
605
|
+
kwargs["compaction_strategy"] = self._compaction_strategy
|
|
606
|
+
# The raw client needs the same tokenizer to estimate live tool
|
|
607
|
+
# definitions before the first calibrated provider response. The
|
|
608
|
+
# strategy remains the single owner of the tokenizer instance.
|
|
609
|
+
kwargs["tokenizer"] = self._compaction_strategy.tokenizer
|
|
610
|
+
if self._chat_options:
|
|
611
|
+
options_copy = dict(self._chat_options)
|
|
612
|
+
if is_string_keyed_dict(extra_body := options_copy.get("extra_body")):
|
|
613
|
+
options_copy["extra_body"] = dict(extra_body)
|
|
614
|
+
kwargs["options"] = options_copy
|
|
615
|
+
if self.inputs.pending_continuation_token is not None:
|
|
616
|
+
# A previous run of this turn left a background response in
|
|
617
|
+
# flight (poll failed, gate blocked the auto-retry). Resume it
|
|
618
|
+
# instead of creating a duplicate; the kernel clears the token
|
|
619
|
+
# on any terminal judgment.
|
|
620
|
+
seeded = kwargs.get("options")
|
|
621
|
+
if not is_string_keyed_dict(seeded):
|
|
622
|
+
seeded = {}
|
|
623
|
+
kwargs["options"] = seeded
|
|
624
|
+
seeded["continuation_token"] = self.inputs.pending_continuation_token
|
|
625
|
+
mirror_to_run_kwargs = continuation_token_observer_for(kwargs)
|
|
626
|
+
|
|
627
|
+
def _observe_continuation_token(token: Any) -> None:
|
|
628
|
+
self.inputs.pending_continuation_token = token
|
|
629
|
+
mirror_to_run_kwargs(token)
|
|
630
|
+
|
|
631
|
+
client_kwargs["continuation_token_observer"] = _observe_continuation_token
|
|
632
|
+
return kwargs
|
|
633
|
+
|
|
634
|
+
def _build_wire_retry_policy(self) -> WireRetryPolicy:
|
|
635
|
+
max_retries = self._effective_max_retries()
|
|
636
|
+
return WireRecipe(
|
|
637
|
+
max_retries=max_retries,
|
|
638
|
+
stall_timeout_seconds=self._stream_attempt_timeout,
|
|
639
|
+
stall_max_retries=max_retries,
|
|
640
|
+
stall_exhausted_action=StallExhaustedAction.BLOCKING_FALLBACK,
|
|
641
|
+
backoff_schedule=self._BACKOFF_SCHEDULE,
|
|
642
|
+
interrupted=lambda: self._interrupt.is_interrupted,
|
|
643
|
+
interruptible_sleep=self._interruptible_sleep,
|
|
644
|
+
publish_retry=self._publish_wire_retry_attempt,
|
|
645
|
+
prepare_retry=self._injection.restore_for_retry,
|
|
646
|
+
hosted_commits_in_flight=self._hosted_commits_in_flight_probe,
|
|
647
|
+
).build()
|
|
648
|
+
|
|
649
|
+
def _effective_max_retries(self) -> int:
|
|
650
|
+
"""Return the injected budget or the lazily resolved class fallback."""
|
|
651
|
+
if self._max_retries_override is not None:
|
|
652
|
+
return self._max_retries_override
|
|
653
|
+
return self._MAX_RETRIES
|
|
654
|
+
|
|
655
|
+
def record_outcome(self, outcome: InvocationOutcome) -> None:
|
|
656
|
+
self.inputs.evidence = self.inputs.evidence.add(outcome.effects)
|
|
657
|
+
self.inputs.outcome = outcome
|
|
658
|
+
|
|
659
|
+
@property
|
|
660
|
+
def _emitter(self) -> BoundEmitter:
|
|
661
|
+
if self._bound_emitter is None:
|
|
662
|
+
raise ValueError("Cannot publish outside an active turn pass")
|
|
663
|
+
return self._bound_emitter
|
|
664
|
+
|
|
665
|
+
async def begin(self, request: RunRequest, resources: PassResources) -> None:
|
|
666
|
+
"""Start presentation and validation inside the backend pass try."""
|
|
667
|
+
self._bound_emitter = self._invocation_publishers.bind(request.origin)
|
|
668
|
+
self.approval.bind_publisher(self._emitter)
|
|
669
|
+
self.tool_events.bind_origin(request.origin)
|
|
670
|
+
if request.intent is RunIntent.FRESH:
|
|
671
|
+
self.inputs.pending_continuation_token = None
|
|
672
|
+
self.state.running = True
|
|
673
|
+
self.state.was_interrupted = False
|
|
674
|
+
self.state.run_failed = False
|
|
675
|
+
self.state.last_error = ""
|
|
676
|
+
self._interrupt.reset()
|
|
677
|
+
self._attempt_trace.reset()
|
|
678
|
+
self._hosted_run_generation = next_hosted_run_generation()
|
|
679
|
+
emitter = self._emitter
|
|
680
|
+
self._hosted_bridge = HostedPresentationBridge(
|
|
681
|
+
lambda operation: self._publish_hosted_operation(operation, emitter=emitter),
|
|
682
|
+
run_generation=self._hosted_run_generation,
|
|
683
|
+
batch_id=self._intermediate_buffer.batch_id if self._intermediate_buffer is not None else 0,
|
|
684
|
+
before_response=self.tool_events.release_intermediate_text,
|
|
685
|
+
)
|
|
686
|
+
if self._response_validation is not None:
|
|
687
|
+
self._response_validation.set_observation_hook(self._hosted_bridge)
|
|
688
|
+
else:
|
|
689
|
+
await self._hosted_bridge.begin_response(response_index=0)
|
|
690
|
+
resources.begin()
|
|
691
|
+
await self._emitter.publish(AgentThinking(session_id=self._session_id))
|
|
692
|
+
await self._flush_carried_compressions()
|
|
693
|
+
|
|
694
|
+
async def succeeded(self, result: AgentResponse[Any]) -> None:
|
|
695
|
+
if not self._interrupt.is_interrupted:
|
|
696
|
+
await self._publish_response_text(result)
|
|
697
|
+
|
|
698
|
+
def cancelled(self) -> None:
|
|
699
|
+
self.state.was_interrupted = True
|
|
700
|
+
|
|
701
|
+
def abort_cause(self) -> AbortCause | None:
|
|
702
|
+
if self._interrupt.is_interrupted or self.state.was_interrupted:
|
|
703
|
+
return AbortCause.USER_CANCEL
|
|
704
|
+
return None
|
|
705
|
+
|
|
706
|
+
async def failed(self, e: Exception) -> None:
|
|
707
|
+
if self._interrupt.is_interrupted:
|
|
708
|
+
self.state.was_interrupted = True
|
|
709
|
+
return
|
|
710
|
+
self.state.run_failed = True
|
|
711
|
+
self.state.last_error = clean_error_message(e)
|
|
712
|
+
tb = traceback.format_exc()
|
|
713
|
+
# Text left buffered by a call that started no tool precedes the error.
|
|
714
|
+
await self.tool_events.release_intermediate_text()
|
|
715
|
+
err_msg = self.state.last_error
|
|
716
|
+
logger.error("Turn error: %s", err_msg)
|
|
717
|
+
if self._hosted_bridge is not None:
|
|
718
|
+
await self._hosted_bridge.attempt_rejected(err_msg)
|
|
719
|
+
strategy = self._compaction_strategy
|
|
720
|
+
display_message, display_hint = display_fields(
|
|
721
|
+
e, max_context_tokens=strategy.max_context_tokens if strategy is not None else None
|
|
722
|
+
)
|
|
723
|
+
await self._emitter.publish(
|
|
724
|
+
Error(
|
|
725
|
+
code="executor_error",
|
|
726
|
+
message=err_msg,
|
|
727
|
+
recoverable=True,
|
|
728
|
+
session_id=self._session_id,
|
|
729
|
+
display_message=display_message,
|
|
730
|
+
display_hint=display_hint,
|
|
731
|
+
)
|
|
732
|
+
)
|
|
733
|
+
logger.debug("Turn traceback:\n%s", tb)
|
|
734
|
+
|
|
735
|
+
async def finished(self) -> None:
|
|
736
|
+
try:
|
|
737
|
+
# An interrupted or cancelled pass publishes no outcome, and the
|
|
738
|
+
# buffer outlives the pass: text still waiting for a tool start
|
|
739
|
+
# would otherwise surface at a tool start in a later turn.
|
|
740
|
+
await self.tool_events.finish_intermediate_text()
|
|
741
|
+
if self._interrupt.is_interrupted and self._hosted_bridge is not None:
|
|
742
|
+
await self._hosted_bridge.attempt_rejected(
|
|
743
|
+
"Execution interrupted",
|
|
744
|
+
status=HostedToolStatus.INTERRUPTED,
|
|
745
|
+
preserve_provisional=True,
|
|
746
|
+
)
|
|
747
|
+
finally:
|
|
748
|
+
# Close can cancel the awaits above. The reset must not erase an
|
|
749
|
+
# interrupt that arrived while they ran.
|
|
750
|
+
if self._interrupt.is_interrupted:
|
|
751
|
+
self.state.was_interrupted = True
|
|
752
|
+
if self._response_validation is not None:
|
|
753
|
+
self._response_validation.set_observation_hook(None)
|
|
754
|
+
self._interrupt.reset()
|
|
755
|
+
self.state.running = False
|
|
756
|
+
if self._bound_emitter is not None:
|
|
757
|
+
self._invocation_publishers.unbind(self._bound_emitter.origin)
|
|
758
|
+
self._bound_emitter = None
|
|
759
|
+
self.approval.bind_publisher(None)
|
|
760
|
+
|
|
761
|
+
async def _flush_carried_compressions(self) -> None:
|
|
762
|
+
"""Commit a prior failed pass's queued folds before retry snapshots.
|
|
763
|
+
|
|
764
|
+
A ``compress_context`` call can validate and queue a fold, then the
|
|
765
|
+
pass can terminate before the history provider reaches its normal
|
|
766
|
+
``after_run`` flush. The next user retry must treat that request as
|
|
767
|
+
pre-existing committed work. If it first runs inside
|
|
768
|
+
``StreamRetryLoop`` instead, a transient failure restores the
|
|
769
|
+
pre-attempt history (removing the new ``CompressedBlock``) after the
|
|
770
|
+
queue has already been consumed. The live ``ContextCompressed``
|
|
771
|
+
event then has no durable counterpart and disappears on replay.
|
|
772
|
+
|
|
773
|
+
Bind the current provider state explicitly and flush before
|
|
774
|
+
the L0 runner captures its attempt
|
|
775
|
+
snapshot. The history provider binds the same state again inside
|
|
776
|
+
``agent.run()``; that later bind is intentionally idempotent.
|
|
777
|
+
"""
|
|
778
|
+
strategy = self._compaction_strategy
|
|
779
|
+
if strategy is None:
|
|
780
|
+
return
|
|
781
|
+
history_state = self.backend.history_state
|
|
782
|
+
strategy.bind_state(history_state)
|
|
783
|
+
if not await strategy.flush_pending_compressions():
|
|
784
|
+
return
|
|
785
|
+
messages = history_state.get("messages", [])
|
|
786
|
+
if isinstance(messages, list):
|
|
787
|
+
# TurnRunner.pre_run captured its metadata floor before this
|
|
788
|
+
# carried fold shortened history. Finalization must start at the
|
|
789
|
+
# post-fold boundary so retry output still receives approval,
|
|
790
|
+
# modified-argument, and timestamp annotations.
|
|
791
|
+
history_state[PRE_OUTPUT_HISTORY_LEN_STATE_KEY] = len(messages)
|
|
792
|
+
|
|
793
|
+
# ── main presentation ────────────────────────────────────────────
|
|
794
|
+
|
|
795
|
+
async def _publish_wire_retry_attempt(
|
|
796
|
+
self,
|
|
797
|
+
message: str,
|
|
798
|
+
attempt: int,
|
|
799
|
+
max_attempts: int,
|
|
800
|
+
delay_seconds: int,
|
|
801
|
+
exc: BaseException,
|
|
802
|
+
) -> None:
|
|
803
|
+
"""The wire retry policy's notifier: UI only.
|
|
804
|
+
|
|
805
|
+
A wire retry is recorded by the loop that performs it, as a new
|
|
806
|
+
exchange under the same run (``retry.scheduled{retry_mode: wire}``).
|
|
807
|
+
Recording a run-level retry here as well would count one retry twice
|
|
808
|
+
and leave a scheduled marker that no ``retry.started`` ever answers.
|
|
809
|
+
"""
|
|
810
|
+
await self._publish_retry_notice(message, attempt, max_attempts, delay_seconds, exc, scope="wire")
|
|
811
|
+
|
|
812
|
+
async def _publish_retry_notice(
|
|
813
|
+
self,
|
|
814
|
+
message: str,
|
|
815
|
+
attempt: int,
|
|
816
|
+
max_attempts: int,
|
|
817
|
+
delay_seconds: int,
|
|
818
|
+
exc: BaseException,
|
|
819
|
+
*,
|
|
820
|
+
scope: Literal["wire", "run"] = "run",
|
|
821
|
+
) -> None:
|
|
822
|
+
if self._hosted_bridge is not None and self.inputs.pending_continuation_token is None:
|
|
823
|
+
await self._hosted_bridge.attempt_rejected(message)
|
|
824
|
+
display_message, display_hint = display_fields(exc, retry_notice=True)
|
|
825
|
+
await self._emitter.publish(
|
|
826
|
+
InvocationRetryAttempt(
|
|
827
|
+
origin=self._emitter.origin,
|
|
828
|
+
message=message,
|
|
829
|
+
attempt=attempt,
|
|
830
|
+
scope=scope,
|
|
831
|
+
max_attempts=max_attempts,
|
|
832
|
+
delay_seconds=delay_seconds,
|
|
833
|
+
session_id=self._session_id,
|
|
834
|
+
display_message=display_message,
|
|
835
|
+
display_hint=display_hint,
|
|
836
|
+
)
|
|
837
|
+
)
|
|
838
|
+
|
|
839
|
+
async def _publish_response_text(self, result: AgentResponse[Any]) -> None:
|
|
840
|
+
"""Publish the final text from a completed AgentResponse.
|
|
841
|
+
|
|
842
|
+
Only publishes the *last* message — intermediate text is handled
|
|
843
|
+
by ``IntermediateTextBuffer`` + ``ToolEventMiddleware``.
|
|
844
|
+
|
|
845
|
+
The final event is also the frontend's turn-complete signal. It
|
|
846
|
+
must be emitted even when the final text is empty, for example
|
|
847
|
+
after response validation exhausts retries and drops an empty /
|
|
848
|
+
whitespace-only assistant message.
|
|
849
|
+
"""
|
|
850
|
+
if self._response_has_hosted_contents(result) and self._hosted_bridge is not None:
|
|
851
|
+
await self._hosted_bridge.reconcile_accepted(result.messages, final=True)
|
|
852
|
+
return
|
|
853
|
+
final_text = self._extract_final_text(result)
|
|
854
|
+
event_kwargs = self._assistant_message_event_kwargs()
|
|
855
|
+
await self._emitter.publish(
|
|
856
|
+
InvocationMessage(
|
|
857
|
+
origin=self._emitter.origin,
|
|
858
|
+
text=final_text,
|
|
859
|
+
is_final=True,
|
|
860
|
+
**event_kwargs,
|
|
861
|
+
session_id=self._session_id,
|
|
862
|
+
)
|
|
863
|
+
)
|
|
864
|
+
|
|
865
|
+
# ── control ──────────────────────────────────────────────────────
|
|
866
|
+
|
|
867
|
+
async def interrupt(self) -> None:
|
|
868
|
+
"""Signal the Turn to stop and cancel any running tool.
|
|
869
|
+
|
|
870
|
+
Sets the interrupt flag for the middleware boundary check AND
|
|
871
|
+
cancels the current ``agent.run()`` child task so long-running
|
|
872
|
+
tools (sub-agents, scripts) are actually terminated instead of
|
|
873
|
+
completing in the background.
|
|
874
|
+
"""
|
|
875
|
+
generation = self._hosted_run_generation
|
|
876
|
+
self._interrupt.set_interrupted()
|
|
877
|
+
if self._hosted_bridge is not None:
|
|
878
|
+
await self._hosted_bridge.attempt_rejected(
|
|
879
|
+
"Execution interrupted",
|
|
880
|
+
status=HostedToolStatus.INTERRUPTED,
|
|
881
|
+
preserve_provisional=True,
|
|
882
|
+
)
|
|
883
|
+
if self._hosted_run_generation != generation:
|
|
884
|
+
return
|
|
885
|
+
if self._attempt_handle.active:
|
|
886
|
+
sleep_call_ids = self._sleep.active_call_ids
|
|
887
|
+
if sleep_call_ids:
|
|
888
|
+
await self._interrupt_active_sleep(set(sleep_call_ids))
|
|
889
|
+
# A wire retry may replace the attempt inside this pass; still
|
|
890
|
+
# cancel it. A successor pass must never inherit this interrupt.
|
|
891
|
+
if self._hosted_run_generation == generation:
|
|
892
|
+
self._attempt_handle.cancel()
|
|
893
|
+
|
|
894
|
+
async def _interrupt_active_sleep(self, call_ids: set[str]) -> None:
|
|
895
|
+
"""Let an active sleep publish its interrupted InvocationToolCallResult before cancellation."""
|
|
896
|
+
if not call_ids:
|
|
897
|
+
return
|
|
898
|
+
pending = set(call_ids)
|
|
899
|
+
loop = asyncio.get_running_loop()
|
|
900
|
+
completed: asyncio.Future[None] = loop.create_future()
|
|
901
|
+
|
|
902
|
+
origin = self._emitter.origin
|
|
903
|
+
|
|
904
|
+
async def _on_tool_result(event: InvocationToolCallResult) -> None:
|
|
905
|
+
if event.origin != origin:
|
|
906
|
+
return
|
|
907
|
+
pending.discard(event.call_id)
|
|
908
|
+
if not pending and not completed.done():
|
|
909
|
+
completed.set_result(None)
|
|
910
|
+
|
|
911
|
+
await self._bus.subscribe(InvocationToolCallResult, _on_tool_result)
|
|
912
|
+
try:
|
|
913
|
+
interrupted = set(self._sleep.interrupt_active())
|
|
914
|
+
pending.intersection_update(interrupted)
|
|
915
|
+
if not pending:
|
|
916
|
+
return
|
|
917
|
+
# Keep Esc responsive: give the interrupted sleep a small
|
|
918
|
+
# writeback window, then let task cancellation win.
|
|
919
|
+
await asyncio.wait_for(completed, timeout=0.5)
|
|
920
|
+
except TimeoutError:
|
|
921
|
+
return
|
|
922
|
+
finally:
|
|
923
|
+
await self._bus.unsubscribe(InvocationToolCallResult, _on_tool_result)
|
|
924
|
+
|
|
925
|
+
async def _interruptible_sleep(self, seconds: int) -> bool:
|
|
926
|
+
"""Sleep in 1-second ticks, returning True if interrupted."""
|
|
927
|
+
for _ in range(seconds):
|
|
928
|
+
if self._interrupt.is_interrupted:
|
|
929
|
+
return True
|
|
930
|
+
await asyncio.sleep(1)
|
|
931
|
+
return self._interrupt.is_interrupted
|
|
932
|
+
|
|
933
|
+
def inject(
|
|
934
|
+
self,
|
|
935
|
+
text: str,
|
|
936
|
+
created_at: datetime | str | None = None,
|
|
937
|
+
injection_id: str | None = None,
|
|
938
|
+
reminders: tuple[str, ...] = (),
|
|
939
|
+
preparation: PreparationTrace | None = None,
|
|
940
|
+
target_turn_id: str | None = None,
|
|
941
|
+
) -> None:
|
|
942
|
+
"""Queue text for injection before the next model call."""
|
|
943
|
+
self._injection.queue(
|
|
944
|
+
text,
|
|
945
|
+
created_at=created_at,
|
|
946
|
+
injection_id=injection_id,
|
|
947
|
+
reminders=reminders,
|
|
948
|
+
preparation=preparation,
|
|
949
|
+
target_turn_id=target_turn_id,
|
|
950
|
+
)
|
|
951
|
+
|
|
952
|
+
def cancel_injection(self, injection_id: str) -> QueuedInjection | None:
|
|
953
|
+
"""Remove a still-pending injection; returns it, or None when too late."""
|
|
954
|
+
return self._injection.cancel(injection_id)
|
|
955
|
+
|
|
956
|
+
def reset_counters(self, *, reset_batch_id: bool = True) -> None:
|
|
957
|
+
"""Reset per-run counters (called by engine before each run).
|
|
958
|
+
|
|
959
|
+
Args:
|
|
960
|
+
reset_batch_id: If True, reset batch_id to 0 (for fresh runs).
|
|
961
|
+
On resume, pass False to continue numbering from the
|
|
962
|
+
interrupted run so batch_ids don't collide.
|
|
963
|
+
"""
|
|
964
|
+
self.approval.reset()
|
|
965
|
+
self.tool_events.reset_invocation_order()
|
|
966
|
+
if reset_batch_id and self._intermediate_buffer is not None:
|
|
967
|
+
self._intermediate_buffer.batch_id = 0
|
|
968
|
+
|
|
969
|
+
def _get_history_messages(self) -> list:
|
|
970
|
+
"""Return the message list from the history provider state."""
|
|
971
|
+
history_state = self._session.state.get("chrys_history", {})
|
|
972
|
+
return history_state.get("messages", [])
|
|
973
|
+
|
|
974
|
+
# ── helpers ───────────────────────────────────────────────────────
|
|
975
|
+
|
|
976
|
+
@staticmethod
|
|
977
|
+
def _response_has_hosted_contents(result: AgentResponse[Any]) -> bool:
|
|
978
|
+
"""Return whether the accepted response contains provider-hosted content."""
|
|
979
|
+
return any(content.provider_hosted for message in result.messages for content in message.contents)
|
|
980
|
+
|
|
981
|
+
@staticmethod
|
|
982
|
+
def _extract_final_text(result: AgentResponse[Any]) -> str:
|
|
983
|
+
"""Extract only the *last* message's text from an AgentResponse.
|
|
984
|
+
|
|
985
|
+
``AgentResponse.text`` concatenates ALL messages from the tool loop.
|
|
986
|
+
This method returns only the final LLM output after all tool calls.
|
|
987
|
+
"""
|
|
988
|
+
if not result.messages:
|
|
989
|
+
return ""
|
|
990
|
+
last_msg = result.messages[-1]
|
|
991
|
+
return "".join(c.text for c in last_msg.contents if c.type == "text" and c.text)
|
|
992
|
+
|
|
993
|
+
def _assistant_message_event_kwargs(self) -> _AssistantMessageEventKwargs:
|
|
994
|
+
"""Return event kwargs matching the timestamp persisted for assistant output."""
|
|
995
|
+
parsed = parse_created_at(self._session.state.get(LAST_ASSISTANT_CREATED_AT_STATE_KEY))
|
|
996
|
+
return {"timestamp": parsed} if parsed is not None else {}
|
|
997
|
+
|
|
998
|
+
def _snapshot_retry_state(self) -> _TurnRetryState:
|
|
999
|
+
"""Capture the four caller-owned retry buffers synchronously."""
|
|
1000
|
+
return _TurnRetryState(
|
|
1001
|
+
loop_recorder=self._loop_recorder.snapshot() if self._loop_recorder is not None else None,
|
|
1002
|
+
tool_events=self.tool_events.snapshot_retry_state(),
|
|
1003
|
+
approval=self.approval.snapshot_retry_state(),
|
|
1004
|
+
compaction=self._compaction_strategy.snapshot_retry_state()
|
|
1005
|
+
if self._compaction_strategy is not None
|
|
1006
|
+
else None,
|
|
1007
|
+
)
|
|
1008
|
+
|
|
1009
|
+
def _restore_retry_state(self, retry_state: object) -> None:
|
|
1010
|
+
"""Restore caller buffers inline; a failure stops the retry transaction."""
|
|
1011
|
+
if not isinstance(retry_state, _TurnRetryState):
|
|
1012
|
+
raise TypeError("Turn retry snapshot has invalid caller state.")
|
|
1013
|
+
if self._loop_recorder is not None and retry_state.loop_recorder is not None:
|
|
1014
|
+
self._loop_recorder.restore(retry_state.loop_recorder)
|
|
1015
|
+
self.tool_events.restore_retry_state(retry_state.tool_events)
|
|
1016
|
+
self.approval.restore_retry_state(retry_state.approval)
|
|
1017
|
+
if self._compaction_strategy is not None and retry_state.compaction is not None:
|
|
1018
|
+
self._compaction_strategy.restore_retry_state(retry_state.compaction)
|
|
1019
|
+
|
|
1020
|
+
async def _reject_stream(self, reason: str) -> None:
|
|
1021
|
+
if self._hosted_bridge is not None:
|
|
1022
|
+
await self._hosted_bridge.attempt_rejected(reason)
|
|
1023
|
+
|
|
1024
|
+
def _make_stream_observer(self) -> _MainStreamObserver:
|
|
1025
|
+
return _MainStreamObserver(self)
|
|
1026
|
+
|
|
1027
|
+
|
|
1028
|
+
class _MainStreamObserver:
|
|
1029
|
+
"""The main shell's final-response text projection for one stream attempt.
|
|
1030
|
+
|
|
1031
|
+
``on_update`` accumulates text, discarding intermediate tool-response
|
|
1032
|
+
text that the wire client's ``result_hook`` publishes separately.
|
|
1033
|
+
Two signals discard the buffer: a non-informational ``function_call``
|
|
1034
|
+
in the update, or a change in ``IntermediateTextBuffer.batch_id`` between
|
|
1035
|
+
updates. The hook advances the batch at the end of a tool-calling response;
|
|
1036
|
+
text/tool order within the same update therefore does not matter.
|
|
1037
|
+
|
|
1038
|
+
``on_retry_boundary`` discards text from the rejected provider attempt.
|
|
1039
|
+
``before_finalize`` first publishes intermediate text no tool start or
|
|
1040
|
+
response start has released, then checks the batch once more after
|
|
1041
|
+
iteration as a safety net for a last tool response with no following
|
|
1042
|
+
update. It emits the final
|
|
1043
|
+
response's remaining text as one cumulative ``InvocationMessage(is_final=False)``
|
|
1044
|
+
snapshot before ``_publish_response_text`` emits ``is_final=True``. The text
|
|
1045
|
+
was buffered whole, so replaying it line by line only fakes streaming: every
|
|
1046
|
+
line would publish the whole text so far through the bus and every subscriber.
|
|
1047
|
+
When the hosted bridge owns the text, this observer discards its buffer and
|
|
1048
|
+
emits no text; interruption also suppresses emission.
|
|
1049
|
+
"""
|
|
1050
|
+
|
|
1051
|
+
def __init__(self, executor: TurnBindings) -> None:
|
|
1052
|
+
self._executor = executor
|
|
1053
|
+
self._emitter = executor._emitter
|
|
1054
|
+
self._buffer = ""
|
|
1055
|
+
self._bridge_owns_text = False
|
|
1056
|
+
self._ibuf = executor._intermediate_buffer
|
|
1057
|
+
self._last_batch_id = self._ibuf.batch_id if self._ibuf is not None else 0
|
|
1058
|
+
|
|
1059
|
+
async def on_retry_boundary(self) -> None:
|
|
1060
|
+
self._buffer = ""
|
|
1061
|
+
executor = self._executor
|
|
1062
|
+
if executor._hosted_bridge is not None and executor.inputs.pending_continuation_token is None:
|
|
1063
|
+
await executor._hosted_bridge.attempt_rejected("Provider response attempt retried")
|
|
1064
|
+
|
|
1065
|
+
def on_update(self, update: AgentResponseUpdate) -> None:
|
|
1066
|
+
if self._ibuf is not None and self._ibuf.batch_id != self._last_batch_id:
|
|
1067
|
+
self._buffer = ""
|
|
1068
|
+
self._last_batch_id = self._ibuf.batch_id
|
|
1069
|
+
text_chunk = ""
|
|
1070
|
+
has_function_call = False
|
|
1071
|
+
for content in update.contents or []:
|
|
1072
|
+
if content.type == "text" and content.text:
|
|
1073
|
+
text_chunk += content.text
|
|
1074
|
+
elif content.type == "function_call" and not content.informational_only:
|
|
1075
|
+
has_function_call = True
|
|
1076
|
+
if content.provider_hosted:
|
|
1077
|
+
self._bridge_owns_text = True
|
|
1078
|
+
self._buffer += text_chunk
|
|
1079
|
+
if has_function_call or self._bridge_owns_text:
|
|
1080
|
+
self._buffer = ""
|
|
1081
|
+
|
|
1082
|
+
async def before_finalize(self) -> None:
|
|
1083
|
+
if self._ibuf is not None and self._ibuf.batch_id != self._last_batch_id:
|
|
1084
|
+
self._buffer = ""
|
|
1085
|
+
executor = self._executor
|
|
1086
|
+
# Text streamed with a call that started no tool precedes the final
|
|
1087
|
+
# text. The next response's start releases it, and a run that ends
|
|
1088
|
+
# without one releases it here.
|
|
1089
|
+
await executor.tool_events.release_intermediate_text()
|
|
1090
|
+
if self._buffer and not self._bridge_owns_text and not executor._interrupt.is_interrupted:
|
|
1091
|
+
await self._emitter.publish(
|
|
1092
|
+
InvocationMessage(
|
|
1093
|
+
origin=self._emitter.origin, text=self._buffer, is_final=False, session_id=executor._session_id
|
|
1094
|
+
)
|
|
1095
|
+
)
|