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,994 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""Kernel pass attempts, task ownership and synchronous retry ports.
|
|
4
|
+
|
|
5
|
+
Local calls keep the kernel wire retry lane; stored calls use the existing
|
|
6
|
+
whole-run retry loop. Caller policy, presentation and pass lifecycle arrive
|
|
7
|
+
through explicit ports. No caller shell or disk writer is owned here.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import asyncio
|
|
13
|
+
import contextlib
|
|
14
|
+
import logging
|
|
15
|
+
import time
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
from enum import Enum
|
|
18
|
+
from typing import TYPE_CHECKING, Any, Protocol, TypedDict, TypeGuard, cast
|
|
19
|
+
|
|
20
|
+
from chrys.foundation.errors import clean_error_message, is_retryable
|
|
21
|
+
from chrys.foundation.retry import (
|
|
22
|
+
HistorySnapshot,
|
|
23
|
+
RetryAttemptInfo,
|
|
24
|
+
StreamRetryLoop,
|
|
25
|
+
StreamStall,
|
|
26
|
+
StreamStallExhausted,
|
|
27
|
+
restore_message_properties,
|
|
28
|
+
snapshot_message_properties,
|
|
29
|
+
)
|
|
30
|
+
from chrys.foundation.trajectory.context import TRAJECTORY_CONTEXT_KWARG, trajectory_scope
|
|
31
|
+
from chrys.foundation.trajectory.envelope import Link, MeasurementSource, measurement
|
|
32
|
+
from chrys.foundation.trajectory.event_types import EventType as TrajectoryEventType
|
|
33
|
+
from chrys.foundation.trajectory.event_types import ModelRunEndReason, RetryMode, RetryReason
|
|
34
|
+
from chrys.foundation.trajectory.ids import new_analytics_id
|
|
35
|
+
from chrys.kernel import is_retry_boundary_update, resolve_storage_mode_and_handles, wire_progress_scope
|
|
36
|
+
from chrys.service.agent_middleware.response_validation import (
|
|
37
|
+
RetryableResponseValidationError,
|
|
38
|
+
hosted_commits_from_error,
|
|
39
|
+
)
|
|
40
|
+
from chrys.service.context.providers.history import PRE_OUTPUT_HISTORY_LEN_STATE_KEY
|
|
41
|
+
|
|
42
|
+
if TYPE_CHECKING:
|
|
43
|
+
from collections.abc import AsyncIterator, Awaitable, Callable, Coroutine, Mapping, Sequence
|
|
44
|
+
|
|
45
|
+
from chrys.foundation.trajectory.context import TrajectoryContext
|
|
46
|
+
from chrys.kernel import Agent, AgentResponse, AgentResponseUpdate, AgentSession, Message, ResponseStream
|
|
47
|
+
from chrys.kernel.loop import StallExhaustedAction
|
|
48
|
+
from chrys.kernel.middleware import ChatMiddleware, FunctionMiddleware
|
|
49
|
+
|
|
50
|
+
logger = logging.getLogger(__name__)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class AgentRunKwargs(TypedDict, total=False):
|
|
54
|
+
"""Keyword arguments forwarded unchanged to ``Agent.run``."""
|
|
55
|
+
|
|
56
|
+
session: AgentSession
|
|
57
|
+
middleware: Sequence[ChatMiddleware | FunctionMiddleware]
|
|
58
|
+
options: Mapping[str, Any]
|
|
59
|
+
compaction_strategy: Any
|
|
60
|
+
tokenizer: Any
|
|
61
|
+
client_kwargs: Mapping[str, Any]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def is_string_keyed_dict(value: object) -> TypeGuard[dict[str, Any]]:
|
|
65
|
+
"""Narrow internal kwargs dictionaries whose producers use string keys."""
|
|
66
|
+
return isinstance(value, dict)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _validation_retry_exemption(exc: BaseException) -> RetryAttemptInfo | None:
|
|
70
|
+
"""Expose the middleware-owned stored validation budget to the retry loop.
|
|
71
|
+
|
|
72
|
+
The exemption is bounded because response validation terminally gives up
|
|
73
|
+
when its carried retry budget or identical-reason guard is reached.
|
|
74
|
+
"""
|
|
75
|
+
if not isinstance(exc, RetryableResponseValidationError):
|
|
76
|
+
return None
|
|
77
|
+
exemption = exc.exemption
|
|
78
|
+
return RetryAttemptInfo(
|
|
79
|
+
reason=str(exc),
|
|
80
|
+
attempt=exemption.attempt,
|
|
81
|
+
max_attempts=exemption.max_attempts,
|
|
82
|
+
delay_seconds=exemption.delay_seconds,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def has_live_continuation_token(run_kwargs: Mapping[str, object] | None) -> bool:
|
|
87
|
+
"""True when the retry-owned options still reference a live background response."""
|
|
88
|
+
if run_kwargs is None:
|
|
89
|
+
return False
|
|
90
|
+
options = run_kwargs.get("options")
|
|
91
|
+
return is_string_keyed_dict(options) and options.get("continuation_token") is not None
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def drop_continuation_token(run_kwargs: AgentRunKwargs) -> None:
|
|
95
|
+
"""Forget the retry-owned options' background response, so the next request starts a new one."""
|
|
96
|
+
options = run_kwargs.get("options")
|
|
97
|
+
if is_string_keyed_dict(options):
|
|
98
|
+
options.pop("continuation_token", None)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def continuation_token_observer_for(run_kwargs: AgentRunKwargs) -> Callable[[Any], None]:
|
|
102
|
+
"""Mirror the kernel's live continuation token into the retry-owned options.
|
|
103
|
+
|
|
104
|
+
A transient failure while polling a stored background response must
|
|
105
|
+
resume that response on the next whole-run attempt — the token has to
|
|
106
|
+
live in the options the retry loop re-issues, not only in the kernel's
|
|
107
|
+
attempt-local copy. Writes go through ``run_kwargs`` at call time so a
|
|
108
|
+
handle-stripping restore that replaces the options dict cannot orphan
|
|
109
|
+
the observer.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
def _observe(token: Any) -> None:
|
|
113
|
+
if token is None:
|
|
114
|
+
drop_continuation_token(run_kwargs)
|
|
115
|
+
return
|
|
116
|
+
raw_options = run_kwargs.get("options")
|
|
117
|
+
options = raw_options if is_string_keyed_dict(raw_options) else None
|
|
118
|
+
if options is None:
|
|
119
|
+
options = {}
|
|
120
|
+
run_kwargs["options"] = options
|
|
121
|
+
options["continuation_token"] = token
|
|
122
|
+
|
|
123
|
+
return _observe
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _run_retry_reason(exc: BaseException) -> str:
|
|
127
|
+
"""Closed reason code for a whole-run retry decision."""
|
|
128
|
+
if isinstance(exc, StreamStall | StreamStallExhausted):
|
|
129
|
+
return RetryReason.STREAM_STALL
|
|
130
|
+
if isinstance(exc, RetryableResponseValidationError):
|
|
131
|
+
return RetryReason.VALIDATION_REJECTED
|
|
132
|
+
return RetryReason.TRANSIENT_ERROR
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class WireRetryPolicyAdapter:
|
|
136
|
+
"""Adapt caller configuration and callbacks to the kernel WireRetryPolicy.
|
|
137
|
+
|
|
138
|
+
Budgets and backoff accept values (the TurnBindings's per-run snapshot) or
|
|
139
|
+
readers (the child controller's live configuration). No budget or stall
|
|
140
|
+
action is defaulted across those policies. An absent pre-retry callback
|
|
141
|
+
means no injection transaction; an absent hosted probe remains None.
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
def __init__(
|
|
145
|
+
self,
|
|
146
|
+
*,
|
|
147
|
+
max_retries: int | Callable[[], int],
|
|
148
|
+
stall_timeout_seconds: float | None,
|
|
149
|
+
stall_max_retries: int | Callable[[], int],
|
|
150
|
+
stall_exhausted_action: StallExhaustedAction,
|
|
151
|
+
backoff_schedule: tuple[int, ...] | Callable[[], tuple[int, ...]],
|
|
152
|
+
interrupted: Callable[[], bool],
|
|
153
|
+
interruptible_sleep: Callable[[int], Awaitable[bool]],
|
|
154
|
+
publish_retry: Callable[[str, int, int, int, BaseException], Awaitable[None]],
|
|
155
|
+
prepare_retry: Callable[[], None] | None = None,
|
|
156
|
+
hosted_commits_in_flight: Callable[[], tuple[str, ...]] | None = None,
|
|
157
|
+
) -> None:
|
|
158
|
+
self._max_retries = max_retries
|
|
159
|
+
self.stall_timeout_seconds = stall_timeout_seconds
|
|
160
|
+
self._stall_max_retries = stall_max_retries
|
|
161
|
+
self.stall_exhausted_action = stall_exhausted_action
|
|
162
|
+
self.backoff_schedule = backoff_schedule
|
|
163
|
+
self.interrupted = interrupted
|
|
164
|
+
self.interruptible_sleep = interruptible_sleep
|
|
165
|
+
self.publish_retry = publish_retry
|
|
166
|
+
self.prepare_retry = prepare_retry
|
|
167
|
+
# Describes only the current wire attempt, never the pass total.
|
|
168
|
+
self.hosted_commits_in_flight = hosted_commits_in_flight
|
|
169
|
+
|
|
170
|
+
@property
|
|
171
|
+
def max_retries(self) -> int:
|
|
172
|
+
return self._max_retries if isinstance(self._max_retries, int) else self._max_retries()
|
|
173
|
+
|
|
174
|
+
@max_retries.setter
|
|
175
|
+
def max_retries(self, value: int) -> None:
|
|
176
|
+
self._max_retries = value
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def stall_max_retries(self) -> int:
|
|
180
|
+
return self._stall_max_retries if isinstance(self._stall_max_retries, int) else self._stall_max_retries()
|
|
181
|
+
|
|
182
|
+
@stall_max_retries.setter
|
|
183
|
+
def stall_max_retries(self, value: int) -> None:
|
|
184
|
+
self._stall_max_retries = value
|
|
185
|
+
|
|
186
|
+
def backoff_seconds(self, attempt: int) -> int:
|
|
187
|
+
backoff = self.backoff_schedule if isinstance(self.backoff_schedule, tuple) else self.backoff_schedule()
|
|
188
|
+
if not backoff:
|
|
189
|
+
return 0
|
|
190
|
+
return backoff[min(attempt, len(backoff) - 1)]
|
|
191
|
+
|
|
192
|
+
def is_retryable(self, exc: BaseException) -> bool:
|
|
193
|
+
return is_retryable(exc)
|
|
194
|
+
|
|
195
|
+
def is_interrupted(self) -> bool:
|
|
196
|
+
return self.interrupted()
|
|
197
|
+
|
|
198
|
+
async def sleep(self, seconds: int) -> bool:
|
|
199
|
+
return await self.interruptible_sleep(seconds)
|
|
200
|
+
|
|
201
|
+
async def on_retry(
|
|
202
|
+
self,
|
|
203
|
+
message: str,
|
|
204
|
+
attempt: int,
|
|
205
|
+
max_attempts: int,
|
|
206
|
+
delay_seconds: int,
|
|
207
|
+
exc: BaseException,
|
|
208
|
+
) -> None:
|
|
209
|
+
await self.publish_retry(message, attempt, max_attempts, delay_seconds, exc)
|
|
210
|
+
|
|
211
|
+
def before_retry(self) -> None:
|
|
212
|
+
if self.prepare_retry is not None:
|
|
213
|
+
self.prepare_retry()
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class RetryParticipant(Protocol):
|
|
217
|
+
"""Caller transaction participating in a whole pass and retry rollback."""
|
|
218
|
+
|
|
219
|
+
def begin_retry(self) -> None: ...
|
|
220
|
+
def end_retry(self) -> None: ...
|
|
221
|
+
def restore_for_retry(self) -> None: ...
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class InterruptProbe(Protocol):
|
|
225
|
+
"""Read the caller's synchronous interrupt latch."""
|
|
226
|
+
|
|
227
|
+
@property
|
|
228
|
+
def is_interrupted(self) -> bool: ...
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class StreamObserver(Protocol):
|
|
232
|
+
"""Observe raw updates and publish caller projection before finalize."""
|
|
233
|
+
|
|
234
|
+
def on_update(self, update: AgentResponseUpdate) -> None: ...
|
|
235
|
+
async def on_retry_boundary(self) -> None: ...
|
|
236
|
+
async def before_finalize(self) -> None: ...
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
@dataclass(frozen=True, slots=True)
|
|
240
|
+
class RestoreAndFallback:
|
|
241
|
+
"""Restore the exhausted stream, reject its presentation, then block once."""
|
|
242
|
+
|
|
243
|
+
reject_stream: Callable[[str], Awaitable[None]]
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
@dataclass(frozen=True, slots=True)
|
|
247
|
+
class KeepAndRaise:
|
|
248
|
+
"""Keep the exhausted attempt for the shell's pause projection."""
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
type StallExhaustionPolicy = RestoreAndFallback | KeepAndRaise
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
class RetryBoundaryPolicy(Enum):
|
|
255
|
+
"""Keep the caller's scheduling observation at an empty retry boundary."""
|
|
256
|
+
|
|
257
|
+
SKIP = "skip"
|
|
258
|
+
OBSERVE = "observe"
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class BlockingCallTiming(Enum):
|
|
262
|
+
"""Task context in which the blocking run awaitable is constructed."""
|
|
263
|
+
|
|
264
|
+
IN_ATTEMPT_TASK = "in_attempt_task"
|
|
265
|
+
BEFORE_ATTEMPT_TASK = "before_attempt_task"
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
@dataclass(frozen=True, slots=True)
|
|
269
|
+
class AttemptRecipe:
|
|
270
|
+
"""Conversation-bound policy choices; no shared controller callbacks.
|
|
271
|
+
|
|
272
|
+
history_state is consumed by the shell to construct HistoryRollback;
|
|
273
|
+
AttemptRunner does not read it.
|
|
274
|
+
"""
|
|
275
|
+
|
|
276
|
+
stall_exhaustion: StallExhaustionPolicy
|
|
277
|
+
stall_error: Callable[[float], StreamStall]
|
|
278
|
+
retry_boundary: RetryBoundaryPolicy
|
|
279
|
+
blocking_call_timing: BlockingCallTiming
|
|
280
|
+
history_state: Callable[[], dict[str, Any]]
|
|
281
|
+
before_attempt: Callable[[], None] | None
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
@dataclass(frozen=True, slots=True)
|
|
285
|
+
class WireRecipe:
|
|
286
|
+
"""The main pass snapshot or child live-reader wire-policy recipe."""
|
|
287
|
+
|
|
288
|
+
max_retries: int | Callable[[], int]
|
|
289
|
+
stall_timeout_seconds: float
|
|
290
|
+
stall_max_retries: int | Callable[[], int]
|
|
291
|
+
stall_exhausted_action: StallExhaustedAction
|
|
292
|
+
backoff_schedule: tuple[int, ...] | Callable[[], tuple[int, ...]]
|
|
293
|
+
interrupted: Callable[[], bool]
|
|
294
|
+
interruptible_sleep: Callable[[int], Awaitable[bool]]
|
|
295
|
+
publish_retry: Callable[[str, int, int, int, BaseException], Awaitable[None]]
|
|
296
|
+
prepare_retry: Callable[[], None] | None
|
|
297
|
+
hosted_commits_in_flight: Callable[[], tuple[str, ...]] | None
|
|
298
|
+
|
|
299
|
+
def build(self) -> WireRetryPolicyAdapter:
|
|
300
|
+
return WireRetryPolicyAdapter(
|
|
301
|
+
max_retries=self.max_retries,
|
|
302
|
+
stall_timeout_seconds=self.stall_timeout_seconds,
|
|
303
|
+
stall_max_retries=self.stall_max_retries,
|
|
304
|
+
stall_exhausted_action=self.stall_exhausted_action,
|
|
305
|
+
backoff_schedule=self.backoff_schedule,
|
|
306
|
+
interrupted=self.interrupted,
|
|
307
|
+
interruptible_sleep=self.interruptible_sleep,
|
|
308
|
+
publish_retry=self.publish_retry,
|
|
309
|
+
prepare_retry=self.prepare_retry,
|
|
310
|
+
hosted_commits_in_flight=self.hosted_commits_in_flight,
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
@dataclass(slots=True)
|
|
315
|
+
class AttemptTaskHandle:
|
|
316
|
+
"""Sole L0 attempt task reference; Turn flags belong to the Turn shell."""
|
|
317
|
+
|
|
318
|
+
task: asyncio.Task[AgentResponse[Any]] | None = None
|
|
319
|
+
|
|
320
|
+
@property
|
|
321
|
+
def active(self) -> bool:
|
|
322
|
+
return self.task is not None and not self.task.done()
|
|
323
|
+
|
|
324
|
+
def cancel(self) -> None:
|
|
325
|
+
if self.task is not None and not self.task.done():
|
|
326
|
+
self.task.cancel()
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
class HistoryRollback:
|
|
330
|
+
"""Synchronous history rollback plus an opaque caller-state participant.
|
|
331
|
+
|
|
332
|
+
Snapshot/restore callbacks run inline, without exception interception or
|
|
333
|
+
await gaps. In particular a failed caller restore stops before after_restore,
|
|
334
|
+
retry notification, sleep or another model attempt.
|
|
335
|
+
"""
|
|
336
|
+
|
|
337
|
+
def __init__(
|
|
338
|
+
self,
|
|
339
|
+
session: AgentSession,
|
|
340
|
+
*,
|
|
341
|
+
snapshot_caller: Callable[[], object],
|
|
342
|
+
restore_caller: Callable[[object], None],
|
|
343
|
+
history_state: Callable[[], dict[str, Any]],
|
|
344
|
+
) -> None:
|
|
345
|
+
self._session = session
|
|
346
|
+
self._snapshot_caller = snapshot_caller
|
|
347
|
+
self._restore_caller = restore_caller
|
|
348
|
+
self._history_state = history_state
|
|
349
|
+
|
|
350
|
+
def snapshot(self) -> HistorySnapshot:
|
|
351
|
+
"""Snapshot messages and compressed block count for rollback.
|
|
352
|
+
|
|
353
|
+
Used before an ``agent.run()`` that may need retry. Captures
|
|
354
|
+
``compressed_msgs`` length alongside messages so ``restore``
|
|
355
|
+
can undo cross-turn compressions (``_compress_state``) that run
|
|
356
|
+
mid-loop, and each message's ``additional_properties`` so in-place
|
|
357
|
+
annotations from the rolled-back attempt (exclusion flags,
|
|
358
|
+
summarized-by markers) can be reverted exactly.
|
|
359
|
+
"""
|
|
360
|
+
history_state = self._history_state()
|
|
361
|
+
messages = list(history_state.get("messages", []))
|
|
362
|
+
return HistorySnapshot(
|
|
363
|
+
messages=messages,
|
|
364
|
+
compressed_count=len(history_state.get("compressed_msgs", [])),
|
|
365
|
+
service_session_id=self._session.service_session_id or "",
|
|
366
|
+
message_properties=snapshot_message_properties(messages),
|
|
367
|
+
pre_output_history_len=history_state.get(PRE_OUTPUT_HISTORY_LEN_STATE_KEY),
|
|
368
|
+
caller_state=self._snapshot_caller(),
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
def restore(self, snapshot: HistorySnapshot) -> None:
|
|
372
|
+
"""Restore history from a snapshot, including compressed block rollback.
|
|
373
|
+
|
|
374
|
+
Undoes ``_compress_state`` list replacements, removes orphaned
|
|
375
|
+
``CompressedBlock`` entries added during the rolled-back run, and
|
|
376
|
+
restores each message's ``additional_properties`` exactly as captured
|
|
377
|
+
— clearing the rolled-back attempt's compaction marks while keeping
|
|
378
|
+
marks that legitimately predate the attempt.
|
|
379
|
+
|
|
380
|
+
Used for retry rollback.
|
|
381
|
+
"""
|
|
382
|
+
history_state = self._history_state()
|
|
383
|
+
restored_messages = list(snapshot.messages)
|
|
384
|
+
history_state["messages"] = restored_messages
|
|
385
|
+
self._session.service_session_id = snapshot.service_session_id or None
|
|
386
|
+
|
|
387
|
+
# Undo compressed block additions from the rolled-back run.
|
|
388
|
+
compressed: list = history_state.get("compressed_msgs", [])
|
|
389
|
+
if len(compressed) > snapshot.compressed_count:
|
|
390
|
+
del compressed[snapshot.compressed_count :]
|
|
391
|
+
|
|
392
|
+
if snapshot.pre_output_history_len is None:
|
|
393
|
+
history_state.pop(PRE_OUTPUT_HISTORY_LEN_STATE_KEY, None)
|
|
394
|
+
else:
|
|
395
|
+
history_state[PRE_OUTPUT_HISTORY_LEN_STATE_KEY] = snapshot.pre_output_history_len
|
|
396
|
+
restore_message_properties(restored_messages, snapshot.message_properties)
|
|
397
|
+
self._restore_caller(snapshot.caller_state)
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
class ModelRunTrace:
|
|
401
|
+
"""Attempt trace observer over the foundation sink port, never a writer owner.
|
|
402
|
+
|
|
403
|
+
The caller supplies first-run causal links. The terminal is queued with
|
|
404
|
+
emit_soon and deliberately never awaits the sink acknowledgement.
|
|
405
|
+
"""
|
|
406
|
+
|
|
407
|
+
def __init__(
|
|
408
|
+
self, *, interrupted: Callable[[], bool], service_side: Callable[[], bool], committed: Callable[[], bool]
|
|
409
|
+
) -> None:
|
|
410
|
+
self.context: TrajectoryContext | None = None
|
|
411
|
+
self.first_run_links: tuple[Link, ...] = ()
|
|
412
|
+
self._interrupted = interrupted
|
|
413
|
+
self._service_side = service_side
|
|
414
|
+
self._committed = committed
|
|
415
|
+
self.reset()
|
|
416
|
+
|
|
417
|
+
def reset(self) -> None:
|
|
418
|
+
self._trajectory_last_run_id = None
|
|
419
|
+
self._run_attempts = 0
|
|
420
|
+
self._trajectory_retry_run_id = None
|
|
421
|
+
|
|
422
|
+
@contextlib.asynccontextmanager
|
|
423
|
+
async def run(self, run_kwargs: AgentRunKwargs, *, stream: bool) -> AsyncIterator[None]:
|
|
424
|
+
"""Bind one ``model.run`` operation around one ``agent.run`` attempt.
|
|
425
|
+
|
|
426
|
+
The narrowed context rides two ways: ambiently (for tool middleware
|
|
427
|
+
and side calls, bound before the attempt task is created so the
|
|
428
|
+
task inherits it) and explicitly in ``client_kwargs`` for the
|
|
429
|
+
kernel loop, whose lazy provider streams resolve outside this
|
|
430
|
+
context.
|
|
431
|
+
"""
|
|
432
|
+
base = self.context
|
|
433
|
+
if base is None:
|
|
434
|
+
yield
|
|
435
|
+
return
|
|
436
|
+
retry_run_id = self._trajectory_retry_run_id
|
|
437
|
+
run_id = retry_run_id or new_analytics_id()
|
|
438
|
+
context = base.with_run(run_id)
|
|
439
|
+
raw_client_kwargs = run_kwargs.get("client_kwargs")
|
|
440
|
+
run_kwargs["client_kwargs"] = {
|
|
441
|
+
**(raw_client_kwargs if raw_client_kwargs is not None else {}),
|
|
442
|
+
TRAJECTORY_CONTEXT_KWARG: context,
|
|
443
|
+
}
|
|
444
|
+
attempt_index = self._run_attempts
|
|
445
|
+
self._run_attempts += 1
|
|
446
|
+
previous_run_id = self._trajectory_last_run_id
|
|
447
|
+
started_ns = time.monotonic_ns()
|
|
448
|
+
sink = context.sink
|
|
449
|
+
|
|
450
|
+
def _finished_draft(outcome: str) -> Any:
|
|
451
|
+
return context.draft(
|
|
452
|
+
TrajectoryEventType.MODEL_RUN_FINISHED,
|
|
453
|
+
operation_id=run_id,
|
|
454
|
+
parent_operation_id=base.innermost_model_operation_id,
|
|
455
|
+
payload={
|
|
456
|
+
"outcome": outcome,
|
|
457
|
+
"duration_ms": max(0, (time.monotonic_ns() - started_ns) // 1_000_000),
|
|
458
|
+
},
|
|
459
|
+
measurements={"/payload/duration_ms": measurement(MeasurementSource.MONOTONIC_CLOCK, method_version=1)},
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
self._trajectory_last_run_id = run_id
|
|
463
|
+
links = self.first_run_links if attempt_index == 0 else ()
|
|
464
|
+
|
|
465
|
+
def _started_draft() -> Any:
|
|
466
|
+
return context.draft(
|
|
467
|
+
TrajectoryEventType.MODEL_RUN_STARTED,
|
|
468
|
+
operation_id=run_id,
|
|
469
|
+
parent_operation_id=base.innermost_model_operation_id,
|
|
470
|
+
payload={
|
|
471
|
+
"attempt_index": attempt_index,
|
|
472
|
+
"stream": stream,
|
|
473
|
+
"service_side_storage": self._service_side(),
|
|
474
|
+
"previous_run_operation_id": previous_run_id,
|
|
475
|
+
},
|
|
476
|
+
links=links,
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
if retry_run_id is not None:
|
|
480
|
+
self._trajectory_retry_run_id = None
|
|
481
|
+
try:
|
|
482
|
+
await sink.emit(
|
|
483
|
+
context.draft(
|
|
484
|
+
TrajectoryEventType.RETRY_STARTED,
|
|
485
|
+
operation_id=run_id,
|
|
486
|
+
payload={
|
|
487
|
+
"retry_mode": RetryMode.RUN,
|
|
488
|
+
"next_operation_id": run_id,
|
|
489
|
+
"previous_operation_id": previous_run_id,
|
|
490
|
+
},
|
|
491
|
+
)
|
|
492
|
+
)
|
|
493
|
+
except asyncio.CancelledError:
|
|
494
|
+
# retry.started already names this run. Entry never reaches
|
|
495
|
+
# the body, so queue its complete interrupted lifecycle before
|
|
496
|
+
# preserving cancellation, without waiting on another ack.
|
|
497
|
+
try:
|
|
498
|
+
sink.emit_soon(_started_draft())
|
|
499
|
+
sink.emit_soon(_finished_draft(ModelRunEndReason.INTERRUPTED))
|
|
500
|
+
except Exception:
|
|
501
|
+
logger.debug("Trajectory interrupted retry settlement failed", exc_info=True)
|
|
502
|
+
raise
|
|
503
|
+
except Exception:
|
|
504
|
+
logger.debug("Trajectory retry.started emit failed", exc_info=True)
|
|
505
|
+
|
|
506
|
+
try:
|
|
507
|
+
await sink.emit(_started_draft())
|
|
508
|
+
except asyncio.CancelledError:
|
|
509
|
+
# The writer commits the opening line before its shielded ack can
|
|
510
|
+
# be cancelled. Context-manager entry will not reach the body
|
|
511
|
+
# below, so settle the operation here before preserving cancel.
|
|
512
|
+
try:
|
|
513
|
+
sink.emit_soon(_finished_draft(ModelRunEndReason.INTERRUPTED))
|
|
514
|
+
except Exception:
|
|
515
|
+
logger.debug("Trajectory model.run.finished emit failed", exc_info=True)
|
|
516
|
+
raise
|
|
517
|
+
except Exception:
|
|
518
|
+
logger.debug("Trajectory model.run.started emit failed", exc_info=True)
|
|
519
|
+
outcome = ModelRunEndReason.COMPLETED
|
|
520
|
+
with trajectory_scope(context):
|
|
521
|
+
try:
|
|
522
|
+
yield
|
|
523
|
+
except asyncio.CancelledError:
|
|
524
|
+
outcome = ModelRunEndReason.INTERRUPTED
|
|
525
|
+
raise
|
|
526
|
+
except BaseException:
|
|
527
|
+
outcome = ModelRunEndReason.INTERRUPTED if self._interrupted() else ModelRunEndReason.FAILED
|
|
528
|
+
raise
|
|
529
|
+
finally:
|
|
530
|
+
draft = _finished_draft(outcome)
|
|
531
|
+
try:
|
|
532
|
+
# Queued, never awaited. The model has already answered by
|
|
533
|
+
# the time this runs and its task is gone, so an ack that
|
|
534
|
+
# waits out a slow writer holds the answer unpublished for
|
|
535
|
+
# that long — and a Stop pressed in that window finds
|
|
536
|
+
# nothing to cancel, marks the turn interrupted, and the
|
|
537
|
+
# finished answer is dropped. Cancellation cannot await
|
|
538
|
+
# here either: the scope is already unwinding. The line
|
|
539
|
+
# takes its sequence here, so this span is only left open
|
|
540
|
+
# when the process does not outlive the queue — and the
|
|
541
|
+
# turn still gets its terminal from the recorder's close.
|
|
542
|
+
sink.emit_soon(draft)
|
|
543
|
+
except Exception:
|
|
544
|
+
logger.debug("Trajectory model.run.finished emit failed", exc_info=True)
|
|
545
|
+
|
|
546
|
+
async def retry_scheduled(
|
|
547
|
+
self, *, exc: BaseException, delay_seconds: int, fallback_to_blocking: bool = False
|
|
548
|
+
) -> None:
|
|
549
|
+
"""Record a service-side whole-run retry decision (``retry.scheduled``)."""
|
|
550
|
+
context = self.context
|
|
551
|
+
if context is None:
|
|
552
|
+
return
|
|
553
|
+
retry_run_id = new_analytics_id()
|
|
554
|
+
self._trajectory_retry_run_id = retry_run_id
|
|
555
|
+
committed = self._committed()
|
|
556
|
+
try:
|
|
557
|
+
await context.sink.emit(
|
|
558
|
+
context.draft(
|
|
559
|
+
TrajectoryEventType.RETRY_SCHEDULED,
|
|
560
|
+
operation_id=retry_run_id,
|
|
561
|
+
payload={
|
|
562
|
+
"reason_code": _run_retry_reason(exc),
|
|
563
|
+
"delay_ms": max(0, int(delay_seconds * 1000)),
|
|
564
|
+
"retry_mode": RetryMode.RUN,
|
|
565
|
+
"previous_operation_id": self._trajectory_last_run_id,
|
|
566
|
+
"committed_work_present": committed,
|
|
567
|
+
"fallback_to_blocking": fallback_to_blocking,
|
|
568
|
+
},
|
|
569
|
+
)
|
|
570
|
+
)
|
|
571
|
+
except Exception:
|
|
572
|
+
logger.debug("Trajectory retry.scheduled emit failed", exc_info=True)
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
class AttemptRunner:
|
|
576
|
+
"""Drive one kernel pass through caller-supplied synchronous/async ports.
|
|
577
|
+
|
|
578
|
+
Construction does not run hooks or mutate history. The shell enters this
|
|
579
|
+
runner only inside its pass try, after its once-per-pass hooks and barriers.
|
|
580
|
+
"""
|
|
581
|
+
|
|
582
|
+
def __init__(
|
|
583
|
+
self,
|
|
584
|
+
*,
|
|
585
|
+
agent: Agent,
|
|
586
|
+
session: AgentSession,
|
|
587
|
+
handle: AttemptTaskHandle,
|
|
588
|
+
rollback: HistoryRollback,
|
|
589
|
+
retry_participant: RetryParticipant | None,
|
|
590
|
+
interrupt: InterruptProbe,
|
|
591
|
+
trace: ModelRunTrace,
|
|
592
|
+
stream_observer: Callable[[], StreamObserver] | None,
|
|
593
|
+
publish_retry: Callable[[str, int, int, int, BaseException], Awaitable[None]],
|
|
594
|
+
interruptible_sleep: Callable[[int], Awaitable[bool]],
|
|
595
|
+
max_retries: Callable[[], int],
|
|
596
|
+
backoff_schedule: Callable[[], tuple[int, ...]],
|
|
597
|
+
stream_timeout: Callable[[], float],
|
|
598
|
+
committed_count: Callable[[], int],
|
|
599
|
+
hosted_commits: Callable[[], tuple[str, ...]],
|
|
600
|
+
recipe: AttemptRecipe,
|
|
601
|
+
) -> None:
|
|
602
|
+
self._agent = agent
|
|
603
|
+
self._session = session
|
|
604
|
+
self.handle = handle
|
|
605
|
+
self._rollback = rollback
|
|
606
|
+
self._retry_participant = retry_participant
|
|
607
|
+
self._interrupt = interrupt
|
|
608
|
+
self.trace = trace
|
|
609
|
+
self._stream_observer = stream_observer
|
|
610
|
+
self._publish_retry_notice = publish_retry
|
|
611
|
+
self._interruptible_sleep = interruptible_sleep
|
|
612
|
+
self._effective_max_retries = max_retries
|
|
613
|
+
self._backoff_schedule = backoff_schedule
|
|
614
|
+
self._stream_timeout = stream_timeout
|
|
615
|
+
self._committed_count = committed_count
|
|
616
|
+
self._hosted_commits_probe = hosted_commits
|
|
617
|
+
self._stall_exhaustion = recipe.stall_exhaustion
|
|
618
|
+
self._stall_error = recipe.stall_error
|
|
619
|
+
self._retry_boundary = recipe.retry_boundary
|
|
620
|
+
self._blocking_call_timing = recipe.blocking_call_timing
|
|
621
|
+
self._before_attempt = recipe.before_attempt
|
|
622
|
+
|
|
623
|
+
async def run(
|
|
624
|
+
self, message: list[Message], run_kwargs: AgentRunKwargs, *, stream: bool, service_side: bool
|
|
625
|
+
) -> AgentResponse[Any]:
|
|
626
|
+
if stream:
|
|
627
|
+
if service_side:
|
|
628
|
+
return await self._stream_with_retry(message, run_kwargs)
|
|
629
|
+
if self._retry_participant is not None:
|
|
630
|
+
self._retry_participant.begin_retry()
|
|
631
|
+
try:
|
|
632
|
+
return await self._stream_single_attempt(message, run_kwargs, watchdog=False)
|
|
633
|
+
finally:
|
|
634
|
+
if self._retry_participant is not None:
|
|
635
|
+
self._retry_participant.end_retry()
|
|
636
|
+
return await self._run_blocking(message, run_kwargs, service_side=service_side)
|
|
637
|
+
|
|
638
|
+
def _may_retry_attempt(self, exc: BaseException, run_kwargs: Mapping[str, object] | None = None) -> bool:
|
|
639
|
+
"""Whole-run retry gate: answered tool work must never re-execute.
|
|
640
|
+
|
|
641
|
+
Locally answered results are counted by the loop recorder. Provider-
|
|
642
|
+
hosted calls (hosted MCP, hosted shell) execute inside the failed
|
|
643
|
+
exchange itself and never reach the recorder — their evidence rides
|
|
644
|
+
on the raised validation error (the middleware swallows the invalid
|
|
645
|
+
response before the kernel loop sees it) or, for stalls and transport
|
|
646
|
+
drops mid-stream, on the middleware's run-scoped observation probe.
|
|
647
|
+
A live continuation token exempts the gate: the retry then resumes
|
|
648
|
+
the already-created background response instead of re-creating the
|
|
649
|
+
request, so nothing hosted runs twice.
|
|
650
|
+
"""
|
|
651
|
+
if self._committed_count():
|
|
652
|
+
return False
|
|
653
|
+
hosted = hosted_commits_from_error(exc)
|
|
654
|
+
if not hosted:
|
|
655
|
+
hosted = self._hosted_commits_probe()
|
|
656
|
+
if hosted and not has_live_continuation_token(run_kwargs):
|
|
657
|
+
logger.warning(
|
|
658
|
+
"Not retrying failed attempt: provider-hosted tool call(s) already executed (%s)",
|
|
659
|
+
", ".join(hosted),
|
|
660
|
+
)
|
|
661
|
+
return False
|
|
662
|
+
return True
|
|
663
|
+
|
|
664
|
+
def _restore_service_retry_inputs(self, run_kwargs: AgentRunKwargs) -> None:
|
|
665
|
+
"""Replay failed-call injections and discard every stale service handle."""
|
|
666
|
+
raw_options = run_kwargs.get("options")
|
|
667
|
+
options = raw_options if is_string_keyed_dict(raw_options) else {}
|
|
668
|
+
if options.get("continuation_token") is None and self._retry_participant is not None:
|
|
669
|
+
# A live token means the retry resumes the already-created
|
|
670
|
+
# background response: the create that consumed the injections
|
|
671
|
+
# succeeded, so the consumed transaction (and its retained-message
|
|
672
|
+
# mirror for the terminal weave) must stay intact. Replaying here
|
|
673
|
+
# would strand the batch — polls skip consumption, then the
|
|
674
|
+
# terminal commit would destroy the held replay.
|
|
675
|
+
self._retry_participant.restore_for_retry()
|
|
676
|
+
self._session.service_session_id = None
|
|
677
|
+
raw_client_kwargs = run_kwargs.get("client_kwargs")
|
|
678
|
+
client_kwargs = raw_client_kwargs if is_string_keyed_dict(raw_client_kwargs) else {}
|
|
679
|
+
try:
|
|
680
|
+
force_stateless = bool(self._agent.client.FORCES_STATELESS)
|
|
681
|
+
except AttributeError:
|
|
682
|
+
force_stateless = False
|
|
683
|
+
resolution = resolve_storage_mode_and_handles(
|
|
684
|
+
options,
|
|
685
|
+
stores_by_default=True,
|
|
686
|
+
client_kwargs=client_kwargs,
|
|
687
|
+
force_stateless=force_stateless,
|
|
688
|
+
)
|
|
689
|
+
if raw_options is not None:
|
|
690
|
+
run_kwargs["options"] = resolution.options_without_handles
|
|
691
|
+
run_kwargs["client_kwargs"] = resolution.client_kwargs_without_handles
|
|
692
|
+
|
|
693
|
+
async def _run_agent(self, input: list[Message], run_kwargs: AgentRunKwargs) -> AgentResponse[Any]:
|
|
694
|
+
"""Run agent.run() in a child task so interrupt() can cancel it.
|
|
695
|
+
|
|
696
|
+
Using a child task isolates cancellation: the parent task (and its
|
|
697
|
+
post-processing) continues normally even when the child is cancelled.
|
|
698
|
+
"""
|
|
699
|
+
|
|
700
|
+
if self._before_attempt is not None:
|
|
701
|
+
self._before_attempt()
|
|
702
|
+
|
|
703
|
+
# Keep the run call and await inside the attempt task. AgentTelemetryLayer
|
|
704
|
+
# sets and resets non-streaming ContextVars inside its returned coroutine,
|
|
705
|
+
# so either call-timing mode below preserves their asyncio context.
|
|
706
|
+
async def _call() -> AgentResponse[Any]:
|
|
707
|
+
return await self._agent.run(input, stream=False, **run_kwargs)
|
|
708
|
+
|
|
709
|
+
async with self.trace.run(run_kwargs, stream=False):
|
|
710
|
+
if self._blocking_call_timing is BlockingCallTiming.BEFORE_ATTEMPT_TASK:
|
|
711
|
+
# Preserve the child's original call timing, including sync
|
|
712
|
+
# exceptions from run(). Kernel telemetry binds its ContextVars
|
|
713
|
+
# inside this coroutine, not while constructing it.
|
|
714
|
+
run = self._agent.run(input, stream=False, **run_kwargs)
|
|
715
|
+
self.handle.task = asyncio.create_task(cast("Coroutine[Any, Any, AgentResponse[Any]]", run))
|
|
716
|
+
else:
|
|
717
|
+
self.handle.task = asyncio.create_task(_call())
|
|
718
|
+
try:
|
|
719
|
+
return await self.handle.task
|
|
720
|
+
finally:
|
|
721
|
+
self.handle.task = None
|
|
722
|
+
|
|
723
|
+
async def _run_blocking(
|
|
724
|
+
self, message: list[Message], run_kwargs: AgentRunKwargs, *, service_side: bool
|
|
725
|
+
) -> AgentResponse[Any]:
|
|
726
|
+
"""Run locally with per-wire retry, or stored mode with outer retry."""
|
|
727
|
+
if self._retry_participant is not None:
|
|
728
|
+
self._retry_participant.begin_retry()
|
|
729
|
+
try:
|
|
730
|
+
if service_side:
|
|
731
|
+
loop = StreamRetryLoop(
|
|
732
|
+
max_retries=self._effective_max_retries(),
|
|
733
|
+
backoff_schedule=self._backoff_schedule(),
|
|
734
|
+
is_retryable=is_retryable,
|
|
735
|
+
snapshot_history=self._rollback.snapshot,
|
|
736
|
+
restore_history=self._rollback.restore,
|
|
737
|
+
publish_retry_attempt=self._publish_retry_attempt,
|
|
738
|
+
is_interrupted=lambda: self._interrupt.is_interrupted,
|
|
739
|
+
interruptible_sleep=self._interruptible_sleep,
|
|
740
|
+
clean_error_message=clean_error_message,
|
|
741
|
+
after_restore=lambda: self._restore_service_retry_inputs(run_kwargs),
|
|
742
|
+
may_retry=lambda exc: self._may_retry_attempt(exc, run_kwargs),
|
|
743
|
+
retry_exemption=_validation_retry_exemption,
|
|
744
|
+
)
|
|
745
|
+
result = await loop.run(lambda: self._run_agent(message, run_kwargs))
|
|
746
|
+
else:
|
|
747
|
+
result = await self._run_agent(message, run_kwargs)
|
|
748
|
+
finally:
|
|
749
|
+
if self._retry_participant is not None:
|
|
750
|
+
self._retry_participant.end_retry()
|
|
751
|
+
|
|
752
|
+
return result
|
|
753
|
+
|
|
754
|
+
async def _stream_with_retry(
|
|
755
|
+
self,
|
|
756
|
+
current_input: list[Message],
|
|
757
|
+
run_kwargs: AgentRunKwargs,
|
|
758
|
+
) -> AgentResponse[Any]:
|
|
759
|
+
"""Execute a streaming agent.run() with retry on transient errors.
|
|
760
|
+
|
|
761
|
+
Delegates the generic retry/backoff/rollback machinery to
|
|
762
|
+
:class:`StreamRetryLoop`. On exhausted stall retries falls back
|
|
763
|
+
to a non-streaming request for this turn. ``StreamRetryLoop``
|
|
764
|
+
captures its own history snapshot on entry, so callers no longer
|
|
765
|
+
pre-snapshot.
|
|
766
|
+
"""
|
|
767
|
+
loop = StreamRetryLoop(
|
|
768
|
+
max_retries=self._effective_max_retries(),
|
|
769
|
+
backoff_schedule=self._backoff_schedule(),
|
|
770
|
+
is_retryable=is_retryable,
|
|
771
|
+
snapshot_history=self._rollback.snapshot,
|
|
772
|
+
restore_history=self._rollback.restore,
|
|
773
|
+
publish_retry_attempt=self._publish_retry_attempt,
|
|
774
|
+
is_interrupted=lambda: self._interrupt.is_interrupted,
|
|
775
|
+
interruptible_sleep=self._interruptible_sleep,
|
|
776
|
+
clean_error_message=clean_error_message,
|
|
777
|
+
restore_on_stall_exhaustion=isinstance(self._stall_exhaustion, RestoreAndFallback),
|
|
778
|
+
after_restore=lambda: self._restore_service_retry_inputs(run_kwargs),
|
|
779
|
+
may_retry=lambda exc: self._may_retry_attempt(exc, run_kwargs),
|
|
780
|
+
retry_exemption=_validation_retry_exemption,
|
|
781
|
+
)
|
|
782
|
+
|
|
783
|
+
async def _attempt() -> AgentResponse[Any]:
|
|
784
|
+
return await self._stream_single_attempt(current_input, run_kwargs)
|
|
785
|
+
|
|
786
|
+
if self._retry_participant is not None:
|
|
787
|
+
self._retry_participant.begin_retry()
|
|
788
|
+
try:
|
|
789
|
+
try:
|
|
790
|
+
return await loop.run(_attempt)
|
|
791
|
+
except StreamStallExhausted as exc:
|
|
792
|
+
if isinstance(self._stall_exhaustion, KeepAndRaise):
|
|
793
|
+
raise
|
|
794
|
+
# The retry loop restored failed stream state before this
|
|
795
|
+
# blocking replacement, including any consumed injection.
|
|
796
|
+
await self._stall_exhaustion.reject_stream("Streaming stalled; using blocking fallback")
|
|
797
|
+
await self.trace.retry_scheduled(exc=exc, delay_seconds=0, fallback_to_blocking=True)
|
|
798
|
+
return await self._run_agent(current_input, run_kwargs)
|
|
799
|
+
finally:
|
|
800
|
+
if self._retry_participant is not None:
|
|
801
|
+
self._retry_participant.end_retry()
|
|
802
|
+
|
|
803
|
+
async def _publish_retry_attempt(
|
|
804
|
+
self,
|
|
805
|
+
message: str,
|
|
806
|
+
attempt: int,
|
|
807
|
+
max_attempts: int,
|
|
808
|
+
delay_seconds: int,
|
|
809
|
+
exc: BaseException,
|
|
810
|
+
) -> None:
|
|
811
|
+
"""Record the whole-run retry before invoking the caller notice port."""
|
|
812
|
+
await self.trace.retry_scheduled(exc=exc, delay_seconds=delay_seconds)
|
|
813
|
+
await self._publish_retry_notice(message, attempt, max_attempts, delay_seconds, exc)
|
|
814
|
+
|
|
815
|
+
async def _stream_single_attempt(
|
|
816
|
+
self,
|
|
817
|
+
current_input: list[Message],
|
|
818
|
+
run_kwargs: AgentRunKwargs,
|
|
819
|
+
*,
|
|
820
|
+
watchdog: bool = True,
|
|
821
|
+
) -> AgentResponse[Any]:
|
|
822
|
+
"""Consume one stream in its owning task and finalize under the watchdog.
|
|
823
|
+
|
|
824
|
+
Retry boundaries are observed before ordinary updates. The caller's
|
|
825
|
+
final text projection runs after iterator completion, before finalize.
|
|
826
|
+
"""
|
|
827
|
+
import time as _time
|
|
828
|
+
|
|
829
|
+
if self._before_attempt is not None:
|
|
830
|
+
self._before_attempt()
|
|
831
|
+
|
|
832
|
+
# Tracks the last point at which our per-chunk watchdog (re)armed
|
|
833
|
+
# its timer. Updated after each successful ``__anext__()``, on each
|
|
834
|
+
# progress report, and after the stream is fully drained (before
|
|
835
|
+
# the finalize wait). Used below to distinguish a genuine stall — our
|
|
836
|
+
# timer actually elapsed — from a ``TimeoutError`` bubbling up
|
|
837
|
+
# from inside the stream/transport (e.g. httpx or SDK internals),
|
|
838
|
+
# which would otherwise be mis-labelled "Stream stalled" and
|
|
839
|
+
# retried with the wrong error message.
|
|
840
|
+
last_wait_start = _time.monotonic()
|
|
841
|
+
|
|
842
|
+
async def _watched[T](awaitable: Awaitable[T]) -> T:
|
|
843
|
+
# Idle timing: work a pull waits on before its next chunk (a
|
|
844
|
+
# compaction pass and its LAST_WORDS side call) reports progress,
|
|
845
|
+
# and every report restarts the stall timer. The pull stays in
|
|
846
|
+
# this task: its telemetry ContextVars are set and reset here.
|
|
847
|
+
timeout = self._stream_timeout()
|
|
848
|
+
if timeout <= 0:
|
|
849
|
+
# wait_for stalls a non-positive timeout even when the pull
|
|
850
|
+
# would finish without suspending; asyncio.timeout does not.
|
|
851
|
+
return await asyncio.wait_for(awaitable, timeout=timeout)
|
|
852
|
+
event_loop = asyncio.get_running_loop()
|
|
853
|
+
watching = True
|
|
854
|
+
async with asyncio.timeout(timeout) as deadline:
|
|
855
|
+
|
|
856
|
+
def _on_progress() -> None:
|
|
857
|
+
nonlocal last_wait_start
|
|
858
|
+
# A task the pull spawned can report after the pull
|
|
859
|
+
# settled, or while the stall is already cancelling it.
|
|
860
|
+
if watching and not deadline.expired():
|
|
861
|
+
last_wait_start = _time.monotonic()
|
|
862
|
+
deadline.reschedule(event_loop.time() + timeout)
|
|
863
|
+
|
|
864
|
+
try:
|
|
865
|
+
with wire_progress_scope(_on_progress):
|
|
866
|
+
return await awaitable
|
|
867
|
+
finally:
|
|
868
|
+
watching = False
|
|
869
|
+
|
|
870
|
+
async def _iterate_and_finalize() -> AgentResponse[Any]:
|
|
871
|
+
nonlocal last_wait_start
|
|
872
|
+
|
|
873
|
+
# ``agent.run(stream=True)`` is invoked INSIDE this task so the
|
|
874
|
+
# OTel telemetry ContextVars (e.g.
|
|
875
|
+
# ``INNER_RESPONSE_TELEMETRY_CAPTURED_FIELDS``) are set and
|
|
876
|
+
# reset in the same asyncio context — the telemetry cleanup
|
|
877
|
+
# hook fires when the stream is consumed, which happens here.
|
|
878
|
+
# The streaming layer keeps the set-in-call / reset-in-finalizer
|
|
879
|
+
# shape; same-task creation remains the correct pattern here.
|
|
880
|
+
# Matches the blocking path (see ``_run_agent``).
|
|
881
|
+
stream: ResponseStream[AgentResponseUpdate, AgentResponse[Any]] = self._agent.run(
|
|
882
|
+
current_input, stream=True, **run_kwargs
|
|
883
|
+
)
|
|
884
|
+
completed = False
|
|
885
|
+
|
|
886
|
+
try:
|
|
887
|
+
observer = self._stream_observer() if self._stream_observer is not None else None
|
|
888
|
+
|
|
889
|
+
# Per-chunk watchdog: each __anext__() gets the stall timeout
|
|
890
|
+
# individually, so the timer resets whenever a chunk arrives
|
|
891
|
+
# (or the work before it reports progress).
|
|
892
|
+
# This measures stream-idle time (matching httpx read-timeout
|
|
893
|
+
# semantics) — tool executions between chunks don't count.
|
|
894
|
+
#
|
|
895
|
+
# When a ``function_call`` chunk is observed, the tool loop
|
|
896
|
+
# synchronously invokes the tool inside the NEXT ``__anext__()``
|
|
897
|
+
# call — so that await blocks for the entire tool duration.
|
|
898
|
+
# Tools (especially sub-agents) can legitimately run for
|
|
899
|
+
# minutes, which would trip a short stream-idle timeout. To
|
|
900
|
+
# avoid a false "Stream stalled" during tool execution we
|
|
901
|
+
# suspend the watchdog until the post-tool chunk arrives.
|
|
902
|
+
# User-initiated cancellation still works — it flows through
|
|
903
|
+
# the caller control port, which cancels the owned task handle.
|
|
904
|
+
expecting_tool_result = False
|
|
905
|
+
aiter = stream.__aiter__()
|
|
906
|
+
while True:
|
|
907
|
+
try:
|
|
908
|
+
if not watchdog or expecting_tool_result:
|
|
909
|
+
update = await aiter.__anext__()
|
|
910
|
+
else:
|
|
911
|
+
update = await _watched(aiter.__anext__())
|
|
912
|
+
except StopAsyncIteration:
|
|
913
|
+
break
|
|
914
|
+
last_wait_start = _time.monotonic()
|
|
915
|
+
|
|
916
|
+
if is_retry_boundary_update(update):
|
|
917
|
+
if observer is not None:
|
|
918
|
+
await observer.on_retry_boundary()
|
|
919
|
+
if self._retry_boundary is RetryBoundaryPolicy.SKIP:
|
|
920
|
+
continue
|
|
921
|
+
|
|
922
|
+
if observer is not None:
|
|
923
|
+
observer.on_update(update)
|
|
924
|
+
has_function_call = False
|
|
925
|
+
has_function_result = False
|
|
926
|
+
text_chunk = ""
|
|
927
|
+
for content in update.contents or []:
|
|
928
|
+
if content.type == "text" and content.text:
|
|
929
|
+
text_chunk += content.text
|
|
930
|
+
elif content.type == "function_call" and not content.informational_only:
|
|
931
|
+
has_function_call = True
|
|
932
|
+
elif content.type == "function_result":
|
|
933
|
+
has_function_result = True
|
|
934
|
+
if has_function_result or text_chunk:
|
|
935
|
+
expecting_tool_result = False
|
|
936
|
+
if has_function_call:
|
|
937
|
+
expecting_tool_result = True
|
|
938
|
+
|
|
939
|
+
await asyncio.sleep(0) # yield to event loop between chunks
|
|
940
|
+
|
|
941
|
+
if observer is not None:
|
|
942
|
+
await observer.before_finalize()
|
|
943
|
+
|
|
944
|
+
# Also guard finalize with the same per-chunk timeout — it
|
|
945
|
+
# shouldn't block once the iterator exhausts, but it's still
|
|
946
|
+
# an await on the same underlying stream.
|
|
947
|
+
last_wait_start = _time.monotonic()
|
|
948
|
+
if not watchdog:
|
|
949
|
+
response = await stream.get_final_response()
|
|
950
|
+
else:
|
|
951
|
+
response = await _watched(stream.get_final_response())
|
|
952
|
+
completed = True
|
|
953
|
+
return response
|
|
954
|
+
except TimeoutError:
|
|
955
|
+
if not watchdog:
|
|
956
|
+
raise
|
|
957
|
+
# ``asyncio.TimeoutError`` is the builtin ``TimeoutError`` on
|
|
958
|
+
# Python 3.11+, so a ``TimeoutError`` raised from *inside*
|
|
959
|
+
# ``__anext__()`` / ``get_final_response()`` (httpx or SDK
|
|
960
|
+
# internals) would look identical to our own ``wait_for``
|
|
961
|
+
# firing. Only treat it as a stall when the current watchdog
|
|
962
|
+
# window actually elapsed (with a small margin for scheduling
|
|
963
|
+
# jitter); otherwise re-raise so ``is_retryable`` classifies
|
|
964
|
+
# the original exception with its real message.
|
|
965
|
+
idle = _time.monotonic() - last_wait_start
|
|
966
|
+
if idle + 0.5 < self._stream_timeout():
|
|
967
|
+
raise
|
|
968
|
+
raise self._stall_error(self._stream_timeout()) from None
|
|
969
|
+
finally:
|
|
970
|
+
# A stream left before its final response is closed here, in
|
|
971
|
+
# this task: its cleanup hooks (the OTel ContextVar resets
|
|
972
|
+
# among them) must run before the attempt ends, in the context
|
|
973
|
+
# ``.run(stream=True)`` was called in. A pull that failed ran
|
|
974
|
+
# them already and one that was cancelled closed the stream; a
|
|
975
|
+
# cancel between chunks, a failing observer or a stall while
|
|
976
|
+
# finalizing leaves it open, and garbage collection would close
|
|
977
|
+
# only the bare generators under it, never its hooks.
|
|
978
|
+
if not completed:
|
|
979
|
+
try:
|
|
980
|
+
await stream.aclose()
|
|
981
|
+
except Exception:
|
|
982
|
+
logger.debug("Failed to close an abandoned attempt stream", exc_info=True)
|
|
983
|
+
|
|
984
|
+
# Wrap the iteration in a child task so ``interrupt()`` can cancel
|
|
985
|
+
# it. Without this, the streaming path has no task handle to
|
|
986
|
+
# cancel and user-initiated interrupts cannot stop an in-flight
|
|
987
|
+
# stream/tool — including long-running sub-agents. (The blocking
|
|
988
|
+
# path already does this in :meth:`_run_agent`.)
|
|
989
|
+
async with self.trace.run(run_kwargs, stream=True):
|
|
990
|
+
self.handle.task = asyncio.create_task(_iterate_and_finalize())
|
|
991
|
+
try:
|
|
992
|
+
return await self.handle.task
|
|
993
|
+
finally:
|
|
994
|
+
self.handle.task = None
|