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,727 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""AgentProfileRegistry — register, load, and switch agent profiles."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import contextlib
|
|
8
|
+
import logging
|
|
9
|
+
import unicodedata
|
|
10
|
+
import uuid
|
|
11
|
+
from collections.abc import Iterable
|
|
12
|
+
from copy import deepcopy
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import TYPE_CHECKING
|
|
16
|
+
|
|
17
|
+
from chrys.service.profiles.agents.loader import (
|
|
18
|
+
AgentProfileLoadError,
|
|
19
|
+
load_profile_from_yaml,
|
|
20
|
+
scan_profile_dir,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from chrys.service.profiles.agents.loader import ProfileDirScan
|
|
25
|
+
from chrys.service.profiles.agents.schema import AgentProfile
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
# Stable hardcoded ids for built-in profiles. Mirrors what is stamped into
|
|
30
|
+
# the YAML files under ``service/profiles/agents/builtins/``. Used by the legacy-id
|
|
31
|
+
# migration so a user's shadow profile (e.g. ``~/.chrys/agents/Code.yaml``)
|
|
32
|
+
# inherits the same id as the builtin it overrides — preserving the
|
|
33
|
+
# "this is still the Code agent" identity for future sync/distribution.
|
|
34
|
+
_BUILTIN_IDS: dict[str, str] = {
|
|
35
|
+
"Code": "b011c0de0001",
|
|
36
|
+
"Explore": "b011e80e0002",
|
|
37
|
+
"General": "b0119e4e0004",
|
|
38
|
+
"QA": "b0119a000005",
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# The removed builtin ``Plan`` sub-agent, whose references user profiles may still carry.
|
|
42
|
+
_RETIRED_PLAN_PROFILE = "Plan"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class AgentProfileRegistrySnapshot:
|
|
47
|
+
"""Restorable snapshot of registry-owned in-memory state.
|
|
48
|
+
|
|
49
|
+
Transactional saves currently mutate only ``profiles``, but the full
|
|
50
|
+
registry metadata is captured so future changes stay rollback-safe.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
profiles: dict[str, AgentProfile]
|
|
54
|
+
builtin_profiles: dict[str, AgentProfile]
|
|
55
|
+
user_dir: Path | None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _default_user_dir() -> Path:
|
|
59
|
+
from chrys.foundation.platform import get_platform
|
|
60
|
+
|
|
61
|
+
return get_platform().config_dir / "agents"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
_MAX_QUARANTINE_SUFFIX = 100
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _free_sibling(path: Path, stem: str, suffix: str) -> Path | None:
|
|
68
|
+
"""Return a free ``<stem>.<suffix>[-N]`` sibling of *path*, or None if all are taken."""
|
|
69
|
+
base = path.with_name(f"{stem}{suffix}")
|
|
70
|
+
if not base.exists():
|
|
71
|
+
return base
|
|
72
|
+
for n in range(2, _MAX_QUARANTINE_SUFFIX + 1):
|
|
73
|
+
candidate = path.with_name(f"{stem}-{n}{suffix}")
|
|
74
|
+
if not candidate.exists():
|
|
75
|
+
return candidate
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _canonical_caseless(value: str) -> str:
|
|
80
|
+
"""Key for the Unicode canonical caseless match (``NFD(casefold(NFD(x)))``).
|
|
81
|
+
|
|
82
|
+
Case folding alone is not enough on either side: the input must be
|
|
83
|
+
normalized so decomposed and precomposed spellings fold alike, and the
|
|
84
|
+
folded result must be normalized again because some foldings emit
|
|
85
|
+
sequences that are canonically equivalent but not byte-identical
|
|
86
|
+
(U+0390 folds to U+03B9 U+0308 U+0301, while U+0399 U+0308 U+0301
|
|
87
|
+
normalizes to U+03AA U+0301 and folds to U+03CA U+0301).
|
|
88
|
+
"""
|
|
89
|
+
return unicodedata.normalize("NFD", unicodedata.normalize("NFD", value).casefold())
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _is_same_filename_spelling(a: str, b: str) -> bool:
|
|
93
|
+
"""True when *a* and *b* differ only in case or Unicode normalization form.
|
|
94
|
+
|
|
95
|
+
macOS filesystems resolve ``Café`` typed as NFC and stored as NFD to the
|
|
96
|
+
same entry (HFS+ even rewrites every name to NFD), and case-insensitive
|
|
97
|
+
ones fold ``code``/``Code``; such a directory listing is still the file
|
|
98
|
+
that ``<name>.yaml`` addresses.
|
|
99
|
+
"""
|
|
100
|
+
return _canonical_caseless(a) == _canonical_caseless(b)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _quarantine_path(path: Path) -> Path | None:
|
|
104
|
+
"""Return a free ``<file>.conflict[-N]`` sibling for *path* (outside the ``*.yaml``/``*.yml`` scan)."""
|
|
105
|
+
return _free_sibling(path, f"{path.name}.conflict", "")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _parking_path(path: Path) -> Path | None:
|
|
109
|
+
"""Return a free ``<stem>.moving[-N].yaml`` sibling used to break a rename cycle mid-migration.
|
|
110
|
+
|
|
111
|
+
The parked file stays inside the ``*.yaml`` scan on purpose: should the
|
|
112
|
+
process die between the two renames, the next load picks it up as an
|
|
113
|
+
ordinary non-canonical file and finishes the move.
|
|
114
|
+
"""
|
|
115
|
+
return _free_sibling(path, f"{path.stem}.moving", ".yaml")
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _profile_id_matches(profiles: Iterable[AgentProfile], profile_id: str) -> list[AgentProfile]:
|
|
119
|
+
"""Return every profile carrying the non-empty stable id."""
|
|
120
|
+
if not profile_id:
|
|
121
|
+
return []
|
|
122
|
+
return [profile for profile in profiles if profile.id == profile_id]
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _warn_ambiguous_profile_id(profile_id: str, matches: Iterable[AgentProfile]) -> None:
|
|
126
|
+
names = ", ".join(repr(name) for name in sorted({profile.name for profile in matches}))
|
|
127
|
+
logger.warning(
|
|
128
|
+
"Agent profile id %r is ambiguous across profiles %s; refusing to resolve by id",
|
|
129
|
+
profile_id,
|
|
130
|
+
names,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def resolve_profile_selector(profiles: Iterable[AgentProfile], selector: str) -> AgentProfile | None:
|
|
135
|
+
"""Resolve a profile by id, name, or display name from *profiles*."""
|
|
136
|
+
raw = selector.strip()
|
|
137
|
+
if not raw:
|
|
138
|
+
return None
|
|
139
|
+
candidates = list(profiles)
|
|
140
|
+
id_matches = _profile_id_matches(candidates, raw)
|
|
141
|
+
if len(id_matches) == 1:
|
|
142
|
+
return id_matches[0]
|
|
143
|
+
if len(id_matches) > 1:
|
|
144
|
+
_warn_ambiguous_profile_id(raw, id_matches)
|
|
145
|
+
return None
|
|
146
|
+
|
|
147
|
+
exact_name_matches = [profile for profile in candidates if profile.name == raw]
|
|
148
|
+
if len(exact_name_matches) == 1:
|
|
149
|
+
return exact_name_matches[0]
|
|
150
|
+
if len(exact_name_matches) > 1:
|
|
151
|
+
logger.warning("Agent profile name %r is ambiguous; use a profile id instead", raw)
|
|
152
|
+
return None
|
|
153
|
+
|
|
154
|
+
exact_display_matches = [profile for profile in candidates if profile.display_name == raw]
|
|
155
|
+
if len(exact_display_matches) == 1:
|
|
156
|
+
return exact_display_matches[0]
|
|
157
|
+
if len(exact_display_matches) > 1:
|
|
158
|
+
logger.warning("Agent profile display name %r is ambiguous; use a profile id instead", raw)
|
|
159
|
+
return None
|
|
160
|
+
|
|
161
|
+
folded = raw.casefold()
|
|
162
|
+
folded_name_matches = [profile for profile in candidates if profile.name.casefold() == folded]
|
|
163
|
+
if len(folded_name_matches) == 1:
|
|
164
|
+
return folded_name_matches[0]
|
|
165
|
+
if len(folded_name_matches) > 1:
|
|
166
|
+
logger.warning("Agent profile name %r is ambiguous ignoring case; use a profile id instead", raw)
|
|
167
|
+
return None
|
|
168
|
+
|
|
169
|
+
folded_display_matches = [
|
|
170
|
+
profile for profile in candidates if profile.display_name and profile.display_name.casefold() == folded
|
|
171
|
+
]
|
|
172
|
+
if len(folded_display_matches) == 1:
|
|
173
|
+
return folded_display_matches[0]
|
|
174
|
+
if len(folded_display_matches) > 1:
|
|
175
|
+
logger.warning("Agent profile display name %r is ambiguous ignoring case; use a profile id instead", raw)
|
|
176
|
+
return None
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class AgentProfileRegistry:
|
|
180
|
+
"""Central registry for all available agent profiles.
|
|
181
|
+
|
|
182
|
+
Manages built-in profiles, user-defined profiles, and profile switching.
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
def __init__(self) -> None:
|
|
186
|
+
self._profiles: dict[str, AgentProfile] = {}
|
|
187
|
+
self._builtin_profiles: dict[str, AgentProfile] = {}
|
|
188
|
+
self._user_dir: Path | None = None
|
|
189
|
+
|
|
190
|
+
def register(self, profile: AgentProfile) -> None:
|
|
191
|
+
"""Register an agent profile definition."""
|
|
192
|
+
conflicts = _profile_id_matches(
|
|
193
|
+
(candidate for name, candidate in self._profiles.items() if name != profile.name),
|
|
194
|
+
profile.id,
|
|
195
|
+
)
|
|
196
|
+
if conflicts:
|
|
197
|
+
names = ", ".join(repr(name) for name in sorted({candidate.name for candidate in conflicts}))
|
|
198
|
+
logger.warning(
|
|
199
|
+
"Duplicate agent profile id %r for profile %r conflicts with registered profiles %s; "
|
|
200
|
+
"id lookups will fail closed",
|
|
201
|
+
profile.id,
|
|
202
|
+
profile.name,
|
|
203
|
+
names,
|
|
204
|
+
)
|
|
205
|
+
self._profiles[profile.name] = profile
|
|
206
|
+
|
|
207
|
+
def snapshot(self) -> AgentProfileRegistrySnapshot:
|
|
208
|
+
"""Return a deep snapshot of registry state for transactional callers."""
|
|
209
|
+
return AgentProfileRegistrySnapshot(
|
|
210
|
+
profiles=deepcopy(self._profiles),
|
|
211
|
+
builtin_profiles=deepcopy(self._builtin_profiles),
|
|
212
|
+
user_dir=self._user_dir,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
def restore(self, snapshot: AgentProfileRegistrySnapshot) -> None:
|
|
216
|
+
"""Restore a snapshot created by :meth:`snapshot`."""
|
|
217
|
+
self._profiles = deepcopy(snapshot.profiles)
|
|
218
|
+
self._builtin_profiles = deepcopy(snapshot.builtin_profiles)
|
|
219
|
+
self._user_dir = snapshot.user_dir
|
|
220
|
+
|
|
221
|
+
def get(self, name: str) -> AgentProfile | None:
|
|
222
|
+
"""Get a registered agent profile by name."""
|
|
223
|
+
return self._profiles.get(name)
|
|
224
|
+
|
|
225
|
+
def get_by_id(self, profile_id: str, *, disambiguating_name: str = "") -> AgentProfile | None:
|
|
226
|
+
"""Get a profile by stable id, using an exact name only to break an id tie.
|
|
227
|
+
|
|
228
|
+
A missing id never falls back to the name: that would let a deleted
|
|
229
|
+
profile silently adopt a different agent whose name was later reused.
|
|
230
|
+
"""
|
|
231
|
+
matches = _profile_id_matches(self._profiles.values(), profile_id)
|
|
232
|
+
if len(matches) == 1:
|
|
233
|
+
return matches[0]
|
|
234
|
+
if len(matches) > 1 and disambiguating_name:
|
|
235
|
+
named_matches = [profile for profile in matches if profile.name == disambiguating_name]
|
|
236
|
+
if len(named_matches) == 1:
|
|
237
|
+
logger.warning(
|
|
238
|
+
"Agent profile id %r is ambiguous; resolving by exact saved profile name %r",
|
|
239
|
+
profile_id,
|
|
240
|
+
disambiguating_name,
|
|
241
|
+
)
|
|
242
|
+
return named_matches[0]
|
|
243
|
+
if len(matches) > 1:
|
|
244
|
+
_warn_ambiguous_profile_id(profile_id, matches)
|
|
245
|
+
return None
|
|
246
|
+
|
|
247
|
+
def resolve_selector(self, selector: str) -> AgentProfile | None:
|
|
248
|
+
"""Resolve a profile by id, name, or display name.
|
|
249
|
+
|
|
250
|
+
Selector precedence is id, exact name, exact display name, case-insensitive
|
|
251
|
+
name, then case-insensitive display name. Ambiguous display/name matches
|
|
252
|
+
return ``None`` so callers can ask the user to disambiguate with an id.
|
|
253
|
+
"""
|
|
254
|
+
return resolve_profile_selector(self._profiles.values(), selector)
|
|
255
|
+
|
|
256
|
+
def list_names(self) -> list[str]:
|
|
257
|
+
"""List names of all registered agent profiles."""
|
|
258
|
+
return list(self._profiles.keys())
|
|
259
|
+
|
|
260
|
+
def list_profiles(self, *, include_sub_agent_only: bool = False) -> list[AgentProfile]:
|
|
261
|
+
"""List registered agent profiles.
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
include_sub_agent_only: If False (default), excludes profiles marked
|
|
265
|
+
``sub_agent_only=True``. Pass True to include all profiles.
|
|
266
|
+
"""
|
|
267
|
+
profiles = list(self._profiles.values())
|
|
268
|
+
if not include_sub_agent_only:
|
|
269
|
+
profiles = [p for p in profiles if not p.sub_agent_only]
|
|
270
|
+
return profiles
|
|
271
|
+
|
|
272
|
+
def load_builtins(self) -> int:
|
|
273
|
+
"""Load all built-in agent profile definitions from service/profiles/agents/builtins/*.yaml.
|
|
274
|
+
|
|
275
|
+
Returns the number of profiles loaded.
|
|
276
|
+
"""
|
|
277
|
+
builtins_dir = Path(__file__).parent / "builtins"
|
|
278
|
+
if not builtins_dir.exists():
|
|
279
|
+
return 0
|
|
280
|
+
|
|
281
|
+
loaded = 0
|
|
282
|
+
# Case-folded key → original filename. Names are case-insensitive
|
|
283
|
+
# because the filesystem is (macOS/Windows), so "Code.yaml" and
|
|
284
|
+
# "code.yaml" would clobber each other on disk.
|
|
285
|
+
seen: dict[str, str] = {}
|
|
286
|
+
for path in sorted(builtins_dir.glob("*.yaml")):
|
|
287
|
+
try:
|
|
288
|
+
profile = load_profile_from_yaml(path)
|
|
289
|
+
key = profile.name.casefold()
|
|
290
|
+
if key in seen:
|
|
291
|
+
logger.warning(
|
|
292
|
+
"Duplicate agent profile name %r in builtins (%s shadows %s) — later file wins.",
|
|
293
|
+
profile.name,
|
|
294
|
+
path.name,
|
|
295
|
+
seen[key],
|
|
296
|
+
)
|
|
297
|
+
seen[key] = path.name
|
|
298
|
+
if not profile.id:
|
|
299
|
+
# Builtin YAMLs must carry a hardcoded ``id``. Refuse to
|
|
300
|
+
# silently auto-assign one because that would defeat the
|
|
301
|
+
# whole point of stable identifiers across installations.
|
|
302
|
+
logger.error(
|
|
303
|
+
"Builtin agent profile %r at %s is missing required 'id' field.",
|
|
304
|
+
profile.name,
|
|
305
|
+
path.name,
|
|
306
|
+
)
|
|
307
|
+
self._builtin_profiles[profile.name] = deepcopy(profile)
|
|
308
|
+
self.register(profile)
|
|
309
|
+
loaded += 1
|
|
310
|
+
except AgentProfileLoadError as e:
|
|
311
|
+
logger.warning("Failed to load builtin agent profile %s: %s", path.name, e)
|
|
312
|
+
return loaded
|
|
313
|
+
|
|
314
|
+
def load_user_profiles(self, directory: Path | None = None) -> int:
|
|
315
|
+
"""Load user-defined agent profiles from a directory.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
directory: Path to scan. Defaults to ``~/.chrys/agents/``.
|
|
319
|
+
|
|
320
|
+
Returns the number of profiles loaded. Warns once per name if
|
|
321
|
+
two YAML files in the directory share the same ``name`` — names
|
|
322
|
+
are registry keys, so later files silently shadow earlier ones.
|
|
323
|
+
Files stored under a filename other than ``<name>.yaml`` are moved
|
|
324
|
+
to that canonical path so later saves and deletes address them.
|
|
325
|
+
"""
|
|
326
|
+
target = directory or _default_user_dir()
|
|
327
|
+
scan = scan_profile_dir(target)
|
|
328
|
+
loaded = self._canonicalize_profile_files(scan.loaded, target)
|
|
329
|
+
# Case-folded names: the registry key is the raw name, but
|
|
330
|
+
# filesystem casing varies, so treat "Code" == "code" for
|
|
331
|
+
# warning purposes.
|
|
332
|
+
seen: set[str] = set()
|
|
333
|
+
registered = 0
|
|
334
|
+
for p in loaded:
|
|
335
|
+
key = p.name.casefold()
|
|
336
|
+
if key in seen:
|
|
337
|
+
logger.warning(
|
|
338
|
+
"Duplicate agent profile name %r found in %s — "
|
|
339
|
+
"rename one via the agent configuration screen to avoid shadowing.",
|
|
340
|
+
p.name,
|
|
341
|
+
target,
|
|
342
|
+
)
|
|
343
|
+
else:
|
|
344
|
+
seen.add(key)
|
|
345
|
+
self._migrate_legacy_id_if_needed(p, target)
|
|
346
|
+
self.register(p)
|
|
347
|
+
registered += 1
|
|
348
|
+
self._drop_retired_plan_refs(loaded, scan, target)
|
|
349
|
+
return registered
|
|
350
|
+
|
|
351
|
+
def _canonicalize_profile_files(
|
|
352
|
+
self, loaded: list[tuple[Path, AgentProfile]], user_dir: Path
|
|
353
|
+
) -> list[AgentProfile]:
|
|
354
|
+
"""Move every loaded profile file to ``<name>.yaml``; return the profiles that may be registered.
|
|
355
|
+
|
|
356
|
+
Every write path (save, delete, rename, built-in reset) addresses a
|
|
357
|
+
user profile by ``<name>.yaml``, while the loader accepts any
|
|
358
|
+
``*.yaml``/``*.yml`` file and takes the name from its contents. A
|
|
359
|
+
profile stored under another filename would therefore look editable
|
|
360
|
+
but keep reappearing from the untouched file on the next load. The
|
|
361
|
+
registry only ever registers profiles whose file sits at the
|
|
362
|
+
canonical path, so:
|
|
363
|
+
|
|
364
|
+
- a file already at ``<name>.yaml`` (or a case-only alias of it on a
|
|
365
|
+
case-insensitive filesystem) is registered as is;
|
|
366
|
+
- a file elsewhere is moved to ``<name>.yaml`` first;
|
|
367
|
+
- a file whose canonical path is taken by a *different* file that
|
|
368
|
+
keeps that name is quarantined as ``<file>.conflict`` (outside the
|
|
369
|
+
``*.yaml``/``*.yml`` scan, contents untouched) and not registered —
|
|
370
|
+
leaving it in place would let it resurface as soon as the canonical
|
|
371
|
+
file is deleted;
|
|
372
|
+
- a file that cannot be moved is not registered either, since every
|
|
373
|
+
editor operation would silently miss it.
|
|
374
|
+
|
|
375
|
+
Moves are planned over the whole directory before anything is
|
|
376
|
+
quarantined: a canonical path occupied by another file that is itself
|
|
377
|
+
about to move (``A.yaml`` defining ``B`` while ``B.yaml`` defines
|
|
378
|
+
``A``) is a pending target, not a conflict, so both files end up
|
|
379
|
+
under their own names. A file that should have moved away but could
|
|
380
|
+
not (permissions, no free quarantine name) keeps blocking its path;
|
|
381
|
+
files waiting on that path are left untouched and skipped rather than
|
|
382
|
+
quarantined, so the next load can finish the migration once the
|
|
383
|
+
blocker is fixed.
|
|
384
|
+
"""
|
|
385
|
+
registrable: list[AgentProfile] = []
|
|
386
|
+
# Canonical paths owned by files that stay put — the only true conflicts.
|
|
387
|
+
claimed: set[Path] = set()
|
|
388
|
+
# Paths still occupied by files that failed to move aside this load.
|
|
389
|
+
blocked: set[Path] = set()
|
|
390
|
+
pending: list[tuple[Path, AgentProfile]] = []
|
|
391
|
+
for path, profile in loaded:
|
|
392
|
+
canonical = user_dir / f"{profile.name}.yaml"
|
|
393
|
+
if path.name == canonical.name:
|
|
394
|
+
claimed.add(canonical)
|
|
395
|
+
registrable.append(profile)
|
|
396
|
+
else:
|
|
397
|
+
pending.append((path, profile))
|
|
398
|
+
|
|
399
|
+
while pending:
|
|
400
|
+
pending_paths = {path for path, _profile in pending}
|
|
401
|
+
waiting: list[tuple[Path, AgentProfile]] = []
|
|
402
|
+
for path, profile in pending:
|
|
403
|
+
canonical = user_dir / f"{profile.name}.yaml"
|
|
404
|
+
try:
|
|
405
|
+
if canonical in claimed:
|
|
406
|
+
if not self._quarantine_conflicting_profile(profile, path, canonical):
|
|
407
|
+
blocked.add(path)
|
|
408
|
+
continue
|
|
409
|
+
if canonical in blocked:
|
|
410
|
+
# The occupant wanted to leave but could not; it is not a
|
|
411
|
+
# rival for the name, so leave this file alone as well.
|
|
412
|
+
self._warn_blocked_profile(profile, path, canonical)
|
|
413
|
+
blocked.add(path)
|
|
414
|
+
continue
|
|
415
|
+
if canonical.exists():
|
|
416
|
+
if canonical in pending_paths:
|
|
417
|
+
# Occupied by a file that is itself moving away; retry once it has.
|
|
418
|
+
waiting.append((path, profile))
|
|
419
|
+
continue
|
|
420
|
+
if _is_same_filename_spelling(path.name, canonical.name) and canonical.samefile(path):
|
|
421
|
+
# Case- or normalization-only difference on a filesystem that
|
|
422
|
+
# folds those: name-based lookups already resolve to this file.
|
|
423
|
+
# Any other alias (e.g. a hard link) would resurface after a delete.
|
|
424
|
+
claimed.add(canonical)
|
|
425
|
+
registrable.append(profile)
|
|
426
|
+
continue
|
|
427
|
+
if not self._quarantine_conflicting_profile(profile, path, canonical):
|
|
428
|
+
blocked.add(path)
|
|
429
|
+
continue
|
|
430
|
+
path.rename(canonical)
|
|
431
|
+
except OSError as exc:
|
|
432
|
+
self._warn_unmovable_profile(profile, path, canonical, exc)
|
|
433
|
+
blocked.add(path)
|
|
434
|
+
continue
|
|
435
|
+
logger.info("Moved agent profile %r from %s to canonical path %s.", profile.name, path.name, canonical)
|
|
436
|
+
claimed.add(canonical)
|
|
437
|
+
registrable.append(profile)
|
|
438
|
+
if len(waiting) == len(pending):
|
|
439
|
+
# Every remaining file waits on another remaining file, so the
|
|
440
|
+
# targets form a cycle. Park one file that is itself a target
|
|
441
|
+
# under a temporary name; the file waiting on it can then move
|
|
442
|
+
# and the cycle unwinds over the following passes.
|
|
443
|
+
targets = {user_dir / f"{profile.name}.yaml" for _path, profile in waiting}
|
|
444
|
+
index = next(i for i, (path, _profile) in enumerate(waiting) if path in targets)
|
|
445
|
+
path, profile = waiting[index]
|
|
446
|
+
parked = _parking_path(path)
|
|
447
|
+
try:
|
|
448
|
+
if parked is None:
|
|
449
|
+
raise OSError("no free temporary name")
|
|
450
|
+
path.rename(parked)
|
|
451
|
+
except OSError as exc:
|
|
452
|
+
self._warn_unmovable_profile(profile, path, user_dir / f"{profile.name}.yaml", exc)
|
|
453
|
+
blocked.add(path)
|
|
454
|
+
waiting.pop(index)
|
|
455
|
+
else:
|
|
456
|
+
waiting[index] = (parked, profile)
|
|
457
|
+
pending = waiting
|
|
458
|
+
return registrable
|
|
459
|
+
|
|
460
|
+
@staticmethod
|
|
461
|
+
def _warn_unmovable_profile(profile: AgentProfile, path: Path, canonical: Path, exc: OSError) -> None:
|
|
462
|
+
logger.warning(
|
|
463
|
+
"Ignoring agent profile %r in %s: it could not be moved to %s (%s). "
|
|
464
|
+
"Rename the file yourself or fix the directory permissions to use it.",
|
|
465
|
+
profile.name,
|
|
466
|
+
path,
|
|
467
|
+
canonical.name,
|
|
468
|
+
exc,
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
@staticmethod
|
|
472
|
+
def _warn_blocked_profile(profile: AgentProfile, path: Path, canonical: Path) -> None:
|
|
473
|
+
logger.warning(
|
|
474
|
+
"Ignoring agent profile %r in %s for now: %s is still occupied by a file that could not be "
|
|
475
|
+
"moved aside. It will be moved into place on the next load once that file is fixed.",
|
|
476
|
+
profile.name,
|
|
477
|
+
path,
|
|
478
|
+
canonical.name,
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
@staticmethod
|
|
482
|
+
def _quarantine_conflicting_profile(profile: AgentProfile, path: Path, canonical: Path) -> bool:
|
|
483
|
+
"""Move a duplicate source out of the scan as ``<file>.conflict``; contents are untouched.
|
|
484
|
+
|
|
485
|
+
Returns whether *path* was actually vacated; a file that stays behind
|
|
486
|
+
keeps blocking that path for anything waiting to move there.
|
|
487
|
+
"""
|
|
488
|
+
quarantine = _quarantine_path(path)
|
|
489
|
+
if quarantine is not None:
|
|
490
|
+
try:
|
|
491
|
+
path.rename(quarantine)
|
|
492
|
+
except OSError as exc:
|
|
493
|
+
logger.warning(
|
|
494
|
+
"Ignoring agent profile %r in %s because %s already defines that name; the duplicate "
|
|
495
|
+
"could not be moved aside (%s) — rename or remove it to keep it from resurfacing.",
|
|
496
|
+
profile.name,
|
|
497
|
+
path,
|
|
498
|
+
canonical.name,
|
|
499
|
+
exc,
|
|
500
|
+
)
|
|
501
|
+
return False
|
|
502
|
+
logger.warning(
|
|
503
|
+
"Agent profile %r in %s duplicates %s; moved it to %s so the canonical file stays the only "
|
|
504
|
+
"source. Rename it back under a different name if you still need it.",
|
|
505
|
+
profile.name,
|
|
506
|
+
path.name,
|
|
507
|
+
canonical.name,
|
|
508
|
+
quarantine.name,
|
|
509
|
+
)
|
|
510
|
+
return True
|
|
511
|
+
logger.warning(
|
|
512
|
+
"Ignoring agent profile %r in %s because %s already defines that name and no free quarantine "
|
|
513
|
+
"name was available; rename or remove the extra file.",
|
|
514
|
+
profile.name,
|
|
515
|
+
path,
|
|
516
|
+
canonical.name,
|
|
517
|
+
)
|
|
518
|
+
return False
|
|
519
|
+
|
|
520
|
+
# =========================================================================
|
|
521
|
+
# BEGIN legacy-id migration
|
|
522
|
+
# ---------------------------------------------------------------------
|
|
523
|
+
# User profile YAMLs written before the ``id`` field existed don't have
|
|
524
|
+
# one. We mint a UUID-hex (12 chars, matching ModelProfile) and rewrite
|
|
525
|
+
# the file in place so the id sticks across runs.
|
|
526
|
+
#
|
|
527
|
+
# TODO(remove-after-migration): once user profiles in the wild all carry
|
|
528
|
+
# an ``id:`` field, delete this method and its single caller in
|
|
529
|
+
# ``load_user_profiles``. The schema default of ``id: ""`` plus the
|
|
530
|
+
# builtin sanity check in ``load_builtins`` is enough on its own.
|
|
531
|
+
# =========================================================================
|
|
532
|
+
def _migrate_legacy_id_if_needed(self, profile: AgentProfile, user_dir: Path) -> None:
|
|
533
|
+
"""Assign an id and rewrite the YAML if the loaded profile lacks one.
|
|
534
|
+
|
|
535
|
+
If the profile's name matches a built-in (e.g. a user's customized
|
|
536
|
+
``Code.yaml`` that shadows the builtin), reuse the builtin's
|
|
537
|
+
hardcoded id so the two stay linked for sync/distribution.
|
|
538
|
+
Otherwise mint a fresh UUID-hex. Callers guarantee the profile's
|
|
539
|
+
file already sits at ``<name>.yaml`` (see
|
|
540
|
+
:meth:`_canonicalize_profile_filename`), so the rewrite updates the
|
|
541
|
+
file that was loaded rather than creating a second one.
|
|
542
|
+
|
|
543
|
+
A failed rewrite restores the empty legacy id in memory. Exposing an
|
|
544
|
+
unpersisted generated id would let sessions record an identity that
|
|
545
|
+
changes on the next process start.
|
|
546
|
+
"""
|
|
547
|
+
if profile.id:
|
|
548
|
+
return
|
|
549
|
+
from chrys.service.profiles.agents.serializer import save_profile
|
|
550
|
+
|
|
551
|
+
builtin_id = _BUILTIN_IDS.get(profile.name)
|
|
552
|
+
migrated_id = builtin_id or uuid.uuid4().hex[:12]
|
|
553
|
+
profile.id = migrated_id
|
|
554
|
+
source = "builtin" if builtin_id else "generated"
|
|
555
|
+
try:
|
|
556
|
+
save_profile(profile, target_dir=user_dir)
|
|
557
|
+
except (OSError, ValueError) as exc:
|
|
558
|
+
profile.id = ""
|
|
559
|
+
logger.warning(
|
|
560
|
+
"Could not persist migrated id %s for legacy profile %r in %s: %s",
|
|
561
|
+
migrated_id,
|
|
562
|
+
profile.name,
|
|
563
|
+
user_dir,
|
|
564
|
+
exc,
|
|
565
|
+
)
|
|
566
|
+
return
|
|
567
|
+
logger.info(
|
|
568
|
+
"Migrated legacy agent profile %r: assigned %s id %s and rewrote %s.",
|
|
569
|
+
profile.name,
|
|
570
|
+
source,
|
|
571
|
+
profile.id,
|
|
572
|
+
user_dir / f"{profile.name}.yaml",
|
|
573
|
+
)
|
|
574
|
+
|
|
575
|
+
# =========================================================================
|
|
576
|
+
# END legacy-id migration
|
|
577
|
+
# =========================================================================
|
|
578
|
+
|
|
579
|
+
# =========================================================================
|
|
580
|
+
# BEGIN retired Plan sub-agent migration
|
|
581
|
+
# ---------------------------------------------------------------------
|
|
582
|
+
# The builtin ``Plan`` sub-agent was removed. User profiles saved while it
|
|
583
|
+
# shipped — every ``Code.yaml`` customized from the agent configuration
|
|
584
|
+
# screen among them — still list it under ``sub_agents``, where every
|
|
585
|
+
# build would now skip it as unknown. Those references are dropped and
|
|
586
|
+
# the file is rewritten once.
|
|
587
|
+
#
|
|
588
|
+
# TODO(remove-after-migration): once user profiles in the wild no longer
|
|
589
|
+
# reference ``Plan``, delete this method, its single caller in
|
|
590
|
+
# ``load_user_profiles`` and ``_RETIRED_PLAN_PROFILE``.
|
|
591
|
+
# =========================================================================
|
|
592
|
+
def _drop_retired_plan_refs(self, profiles: Iterable[AgentProfile], scan: ProfileDirScan, user_dir: Path) -> None:
|
|
593
|
+
"""Remove user profiles' sub-agent references to the retired builtin ``Plan`` and rewrite their YAML.
|
|
594
|
+
|
|
595
|
+
A ``Plan`` profile of the user's own keeps every reference valid, so
|
|
596
|
+
nothing is touched while one exists: registered, or parsed but kept
|
|
597
|
+
out of the registry by a file move that failed. A profile file that
|
|
598
|
+
failed to parse might be that ``Plan``, so the migration waits until
|
|
599
|
+
the directory loads cleanly. A failed rewrite still drops the
|
|
600
|
+
references in memory and is retried on the next load.
|
|
601
|
+
"""
|
|
602
|
+
referencing = [
|
|
603
|
+
profile
|
|
604
|
+
for profile in profiles
|
|
605
|
+
if any(ref.profile == _RETIRED_PLAN_PROFILE for ref in profile.sub_agents.agents)
|
|
606
|
+
]
|
|
607
|
+
if not referencing:
|
|
608
|
+
return
|
|
609
|
+
if self.get(_RETIRED_PLAN_PROFILE) is not None or any(
|
|
610
|
+
parsed.name == _RETIRED_PLAN_PROFILE for _path, parsed in scan.loaded
|
|
611
|
+
):
|
|
612
|
+
return
|
|
613
|
+
if scan.invalid:
|
|
614
|
+
logger.warning(
|
|
615
|
+
"Keeping references to the removed Plan sub-agent in %s for now: %s could not be loaded and "
|
|
616
|
+
"may define a Plan profile of your own. Fix or remove it to finish the migration.",
|
|
617
|
+
", ".join(repr(profile.name) for profile in referencing),
|
|
618
|
+
", ".join(path.name for path in scan.invalid),
|
|
619
|
+
)
|
|
620
|
+
return
|
|
621
|
+
from chrys.service.profiles.agents.serializer import save_profile
|
|
622
|
+
|
|
623
|
+
for profile in referencing:
|
|
624
|
+
profile.sub_agents.agents = [
|
|
625
|
+
ref for ref in profile.sub_agents.agents if ref.profile != _RETIRED_PLAN_PROFILE
|
|
626
|
+
]
|
|
627
|
+
try:
|
|
628
|
+
path = save_profile(profile, target_dir=user_dir)
|
|
629
|
+
except (OSError, ValueError) as exc:
|
|
630
|
+
logger.warning(
|
|
631
|
+
"Dropped agent profile %r's references to the removed Plan sub-agent for this run but could "
|
|
632
|
+
"not rewrite its file in %s: %s",
|
|
633
|
+
profile.name,
|
|
634
|
+
user_dir,
|
|
635
|
+
exc,
|
|
636
|
+
)
|
|
637
|
+
continue
|
|
638
|
+
logger.info(
|
|
639
|
+
"Dropped agent profile %r's references to the removed Plan sub-agent and rewrote %s.",
|
|
640
|
+
profile.name,
|
|
641
|
+
path,
|
|
642
|
+
)
|
|
643
|
+
|
|
644
|
+
# =========================================================================
|
|
645
|
+
# END retired Plan sub-agent migration
|
|
646
|
+
# =========================================================================
|
|
647
|
+
|
|
648
|
+
def remove(self, name: str, *, cascade: bool = True) -> bool:
|
|
649
|
+
"""Remove a profile from the registry.
|
|
650
|
+
|
|
651
|
+
Args:
|
|
652
|
+
name: Registry name to remove.
|
|
653
|
+
cascade: When True, remove sub-agent references to ``name`` from
|
|
654
|
+
other profiles. Transactional rename paths pass False after
|
|
655
|
+
retargeting references themselves.
|
|
656
|
+
"""
|
|
657
|
+
existed = self._profiles.pop(name, None) is not None
|
|
658
|
+
if existed and cascade:
|
|
659
|
+
self._remove_sub_agent_refs(name)
|
|
660
|
+
return existed
|
|
661
|
+
|
|
662
|
+
def is_builtin(self, name: str) -> bool:
|
|
663
|
+
"""Check whether a profile was loaded from the builtins directory."""
|
|
664
|
+
return name in self._builtin_profiles
|
|
665
|
+
|
|
666
|
+
def resolve_builtin_filename(self, name: str) -> str | None:
|
|
667
|
+
"""Resolve a name that could address a builtin's shadow on a caseless filesystem.
|
|
668
|
+
|
|
669
|
+
This is a deletion guard, not profile identity: registered names and
|
|
670
|
+
reset templates retain exact spelling, including distinct user names.
|
|
671
|
+
"""
|
|
672
|
+
return next(
|
|
673
|
+
(builtin_name for builtin_name in self._builtin_profiles if _is_same_filename_spelling(name, builtin_name)),
|
|
674
|
+
None,
|
|
675
|
+
)
|
|
676
|
+
|
|
677
|
+
def get_builtin_template(self, name: str) -> AgentProfile | None:
|
|
678
|
+
"""Return an isolated copy of the pristine built-in profile named *name*."""
|
|
679
|
+
profile = self._builtin_profiles.get(name)
|
|
680
|
+
return deepcopy(profile) if profile is not None else None
|
|
681
|
+
|
|
682
|
+
def build_builtin_reset(
|
|
683
|
+
self,
|
|
684
|
+
name: str,
|
|
685
|
+
*,
|
|
686
|
+
preserve_from: AgentProfile | None = None,
|
|
687
|
+
) -> AgentProfile:
|
|
688
|
+
"""Build a pristine built-in profile while retaining user-owned integrations."""
|
|
689
|
+
template = self.get_builtin_template(name)
|
|
690
|
+
if template is None:
|
|
691
|
+
msg = f"Agent profile is not built-in: {name}"
|
|
692
|
+
raise ValueError(msg)
|
|
693
|
+
current = preserve_from or self._profiles.get(name)
|
|
694
|
+
if current is None:
|
|
695
|
+
msg = f"Agent profile not found: {name}"
|
|
696
|
+
raise ValueError(msg)
|
|
697
|
+
template.skills = deepcopy(current.skills)
|
|
698
|
+
template.tools.mcp = deepcopy(current.tools.mcp)
|
|
699
|
+
template.memory = deepcopy(current.memory)
|
|
700
|
+
return template
|
|
701
|
+
|
|
702
|
+
def profiles_referencing(self, name: str) -> list[AgentProfile]:
|
|
703
|
+
"""Return other profiles whose sub-agent references a removal would change."""
|
|
704
|
+
return [
|
|
705
|
+
profile
|
|
706
|
+
for profile in self._profiles.values()
|
|
707
|
+
if profile.name != name and any(ref.profile == name for ref in profile.sub_agents.agents)
|
|
708
|
+
]
|
|
709
|
+
|
|
710
|
+
def _remove_sub_agent_refs(self, name: str) -> None:
|
|
711
|
+
"""Remove all sub-agent references to *name* from other profiles and persist changes."""
|
|
712
|
+
from chrys.service.profiles.agents.serializer import save_profile
|
|
713
|
+
|
|
714
|
+
for profile in self.profiles_referencing(name):
|
|
715
|
+
profile.sub_agents.agents = [a for a in profile.sub_agents.agents if a.profile != name]
|
|
716
|
+
if not self.is_builtin(profile.name):
|
|
717
|
+
save_profile(profile)
|
|
718
|
+
|
|
719
|
+
def load_all(self, user_dir: Path | None = None) -> int:
|
|
720
|
+
"""Load builtins + user profiles. Returns total loaded."""
|
|
721
|
+
target = user_dir or _default_user_dir()
|
|
722
|
+
self._user_dir = target
|
|
723
|
+
with contextlib.suppress(OSError):
|
|
724
|
+
(target / ".hidden.yaml").unlink(missing_ok=True)
|
|
725
|
+
count = self.load_builtins()
|
|
726
|
+
count += self.load_user_profiles(target)
|
|
727
|
+
return count
|