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,808 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""Agent profile loader — load agent profile definitions from YAML files and directories."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
import math
|
|
9
|
+
import ntpath
|
|
10
|
+
import re
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from pathlib import PurePath, PureWindowsPath
|
|
13
|
+
from typing import TYPE_CHECKING, Any, Literal, cast
|
|
14
|
+
|
|
15
|
+
import yaml
|
|
16
|
+
|
|
17
|
+
from chrys.foundation.branding import APP_DISPLAY_NAME
|
|
18
|
+
from chrys.foundation.text.encoding import decode_bytes
|
|
19
|
+
from chrys.foundation.tool_kinds import strip_legacy_kind_prefix
|
|
20
|
+
from chrys.service.mcp.validation import (
|
|
21
|
+
MCP_PROGRESSIVE_CONTROL_TOOL_NAMES,
|
|
22
|
+
validate_mcp_tool_loading_policy,
|
|
23
|
+
validate_mcp_tool_name_prefix,
|
|
24
|
+
)
|
|
25
|
+
from chrys.service.profiles.agents.schema import (
|
|
26
|
+
DEFAULT_SUB_AGENT_CONCURRENCY,
|
|
27
|
+
AcpAgentConfig,
|
|
28
|
+
AgentProfile,
|
|
29
|
+
ApprovalConfig,
|
|
30
|
+
CompactionConfig,
|
|
31
|
+
MCPServerConfig,
|
|
32
|
+
MemoryConfig,
|
|
33
|
+
ModelConfig,
|
|
34
|
+
ShellFilterConfig,
|
|
35
|
+
SkillConfig,
|
|
36
|
+
SkillResourceConfig,
|
|
37
|
+
SkillsConfig,
|
|
38
|
+
SkillScriptConfig,
|
|
39
|
+
SubAgentRef,
|
|
40
|
+
SubAgentsConfig,
|
|
41
|
+
ToolsConfig,
|
|
42
|
+
)
|
|
43
|
+
from chrys.service.profiles.coercion import coerce_bool
|
|
44
|
+
|
|
45
|
+
if TYPE_CHECKING:
|
|
46
|
+
from pathlib import Path
|
|
47
|
+
|
|
48
|
+
logger = logging.getLogger(__name__)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class AgentProfileLoadError(Exception):
|
|
52
|
+
"""Raised when an agent profile YAML file cannot be loaded or validated."""
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
_VALID_TRANSPORTS = {"stdio", "http"}
|
|
56
|
+
_VALID_APPROVAL_RULES = frozenset({"auto", "require", "skip"})
|
|
57
|
+
_SKILL_NAME_RE = re.compile(r"^[a-z0-9](?:[a-z0-9]|-(?=[a-z0-9])){0,63}$")
|
|
58
|
+
_MAX_SKILL_DESCRIPTION_LENGTH = 1024
|
|
59
|
+
_ENV_NAME_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
|
|
60
|
+
_ACP_DEPTH_ENV = "CHRYS_ACP_SUBAGENT_DEPTH"
|
|
61
|
+
_ACP_RESULT_MODES = frozenset({"last_segment", "transcript"})
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _mapping_section(raw: object, section: str) -> dict[str, Any]:
|
|
65
|
+
"""Return a YAML section mapping or raise a profile load error."""
|
|
66
|
+
if raw is None:
|
|
67
|
+
return {}
|
|
68
|
+
if not isinstance(raw, dict):
|
|
69
|
+
msg = f"Agent profile section '{section}' must be a mapping, got {type(raw).__name__}"
|
|
70
|
+
raise AgentProfileLoadError(msg)
|
|
71
|
+
return cast("dict[str, Any]", raw)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _list_section(raw: object, section: str) -> list[Any]:
|
|
75
|
+
"""Return a YAML section list or raise a profile load error."""
|
|
76
|
+
if raw is None:
|
|
77
|
+
return []
|
|
78
|
+
if not isinstance(raw, list):
|
|
79
|
+
msg = f"Agent profile section '{section}' must be a list, got {type(raw).__name__}"
|
|
80
|
+
raise AgentProfileLoadError(msg)
|
|
81
|
+
return raw
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _mapping_entry(raw: object, section: str) -> dict[str, Any]:
|
|
85
|
+
"""Return a list entry mapping or raise a profile load error."""
|
|
86
|
+
if not isinstance(raw, dict):
|
|
87
|
+
msg = f"Agent profile section '{section}' entries must be mappings, got {type(raw).__name__}"
|
|
88
|
+
raise AgentProfileLoadError(msg)
|
|
89
|
+
return cast("dict[str, Any]", raw)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _string_list_field(raw: object, field: str) -> list[str]:
|
|
93
|
+
"""Parse a list-of-strings field without accepting scalar iterables."""
|
|
94
|
+
if raw is None:
|
|
95
|
+
return []
|
|
96
|
+
if not isinstance(raw, list) or not all(isinstance(item, str) for item in raw):
|
|
97
|
+
msg = f"Agent profile field '{field}' must be a list of strings; got {raw!r}"
|
|
98
|
+
raise AgentProfileLoadError(msg)
|
|
99
|
+
return list(cast("list[str]", raw))
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _instructions_field(raw: object, field: str) -> str:
|
|
103
|
+
"""Parse instructions text; a list of strings joins one item per line.
|
|
104
|
+
|
|
105
|
+
Any other value would reach the prompt as its Python repr.
|
|
106
|
+
"""
|
|
107
|
+
if raw is None:
|
|
108
|
+
return ""
|
|
109
|
+
if isinstance(raw, str):
|
|
110
|
+
return raw
|
|
111
|
+
if not isinstance(raw, list):
|
|
112
|
+
msg = f"Agent profile field '{field}' must be a string or a list of strings; got {type(raw).__name__}"
|
|
113
|
+
raise AgentProfileLoadError(msg)
|
|
114
|
+
for index, item in enumerate(raw, start=1):
|
|
115
|
+
if not isinstance(item, str):
|
|
116
|
+
# YAML reads an unquoted `- Note: text` or `- Heading:` item as a mapping.
|
|
117
|
+
msg = (
|
|
118
|
+
f"Agent profile field '{field}' must be a string or a list of strings; item {index} is a "
|
|
119
|
+
f"{type(item).__name__} (quote list items that contain ': ' or end with ':')"
|
|
120
|
+
)
|
|
121
|
+
raise AgentProfileLoadError(msg)
|
|
122
|
+
return "\n".join(cast("list[str]", raw))
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _approval_rule(raw: object, field: str) -> Literal["auto", "require", "skip"]:
|
|
126
|
+
"""Parse an approval rule without YAML truthiness coercion."""
|
|
127
|
+
if not isinstance(raw, str) or raw not in _VALID_APPROVAL_RULES:
|
|
128
|
+
allowed = ", ".join(sorted(_VALID_APPROVAL_RULES))
|
|
129
|
+
msg = f"Agent profile field '{field}' must be one of: {allowed}; got {raw!r}"
|
|
130
|
+
raise AgentProfileLoadError(msg)
|
|
131
|
+
return cast("Literal['auto', 'require', 'skip']", raw)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _acp_string(raw: object, field: str, *, reject_nul: bool = False) -> str:
|
|
135
|
+
if type(raw) is not str:
|
|
136
|
+
msg = f"Agent profile field 'acp.{field}' must be a string; got {raw!r}"
|
|
137
|
+
raise AgentProfileLoadError(msg)
|
|
138
|
+
if reject_nul and "\0" in raw:
|
|
139
|
+
msg = f"Agent profile field 'acp.{field}' must not contain an embedded NUL"
|
|
140
|
+
raise AgentProfileLoadError(msg)
|
|
141
|
+
return raw
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _acp_timeout(
|
|
145
|
+
raw: object,
|
|
146
|
+
field: str,
|
|
147
|
+
*,
|
|
148
|
+
default: float,
|
|
149
|
+
allow_zero: bool,
|
|
150
|
+
) -> float:
|
|
151
|
+
if type(raw) not in {int, float}:
|
|
152
|
+
logger.warning("Invalid acp.%s %r; using default %s", field, raw, default)
|
|
153
|
+
return default
|
|
154
|
+
try:
|
|
155
|
+
value = float(cast("int | float", raw))
|
|
156
|
+
except OverflowError:
|
|
157
|
+
# YAML ints are arbitrary-precision; one absurd timeout must not
|
|
158
|
+
# abort loading the whole profile registry.
|
|
159
|
+
logger.warning("Invalid acp.%s %r; using default %s", field, raw, default)
|
|
160
|
+
return default
|
|
161
|
+
if not math.isfinite(value) or value < 0 or (not allow_zero and value == 0):
|
|
162
|
+
logger.warning("Invalid acp.%s %r; using default %s", field, raw, default)
|
|
163
|
+
return default
|
|
164
|
+
return value
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _parse_acp(raw: object) -> AcpAgentConfig:
|
|
168
|
+
"""Parse the ACP discriminator with strict per-field type enforcement."""
|
|
169
|
+
raw_map = _mapping_section(raw, "acp")
|
|
170
|
+
defaults = AcpAgentConfig()
|
|
171
|
+
|
|
172
|
+
command = _acp_string(raw_map.get("command", defaults.command), "command", reject_nul=True)
|
|
173
|
+
cwd = _acp_string(raw_map.get("cwd", defaults.cwd), "cwd", reject_nul=True)
|
|
174
|
+
session_mode = _acp_string(raw_map.get("session_mode", defaults.session_mode), "session_mode")
|
|
175
|
+
model_id = _acp_string(raw_map.get("model_id", defaults.model_id), "model_id")
|
|
176
|
+
|
|
177
|
+
raw_args = raw_map.get("args", defaults.args)
|
|
178
|
+
if type(raw_args) is not list:
|
|
179
|
+
msg = f"Agent profile field 'acp.args' must be a list of strings; got {raw_args!r}"
|
|
180
|
+
raise AgentProfileLoadError(msg)
|
|
181
|
+
args = [_acp_string(value, f"args[{index}]", reject_nul=True) for index, value in enumerate(raw_args)]
|
|
182
|
+
|
|
183
|
+
raw_env = raw_map.get("env", defaults.env)
|
|
184
|
+
if type(raw_env) is not dict:
|
|
185
|
+
msg = f"Agent profile field 'acp.env' must be a mapping of strings; got {raw_env!r}"
|
|
186
|
+
raise AgentProfileLoadError(msg)
|
|
187
|
+
env: dict[str, str] = {}
|
|
188
|
+
env_keys: set[str] = set()
|
|
189
|
+
for key, value in raw_env.items():
|
|
190
|
+
if type(key) is not str or _ENV_NAME_RE.fullmatch(key) is None:
|
|
191
|
+
msg = f"Agent profile field 'acp.env' has invalid environment variable name {key!r}"
|
|
192
|
+
raise AgentProfileLoadError(msg)
|
|
193
|
+
folded = key.casefold()
|
|
194
|
+
if folded == _ACP_DEPTH_ENV.casefold():
|
|
195
|
+
msg = f"Agent profile field 'acp.env' reserves {_ACP_DEPTH_ENV!r} for {APP_DISPLAY_NAME}"
|
|
196
|
+
raise AgentProfileLoadError(msg)
|
|
197
|
+
if folded in env_keys:
|
|
198
|
+
msg = f"Agent profile field 'acp.env' has case-insensitive duplicate key {key!r}"
|
|
199
|
+
raise AgentProfileLoadError(msg)
|
|
200
|
+
env_keys.add(folded)
|
|
201
|
+
env[key] = _acp_string(value, f"env.{key}", reject_nul=True)
|
|
202
|
+
|
|
203
|
+
raw_options = raw_map.get("config_options", defaults.config_options)
|
|
204
|
+
if type(raw_options) is not dict:
|
|
205
|
+
msg = f"Agent profile field 'acp.config_options' must be a mapping; got {raw_options!r}"
|
|
206
|
+
raise AgentProfileLoadError(msg)
|
|
207
|
+
config_options: dict[str, str | bool] = {}
|
|
208
|
+
for key, value in raw_options.items():
|
|
209
|
+
if type(key) is not str or not key:
|
|
210
|
+
msg = f"Agent profile field 'acp.config_options' keys must be non-empty strings; got {key!r}"
|
|
211
|
+
raise AgentProfileLoadError(msg)
|
|
212
|
+
if type(value) not in {str, bool}:
|
|
213
|
+
msg = (
|
|
214
|
+
"Agent profile field 'acp.config_options' values must be strings or booleans; "
|
|
215
|
+
f"got {value!r} for {key!r}"
|
|
216
|
+
)
|
|
217
|
+
raise AgentProfileLoadError(msg)
|
|
218
|
+
config_options[key] = value
|
|
219
|
+
|
|
220
|
+
allow_external_cwd = raw_map.get("allow_external_cwd", defaults.allow_external_cwd)
|
|
221
|
+
if type(allow_external_cwd) is not bool:
|
|
222
|
+
msg = f"Agent profile field 'acp.allow_external_cwd' must be a boolean; got {allow_external_cwd!r}"
|
|
223
|
+
raise AgentProfileLoadError(msg)
|
|
224
|
+
best_effort_options = raw_map.get("best_effort_options", defaults.best_effort_options)
|
|
225
|
+
if type(best_effort_options) is not bool:
|
|
226
|
+
msg = f"Agent profile field 'acp.best_effort_options' must be a boolean; got {best_effort_options!r}"
|
|
227
|
+
raise AgentProfileLoadError(msg)
|
|
228
|
+
result_mode = raw_map.get("result_mode", defaults.result_mode)
|
|
229
|
+
if type(result_mode) is not str or result_mode not in _ACP_RESULT_MODES:
|
|
230
|
+
msg = f"Agent profile field 'acp.result_mode' must be one of: last_segment, transcript; got {result_mode!r}"
|
|
231
|
+
raise AgentProfileLoadError(msg)
|
|
232
|
+
|
|
233
|
+
return AcpAgentConfig(
|
|
234
|
+
command=command,
|
|
235
|
+
args=args,
|
|
236
|
+
env=env,
|
|
237
|
+
cwd=cwd,
|
|
238
|
+
allow_external_cwd=allow_external_cwd,
|
|
239
|
+
session_mode=session_mode,
|
|
240
|
+
model_id=model_id,
|
|
241
|
+
config_options=config_options,
|
|
242
|
+
best_effort_options=best_effort_options,
|
|
243
|
+
result_mode=result_mode,
|
|
244
|
+
handshake_timeout_seconds=_acp_timeout(
|
|
245
|
+
raw_map.get("handshake_timeout_seconds", defaults.handshake_timeout_seconds),
|
|
246
|
+
"handshake_timeout_seconds",
|
|
247
|
+
default=defaults.handshake_timeout_seconds,
|
|
248
|
+
allow_zero=False,
|
|
249
|
+
),
|
|
250
|
+
idle_timeout_seconds=_acp_timeout(
|
|
251
|
+
raw_map.get("idle_timeout_seconds", defaults.idle_timeout_seconds),
|
|
252
|
+
"idle_timeout_seconds",
|
|
253
|
+
default=defaults.idle_timeout_seconds,
|
|
254
|
+
allow_zero=True,
|
|
255
|
+
),
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def _parse_mcp_servers(raw: object) -> list[MCPServerConfig]:
|
|
260
|
+
configs: list[MCPServerConfig] = []
|
|
261
|
+
for raw_entry in _list_section(raw, "tools.mcp"):
|
|
262
|
+
entry = _mapping_entry(raw_entry, "tools.mcp")
|
|
263
|
+
if "name" not in entry:
|
|
264
|
+
msg = "MCP server missing required 'name' field"
|
|
265
|
+
raise AgentProfileLoadError(msg)
|
|
266
|
+
if "transport" not in entry:
|
|
267
|
+
msg = f"MCP server '{entry.get('name', '?')}' missing required 'transport' field"
|
|
268
|
+
raise AgentProfileLoadError(msg)
|
|
269
|
+
transport = entry["transport"]
|
|
270
|
+
if transport not in _VALID_TRANSPORTS:
|
|
271
|
+
msg = f"MCP server '{entry.get('name', '?')}' has invalid transport '{transport}', must be one of {_VALID_TRANSPORTS}"
|
|
272
|
+
raise AgentProfileLoadError(msg)
|
|
273
|
+
allowed_tools = (
|
|
274
|
+
None
|
|
275
|
+
if entry.get("allowed_tools") is None
|
|
276
|
+
else _string_list_field(entry.get("allowed_tools"), "tools.mcp.allowed_tools")
|
|
277
|
+
)
|
|
278
|
+
use_progressive_disclosure = coerce_bool(entry.get("use_progressive_disclosure"), default=False)
|
|
279
|
+
tool_name_prefix = entry.get("tool_name_prefix", "")
|
|
280
|
+
prefix_error = validate_mcp_tool_name_prefix(
|
|
281
|
+
tool_name_prefix,
|
|
282
|
+
generated_suffixes=MCP_PROGRESSIVE_CONTROL_TOOL_NAMES if use_progressive_disclosure else (),
|
|
283
|
+
)
|
|
284
|
+
if prefix_error is not None:
|
|
285
|
+
msg = f"MCP server '{entry.get('name', '?')}' has invalid tool name prefix: {prefix_error}"
|
|
286
|
+
raise AgentProfileLoadError(msg)
|
|
287
|
+
always_load = _string_list_field(entry.get("always_load"), "tools.mcp.always_load")
|
|
288
|
+
policy_errors = validate_mcp_tool_loading_policy(
|
|
289
|
+
allowed_tools=allowed_tools,
|
|
290
|
+
use_progressive_disclosure=use_progressive_disclosure,
|
|
291
|
+
always_load=always_load,
|
|
292
|
+
)
|
|
293
|
+
if policy_errors:
|
|
294
|
+
msg = f"MCP server '{entry.get('name', '?')}' has invalid tool loading policy: {' '.join(policy_errors)}"
|
|
295
|
+
raise AgentProfileLoadError(msg)
|
|
296
|
+
max_tool_result_tokens = entry.get("max_tool_result_tokens")
|
|
297
|
+
if not (
|
|
298
|
+
max_tool_result_tokens is None
|
|
299
|
+
or (type(max_tool_result_tokens) is int and (max_tool_result_tokens == 0 or max_tool_result_tokens >= 100))
|
|
300
|
+
):
|
|
301
|
+
msg = (
|
|
302
|
+
f"MCP server '{entry.get('name', '?')}' has invalid max_tool_result_tokens; "
|
|
303
|
+
"expected null, 0, or an integer of at least 100"
|
|
304
|
+
)
|
|
305
|
+
raise AgentProfileLoadError(msg)
|
|
306
|
+
configs.append(
|
|
307
|
+
MCPServerConfig(
|
|
308
|
+
name=entry["name"],
|
|
309
|
+
transport=transport,
|
|
310
|
+
# stdio
|
|
311
|
+
command=entry.get("command", ""),
|
|
312
|
+
args=entry.get("args", []),
|
|
313
|
+
env=entry.get("env", {}) if transport == "stdio" else {},
|
|
314
|
+
encoding=entry.get("encoding"),
|
|
315
|
+
# http
|
|
316
|
+
url=entry.get("url", ""),
|
|
317
|
+
headers=entry.get("headers", {}),
|
|
318
|
+
resolve_header_templates=coerce_bool(entry.get("resolve_header_templates"), default=True),
|
|
319
|
+
terminate_on_close=entry.get("terminate_on_close"),
|
|
320
|
+
verify_ssl=coerce_bool(entry.get("verify_ssl"), default=True),
|
|
321
|
+
bypass_proxy=coerce_bool(entry.get("bypass_proxy"), default=False),
|
|
322
|
+
# common
|
|
323
|
+
enabled=entry.get("enabled", entry.get("enable", True)),
|
|
324
|
+
description=entry.get("description", ""),
|
|
325
|
+
tool_name_prefix=tool_name_prefix,
|
|
326
|
+
allowed_tools=allowed_tools,
|
|
327
|
+
use_progressive_disclosure=use_progressive_disclosure,
|
|
328
|
+
always_load=always_load,
|
|
329
|
+
request_timeout=entry.get("timeout", entry.get("request_timeout")),
|
|
330
|
+
max_tool_result_tokens=max_tool_result_tokens,
|
|
331
|
+
load_prompts=entry.get("load_prompts", True),
|
|
332
|
+
expose_instructions=coerce_bool(entry.get("expose_instructions"), default=True),
|
|
333
|
+
)
|
|
334
|
+
)
|
|
335
|
+
return configs
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def _parse_skill_resources(raw: object, section: str) -> list[SkillResourceConfig]:
|
|
339
|
+
resources: list[SkillResourceConfig] = []
|
|
340
|
+
for raw_entry in _list_section(raw, section):
|
|
341
|
+
entry = _mapping_entry(raw_entry, section)
|
|
342
|
+
if "name" not in entry:
|
|
343
|
+
msg = f"Skill resource in '{section}' missing required 'name' field"
|
|
344
|
+
raise AgentProfileLoadError(msg)
|
|
345
|
+
resources.append(
|
|
346
|
+
SkillResourceConfig(
|
|
347
|
+
name=entry["name"],
|
|
348
|
+
description=entry.get("description", ""),
|
|
349
|
+
content=entry.get("content", ""),
|
|
350
|
+
path=entry.get("path", ""),
|
|
351
|
+
)
|
|
352
|
+
)
|
|
353
|
+
return resources
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def _parse_skill_scripts(raw: object, section: str) -> list[SkillScriptConfig]:
|
|
357
|
+
scripts: list[SkillScriptConfig] = []
|
|
358
|
+
for raw_entry in _list_section(raw, section):
|
|
359
|
+
entry = _mapping_entry(raw_entry, section)
|
|
360
|
+
if "name" not in entry:
|
|
361
|
+
msg = f"Skill script in '{section}' missing required 'name' field"
|
|
362
|
+
raise AgentProfileLoadError(msg)
|
|
363
|
+
scripts.append(
|
|
364
|
+
SkillScriptConfig(
|
|
365
|
+
name=entry["name"],
|
|
366
|
+
description=entry.get("description", ""),
|
|
367
|
+
path=entry.get("path", ""),
|
|
368
|
+
)
|
|
369
|
+
)
|
|
370
|
+
return scripts
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def _validate_inline_skill_metadata(entry: dict[str, Any], index: int) -> None:
|
|
374
|
+
"""Validate inline profile skill metadata against the Agent Skills specification rules."""
|
|
375
|
+
location = f"skills.inline[{index}]"
|
|
376
|
+
name = entry["name"]
|
|
377
|
+
if not isinstance(name, str) or not name.strip():
|
|
378
|
+
msg = f"{location}.name must be a non-empty string"
|
|
379
|
+
raise AgentProfileLoadError(msg)
|
|
380
|
+
if not _SKILL_NAME_RE.fullmatch(name):
|
|
381
|
+
msg = (
|
|
382
|
+
f"{location}.name '{name}' is not a valid skill name; "
|
|
383
|
+
"use 1-64 lowercase letters, numbers, and single hyphens, with no leading or trailing hyphen."
|
|
384
|
+
)
|
|
385
|
+
raise AgentProfileLoadError(msg)
|
|
386
|
+
|
|
387
|
+
description = entry.get("description", "")
|
|
388
|
+
if not isinstance(description, str):
|
|
389
|
+
msg = f"{location}.description must be a string"
|
|
390
|
+
raise AgentProfileLoadError(msg)
|
|
391
|
+
if not description.strip():
|
|
392
|
+
msg = f"{location}.description is required for inline skills"
|
|
393
|
+
raise AgentProfileLoadError(msg)
|
|
394
|
+
if len(description) > _MAX_SKILL_DESCRIPTION_LENGTH:
|
|
395
|
+
msg = f"{location}.description must be {_MAX_SKILL_DESCRIPTION_LENGTH} characters or fewer"
|
|
396
|
+
raise AgentProfileLoadError(msg)
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def _parse_skills(raw: object) -> SkillsConfig:
|
|
400
|
+
if raw is None:
|
|
401
|
+
return SkillsConfig()
|
|
402
|
+
raw_map = _mapping_section(raw, "skills")
|
|
403
|
+
inline: list[SkillConfig] = []
|
|
404
|
+
for index, raw_entry in enumerate(_list_section(raw_map.get("inline", []), "skills.inline"), start=1):
|
|
405
|
+
entry = _mapping_entry(raw_entry, "skills.inline")
|
|
406
|
+
if "name" not in entry:
|
|
407
|
+
msg = "Inline skill missing required 'name' field"
|
|
408
|
+
raise AgentProfileLoadError(msg)
|
|
409
|
+
_validate_inline_skill_metadata(entry, index)
|
|
410
|
+
inline.append(
|
|
411
|
+
SkillConfig(
|
|
412
|
+
name=entry["name"],
|
|
413
|
+
description=entry.get("description", ""),
|
|
414
|
+
instructions=_instructions_field(entry.get("instructions"), f"skills.inline[{index}].instructions"),
|
|
415
|
+
resources=_parse_skill_resources(entry.get("resources", []), "skills.inline.resources"),
|
|
416
|
+
scripts=_parse_skill_scripts(entry.get("scripts", []), "skills.inline.scripts"),
|
|
417
|
+
)
|
|
418
|
+
)
|
|
419
|
+
return SkillsConfig(
|
|
420
|
+
paths=raw_map.get("paths", []),
|
|
421
|
+
inline=inline,
|
|
422
|
+
script_timeout=raw_map.get("script_timeout", 300),
|
|
423
|
+
script_extensions=raw_map.get("script_extensions", [".py", ".sh", ".ps1"]),
|
|
424
|
+
auto_load_user_agents_skills=raw_map.get("auto_load_user_agents_skills", True),
|
|
425
|
+
auto_load_cwd_agents_skills=raw_map.get("auto_load_cwd_agents_skills", True),
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def _parse_shell_filter(raw: object) -> ShellFilterConfig | None:
|
|
430
|
+
if raw is None:
|
|
431
|
+
return None
|
|
432
|
+
if isinstance(raw, str):
|
|
433
|
+
# Shorthand: shell_filter: read_only
|
|
434
|
+
return ShellFilterConfig(preset=raw)
|
|
435
|
+
raw_map = _mapping_section(raw, "tools.shell_filter")
|
|
436
|
+
return ShellFilterConfig(
|
|
437
|
+
preset=raw_map.get("preset", ""),
|
|
438
|
+
mode=raw_map.get("mode", "whitelist"),
|
|
439
|
+
commands=raw_map.get("commands", []),
|
|
440
|
+
allow_redirections=raw_map.get("allow_redirections", True),
|
|
441
|
+
allow_subshells=raw_map.get("allow_subshells", True),
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def _parse_tools(raw: object) -> ToolsConfig:
|
|
446
|
+
if raw is None:
|
|
447
|
+
return ToolsConfig()
|
|
448
|
+
from chrys.service.tools.builtins.web.config import mapping, parse_fetch, parse_search
|
|
449
|
+
|
|
450
|
+
known_keys = {"builtins", "custom", "mcp", "shell_filter", "web_search", "web_fetch"}
|
|
451
|
+
# YAML comments routinely leave sections behind as null. Older versions also
|
|
452
|
+
# ignored unknown tool keys and categories, so retain the understood subset
|
|
453
|
+
# instead of dropping an otherwise usable user override during an upgrade.
|
|
454
|
+
if isinstance(raw, dict):
|
|
455
|
+
unknown_keys = sorted(key for key in raw if isinstance(key, str) and key not in known_keys)
|
|
456
|
+
if unknown_keys:
|
|
457
|
+
logger.warning("Ignoring unknown agent tool keys: %s", ", ".join(unknown_keys))
|
|
458
|
+
raw = {
|
|
459
|
+
key: value
|
|
460
|
+
for key, value in raw.items()
|
|
461
|
+
if value is not None and (not isinstance(key, str) or key in known_keys)
|
|
462
|
+
}
|
|
463
|
+
try:
|
|
464
|
+
raw_map = mapping(
|
|
465
|
+
raw,
|
|
466
|
+
known_keys,
|
|
467
|
+
"tools",
|
|
468
|
+
nullable=frozenset({"shell_filter"}),
|
|
469
|
+
)
|
|
470
|
+
builtins = raw_map.get("builtins", [])
|
|
471
|
+
if not isinstance(builtins, list):
|
|
472
|
+
raise ValueError("tools.builtins must be a list")
|
|
473
|
+
# A stray "-" loads as null. Unknown names stay: the registry ignores them, and
|
|
474
|
+
# keeping them means a save from this version never deletes a newer version's tool.
|
|
475
|
+
if any(not isinstance(category, str) for category in builtins):
|
|
476
|
+
logger.warning("Ignoring non-string entries in tools.builtins")
|
|
477
|
+
raw_map["builtins"] = [category for category in builtins if isinstance(category, str)]
|
|
478
|
+
|
|
479
|
+
search_raw = raw_map.get("web_search")
|
|
480
|
+
if isinstance(search_raw, dict) and isinstance(search_raw.get("mode"), bool):
|
|
481
|
+
search_raw = {**search_raw, "mode": "auto" if search_raw["mode"] else "off"}
|
|
482
|
+
fetch_raw = raw_map.get("web_fetch")
|
|
483
|
+
if isinstance(fetch_raw, dict) and isinstance(fetch_raw.get("mode"), bool):
|
|
484
|
+
fetch_raw = {**fetch_raw, "mode": "on" if fetch_raw["mode"] else "off"}
|
|
485
|
+
web_search = parse_search(search_raw) if "web_search" in raw_map else None
|
|
486
|
+
web_fetch = parse_fetch(fetch_raw) if "web_fetch" in raw_map else None
|
|
487
|
+
except ValueError as err:
|
|
488
|
+
raise AgentProfileLoadError(str(err)) from err
|
|
489
|
+
return ToolsConfig(
|
|
490
|
+
builtins=raw_map.get("builtins", []),
|
|
491
|
+
custom=raw_map.get("custom", []),
|
|
492
|
+
mcp=_parse_mcp_servers(raw_map.get("mcp", [])),
|
|
493
|
+
shell_filter=_parse_shell_filter(raw_map.get("shell_filter")),
|
|
494
|
+
web_search=web_search,
|
|
495
|
+
web_fetch=web_fetch,
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
def _parse_approval(raw: object) -> ApprovalConfig:
|
|
500
|
+
if raw is None:
|
|
501
|
+
return ApprovalConfig()
|
|
502
|
+
raw_map = _mapping_section(raw, "approval")
|
|
503
|
+
defaults = ApprovalConfig()
|
|
504
|
+
raw_overrides = raw_map.get("overrides")
|
|
505
|
+
if raw_overrides is None:
|
|
506
|
+
overrides = dict(defaults.overrides)
|
|
507
|
+
else:
|
|
508
|
+
if not isinstance(raw_overrides, dict):
|
|
509
|
+
msg = f"Agent profile section 'approval.overrides' must be a mapping, got {type(raw_overrides).__name__}"
|
|
510
|
+
raise AgentProfileLoadError(msg)
|
|
511
|
+
overrides = {}
|
|
512
|
+
for key, value in raw_overrides.items():
|
|
513
|
+
if not isinstance(key, str) or not key:
|
|
514
|
+
msg = f"Agent profile section 'approval.overrides' keys must be non-empty strings, got {key!r}"
|
|
515
|
+
raise AgentProfileLoadError(msg)
|
|
516
|
+
# Keys are bare kinds, ``kind.tool_name`` compounds, or bare tool
|
|
517
|
+
# names; legacy pre-P1 keys carried a ``chrys.`` prefix.
|
|
518
|
+
normalized_key = strip_legacy_kind_prefix(key, allow_tool_suffix=True, source="approval.overrides")
|
|
519
|
+
overrides[normalized_key] = _approval_rule(value, f"approval.overrides.{normalized_key}")
|
|
520
|
+
return ApprovalConfig(
|
|
521
|
+
default=_approval_rule(raw_map.get("default", defaults.default), "approval.default"),
|
|
522
|
+
overrides=overrides,
|
|
523
|
+
user_can_override=raw_map.get("user_can_override", defaults.user_can_override),
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
def _parse_model(raw: object, source: str = "") -> ModelConfig:
|
|
528
|
+
if raw is None:
|
|
529
|
+
return ModelConfig()
|
|
530
|
+
raw_map = _mapping_section(raw, "model")
|
|
531
|
+
profile_id = (raw_map.get("profile_id") or "").strip()
|
|
532
|
+
# Legacy key ``profile`` (previously stored a model-profile *name*) is
|
|
533
|
+
# ignored — the new schema is id-only. Emit a one-line warning so
|
|
534
|
+
# users know to re-select their model profile via the TUI.
|
|
535
|
+
if not profile_id and raw_map.get("profile"):
|
|
536
|
+
logger.warning(
|
|
537
|
+
"Ignoring legacy `model.profile` field in %s — replace with `model.profile_id: <id>` "
|
|
538
|
+
"or re-select the model profile in the agent configuration screen.",
|
|
539
|
+
source or "agent profile YAML",
|
|
540
|
+
)
|
|
541
|
+
return ModelConfig(profile_id=profile_id)
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
def _parse_compaction(raw: object) -> CompactionConfig:
|
|
545
|
+
if raw is None:
|
|
546
|
+
return CompactionConfig()
|
|
547
|
+
raw_map = _mapping_section(raw, "compaction")
|
|
548
|
+
defaults = CompactionConfig()
|
|
549
|
+
legacy_keys = {"reserved_context_pct", "compaction_target_pct", "force_compress_pct"}
|
|
550
|
+
reserved = raw_map.get("reserved_context_pct")
|
|
551
|
+
legacy_disabled = isinstance(reserved, int | float) and not isinstance(reserved, bool) and reserved >= 1.0
|
|
552
|
+
if legacy_disabled:
|
|
553
|
+
logger.warning(
|
|
554
|
+
"Compaction thresholds are now derived from the model profile; this profile stays disabled "
|
|
555
|
+
"via the legacy reserved_context_pct >= 1.0 switch for this load"
|
|
556
|
+
)
|
|
557
|
+
ignored_legacy = (legacy_keys & raw_map.keys()) - ({"reserved_context_pct"} if legacy_disabled else set())
|
|
558
|
+
if ignored_legacy:
|
|
559
|
+
logger.info(
|
|
560
|
+
"Ignoring obsolete compaction threshold field(s): %s; thresholds are derived from the model profile",
|
|
561
|
+
", ".join(sorted(ignored_legacy)),
|
|
562
|
+
)
|
|
563
|
+
last_words_template = raw_map.get("last_words_template", defaults.last_words_template)
|
|
564
|
+
if not isinstance(last_words_template, str):
|
|
565
|
+
msg = f"Agent profile field 'compaction.last_words_template' must be a string; got {last_words_template!r}"
|
|
566
|
+
raise AgentProfileLoadError(msg)
|
|
567
|
+
phase4_side_call_token_budget = raw_map.get(
|
|
568
|
+
"phase4_side_call_token_budget",
|
|
569
|
+
defaults.phase4_side_call_token_budget,
|
|
570
|
+
)
|
|
571
|
+
if phase4_side_call_token_budget is None:
|
|
572
|
+
# Explicit ``null`` keeps its pre--1 meaning: use the default.
|
|
573
|
+
phase4_side_call_token_budget = defaults.phase4_side_call_token_budget
|
|
574
|
+
if (
|
|
575
|
+
not isinstance(phase4_side_call_token_budget, int)
|
|
576
|
+
or isinstance(phase4_side_call_token_budget, bool)
|
|
577
|
+
or phase4_side_call_token_budget < -1
|
|
578
|
+
):
|
|
579
|
+
msg = (
|
|
580
|
+
"Agent profile field 'compaction.phase4_side_call_token_budget' must be "
|
|
581
|
+
f"an integer >= -1 (-1 means unlimited) or null; got {phase4_side_call_token_budget!r}"
|
|
582
|
+
)
|
|
583
|
+
raise AgentProfileLoadError(msg)
|
|
584
|
+
return CompactionConfig(
|
|
585
|
+
enabled=not legacy_disabled,
|
|
586
|
+
last_words_template=last_words_template,
|
|
587
|
+
last_words_max_output_tokens=raw_map.get(
|
|
588
|
+
"last_words_max_output_tokens",
|
|
589
|
+
defaults.last_words_max_output_tokens,
|
|
590
|
+
),
|
|
591
|
+
phase4_side_call_token_budget=phase4_side_call_token_budget,
|
|
592
|
+
)
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
def _parse_memory(raw: object) -> MemoryConfig:
|
|
596
|
+
if raw is None:
|
|
597
|
+
return MemoryConfig()
|
|
598
|
+
raw_map = _mapping_section(raw, "memory")
|
|
599
|
+
return MemoryConfig(
|
|
600
|
+
files=list(raw_map.get("files", [])),
|
|
601
|
+
folders=list(raw_map.get("folders", [])),
|
|
602
|
+
)
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
def _parse_sub_agents(raw: object) -> SubAgentsConfig:
|
|
606
|
+
if raw is None:
|
|
607
|
+
return SubAgentsConfig()
|
|
608
|
+
raw_map = _mapping_section(raw, "sub_agents")
|
|
609
|
+
agents = [
|
|
610
|
+
SubAgentRef(
|
|
611
|
+
profile=entry["profile"],
|
|
612
|
+
tool_name=entry.get("tool_name", ""),
|
|
613
|
+
tool_description=entry.get("tool_description", ""),
|
|
614
|
+
max_concurrency=entry.get("max_concurrency", DEFAULT_SUB_AGENT_CONCURRENCY),
|
|
615
|
+
)
|
|
616
|
+
for entry in (
|
|
617
|
+
_mapping_entry(raw_entry, "sub_agents.agents")
|
|
618
|
+
for raw_entry in _list_section(raw_map.get("agents", []), "sub_agents.agents")
|
|
619
|
+
)
|
|
620
|
+
if "profile" in entry
|
|
621
|
+
]
|
|
622
|
+
return SubAgentsConfig(
|
|
623
|
+
max_total_concurrency=raw_map.get("max_total_concurrency", DEFAULT_SUB_AGENT_CONCURRENCY),
|
|
624
|
+
agents=agents,
|
|
625
|
+
)
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
def is_filename_safe_profile_name(name: str) -> bool:
|
|
629
|
+
"""Return True when ``name`` can serve as a bare ``<name>.yaml`` stem.
|
|
630
|
+
|
|
631
|
+
Every persistence path resolves a user profile as ``<name>.yaml`` under
|
|
632
|
+
the profiles directory, so a name carrying separators, drive or root
|
|
633
|
+
prefixes, ``.``/``..``, or NUL would escape that directory. The
|
|
634
|
+
resulting filename must also be legal on every platform a config
|
|
635
|
+
directory can be shared with: Windows device names (``CON``, ``NUL``,
|
|
636
|
+
``COM1`` …), NTFS stream syntax (``foo:bar``), wildcard and control
|
|
637
|
+
characters are rejected regardless of the current OS. The ACP write
|
|
638
|
+
path uses the same predicate.
|
|
639
|
+
"""
|
|
640
|
+
if not name or name != name.strip() or name in {".", ".."} or "\0" in name:
|
|
641
|
+
return False
|
|
642
|
+
posix_path = PurePath(name)
|
|
643
|
+
windows_path = PureWindowsPath(name)
|
|
644
|
+
if (
|
|
645
|
+
posix_path.is_absolute()
|
|
646
|
+
or windows_path.is_absolute()
|
|
647
|
+
or windows_path.drive
|
|
648
|
+
or posix_path.name != name
|
|
649
|
+
or windows_path.name != name
|
|
650
|
+
):
|
|
651
|
+
return False
|
|
652
|
+
return not ntpath.isreserved(f"{name}.yaml")
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
def load_profile_from_yaml(path: Path) -> AgentProfile:
|
|
656
|
+
"""Load an agent profile from a YAML file.
|
|
657
|
+
|
|
658
|
+
Raises:
|
|
659
|
+
AgentProfileLoadError: If the file cannot be read or is missing required fields.
|
|
660
|
+
"""
|
|
661
|
+
try:
|
|
662
|
+
text = decode_bytes(path.read_bytes())
|
|
663
|
+
except FileNotFoundError as e:
|
|
664
|
+
raise AgentProfileLoadError(f"Agent profile file not found: {path}") from e
|
|
665
|
+
except OSError as e:
|
|
666
|
+
raise AgentProfileLoadError(f"Cannot read agent profile file {path}: {e}") from e
|
|
667
|
+
|
|
668
|
+
try:
|
|
669
|
+
data = yaml.safe_load(text)
|
|
670
|
+
except (yaml.YAMLError, AttributeError, LookupError, TypeError, ValueError) as e:
|
|
671
|
+
# PyYAML scalar constructors can raise built-in errors for invalid values.
|
|
672
|
+
raise AgentProfileLoadError(f"Invalid YAML in {path}: {e}") from e
|
|
673
|
+
|
|
674
|
+
if not isinstance(data, dict):
|
|
675
|
+
raise AgentProfileLoadError(f"Agent profile YAML must be a mapping, got {type(data).__name__} in {path}")
|
|
676
|
+
|
|
677
|
+
if "name" not in data:
|
|
678
|
+
raise AgentProfileLoadError(f"Agent profile YAML missing required 'name' field in {path}")
|
|
679
|
+
raw_name = data["name"]
|
|
680
|
+
if not isinstance(raw_name, str) or not raw_name.strip():
|
|
681
|
+
raise AgentProfileLoadError(f"Agent profile 'name' must be a non-empty string in {path}")
|
|
682
|
+
name = raw_name.strip()
|
|
683
|
+
if not is_filename_safe_profile_name(name):
|
|
684
|
+
raise AgentProfileLoadError(
|
|
685
|
+
f"Agent profile name {name!r} in {path} is not filename-safe; use a bare name, not a path"
|
|
686
|
+
)
|
|
687
|
+
|
|
688
|
+
if "hooks" in data:
|
|
689
|
+
logger.warning(
|
|
690
|
+
"Ignoring legacy `hooks` section in %s. Configure lifecycle hooks in the global hooks config instead.",
|
|
691
|
+
path,
|
|
692
|
+
)
|
|
693
|
+
|
|
694
|
+
raw_id = data.get("id")
|
|
695
|
+
if raw_id is not None and not isinstance(raw_id, str):
|
|
696
|
+
raise AgentProfileLoadError(f"Agent profile 'id' must be a string or null in {path}")
|
|
697
|
+
profile_id = (raw_id or "").strip()
|
|
698
|
+
acp_present = "acp" in data
|
|
699
|
+
|
|
700
|
+
try:
|
|
701
|
+
profile = AgentProfile(
|
|
702
|
+
name=name,
|
|
703
|
+
id=profile_id,
|
|
704
|
+
display_name=data.get("display_name", ""),
|
|
705
|
+
description=data.get("description", ""),
|
|
706
|
+
sub_agent_only=bool(data.get("sub_agent_only", False)),
|
|
707
|
+
acp=_parse_acp(data.get("acp")) if acp_present else None,
|
|
708
|
+
instructions=_instructions_field(data.get("instructions"), "instructions"),
|
|
709
|
+
tools=_parse_tools(data.get("tools")),
|
|
710
|
+
skills=_parse_skills(data.get("skills")),
|
|
711
|
+
approval=_parse_approval(data.get("approval")),
|
|
712
|
+
sub_agents=_parse_sub_agents(data.get("sub_agents")),
|
|
713
|
+
model=_parse_model(data.get("model"), source=str(path)),
|
|
714
|
+
compaction=_parse_compaction(data.get("compaction")),
|
|
715
|
+
memory=_parse_memory(data.get("memory")),
|
|
716
|
+
metadata=data.get("metadata", {}),
|
|
717
|
+
)
|
|
718
|
+
if not acp_present:
|
|
719
|
+
return profile
|
|
720
|
+
if not profile.sub_agent_only:
|
|
721
|
+
logger.warning("ACP profile %s must be sub-agent-only; forcing sub_agent_only=true", profile.name)
|
|
722
|
+
profile.sub_agent_only = True
|
|
723
|
+
|
|
724
|
+
conflicting: list[str] = []
|
|
725
|
+
if profile.tools != ToolsConfig():
|
|
726
|
+
conflicting.append("tools")
|
|
727
|
+
profile.tools = ToolsConfig()
|
|
728
|
+
if profile.skills != SkillsConfig():
|
|
729
|
+
conflicting.append("skills")
|
|
730
|
+
profile.skills = SkillsConfig()
|
|
731
|
+
if profile.sub_agents.agents:
|
|
732
|
+
conflicting.append("sub_agents.agents")
|
|
733
|
+
profile.sub_agents = SubAgentsConfig()
|
|
734
|
+
if profile.memory != MemoryConfig():
|
|
735
|
+
conflicting.append("memory")
|
|
736
|
+
profile.memory = MemoryConfig()
|
|
737
|
+
if profile.compaction != CompactionConfig():
|
|
738
|
+
conflicting.append("compaction")
|
|
739
|
+
profile.compaction = CompactionConfig()
|
|
740
|
+
if profile.instructions:
|
|
741
|
+
conflicting.append("instructions")
|
|
742
|
+
profile.instructions = ""
|
|
743
|
+
if profile.approval != ApprovalConfig():
|
|
744
|
+
conflicting.append("approval")
|
|
745
|
+
profile.approval = ApprovalConfig()
|
|
746
|
+
if profile.model.profile_id:
|
|
747
|
+
conflicting.append("model.profile_id")
|
|
748
|
+
profile.model = ModelConfig()
|
|
749
|
+
for section in conflicting:
|
|
750
|
+
logger.warning("ACP profile %s ignores conflicting section %s; reset to defaults", profile.name, section)
|
|
751
|
+
return profile
|
|
752
|
+
except AgentProfileLoadError:
|
|
753
|
+
raise
|
|
754
|
+
except (AttributeError, KeyError, TypeError, ValueError) as e:
|
|
755
|
+
raise AgentProfileLoadError(f"Invalid agent profile structure in {path}: {e}") from e
|
|
756
|
+
|
|
757
|
+
|
|
758
|
+
@dataclass(frozen=True)
|
|
759
|
+
class ProfileDirScan:
|
|
760
|
+
"""The profiles parsed from one directory, with their source paths, and the files skipped as invalid."""
|
|
761
|
+
|
|
762
|
+
loaded: list[tuple[Path, AgentProfile]]
|
|
763
|
+
invalid: list[Path]
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
def scan_profile_dir(directory: Path) -> ProfileDirScan:
|
|
767
|
+
"""Load all agent profile YAML files from a directory, recording the ones that fail to parse.
|
|
768
|
+
|
|
769
|
+
Non-YAML files are ignored, and so are dotfiles even when their suffix is
|
|
770
|
+
YAML. Files that fail to parse are skipped with a warning and listed in
|
|
771
|
+
``invalid``. ``*.yaml`` files come before ``*.yml`` files, each group in
|
|
772
|
+
sorted order.
|
|
773
|
+
"""
|
|
774
|
+
loaded: list[tuple[Path, AgentProfile]] = []
|
|
775
|
+
invalid: list[Path] = []
|
|
776
|
+
if not directory.is_dir():
|
|
777
|
+
return ProfileDirScan(loaded, invalid)
|
|
778
|
+
for pattern in ("*.yaml", "*.yml"):
|
|
779
|
+
for path in sorted(directory.glob(pattern)):
|
|
780
|
+
if path.name.startswith("."):
|
|
781
|
+
continue
|
|
782
|
+
try:
|
|
783
|
+
loaded.append((path, load_profile_from_yaml(path)))
|
|
784
|
+
except AgentProfileLoadError as exc:
|
|
785
|
+
# Skip invalid files — caller can use load_profile_from_yaml for strict loading.
|
|
786
|
+
# A dropped user file silently falls back to a builtin of the same name, so
|
|
787
|
+
# this must be loud enough to be noticed in a support log.
|
|
788
|
+
logger.error(
|
|
789
|
+
"Skipping invalid agent profile %s: %s (any builtin with this name stays active)", path, exc
|
|
790
|
+
)
|
|
791
|
+
invalid.append(path)
|
|
792
|
+
return ProfileDirScan(loaded, invalid)
|
|
793
|
+
|
|
794
|
+
|
|
795
|
+
def load_profile_files_from_dir(directory: Path) -> list[tuple[Path, AgentProfile]]:
|
|
796
|
+
"""Load all agent profile YAML files from a directory, keeping their source paths.
|
|
797
|
+
|
|
798
|
+
See :func:`scan_profile_dir` for the scan rules.
|
|
799
|
+
"""
|
|
800
|
+
return scan_profile_dir(directory).loaded
|
|
801
|
+
|
|
802
|
+
|
|
803
|
+
def load_profiles_from_dir(directory: Path) -> list[AgentProfile]:
|
|
804
|
+
"""Load all agent profile YAML files from a directory.
|
|
805
|
+
|
|
806
|
+
See :func:`scan_profile_dir` for the scan rules.
|
|
807
|
+
"""
|
|
808
|
+
return [profile for _path, profile in load_profile_files_from_dir(directory)]
|