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,154 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""Session lock helpers for cross-process active-session ownership."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import contextlib
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
from datetime import UTC, datetime
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import TYPE_CHECKING
|
|
13
|
+
|
|
14
|
+
from chrys.foundation.branding import APP_DISPLAY_NAME
|
|
15
|
+
from chrys.foundation.platform.files import atomic_write_text
|
|
16
|
+
from chrys.foundation.util.lock import FileLock
|
|
17
|
+
from chrys.foundation.util.session_ids import session_short_id
|
|
18
|
+
from chrys.service.state.store import (
|
|
19
|
+
SESSION_ACTIVE_LOCK_TIMEOUT_SECONDS,
|
|
20
|
+
session_active_lock_path,
|
|
21
|
+
session_active_owner_path,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from chrys.service.state.store import StateStore
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
SESSION_RESTORE_ACTIVE_LOCK_TIMEOUT_SECONDS = 0.25
|
|
29
|
+
"""Fast contention check for interactive session restore."""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ActiveSessionGuard:
|
|
33
|
+
"""Owns the long-lived lock that marks a Chrys session active."""
|
|
34
|
+
|
|
35
|
+
def __init__(self, state_store: StateStore | None) -> None:
|
|
36
|
+
self._state_store = state_store
|
|
37
|
+
self._lock: FileLock | None = None
|
|
38
|
+
self._session_id: str | None = None
|
|
39
|
+
|
|
40
|
+
def owns(self, session_id: str) -> bool:
|
|
41
|
+
"""Return whether this guard currently owns *session_id*."""
|
|
42
|
+
if self._lock is None or self._session_id is None:
|
|
43
|
+
return False
|
|
44
|
+
if self._session_id == session_id:
|
|
45
|
+
return True
|
|
46
|
+
owned_path = self._lock_path(self._session_id)
|
|
47
|
+
requested_path = self._lock_path(session_id)
|
|
48
|
+
return owned_path is not None and owned_path == requested_path
|
|
49
|
+
|
|
50
|
+
def ensure(self, session_id: str) -> bool:
|
|
51
|
+
"""Ensure this process owns *session_id* or report lock contention."""
|
|
52
|
+
if self.owns(session_id):
|
|
53
|
+
return True
|
|
54
|
+
try:
|
|
55
|
+
lock = self.acquire_for_restore(session_id)
|
|
56
|
+
except TimeoutError:
|
|
57
|
+
return False
|
|
58
|
+
self.install(session_id, lock)
|
|
59
|
+
return True
|
|
60
|
+
|
|
61
|
+
def acquire_for_restore(
|
|
62
|
+
self,
|
|
63
|
+
session_id: str,
|
|
64
|
+
*,
|
|
65
|
+
timeout: float | None = None,
|
|
66
|
+
) -> FileLock | None:
|
|
67
|
+
"""Acquire a lock handle for a session that is about to be restored."""
|
|
68
|
+
if timeout is None:
|
|
69
|
+
timeout = SESSION_ACTIVE_LOCK_TIMEOUT_SECONDS
|
|
70
|
+
path = self._lock_path(session_id)
|
|
71
|
+
if path is None:
|
|
72
|
+
return None
|
|
73
|
+
lock = FileLock(path, timeout=timeout)
|
|
74
|
+
lock.acquire()
|
|
75
|
+
return lock
|
|
76
|
+
|
|
77
|
+
def install(self, session_id: str, lock: FileLock | None) -> None:
|
|
78
|
+
"""Make *lock* the active lock held by this guard."""
|
|
79
|
+
if lock is None:
|
|
80
|
+
return
|
|
81
|
+
self.release()
|
|
82
|
+
self._lock = lock
|
|
83
|
+
self._session_id = session_id
|
|
84
|
+
self._write_owner(session_id)
|
|
85
|
+
|
|
86
|
+
def release(self) -> None:
|
|
87
|
+
"""Release the active-session lock held by this guard."""
|
|
88
|
+
lock = self._lock
|
|
89
|
+
session_id = self._session_id
|
|
90
|
+
if lock is None:
|
|
91
|
+
return
|
|
92
|
+
if session_id is not None:
|
|
93
|
+
owner_path = self._owner_path(session_id)
|
|
94
|
+
if owner_path is not None:
|
|
95
|
+
with contextlib.suppress(OSError):
|
|
96
|
+
owner_path.unlink()
|
|
97
|
+
self._lock = None
|
|
98
|
+
self._session_id = None
|
|
99
|
+
lock.release()
|
|
100
|
+
|
|
101
|
+
def conflict_message(self, session_id: str) -> str:
|
|
102
|
+
"""Return a user-facing message for an active-session conflict."""
|
|
103
|
+
detail = self._format_owner(self._read_owner(session_id))
|
|
104
|
+
return (
|
|
105
|
+
f"Session '{session_short_id(session_id)}' is already open in another {APP_DISPLAY_NAME} instance{detail}."
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
def _sessions_root_dir(self, session_id: str) -> Path | None:
|
|
109
|
+
if self._state_store is None:
|
|
110
|
+
return None
|
|
111
|
+
return self._state_store.session_dir(session_id).parent
|
|
112
|
+
|
|
113
|
+
def _lock_path(self, session_id: str) -> Path | None:
|
|
114
|
+
sessions_dir = self._sessions_root_dir(session_id)
|
|
115
|
+
if sessions_dir is None:
|
|
116
|
+
return None
|
|
117
|
+
path = session_active_lock_path(sessions_dir, session_id)
|
|
118
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
119
|
+
return path
|
|
120
|
+
|
|
121
|
+
def _owner_path(self, session_id: str) -> Path | None:
|
|
122
|
+
sessions_dir = self._sessions_root_dir(session_id)
|
|
123
|
+
if sessions_dir is None:
|
|
124
|
+
return None
|
|
125
|
+
path = session_active_owner_path(sessions_dir, session_id)
|
|
126
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
127
|
+
return path
|
|
128
|
+
|
|
129
|
+
def _write_owner(self, session_id: str) -> None:
|
|
130
|
+
path = self._owner_path(session_id)
|
|
131
|
+
if path is None:
|
|
132
|
+
return
|
|
133
|
+
owner = {
|
|
134
|
+
"session_id": session_id,
|
|
135
|
+
"pid": os.getpid(),
|
|
136
|
+
"started_at": datetime.now(UTC).isoformat(),
|
|
137
|
+
}
|
|
138
|
+
with contextlib.suppress(OSError):
|
|
139
|
+
atomic_write_text(path, json.dumps(owner, indent=2, ensure_ascii=False))
|
|
140
|
+
|
|
141
|
+
def _read_owner(self, session_id: str) -> dict[str, object]:
|
|
142
|
+
path = self._owner_path(session_id)
|
|
143
|
+
if path is None or not path.exists():
|
|
144
|
+
return {}
|
|
145
|
+
try:
|
|
146
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
147
|
+
except json.JSONDecodeError, OSError:
|
|
148
|
+
return {}
|
|
149
|
+
return data if isinstance(data, dict) else {}
|
|
150
|
+
|
|
151
|
+
@staticmethod
|
|
152
|
+
def _format_owner(owner: dict[str, object]) -> str:
|
|
153
|
+
pid = owner.get("pid")
|
|
154
|
+
return f" (pid={pid})" if pid else ""
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""Serializers for session state objects (CompressedBlock, Message, etc.).
|
|
4
|
+
|
|
5
|
+
Provides JSON-compatible serialization/deserialization for persisting
|
|
6
|
+
session state to disk via JsonFileStateStore.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
from collections.abc import Mapping
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from chrys.kernel import Message
|
|
16
|
+
from chrys.service.agent_middleware.reminders.archive_pointer import CATALOG_POINTER_RECORD_COUNT_STATE_KEY
|
|
17
|
+
from chrys.service.context.providers.history import CompressedBlock
|
|
18
|
+
from chrys.service.session.runtime_metadata import (
|
|
19
|
+
CONTEXT_CALIBRATION_KEY,
|
|
20
|
+
LAST_USAGE_KEY,
|
|
21
|
+
TOTAL_SESSION_CACHE_HIT_TOKENS_KEY,
|
|
22
|
+
TOTAL_SESSION_INPUT_TOKENS_KEY,
|
|
23
|
+
TOTAL_SESSION_OUTPUT_TOKENS_KEY,
|
|
24
|
+
TOTAL_SESSION_TOKENS_KEY,
|
|
25
|
+
)
|
|
26
|
+
from chrys.service.trajectory.state import TRAJECTORY_STATE_KEY
|
|
27
|
+
|
|
28
|
+
# ``context_calibration`` must survive the round trip: restore-time strategy
|
|
29
|
+
# rehydration reads it from the persisted state (an allowlist miss here would
|
|
30
|
+
# silently disable rehydration while in-memory round-trip tests stay green).
|
|
31
|
+
_COPY_AS_DICT_KEYS = (
|
|
32
|
+
LAST_USAGE_KEY,
|
|
33
|
+
CONTEXT_CALIBRATION_KEY,
|
|
34
|
+
)
|
|
35
|
+
_COPY_AS_LIST_KEYS = ("agent_profile_switches",)
|
|
36
|
+
_PASS_THROUGH_OPTIONAL_KEYS = (
|
|
37
|
+
"chrys_mutations",
|
|
38
|
+
"chrys_workspace_baseline",
|
|
39
|
+
# Session todo list — truthy filter drops empty lists (empty ≡ absent).
|
|
40
|
+
"chrys_todos",
|
|
41
|
+
# Phase 4 LAST_WORDS note — replaces the compacted turn's dropped
|
|
42
|
+
# tool-call history, so a resume after restart can re-inject it.
|
|
43
|
+
"last_words",
|
|
44
|
+
# JSON-only siblings: code-generated dropped-call stubs and the
|
|
45
|
+
# attempt/spend breaker for the interrupted logical turn.
|
|
46
|
+
"last_words_manifest",
|
|
47
|
+
"last_words_breaker",
|
|
48
|
+
# Trajectory turn registry (turn_number -> turn_id / first sequence) —
|
|
49
|
+
# rollback needs it to name the superseded range, and it must travel with
|
|
50
|
+
# the messages it describes through save/restore/snapshot/fork.
|
|
51
|
+
TRAJECTORY_STATE_KEY,
|
|
52
|
+
TOTAL_SESSION_TOKENS_KEY,
|
|
53
|
+
TOTAL_SESSION_INPUT_TOKENS_KEY,
|
|
54
|
+
TOTAL_SESSION_OUTPUT_TOKENS_KEY,
|
|
55
|
+
)
|
|
56
|
+
# Keys where ``None`` and ``0`` are semantically distinct (e.g. provider
|
|
57
|
+
# didn't report vs. reported zero), so the truthy-only filter above would
|
|
58
|
+
# corrupt round-trips.
|
|
59
|
+
_PASS_THROUGH_NULLABLE_KEYS = (
|
|
60
|
+
CATALOG_POINTER_RECORD_COUNT_STATE_KEY,
|
|
61
|
+
TOTAL_SESSION_CACHE_HIT_TOKENS_KEY,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def serialize_message(msg: Message) -> dict[str, Any]:
|
|
66
|
+
"""Convert a Message to a JSON-serializable dict via framework serialization."""
|
|
67
|
+
return msg.to_dict()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def deserialize_message(data: dict[str, Any]) -> Message:
|
|
71
|
+
"""Reconstruct a Message from a serialized dict via framework deserialization."""
|
|
72
|
+
return Message.from_dict(data)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def serialized_message_payload(msg: Message) -> str:
|
|
76
|
+
"""Canonical JSON payload string for a message.
|
|
77
|
+
|
|
78
|
+
Injected into ``chrys.kernel.LoopRecorder`` as the
|
|
79
|
+
``message_hasher`` (the kernel package cannot import chrys, so the
|
|
80
|
+
serializer is supplied from here) — checkpoint suppression keys on this
|
|
81
|
+
payload's hash staying stable across equal snapshots.
|
|
82
|
+
"""
|
|
83
|
+
return json.dumps(
|
|
84
|
+
serialize_message(msg),
|
|
85
|
+
sort_keys=True,
|
|
86
|
+
separators=(",", ":"),
|
|
87
|
+
ensure_ascii=False,
|
|
88
|
+
default=str,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def serialize_compressed_block(block: CompressedBlock) -> dict[str, Any]:
|
|
93
|
+
"""Convert a CompressedBlock to a JSON-serializable dict."""
|
|
94
|
+
return {
|
|
95
|
+
"compressed_context_id": block.compressed_context_id,
|
|
96
|
+
"messages": [serialize_message(m) for m in block.messages],
|
|
97
|
+
"summary_text": block.summary_text,
|
|
98
|
+
"marker_id": block.marker_id,
|
|
99
|
+
"turn_range": list(block.turn_range),
|
|
100
|
+
"created_at": block.created_at,
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def deserialize_compressed_block(data: dict[str, Any]) -> CompressedBlock:
|
|
105
|
+
"""Reconstruct a CompressedBlock from a serialized dict."""
|
|
106
|
+
return CompressedBlock(
|
|
107
|
+
compressed_context_id=data["compressed_context_id"],
|
|
108
|
+
messages=[deserialize_message(m) for m in data.get("messages", [])],
|
|
109
|
+
summary_text=data["summary_text"],
|
|
110
|
+
marker_id=data.get("marker_id", ""),
|
|
111
|
+
turn_range=tuple(data.get("turn_range", [0, 0])),
|
|
112
|
+
created_at=data.get("created_at", ""),
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def serialize_state(state: dict[str, Any]) -> dict[str, Any]:
|
|
117
|
+
"""Serialize the full provider state to JSON-safe dict."""
|
|
118
|
+
result: dict[str, Any] = {
|
|
119
|
+
"messages": [serialize_message(m) for m in state.get("messages", [])],
|
|
120
|
+
"compressed_msgs": [serialize_compressed_block(b) for b in state.get("compressed_msgs", [])],
|
|
121
|
+
"turn_counter": state.get("turn_counter", 0),
|
|
122
|
+
}
|
|
123
|
+
_copy_optional_state_keys(state, result)
|
|
124
|
+
return result
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def deserialize_state(data: dict[str, Any]) -> dict[str, Any]:
|
|
128
|
+
"""Deserialize provider state from a JSON dict."""
|
|
129
|
+
result: dict[str, Any] = {
|
|
130
|
+
"messages": [deserialize_message(m) for m in data.get("messages", [])],
|
|
131
|
+
"compressed_msgs": [deserialize_compressed_block(b) for b in data.get("compressed_msgs", [])],
|
|
132
|
+
"turn_counter": data.get("turn_counter", 0),
|
|
133
|
+
}
|
|
134
|
+
_copy_optional_state_keys(data, result)
|
|
135
|
+
return result
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _copy_optional_state_keys(source: dict[str, Any], target: dict[str, Any]) -> None:
|
|
139
|
+
"""Copy optional provider-state keys, preserving existing truthy-only behavior."""
|
|
140
|
+
for key in _COPY_AS_DICT_KEYS:
|
|
141
|
+
value = source.get(key)
|
|
142
|
+
# Mapping-gated: a corrupted state file must degrade to "record
|
|
143
|
+
# absent" (downstream restore validates leniently), not crash the
|
|
144
|
+
# whole session load inside ``dict()``. Truthiness keeps the
|
|
145
|
+
# table-wide empty ≡ absent filter.
|
|
146
|
+
if value and isinstance(value, Mapping):
|
|
147
|
+
target[key] = dict(value)
|
|
148
|
+
for key in _COPY_AS_LIST_KEYS:
|
|
149
|
+
if source.get(key):
|
|
150
|
+
target[key] = list(source[key])
|
|
151
|
+
for key in _PASS_THROUGH_OPTIONAL_KEYS:
|
|
152
|
+
if source.get(key):
|
|
153
|
+
target[key] = source[key]
|
|
154
|
+
for key in _PASS_THROUGH_NULLABLE_KEYS:
|
|
155
|
+
if source.get(key) is not None:
|
|
156
|
+
target[key] = source[key]
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
# Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""Persisted listing metadata for the session browser.
|
|
4
|
+
|
|
5
|
+
``<sessions_root>/.cache/session_catalog.json`` keeps, per session folder, the
|
|
6
|
+
listing metadata derived from that session's ``session.json`` together with
|
|
7
|
+
the file's ``(inode, mtime_ns, size)`` signature, so listing sessions costs a
|
|
8
|
+
``stat`` per folder instead of a parse per envelope. Session files stay the
|
|
9
|
+
only source of truth: an entry is used only while its signature still matches
|
|
10
|
+
the file on disk, and a missing, corrupt or differently versioned catalog is
|
|
11
|
+
rebuilt from the envelopes.
|
|
12
|
+
|
|
13
|
+
Only metadata read from a valid primary file with no recovery sidecar, whose
|
|
14
|
+
signature was identical before and after the read, is recorded; a session
|
|
15
|
+
resolved from a sidecar, backup or rollback snapshot is parsed live every
|
|
16
|
+
time. Directory sizes and workflow run status are never recorded (both
|
|
17
|
+
change without touching ``session.json``); a workflow session's listing time
|
|
18
|
+
is, keyed on its latest run's header and log signatures.
|
|
19
|
+
|
|
20
|
+
The file holds prompt excerpts, so it is owner-only and a deleted session's
|
|
21
|
+
entry is removed with it. Writers re-read and merge under a leaf
|
|
22
|
+
:class:`FileLock` (nothing else is acquired while it is held), keep only
|
|
23
|
+
entries whose session file still exists, and give up after
|
|
24
|
+
:data:`SESSION_CATALOG_LOCK_TIMEOUT_SECONDS` — a stuck peer costs parses, never
|
|
25
|
+
a failed session operation. A catalog that exists but cannot be read right now
|
|
26
|
+
is never overwritten, and one written by a newer derivation is not downgraded
|
|
27
|
+
(deleting a session drops such a file whole rather than leave its excerpts).
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import dataclasses
|
|
33
|
+
import errno
|
|
34
|
+
import functools
|
|
35
|
+
import json
|
|
36
|
+
import logging
|
|
37
|
+
import os
|
|
38
|
+
import typing
|
|
39
|
+
from collections.abc import Callable, Collection, Mapping
|
|
40
|
+
from dataclasses import dataclass
|
|
41
|
+
from datetime import datetime
|
|
42
|
+
from pathlib import Path
|
|
43
|
+
from typing import Any
|
|
44
|
+
|
|
45
|
+
from chrys.foundation.models.session_surface import SessionSurface, parse_session_surface
|
|
46
|
+
from chrys.foundation.platform.files import atomic_write_owner_only_bytes, read_owner_verified_bounded
|
|
47
|
+
from chrys.foundation.util.lock import FileLock
|
|
48
|
+
from chrys.service.state._session_meta import ChatSessionMeta, SessionMeta, WorkflowSessionMeta
|
|
49
|
+
|
|
50
|
+
logger = logging.getLogger(__name__)
|
|
51
|
+
|
|
52
|
+
CATALOG_DERIVATION_VERSION = 1
|
|
53
|
+
"""Bump whenever the recorded metadata changes shape, meaning or derivation (turn counts, prompt excerpts, …),
|
|
54
|
+
so older catalogs rebuild; ``tests/service/state/test_session_catalog.py`` pins all three."""
|
|
55
|
+
|
|
56
|
+
SESSION_CATALOG_DIR_NAME = ".cache"
|
|
57
|
+
SESSION_CATALOG_FILE_NAME = "session_catalog.json"
|
|
58
|
+
SESSION_CATALOG_LOCK_FILE_NAME = "session_catalog.lock"
|
|
59
|
+
SESSION_CATALOG_LOCK_TIMEOUT_SECONDS = 2.0
|
|
60
|
+
MAX_SESSION_CATALOG_BYTES = 256 * 1024 * 1024
|
|
61
|
+
|
|
62
|
+
type FileSignature = tuple[int, int, int]
|
|
63
|
+
"""``(st_ino, st_mtime_ns, st_size)``: an atomic replace changes the inode even when size and mtime match."""
|
|
64
|
+
|
|
65
|
+
# Never recorded: the folder size and the run summary change without touching ``session.json``.
|
|
66
|
+
_UNRECORDED_FIELDS = frozenset({"kind", "size_bytes", "latest_run"})
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def file_signature(path: Path) -> FileSignature | None:
|
|
70
|
+
"""The change signature of *path*, or ``None`` when it cannot be stat'ed."""
|
|
71
|
+
try:
|
|
72
|
+
stat = os.stat(path)
|
|
73
|
+
except OSError:
|
|
74
|
+
return None
|
|
75
|
+
return (stat.st_ino, stat.st_mtime_ns, stat.st_size)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass(frozen=True, slots=True)
|
|
79
|
+
class RunListingKey:
|
|
80
|
+
"""The files a workflow session's listing time is read from."""
|
|
81
|
+
|
|
82
|
+
run_id: str
|
|
83
|
+
header: FileSignature | None
|
|
84
|
+
events: FileSignature | None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@dataclass(frozen=True, slots=True)
|
|
88
|
+
class RunListing:
|
|
89
|
+
"""A workflow session's listing time; ``None`` when its latest run has no readable summary."""
|
|
90
|
+
|
|
91
|
+
key: RunListingKey
|
|
92
|
+
listed_at: datetime | None
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@dataclass(frozen=True, slots=True)
|
|
96
|
+
class CatalogEntry:
|
|
97
|
+
"""Listing metadata (``size_bytes`` and ``latest_run`` unset) derived from one ``session.json``."""
|
|
98
|
+
|
|
99
|
+
signature: FileSignature
|
|
100
|
+
meta: SessionMeta
|
|
101
|
+
run: RunListing | None = None
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@dataclass(frozen=True, slots=True)
|
|
105
|
+
class CatalogCommit:
|
|
106
|
+
"""The outcome of one merge into the catalog file."""
|
|
107
|
+
|
|
108
|
+
settled: bool
|
|
109
|
+
"""No retry needed: the updates were written, or a newer derivation owns the file."""
|
|
110
|
+
signature: FileSignature | None = None
|
|
111
|
+
"""The file as this merge wrote it, read before the lock was released."""
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@functools.cache
|
|
115
|
+
def _recorded_fields(cls: type[SessionMeta]) -> dict[str, object]:
|
|
116
|
+
hints = typing.get_type_hints(cls)
|
|
117
|
+
return {
|
|
118
|
+
field.name: hints[field.name]
|
|
119
|
+
for field in dataclasses.fields(cls)
|
|
120
|
+
if field.init and field.name not in _UNRECORDED_FIELDS
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _meta_class(kind: object) -> type[SessionMeta]:
|
|
125
|
+
if kind == "chat":
|
|
126
|
+
return ChatSessionMeta
|
|
127
|
+
if kind == "workflow":
|
|
128
|
+
return WorkflowSessionMeta
|
|
129
|
+
raise ValueError(f"Unknown session kind in catalog: {kind!r}")
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _encode_value(value: object) -> object:
|
|
133
|
+
if isinstance(value, datetime):
|
|
134
|
+
return value.isoformat()
|
|
135
|
+
if isinstance(value, SessionSurface):
|
|
136
|
+
return value.value
|
|
137
|
+
if isinstance(value, list):
|
|
138
|
+
return list(value)
|
|
139
|
+
return value
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _decode_value(hint: object, value: object) -> object:
|
|
143
|
+
if hint is str and isinstance(value, str):
|
|
144
|
+
return value
|
|
145
|
+
if hint is int and type(value) is int:
|
|
146
|
+
return value
|
|
147
|
+
if hint is datetime and isinstance(value, str):
|
|
148
|
+
return datetime.fromisoformat(value)
|
|
149
|
+
if hint == list[str] and isinstance(value, list) and all(isinstance(item, str) for item in value):
|
|
150
|
+
return list(value)
|
|
151
|
+
if hint == SessionSurface | None:
|
|
152
|
+
return None if value is None else parse_session_surface(value)
|
|
153
|
+
raise TypeError(f"Catalog value {value!r} does not match {hint!r}")
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def encode_meta(meta: SessionMeta) -> dict[str, Any]:
|
|
157
|
+
"""The recorded fields of *meta* as JSON values."""
|
|
158
|
+
values = dataclasses.asdict(meta)
|
|
159
|
+
record: dict[str, Any] = {"kind": meta.kind}
|
|
160
|
+
for name in _recorded_fields(type(meta)):
|
|
161
|
+
record[name] = _encode_value(values[name])
|
|
162
|
+
return record
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def decode_meta(record: object) -> SessionMeta:
|
|
166
|
+
"""Rebuild listing metadata; raises ``ValueError``/``TypeError``/``KeyError`` on anything malformed."""
|
|
167
|
+
if not isinstance(record, dict):
|
|
168
|
+
raise TypeError("A catalog meta record must be an object.")
|
|
169
|
+
cls = _meta_class(record.get("kind"))
|
|
170
|
+
# Each value was checked against its field's hint by ``_decode_value``.
|
|
171
|
+
values: dict[str, Any] = {name: _decode_value(hint, record[name]) for name, hint in _recorded_fields(cls).items()}
|
|
172
|
+
return cls(**values)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _encode_signature(signature: FileSignature | None) -> list[int] | None:
|
|
176
|
+
return list(signature) if signature is not None else None
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _decode_signature(value: object, *, optional: bool = False) -> FileSignature | None:
|
|
180
|
+
if value is None and optional:
|
|
181
|
+
return None
|
|
182
|
+
if isinstance(value, list) and len(value) == 3 and all(type(item) is int for item in value):
|
|
183
|
+
return (value[0], value[1], value[2])
|
|
184
|
+
raise TypeError(f"Invalid catalog file signature: {value!r}")
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def encode_entry(entry: CatalogEntry) -> dict[str, Any]:
|
|
188
|
+
record: dict[str, Any] = {"signature": list(entry.signature), "meta": encode_meta(entry.meta)}
|
|
189
|
+
if entry.run is not None:
|
|
190
|
+
record["run"] = {
|
|
191
|
+
"run_id": entry.run.key.run_id,
|
|
192
|
+
"header": _encode_signature(entry.run.key.header),
|
|
193
|
+
"events": _encode_signature(entry.run.key.events),
|
|
194
|
+
"listed_at": entry.run.listed_at.isoformat() if entry.run.listed_at is not None else None,
|
|
195
|
+
}
|
|
196
|
+
return record
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def is_recordable(entry: CatalogEntry) -> bool:
|
|
200
|
+
"""Whether *entry* reads back from the catalog; one that would not (a malformed envelope) stays unrecorded."""
|
|
201
|
+
try:
|
|
202
|
+
decode_entry(encode_entry(entry))
|
|
203
|
+
except KeyError, ValueError, TypeError:
|
|
204
|
+
return False
|
|
205
|
+
return True
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def decode_entry(record: object) -> CatalogEntry:
|
|
209
|
+
"""Rebuild one entry; raises ``ValueError``/``TypeError``/``KeyError`` on anything malformed."""
|
|
210
|
+
if not isinstance(record, dict):
|
|
211
|
+
raise TypeError("A catalog entry must be an object.")
|
|
212
|
+
signature = _decode_signature(record["signature"])
|
|
213
|
+
if signature is None:
|
|
214
|
+
raise TypeError("A catalog entry needs a file signature.")
|
|
215
|
+
meta = decode_meta(record["meta"])
|
|
216
|
+
run: RunListing | None = None
|
|
217
|
+
raw_run = record.get("run")
|
|
218
|
+
if raw_run is not None:
|
|
219
|
+
if not isinstance(raw_run, dict) or not isinstance(raw_run.get("run_id"), str):
|
|
220
|
+
raise TypeError("Invalid catalog run record.")
|
|
221
|
+
listed_at = raw_run.get("listed_at")
|
|
222
|
+
if listed_at is not None and not isinstance(listed_at, str):
|
|
223
|
+
raise TypeError("Invalid catalog run time.")
|
|
224
|
+
run = RunListing(
|
|
225
|
+
RunListingKey(
|
|
226
|
+
raw_run["run_id"],
|
|
227
|
+
_decode_signature(raw_run.get("header"), optional=True),
|
|
228
|
+
_decode_signature(raw_run.get("events"), optional=True),
|
|
229
|
+
),
|
|
230
|
+
datetime.fromisoformat(listed_at) if listed_at is not None else None,
|
|
231
|
+
)
|
|
232
|
+
return CatalogEntry(signature, meta, run)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
class SessionCatalogFile:
|
|
236
|
+
"""Locked read-merge-write access to one sessions root's listing catalog."""
|
|
237
|
+
|
|
238
|
+
def __init__(self, sessions_root: Path) -> None:
|
|
239
|
+
self._root = Path(sessions_root)
|
|
240
|
+
|
|
241
|
+
@property
|
|
242
|
+
def path(self) -> Path:
|
|
243
|
+
return self._root / SESSION_CATALOG_DIR_NAME / SESSION_CATALOG_FILE_NAME
|
|
244
|
+
|
|
245
|
+
@property
|
|
246
|
+
def lock_path(self) -> Path:
|
|
247
|
+
return self._root / ".locks" / SESSION_CATALOG_LOCK_FILE_NAME
|
|
248
|
+
|
|
249
|
+
def load(self) -> dict[str, CatalogEntry] | None:
|
|
250
|
+
"""Entries of the current derivation, skipping malformed ones; ``None`` when the file cannot be read now.
|
|
251
|
+
|
|
252
|
+
A missing, corrupt or differently versioned catalog reads as empty.
|
|
253
|
+
"""
|
|
254
|
+
read = self._read_records()
|
|
255
|
+
if read is None:
|
|
256
|
+
return None
|
|
257
|
+
version, records = read
|
|
258
|
+
if version != CATALOG_DERIVATION_VERSION:
|
|
259
|
+
return {}
|
|
260
|
+
entries: dict[str, CatalogEntry] = {}
|
|
261
|
+
for short_id, record in records.items():
|
|
262
|
+
try:
|
|
263
|
+
entries[short_id] = decode_entry(record)
|
|
264
|
+
except KeyError, ValueError, TypeError:
|
|
265
|
+
logger.debug("Skipping malformed session catalog entry %s", short_id, exc_info=True)
|
|
266
|
+
return entries
|
|
267
|
+
|
|
268
|
+
def commit(
|
|
269
|
+
self,
|
|
270
|
+
updates: Mapping[str, CatalogEntry],
|
|
271
|
+
*,
|
|
272
|
+
still_valid: Callable[[str, CatalogEntry], bool],
|
|
273
|
+
is_live: Callable[[str], bool],
|
|
274
|
+
) -> CatalogCommit:
|
|
275
|
+
"""Merge *updates* into the file.
|
|
276
|
+
|
|
277
|
+
Under the lock, each update is re-checked with *still_valid* (so an
|
|
278
|
+
entry whose session changed since it was derived never replaces a
|
|
279
|
+
peer's fresher one) and every entry whose session is gone is dropped.
|
|
280
|
+
"""
|
|
281
|
+
try:
|
|
282
|
+
with self._lock():
|
|
283
|
+
read = self._read_records()
|
|
284
|
+
if read is None:
|
|
285
|
+
return CatalogCommit(settled=False)
|
|
286
|
+
version, records = read
|
|
287
|
+
if version is not None and version > CATALOG_DERIVATION_VERSION:
|
|
288
|
+
return CatalogCommit(settled=True)
|
|
289
|
+
current = records if version == CATALOG_DERIVATION_VERSION else {}
|
|
290
|
+
merged = dict(current)
|
|
291
|
+
for short_id, entry in updates.items():
|
|
292
|
+
if still_valid(short_id, entry):
|
|
293
|
+
merged[short_id] = encode_entry(entry)
|
|
294
|
+
merged = {short_id: record for short_id, record in merged.items() if is_live(short_id)}
|
|
295
|
+
if (merged != current or version != CATALOG_DERIVATION_VERSION) and not self._write(merged):
|
|
296
|
+
return CatalogCommit(settled=False)
|
|
297
|
+
return CatalogCommit(settled=True, signature=file_signature(self.path))
|
|
298
|
+
except TimeoutError:
|
|
299
|
+
logger.debug("Session catalog %s is locked; skipping update", self.path)
|
|
300
|
+
except OSError:
|
|
301
|
+
logger.debug("Failed to update session catalog %s", self.path, exc_info=True)
|
|
302
|
+
return CatalogCommit(settled=False)
|
|
303
|
+
|
|
304
|
+
def remove(self, short_ids: Collection[str]) -> bool:
|
|
305
|
+
"""Drop the entries of deleted sessions; whether none of them remains.
|
|
306
|
+
|
|
307
|
+
A catalog this version cannot read or rewrite (unreadable, corrupt or
|
|
308
|
+
another version) goes whole: it is only a cache, and it may still hold
|
|
309
|
+
the deleted sessions' prompt excerpts.
|
|
310
|
+
"""
|
|
311
|
+
try:
|
|
312
|
+
with self._lock():
|
|
313
|
+
read = self._read_records()
|
|
314
|
+
if read is None or read[0] != CATALOG_DERIVATION_VERSION:
|
|
315
|
+
self.path.unlink(missing_ok=True)
|
|
316
|
+
return True
|
|
317
|
+
records = read[1]
|
|
318
|
+
if not any(short_id in records for short_id in short_ids):
|
|
319
|
+
return True
|
|
320
|
+
return self._write({key: value for key, value in records.items() if key not in short_ids})
|
|
321
|
+
except TimeoutError:
|
|
322
|
+
logger.warning("Session catalog %s is locked; a deleted session's entry remains for now", self.path)
|
|
323
|
+
except OSError:
|
|
324
|
+
logger.warning("Failed to remove deleted sessions from catalog %s", self.path, exc_info=True)
|
|
325
|
+
return False
|
|
326
|
+
|
|
327
|
+
def _lock(self) -> FileLock:
|
|
328
|
+
self.lock_path.parent.mkdir(parents=True, exist_ok=True)
|
|
329
|
+
return FileLock(self.lock_path, timeout=SESSION_CATALOG_LOCK_TIMEOUT_SECONDS)
|
|
330
|
+
|
|
331
|
+
def _read_records(self) -> tuple[int | None, dict[str, Any]] | None:
|
|
332
|
+
"""``(version, records)``; ``(None, {})`` when missing or corrupt, ``None`` when unreadable right now."""
|
|
333
|
+
try:
|
|
334
|
+
raw = json.loads(read_owner_verified_bounded(self.path, max_bytes=MAX_SESSION_CATALOG_BYTES))
|
|
335
|
+
except OSError as exc:
|
|
336
|
+
if isinstance(exc, FileNotFoundError) or exc.errno in (errno.ENOENT, errno.ENOTDIR):
|
|
337
|
+
return None, {}
|
|
338
|
+
# Possibly transient (a peer's replace on Windows): never mistake it for an empty catalog.
|
|
339
|
+
logger.debug("Cannot read session catalog %s now", self.path, exc_info=True)
|
|
340
|
+
return None
|
|
341
|
+
except ValueError:
|
|
342
|
+
logger.debug("Corrupt session catalog %s", self.path, exc_info=True)
|
|
343
|
+
return None, {}
|
|
344
|
+
if not isinstance(raw, dict) or type(raw.get("version")) is not int:
|
|
345
|
+
return None, {}
|
|
346
|
+
sessions = raw.get("sessions")
|
|
347
|
+
if not isinstance(sessions, dict):
|
|
348
|
+
return None, {}
|
|
349
|
+
return raw["version"], {key: value for key, value in sessions.items() if isinstance(key, str)}
|
|
350
|
+
|
|
351
|
+
def _write(self, records: dict[str, Any]) -> bool:
|
|
352
|
+
text = json.dumps(
|
|
353
|
+
{"version": CATALOG_DERIVATION_VERSION, "sessions": records}, ensure_ascii=False, separators=(",", ":")
|
|
354
|
+
)
|
|
355
|
+
# A lone surrogate (a surrogateescaped path) is written as its JSON escape and reads back unchanged.
|
|
356
|
+
payload = text.encode("utf-8", errors="backslashreplace")
|
|
357
|
+
if len(payload) > MAX_SESSION_CATALOG_BYTES:
|
|
358
|
+
logger.warning("Session catalog %s would exceed its size ceiling; not written", self.path)
|
|
359
|
+
return False
|
|
360
|
+
atomic_write_owner_only_bytes(self.path, payload)
|
|
361
|
+
return True
|