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,792 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""Memory loader — reads ``MemoryConfig`` files/folders into a renderable system-prompt block.
|
|
4
|
+
|
|
5
|
+
Resolves relative user-configured paths against the workspace cwd, accepts
|
|
6
|
+
absolute paths for files and folders, filters to ``.md`` and ``.txt`` files,
|
|
7
|
+
scans folders breadth-first with hard bounds (``FOLDER_MAX_DEPTH``
|
|
8
|
+
subdirectory levels, ``FOLDER_MAX_FILES`` files per folder, symlink cycle
|
|
9
|
+
detection), decodes via
|
|
10
|
+
:func:`chrys.foundation.text.encoding.decode_bytes`, and renders the combined content
|
|
11
|
+
into a markdown block ready for injection by
|
|
12
|
+
:class:`chrys.service.context.providers.memory.MemoryProvider`. Each
|
|
13
|
+
physical file loads at most once — explicit file entries win over
|
|
14
|
+
folder-discovered duplicates.
|
|
15
|
+
|
|
16
|
+
Loading is capped at a token budget (default 35,000). If the cap is hit,
|
|
17
|
+
the remaining files are reported as ``skipped_files`` and ``truncated`` is
|
|
18
|
+
set so callers (``agent_builder``) can surface a ``Warning`` event.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import logging
|
|
24
|
+
import os
|
|
25
|
+
import posixpath
|
|
26
|
+
from collections import deque
|
|
27
|
+
from dataclasses import dataclass, field
|
|
28
|
+
from itertools import islice
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
|
|
31
|
+
from chrys.foundation.events.types import Warning
|
|
32
|
+
from chrys.foundation.i18n import msg
|
|
33
|
+
from chrys.foundation.platform.paths import is_absolute_path as _is_absolute_path
|
|
34
|
+
from chrys.foundation.text.encoding import decode_bytes
|
|
35
|
+
from chrys.foundation.text.tokenizer import MixedLanguageTokenizer
|
|
36
|
+
from chrys.service.profiles.agents.schema import MemoryConfig
|
|
37
|
+
|
|
38
|
+
logger = logging.getLogger(__name__)
|
|
39
|
+
|
|
40
|
+
DEFAULT_TOKEN_CAP = 35_000
|
|
41
|
+
|
|
42
|
+
_MEMORY_TRUNCATED = msg(
|
|
43
|
+
"builder.memory_truncated",
|
|
44
|
+
fallback="Auto-loaded memory truncated at the token cap. Loaded {loaded_count} file(s); skipped {skipped_count}.",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
_ALLOWED_SUFFIXES = (".md", ".txt")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class MemoryContent:
|
|
52
|
+
"""Result of :func:`load_memory_content`.
|
|
53
|
+
|
|
54
|
+
``text`` is the rendered markdown block (empty when nothing was loaded).
|
|
55
|
+
``loaded_files`` and ``skipped_files`` are workspace-cwd-relative paths
|
|
56
|
+
for workspace-contained files, or absolute paths for files loaded via
|
|
57
|
+
absolute file/folder entries outside the workspace.
|
|
58
|
+
``loaded_files_by_source`` maps each configured file/folder location
|
|
59
|
+
to the display paths loaded from it.
|
|
60
|
+
``missing_paths`` lists configured entries (files or folders) that did
|
|
61
|
+
not exist on disk.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
text: str = ""
|
|
65
|
+
loaded_files: list[str] = field(default_factory=list)
|
|
66
|
+
loaded_files_by_source: dict[str, list[str]] = field(default_factory=dict)
|
|
67
|
+
skipped_files: list[str] = field(default_factory=list)
|
|
68
|
+
missing_paths: list[str] = field(default_factory=list)
|
|
69
|
+
token_count: int = 0
|
|
70
|
+
truncated: bool = False
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def memory_truncated_warning(content: MemoryContent, *, session_id: str | None) -> Warning:
|
|
74
|
+
"""The warning every agent build publishes when *content* stopped at the token cap.
|
|
75
|
+
|
|
76
|
+
Truncation is surfaced because the agent's reference context is incomplete;
|
|
77
|
+
missing paths are silently skipped, since built-in profiles configure optional
|
|
78
|
+
files such as ``AGENTS.md`` that need not exist in every workspace.
|
|
79
|
+
"""
|
|
80
|
+
loaded = len(content.loaded_files)
|
|
81
|
+
skipped = len(content.skipped_files)
|
|
82
|
+
return Warning(
|
|
83
|
+
code="memory_truncated",
|
|
84
|
+
message=f"Auto-loaded memory truncated at the token cap. Loaded {loaded} file(s); skipped {skipped}.",
|
|
85
|
+
display_message=_MEMORY_TRUNCATED.bind(loaded_count=loaded, skipped_count=skipped),
|
|
86
|
+
session_id=session_id,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@dataclass
|
|
91
|
+
class _Candidate:
|
|
92
|
+
"""A single file selected for loading, with display metadata.
|
|
93
|
+
|
|
94
|
+
``rel_path`` is the display path used in the rendered ``## File:`` header.
|
|
95
|
+
It is workspace-relative for workspace-contained files and absolute for
|
|
96
|
+
absolute files outside the workspace. ``folder_root`` is non-empty only
|
|
97
|
+
for files discovered via the bounded folder scan — used to group them
|
|
98
|
+
under a ``## Folder:`` header.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
abs_path: Path
|
|
102
|
+
rel_path: str
|
|
103
|
+
source: str = ""
|
|
104
|
+
folder_root: str = ""
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def load_memory_content(
|
|
108
|
+
memory: MemoryConfig,
|
|
109
|
+
workspace_cwd: str | None,
|
|
110
|
+
*,
|
|
111
|
+
token_cap: int = DEFAULT_TOKEN_CAP,
|
|
112
|
+
) -> MemoryContent:
|
|
113
|
+
"""Load and render auto-load memory files for the given workspace.
|
|
114
|
+
|
|
115
|
+
Returns a zero-state :class:`MemoryContent` when there is nothing to
|
|
116
|
+
load (no workspace, no configured paths). Otherwise builds the
|
|
117
|
+
markdown block in files-first / folder-grouped order, capping at
|
|
118
|
+
``token_cap`` total tokens.
|
|
119
|
+
"""
|
|
120
|
+
if not workspace_cwd or (not memory.files and not memory.folders):
|
|
121
|
+
return MemoryContent()
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
base = Path(workspace_cwd).resolve()
|
|
125
|
+
except OSError:
|
|
126
|
+
return MemoryContent()
|
|
127
|
+
missing: list[str] = []
|
|
128
|
+
file_candidates: list[_Candidate] = []
|
|
129
|
+
folder_candidates: dict[str, list[_Candidate]] = {}
|
|
130
|
+
# Each physical file loads once: explicit files win over
|
|
131
|
+
# folder-discovered duplicates, earlier folders over later ones.
|
|
132
|
+
# Identity is filesystem-aware (see _file_identity) so case aliases on
|
|
133
|
+
# case-insensitive filesystems and symlinked spellings collapse.
|
|
134
|
+
seen_files: set[object] = set()
|
|
135
|
+
|
|
136
|
+
# Files: each configured path resolves to a single file. Relative files
|
|
137
|
+
# stay workspace-contained; absolute files are intentionally allowed.
|
|
138
|
+
for raw in memory.files:
|
|
139
|
+
path = _safe_resolve_file(raw, base)
|
|
140
|
+
if path is None or not path.is_file() or path.suffix.lower() not in _ALLOWED_SUFFIXES:
|
|
141
|
+
missing.append(raw)
|
|
142
|
+
continue
|
|
143
|
+
identity = _file_identity(path)
|
|
144
|
+
if identity in seen_files:
|
|
145
|
+
continue
|
|
146
|
+
seen_files.add(identity)
|
|
147
|
+
rel_path = _rel(path, base)
|
|
148
|
+
file_candidates.append(_Candidate(abs_path=path, rel_path=rel_path, source=rel_path))
|
|
149
|
+
|
|
150
|
+
# Folders: bounded breadth-first scan (see _walk_folder) with symlink
|
|
151
|
+
# cycle detection + containment. Relative folders must stay inside the
|
|
152
|
+
# workspace; absolute folders are intentionally allowed anywhere
|
|
153
|
+
# (matching absolute files) but their scan is contained to the folder
|
|
154
|
+
# itself so a symlink inside cannot surface files from elsewhere.
|
|
155
|
+
seen_folders: set[object] = set()
|
|
156
|
+
for raw in memory.folders:
|
|
157
|
+
folder = _safe_resolve_folder(raw, base)
|
|
158
|
+
if folder is None or not folder.is_dir():
|
|
159
|
+
missing.append(raw)
|
|
160
|
+
continue
|
|
161
|
+
folder_identity = _file_identity(folder)
|
|
162
|
+
if folder_identity in seen_folders:
|
|
163
|
+
# Alias (symlink / case spelling) of an already-scanned folder —
|
|
164
|
+
# its files are all loaded there; re-registering would overwrite
|
|
165
|
+
# that group with an empty deduped list.
|
|
166
|
+
continue
|
|
167
|
+
seen_folders.add(folder_identity)
|
|
168
|
+
# Workspace-contained folders keep the workspace as the containment
|
|
169
|
+
# root (a symlink may legitimately target elsewhere in the
|
|
170
|
+
# workspace); folders outside it are contained to themselves.
|
|
171
|
+
containment_root = base if folder.is_relative_to(base) else folder
|
|
172
|
+
# The walk itself filters through seen_files so duplicates of
|
|
173
|
+
# already-loaded files never consume the per-folder cap.
|
|
174
|
+
discovered = _walk_folder(folder, containment_root, seen_files)
|
|
175
|
+
folder_root = _rel(folder, base)
|
|
176
|
+
# Normalize folder header to always end with `/` so the header is
|
|
177
|
+
# visually distinct from a file path.
|
|
178
|
+
if not folder_root.endswith("/"):
|
|
179
|
+
folder_root += "/"
|
|
180
|
+
folder_candidates[folder_root] = [
|
|
181
|
+
_Candidate(abs_path=p, rel_path=_rel(p, base), source=folder_root, folder_root=folder_root)
|
|
182
|
+
for p in discovered
|
|
183
|
+
]
|
|
184
|
+
|
|
185
|
+
# Render header + sections, counting tokens incrementally. We hold the
|
|
186
|
+
# rendered pieces in a list and join at the end to avoid quadratic
|
|
187
|
+
# string concatenation, but token-count each piece as it's added.
|
|
188
|
+
tokenizer = MixedLanguageTokenizer()
|
|
189
|
+
pieces: list[str] = [_HEADER]
|
|
190
|
+
token_count = tokenizer.count_tokens(_HEADER)
|
|
191
|
+
loaded: list[str] = []
|
|
192
|
+
loaded_by_source: dict[str, list[str]] = {}
|
|
193
|
+
skipped: list[str] = []
|
|
194
|
+
truncated = False
|
|
195
|
+
|
|
196
|
+
def _try_add(section: str, rel_path: str, source: str) -> bool:
|
|
197
|
+
"""Add a section if it fits the cap. Return False on overflow."""
|
|
198
|
+
nonlocal token_count, truncated
|
|
199
|
+
section_tokens = tokenizer.count_tokens(section)
|
|
200
|
+
if token_count + section_tokens > token_cap:
|
|
201
|
+
truncated = True
|
|
202
|
+
skipped.append(rel_path)
|
|
203
|
+
return False
|
|
204
|
+
pieces.append(section)
|
|
205
|
+
token_count += section_tokens
|
|
206
|
+
loaded.append(rel_path)
|
|
207
|
+
loaded_by_source.setdefault(source or rel_path, []).append(rel_path)
|
|
208
|
+
return True
|
|
209
|
+
|
|
210
|
+
# Files first.
|
|
211
|
+
for cand in file_candidates:
|
|
212
|
+
if truncated:
|
|
213
|
+
skipped.append(cand.rel_path)
|
|
214
|
+
continue
|
|
215
|
+
section = _render_file_section(cand, level=2)
|
|
216
|
+
if section is None:
|
|
217
|
+
missing.append(cand.rel_path)
|
|
218
|
+
continue
|
|
219
|
+
_try_add(section, cand.rel_path, cand.source)
|
|
220
|
+
|
|
221
|
+
# Then folders, grouped under a ``## Folder:`` header.
|
|
222
|
+
for folder_root, cands in folder_candidates.items():
|
|
223
|
+
if not cands:
|
|
224
|
+
continue
|
|
225
|
+
if truncated:
|
|
226
|
+
skipped.extend(c.rel_path for c in cands)
|
|
227
|
+
continue
|
|
228
|
+
# The folder header itself is cheap; include it eagerly even if
|
|
229
|
+
# subsequent files overflow — the agent still sees a clear
|
|
230
|
+
# "we tried to load this folder" anchor.
|
|
231
|
+
folder_header = f"\n## Folder: {folder_root}\n"
|
|
232
|
+
header_tokens = tokenizer.count_tokens(folder_header)
|
|
233
|
+
if token_count + header_tokens > token_cap:
|
|
234
|
+
truncated = True
|
|
235
|
+
skipped.extend(c.rel_path for c in cands)
|
|
236
|
+
continue
|
|
237
|
+
pieces.append(folder_header)
|
|
238
|
+
token_count += header_tokens
|
|
239
|
+
for cand in cands:
|
|
240
|
+
if truncated:
|
|
241
|
+
skipped.append(cand.rel_path)
|
|
242
|
+
continue
|
|
243
|
+
section = _render_file_section(cand, level=3)
|
|
244
|
+
if section is None:
|
|
245
|
+
missing.append(cand.rel_path)
|
|
246
|
+
continue
|
|
247
|
+
_try_add(section, cand.rel_path, cand.source)
|
|
248
|
+
|
|
249
|
+
if not loaded:
|
|
250
|
+
# Nothing actually got rendered; collapse to empty content so the
|
|
251
|
+
# provider stays a no-op. We still surface ``missing_paths`` /
|
|
252
|
+
# ``skipped_files`` so the caller can publish warnings.
|
|
253
|
+
return MemoryContent(
|
|
254
|
+
text="",
|
|
255
|
+
loaded_files=[],
|
|
256
|
+
loaded_files_by_source={},
|
|
257
|
+
skipped_files=skipped,
|
|
258
|
+
missing_paths=missing,
|
|
259
|
+
token_count=0,
|
|
260
|
+
truncated=truncated,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# Recompute the final count over the joined text — tokenizing piece
|
|
264
|
+
# by piece during accumulation can drift by a token or two vs. the
|
|
265
|
+
# whole-text count (heuristic tokenizer boundaries shift across
|
|
266
|
+
# joins, esp. with mixed line endings on Windows). The in-loop sum
|
|
267
|
+
# is fine as a conservative budget gate; what we *report* should
|
|
268
|
+
# equal what consumers see.
|
|
269
|
+
text = "".join(pieces)
|
|
270
|
+
return MemoryContent(
|
|
271
|
+
text=text,
|
|
272
|
+
loaded_files=loaded,
|
|
273
|
+
loaded_files_by_source=loaded_by_source,
|
|
274
|
+
skipped_files=skipped,
|
|
275
|
+
missing_paths=missing,
|
|
276
|
+
token_count=tokenizer.count_tokens(text),
|
|
277
|
+
truncated=truncated,
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
_HEADER = (
|
|
282
|
+
"# Auto-loaded memory\n\n"
|
|
283
|
+
"The following files were auto-loaded as reference context for the current task.\n"
|
|
284
|
+
"Relative paths are resolved from the workspace working directory.\n"
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def validate_memory_config(memory: MemoryConfig, *, workspace_cwd: str | None = None) -> list[str]:
|
|
289
|
+
"""Validate that a :class:`MemoryConfig` is internally consistent.
|
|
290
|
+
|
|
291
|
+
Errors checked:
|
|
292
|
+
|
|
293
|
+
* Files must end in ``.md`` or ``.txt``.
|
|
294
|
+
* No duplicate files (compared case-insensitively after path
|
|
295
|
+
normalization, so ``AGENTS.md`` and ``agents.md`` collide and
|
|
296
|
+
``docs/spec.md`` collides with ``./docs/spec.md``).
|
|
297
|
+
* No duplicate folders, same comparison rules.
|
|
298
|
+
* No filesystem root as a folder ("/", "C:\\") — even the bounded
|
|
299
|
+
folder scan has no business starting at one; the loader refuses
|
|
300
|
+
them too.
|
|
301
|
+
|
|
302
|
+
A file inside a configured folder is deliberately NOT an error: the
|
|
303
|
+
bounded folder scan (depth / file-count caps) does not guarantee the
|
|
304
|
+
folder reaches it, so the explicit entry is how users pin a deep or
|
|
305
|
+
over-cap file. The loader deduplicates when both do load it.
|
|
306
|
+
|
|
307
|
+
Indices in error messages are **1-based positions in the original
|
|
308
|
+
list** so they line up with the per-card display in the UI. Empty /
|
|
309
|
+
whitespace-only entries are skipped without error (the panel filters
|
|
310
|
+
them on save) but still consume an index slot to keep this alignment.
|
|
311
|
+
When ``workspace_cwd`` is provided, relative paths are normalized through
|
|
312
|
+
that cwd before duplicate checks.
|
|
313
|
+
Absolute file and folder entries are accepted even when they target
|
|
314
|
+
another host; incompatible host paths are reported as missing at load
|
|
315
|
+
time.
|
|
316
|
+
"""
|
|
317
|
+
errors: list[str] = []
|
|
318
|
+
|
|
319
|
+
# Per-entry checks, plus collect normalized values for cross-checks.
|
|
320
|
+
# ``""`` placeholders preserve list-position indexing for blanks and
|
|
321
|
+
# entries that already failed a per-entry check.
|
|
322
|
+
file_norms: list[str] = []
|
|
323
|
+
folder_norms: list[str] = []
|
|
324
|
+
|
|
325
|
+
for i, raw in enumerate(memory.files, 1):
|
|
326
|
+
p = raw.strip()
|
|
327
|
+
if not p:
|
|
328
|
+
file_norms.append("")
|
|
329
|
+
continue
|
|
330
|
+
expanded = os.path.expanduser(p)
|
|
331
|
+
if not expanded.lower().endswith(_ALLOWED_SUFFIXES):
|
|
332
|
+
errors.append(f"Memory File {i}: '{p}' must end with .md or .txt.")
|
|
333
|
+
# Still include in cross-checks — a wrong-extension file can
|
|
334
|
+
# still duplicate or sit under a folder, and reporting both
|
|
335
|
+
# surfaces in one save round-trip is friendlier.
|
|
336
|
+
file_norms.append(_normalize_for_compare(expanded, workspace_cwd=workspace_cwd))
|
|
337
|
+
|
|
338
|
+
for i, raw in enumerate(memory.folders, 1):
|
|
339
|
+
p = raw.strip()
|
|
340
|
+
if not p:
|
|
341
|
+
folder_norms.append("")
|
|
342
|
+
continue
|
|
343
|
+
expanded = os.path.expanduser(p)
|
|
344
|
+
norm = _normalize_for_compare(expanded, workspace_cwd=workspace_cwd)
|
|
345
|
+
# Check the raw form too: a bare drive ("C:") is drive-relative
|
|
346
|
+
# (``ntpath.isabs`` is false), so it rebases onto ``workspace_cwd``
|
|
347
|
+
# before the root check would ever see "c:".
|
|
348
|
+
if _is_root_norm(norm) or _is_root_norm(_normalize_for_compare(expanded)):
|
|
349
|
+
errors.append(
|
|
350
|
+
f"Memory Folder {i}: '{p}' is a filesystem root; walking it would scan the "
|
|
351
|
+
f"entire volume. Configure a specific folder instead."
|
|
352
|
+
)
|
|
353
|
+
# Excluded from duplicate cross-checks — the entry is already
|
|
354
|
+
# rejected outright.
|
|
355
|
+
folder_norms.append("")
|
|
356
|
+
continue
|
|
357
|
+
folder_norms.append(norm)
|
|
358
|
+
|
|
359
|
+
# Duplicate detection — flag the *later* occurrence so the first one
|
|
360
|
+
# acts as the canonical entry.
|
|
361
|
+
_flag_duplicates(errors, memory.files, file_norms, "File")
|
|
362
|
+
_flag_duplicates(errors, memory.folders, folder_norms, "Folder")
|
|
363
|
+
|
|
364
|
+
return errors
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def _normalize_for_compare(p: str, *, workspace_cwd: str | None = None) -> str:
|
|
368
|
+
"""Normalize a path for case-insensitive comparison.
|
|
369
|
+
|
|
370
|
+
Uses :mod:`posixpath` so the result always uses forward slashes,
|
|
371
|
+
making comparisons stable across YAML authored on Windows vs POSIX.
|
|
372
|
+
Collapses ``./``, redundant separators, and trailing slashes; then
|
|
373
|
+
lower-cases so ``AGENTS.md`` and ``agents.md`` compare equal.
|
|
374
|
+
|
|
375
|
+
Drive-absolute Windows paths normalize their tail against the drive
|
|
376
|
+
anchor, mirroring Windows semantics where ".." clamps at the drive
|
|
377
|
+
root: "C:\\.." → "c:". Fed whole to :func:`posixpath.normpath`,
|
|
378
|
+
"c:/.." would instead treat "c:" as an ordinary consumable component
|
|
379
|
+
and reduce to "." — hiding a drive root from the validator's root
|
|
380
|
+
check. UNC paths likewise anchor at host+share (mirroring
|
|
381
|
+
:func:`ntpath.splitdrive`): "\\\\server\\share\\..\\docs" →
|
|
382
|
+
"//server/share/docs", where posixpath alone would let ".." consume
|
|
383
|
+
the share and produce "//server/docs" — misread as a share root.
|
|
384
|
+
Redirector-prefixed UNC forms preserve their leading ";" resolver
|
|
385
|
+
specifiers but locate host+share after those directives.
|
|
386
|
+
"""
|
|
387
|
+
cleaned = p.strip()
|
|
388
|
+
if workspace_cwd and not _is_absolute_path(cleaned):
|
|
389
|
+
cleaned = str(Path(workspace_cwd) / cleaned)
|
|
390
|
+
cleaned = cleaned.replace("\\", "/")
|
|
391
|
+
if len(cleaned) >= 3 and cleaned[0].isalpha() and cleaned[1:3] == ":/":
|
|
392
|
+
# Pin the tail to exactly one leading slash first: POSIX gives
|
|
393
|
+
# "//" special root status and preserves it ("//docs", "//.." →
|
|
394
|
+
# "//"), but after a drive anchor extra separators are noise on
|
|
395
|
+
# Windows — without the pin, "C://docs" would not normalize
|
|
396
|
+
# together with "C:/docs", so separator variants dodge duplicate
|
|
397
|
+
# detection ("C://.."-style roots would still be caught, but only
|
|
398
|
+
# by the trailing-slash catch-all, as "c://" instead of the
|
|
399
|
+
# canonical bare-drive "c:").
|
|
400
|
+
tail = posixpath.normpath("/" + cleaned[2:].lstrip("/"))
|
|
401
|
+
return (cleaned[:2] + ("" if tail == "/" else tail)).lower()
|
|
402
|
+
if cleaned.startswith("//"):
|
|
403
|
+
parts = cleaned[2:].split("/")
|
|
404
|
+
anchor_parts = _strip_redirector_specifiers(parts)
|
|
405
|
+
specifier_count = len(parts) - len(anchor_parts)
|
|
406
|
+
if (
|
|
407
|
+
len(anchor_parts) >= 2
|
|
408
|
+
and anchor_parts[0] not in ("", "?", ".", "..")
|
|
409
|
+
and anchor_parts[1] not in ("", ".", "..")
|
|
410
|
+
):
|
|
411
|
+
# UNC paths anchor at host+share (ntpath.splitdrive's drive),
|
|
412
|
+
# so ".." clamps at the share instead of consuming it. The
|
|
413
|
+
# leading ";" components in redirector forms are resolver
|
|
414
|
+
# directives, not host/share; retain them in the spelling but
|
|
415
|
+
# locate the anchor after all of them.
|
|
416
|
+
# Extended/device forms ("//?/", "//./") keep the whole-path
|
|
417
|
+
# treatment (Windows disables normalization after "\\?\"
|
|
418
|
+
# anyway), as do paths with an empty or dot host/share slot —
|
|
419
|
+
# there is no meaningful anchor to preserve.
|
|
420
|
+
anchor = "//" + "/".join([*parts[:specifier_count], *anchor_parts[:2]])
|
|
421
|
+
rest = "/".join(anchor_parts[2:])
|
|
422
|
+
tail = posixpath.normpath("/" + rest.lstrip("/"))
|
|
423
|
+
return (anchor + ("" if tail == "/" else tail)).lower()
|
|
424
|
+
return posixpath.normpath(cleaned).lower()
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def _is_bare_drive_norm(norm: str) -> bool:
|
|
428
|
+
"""Whether a normalized path is a bare Windows drive ("C:\\" → "c:")."""
|
|
429
|
+
return len(norm) == 2 and norm[1] == ":" and norm[0].isalpha()
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def _strip_redirector_specifiers(segments: list[str]) -> list[str]:
|
|
433
|
+
"""Drop leading ";"-prefixed redirector/session specifiers.
|
|
434
|
+
|
|
435
|
+
Windows accepts both the fused form (";CSC;LanmanRedirector") and a
|
|
436
|
+
chain of *separate* ";" components (";LanmanRedirector/;a" — the
|
|
437
|
+
CVE-2019-1220 syntax "\\\\;LanmanRedirector\\;xxxx\\server\\share").
|
|
438
|
+
Every leading component starting with ";" is a resolver directive,
|
|
439
|
+
not share content, so all of them must be consumed before the
|
|
440
|
+
server/share depth rule is applied — stripping only the first would
|
|
441
|
+
let a chained share root masquerade as a "subfolder". A genuine
|
|
442
|
+
directory named ";x" can only appear *after* the server component,
|
|
443
|
+
which never starts with ";", so real children are never stripped.
|
|
444
|
+
"""
|
|
445
|
+
index = 0
|
|
446
|
+
while index < len(segments) and segments[index].startswith(";"):
|
|
447
|
+
index += 1
|
|
448
|
+
return segments[index:]
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def _device_namespace_root(body: str) -> bool | None:
|
|
452
|
+
"""Classify a Windows device-namespace path body as root / not-root.
|
|
453
|
+
|
|
454
|
+
*body* is the normalized remainder after the leading slashes and any
|
|
455
|
+
"?/" / "./" prefix: "c:/docs", "globalroot/device/harddiskvolume1",
|
|
456
|
+
"volume{guid}/docs". Returns None when the body is not a recognized
|
|
457
|
+
device form (a real UNC hostname cannot contain ":", "{", or a
|
|
458
|
+
leading ";", and "globalroot" is a reserved object-manager name, so
|
|
459
|
+
misclassifying a genuine server by these heads is not a practical
|
|
460
|
+
concern).
|
|
461
|
+
"""
|
|
462
|
+
head, _, rest = body.partition("/")
|
|
463
|
+
if head.startswith(";"):
|
|
464
|
+
# "\\\\;LanmanRedirector\\server\\share" (optionally chained, either
|
|
465
|
+
# fused "\\\\;CSC;LanmanRedirector\\..." or as separate components
|
|
466
|
+
# "\\\\;LanmanRedirector\\;a\\...") explicitly routes UNC resolution
|
|
467
|
+
# through a named redirector; the remainder is ordinary
|
|
468
|
+
# server/share space, so the share-root rule applies to it —
|
|
469
|
+
# without this, the component-count fallback would count the
|
|
470
|
+
# redirector specifiers as path segments and wave a share root
|
|
471
|
+
# through as a "subfolder".
|
|
472
|
+
return len(_strip_redirector_specifiers(body.split("/"))) <= 2
|
|
473
|
+
if _is_bare_drive_norm(head):
|
|
474
|
+
# "c:" addresses the whole drive; "c:/docs" is an ordinary folder.
|
|
475
|
+
return not rest
|
|
476
|
+
if head == "unc":
|
|
477
|
+
# "unc/server" or "unc/server/share" is a host/share root — both the
|
|
478
|
+
# "\\\\?\\UNC\\..." extended and "\\\\.\\UNC\\..." device spellings land
|
|
479
|
+
# here (the latter with its "./" collapsed by normalization, so a
|
|
480
|
+
# genuine server literally named "unc" is conservatively treated as
|
|
481
|
+
# this form too). "\\\\?\\UNC\\;LanmanRedirector\\..." routes through a
|
|
482
|
+
# redirector just like the "\\\\;..." spelling, so specifiers are
|
|
483
|
+
# stripped before counting.
|
|
484
|
+
return len(_strip_redirector_specifiers(body.split("/")[1:])) <= 2
|
|
485
|
+
if head == "globalroot":
|
|
486
|
+
# GLOBALROOT exposes the NT object namespace. "??"-style arcs
|
|
487
|
+
# alias the regular Win32 namespace, so they recurse. Device paths
|
|
488
|
+
# split two ways: volume devices address a whole volume themselves
|
|
489
|
+
# ("globalroot/device/harddiskvolumeshadowcopy1" — the one form not
|
|
490
|
+
# reachable by drive letter, so its subfolders stay valid), while
|
|
491
|
+
# network-provider devices (mup, lanmanredirector, p9rdr, webdav…)
|
|
492
|
+
# re-enter share space two levels deeper, optionally behind the
|
|
493
|
+
# same ";" specifier chain ("device/mup/;lanmanredirector/…").
|
|
494
|
+
# Providers are an open set, so every device that is not a
|
|
495
|
+
# recognized volume gets the conservative share-depth rule.
|
|
496
|
+
segments = body.split("/")
|
|
497
|
+
if len(segments) >= 2 and segments[1] in ("??", "global??"):
|
|
498
|
+
return _is_root_norm("//?/" + "/".join(segments[2:]))
|
|
499
|
+
if len(segments) >= 3 and segments[1] == "device":
|
|
500
|
+
if segments[2].startswith(("harddiskvolume", "cdrom")):
|
|
501
|
+
return len(segments) <= 3
|
|
502
|
+
return len(_strip_redirector_specifiers(segments[3:])) <= 2
|
|
503
|
+
return body.count("/") <= 2
|
|
504
|
+
if head.startswith("volume{"):
|
|
505
|
+
# "volume{guid}" addresses a whole volume by GUID.
|
|
506
|
+
return not rest
|
|
507
|
+
return None
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
def _is_root_norm(norm: str) -> bool:
|
|
511
|
+
"""Whether a normalized path is a filesystem root.
|
|
512
|
+
|
|
513
|
+
Roots keep their trailing slash through :func:`_normalize_for_compare`
|
|
514
|
+
("/" and the POSIX-special "//"); Windows drive roots normalize to a
|
|
515
|
+
bare drive letter ("C:\\" → "c:"); UNC hosts and share roots normalize
|
|
516
|
+
to "//server" / "//server/share" — pathlib treats a share root as its
|
|
517
|
+
own parent, so the loader could never walk one anyway. Extended-length
|
|
518
|
+
forms alias the plain ones and are unwrapped first: "//?/c:" ≙ "c:",
|
|
519
|
+
"//?/unc/server/share" ≙ "//server/share"; device-namespace volume
|
|
520
|
+
addresses ("//?/globalroot/device/<volume>", "//?/volume{guid}") are
|
|
521
|
+
volume roots too. Redirector-prefixed UNC forms
|
|
522
|
+
("\\\\;LanmanRedirector\\server\\share", chained "\\\\;a;b\\...") strip
|
|
523
|
+
the ";" specifier before the share-root rule. Note :func:`_normalize_for_compare` collapses the
|
|
524
|
+
"\\\\.\\" device prefix outright ("\\\\.\\C:\\docs" → "//c:/docs"), so the
|
|
525
|
+
plain-"//" branch must also recognize device heads rather than treat
|
|
526
|
+
them as UNC hostnames. The final UNC test is deliberately
|
|
527
|
+
conservative: a POSIX "//tmp"-style doubled-slash path is also flagged
|
|
528
|
+
even though it aliases "/tmp" — configure it without the doubled slash
|
|
529
|
+
instead.
|
|
530
|
+
"""
|
|
531
|
+
if norm.endswith("/"):
|
|
532
|
+
return True
|
|
533
|
+
if _is_bare_drive_norm(norm):
|
|
534
|
+
return True
|
|
535
|
+
if not norm.startswith("//"):
|
|
536
|
+
return False
|
|
537
|
+
body = norm[2:]
|
|
538
|
+
if body.startswith(("?/", "./")):
|
|
539
|
+
# Extended-length ("\\\\?\\...") / device ("\\\\.\\...") prefix.
|
|
540
|
+
body = body[2:]
|
|
541
|
+
device = _device_namespace_root(body)
|
|
542
|
+
if device is not None:
|
|
543
|
+
return device
|
|
544
|
+
return _is_root_norm(body)
|
|
545
|
+
device = _device_namespace_root(body)
|
|
546
|
+
if device is not None:
|
|
547
|
+
return device
|
|
548
|
+
# "//server" or "//server/share" is a host/share root.
|
|
549
|
+
return norm.count("/") <= 3
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
def _flag_duplicates(
|
|
553
|
+
errors: list[str],
|
|
554
|
+
raw_values: list[str],
|
|
555
|
+
norms: list[str],
|
|
556
|
+
kind: str,
|
|
557
|
+
) -> None:
|
|
558
|
+
"""Append duplicate-entry errors to *errors* in 1-based list order."""
|
|
559
|
+
seen: dict[str, int] = {}
|
|
560
|
+
for i, norm in enumerate(norms, 1):
|
|
561
|
+
if not norm:
|
|
562
|
+
continue
|
|
563
|
+
if norm in seen:
|
|
564
|
+
first = seen[norm]
|
|
565
|
+
errors.append(
|
|
566
|
+
f"Memory {kind} {i}: '{raw_values[i - 1].strip()}' duplicates {kind} {first} "
|
|
567
|
+
f"(paths are matched case-insensitively after normalization)."
|
|
568
|
+
)
|
|
569
|
+
else:
|
|
570
|
+
seen[norm] = i
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
def _safe_resolve_file(raw: str, base: Path) -> Path | None:
|
|
574
|
+
"""Resolve a configured file path.
|
|
575
|
+
|
|
576
|
+
Relative file paths resolve under *base* and must stay inside it.
|
|
577
|
+
Native absolute file paths are allowed. Foreign absolute paths (for
|
|
578
|
+
example ``C:\\foo.md`` on POSIX) cannot be loaded on this host and return
|
|
579
|
+
``None`` so callers can report the configured path as missing.
|
|
580
|
+
"""
|
|
581
|
+
expanded = os.path.expanduser(raw.strip())
|
|
582
|
+
if _is_absolute_path(expanded):
|
|
583
|
+
path = Path(expanded)
|
|
584
|
+
if not path.is_absolute():
|
|
585
|
+
return None
|
|
586
|
+
try:
|
|
587
|
+
return path.resolve()
|
|
588
|
+
except OSError:
|
|
589
|
+
return path
|
|
590
|
+
try:
|
|
591
|
+
resolved = (base / expanded).resolve()
|
|
592
|
+
except OSError:
|
|
593
|
+
return None
|
|
594
|
+
if not resolved.is_relative_to(base):
|
|
595
|
+
return None
|
|
596
|
+
return resolved
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
def _safe_resolve_folder(raw: str, base: Path) -> Path | None:
|
|
600
|
+
"""Resolve a configured folder path.
|
|
601
|
+
|
|
602
|
+
Relative folder paths resolve under *base* and must stay inside it
|
|
603
|
+
(``..`` traversal or symlink targets escaping *base* return ``None``).
|
|
604
|
+
Native absolute folder paths are allowed, mirroring absolute files.
|
|
605
|
+
Foreign absolute paths (for example ``C:\\docs`` on POSIX) cannot be
|
|
606
|
+
loaded on this host and return ``None`` so callers can report the
|
|
607
|
+
configured path as missing. Filesystem roots return ``None`` — even
|
|
608
|
+
the bounded scan has no business starting at one. *base* is expected
|
|
609
|
+
to be already resolved.
|
|
610
|
+
"""
|
|
611
|
+
expanded = os.path.expanduser(raw.strip())
|
|
612
|
+
if _is_root_norm(_normalize_for_compare(expanded)):
|
|
613
|
+
# Bare drives ("C:") rebase onto *base* and UNC share roots resolve
|
|
614
|
+
# to their own parent — neither would reach the parent==self check
|
|
615
|
+
# below in its configured form, so reject that form directly.
|
|
616
|
+
return None
|
|
617
|
+
if _is_absolute_path(expanded):
|
|
618
|
+
path = Path(expanded)
|
|
619
|
+
if not path.is_absolute():
|
|
620
|
+
return None
|
|
621
|
+
try:
|
|
622
|
+
resolved = path.resolve()
|
|
623
|
+
except OSError:
|
|
624
|
+
resolved = path
|
|
625
|
+
else:
|
|
626
|
+
try:
|
|
627
|
+
resolved = (base / expanded).resolve()
|
|
628
|
+
except OSError:
|
|
629
|
+
return None
|
|
630
|
+
if not resolved.is_relative_to(base):
|
|
631
|
+
return None
|
|
632
|
+
if resolved.parent == resolved:
|
|
633
|
+
# A root is never a curated memory folder.
|
|
634
|
+
return None
|
|
635
|
+
return resolved
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
def _file_identity(path: Path) -> object:
|
|
639
|
+
"""Filesystem-aware identity key: same physical file/dir → same key.
|
|
640
|
+
|
|
641
|
+
``(st_dev, st_ino)`` collapses case aliases on case-insensitive
|
|
642
|
+
filesystems ("DOCS/SPEC.MD" vs "docs/spec.md"), symlinked spellings,
|
|
643
|
+
and hardlinks — a raw ``realpath`` string preserves the queried case
|
|
644
|
+
and misses the first class. Falls back to the case-folded realpath
|
|
645
|
+
when ``stat`` fails or the filesystem reports no inode (st_ino == 0 on
|
|
646
|
+
some Windows network shares — a zero inode is NOT an identity; two
|
|
647
|
+
distinct files would collide on it).
|
|
648
|
+
"""
|
|
649
|
+
try:
|
|
650
|
+
st = os.stat(path)
|
|
651
|
+
except OSError:
|
|
652
|
+
return os.path.normcase(os.path.realpath(path))
|
|
653
|
+
if st.st_ino:
|
|
654
|
+
return (st.st_dev, st.st_ino)
|
|
655
|
+
return os.path.normcase(os.path.realpath(path))
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
def _rel(path: Path, base: Path) -> str:
|
|
659
|
+
"""Return *path* relative to *base* in POSIX form.
|
|
660
|
+
|
|
661
|
+
Always emits ``/`` separators so headers, ``loaded_files``, and
|
|
662
|
+
``skipped_files`` stay consistent across platforms.
|
|
663
|
+
"""
|
|
664
|
+
try:
|
|
665
|
+
return path.relative_to(base).as_posix()
|
|
666
|
+
except ValueError:
|
|
667
|
+
return path.as_posix()
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
FOLDER_MAX_FILES = 100
|
|
671
|
+
"""Per-folder cap on collected ``.md``/``.txt`` files."""
|
|
672
|
+
|
|
673
|
+
FOLDER_MAX_DEPTH = 2
|
|
674
|
+
"""How many subdirectory levels below a configured folder are scanned."""
|
|
675
|
+
|
|
676
|
+
FOLDER_SCAN_MAX_ENTRIES = 10_000
|
|
677
|
+
"""Work bound: total directory entries examined per configured folder.
|
|
678
|
+
|
|
679
|
+
The file/depth caps bound what is *collected*, not what is *scanned* — a
|
|
680
|
+
huge or file-less tree (thousands of subdirectories, a directory with a
|
|
681
|
+
million entries) would still stall agent construction without a bound on
|
|
682
|
+
the traversal itself.
|
|
683
|
+
"""
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
def _walk_folder(folder: Path, base: Path, seen_files: set[object]) -> list[Path]:
|
|
687
|
+
"""Collect ``.md``/``.txt`` files under *folder*, breadth-first, bounded.
|
|
688
|
+
|
|
689
|
+
The scan is deliberately bounded so a huge tree (or an accidentally
|
|
690
|
+
configured volume/share) cannot stall agent construction: the folder's
|
|
691
|
+
own files are collected first, then subdirectories up to
|
|
692
|
+
``FOLDER_MAX_DEPTH`` levels deep, stopping once ``FOLDER_MAX_FILES``
|
|
693
|
+
files are found — breadth-first, so shallow (typically curated) files
|
|
694
|
+
win over deep ones when the cap bites.
|
|
695
|
+
|
|
696
|
+
Only files whose physical identity (see ``_file_identity``) is not
|
|
697
|
+
already in *seen_files* are collected — and only those count toward
|
|
698
|
+
``FOLDER_MAX_FILES``, so aliases of an already-loaded file (explicit,
|
|
699
|
+
earlier-folder, or within this folder) cannot crowd unique files out
|
|
700
|
+
of the cap. Collected identities are registered in *seen_files* as
|
|
701
|
+
the scan goes.
|
|
702
|
+
|
|
703
|
+
The traversal itself is work-bounded: at most
|
|
704
|
+
``FOLDER_SCAN_MAX_ENTRIES`` directory entries are examined in total
|
|
705
|
+
(a directory wider than the remaining budget is truncated in
|
|
706
|
+
filesystem order), so the file/depth caps cannot be defeated by huge
|
|
707
|
+
or file-less trees. Since every queued subdirectory was itself an
|
|
708
|
+
entry of its parent, the budget also bounds the number of ``scandir``
|
|
709
|
+
calls.
|
|
710
|
+
|
|
711
|
+
Follows symlinks but tracks visited directory ``realpath``s to break
|
|
712
|
+
cycles **and** prunes any directory or single-file symlink whose
|
|
713
|
+
realpath escapes *base* (the workspace for workspace-contained folders,
|
|
714
|
+
the folder itself for absolute folders outside it), so a configured
|
|
715
|
+
folder cannot be tricked into surfacing files from elsewhere. Returned
|
|
716
|
+
paths keep the scan order — the folder's own files first, then
|
|
717
|
+
subdirectory files level by level, name-sorted within each directory.
|
|
718
|
+
Order is load-bearing: the token cap downstream consumes candidates in
|
|
719
|
+
list order, so shallow files must come first to win when the budget
|
|
720
|
+
bites.
|
|
721
|
+
"""
|
|
722
|
+
results: list[Path] = []
|
|
723
|
+
visited: set[str] = set()
|
|
724
|
+
queue: deque[tuple[Path, int]] = deque([(folder, 0)])
|
|
725
|
+
entry_budget = FOLDER_SCAN_MAX_ENTRIES
|
|
726
|
+
|
|
727
|
+
while queue and len(results) < FOLDER_MAX_FILES and entry_budget > 0:
|
|
728
|
+
dirpath, depth = queue.popleft()
|
|
729
|
+
try:
|
|
730
|
+
real = Path(os.path.realpath(dirpath))
|
|
731
|
+
except OSError:
|
|
732
|
+
continue
|
|
733
|
+
if not real.is_relative_to(base):
|
|
734
|
+
# Symlinked into a non-workspace location — prune entirely.
|
|
735
|
+
continue
|
|
736
|
+
if str(real) in visited:
|
|
737
|
+
# Cycle node already scanned via another route.
|
|
738
|
+
continue
|
|
739
|
+
visited.add(str(real))
|
|
740
|
+
try:
|
|
741
|
+
with os.scandir(dirpath) as scan:
|
|
742
|
+
entries = list(islice(scan, entry_budget))
|
|
743
|
+
except OSError:
|
|
744
|
+
continue
|
|
745
|
+
entry_budget -= len(entries)
|
|
746
|
+
entries.sort(key=lambda e: e.name)
|
|
747
|
+
subdirs: list[Path] = []
|
|
748
|
+
for entry in entries:
|
|
749
|
+
try:
|
|
750
|
+
is_dir = entry.is_dir(follow_symlinks=True)
|
|
751
|
+
except OSError:
|
|
752
|
+
continue
|
|
753
|
+
if is_dir:
|
|
754
|
+
subdirs.append(Path(entry.path))
|
|
755
|
+
continue
|
|
756
|
+
if not entry.name.lower().endswith(_ALLOWED_SUFFIXES):
|
|
757
|
+
continue
|
|
758
|
+
file_path = Path(entry.path)
|
|
759
|
+
try:
|
|
760
|
+
file_real = Path(os.path.realpath(file_path))
|
|
761
|
+
except OSError:
|
|
762
|
+
continue
|
|
763
|
+
if not file_real.is_relative_to(base):
|
|
764
|
+
# Single-file symlink pointing outside the workspace.
|
|
765
|
+
continue
|
|
766
|
+
identity = _file_identity(file_path)
|
|
767
|
+
if identity in seen_files:
|
|
768
|
+
continue
|
|
769
|
+
seen_files.add(identity)
|
|
770
|
+
results.append(file_path)
|
|
771
|
+
if len(results) >= FOLDER_MAX_FILES:
|
|
772
|
+
break
|
|
773
|
+
if depth < FOLDER_MAX_DEPTH:
|
|
774
|
+
queue.extend((subdir, depth + 1) for subdir in subdirs)
|
|
775
|
+
|
|
776
|
+
return results
|
|
777
|
+
|
|
778
|
+
|
|
779
|
+
def _render_file_section(cand: _Candidate, *, level: int) -> str | None:
|
|
780
|
+
"""Render a single file as ``## File: <path>\\n\\n<content>\\n``.
|
|
781
|
+
|
|
782
|
+
Returns ``None`` if the file became unreadable between discovery and
|
|
783
|
+
rendering (e.g. deleted mid-build) so the caller can record it as
|
|
784
|
+
missing rather than emitting an empty section.
|
|
785
|
+
"""
|
|
786
|
+
try:
|
|
787
|
+
content = decode_bytes(cand.abs_path.read_bytes())
|
|
788
|
+
except OSError as e:
|
|
789
|
+
logger.warning("Memory: failed to read %s: %s", cand.abs_path, e)
|
|
790
|
+
return None
|
|
791
|
+
prefix = "#" * level
|
|
792
|
+
return f"\n{prefix} File: {cand.rel_path}\n\n{content}\n"
|