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,762 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""One live session's trajectory recorder.
|
|
4
|
+
|
|
5
|
+
:class:`SessionTrajectory` is the engine-owned :class:`TrajectorySink`. It is
|
|
6
|
+
created cheaply whenever a session becomes current and activates itself on
|
|
7
|
+
the first event: ensure the owner-only ``<session>/trajectory/`` directory,
|
|
8
|
+
take the per-session writer lease, recover a torn tail under the session
|
|
9
|
+
write lock, then start the single writer and record the coverage prelude
|
|
10
|
+
(``coverage.started`` / ``runtime.started`` / ``runtime.recovered`` /
|
|
11
|
+
``session.started``). Activation failures never fail the session — the
|
|
12
|
+
recorder simply stays disabled and every ``emit`` answers ``DEGRADED``.
|
|
13
|
+
|
|
14
|
+
Lazy activation keeps the session directory unmaterialized for sessions that
|
|
15
|
+
never produce an event, which is what the empty-session cleanup relies on;
|
|
16
|
+
the engine closes the recorder *before* that cleanup runs, so a writer never
|
|
17
|
+
outlives its directory.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import asyncio
|
|
23
|
+
import contextlib
|
|
24
|
+
import logging
|
|
25
|
+
import os
|
|
26
|
+
import threading
|
|
27
|
+
from collections.abc import Callable, Mapping
|
|
28
|
+
from dataclasses import dataclass, replace
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
from typing import Any, Final
|
|
31
|
+
|
|
32
|
+
from chrys.foundation.platform.files import SecureFileError, secure_open_owner_only_append
|
|
33
|
+
from chrys.foundation.trajectory.context import TrajectoryContext, main_actor, side_call_actor, sub_agent_actor
|
|
34
|
+
from chrys.foundation.trajectory.envelope import (
|
|
35
|
+
INT64_MAX,
|
|
36
|
+
SYSTEM_ACTOR,
|
|
37
|
+
Actor,
|
|
38
|
+
EventDraft,
|
|
39
|
+
monotonic_now_ns,
|
|
40
|
+
utc_now_rfc3339,
|
|
41
|
+
)
|
|
42
|
+
from chrys.foundation.trajectory.event_types import CoverageReason, EventType
|
|
43
|
+
from chrys.foundation.trajectory.finalizers import RecordingFinalizers
|
|
44
|
+
from chrys.foundation.trajectory.fingerprint import DOMAIN_WORKSPACE, fingerprint_text
|
|
45
|
+
from chrys.foundation.trajectory.ids import new_analytics_id
|
|
46
|
+
from chrys.foundation.trajectory.keys import ensure_owner_only_directory, load_or_create_fingerprint_key
|
|
47
|
+
from chrys.foundation.trajectory.lease import WRITER_LEASE_FILE_NAME, WriterLease
|
|
48
|
+
from chrys.foundation.trajectory.recovery import (
|
|
49
|
+
RecoveryScan,
|
|
50
|
+
scan_for_recovery,
|
|
51
|
+
scan_open_file,
|
|
52
|
+
truncate_torn_tail,
|
|
53
|
+
)
|
|
54
|
+
from chrys.foundation.trajectory.revisions import RevisionRegistry
|
|
55
|
+
from chrys.foundation.trajectory.writer import (
|
|
56
|
+
DEFAULT_CLOSE_JOIN_TIMEOUT_SECONDS,
|
|
57
|
+
DEFAULT_WRITE_ACK_TIMEOUT_SECONDS,
|
|
58
|
+
EmitResult,
|
|
59
|
+
FdWriteBackend,
|
|
60
|
+
TrajectoryWriter,
|
|
61
|
+
)
|
|
62
|
+
from chrys.foundation.util.lock import FileLock
|
|
63
|
+
|
|
64
|
+
logger = logging.getLogger(__name__)
|
|
65
|
+
|
|
66
|
+
TRAJECTORY_DIR_NAME: Final = "trajectory"
|
|
67
|
+
TRAJECTORY_EVENTS_FILE_NAME: Final = "events.jsonl"
|
|
68
|
+
ACTIVATION_LOCK_TIMEOUT_SECONDS: Final = 10.0
|
|
69
|
+
MAX_ACTIVATION_ATTEMPTS: Final = 2
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class TrajectoryDisabledReason:
|
|
73
|
+
"""Why a session records nothing (diagnostics only; never persisted)."""
|
|
74
|
+
|
|
75
|
+
NO_SESSION_DIR = "no_session_dir"
|
|
76
|
+
LEASE_HELD = "lease_held"
|
|
77
|
+
KEY_UNAVAILABLE = "key_unavailable"
|
|
78
|
+
ACTIVATION_FAILED = "activation_failed"
|
|
79
|
+
SCHEMA_TOO_NEW = "schema_too_new"
|
|
80
|
+
UNREADABLE_TAIL = "unreadable_tail"
|
|
81
|
+
SEQUENCE_EXHAUSTED = "sequence_exhausted"
|
|
82
|
+
CLOSED = "closed"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@dataclass(frozen=True, slots=True)
|
|
86
|
+
class SessionStartInfo:
|
|
87
|
+
"""Inputs of ``session.started``; the recorder adds environment and fingerprints."""
|
|
88
|
+
|
|
89
|
+
primary_cwd: str
|
|
90
|
+
agent_profile_fingerprint: str
|
|
91
|
+
model_profile_fingerprint: str
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def trajectory_dir(session_dir: Path) -> Path:
|
|
95
|
+
return session_dir / TRAJECTORY_DIR_NAME
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def trajectory_events_path(session_dir: Path) -> Path:
|
|
99
|
+
return trajectory_dir(session_dir) / TRAJECTORY_EVENTS_FILE_NAME
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def trajectory_lease_path(session_dir: Path) -> Path:
|
|
103
|
+
return trajectory_dir(session_dir) / WRITER_LEASE_FILE_NAME
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def runtime_environment() -> dict[str, str]:
|
|
107
|
+
"""``app_version`` / ``os_name`` / ``arch`` as stamped on runtime and session starts."""
|
|
108
|
+
from chrys import __version__
|
|
109
|
+
from chrys.foundation.platform import get_platform
|
|
110
|
+
|
|
111
|
+
platform = get_platform()
|
|
112
|
+
return {"app_version": __version__, "os_name": platform.os_name, "arch": platform.arch}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _session_has_persisted_state(session_dir: Path) -> bool:
|
|
116
|
+
from chrys.service.state.store import session_dir_has_artifacts
|
|
117
|
+
|
|
118
|
+
return session_dir_has_artifacts(session_dir)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@dataclass(frozen=True, slots=True)
|
|
122
|
+
class _Activation:
|
|
123
|
+
"""The writer and the verdict on the activation that made it, as one answer.
|
|
124
|
+
|
|
125
|
+
Both are written by an activation running on another thread, so reading
|
|
126
|
+
them one at a time can mix two attempts: a producer that read
|
|
127
|
+
``disabled_reason`` before a failed activation and ``writer`` after it
|
|
128
|
+
would write into a log whose runtime was never announced, and one that
|
|
129
|
+
read ``writer`` before a successful activation would give up on a session
|
|
130
|
+
that is recording. They are replaced together and read together.
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
writer: TrajectoryWriter | None = None
|
|
134
|
+
disabled_reason: str | None = None
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class SessionTrajectory:
|
|
138
|
+
"""Engine-owned recorder for one session (see module docstring)."""
|
|
139
|
+
|
|
140
|
+
def __init__(
|
|
141
|
+
self,
|
|
142
|
+
*,
|
|
143
|
+
session_id: str,
|
|
144
|
+
session_dir: Path | None,
|
|
145
|
+
write_lock_path: Path | None = None,
|
|
146
|
+
config_dir: Path | None = None,
|
|
147
|
+
session_start_info: Callable[[], SessionStartInfo | None] | None = None,
|
|
148
|
+
write_ack_timeout: float = DEFAULT_WRITE_ACK_TIMEOUT_SECONDS,
|
|
149
|
+
close_join_timeout: float = DEFAULT_CLOSE_JOIN_TIMEOUT_SECONDS,
|
|
150
|
+
persisted_state_probe: Callable[[Path], bool] = _session_has_persisted_state,
|
|
151
|
+
on_activation_failed: Callable[[str], None] | None = None,
|
|
152
|
+
) -> None:
|
|
153
|
+
self._session_id = session_id
|
|
154
|
+
self._session_dir = session_dir
|
|
155
|
+
self._write_lock_path = write_lock_path
|
|
156
|
+
self._config_dir = config_dir
|
|
157
|
+
self._session_start_info = session_start_info
|
|
158
|
+
self._write_ack_timeout = write_ack_timeout
|
|
159
|
+
self._close_join_timeout = close_join_timeout
|
|
160
|
+
self._persisted_state_probe = persisted_state_probe
|
|
161
|
+
self._on_activation_failed = on_activation_failed
|
|
162
|
+
|
|
163
|
+
self._main_actor = main_actor(session_id)
|
|
164
|
+
# The prelude is written when the log opens, and the log opens on the
|
|
165
|
+
# first event the session records — which can be long after the
|
|
166
|
+
# runtime took this session. Stamped here, ``runtime.started`` says
|
|
167
|
+
# when the runtime started rather than when it first had something to
|
|
168
|
+
# say, and an idle stretch before the first turn stays visible.
|
|
169
|
+
self._bound_at = utc_now_rfc3339()
|
|
170
|
+
self._bound_monotonic_ns = monotonic_now_ns()
|
|
171
|
+
# Context-revision chains live for the whole runtime so every run's
|
|
172
|
+
# exchanges extend the same per-actor lineage (delta, not checkpoint).
|
|
173
|
+
self._revisions = RevisionRegistry()
|
|
174
|
+
self._finalizers = RecordingFinalizers()
|
|
175
|
+
self._activation_lock = threading.Lock()
|
|
176
|
+
self._activation = _Activation()
|
|
177
|
+
self._activation_attempts = 0
|
|
178
|
+
self._activation_failure_reported = False
|
|
179
|
+
# A writer whose prelude failed belongs only to the thread holding
|
|
180
|
+
# ``_activation_lock``. Publishing it through ``_activation`` would
|
|
181
|
+
# expose a transient ACTIVATION_FAILED verdict to lock-free emitters,
|
|
182
|
+
# which would then drop events even if the retry succeeds.
|
|
183
|
+
self._failed_activation_writer: TrajectoryWriter | None = None
|
|
184
|
+
self._lease: WriterLease | None = None
|
|
185
|
+
self._key: bytes | None = None
|
|
186
|
+
self._branch_id: str | None = None
|
|
187
|
+
self._coverage_reason: str | None = None
|
|
188
|
+
# Tail repair changes the file before a prelude can fail. A retry's
|
|
189
|
+
# rescan sees the repaired file, so keep the removed-byte evidence
|
|
190
|
+
# independently of the current scan until a runtime can report it.
|
|
191
|
+
self._recovered_truncated_bytes = 0
|
|
192
|
+
self._closed = False
|
|
193
|
+
self._worker_stuck = False
|
|
194
|
+
self._recovery: RecoveryScan | None = None
|
|
195
|
+
self._background_waits: set[asyncio.Task[Any]] = set()
|
|
196
|
+
self._close_task: asyncio.Task[bool] | None = None
|
|
197
|
+
|
|
198
|
+
# ----------------------------------------------------------------- props
|
|
199
|
+
|
|
200
|
+
@property
|
|
201
|
+
def session_id(self) -> str:
|
|
202
|
+
return self._session_id
|
|
203
|
+
|
|
204
|
+
@property
|
|
205
|
+
def session_dir(self) -> Path | None:
|
|
206
|
+
return self._session_dir
|
|
207
|
+
|
|
208
|
+
@property
|
|
209
|
+
def main_actor(self) -> Actor:
|
|
210
|
+
return self._main_actor
|
|
211
|
+
|
|
212
|
+
@property
|
|
213
|
+
def is_active(self) -> bool:
|
|
214
|
+
# A terminal cleanup failure can still leave its writer published so
|
|
215
|
+
# ``close`` can reach the worker; that runtime is not recording.
|
|
216
|
+
state = self._activation
|
|
217
|
+
return state.writer is not None and state.disabled_reason is None and not self._closed
|
|
218
|
+
|
|
219
|
+
@property
|
|
220
|
+
def is_closed(self) -> bool:
|
|
221
|
+
return self._closed
|
|
222
|
+
|
|
223
|
+
@property
|
|
224
|
+
def disabled_reason(self) -> str | None:
|
|
225
|
+
return self._activation.disabled_reason
|
|
226
|
+
|
|
227
|
+
@property
|
|
228
|
+
def runtime_id(self) -> str | None:
|
|
229
|
+
writer = self._activation.writer
|
|
230
|
+
return writer.runtime_id if writer is not None else None
|
|
231
|
+
|
|
232
|
+
@property
|
|
233
|
+
def coverage_reason(self) -> str | None:
|
|
234
|
+
return self._coverage_reason
|
|
235
|
+
|
|
236
|
+
@property
|
|
237
|
+
def branch_id(self) -> str | None:
|
|
238
|
+
return self._branch_id
|
|
239
|
+
|
|
240
|
+
@property
|
|
241
|
+
def fingerprint_key(self) -> bytes | None:
|
|
242
|
+
return self._key
|
|
243
|
+
|
|
244
|
+
@property
|
|
245
|
+
def worker_stuck(self) -> bool:
|
|
246
|
+
"""Whether the last close abandoned a worker that still holds the lease."""
|
|
247
|
+
return self._worker_stuck
|
|
248
|
+
|
|
249
|
+
@property
|
|
250
|
+
def writer(self) -> TrajectoryWriter | None:
|
|
251
|
+
return self._activation.writer
|
|
252
|
+
|
|
253
|
+
def side_call_actor(self, role: str) -> Actor:
|
|
254
|
+
return side_call_actor(self._session_id, role)
|
|
255
|
+
|
|
256
|
+
def main_actor_draft(
|
|
257
|
+
self,
|
|
258
|
+
event_type: str,
|
|
259
|
+
*,
|
|
260
|
+
payload: Mapping[str, Any] | None = None,
|
|
261
|
+
turn_id: str | None = None,
|
|
262
|
+
operation_id: str | None = None,
|
|
263
|
+
parent_operation_id: str | None = None,
|
|
264
|
+
) -> EventDraft:
|
|
265
|
+
"""Build a draft attributed to the session's main agent."""
|
|
266
|
+
return EventDraft(
|
|
267
|
+
event_type=event_type,
|
|
268
|
+
actor=self._main_actor,
|
|
269
|
+
payload=payload if payload is not None else {},
|
|
270
|
+
turn_id=turn_id,
|
|
271
|
+
operation_id=operation_id,
|
|
272
|
+
parent_operation_id=parent_operation_id,
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
def context(self, *, turn_id: str | None = None, run_operation_id: str | None = None) -> TrajectoryContext:
|
|
276
|
+
"""An ambient :class:`TrajectoryContext` for the main agent of this session."""
|
|
277
|
+
return TrajectoryContext(
|
|
278
|
+
sink=self,
|
|
279
|
+
session_id=self._session_id,
|
|
280
|
+
actor=self._main_actor,
|
|
281
|
+
turn_id=turn_id,
|
|
282
|
+
run_operation_id=run_operation_id,
|
|
283
|
+
revisions=self._revisions,
|
|
284
|
+
finalizers=self._finalizers,
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
def sub_agent_actor(self, invocation_id: str) -> Actor:
|
|
288
|
+
return sub_agent_actor(self._session_id, invocation_id)
|
|
289
|
+
|
|
290
|
+
# ------------------------------------------------------------------ emit
|
|
291
|
+
|
|
292
|
+
async def emit(
|
|
293
|
+
self, draft: EventDraft, *, payload_factory: Callable[[int], Mapping[str, Any]] | None = None
|
|
294
|
+
) -> EmitResult:
|
|
295
|
+
state = self._activation
|
|
296
|
+
if state.disabled_reason is not None:
|
|
297
|
+
return EmitResult.DEGRADED
|
|
298
|
+
if state.writer is not None:
|
|
299
|
+
return await state.writer.emit(draft, payload_factory=payload_factory)
|
|
300
|
+
if self._closed:
|
|
301
|
+
return EmitResult.DEGRADED
|
|
302
|
+
# Opening the log and committing this draft ride the same thread
|
|
303
|
+
# call, so a producer cancelled while the log is still opening
|
|
304
|
+
# keeps the line an active writer would already have committed
|
|
305
|
+
# for it — otherwise the event that triggered activation is the
|
|
306
|
+
# one event activation loses, and nothing accounts for it.
|
|
307
|
+
return await asyncio.to_thread(self.emit_blocking, draft, payload_factory=payload_factory)
|
|
308
|
+
|
|
309
|
+
def emit_blocking(
|
|
310
|
+
self, draft: EventDraft, *, payload_factory: Callable[[int], Mapping[str, Any]] | None = None
|
|
311
|
+
) -> EmitResult:
|
|
312
|
+
state = self._activation
|
|
313
|
+
if state.writer is None and state.disabled_reason is None and not self._closed:
|
|
314
|
+
# A no-op when another thread got there first; it holds the
|
|
315
|
+
# activation lock either way, so the read below is of a settled
|
|
316
|
+
# state and never of an activation halfway through.
|
|
317
|
+
self._activate()
|
|
318
|
+
state = self._activation
|
|
319
|
+
if state.writer is None or state.disabled_reason is not None:
|
|
320
|
+
# Failed activation cleanup can leave a terminal writer behind:
|
|
321
|
+
# the runtime it belongs to was never announced.
|
|
322
|
+
return EmitResult.DEGRADED
|
|
323
|
+
return state.writer.emit_blocking(draft, payload_factory=payload_factory)
|
|
324
|
+
|
|
325
|
+
def emit_soon(
|
|
326
|
+
self, draft: EventDraft, *, payload_factory: Callable[[int], Mapping[str, Any]] | None = None
|
|
327
|
+
) -> EmitResult | None:
|
|
328
|
+
"""Queue *draft* in sequence order from a synchronous call site on the event loop.
|
|
329
|
+
|
|
330
|
+
With an active writer the sequence is taken right here (so ordering
|
|
331
|
+
against later ``emit`` calls holds) and the bounded ack wait runs in
|
|
332
|
+
a background task. Before activation the whole ``emit`` is scheduled
|
|
333
|
+
instead — activation must not block the loop — which only affects
|
|
334
|
+
events that precede the recorder's first awaited event.
|
|
335
|
+
|
|
336
|
+
``None`` says the line took its sequence; a result says the sink
|
|
337
|
+
refused it outright and no line will ever carry it.
|
|
338
|
+
"""
|
|
339
|
+
state = self._activation
|
|
340
|
+
if state.disabled_reason is not None:
|
|
341
|
+
return EmitResult.DEGRADED
|
|
342
|
+
writer = state.writer
|
|
343
|
+
if writer is not None:
|
|
344
|
+
submitted = writer.submit(draft, payload_factory=payload_factory)
|
|
345
|
+
if isinstance(submitted.result, EmitResult):
|
|
346
|
+
return submitted.result
|
|
347
|
+
try:
|
|
348
|
+
loop = asyncio.get_running_loop()
|
|
349
|
+
except RuntimeError:
|
|
350
|
+
return None
|
|
351
|
+
task = loop.create_task(writer.wait(submitted))
|
|
352
|
+
self._background_waits.add(task)
|
|
353
|
+
task.add_done_callback(self._background_waits.discard)
|
|
354
|
+
return None
|
|
355
|
+
if self._closed:
|
|
356
|
+
return EmitResult.DEGRADED
|
|
357
|
+
try:
|
|
358
|
+
loop = asyncio.get_running_loop()
|
|
359
|
+
except RuntimeError:
|
|
360
|
+
return self.emit_blocking(draft, payload_factory=payload_factory)
|
|
361
|
+
task = loop.create_task(self.emit(draft, payload_factory=payload_factory))
|
|
362
|
+
self._background_waits.add(task)
|
|
363
|
+
task.add_done_callback(self._background_waits.discard)
|
|
364
|
+
return None
|
|
365
|
+
|
|
366
|
+
async def ensure_active(self) -> bool:
|
|
367
|
+
"""Open the log now if it has not been opened yet; return whether it is recording.
|
|
368
|
+
|
|
369
|
+
Callers that must know the live branch — or place an event against a
|
|
370
|
+
known sequence — cannot wait for the first emit to activate lazily.
|
|
371
|
+
"""
|
|
372
|
+
if self._needs_activation():
|
|
373
|
+
await asyncio.to_thread(self._activate)
|
|
374
|
+
return self.is_active
|
|
375
|
+
|
|
376
|
+
async def checkpoint(self) -> EmitResult:
|
|
377
|
+
state = self._activation
|
|
378
|
+
if state.writer is None or not self.is_active:
|
|
379
|
+
return EmitResult.DEGRADED
|
|
380
|
+
return await state.writer.checkpoint()
|
|
381
|
+
|
|
382
|
+
def set_branch_id(self, branch_id: str) -> None:
|
|
383
|
+
self._branch_id = branch_id
|
|
384
|
+
writer = self._activation.writer
|
|
385
|
+
if writer is not None:
|
|
386
|
+
writer.set_branch_id(branch_id)
|
|
387
|
+
|
|
388
|
+
def pin_session_start_info(self) -> None:
|
|
389
|
+
"""Resolve what ``session.started`` reports now, and keep that answer.
|
|
390
|
+
|
|
391
|
+
Resolution is deferred to the first event because the fingerprints are
|
|
392
|
+
not known until the build finishes — but the build that finished is the
|
|
393
|
+
one the session started with, and reading its source again later would
|
|
394
|
+
let a profile switch rewrite the value the session opened with.
|
|
395
|
+
"""
|
|
396
|
+
resolve = self._session_start_info
|
|
397
|
+
if resolve is None:
|
|
398
|
+
return
|
|
399
|
+
info = resolve()
|
|
400
|
+
self._session_start_info = lambda: info
|
|
401
|
+
|
|
402
|
+
def last_assigned_sequence(self) -> int:
|
|
403
|
+
"""Highest sequence this session's log has handed out (0 when none)."""
|
|
404
|
+
writer = self._activation.writer
|
|
405
|
+
if writer is not None:
|
|
406
|
+
return writer.last_assigned_sequence
|
|
407
|
+
if self._recovery is not None:
|
|
408
|
+
return self._recovery.last_sequence
|
|
409
|
+
if self._session_dir is None:
|
|
410
|
+
return 0
|
|
411
|
+
path = trajectory_events_path(self._session_dir)
|
|
412
|
+
if not path.is_file():
|
|
413
|
+
return 0
|
|
414
|
+
try:
|
|
415
|
+
return scan_for_recovery(path).last_sequence
|
|
416
|
+
except OSError:
|
|
417
|
+
return 0
|
|
418
|
+
|
|
419
|
+
async def close(self, *, reason: str) -> bool:
|
|
420
|
+
"""Close the runtime (see :meth:`TrajectoryWriter.close`); idempotent."""
|
|
421
|
+
with self._activation_lock:
|
|
422
|
+
if self._close_task is not None:
|
|
423
|
+
task = self._close_task
|
|
424
|
+
else:
|
|
425
|
+
if self._closed:
|
|
426
|
+
return True
|
|
427
|
+
self._closed = True
|
|
428
|
+
state = self._activation
|
|
429
|
+
writer = state.writer
|
|
430
|
+
if writer is None:
|
|
431
|
+
self._finalizers.close()
|
|
432
|
+
self._activation = replace(
|
|
433
|
+
state, disabled_reason=state.disabled_reason or TrajectoryDisabledReason.CLOSED
|
|
434
|
+
)
|
|
435
|
+
return True
|
|
436
|
+
task = asyncio.ensure_future(self._close_writer(writer, reason=reason))
|
|
437
|
+
self._close_task = task
|
|
438
|
+
# The close is what hands the descriptor and this session's writer
|
|
439
|
+
# lease back, and ``_closed`` is already set, so it can never be
|
|
440
|
+
# abandoned half-done: cancellation detaches this caller, and every
|
|
441
|
+
# later caller waits on the same close rather than being told the
|
|
442
|
+
# runtime is already closed.
|
|
443
|
+
return await asyncio.shield(task)
|
|
444
|
+
|
|
445
|
+
async def _close_writer(self, writer: TrajectoryWriter, *, reason: str) -> bool:
|
|
446
|
+
# Producers can retire while this recorder stays open. Preserve their
|
|
447
|
+
# real results until here, then settle anything still being observed
|
|
448
|
+
# before freezing the writer (including starts whose ack is pending).
|
|
449
|
+
self._finalizers.close()
|
|
450
|
+
if self._background_waits:
|
|
451
|
+
# ``emit_soon`` acks still in flight: their lines are already
|
|
452
|
+
# queued ahead of the close marker, so only the waits are gathered.
|
|
453
|
+
await asyncio.gather(*tuple(self._background_waits), return_exceptions=True)
|
|
454
|
+
closed = False
|
|
455
|
+
try:
|
|
456
|
+
closed = await writer.close(reason=reason, join_timeout=self._close_join_timeout)
|
|
457
|
+
finally:
|
|
458
|
+
if writer.thread_alive:
|
|
459
|
+
# The lease is bound to the worker: a stuck thread keeps it so
|
|
460
|
+
# no second writer can ever open this session's file. That
|
|
461
|
+
# thread hands it back itself if its write ever returns.
|
|
462
|
+
self._worker_stuck = True
|
|
463
|
+
return closed
|
|
464
|
+
|
|
465
|
+
def _release_lease(self) -> None:
|
|
466
|
+
lease, self._lease = self._lease, None
|
|
467
|
+
if lease is not None:
|
|
468
|
+
lease.release()
|
|
469
|
+
|
|
470
|
+
# ------------------------------------------------------------ activation
|
|
471
|
+
|
|
472
|
+
def _needs_activation(self) -> bool:
|
|
473
|
+
state = self._activation
|
|
474
|
+
return state.writer is None and state.disabled_reason is None and not self._closed
|
|
475
|
+
|
|
476
|
+
def _activate(self) -> None:
|
|
477
|
+
with self._activation_lock:
|
|
478
|
+
while self._needs_activation() and self._activation_attempts < MAX_ACTIVATION_ATTEMPTS:
|
|
479
|
+
self._activation_attempts += 1
|
|
480
|
+
try:
|
|
481
|
+
self._activate_locked()
|
|
482
|
+
except Exception:
|
|
483
|
+
logger.warning("Trajectory activation failed for session %s", self._session_id, exc_info=True)
|
|
484
|
+
if self._reset_failed_activation() and self._activation_attempts < MAX_ACTIVATION_ATTEMPTS:
|
|
485
|
+
continue
|
|
486
|
+
self._disable(TrajectoryDisabledReason.ACTIVATION_FAILED)
|
|
487
|
+
self._report_activation_failure()
|
|
488
|
+
except BaseException:
|
|
489
|
+
# Preserve cancellation/interrupt propagation, but do not
|
|
490
|
+
# strand the private failed writer and its file lease.
|
|
491
|
+
self._reset_failed_activation()
|
|
492
|
+
raise
|
|
493
|
+
return
|
|
494
|
+
|
|
495
|
+
def _reset_failed_activation(self) -> bool:
|
|
496
|
+
"""Release a failed attempt so the same first event can retry safely."""
|
|
497
|
+
writer, self._failed_activation_writer = self._failed_activation_writer, None
|
|
498
|
+
if writer is not None:
|
|
499
|
+
if not writer.abort(join_timeout=self._close_join_timeout):
|
|
500
|
+
# Cleanup did not finish, so retrying could put two writers on
|
|
501
|
+
# one file. This failure really is terminal and may now be
|
|
502
|
+
# observed by the lock-free emit paths.
|
|
503
|
+
self._activation = _Activation(
|
|
504
|
+
writer=writer,
|
|
505
|
+
disabled_reason=TrajectoryDisabledReason.ACTIVATION_FAILED,
|
|
506
|
+
)
|
|
507
|
+
return False
|
|
508
|
+
else:
|
|
509
|
+
self._release_lease()
|
|
510
|
+
self._activation = _Activation()
|
|
511
|
+
self._coverage_reason = None
|
|
512
|
+
self._recovery = None
|
|
513
|
+
return True
|
|
514
|
+
|
|
515
|
+
def _report_activation_failure(self) -> None:
|
|
516
|
+
if self._activation_failure_reported or self._on_activation_failed is None:
|
|
517
|
+
return
|
|
518
|
+
self._activation_failure_reported = True
|
|
519
|
+
try:
|
|
520
|
+
self._on_activation_failed(TrajectoryDisabledReason.ACTIVATION_FAILED)
|
|
521
|
+
except Exception:
|
|
522
|
+
logger.debug("Trajectory activation failure callback failed", exc_info=True)
|
|
523
|
+
|
|
524
|
+
def _disable(self, reason: str) -> None:
|
|
525
|
+
"""Stop recording for *reason*; the emit paths refuse from here on.
|
|
526
|
+
|
|
527
|
+
Failed activation cleanup can leave a writer whose worker did not
|
|
528
|
+
exit. Its lease has to stay with it: a runtime that gave up still owns
|
|
529
|
+
the file until its worker exits.
|
|
530
|
+
"""
|
|
531
|
+
state = self._activation
|
|
532
|
+
self._activation = replace(state, disabled_reason=reason)
|
|
533
|
+
if state.writer is not None:
|
|
534
|
+
# The lease belongs to the worker, and a started writer still has
|
|
535
|
+
# the descriptor: handing it back here would let a second writer
|
|
536
|
+
# open this same file and append beside the first. Whatever gave
|
|
537
|
+
# up on this runtime, only the worker's exit ends its claim.
|
|
538
|
+
return
|
|
539
|
+
self._release_lease()
|
|
540
|
+
|
|
541
|
+
def _activate_locked(self) -> None:
|
|
542
|
+
session_dir = self._session_dir
|
|
543
|
+
if session_dir is None:
|
|
544
|
+
self._disable(TrajectoryDisabledReason.NO_SESSION_DIR)
|
|
545
|
+
return
|
|
546
|
+
directory = trajectory_dir(session_dir)
|
|
547
|
+
ensure_owner_only_directory(directory)
|
|
548
|
+
lease = WriterLease.try_acquire(trajectory_lease_path(session_dir))
|
|
549
|
+
if lease is None:
|
|
550
|
+
logger.info(
|
|
551
|
+
"Trajectory writer lease for session %s is held elsewhere; recording disabled", self._session_id
|
|
552
|
+
)
|
|
553
|
+
self._disable(TrajectoryDisabledReason.LEASE_HELD)
|
|
554
|
+
return
|
|
555
|
+
self._lease = lease
|
|
556
|
+
try:
|
|
557
|
+
self._key = self._load_key()
|
|
558
|
+
except SecureFileError, OSError, TimeoutError:
|
|
559
|
+
logger.warning("Trajectory fingerprint key unavailable; recording disabled", exc_info=True)
|
|
560
|
+
self._disable(TrajectoryDisabledReason.KEY_UNAVAILABLE)
|
|
561
|
+
return
|
|
562
|
+
|
|
563
|
+
events_path = trajectory_events_path(session_dir)
|
|
564
|
+
handle = secure_open_owner_only_append(events_path)
|
|
565
|
+
fd = handle.fd
|
|
566
|
+
try:
|
|
567
|
+
# Whether there is anything to recover is the open's own answer.
|
|
568
|
+
# A probe taken before it can be stale by the time it is used —
|
|
569
|
+
# or simply unable to stat — and a log wrongly called absent has
|
|
570
|
+
# its sequences handed out a second time.
|
|
571
|
+
scan = None if handle.created else self._recover(fd)
|
|
572
|
+
except BaseException:
|
|
573
|
+
os.close(fd)
|
|
574
|
+
raise
|
|
575
|
+
self._recovery = scan
|
|
576
|
+
if scan is not None:
|
|
577
|
+
self._recovered_truncated_bytes += scan.truncated_bytes
|
|
578
|
+
if scan is not None and scan.newer_schema_version is not None:
|
|
579
|
+
# Another build wrote lines this one cannot read, so it cannot
|
|
580
|
+
# know which slots they took. Reading such a log wrongly is a
|
|
581
|
+
# diagnostic; writing into it would put two events on one slot and
|
|
582
|
+
# damage the file for every later reader, so this session records
|
|
583
|
+
# nothing instead.
|
|
584
|
+
logger.warning(
|
|
585
|
+
"Trajectory for session %s was written under schema %d; recording disabled",
|
|
586
|
+
self._session_id,
|
|
587
|
+
scan.newer_schema_version,
|
|
588
|
+
)
|
|
589
|
+
os.close(fd)
|
|
590
|
+
self._disable(TrajectoryDisabledReason.SCHEMA_TOO_NEW)
|
|
591
|
+
return
|
|
592
|
+
if scan is not None and scan.unreadable_tail:
|
|
593
|
+
# A complete line past the last readable event does not even name
|
|
594
|
+
# the slot it took, so the next free sequence is exactly what
|
|
595
|
+
# cannot be established. Resuming would be a guess, and a wrong
|
|
596
|
+
# guess puts two events on one slot; this session records nothing
|
|
597
|
+
# instead, and the damaged file is left exactly as it is.
|
|
598
|
+
logger.warning(
|
|
599
|
+
"Trajectory for session %s ends in a line that names no sequence; recording disabled",
|
|
600
|
+
self._session_id,
|
|
601
|
+
)
|
|
602
|
+
os.close(fd)
|
|
603
|
+
self._disable(TrajectoryDisabledReason.UNREADABLE_TAIL)
|
|
604
|
+
return
|
|
605
|
+
if scan is not None and scan.last_sequence >= INT64_MAX:
|
|
606
|
+
# Every slot this build can name is spent. The next sequence is
|
|
607
|
+
# one no line may carry, and the writer refuses events that would
|
|
608
|
+
# — but a gap is encoded past that refusal, so a writer opened
|
|
609
|
+
# here would spend the file on lines nothing can read back.
|
|
610
|
+
logger.warning(
|
|
611
|
+
"Trajectory for session %s has spent every sequence it can encode; recording disabled",
|
|
612
|
+
self._session_id,
|
|
613
|
+
)
|
|
614
|
+
os.close(fd)
|
|
615
|
+
self._disable(TrajectoryDisabledReason.SEQUENCE_EXHAUSTED)
|
|
616
|
+
return
|
|
617
|
+
truncated_bytes = self._recovered_truncated_bytes
|
|
618
|
+
last_event = scan.last_event if scan is not None else None
|
|
619
|
+
resumed = last_event is not None
|
|
620
|
+
if last_event is not None:
|
|
621
|
+
# Recovery only supplies a branch when nobody has chosen one: a
|
|
622
|
+
# rollback that opened a branch before the log was activated is
|
|
623
|
+
# already on the new branch, and must not be pulled back onto the
|
|
624
|
+
# one it superseded.
|
|
625
|
+
if self._branch_id is None:
|
|
626
|
+
self._branch_id = last_event.branch_id
|
|
627
|
+
self._coverage_reason = CoverageReason.RUNTIME_RESUMED
|
|
628
|
+
else:
|
|
629
|
+
if self._branch_id is None:
|
|
630
|
+
self._branch_id = new_analytics_id()
|
|
631
|
+
self._coverage_reason = (
|
|
632
|
+
CoverageReason.FEATURE_INTRODUCED
|
|
633
|
+
if self._persisted_state_probe(session_dir)
|
|
634
|
+
else CoverageReason.SESSION_STARTED
|
|
635
|
+
)
|
|
636
|
+
writer = TrajectoryWriter(
|
|
637
|
+
backend=FdWriteBackend(fd),
|
|
638
|
+
session_id=self._session_id,
|
|
639
|
+
runtime_id=new_analytics_id(),
|
|
640
|
+
coverage_id=new_analytics_id(),
|
|
641
|
+
branch_id=self._branch_id,
|
|
642
|
+
initial_sequence=scan.last_sequence if scan is not None else 0,
|
|
643
|
+
initial_offset=scan.complete_offset if scan is not None else 0,
|
|
644
|
+
# Slots this file spent on lines no reader can show. They are a
|
|
645
|
+
# hole in the sequence the moment this runtime appends past them,
|
|
646
|
+
# so the gap that explains them goes in ahead of the prelude.
|
|
647
|
+
recovered_gap=scan.unreadable_slots if scan is not None else None,
|
|
648
|
+
write_ack_timeout=self._write_ack_timeout,
|
|
649
|
+
# The lease belongs to the worker, not to this object: it is
|
|
650
|
+
# handed back when the worker's descriptor is, which for an
|
|
651
|
+
# abandoned worker can be long after ``close`` returned.
|
|
652
|
+
on_worker_exit=self._release_lease,
|
|
653
|
+
)
|
|
654
|
+
try:
|
|
655
|
+
writer.start()
|
|
656
|
+
self._emit_prelude(writer, scan=scan, truncated_bytes=truncated_bytes, resumed=resumed)
|
|
657
|
+
except BaseException:
|
|
658
|
+
# Keep a retryable failure private while the activation lock is
|
|
659
|
+
# held. Concurrent producers continue to see an unactivated sink,
|
|
660
|
+
# join this activation through ``_activate()``, and emit against
|
|
661
|
+
# the successful retry instead of returning DEGRADED.
|
|
662
|
+
self._failed_activation_writer = writer
|
|
663
|
+
raise
|
|
664
|
+
else:
|
|
665
|
+
# Published last, and in one store. Every emit path that skips the
|
|
666
|
+
# activation lock does so by finding a writer here, so a producer
|
|
667
|
+
# that finds one before the prelude has been submitted takes a
|
|
668
|
+
# sequence ahead of the events that say which runtime and coverage
|
|
669
|
+
# it belongs to. Failed attempts are never published here.
|
|
670
|
+
self._activation = _Activation(writer=writer)
|
|
671
|
+
|
|
672
|
+
def _load_key(self) -> bytes:
|
|
673
|
+
config_dir = self._config_dir
|
|
674
|
+
if config_dir is None:
|
|
675
|
+
from chrys.foundation.platform import get_platform
|
|
676
|
+
|
|
677
|
+
config_dir = get_platform().config_dir
|
|
678
|
+
return load_or_create_fingerprint_key(config_dir)
|
|
679
|
+
|
|
680
|
+
def _recover(self, fd: int) -> RecoveryScan:
|
|
681
|
+
"""Scan and truncate the torn tail under the session write lock.
|
|
682
|
+
|
|
683
|
+
Through the descriptor, never the name: the file this writer holds is
|
|
684
|
+
the one it has to measure, and the two stop being the same file the
|
|
685
|
+
moment anything replaces the path behind it.
|
|
686
|
+
"""
|
|
687
|
+
lock = (
|
|
688
|
+
FileLock(self._write_lock_path, timeout=ACTIVATION_LOCK_TIMEOUT_SECONDS)
|
|
689
|
+
if self._write_lock_path is not None
|
|
690
|
+
else contextlib.nullcontext()
|
|
691
|
+
)
|
|
692
|
+
with lock:
|
|
693
|
+
scan = scan_open_file(fd)
|
|
694
|
+
if scan.newer_schema_version is None and not scan.unreadable_tail and scan.last_sequence < INT64_MAX:
|
|
695
|
+
# A file this build is not going to write to is a file it does
|
|
696
|
+
# not touch, torn tail included: truncating is the writer's
|
|
697
|
+
# repair, and there is no writer here.
|
|
698
|
+
truncate_torn_tail(fd, scan)
|
|
699
|
+
return scan
|
|
700
|
+
|
|
701
|
+
def _prelude_draft(self, event_type: str, *, actor: Actor, payload: Mapping[str, Any]) -> EventDraft:
|
|
702
|
+
"""A prelude event, stamped with the moment this recorder was bound."""
|
|
703
|
+
return EventDraft(
|
|
704
|
+
event_type=event_type,
|
|
705
|
+
actor=actor,
|
|
706
|
+
payload=payload,
|
|
707
|
+
occurred_at=self._bound_at,
|
|
708
|
+
monotonic_ns=self._bound_monotonic_ns,
|
|
709
|
+
)
|
|
710
|
+
|
|
711
|
+
def _emit_prelude(
|
|
712
|
+
self, writer: TrajectoryWriter, *, scan: RecoveryScan | None, truncated_bytes: int, resumed: bool
|
|
713
|
+
) -> None:
|
|
714
|
+
environment = runtime_environment()
|
|
715
|
+
self._require_prelude_event(
|
|
716
|
+
writer,
|
|
717
|
+
self._prelude_draft(
|
|
718
|
+
EventType.COVERAGE_STARTED,
|
|
719
|
+
actor=SYSTEM_ACTOR,
|
|
720
|
+
payload={
|
|
721
|
+
"coverage_id": writer.coverage_id,
|
|
722
|
+
"runtime_id": writer.runtime_id,
|
|
723
|
+
"coverage_reason": self._coverage_reason,
|
|
724
|
+
},
|
|
725
|
+
),
|
|
726
|
+
)
|
|
727
|
+
self._require_prelude_event(
|
|
728
|
+
writer,
|
|
729
|
+
self._prelude_draft(EventType.RUNTIME_STARTED, actor=SYSTEM_ACTOR, payload=environment),
|
|
730
|
+
)
|
|
731
|
+
previous_unclosed = (
|
|
732
|
+
scan is not None
|
|
733
|
+
and scan.last_event is not None
|
|
734
|
+
and scan.last_event.event_type != EventType.RUNTIME_FINISHED
|
|
735
|
+
)
|
|
736
|
+
if truncated_bytes > 0 or previous_unclosed:
|
|
737
|
+
self._require_prelude_event(
|
|
738
|
+
writer,
|
|
739
|
+
self._prelude_draft(
|
|
740
|
+
EventType.RUNTIME_RECOVERED,
|
|
741
|
+
actor=SYSTEM_ACTOR,
|
|
742
|
+
payload={
|
|
743
|
+
"truncated_bytes": truncated_bytes,
|
|
744
|
+
"resumed_from_sequence": scan.last_sequence if scan is not None else 0,
|
|
745
|
+
},
|
|
746
|
+
),
|
|
747
|
+
)
|
|
748
|
+
if not resumed and self._coverage_reason == CoverageReason.SESSION_STARTED:
|
|
749
|
+
info = self._session_start_info() if self._session_start_info is not None else None
|
|
750
|
+
payload: dict[str, Any] = dict(environment)
|
|
751
|
+
if info is not None and self._key is not None:
|
|
752
|
+
payload["workspace_fingerprint"] = fingerprint_text(self._key, DOMAIN_WORKSPACE, info.primary_cwd)
|
|
753
|
+
payload["agent_profile_fingerprint"] = info.agent_profile_fingerprint
|
|
754
|
+
payload["model_profile_fingerprint"] = info.model_profile_fingerprint
|
|
755
|
+
self._require_prelude_event(
|
|
756
|
+
writer, self._prelude_draft(EventType.SESSION_STARTED, actor=self._main_actor, payload=payload)
|
|
757
|
+
)
|
|
758
|
+
|
|
759
|
+
@staticmethod
|
|
760
|
+
def _require_prelude_event(writer: TrajectoryWriter, draft: EventDraft) -> None:
|
|
761
|
+
if writer.emit_blocking(draft) is not EmitResult.WRITTEN:
|
|
762
|
+
raise OSError("trajectory prelude event was not written")
|