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,1237 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""Causal dependency graph and time attribution for one physical turn attempt.
|
|
4
|
+
|
|
5
|
+
Hook ownership, typed dependency edges, time slices, the wall-time partition,
|
|
6
|
+
critical paths and tool/MCP critical-path contributions all rely on one
|
|
7
|
+
definition of which operation displaces, waits on, or merely overlaps
|
|
8
|
+
another, so they live together. ``_critical_path`` holds the interval-path
|
|
9
|
+
search itself.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from array import array
|
|
15
|
+
from collections import Counter, defaultdict
|
|
16
|
+
from collections.abc import Callable
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
from itertools import pairwise
|
|
19
|
+
from threading import Event
|
|
20
|
+
from typing import Final
|
|
21
|
+
|
|
22
|
+
from chrys.foundation.trajectory.envelope import LinkRelation
|
|
23
|
+
from chrys.foundation.trajectory.event_types import RetryMode, ToolOutcome, WaitCategory
|
|
24
|
+
from chrys.service.analytics._context_evidence import _CompactionConsumptionResolution, _RevisionResolution
|
|
25
|
+
from chrys.service.analytics._critical_path import _longest_interval_path, _PathResolution
|
|
26
|
+
from chrys.service.analytics._facts import _HOOK_EXECUTION_MODES, _Endpoint, _Intermediate, _payload_bool, _payload_str
|
|
27
|
+
from chrys.service.analytics._timeline import _hook_drain_scope, _ResolvedNode, _tool_context_for_start
|
|
28
|
+
from chrys.service.analytics.classification import tool_failed
|
|
29
|
+
from chrys.service.analytics.math import clip_interval, interval_union, subtract_intervals
|
|
30
|
+
from chrys.service.analytics.model import (
|
|
31
|
+
FLOW_TERMINAL_INDEX,
|
|
32
|
+
HookOwnership,
|
|
33
|
+
TimelineOperation,
|
|
34
|
+
TimeSlice,
|
|
35
|
+
TurnFlow,
|
|
36
|
+
WallBucket,
|
|
37
|
+
)
|
|
38
|
+
from chrys.service.analytics.reader import raise_if_cancelled as _check_cancelled
|
|
39
|
+
from chrys.service.hooks.events import HookEvent
|
|
40
|
+
|
|
41
|
+
_WALL_PRIORITY: Final = {
|
|
42
|
+
WallBucket.IDLE: 0,
|
|
43
|
+
WallBucket.WAIT: 1,
|
|
44
|
+
WallBucket.TOOLS: 2,
|
|
45
|
+
WallBucket.MODEL: 3,
|
|
46
|
+
}
|
|
47
|
+
# Calls closed before dispatch have no tool preamble and no caused-by edge.
|
|
48
|
+
_NEVER_DISPATCHED_TOOL_OUTCOMES: Final = frozenset(
|
|
49
|
+
{
|
|
50
|
+
ToolOutcome.INVALID_ARGUMENTS,
|
|
51
|
+
ToolOutcome.UNKNOWN_TOOL,
|
|
52
|
+
ToolOutcome.FILTERED,
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
_MODEL_OPERATION_FAMILIES: Final = frozenset({"model.run", "model.cycle", "model.exchange"})
|
|
56
|
+
_RETRY_EXCHANGE_MODES: Final = frozenset(
|
|
57
|
+
{RetryMode.WIRE, RetryMode.STALL_FALLBACK, RetryMode.VALIDATION, RetryMode.CONTEXT_OVERFLOW}
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass(frozen=True, slots=True)
|
|
62
|
+
class _DependencyProof:
|
|
63
|
+
edges: dict[str, set[str]]
|
|
64
|
+
turn_root_id: str | None
|
|
65
|
+
response_terminal_id: str | None
|
|
66
|
+
# Displacing child -> parent pairs; an edge absent here is a causal pointer.
|
|
67
|
+
parents: dict[str, str]
|
|
68
|
+
# Edges that fork a concurrent hook off its target; the hook only depends
|
|
69
|
+
# on the target's triggering event, never on the target completing.
|
|
70
|
+
fork_edges: frozenset[tuple[str, str]] = frozenset()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass(frozen=True, slots=True)
|
|
74
|
+
class _TurnProjection:
|
|
75
|
+
slices: tuple[TimeSlice, ...]
|
|
76
|
+
dependency: _DependencyProof
|
|
77
|
+
timeline_exact: bool
|
|
78
|
+
dag_exact: bool
|
|
79
|
+
response_dependency_exact: bool
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@dataclass(frozen=True, slots=True)
|
|
83
|
+
class _CriticalPaths:
|
|
84
|
+
compute_ns: int | None
|
|
85
|
+
response_ns: int | None
|
|
86
|
+
acyclic: bool
|
|
87
|
+
response_reachable: bool
|
|
88
|
+
compute_bounded: bool
|
|
89
|
+
response_bounded: bool
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _project_turn(
|
|
93
|
+
nodes: list[_ResolvedNode],
|
|
94
|
+
turn_bounds: tuple[int, int],
|
|
95
|
+
*,
|
|
96
|
+
start: _Endpoint,
|
|
97
|
+
finish: _Endpoint | None,
|
|
98
|
+
tail_end: int | None,
|
|
99
|
+
waited_hook_ids: frozenset[str] | None,
|
|
100
|
+
suspension_deductions: dict[str, list[tuple[int, int]]],
|
|
101
|
+
revisions: _RevisionResolution,
|
|
102
|
+
compaction_consumption: _CompactionConsumptionResolution,
|
|
103
|
+
mapped_carrier: Callable[[str], str | None],
|
|
104
|
+
cancel_event: Event | None,
|
|
105
|
+
diagnostics: list[str],
|
|
106
|
+
) -> _TurnProjection:
|
|
107
|
+
by_operation: dict[str, list[_ResolvedNode]] = defaultdict(list)
|
|
108
|
+
for node in nodes:
|
|
109
|
+
by_operation[node.operation_id].append(node)
|
|
110
|
+
preparation_for_tool = {
|
|
111
|
+
target: node
|
|
112
|
+
for node in nodes
|
|
113
|
+
if node.family == "preparation"
|
|
114
|
+
and _payload_str(node.start.payload, "scope") == "tool_preamble"
|
|
115
|
+
and (target := _payload_str(node.start.payload, "target_operation_id")) is not None
|
|
116
|
+
}
|
|
117
|
+
parents: dict[str, str] = {}
|
|
118
|
+
graph_edges: dict[str, set[str]] = defaultdict(set)
|
|
119
|
+
unsafe_work_nodes: set[str] = set()
|
|
120
|
+
timeline_exact = True
|
|
121
|
+
dag_exact = True
|
|
122
|
+
for node in nodes:
|
|
123
|
+
_check_cancelled(cancel_event)
|
|
124
|
+
parent = _displacing_parent(node, by_operation, preparation_for_tool, diagnostics)
|
|
125
|
+
if parent is False:
|
|
126
|
+
timeline_exact = False
|
|
127
|
+
dag_exact = False
|
|
128
|
+
if node.family == "hook.operation":
|
|
129
|
+
unsafe_work_nodes.add(node.node_id)
|
|
130
|
+
elif isinstance(parent, str):
|
|
131
|
+
parents[node.node_id] = parent
|
|
132
|
+
graph_edges[parent].add(node.node_id)
|
|
133
|
+
for relation, target_operation_id in node.start.links:
|
|
134
|
+
if relation != LinkRelation.CAUSED_BY:
|
|
135
|
+
continue
|
|
136
|
+
# A retry lifecycle deliberately shares the next operation's id.
|
|
137
|
+
# caused_by names the work operation, never that retry marker.
|
|
138
|
+
targets = [
|
|
139
|
+
target
|
|
140
|
+
for target in by_operation.get(target_operation_id, [])
|
|
141
|
+
if target.family != "retry" and target.node_id != node.node_id
|
|
142
|
+
]
|
|
143
|
+
if len(targets) == 1:
|
|
144
|
+
graph_edges[targets[0].node_id].add(node.node_id)
|
|
145
|
+
else:
|
|
146
|
+
diagnostics.append("caused_by target cannot be resolved uniquely")
|
|
147
|
+
dag_exact = False
|
|
148
|
+
response_dependency_exact = _validate_async_hook_fences(nodes, waited_hook_ids, diagnostics)
|
|
149
|
+
children: dict[str, set[str]] = defaultdict(set)
|
|
150
|
+
for child, parent in parents.items():
|
|
151
|
+
children[parent].add(child)
|
|
152
|
+
node_by_id = {node.node_id: node for node in nodes}
|
|
153
|
+
if _has_cycle(children):
|
|
154
|
+
diagnostics.append("displacing-edge graph contains a cycle")
|
|
155
|
+
return _TurnProjection(
|
|
156
|
+
slices=(),
|
|
157
|
+
dependency=_DependencyProof(graph_edges, None, None, parents),
|
|
158
|
+
timeline_exact=False,
|
|
159
|
+
dag_exact=False,
|
|
160
|
+
response_dependency_exact=response_dependency_exact,
|
|
161
|
+
)
|
|
162
|
+
slices: list[TimeSlice] = []
|
|
163
|
+
for node in nodes:
|
|
164
|
+
_check_cancelled(cancel_event)
|
|
165
|
+
if _skip_node(node):
|
|
166
|
+
continue
|
|
167
|
+
descendants = _descendants(node.node_id, children)
|
|
168
|
+
removed = [node_by_id[descendant].interval for descendant in descendants]
|
|
169
|
+
removed.extend(suspension_deductions.get(node.node_id, ()))
|
|
170
|
+
residuals = subtract_intervals(node.interval, removed)
|
|
171
|
+
attributes = _slice_attributes(node)
|
|
172
|
+
if attributes is None:
|
|
173
|
+
continue
|
|
174
|
+
bucket, counts_as_work, compute_weight, response_weight, owner = attributes
|
|
175
|
+
if node.family == "hook.operation":
|
|
176
|
+
mode = _payload_str(node.start.payload, "execution_mode")
|
|
177
|
+
hook_event = _payload_str(node.start.payload, "hook_event")
|
|
178
|
+
if hook_event == "user_interrupt" or mode == "fire_and_forget":
|
|
179
|
+
response_weight = False
|
|
180
|
+
elif mode == "async":
|
|
181
|
+
response_weight = waited_hook_ids is not None and node.operation_id in waited_hook_ids
|
|
182
|
+
if node.node_id in unsafe_work_nodes:
|
|
183
|
+
counts_as_work = False
|
|
184
|
+
compute_weight = False
|
|
185
|
+
response_weight = False
|
|
186
|
+
for index, residual in enumerate(residuals):
|
|
187
|
+
clipped = clip_interval(residual, turn_bounds)
|
|
188
|
+
if clipped is None:
|
|
189
|
+
continue
|
|
190
|
+
slices.append(
|
|
191
|
+
TimeSlice(
|
|
192
|
+
family=node.family,
|
|
193
|
+
slice_index=index,
|
|
194
|
+
turn_id=start.turn_id or "",
|
|
195
|
+
runtime_id=node.start.runtime_id,
|
|
196
|
+
operation_id=node.operation_id,
|
|
197
|
+
owner=owner,
|
|
198
|
+
start_ns=clipped[0],
|
|
199
|
+
end_ns=clipped[1],
|
|
200
|
+
wall_bucket=bucket,
|
|
201
|
+
counts_as_work=counts_as_work,
|
|
202
|
+
compute_weight=compute_weight,
|
|
203
|
+
response_weight=response_weight,
|
|
204
|
+
outcome=_payload_str(node.finish.payload, "outcome"),
|
|
205
|
+
tool_name=_payload_str(node.start.payload, "tool_name"),
|
|
206
|
+
tool_kind=_payload_str(node.start.payload, "tool_kind"),
|
|
207
|
+
)
|
|
208
|
+
)
|
|
209
|
+
typed_exact, response_source_id = _connect_typed_dependencies(
|
|
210
|
+
nodes,
|
|
211
|
+
graph_edges,
|
|
212
|
+
main_actor_id=start.actor_id,
|
|
213
|
+
revisions=revisions,
|
|
214
|
+
compaction_consumption=compaction_consumption,
|
|
215
|
+
mapped_carrier=mapped_carrier,
|
|
216
|
+
cancel_event=cancel_event,
|
|
217
|
+
diagnostics=diagnostics,
|
|
218
|
+
)
|
|
219
|
+
dag_exact = dag_exact and typed_exact
|
|
220
|
+
roots = [
|
|
221
|
+
node.node_id
|
|
222
|
+
for node in nodes
|
|
223
|
+
if node.family == "preparation" and _payload_str(node.start.payload, "scope") == "turn_preamble"
|
|
224
|
+
]
|
|
225
|
+
turn_root_id = roots[0] if len(roots) == 1 else None
|
|
226
|
+
response_terminal_id = (
|
|
227
|
+
f"turn.response:{start.turn_id}"
|
|
228
|
+
if finish is not None and tail_end is not None and response_source_id is not None
|
|
229
|
+
else None
|
|
230
|
+
)
|
|
231
|
+
if response_terminal_id is not None and response_source_id is not None:
|
|
232
|
+
graph_edges[response_source_id].add(response_terminal_id)
|
|
233
|
+
fork_edges: set[tuple[str, str]] = set()
|
|
234
|
+
hook_dag_exact, hook_response_exact = _connect_hook_dependencies(
|
|
235
|
+
nodes,
|
|
236
|
+
graph_edges,
|
|
237
|
+
waited_hook_ids=waited_hook_ids,
|
|
238
|
+
turn_root_id=turn_root_id,
|
|
239
|
+
response_source_id=response_source_id,
|
|
240
|
+
response_terminal_id=response_terminal_id,
|
|
241
|
+
fork_edges=fork_edges,
|
|
242
|
+
diagnostics=diagnostics,
|
|
243
|
+
)
|
|
244
|
+
dag_exact = dag_exact and hook_dag_exact
|
|
245
|
+
response_dependency_exact = response_dependency_exact and hook_response_exact
|
|
246
|
+
if response_source_id is not None and response_terminal_id is not None:
|
|
247
|
+
response_descendants = _descendants(response_source_id, children)
|
|
248
|
+
for node_id in response_descendants:
|
|
249
|
+
if not children.get(node_id, set()) & response_descendants:
|
|
250
|
+
graph_edges[node_id].add(response_terminal_id)
|
|
251
|
+
return _TurnProjection(
|
|
252
|
+
slices=tuple(slices),
|
|
253
|
+
dependency=_DependencyProof(graph_edges, turn_root_id, response_terminal_id, parents, frozenset(fork_edges)),
|
|
254
|
+
timeline_exact=timeline_exact,
|
|
255
|
+
dag_exact=dag_exact,
|
|
256
|
+
response_dependency_exact=response_dependency_exact,
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _turn_flow(
|
|
261
|
+
turn_id: str,
|
|
262
|
+
dependency: _DependencyProof,
|
|
263
|
+
operations: tuple[TimelineOperation, ...],
|
|
264
|
+
*,
|
|
265
|
+
acyclic: bool,
|
|
266
|
+
) -> TurnFlow:
|
|
267
|
+
"""Index-encode the typed per-turn dependency graph against the operations tuple."""
|
|
268
|
+
index_by_node: dict[str, int] = {}
|
|
269
|
+
for index, operation in enumerate(operations):
|
|
270
|
+
index_by_node.setdefault(f"{operation.family}:{operation.operation_id}", index)
|
|
271
|
+
terminal_id = dependency.response_terminal_id
|
|
272
|
+
parent_pairs = array("I")
|
|
273
|
+
causal_pairs = array("I")
|
|
274
|
+
for source, targets in sorted(dependency.edges.items()):
|
|
275
|
+
source_index = index_by_node.get(source)
|
|
276
|
+
if source_index is None:
|
|
277
|
+
continue
|
|
278
|
+
for target in sorted(targets):
|
|
279
|
+
if target == terminal_id:
|
|
280
|
+
target_index = FLOW_TERMINAL_INDEX
|
|
281
|
+
else:
|
|
282
|
+
found = index_by_node.get(target)
|
|
283
|
+
if found is None:
|
|
284
|
+
continue
|
|
285
|
+
target_index = found
|
|
286
|
+
pairs = parent_pairs if dependency.parents.get(target) == source else causal_pairs
|
|
287
|
+
pairs.append(source_index)
|
|
288
|
+
pairs.append(target_index)
|
|
289
|
+
root_id = dependency.turn_root_id
|
|
290
|
+
return TurnFlow(
|
|
291
|
+
turn_id=turn_id,
|
|
292
|
+
root_index=index_by_node.get(root_id) if root_id is not None else None,
|
|
293
|
+
has_terminal=terminal_id is not None,
|
|
294
|
+
parent_pairs=parent_pairs.tobytes(),
|
|
295
|
+
causal_pairs=causal_pairs.tobytes(),
|
|
296
|
+
acyclic=acyclic,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def _displacing_parent(
|
|
301
|
+
node: _ResolvedNode,
|
|
302
|
+
by_operation: dict[str, list[_ResolvedNode]],
|
|
303
|
+
preparation_for_tool: dict[str, _ResolvedNode],
|
|
304
|
+
diagnostics: list[str],
|
|
305
|
+
) -> str | bool | None:
|
|
306
|
+
if node.family == "preparation":
|
|
307
|
+
scope = _payload_str(node.start.payload, "scope")
|
|
308
|
+
if scope == "turn_preamble":
|
|
309
|
+
return None
|
|
310
|
+
if scope == "pre_turn":
|
|
311
|
+
return None
|
|
312
|
+
if node.family == "hook.operation":
|
|
313
|
+
hook_event = _payload_str(node.start.payload, "hook_event")
|
|
314
|
+
mode = _payload_str(node.start.payload, "execution_mode")
|
|
315
|
+
ownership = classify_hook_ownership(hook_event, mode)
|
|
316
|
+
if ownership is HookOwnership.CONCURRENT:
|
|
317
|
+
return None
|
|
318
|
+
target = _payload_str(node.start.payload, "target_operation_id")
|
|
319
|
+
if ownership is HookOwnership.TOOL_PREAMBLE and target is not None:
|
|
320
|
+
preamble = preparation_for_tool.get(target)
|
|
321
|
+
if preamble is not None:
|
|
322
|
+
return preamble.node_id
|
|
323
|
+
elif ownership in {
|
|
324
|
+
HookOwnership.TOOL_TAIL,
|
|
325
|
+
HookOwnership.APPROVAL,
|
|
326
|
+
HookOwnership.TURN_PREAMBLE,
|
|
327
|
+
HookOwnership.SUB_AGENT,
|
|
328
|
+
HookOwnership.COMPACTION,
|
|
329
|
+
}:
|
|
330
|
+
families = _hook_target_families(hook_event)
|
|
331
|
+
resolved = [candidate for candidate in by_operation.get(target or "", []) if candidate.family in families]
|
|
332
|
+
if len(resolved) == 1:
|
|
333
|
+
return resolved[0].node_id
|
|
334
|
+
elif ownership in {HookOwnership.TURN_TAIL, HookOwnership.SESSION_ROOT, HookOwnership.WORKFLOW_ROOT}:
|
|
335
|
+
return None
|
|
336
|
+
diagnostics.append(f"blocking hook {hook_event or 'unknown'} has no placeable displacing edge")
|
|
337
|
+
return False
|
|
338
|
+
if node.family == "tool.operation":
|
|
339
|
+
parent = _payload_str(node.start.payload, "parent_model_operation_id")
|
|
340
|
+
if parent is None:
|
|
341
|
+
diagnostics.append("tool operation lacks parent_model_operation_id")
|
|
342
|
+
return False
|
|
343
|
+
candidates = [
|
|
344
|
+
candidate for candidate in by_operation.get(parent, []) if candidate.family in _MODEL_OPERATION_FAMILIES
|
|
345
|
+
]
|
|
346
|
+
if len(candidates) == 1:
|
|
347
|
+
return candidates[0].node_id
|
|
348
|
+
diagnostics.append("tool parent model operation cannot be resolved uniquely")
|
|
349
|
+
return False
|
|
350
|
+
if node.family == "turn.suspension":
|
|
351
|
+
return None
|
|
352
|
+
target = None
|
|
353
|
+
families: frozenset[str] | None = None
|
|
354
|
+
if node.family == "approval":
|
|
355
|
+
generic_target = _payload_str(node.start.payload, "target_operation_id")
|
|
356
|
+
target = generic_target or _payload_str(node.start.payload, "target_tool_operation_id")
|
|
357
|
+
if generic_target is None:
|
|
358
|
+
families = frozenset({"tool.operation"})
|
|
359
|
+
elif node.family in {"preparation", "compaction"}:
|
|
360
|
+
target = node.start.parent_operation_id
|
|
361
|
+
families = _MODEL_OPERATION_FAMILIES
|
|
362
|
+
elif node.family == "compaction.phase":
|
|
363
|
+
target = node.start.parent_operation_id
|
|
364
|
+
families = frozenset({"compaction"})
|
|
365
|
+
elif node.family == "model.cycle":
|
|
366
|
+
target = node.start.parent_operation_id
|
|
367
|
+
families = frozenset({"model.run", "sub_agent"})
|
|
368
|
+
elif node.family == "model.exchange":
|
|
369
|
+
target = node.start.parent_operation_id
|
|
370
|
+
families = frozenset({"model.run", "model.cycle"})
|
|
371
|
+
elif node.family == "sub_agent":
|
|
372
|
+
target = node.start.parent_operation_id
|
|
373
|
+
families = frozenset({"tool.operation", *_MODEL_OPERATION_FAMILIES})
|
|
374
|
+
elif node.family == "wait":
|
|
375
|
+
target = node.start.parent_operation_id
|
|
376
|
+
category = _payload_str(node.start.payload, "category")
|
|
377
|
+
families = _wait_parent_families(category)
|
|
378
|
+
elif node.family == "retry":
|
|
379
|
+
target = node.start.parent_operation_id
|
|
380
|
+
families = _retry_parent_families(_payload_str(node.start.payload, "retry_mode"))
|
|
381
|
+
else:
|
|
382
|
+
target = node.start.parent_operation_id
|
|
383
|
+
if target is None:
|
|
384
|
+
return None
|
|
385
|
+
candidates = [
|
|
386
|
+
candidate
|
|
387
|
+
for candidate in by_operation.get(target, [])
|
|
388
|
+
if candidate.node_id != node.node_id
|
|
389
|
+
and candidate.family != "retry"
|
|
390
|
+
and (families is None or candidate.family in families)
|
|
391
|
+
]
|
|
392
|
+
if len(candidates) == 1:
|
|
393
|
+
return candidates[0].node_id
|
|
394
|
+
if candidates:
|
|
395
|
+
diagnostics.append(f"{node.family} parent operation is ambiguous")
|
|
396
|
+
return False
|
|
397
|
+
diagnostics.append(f"{node.family} parent operation is missing")
|
|
398
|
+
return False
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def _wait_parent_families(category: str | None) -> frozenset[str] | None:
|
|
402
|
+
if category == WaitCategory.USER_INPUT:
|
|
403
|
+
return frozenset({"tool.operation", "sub_agent"})
|
|
404
|
+
if category == WaitCategory.MCP_CONNECT:
|
|
405
|
+
return frozenset({"tool.operation"})
|
|
406
|
+
if category in {WaitCategory.INPUT_ADMISSION, WaitCategory.TOOL_ADMISSION}:
|
|
407
|
+
return frozenset({"preparation"})
|
|
408
|
+
if category == WaitCategory.RATE_LIMIT:
|
|
409
|
+
return _MODEL_OPERATION_FAMILIES
|
|
410
|
+
if category == WaitCategory.SUB_AGENT_CONCURRENCY:
|
|
411
|
+
return frozenset({"sub_agent"})
|
|
412
|
+
return None
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def _retry_parent_families(retry_mode: str | None) -> frozenset[str] | None:
|
|
416
|
+
if retry_mode in _RETRY_EXCHANGE_MODES:
|
|
417
|
+
return frozenset({"model.run", "model.cycle"})
|
|
418
|
+
if retry_mode == RetryMode.RUN:
|
|
419
|
+
return frozenset({"sub_agent"})
|
|
420
|
+
if retry_mode == RetryMode.COMPACTION:
|
|
421
|
+
return frozenset({"compaction"})
|
|
422
|
+
if retry_mode == RetryMode.CONNECTION:
|
|
423
|
+
return frozenset({"sub_agent"})
|
|
424
|
+
return None
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def _hook_target_families(hook_event: str | None) -> frozenset[str]:
|
|
428
|
+
if hook_event in {HookEvent.APPROVAL_REQUESTED, HookEvent.APPROVAL_RESOLVED}:
|
|
429
|
+
return frozenset({"approval"})
|
|
430
|
+
if hook_event in {HookEvent.BEFORE_TOOL_CALL, HookEvent.AFTER_TOOL_CALL, HookEvent.TOOL_ERROR}:
|
|
431
|
+
return frozenset({"tool.operation"})
|
|
432
|
+
if hook_event == HookEvent.BEFORE_TURN:
|
|
433
|
+
return frozenset({"preparation"})
|
|
434
|
+
if hook_event in {HookEvent.SUB_AGENT_START, HookEvent.SUB_AGENT_END}:
|
|
435
|
+
return frozenset({"sub_agent"})
|
|
436
|
+
if hook_event == HookEvent.PRE_COMPACT:
|
|
437
|
+
return frozenset({"compaction"})
|
|
438
|
+
return frozenset()
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
def classify_hook_ownership(hook_event: str | None, execution_mode: str | None) -> HookOwnership:
|
|
442
|
+
"""Apply the ordered, exhaustive hook ownership matrix without timing guesses."""
|
|
443
|
+
if execution_mode not in _HOOK_EXECUTION_MODES:
|
|
444
|
+
return HookOwnership.UNSAFE
|
|
445
|
+
if hook_event == HookEvent.USER_INTERRUPT or execution_mode != "blocking":
|
|
446
|
+
return HookOwnership.CONCURRENT
|
|
447
|
+
if hook_event == HookEvent.BEFORE_TOOL_CALL:
|
|
448
|
+
return HookOwnership.TOOL_PREAMBLE
|
|
449
|
+
if hook_event in {HookEvent.AFTER_TOOL_CALL, HookEvent.TOOL_ERROR}:
|
|
450
|
+
return HookOwnership.TOOL_TAIL
|
|
451
|
+
if hook_event in {HookEvent.APPROVAL_REQUESTED, HookEvent.APPROVAL_RESOLVED}:
|
|
452
|
+
return HookOwnership.APPROVAL
|
|
453
|
+
if hook_event == HookEvent.BEFORE_TURN:
|
|
454
|
+
return HookOwnership.TURN_PREAMBLE
|
|
455
|
+
if hook_event == HookEvent.AFTER_TURN:
|
|
456
|
+
return HookOwnership.TURN_TAIL
|
|
457
|
+
if hook_event == HookEvent.USER_PROMPT_SUBMIT:
|
|
458
|
+
return HookOwnership.PRE_TURN
|
|
459
|
+
if hook_event in {HookEvent.SUB_AGENT_START, HookEvent.SUB_AGENT_END}:
|
|
460
|
+
return HookOwnership.SUB_AGENT
|
|
461
|
+
if hook_event == HookEvent.PRE_COMPACT:
|
|
462
|
+
return HookOwnership.COMPACTION
|
|
463
|
+
if hook_event in {HookEvent.WORKFLOW_RUN_START, HookEvent.WORKFLOW_RUN_END}:
|
|
464
|
+
return HookOwnership.WORKFLOW_ROOT
|
|
465
|
+
if hook_event in {HookEvent.SESSION_START, HookEvent.SESSION_RESTORED, HookEvent.SESSION_END}:
|
|
466
|
+
return HookOwnership.SESSION_ROOT
|
|
467
|
+
return HookOwnership.UNSAFE
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
def _slice_attributes(
|
|
471
|
+
node: _ResolvedNode,
|
|
472
|
+
) -> tuple[WallBucket, bool, bool, bool, str] | None:
|
|
473
|
+
family = node.family
|
|
474
|
+
if family in {"model.run", "model.cycle", "model.exchange"}:
|
|
475
|
+
return WallBucket.MODEL, True, True, True, "model"
|
|
476
|
+
if family in {"compaction", "compaction.phase"}:
|
|
477
|
+
return WallBucket.MODEL, True, True, True, "compaction"
|
|
478
|
+
if family == "tool.operation":
|
|
479
|
+
if _payload_str(node.start.payload, "tool_kind") == "sleep":
|
|
480
|
+
return WallBucket.WAIT, False, False, True, "tool sleep"
|
|
481
|
+
return WallBucket.TOOLS, True, True, True, "tool"
|
|
482
|
+
if family == "sub_agent":
|
|
483
|
+
return WallBucket.TOOLS, True, True, True, "sub-agent"
|
|
484
|
+
if family == "continuation.poll":
|
|
485
|
+
return WallBucket.WAIT, False, False, True, "continuation poll"
|
|
486
|
+
if family == "turn.suspension":
|
|
487
|
+
return WallBucket.WAIT, False, False, True, "sub-agent suspension"
|
|
488
|
+
if family in {"wait", "approval", "retry"}:
|
|
489
|
+
if family == "wait" and _payload_str(node.start.payload, "category") == "input_admission":
|
|
490
|
+
return None
|
|
491
|
+
owner = (
|
|
492
|
+
"approval" if family == "approval" or _payload_str(node.start.payload, "category") == "approval" else family
|
|
493
|
+
)
|
|
494
|
+
return WallBucket.WAIT, False, False, True, owner
|
|
495
|
+
if family == "preparation":
|
|
496
|
+
scope = _payload_str(node.start.payload, "scope")
|
|
497
|
+
if scope == "pre_turn":
|
|
498
|
+
return None
|
|
499
|
+
return WallBucket.TOOLS, True, True, True, "preparation"
|
|
500
|
+
if family == "hook.operation":
|
|
501
|
+
outcome = _payload_str(node.finish.payload, "outcome")
|
|
502
|
+
if outcome == "detached":
|
|
503
|
+
return None
|
|
504
|
+
mode = _payload_str(node.start.payload, "execution_mode")
|
|
505
|
+
hook_event = _payload_str(node.start.payload, "hook_event")
|
|
506
|
+
if mode == "fire_and_forget" or hook_event == "user_interrupt":
|
|
507
|
+
return WallBucket.TOOLS, True, True, False, "hook"
|
|
508
|
+
return WallBucket.TOOLS, True, True, True, "hook"
|
|
509
|
+
return None
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
def _skip_node(node: _ResolvedNode) -> bool:
|
|
513
|
+
return node.family == "tool.operation" and _payload_bool(node.finish.payload, "abandoned")
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
def _never_dispatched(node: _ResolvedNode) -> bool:
|
|
517
|
+
return (
|
|
518
|
+
node.family == "tool.operation"
|
|
519
|
+
and _payload_str(node.finish.payload, "outcome") in _NEVER_DISPATCHED_TOOL_OUTCOMES
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
def _required_causes_present(nodes: list[_ResolvedNode], diagnostics: list[str]) -> bool:
|
|
524
|
+
preambles = [
|
|
525
|
+
node
|
|
526
|
+
for node in nodes
|
|
527
|
+
if node.family == "preparation" and _payload_str(node.start.payload, "scope") == "turn_preamble"
|
|
528
|
+
]
|
|
529
|
+
runs = sorted((node for node in nodes if node.family == "model.run"), key=lambda node: node.start.sequence)
|
|
530
|
+
exact = True
|
|
531
|
+
if len(preambles) != 1 or not runs:
|
|
532
|
+
diagnostics.append("turn_preamble or first model.run is missing")
|
|
533
|
+
exact = False
|
|
534
|
+
elif (LinkRelation.CAUSED_BY, preambles[0].operation_id) not in runs[0].start.links:
|
|
535
|
+
diagnostics.append("first model.run lacks caused_by link to turn_preamble")
|
|
536
|
+
exact = False
|
|
537
|
+
tool_preambles = {
|
|
538
|
+
target: node
|
|
539
|
+
for node in nodes
|
|
540
|
+
if node.family == "preparation"
|
|
541
|
+
and _payload_str(node.start.payload, "scope") == "tool_preamble"
|
|
542
|
+
and (target := _payload_str(node.start.payload, "target_operation_id")) is not None
|
|
543
|
+
}
|
|
544
|
+
tools = [node for node in nodes if node.family == "tool.operation" and not _never_dispatched(node)]
|
|
545
|
+
if len(tool_preambles) != len(tools):
|
|
546
|
+
diagnostics.append("tool_preamble to tool pairing is not one-to-one")
|
|
547
|
+
exact = False
|
|
548
|
+
for tool in tools:
|
|
549
|
+
preamble = tool_preambles.get(tool.operation_id)
|
|
550
|
+
if preamble is None or (LinkRelation.CAUSED_BY, preamble.operation_id) not in tool.start.links:
|
|
551
|
+
diagnostics.append("tool operation lacks caused_by link to its tool_preamble")
|
|
552
|
+
exact = False
|
|
553
|
+
return exact
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
def _validate_async_hook_fences(
|
|
557
|
+
nodes: list[_ResolvedNode],
|
|
558
|
+
waited_hook_ids: frozenset[str] | None,
|
|
559
|
+
diagnostics: list[str],
|
|
560
|
+
) -> bool:
|
|
561
|
+
async_turn_hooks = {
|
|
562
|
+
node.operation_id
|
|
563
|
+
for node in nodes
|
|
564
|
+
if node.family == "hook.operation"
|
|
565
|
+
and _payload_str(node.start.payload, "execution_mode") == "async"
|
|
566
|
+
and _hook_drain_scope(node.start) == "turn"
|
|
567
|
+
}
|
|
568
|
+
if not async_turn_hooks:
|
|
569
|
+
return True
|
|
570
|
+
if waited_hook_ids is None:
|
|
571
|
+
diagnostics.append("async hook response dependencies lack a complete turn fence")
|
|
572
|
+
return False
|
|
573
|
+
known_hooks = {node.operation_id: node for node in nodes if node.family == "hook.operation"}
|
|
574
|
+
invalid_members = [
|
|
575
|
+
operation_id
|
|
576
|
+
for operation_id in waited_hook_ids
|
|
577
|
+
if operation_id not in known_hooks
|
|
578
|
+
or _payload_str(known_hooks[operation_id].start.payload, "execution_mode") != "async"
|
|
579
|
+
or _hook_drain_scope(known_hooks[operation_id].start) != "turn"
|
|
580
|
+
]
|
|
581
|
+
if invalid_members:
|
|
582
|
+
diagnostics.append("turn fence contains a hook that is not an async turn-scope dependency")
|
|
583
|
+
return False
|
|
584
|
+
return True
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
def _connect_typed_dependencies(
|
|
588
|
+
nodes: list[_ResolvedNode],
|
|
589
|
+
edges: dict[str, set[str]],
|
|
590
|
+
*,
|
|
591
|
+
main_actor_id: str | None,
|
|
592
|
+
revisions: _RevisionResolution,
|
|
593
|
+
compaction_consumption: _CompactionConsumptionResolution,
|
|
594
|
+
mapped_carrier: Callable[[str], str | None],
|
|
595
|
+
cancel_event: Event | None,
|
|
596
|
+
diagnostics: list[str],
|
|
597
|
+
) -> tuple[bool, str | None]:
|
|
598
|
+
"""Connect only producer-declared causal pointers; never infer adjacency."""
|
|
599
|
+
by_operation: dict[str, list[_ResolvedNode]] = defaultdict(list)
|
|
600
|
+
for node in nodes:
|
|
601
|
+
by_operation[node.operation_id].append(node)
|
|
602
|
+
exact = compaction_consumption.exact
|
|
603
|
+
|
|
604
|
+
compaction_bridges: dict[str, str] = {}
|
|
605
|
+
for run_id, consumption in compaction_consumption.by_run.items():
|
|
606
|
+
_check_cancelled(cancel_event)
|
|
607
|
+
compactions = [candidate for candidate in by_operation.get(run_id, ()) if candidate.family == "compaction"]
|
|
608
|
+
if len(compactions) != 1:
|
|
609
|
+
diagnostics.append("Phase-4 consumed items cannot resolve their compaction run uniquely")
|
|
610
|
+
exact = False
|
|
611
|
+
continue
|
|
612
|
+
compaction = compactions[0]
|
|
613
|
+
candidates = [
|
|
614
|
+
candidate
|
|
615
|
+
for candidate in by_operation.get(consumption.next_exchange_operation_id, ())
|
|
616
|
+
if candidate.family == "model.exchange"
|
|
617
|
+
and candidate.start.scope == compaction.start.scope
|
|
618
|
+
and candidate.start.sequence > compaction.finish.sequence
|
|
619
|
+
and candidate.start.monotonic_ns >= compaction.finish.monotonic_ns
|
|
620
|
+
]
|
|
621
|
+
if len(candidates) != 1:
|
|
622
|
+
diagnostics.append("Phase-4 compaction successor exchange cannot be resolved uniquely")
|
|
623
|
+
exact = False
|
|
624
|
+
continue
|
|
625
|
+
edges[compaction.node_id].add(candidates[0].node_id)
|
|
626
|
+
compaction_bridges[compaction.operation_id] = compaction.node_id
|
|
627
|
+
|
|
628
|
+
for run in (node for node in nodes if node.family == "model.run"):
|
|
629
|
+
_check_cancelled(cancel_event)
|
|
630
|
+
previous = _payload_str(run.start.payload, "previous_run_operation_id")
|
|
631
|
+
if previous is not None:
|
|
632
|
+
exact = (
|
|
633
|
+
_connect_unique(
|
|
634
|
+
by_operation,
|
|
635
|
+
previous,
|
|
636
|
+
run.node_id,
|
|
637
|
+
edges,
|
|
638
|
+
diagnostics,
|
|
639
|
+
"previous model run",
|
|
640
|
+
families=frozenset({"model.run"}),
|
|
641
|
+
)
|
|
642
|
+
and exact
|
|
643
|
+
)
|
|
644
|
+
|
|
645
|
+
for retry in (node for node in nodes if node.family == "retry"):
|
|
646
|
+
retry_mode = _payload_str(retry.start.payload, "retry_mode")
|
|
647
|
+
neighbor_families = _retry_neighbor_families(retry_mode)
|
|
648
|
+
previous = _payload_str(retry.start.payload, "previous_operation_id")
|
|
649
|
+
following = _payload_str(retry.start.payload, "next_operation_id") or _payload_str(
|
|
650
|
+
retry.finish.payload, "next_operation_id"
|
|
651
|
+
)
|
|
652
|
+
if previous is not None:
|
|
653
|
+
exact = (
|
|
654
|
+
_connect_unique(
|
|
655
|
+
by_operation,
|
|
656
|
+
previous,
|
|
657
|
+
retry.node_id,
|
|
658
|
+
edges,
|
|
659
|
+
diagnostics,
|
|
660
|
+
"retry predecessor",
|
|
661
|
+
families=neighbor_families,
|
|
662
|
+
)
|
|
663
|
+
and exact
|
|
664
|
+
)
|
|
665
|
+
if following is not None:
|
|
666
|
+
exact = (
|
|
667
|
+
_connect_unique(
|
|
668
|
+
by_operation,
|
|
669
|
+
following,
|
|
670
|
+
None,
|
|
671
|
+
edges,
|
|
672
|
+
diagnostics,
|
|
673
|
+
"retry successor",
|
|
674
|
+
source=retry.node_id,
|
|
675
|
+
families=neighbor_families,
|
|
676
|
+
)
|
|
677
|
+
and exact
|
|
678
|
+
)
|
|
679
|
+
|
|
680
|
+
for poll in (node for node in nodes if node.family == "continuation.poll"):
|
|
681
|
+
_check_cancelled(cancel_event)
|
|
682
|
+
previous = _payload_str(poll.start.payload, "previous_exchange_operation_id")
|
|
683
|
+
following = _payload_str(poll.start.payload, "next_exchange_operation_id")
|
|
684
|
+
if previous is None or following is None:
|
|
685
|
+
diagnostics.append("continuation poll lacks typed neighboring exchanges")
|
|
686
|
+
exact = False
|
|
687
|
+
continue
|
|
688
|
+
exact = (
|
|
689
|
+
_connect_unique(
|
|
690
|
+
by_operation,
|
|
691
|
+
previous,
|
|
692
|
+
poll.node_id,
|
|
693
|
+
edges,
|
|
694
|
+
diagnostics,
|
|
695
|
+
"continuation poll predecessor",
|
|
696
|
+
families=frozenset({"model.exchange"}),
|
|
697
|
+
)
|
|
698
|
+
and exact
|
|
699
|
+
)
|
|
700
|
+
exact = (
|
|
701
|
+
_connect_unique(
|
|
702
|
+
by_operation,
|
|
703
|
+
following,
|
|
704
|
+
None,
|
|
705
|
+
edges,
|
|
706
|
+
diagnostics,
|
|
707
|
+
"continuation poll successor",
|
|
708
|
+
source=poll.node_id,
|
|
709
|
+
families=frozenset({"model.exchange"}),
|
|
710
|
+
)
|
|
711
|
+
and exact
|
|
712
|
+
)
|
|
713
|
+
|
|
714
|
+
response_source_id: str | None = None
|
|
715
|
+
response_source_operation_id: str | None = None
|
|
716
|
+
# Sub-agent cycles are inlined in the turn; only the turn's own actor
|
|
717
|
+
# answers the user, so the response source is its last final exchange.
|
|
718
|
+
cycles_with_final = [
|
|
719
|
+
(node, final_exchange)
|
|
720
|
+
for node in nodes
|
|
721
|
+
if node.family == "model.cycle"
|
|
722
|
+
and node.start.actor_id == main_actor_id
|
|
723
|
+
and (final_exchange := _payload_str(node.finish.payload, "final_exchange_operation_id")) is not None
|
|
724
|
+
]
|
|
725
|
+
if not cycles_with_final:
|
|
726
|
+
diagnostics.append("response linkage lacks final_exchange_operation_id")
|
|
727
|
+
else:
|
|
728
|
+
_, final_exchange_id = max(cycles_with_final, key=lambda item: item[0].finish.sequence)
|
|
729
|
+
candidates = [node for node in by_operation.get(final_exchange_id, ()) if node.family == "model.exchange"]
|
|
730
|
+
if len(candidates) != 1:
|
|
731
|
+
diagnostics.append("final exchange operation cannot be resolved uniquely")
|
|
732
|
+
exact = False
|
|
733
|
+
else:
|
|
734
|
+
response_source_id = candidates[0].node_id
|
|
735
|
+
response_source_operation_id = candidates[0].operation_id
|
|
736
|
+
|
|
737
|
+
exchanges_by_revision: dict[str, list[_ResolvedNode]] = defaultdict(list)
|
|
738
|
+
for exchange in (node for node in nodes if node.family == "model.exchange"):
|
|
739
|
+
_check_cancelled(cancel_event)
|
|
740
|
+
revision_id = _payload_str(exchange.start.payload, "context_revision_id")
|
|
741
|
+
if revision_id is not None:
|
|
742
|
+
exchanges_by_revision[revision_id].append(exchange)
|
|
743
|
+
|
|
744
|
+
valid_exchange_memberships: dict[str, tuple[str, ...]] = {}
|
|
745
|
+
invalid_exchange_membership_diagnostics: list[str] = []
|
|
746
|
+
for revision_id, exchanges in exchanges_by_revision.items():
|
|
747
|
+
_check_cancelled(cancel_event)
|
|
748
|
+
revision_errors = revisions.errors.get(revision_id, ())
|
|
749
|
+
if revision_errors:
|
|
750
|
+
invalid_exchange_membership_diagnostics.extend(revision_errors)
|
|
751
|
+
continue
|
|
752
|
+
membership = revisions.memberships.get(revision_id)
|
|
753
|
+
revision = revisions.endpoints.get(revision_id)
|
|
754
|
+
if membership is None or revision is None:
|
|
755
|
+
invalid_exchange_membership_diagnostics.append(f"context revision {revision_id} cannot be resolved")
|
|
756
|
+
continue
|
|
757
|
+
if len(exchanges) != 1:
|
|
758
|
+
invalid_exchange_membership_diagnostics.append(
|
|
759
|
+
f"context revision {revision_id} is claimed by multiple exchanges"
|
|
760
|
+
)
|
|
761
|
+
continue
|
|
762
|
+
exchange = exchanges[0]
|
|
763
|
+
if (
|
|
764
|
+
revision.parent_operation_id != exchange.operation_id
|
|
765
|
+
or revision.runtime_id != exchange.start.runtime_id
|
|
766
|
+
or revision.branch_id != exchange.start.branch_id
|
|
767
|
+
or revision.coverage_id != exchange.start.coverage_id
|
|
768
|
+
or revision.actor_id != exchange.start.actor_id
|
|
769
|
+
or revision.sequence >= exchange.start.sequence
|
|
770
|
+
):
|
|
771
|
+
invalid_exchange_membership_diagnostics.append(
|
|
772
|
+
f"context revision {revision_id} does not belong to its claiming exchange"
|
|
773
|
+
)
|
|
774
|
+
continue
|
|
775
|
+
valid_exchange_memberships[revision_id] = membership
|
|
776
|
+
diagnostics.extend(invalid_exchange_membership_diagnostics)
|
|
777
|
+
|
|
778
|
+
for tool in (node for node in nodes if node.family == "tool.operation" and not _skip_node(node)):
|
|
779
|
+
_check_cancelled(cancel_event)
|
|
780
|
+
parent = _payload_str(tool.start.payload, "parent_model_operation_id")
|
|
781
|
+
if parent is None:
|
|
782
|
+
diagnostics.append("tool call producer lacks parent_model_operation_id")
|
|
783
|
+
exact = False
|
|
784
|
+
else:
|
|
785
|
+
exact = (
|
|
786
|
+
_connect_unique(
|
|
787
|
+
by_operation,
|
|
788
|
+
parent,
|
|
789
|
+
tool.node_id,
|
|
790
|
+
edges,
|
|
791
|
+
diagnostics,
|
|
792
|
+
"tool call producer",
|
|
793
|
+
families=frozenset({"model.run", "model.cycle", "model.exchange"}),
|
|
794
|
+
)
|
|
795
|
+
and exact
|
|
796
|
+
)
|
|
797
|
+
call_item_id = _payload_str(tool.start.payload, "call_item_id")
|
|
798
|
+
result_item_id = _payload_str(tool.finish.payload, "result_item_id")
|
|
799
|
+
if result_item_id is None and _never_dispatched(tool):
|
|
800
|
+
# A call the kernel closed without dispatching may own no result
|
|
801
|
+
# item at all (a filtered call never gets one), so there is no
|
|
802
|
+
# fan-in edge to demand. One that does carry a result (invalid
|
|
803
|
+
# arguments, unknown tool) is consumed like any other and falls
|
|
804
|
+
# through to the ordinary requirement.
|
|
805
|
+
continue
|
|
806
|
+
if call_item_id is None or result_item_id is None:
|
|
807
|
+
diagnostics.append("tool result fan-in lacks call_item_id or result_item_id")
|
|
808
|
+
exact = False
|
|
809
|
+
continue
|
|
810
|
+
result_carrier_item_id = _payload_str(tool.finish.payload, "result_carrier_item_id")
|
|
811
|
+
fan_in_item_ids = {result_item_id}
|
|
812
|
+
if result_carrier_item_id is not None:
|
|
813
|
+
fan_in_item_ids.add(result_carrier_item_id)
|
|
814
|
+
consumers = {
|
|
815
|
+
exchange.node_id
|
|
816
|
+
for revision_id, memberships in valid_exchange_memberships.items()
|
|
817
|
+
if fan_in_item_ids.intersection(memberships)
|
|
818
|
+
for exchange in exchanges_by_revision.get(revision_id, ())
|
|
819
|
+
}
|
|
820
|
+
if not consumers:
|
|
821
|
+
needs_consumer = response_source_operation_id is None or parent != response_source_operation_id
|
|
822
|
+
carrier_mapping_missing = False
|
|
823
|
+
if needs_consumer and result_carrier_item_id is None:
|
|
824
|
+
result_carrier_item_id = mapped_carrier(result_item_id)
|
|
825
|
+
if result_carrier_item_id is None:
|
|
826
|
+
carrier_mapping_missing = True
|
|
827
|
+
else:
|
|
828
|
+
fan_in_item_ids.add(result_carrier_item_id)
|
|
829
|
+
consumers = {
|
|
830
|
+
exchange.node_id
|
|
831
|
+
for revision_id, memberships in valid_exchange_memberships.items()
|
|
832
|
+
if result_carrier_item_id in memberships
|
|
833
|
+
for exchange in exchanges_by_revision.get(revision_id, ())
|
|
834
|
+
}
|
|
835
|
+
if needs_consumer and not consumers:
|
|
836
|
+
compaction_runs = [
|
|
837
|
+
run_id
|
|
838
|
+
for run_id, consumption in compaction_consumption.by_run.items()
|
|
839
|
+
if fan_in_item_ids.intersection(consumption.item_ids)
|
|
840
|
+
]
|
|
841
|
+
if len(compaction_runs) == 1:
|
|
842
|
+
compaction_node_id = compaction_bridges.get(compaction_runs[0])
|
|
843
|
+
if compaction_node_id is not None:
|
|
844
|
+
consumers = {compaction_node_id}
|
|
845
|
+
elif len(compaction_runs) > 1:
|
|
846
|
+
diagnostics.append("tool result fan-in matches multiple Phase-4 compaction runs")
|
|
847
|
+
exact = False
|
|
848
|
+
if needs_consumer and not consumers:
|
|
849
|
+
if carrier_mapping_missing:
|
|
850
|
+
diagnostics.append("carrier mapping unavailable")
|
|
851
|
+
diagnostics.append("tool result fan-in cannot resolve a consuming context revision")
|
|
852
|
+
exact = False
|
|
853
|
+
if not consumers:
|
|
854
|
+
continue
|
|
855
|
+
edges[tool.node_id].update(consumers)
|
|
856
|
+
return exact, response_source_id
|
|
857
|
+
|
|
858
|
+
|
|
859
|
+
def _retry_neighbor_families(retry_mode: str | None) -> frozenset[str] | None:
|
|
860
|
+
if retry_mode in _RETRY_EXCHANGE_MODES:
|
|
861
|
+
return frozenset({"model.exchange"})
|
|
862
|
+
if retry_mode == RetryMode.RUN:
|
|
863
|
+
return frozenset({"model.run"})
|
|
864
|
+
return None
|
|
865
|
+
|
|
866
|
+
|
|
867
|
+
def _connect_unique(
|
|
868
|
+
by_operation: dict[str, list[_ResolvedNode]],
|
|
869
|
+
operation_id: str,
|
|
870
|
+
target_node_id: str | None,
|
|
871
|
+
edges: dict[str, set[str]],
|
|
872
|
+
diagnostics: list[str],
|
|
873
|
+
label: str,
|
|
874
|
+
*,
|
|
875
|
+
source: str | None = None,
|
|
876
|
+
families: frozenset[str] | None = None,
|
|
877
|
+
) -> bool:
|
|
878
|
+
candidates = [
|
|
879
|
+
candidate
|
|
880
|
+
for candidate in by_operation.get(operation_id, ())
|
|
881
|
+
if candidate.node_id != source
|
|
882
|
+
and candidate.node_id != target_node_id
|
|
883
|
+
and (families is None or candidate.family in families)
|
|
884
|
+
]
|
|
885
|
+
if len(candidates) != 1:
|
|
886
|
+
diagnostics.append(f"{label} cannot be resolved uniquely")
|
|
887
|
+
return False
|
|
888
|
+
if source is None and target_node_id is not None:
|
|
889
|
+
edges[candidates[0].node_id].add(target_node_id)
|
|
890
|
+
elif source is not None:
|
|
891
|
+
edges[source].add(candidates[0].node_id)
|
|
892
|
+
return True
|
|
893
|
+
|
|
894
|
+
|
|
895
|
+
def _connect_hook_dependencies(
|
|
896
|
+
nodes: list[_ResolvedNode],
|
|
897
|
+
edges: dict[str, set[str]],
|
|
898
|
+
*,
|
|
899
|
+
waited_hook_ids: frozenset[str] | None,
|
|
900
|
+
turn_root_id: str | None,
|
|
901
|
+
response_source_id: str | None,
|
|
902
|
+
response_terminal_id: str | None,
|
|
903
|
+
fork_edges: set[tuple[str, str]],
|
|
904
|
+
diagnostics: list[str],
|
|
905
|
+
) -> tuple[bool, bool]:
|
|
906
|
+
by_operation: dict[str, list[_ResolvedNode]] = defaultdict(list)
|
|
907
|
+
for node in nodes:
|
|
908
|
+
by_operation[node.operation_id].append(node)
|
|
909
|
+
runs = sorted((node for node in nodes if node.family == "model.run"), key=lambda node: node.start.sequence)
|
|
910
|
+
dag_exact = True
|
|
911
|
+
response_exact = True
|
|
912
|
+
after_tool_hooks: list[tuple[_ResolvedNode, _ResolvedNode]] = []
|
|
913
|
+
blocking_dispatches: dict[tuple[str, str | None, str], list[_ResolvedNode]] = defaultdict(list)
|
|
914
|
+
for hook in (node for node in nodes if node.family == "hook.operation"):
|
|
915
|
+
mode = _payload_str(hook.start.payload, "execution_mode")
|
|
916
|
+
event = _payload_str(hook.start.payload, "hook_event")
|
|
917
|
+
if mode not in _HOOK_EXECUTION_MODES:
|
|
918
|
+
continue
|
|
919
|
+
target = _payload_str(hook.start.payload, "target_operation_id")
|
|
920
|
+
if mode == "blocking" and event != HookEvent.USER_INTERRUPT:
|
|
921
|
+
scope = _hook_drain_scope(hook.start)
|
|
922
|
+
if event is None or scope is None:
|
|
923
|
+
diagnostics.append("blocking hook dispatch lacks field-based ordering evidence")
|
|
924
|
+
dag_exact = False
|
|
925
|
+
else:
|
|
926
|
+
blocking_dispatches[(event, target, scope)].append(hook)
|
|
927
|
+
target_families = _hook_target_families(event)
|
|
928
|
+
targets = [
|
|
929
|
+
candidate
|
|
930
|
+
for candidate in by_operation.get(target or "", ())
|
|
931
|
+
if candidate.family != "retry" and (not target_families or candidate.family in target_families)
|
|
932
|
+
]
|
|
933
|
+
if mode != "blocking" or event == HookEvent.USER_INTERRUPT:
|
|
934
|
+
if target is not None and len(targets) == 1:
|
|
935
|
+
edges[targets[0].node_id].add(hook.node_id)
|
|
936
|
+
fork_edges.add((targets[0].node_id, hook.node_id))
|
|
937
|
+
elif event == HookEvent.BEFORE_TOOL_CALL and len(targets) == 1:
|
|
938
|
+
edges[hook.node_id].add(targets[0].node_id)
|
|
939
|
+
elif event == HookEvent.BEFORE_TURN and runs:
|
|
940
|
+
edges[hook.node_id].add(runs[0].node_id)
|
|
941
|
+
elif event in {HookEvent.AFTER_TOOL_CALL, HookEvent.TOOL_ERROR} and len(targets) == 1:
|
|
942
|
+
after_tool_hooks.append((hook, targets[0]))
|
|
943
|
+
elif event == HookEvent.AFTER_TURN and response_source_id is not None:
|
|
944
|
+
edges[response_source_id].add(hook.node_id)
|
|
945
|
+
if response_terminal_id is not None:
|
|
946
|
+
edges[hook.node_id].add(response_terminal_id)
|
|
947
|
+
after_tool_hook_node_ids = {hook.node_id for hook, _ in after_tool_hooks}
|
|
948
|
+
for hook, target in after_tool_hooks:
|
|
949
|
+
for successor in tuple(edges.get(target.node_id, ())):
|
|
950
|
+
if successor not in after_tool_hook_node_ids:
|
|
951
|
+
edges[hook.node_id].add(successor)
|
|
952
|
+
for dispatch_hooks in blocking_dispatches.values():
|
|
953
|
+
ordered = sorted(dispatch_hooks, key=lambda hook: hook.start.sequence)
|
|
954
|
+
for previous, current in pairwise(ordered):
|
|
955
|
+
edges[previous.node_id].add(current.node_id)
|
|
956
|
+
if (
|
|
957
|
+
previous.start.runtime_id != current.start.runtime_id
|
|
958
|
+
or previous.start.branch_id != current.start.branch_id
|
|
959
|
+
or previous.finish.sequence >= current.start.sequence
|
|
960
|
+
or previous.finish.monotonic_ns > current.start.monotonic_ns
|
|
961
|
+
):
|
|
962
|
+
diagnostics.append("blocking hook dispatch has inconsistent serial intervals")
|
|
963
|
+
dag_exact = False
|
|
964
|
+
if waited_hook_ids is not None and response_terminal_id is not None:
|
|
965
|
+
hooks = {node.operation_id: node for node in nodes if node.family == "hook.operation"}
|
|
966
|
+
for operation_id in waited_hook_ids:
|
|
967
|
+
hook = hooks.get(operation_id)
|
|
968
|
+
if hook is None:
|
|
969
|
+
diagnostics.append("turn response fence names an unknown hook operation")
|
|
970
|
+
response_exact = False
|
|
971
|
+
else:
|
|
972
|
+
edges[hook.node_id].add(response_terminal_id)
|
|
973
|
+
if turn_root_id is None or not _reachable(edges, turn_root_id, hook.node_id):
|
|
974
|
+
diagnostics.append("waited async hook lacks a typed fork path from the turn root")
|
|
975
|
+
response_exact = False
|
|
976
|
+
return dag_exact, response_exact
|
|
977
|
+
|
|
978
|
+
|
|
979
|
+
def _reachable(edges: dict[str, set[str]], source: str, target: str) -> bool:
|
|
980
|
+
pending = [source]
|
|
981
|
+
seen: set[str] = set()
|
|
982
|
+
while pending:
|
|
983
|
+
node_id = pending.pop()
|
|
984
|
+
if node_id == target:
|
|
985
|
+
return True
|
|
986
|
+
if node_id in seen:
|
|
987
|
+
continue
|
|
988
|
+
seen.add(node_id)
|
|
989
|
+
pending.extend(edges.get(node_id, ()))
|
|
990
|
+
return False
|
|
991
|
+
|
|
992
|
+
|
|
993
|
+
def _critical_paths(
|
|
994
|
+
nodes: list[_ResolvedNode],
|
|
995
|
+
slices: tuple[TimeSlice, ...],
|
|
996
|
+
dependency: _DependencyProof,
|
|
997
|
+
*,
|
|
998
|
+
cancel_event: Event | None,
|
|
999
|
+
) -> _CriticalPaths:
|
|
1000
|
+
compute_intervals, response_intervals = _critical_path_interval_maps(nodes, slices, cancel_event=cancel_event)
|
|
1001
|
+
terminal_id = dependency.response_terminal_id
|
|
1002
|
+
if terminal_id is not None:
|
|
1003
|
+
compute_intervals[terminal_id] = []
|
|
1004
|
+
response_intervals[terminal_id] = [
|
|
1005
|
+
(item.start_ns, item.end_ns) for item in slices if item.operation_id is None and item.response_weight
|
|
1006
|
+
]
|
|
1007
|
+
compute = _longest_interval_path(
|
|
1008
|
+
compute_intervals,
|
|
1009
|
+
dependency.edges,
|
|
1010
|
+
parents=dependency.parents,
|
|
1011
|
+
fork_edges=dependency.fork_edges,
|
|
1012
|
+
cancel_event=cancel_event,
|
|
1013
|
+
)
|
|
1014
|
+
if dependency.turn_root_id is None or terminal_id is None:
|
|
1015
|
+
response = _PathResolution(None, compute.acyclic, True)
|
|
1016
|
+
response_reachable = False
|
|
1017
|
+
else:
|
|
1018
|
+
response = _longest_interval_path(
|
|
1019
|
+
response_intervals,
|
|
1020
|
+
dependency.edges,
|
|
1021
|
+
parents=dependency.parents,
|
|
1022
|
+
fork_edges=dependency.fork_edges,
|
|
1023
|
+
root_id=dependency.turn_root_id,
|
|
1024
|
+
terminal_id=terminal_id,
|
|
1025
|
+
cancel_event=cancel_event,
|
|
1026
|
+
)
|
|
1027
|
+
response_reachable = _reachable(dependency.edges, dependency.turn_root_id, terminal_id)
|
|
1028
|
+
return _CriticalPaths(
|
|
1029
|
+
compute_ns=compute.value,
|
|
1030
|
+
response_ns=response.value,
|
|
1031
|
+
acyclic=compute.acyclic and response.acyclic,
|
|
1032
|
+
response_reachable=response_reachable,
|
|
1033
|
+
compute_bounded=compute.bounded,
|
|
1034
|
+
response_bounded=response.bounded,
|
|
1035
|
+
)
|
|
1036
|
+
|
|
1037
|
+
|
|
1038
|
+
def _critical_path_interval_maps(
|
|
1039
|
+
nodes: list[_ResolvedNode],
|
|
1040
|
+
slices: tuple[TimeSlice, ...],
|
|
1041
|
+
*,
|
|
1042
|
+
cancel_event: Event | None,
|
|
1043
|
+
) -> tuple[dict[str, list[tuple[int, int]]], dict[str, list[tuple[int, int]]]]:
|
|
1044
|
+
compute_intervals = {node.node_id: [] for node in nodes}
|
|
1045
|
+
response_intervals = {node.node_id: [] for node in nodes}
|
|
1046
|
+
for item in slices:
|
|
1047
|
+
_check_cancelled(cancel_event)
|
|
1048
|
+
if item.operation_id is None:
|
|
1049
|
+
continue
|
|
1050
|
+
candidates = [node for node in nodes if node.operation_id == item.operation_id]
|
|
1051
|
+
if len(candidates) != 1:
|
|
1052
|
+
candidates = [
|
|
1053
|
+
node
|
|
1054
|
+
for node in candidates
|
|
1055
|
+
if (attributes := _slice_attributes(node)) is not None and attributes[4] == item.owner
|
|
1056
|
+
]
|
|
1057
|
+
if len(candidates) != 1:
|
|
1058
|
+
continue
|
|
1059
|
+
node_id = candidates[0].node_id
|
|
1060
|
+
if item.compute_weight:
|
|
1061
|
+
compute_intervals[node_id].append((item.start_ns, item.end_ns))
|
|
1062
|
+
if item.response_weight:
|
|
1063
|
+
response_intervals[node_id].append((item.start_ns, item.end_ns))
|
|
1064
|
+
return compute_intervals, response_intervals
|
|
1065
|
+
|
|
1066
|
+
|
|
1067
|
+
def _failed_tool_cp_contributions(
|
|
1068
|
+
nodes: list[_ResolvedNode],
|
|
1069
|
+
slices: tuple[TimeSlice, ...],
|
|
1070
|
+
dependency: _DependencyProof,
|
|
1071
|
+
*,
|
|
1072
|
+
response_cp: int,
|
|
1073
|
+
cancel_event: Event | None,
|
|
1074
|
+
) -> dict[str, int]:
|
|
1075
|
+
groups = {
|
|
1076
|
+
node.operation_id: frozenset((node.node_id,))
|
|
1077
|
+
for node in nodes
|
|
1078
|
+
if node.family == "tool.operation" and tool_failed(_payload_str(node.finish.payload, "outcome"))
|
|
1079
|
+
}
|
|
1080
|
+
return {
|
|
1081
|
+
operation_id: contribution
|
|
1082
|
+
for operation_id, contribution in _grouped_cp_contributions(
|
|
1083
|
+
nodes,
|
|
1084
|
+
slices,
|
|
1085
|
+
dependency,
|
|
1086
|
+
groups=groups,
|
|
1087
|
+
response_cp=response_cp,
|
|
1088
|
+
cancel_event=cancel_event,
|
|
1089
|
+
).items()
|
|
1090
|
+
if contribution
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
|
|
1094
|
+
def _server_tool_cp_contributions(
|
|
1095
|
+
intermediate: _Intermediate,
|
|
1096
|
+
nodes: list[_ResolvedNode],
|
|
1097
|
+
slices: tuple[TimeSlice, ...],
|
|
1098
|
+
dependency: _DependencyProof,
|
|
1099
|
+
*,
|
|
1100
|
+
response_cp: int,
|
|
1101
|
+
cancel_event: Event | None,
|
|
1102
|
+
) -> dict[str, int]:
|
|
1103
|
+
grouped: dict[str, set[str]] = defaultdict(set)
|
|
1104
|
+
tool_nodes = intermediate.nodes.get("tool.operation", {})
|
|
1105
|
+
for node in nodes:
|
|
1106
|
+
_check_cancelled(cancel_event)
|
|
1107
|
+
if node.family != "tool.operation":
|
|
1108
|
+
continue
|
|
1109
|
+
raw_node = tool_nodes.get(node.operation_id)
|
|
1110
|
+
context = _tool_context_for_start(raw_node, node.start) if raw_node is not None else None
|
|
1111
|
+
if context is not None and context.server_name is not None:
|
|
1112
|
+
grouped[context.server_name].add(node.node_id)
|
|
1113
|
+
return _grouped_cp_contributions(
|
|
1114
|
+
nodes,
|
|
1115
|
+
slices,
|
|
1116
|
+
dependency,
|
|
1117
|
+
groups={name: frozenset(node_ids) for name, node_ids in grouped.items()},
|
|
1118
|
+
response_cp=response_cp,
|
|
1119
|
+
cancel_event=cancel_event,
|
|
1120
|
+
)
|
|
1121
|
+
|
|
1122
|
+
|
|
1123
|
+
def _grouped_cp_contributions(
|
|
1124
|
+
nodes: list[_ResolvedNode],
|
|
1125
|
+
slices: tuple[TimeSlice, ...],
|
|
1126
|
+
dependency: _DependencyProof,
|
|
1127
|
+
*,
|
|
1128
|
+
groups: dict[str, frozenset[str]],
|
|
1129
|
+
response_cp: int,
|
|
1130
|
+
cancel_event: Event | None,
|
|
1131
|
+
) -> dict[str, int]:
|
|
1132
|
+
"""Resolve one leave-one-group-out response path per supplied group."""
|
|
1133
|
+
terminal_id = dependency.response_terminal_id
|
|
1134
|
+
root_id = dependency.turn_root_id
|
|
1135
|
+
if not groups or root_id is None or terminal_id is None or response_cp <= 0:
|
|
1136
|
+
return {}
|
|
1137
|
+
_, response_intervals = _critical_path_interval_maps(nodes, slices, cancel_event=cancel_event)
|
|
1138
|
+
response_intervals[terminal_id] = [
|
|
1139
|
+
(item.start_ns, item.end_ns) for item in slices if item.operation_id is None and item.response_weight
|
|
1140
|
+
]
|
|
1141
|
+
children: dict[str, set[str]] = defaultdict(set)
|
|
1142
|
+
for child, parent in dependency.parents.items():
|
|
1143
|
+
children[parent].add(child)
|
|
1144
|
+
contributions: dict[str, int] = {}
|
|
1145
|
+
for group, node_ids in groups.items():
|
|
1146
|
+
_check_cancelled(cancel_event)
|
|
1147
|
+
without = dict(response_intervals)
|
|
1148
|
+
# A member's contribution spans everything it displaced: the approval
|
|
1149
|
+
# and sub-agent inside a tool left with it.
|
|
1150
|
+
for node_id in node_ids:
|
|
1151
|
+
without[node_id] = []
|
|
1152
|
+
for descendant in _descendants(node_id, children):
|
|
1153
|
+
without[descendant] = []
|
|
1154
|
+
resolved = _longest_interval_path(
|
|
1155
|
+
without,
|
|
1156
|
+
dependency.edges,
|
|
1157
|
+
parents=dependency.parents,
|
|
1158
|
+
fork_edges=dependency.fork_edges,
|
|
1159
|
+
root_id=root_id,
|
|
1160
|
+
terminal_id=terminal_id,
|
|
1161
|
+
cancel_event=cancel_event,
|
|
1162
|
+
)
|
|
1163
|
+
if resolved.value is not None and resolved.acyclic and resolved.bounded:
|
|
1164
|
+
contributions[group] = max(0, response_cp - resolved.value)
|
|
1165
|
+
return contributions
|
|
1166
|
+
|
|
1167
|
+
|
|
1168
|
+
def _wall_partition(
|
|
1169
|
+
bounds: tuple[int, int],
|
|
1170
|
+
slices: tuple[TimeSlice, ...],
|
|
1171
|
+
*,
|
|
1172
|
+
cancel_event: Event | None,
|
|
1173
|
+
) -> tuple[dict[WallBucket, int], tuple[tuple[int, int], ...]]:
|
|
1174
|
+
points = {bounds[0], bounds[1]}
|
|
1175
|
+
changes: dict[int, Counter[WallBucket]] = defaultdict(Counter)
|
|
1176
|
+
for item in slices:
|
|
1177
|
+
_check_cancelled(cancel_event)
|
|
1178
|
+
if item.wall_bucket is None:
|
|
1179
|
+
continue
|
|
1180
|
+
start = max(bounds[0], item.start_ns)
|
|
1181
|
+
end = min(bounds[1], item.end_ns)
|
|
1182
|
+
if end <= start:
|
|
1183
|
+
continue
|
|
1184
|
+
points.add(start)
|
|
1185
|
+
points.add(end)
|
|
1186
|
+
changes[start][item.wall_bucket] += 1
|
|
1187
|
+
changes[end][item.wall_bucket] -= 1
|
|
1188
|
+
ordered = sorted(points)
|
|
1189
|
+
durations = dict.fromkeys(WallBucket, 0)
|
|
1190
|
+
idle: list[tuple[int, int]] = []
|
|
1191
|
+
active_counts: Counter[WallBucket] = Counter()
|
|
1192
|
+
for left, right in pairwise(ordered):
|
|
1193
|
+
_check_cancelled(cancel_event)
|
|
1194
|
+
for bucket, delta in changes.get(left, {}).items():
|
|
1195
|
+
count = active_counts[bucket] + delta
|
|
1196
|
+
if count > 0:
|
|
1197
|
+
active_counts[bucket] = count
|
|
1198
|
+
else:
|
|
1199
|
+
active_counts.pop(bucket, None)
|
|
1200
|
+
if right <= left:
|
|
1201
|
+
continue
|
|
1202
|
+
bucket = max(active_counts, key=lambda value: _WALL_PRIORITY[value]) if active_counts else WallBucket.IDLE
|
|
1203
|
+
durations[bucket] += right - left
|
|
1204
|
+
if bucket is WallBucket.IDLE:
|
|
1205
|
+
idle.append((left, right))
|
|
1206
|
+
return durations, interval_union(idle)
|
|
1207
|
+
|
|
1208
|
+
|
|
1209
|
+
def _descendants(node_id: str, children: dict[str, set[str]]) -> set[str]:
|
|
1210
|
+
result: set[str] = set()
|
|
1211
|
+
pending = list(children.get(node_id, set()))
|
|
1212
|
+
while pending:
|
|
1213
|
+
child = pending.pop()
|
|
1214
|
+
if child in result:
|
|
1215
|
+
continue
|
|
1216
|
+
result.add(child)
|
|
1217
|
+
pending.extend(children.get(child, set()))
|
|
1218
|
+
return result
|
|
1219
|
+
|
|
1220
|
+
|
|
1221
|
+
def _has_cycle(children: dict[str, set[str]]) -> bool:
|
|
1222
|
+
visiting: set[str] = set()
|
|
1223
|
+
visited: set[str] = set()
|
|
1224
|
+
|
|
1225
|
+
def visit(node_id: str) -> bool:
|
|
1226
|
+
if node_id in visiting:
|
|
1227
|
+
return True
|
|
1228
|
+
if node_id in visited:
|
|
1229
|
+
return False
|
|
1230
|
+
visiting.add(node_id)
|
|
1231
|
+
if any(visit(child) for child in children.get(node_id, set())):
|
|
1232
|
+
return True
|
|
1233
|
+
visiting.remove(node_id)
|
|
1234
|
+
visited.add(node_id)
|
|
1235
|
+
return False
|
|
1236
|
+
|
|
1237
|
+
return any(visit(node_id) for node_id in children)
|