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.
Files changed (1138) hide show
  1. chrys/__init__.py +16 -0
  2. chrys/app/__init__.py +3 -0
  3. chrys/app/acp/__init__.py +3 -0
  4. chrys/app/acp/bridge.py +664 -0
  5. chrys/app/acp/content.py +132 -0
  6. chrys/app/acp/doc/frontend-api.md +425 -0
  7. chrys/app/acp/history.py +585 -0
  8. chrys/app/acp/server.py +1796 -0
  9. chrys/app/acp/session_manager.py +1815 -0
  10. chrys/app/acp/tool_status.py +45 -0
  11. chrys/app/cli/__init__.py +3 -0
  12. chrys/app/cli/acp.py +178 -0
  13. chrys/app/cli/app.py +165 -0
  14. chrys/app/cli/approvals.py +101 -0
  15. chrys/app/cli/headless.py +223 -0
  16. chrys/app/cli/launch_cwd.py +42 -0
  17. chrys/app/cli/profiles.py +298 -0
  18. chrys/app/cli/progress.py +857 -0
  19. chrys/app/cli/run.py +425 -0
  20. chrys/app/cli/serve.py +1593 -0
  21. chrys/app/cli/tool_summary.py +225 -0
  22. chrys/app/cli/trajectory.py +201 -0
  23. chrys/app/cli/workflow.py +601 -0
  24. chrys/app/features/__init__.py +3 -0
  25. chrys/app/features/buddy/__init__.py +3 -0
  26. chrys/app/features/buddy/actions.py +68 -0
  27. chrys/app/features/buddy/animation.py +76 -0
  28. chrys/app/features/buddy/commands.py +196 -0
  29. chrys/app/features/buddy/docs/design.md +187 -0
  30. chrys/app/features/buddy/hatchery.py +80 -0
  31. chrys/app/features/buddy/lifecycle.py +41 -0
  32. chrys/app/features/buddy/model.py +265 -0
  33. chrys/app/features/buddy/pixel_renderer.py +113 -0
  34. chrys/app/features/buddy/pixel_sprites.py +279 -0
  35. chrys/app/features/buddy/portrait.py +76 -0
  36. chrys/app/features/buddy/progression.py +58 -0
  37. chrys/app/features/buddy/replies.py +191 -0
  38. chrys/app/features/buddy/sprites/alien.toml +70 -0
  39. chrys/app/features/buddy/sprites/axolotl.toml +71 -0
  40. chrys/app/features/buddy/sprites/bat.toml +69 -0
  41. chrys/app/features/buddy/sprites/bee.toml +69 -0
  42. chrys/app/features/buddy/sprites/cactus.toml +73 -0
  43. chrys/app/features/buddy/sprites/capybara.toml +70 -0
  44. chrys/app/features/buddy/sprites/cat.toml +69 -0
  45. chrys/app/features/buddy/sprites/crab.toml +70 -0
  46. chrys/app/features/buddy/sprites/dragon.toml +71 -0
  47. chrys/app/features/buddy/sprites/duck.toml +69 -0
  48. chrys/app/features/buddy/sprites/fox.toml +69 -0
  49. chrys/app/features/buddy/sprites/frog.toml +70 -0
  50. chrys/app/features/buddy/sprites/ghost.toml +70 -0
  51. chrys/app/features/buddy/sprites/goose.toml +69 -0
  52. chrys/app/features/buddy/sprites/jelly.toml +70 -0
  53. chrys/app/features/buddy/sprites/llama.toml +70 -0
  54. chrys/app/features/buddy/sprites/mushroom.toml +69 -0
  55. chrys/app/features/buddy/sprites/octopus.toml +70 -0
  56. chrys/app/features/buddy/sprites/owl.toml +70 -0
  57. chrys/app/features/buddy/sprites/panda.toml +71 -0
  58. chrys/app/features/buddy/sprites/penguin.toml +69 -0
  59. chrys/app/features/buddy/sprites/rabbit.toml +70 -0
  60. chrys/app/features/buddy/sprites/robot.toml +72 -0
  61. chrys/app/features/buddy/sprites/shark.toml +70 -0
  62. chrys/app/features/buddy/sprites/snail.toml +69 -0
  63. chrys/app/features/buddy/sprites/snake.toml +69 -0
  64. chrys/app/features/buddy/sprites/tabby.toml +69 -0
  65. chrys/app/features/buddy/sprites/turtle.toml +69 -0
  66. chrys/app/features/buddy/store.py +143 -0
  67. chrys/app/features/session_title/__init__.py +7 -0
  68. chrys/app/features/session_title/updater.py +582 -0
  69. chrys/app/installer.py +651 -0
  70. chrys/app/parsing.py +17 -0
  71. chrys/app/tui/__init__.py +3 -0
  72. chrys/app/tui/app.py +1287 -0
  73. chrys/app/tui/behaviors/__init__.py +3 -0
  74. chrys/app/tui/behaviors/click_outside_dismiss.py +90 -0
  75. chrys/app/tui/behaviors/insert_clipboard.py +73 -0
  76. chrys/app/tui/behaviors/right_click_copy.py +146 -0
  77. chrys/app/tui/binding_display.py +139 -0
  78. chrys/app/tui/buddy_messages.py +17 -0
  79. chrys/app/tui/buddy_reply.py +86 -0
  80. chrys/app/tui/chrys.tcss +549 -0
  81. chrys/app/tui/clipboard.py +109 -0
  82. chrys/app/tui/copy_messages.py +10 -0
  83. chrys/app/tui/i18n.py +230 -0
  84. chrys/app/tui/language.py +58 -0
  85. chrys/app/tui/notifications/__init__.py +12 -0
  86. chrys/app/tui/notifications/drivers.py +180 -0
  87. chrys/app/tui/notifications/service.py +126 -0
  88. chrys/app/tui/notifications/settings.py +141 -0
  89. chrys/app/tui/screens/__init__.py +3 -0
  90. chrys/app/tui/screens/agents/__init__.py +8 -0
  91. chrys/app/tui/screens/agents/agent_draft_store.py +937 -0
  92. chrys/app/tui/screens/agents/config.py +2142 -0
  93. chrys/app/tui/screens/agents/config.tcss +169 -0
  94. chrys/app/tui/screens/agents/panels/__init__.py +3 -0
  95. chrys/app/tui/screens/agents/panels/acp.py +1176 -0
  96. chrys/app/tui/screens/agents/panels/basic.py +497 -0
  97. chrys/app/tui/screens/agents/panels/compaction.py +231 -0
  98. chrys/app/tui/screens/agents/panels/config_card.py +218 -0
  99. chrys/app/tui/screens/agents/panels/instructions.py +91 -0
  100. chrys/app/tui/screens/agents/panels/markdown_report.py +76 -0
  101. chrys/app/tui/screens/agents/panels/mcp.py +2262 -0
  102. chrys/app/tui/screens/agents/panels/memory.py +684 -0
  103. chrys/app/tui/screens/agents/panels/path_entry.py +227 -0
  104. chrys/app/tui/screens/agents/panels/skills.py +806 -0
  105. chrys/app/tui/screens/agents/panels/subagents.py +652 -0
  106. chrys/app/tui/screens/agents/panels/tools.py +205 -0
  107. chrys/app/tui/screens/agents/picker.py +103 -0
  108. chrys/app/tui/screens/agents/skill_paths.py +38 -0
  109. chrys/app/tui/screens/agents/validation_messages.py +452 -0
  110. chrys/app/tui/screens/dialogs/__init__.py +35 -0
  111. chrys/app/tui/screens/dialogs/agent_load.py +834 -0
  112. chrys/app/tui/screens/dialogs/agent_load.tcss +78 -0
  113. chrys/app/tui/screens/dialogs/app_mode.py +55 -0
  114. chrys/app/tui/screens/dialogs/approval/__init__.py +15 -0
  115. chrys/app/tui/screens/dialogs/approval/approval.tcss +311 -0
  116. chrys/app/tui/screens/dialogs/approval/bodies/__init__.py +3 -0
  117. chrys/app/tui/screens/dialogs/approval/bodies/file_edit.py +210 -0
  118. chrys/app/tui/screens/dialogs/approval/bodies/sub_agent.py +82 -0
  119. chrys/app/tui/screens/dialogs/approval/body.py +110 -0
  120. chrys/app/tui/screens/dialogs/approval/dialog.py +565 -0
  121. chrys/app/tui/screens/dialogs/approval/mode.py +105 -0
  122. chrys/app/tui/screens/dialogs/ask_user.py +148 -0
  123. chrys/app/tui/screens/dialogs/ask_user.tcss +62 -0
  124. chrys/app/tui/screens/dialogs/base.py +84 -0
  125. chrys/app/tui/screens/dialogs/confirm.py +184 -0
  126. chrys/app/tui/screens/dialogs/confirm.tcss +46 -0
  127. chrys/app/tui/screens/dialogs/connection_test.py +183 -0
  128. chrys/app/tui/screens/dialogs/connection_test.tcss +92 -0
  129. chrys/app/tui/screens/dialogs/diagram.py +102 -0
  130. chrys/app/tui/screens/dialogs/diagram.tcss +24 -0
  131. chrys/app/tui/screens/dialogs/editor.py +378 -0
  132. chrys/app/tui/screens/dialogs/editor.tcss +132 -0
  133. chrys/app/tui/screens/dialogs/file_picker.py +582 -0
  134. chrys/app/tui/screens/dialogs/file_picker.tcss +78 -0
  135. chrys/app/tui/screens/dialogs/fork_session.py +219 -0
  136. chrys/app/tui/screens/dialogs/fork_session.tcss +75 -0
  137. chrys/app/tui/screens/dialogs/image_compression.py +102 -0
  138. chrys/app/tui/screens/dialogs/image_compression.tcss +28 -0
  139. chrys/app/tui/screens/dialogs/man_page.py +172 -0
  140. chrys/app/tui/screens/dialogs/man_page.tcss +39 -0
  141. chrys/app/tui/screens/dialogs/new_folder.py +277 -0
  142. chrys/app/tui/screens/dialogs/new_folder.tcss +51 -0
  143. chrys/app/tui/screens/dialogs/notice.tcss +14 -0
  144. chrys/app/tui/screens/dialogs/prompt_cache.py +144 -0
  145. chrys/app/tui/screens/dialogs/prompt_cache.tcss +38 -0
  146. chrys/app/tui/screens/dialogs/runtime_details.py +371 -0
  147. chrys/app/tui/screens/dialogs/runtime_details.tcss +49 -0
  148. chrys/app/tui/screens/dialogs/session_title.py +110 -0
  149. chrys/app/tui/screens/dialogs/session_title.tcss +43 -0
  150. chrys/app/tui/screens/dialogs/tool_view.py +207 -0
  151. chrys/app/tui/screens/dialogs/tool_view.tcss +43 -0
  152. chrys/app/tui/screens/dialogs/vision_unsupported.py +122 -0
  153. chrys/app/tui/screens/dialogs/vision_unsupported.tcss +35 -0
  154. chrys/app/tui/screens/dialogs/workflow_confirm.py +91 -0
  155. chrys/app/tui/screens/dialogs/workflow_confirm.tcss +17 -0
  156. chrys/app/tui/screens/dialogs/workflow_input.py +258 -0
  157. chrys/app/tui/screens/dialogs/workflow_input.tcss +73 -0
  158. chrys/app/tui/screens/dialogs/workflow_node.py +462 -0
  159. chrys/app/tui/screens/dialogs/workflow_node.tcss +41 -0
  160. chrys/app/tui/screens/dialogs/workflow_picker.py +46 -0
  161. chrys/app/tui/screens/dialogs/workflow_result.py +136 -0
  162. chrys/app/tui/screens/dialogs/workflow_result.tcss +14 -0
  163. chrys/app/tui/screens/diff/__init__.py +22 -0
  164. chrys/app/tui/screens/diff/rollback_modal.py +1381 -0
  165. chrys/app/tui/screens/diff/rollback_modal.tcss +248 -0
  166. chrys/app/tui/screens/diff/screen.py +1532 -0
  167. chrys/app/tui/screens/diff/screen.tcss +104 -0
  168. chrys/app/tui/screens/guides/__init__.py +7 -0
  169. chrys/app/tui/screens/guides/_docs/en/guides/configuration/agents.md +178 -0
  170. chrys/app/tui/screens/guides/_docs/en/guides/configuration/approval.md +117 -0
  171. chrys/app/tui/screens/guides/_docs/en/guides/configuration/chat-options.md +276 -0
  172. chrys/app/tui/screens/guides/_docs/en/guides/configuration/compaction.md +42 -0
  173. chrys/app/tui/screens/guides/_docs/en/guides/configuration/memory.md +48 -0
  174. chrys/app/tui/screens/guides/_docs/en/guides/configuration/models.md +158 -0
  175. chrys/app/tui/screens/guides/_docs/en/guides/configuration/settings.md +210 -0
  176. chrys/app/tui/screens/guides/_docs/en/guides/configuration/themes.md +61 -0
  177. chrys/app/tui/screens/guides/_docs/en/guides/configuration/tools.md +60 -0
  178. chrys/app/tui/screens/guides/_docs/en/guides/configuration/web-tools.md +286 -0
  179. chrys/app/tui/screens/guides/_docs/en/guides/daily-use/sessions.md +77 -0
  180. chrys/app/tui/screens/guides/_docs/en/guides/daily-use/workspaces.md +51 -0
  181. chrys/app/tui/screens/guides/_docs/en/guides/extensions/external-acp-agents.md +126 -0
  182. chrys/app/tui/screens/guides/_docs/en/guides/extensions/hooks.md +167 -0
  183. chrys/app/tui/screens/guides/_docs/en/guides/extensions/mcp.md +95 -0
  184. chrys/app/tui/screens/guides/_docs/en/guides/extensions/skills.md +175 -0
  185. chrys/app/tui/screens/guides/_docs/en/guides/running/icode-acp.md +61 -0
  186. chrys/app/tui/screens/guides/_docs/en/guides/running/icode-run.md +190 -0
  187. chrys/app/tui/screens/guides/_docs/en/guides/running/icode-serve.md +239 -0
  188. chrys/app/tui/screens/guides/_docs/en/guides/running/workflows.md +664 -0
  189. chrys/app/tui/screens/guides/_docs/en/reference/agent-profile.md +548 -0
  190. chrys/app/tui/screens/guides/_docs/en/reference/hooks.md +602 -0
  191. chrys/app/tui/screens/guides/_docs/en/reference/opentelemetry.md +177 -0
  192. chrys/app/tui/screens/guides/_docs/en/reference/settings.md +245 -0
  193. chrys/app/tui/screens/guides/_docs/en/reference/themes.md +149 -0
  194. chrys/app/tui/screens/guides/_docs/en/reference/tool-kinds-and-names.md +58 -0
  195. chrys/app/tui/screens/guides/_docs/en/reference/tui-slash-commands.md +76 -0
  196. chrys/app/tui/screens/guides/_docs/en/reference/workflows.md +1006 -0
  197. chrys/app/tui/screens/guides/_docs/en/start/getting-started.md +170 -0
  198. chrys/app/tui/screens/guides/_docs/en/start/what-is-icode.md +34 -0
  199. chrys/app/tui/screens/guides/_docs/index.yaml +100 -0
  200. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/agents.md +178 -0
  201. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/approval.md +117 -0
  202. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/chat-options.md +276 -0
  203. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/compaction.md +42 -0
  204. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/memory.md +48 -0
  205. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/models.md +158 -0
  206. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/settings.md +210 -0
  207. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/themes.md +61 -0
  208. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/tools.md +60 -0
  209. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/web-tools.md +286 -0
  210. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/daily-use/sessions.md +77 -0
  211. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/daily-use/workspaces.md +51 -0
  212. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/external-acp-agents.md +126 -0
  213. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/hooks.md +167 -0
  214. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/mcp.md +95 -0
  215. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/skills.md +175 -0
  216. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/icode-acp.md +61 -0
  217. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/icode-run.md +190 -0
  218. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/icode-serve.md +239 -0
  219. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/workflows.md +664 -0
  220. chrys/app/tui/screens/guides/_docs/zh-Hans/reference/agent-profile.md +548 -0
  221. chrys/app/tui/screens/guides/_docs/zh-Hans/reference/hooks.md +602 -0
  222. chrys/app/tui/screens/guides/_docs/zh-Hans/reference/opentelemetry.md +177 -0
  223. chrys/app/tui/screens/guides/_docs/zh-Hans/reference/settings.md +245 -0
  224. chrys/app/tui/screens/guides/_docs/zh-Hans/reference/themes.md +149 -0
  225. chrys/app/tui/screens/guides/_docs/zh-Hans/reference/tool-kinds-and-names.md +58 -0
  226. chrys/app/tui/screens/guides/_docs/zh-Hans/reference/tui-slash-commands.md +76 -0
  227. chrys/app/tui/screens/guides/_docs/zh-Hans/reference/workflows.md +1005 -0
  228. chrys/app/tui/screens/guides/_docs/zh-Hans/start/getting-started.md +170 -0
  229. chrys/app/tui/screens/guides/_docs/zh-Hans/start/what-is-icode.md +34 -0
  230. chrys/app/tui/screens/guides/index.py +215 -0
  231. chrys/app/tui/screens/guides/reader.py +173 -0
  232. chrys/app/tui/screens/guides/screen.py +442 -0
  233. chrys/app/tui/screens/guides/screen.tcss +52 -0
  234. chrys/app/tui/screens/logs/__init__.py +7 -0
  235. chrys/app/tui/screens/logs/screen.py +504 -0
  236. chrys/app/tui/screens/logs/screen.tcss +45 -0
  237. chrys/app/tui/screens/main/__init__.py +7 -0
  238. chrys/app/tui/screens/main/action_availability.py +30 -0
  239. chrys/app/tui/screens/main/buddy_command.py +143 -0
  240. chrys/app/tui/screens/main/commands.py +1073 -0
  241. chrys/app/tui/screens/main/config_actions.py +464 -0
  242. chrys/app/tui/screens/main/copy_actions.py +117 -0
  243. chrys/app/tui/screens/main/dialog_controllers.py +826 -0
  244. chrys/app/tui/screens/main/dialog_gateway.py +166 -0
  245. chrys/app/tui/screens/main/diff_controller.py +478 -0
  246. chrys/app/tui/screens/main/engine_read_model.py +184 -0
  247. chrys/app/tui/screens/main/event_handlers.py +2014 -0
  248. chrys/app/tui/screens/main/input_flow.py +241 -0
  249. chrys/app/tui/screens/main/live_diff.py +244 -0
  250. chrys/app/tui/screens/main/main_screen_presenter.py +295 -0
  251. chrys/app/tui/screens/main/model_indicator.py +217 -0
  252. chrys/app/tui/screens/main/navigation.py +343 -0
  253. chrys/app/tui/screens/main/ports.py +571 -0
  254. chrys/app/tui/screens/main/recent_dirs.py +123 -0
  255. chrys/app/tui/screens/main/rollback_controller.py +586 -0
  256. chrys/app/tui/screens/main/runtime_info.py +71 -0
  257. chrys/app/tui/screens/main/screen.py +2265 -0
  258. chrys/app/tui/screens/main/screen.tcss +21 -0
  259. chrys/app/tui/screens/main/session_handlers.py +1127 -0
  260. chrys/app/tui/screens/main/session_title.py +239 -0
  261. chrys/app/tui/screens/main/settings_coordinator.py +365 -0
  262. chrys/app/tui/screens/main/settings_persistence.py +184 -0
  263. chrys/app/tui/screens/main/shell_mode.py +112 -0
  264. chrys/app/tui/screens/main/state.py +289 -0
  265. chrys/app/tui/screens/main/submit_state.py +11 -0
  266. chrys/app/tui/screens/main/subscriptions.py +177 -0
  267. chrys/app/tui/screens/main/suggestions.py +982 -0
  268. chrys/app/tui/screens/main/tool_action_bridge.py +75 -0
  269. chrys/app/tui/screens/main/view_adapter.py +1567 -0
  270. chrys/app/tui/screens/main/workflow_browser.py +747 -0
  271. chrys/app/tui/screens/main/workflow_content.py +248 -0
  272. chrys/app/tui/screens/main/workflow_controller.py +240 -0
  273. chrys/app/tui/screens/main/workflow_feedback.py +137 -0
  274. chrys/app/tui/screens/main/workflow_flow.py +49 -0
  275. chrys/app/tui/screens/main/workflow_run_control.py +456 -0
  276. chrys/app/tui/screens/main/workflow_session_view.py +316 -0
  277. chrys/app/tui/screens/main/workspace_actions.py +272 -0
  278. chrys/app/tui/screens/main/workspace_branch.py +194 -0
  279. chrys/app/tui/screens/menus/__init__.py +8 -0
  280. chrys/app/tui/screens/menus/languages.py +114 -0
  281. chrys/app/tui/screens/models/__init__.py +7 -0
  282. chrys/app/tui/screens/models/picker.py +147 -0
  283. chrys/app/tui/screens/models/screen.py +2043 -0
  284. chrys/app/tui/screens/models/screen.tcss +255 -0
  285. chrys/app/tui/screens/sessions/__init__.py +8 -0
  286. chrys/app/tui/screens/sessions/presenter.py +586 -0
  287. chrys/app/tui/screens/sessions/screen.py +779 -0
  288. chrys/app/tui/screens/sessions/screen.tcss +127 -0
  289. chrys/app/tui/screens/settings/__init__.py +29 -0
  290. chrys/app/tui/screens/settings/danger.py +26 -0
  291. chrys/app/tui/screens/settings/dialog.py +281 -0
  292. chrys/app/tui/screens/settings/layout.py +519 -0
  293. chrys/app/tui/screens/settings/migrate_dialog.py +253 -0
  294. chrys/app/tui/screens/settings/panes/__init__.py +3 -0
  295. chrys/app/tui/screens/settings/panes/notifications.py +248 -0
  296. chrys/app/tui/screens/settings/panes/sessions.py +148 -0
  297. chrys/app/tui/screens/settings/ports.py +55 -0
  298. chrys/app/tui/screens/settings/provenance.py +102 -0
  299. chrys/app/tui/screens/settings/rows.py +546 -0
  300. chrys/app/tui/screens/settings/settings.tcss +406 -0
  301. chrys/app/tui/screens/themes/__init__.py +8 -0
  302. chrys/app/tui/screens/themes/dialogs.py +330 -0
  303. chrys/app/tui/screens/themes/editor.py +632 -0
  304. chrys/app/tui/screens/themes/editor.tcss +268 -0
  305. chrys/app/tui/screens/themes/messages.py +95 -0
  306. chrys/app/tui/screens/themes/palette.py +694 -0
  307. chrys/app/tui/screens/themes/panel.py +113 -0
  308. chrys/app/tui/screens/themes/picker.py +203 -0
  309. chrys/app/tui/screens/themes/save.py +191 -0
  310. chrys/app/tui/support/__init__.py +3 -0
  311. chrys/app/tui/support/file_manager.py +64 -0
  312. chrys/app/tui/support/gc_freeze.py +1114 -0
  313. chrys/app/tui/support/path_shortcuts.py +50 -0
  314. chrys/app/tui/support/prompt_history.py +298 -0
  315. chrys/app/tui/support/workspace_mru.py +476 -0
  316. chrys/app/tui/terminal/__init__.py +6 -0
  317. chrys/app/tui/terminal/_pty_backend.py +114 -0
  318. chrys/app/tui/terminal/emulator/__init__.py +24 -0
  319. chrys/app/tui/terminal/emulator/buffer.py +456 -0
  320. chrys/app/tui/terminal/emulator/charsets.py +113 -0
  321. chrys/app/tui/terminal/emulator/core.py +963 -0
  322. chrys/app/tui/terminal/emulator/input.py +342 -0
  323. chrys/app/tui/terminal/emulator/parser.py +227 -0
  324. chrys/app/tui/terminal/emulator/pen.py +158 -0
  325. chrys/app/tui/terminal/launcher.py +455 -0
  326. chrys/app/tui/terminal/leaked_reports.py +44 -0
  327. chrys/app/tui/terminal/panel.py +323 -0
  328. chrys/app/tui/terminal/pty_process.py +405 -0
  329. chrys/app/tui/terminal/rendering.py +151 -0
  330. chrys/app/tui/terminal/shell.py +273 -0
  331. chrys/app/tui/terminal/shell_integration.py +199 -0
  332. chrys/app/tui/terminal/title.py +224 -0
  333. chrys/app/tui/terminal/widget.py +654 -0
  334. chrys/app/tui/theme.py +422 -0
  335. chrys/app/tui/theme_loader.py +201 -0
  336. chrys/app/tui/theme_stylesheet.py +49 -0
  337. chrys/app/tui/themes/__init__.py +3 -0
  338. chrys/app/tui/themes/document.py +120 -0
  339. chrys/app/tui/themes/preview.py +170 -0
  340. chrys/app/tui/themes/store.py +176 -0
  341. chrys/app/tui/toasts.py +69 -0
  342. chrys/app/tui/util/__init__.py +3 -0
  343. chrys/app/tui/util/context_pressure.py +146 -0
  344. chrys/app/tui/util/debug_log.py +292 -0
  345. chrys/app/tui/util/diff_entries.py +64 -0
  346. chrys/app/tui/util/formatting.py +53 -0
  347. chrys/app/tui/util/git_branch.py +326 -0
  348. chrys/app/tui/util/invocation_progress.py +57 -0
  349. chrys/app/tui/util/logo.py +32 -0
  350. chrys/app/tui/util/message_gate.py +35 -0
  351. chrys/app/tui/util/mutation_sources.py +14 -0
  352. chrys/app/tui/util/picker_text.py +27 -0
  353. chrys/app/tui/util/removal.py +47 -0
  354. chrys/app/tui/util/rich_style.py +36 -0
  355. chrys/app/tui/util/source_text.py +12 -0
  356. chrys/app/tui/util/static_update.py +88 -0
  357. chrys/app/tui/util/todo_format.py +51 -0
  358. chrys/app/tui/util/visibility.py +214 -0
  359. chrys/app/tui/widgets/__init__.py +60 -0
  360. chrys/app/tui/widgets/ask_user_controls.py +652 -0
  361. chrys/app/tui/widgets/ask_user_markdown.py +17 -0
  362. chrys/app/tui/widgets/ask_user_prompt.py +606 -0
  363. chrys/app/tui/widgets/buttons.py +52 -0
  364. chrys/app/tui/widgets/chat/__init__.py +3 -0
  365. chrys/app/tui/widgets/chat/agent_transcript_surface.py +818 -0
  366. chrys/app/tui/widgets/chat/chrome.py +296 -0
  367. chrys/app/tui/widgets/chat/compaction_card.py +353 -0
  368. chrys/app/tui/widgets/chat/context_fold.py +77 -0
  369. chrys/app/tui/widgets/chat/file_snapshot.py +91 -0
  370. chrys/app/tui/widgets/chat/image_preview.py +214 -0
  371. chrys/app/tui/widgets/chat/live_renderer.py +383 -0
  372. chrys/app/tui/widgets/chat/messages.py +1039 -0
  373. chrys/app/tui/widgets/chat/panel.py +1769 -0
  374. chrys/app/tui/widgets/chat/ports.py +245 -0
  375. chrys/app/tui/widgets/chat/renderers/__init__.py +7 -0
  376. chrys/app/tui/widgets/chat/renderers/ask_user.py +747 -0
  377. chrys/app/tui/widgets/chat/renderers/execute.py +431 -0
  378. chrys/app/tui/widgets/chat/renderers/file_edit.py +925 -0
  379. chrys/app/tui/widgets/chat/renderers/hosted_code.py +83 -0
  380. chrys/app/tui/widgets/chat/renderers/hosted_generic.py +330 -0
  381. chrys/app/tui/widgets/chat/renderers/hosted_image.py +107 -0
  382. chrys/app/tui/widgets/chat/renderers/hosted_mcp.py +43 -0
  383. chrys/app/tui/widgets/chat/renderers/hosted_search.py +167 -0
  384. chrys/app/tui/widgets/chat/renderers/hosted_shell.py +132 -0
  385. chrys/app/tui/widgets/chat/renderers/read_file.py +399 -0
  386. chrys/app/tui/widgets/chat/renderers/search.py +348 -0
  387. chrys/app/tui/widgets/chat/renderers/skill.py +564 -0
  388. chrys/app/tui/widgets/chat/renderers/sleep.py +287 -0
  389. chrys/app/tui/widgets/chat/renderers/sub_agent.py +1385 -0
  390. chrys/app/tui/widgets/chat/renderers/todo.py +114 -0
  391. chrys/app/tui/widgets/chat/renderers/web.py +69 -0
  392. chrys/app/tui/widgets/chat/replay.py +1605 -0
  393. chrys/app/tui/widgets/chat/replay_mount.py +322 -0
  394. chrys/app/tui/widgets/chat/scroll_controller.py +579 -0
  395. chrys/app/tui/widgets/chat/selection_controller.py +435 -0
  396. chrys/app/tui/widgets/chat/session_json.py +508 -0
  397. chrys/app/tui/widgets/chat/toc_model.py +156 -0
  398. chrys/app/tui/widgets/chat/tool_call.py +2478 -0
  399. chrys/app/tui/widgets/chat/tool_registry.py +753 -0
  400. chrys/app/tui/widgets/chat/tool_renderers.py +247 -0
  401. chrys/app/tui/widgets/chat/tool_view_builders.py +260 -0
  402. chrys/app/tui/widgets/chat/transcript_reader.py +57 -0
  403. chrys/app/tui/widgets/checkbox.py +24 -0
  404. chrys/app/tui/widgets/chrome/__init__.py +3 -0
  405. chrys/app/tui/widgets/chrome/app_header.py +283 -0
  406. chrys/app/tui/widgets/chrome/commands.py +95 -0
  407. chrys/app/tui/widgets/chrome/file_index.py +68 -0
  408. chrys/app/tui/widgets/chrome/file_scanner.py +670 -0
  409. chrys/app/tui/widgets/chrome/footer.py +284 -0
  410. chrys/app/tui/widgets/chrome/image_paste.py +351 -0
  411. chrys/app/tui/widgets/chrome/input_bar.py +1299 -0
  412. chrys/app/tui/widgets/chrome/status_bar.py +911 -0
  413. chrys/app/tui/widgets/chrome/suggestion_list.py +652 -0
  414. chrys/app/tui/widgets/click_affordance.py +24 -0
  415. chrys/app/tui/widgets/color_picker/__init__.py +254 -0
  416. chrys/app/tui/widgets/color_picker/controls.py +171 -0
  417. chrys/app/tui/widgets/color_picker/model.py +89 -0
  418. chrys/app/tui/widgets/dialog_buttons.py +58 -0
  419. chrys/app/tui/widgets/dialog_scroll.py +24 -0
  420. chrys/app/tui/widgets/diff_view/__init__.py +20 -0
  421. chrys/app/tui/widgets/diff_view/cells.py +59 -0
  422. chrys/app/tui/widgets/diff_view/code.py +280 -0
  423. chrys/app/tui/widgets/diff_view/compute.py +286 -0
  424. chrys/app/tui/widgets/diff_view/gutter.py +161 -0
  425. chrys/app/tui/widgets/diff_view/inline.py +77 -0
  426. chrys/app/tui/widgets/diff_view/palette.py +87 -0
  427. chrys/app/tui/widgets/diff_view/protocols.py +24 -0
  428. chrys/app/tui/widgets/diff_view/rows.py +138 -0
  429. chrys/app/tui/widgets/diff_view/unified.py +267 -0
  430. chrys/app/tui/widgets/diff_view/widget.py +209 -0
  431. chrys/app/tui/widgets/editor/__init__.py +79 -0
  432. chrys/app/tui/widgets/editor/coordinates.py +30 -0
  433. chrys/app/tui/widgets/editor/highlighter.py +139 -0
  434. chrys/app/tui/widgets/editor/keymaps.py +357 -0
  435. chrys/app/tui/widgets/editor/types.py +216 -0
  436. chrys/app/tui/widgets/editor/vim.py +1195 -0
  437. chrys/app/tui/widgets/editor/widget.py +401 -0
  438. chrys/app/tui/widgets/file_preview.py +58 -0
  439. chrys/app/tui/widgets/hatch.py +138 -0
  440. chrys/app/tui/widgets/input.py +66 -0
  441. chrys/app/tui/widgets/loading.py +125 -0
  442. chrys/app/tui/widgets/markdown/__init__.py +25 -0
  443. chrys/app/tui/widgets/markdown/_utils.py +21 -0
  444. chrys/app/tui/widgets/markdown/anchors.py +71 -0
  445. chrys/app/tui/widgets/markdown/blocks.py +115 -0
  446. chrys/app/tui/widgets/markdown/copyable.py +163 -0
  447. chrys/app/tui/widgets/markdown/diagram/__init__.py +35 -0
  448. chrys/app/tui/widgets/markdown/diagram/canvas.py +409 -0
  449. chrys/app/tui/widgets/markdown/diagram/charts.py +52 -0
  450. chrys/app/tui/widgets/markdown/diagram/diagnostics.py +101 -0
  451. chrys/app/tui/widgets/markdown/diagram/geometry.py +46 -0
  452. chrys/app/tui/widgets/markdown/diagram/layout.py +494 -0
  453. chrys/app/tui/widgets/markdown/diagram/messages.py +18 -0
  454. chrys/app/tui/widgets/markdown/diagram/model.py +275 -0
  455. chrys/app/tui/widgets/markdown/diagram/parser.py +173 -0
  456. chrys/app/tui/widgets/markdown/diagram/parsers/__init__.py +3 -0
  457. chrys/app/tui/widgets/markdown/diagram/parsers/charts.py +349 -0
  458. chrys/app/tui/widgets/markdown/diagram/parsers/common.py +403 -0
  459. chrys/app/tui/widgets/markdown/diagram/parsers/graphs.py +1072 -0
  460. chrys/app/tui/widgets/markdown/diagram/parsers/planning.py +317 -0
  461. chrys/app/tui/widgets/markdown/diagram/parsers/schedule.py +298 -0
  462. chrys/app/tui/widgets/markdown/diagram/parsers/schemas.py +241 -0
  463. chrys/app/tui/widgets/markdown/diagram/parsers/structure.py +221 -0
  464. chrys/app/tui/widgets/markdown/diagram/presentation.py +32 -0
  465. chrys/app/tui/widgets/markdown/diagram/renderers/__init__.py +3 -0
  466. chrys/app/tui/widgets/markdown/diagram/renderers/charts.py +275 -0
  467. chrys/app/tui/widgets/markdown/diagram/renderers/common.py +73 -0
  468. chrys/app/tui/widgets/markdown/diagram/renderers/planning.py +157 -0
  469. chrys/app/tui/widgets/markdown/diagram/renderers/schedule.py +202 -0
  470. chrys/app/tui/widgets/markdown/diagram/renderers/structure.py +77 -0
  471. chrys/app/tui/widgets/markdown/diagram/renderers/xy.py +385 -0
  472. chrys/app/tui/widgets/markdown/diagram/router.py +651 -0
  473. chrys/app/tui/widgets/markdown/diagram/specs/__init__.py +3 -0
  474. chrys/app/tui/widgets/markdown/diagram/specs/charts.py +94 -0
  475. chrys/app/tui/widgets/markdown/diagram/specs/planning.py +68 -0
  476. chrys/app/tui/widgets/markdown/diagram/specs/schedule.py +65 -0
  477. chrys/app/tui/widgets/markdown/diagram/specs/structure.py +23 -0
  478. chrys/app/tui/widgets/markdown/diagram/viewer.py +92 -0
  479. chrys/app/tui/widgets/markdown/links.py +47 -0
  480. chrys/app/tui/widgets/markdown/math/__init__.py +36 -0
  481. chrys/app/tui/widgets/markdown/math/layout.py +739 -0
  482. chrys/app/tui/widgets/markdown/math/markdown.py +291 -0
  483. chrys/app/tui/widgets/markdown/math/parser.py +325 -0
  484. chrys/app/tui/widgets/markdown/math/symbols.py +325 -0
  485. chrys/app/tui/widgets/markdown/navigator.py +80 -0
  486. chrys/app/tui/widgets/markdown/parser.py +1057 -0
  487. chrys/app/tui/widgets/markdown/stream.py +73 -0
  488. chrys/app/tui/widgets/markdown/toc.py +98 -0
  489. chrys/app/tui/widgets/markdown/viewer.py +146 -0
  490. chrys/app/tui/widgets/markdown/widget.py +2046 -0
  491. chrys/app/tui/widgets/marquee.py +101 -0
  492. chrys/app/tui/widgets/option_menu.py +82 -0
  493. chrys/app/tui/widgets/page_navigator.py +118 -0
  494. chrys/app/tui/widgets/select.py +196 -0
  495. chrys/app/tui/widgets/selection.py +59 -0
  496. chrys/app/tui/widgets/sidebar/__init__.py +3 -0
  497. chrys/app/tui/widgets/sidebar/buddy.py +367 -0
  498. chrys/app/tui/widgets/sidebar/context.py +482 -0
  499. chrys/app/tui/widgets/sidebar/debug.py +195 -0
  500. chrys/app/tui/widgets/sidebar/empty_state.py +11 -0
  501. chrys/app/tui/widgets/sidebar/panel.py +198 -0
  502. chrys/app/tui/widgets/sidebar/tasks.py +198 -0
  503. chrys/app/tui/widgets/sidebar/toc.py +166 -0
  504. chrys/app/tui/widgets/sidebar/toc_types.py +30 -0
  505. chrys/app/tui/widgets/syntax_theme.py +85 -0
  506. chrys/app/tui/widgets/text_area.py +258 -0
  507. chrys/app/tui/widgets/trajectory/__init__.py +8 -0
  508. chrys/app/tui/widgets/trajectory/chartkit.py +264 -0
  509. chrys/app/tui/widgets/trajectory/insights.py +1266 -0
  510. chrys/app/tui/widgets/trajectory/overview.py +1101 -0
  511. chrys/app/tui/widgets/trajectory/panel.py +983 -0
  512. chrys/app/tui/widgets/trajectory/presentation.py +311 -0
  513. chrys/app/tui/widgets/trajectory/session_info.py +124 -0
  514. chrys/app/tui/widgets/trajectory/text_view.py +124 -0
  515. chrys/app/tui/widgets/trajectory/timeline.py +380 -0
  516. chrys/app/tui/widgets/welcome.py +126 -0
  517. chrys/app/tui/widgets/workflow/__init__.py +5 -0
  518. chrys/app/tui/widgets/workflow/edges.py +89 -0
  519. chrys/app/tui/widgets/workflow/graph.py +869 -0
  520. chrys/app/tui/widgets/workflow/info.py +310 -0
  521. chrys/app/tui/widgets/workflow/node_view.py +27 -0
  522. chrys/app/tui/widgets/workflow/output.py +225 -0
  523. chrys/app/tui/widgets/workflow/panel.py +658 -0
  524. chrys/app/tui/widgets/workflow/projector.py +324 -0
  525. chrys/app/tui/widgets/workflow/records.py +69 -0
  526. chrys/app/tui/widgets/workflow/scrollbar.py +82 -0
  527. chrys/app/tui/widgets/workflow/selection.py +151 -0
  528. chrys/app/tui/widgets/workflow/source.py +65 -0
  529. chrys/app/tui/widgets/workflow/text.py +257 -0
  530. chrys/app/tui/widgets/workflow/values.py +317 -0
  531. chrys/foundation/__init__.py +0 -0
  532. chrys/foundation/branding.py +15 -0
  533. chrys/foundation/config/__init__.py +7 -0
  534. chrys/foundation/config/coercion.py +374 -0
  535. chrys/foundation/config/context.py +34 -0
  536. chrys/foundation/config/env_file.py +141 -0
  537. chrys/foundation/config/env_layers.py +370 -0
  538. chrys/foundation/config/migrations.py +521 -0
  539. chrys/foundation/config/notification_events.py +22 -0
  540. chrys/foundation/config/process_settings.py +332 -0
  541. chrys/foundation/config/runtime_pointer.py +133 -0
  542. chrys/foundation/config/settings.py +1952 -0
  543. chrys/foundation/config/settings_store.py +996 -0
  544. chrys/foundation/config/spec.py +396 -0
  545. chrys/foundation/config/user_settings.py +140 -0
  546. chrys/foundation/config/warnings.py +262 -0
  547. chrys/foundation/config/web_values.py +135 -0
  548. chrys/foundation/config/yaml_store.py +487 -0
  549. chrys/foundation/errors/__init__.py +68 -0
  550. chrys/foundation/errors/_legacy.py +117 -0
  551. chrys/foundation/errors/_text.py +13 -0
  552. chrys/foundation/errors/_walk.py +85 -0
  553. chrys/foundation/errors/classify.py +278 -0
  554. chrys/foundation/errors/display.py +274 -0
  555. chrys/foundation/errors/formatting.py +283 -0
  556. chrys/foundation/errors/kinds.py +67 -0
  557. chrys/foundation/errors/network.py +497 -0
  558. chrys/foundation/errors/provider.py +445 -0
  559. chrys/foundation/errors/route.py +85 -0
  560. chrys/foundation/events/__init__.py +8 -0
  561. chrys/foundation/events/bus.py +143 -0
  562. chrys/foundation/events/types.py +1629 -0
  563. chrys/foundation/events/workflow.py +38 -0
  564. chrys/foundation/hosted_tools.py +142 -0
  565. chrys/foundation/i18n/__init__.py +32 -0
  566. chrys/foundation/i18n/_catalogs/zh-Hans/LC_MESSAGES/chrys.mo +0 -0
  567. chrys/foundation/i18n/catalogs.py +42 -0
  568. chrys/foundation/i18n/formatting.py +202 -0
  569. chrys/foundation/i18n/locale.py +92 -0
  570. chrys/foundation/i18n/localizer.py +187 -0
  571. chrys/foundation/i18n/messages.py +268 -0
  572. chrys/foundation/io/__init__.py +3 -0
  573. chrys/foundation/io/result_content.py +237 -0
  574. chrys/foundation/models/__init__.py +3 -0
  575. chrys/foundation/models/approval_reuse.py +23 -0
  576. chrys/foundation/models/ask_user.py +468 -0
  577. chrys/foundation/models/execution.py +18 -0
  578. chrys/foundation/models/history_markers.py +115 -0
  579. chrys/foundation/models/invocations.py +63 -0
  580. chrys/foundation/models/mutation_scope.py +28 -0
  581. chrys/foundation/models/session_env.py +87 -0
  582. chrys/foundation/models/session_surface.py +29 -0
  583. chrys/foundation/models/todos.py +74 -0
  584. chrys/foundation/models/turns.py +125 -0
  585. chrys/foundation/models/workflow_session.py +89 -0
  586. chrys/foundation/models/workspace.py +59 -0
  587. chrys/foundation/net/__init__.py +3 -0
  588. chrys/foundation/net/address_class.py +57 -0
  589. chrys/foundation/net/route_probe.py +94 -0
  590. chrys/foundation/net/url.py +107 -0
  591. chrys/foundation/observability/__init__.py +3 -0
  592. chrys/foundation/observability/exporters.py +174 -0
  593. chrys/foundation/observability/gate.py +31 -0
  594. chrys/foundation/observability/setup.py +497 -0
  595. chrys/foundation/observability/sink.py +228 -0
  596. chrys/foundation/patches/__init__.py +19 -0
  597. chrys/foundation/patches/mdurl_cache.py +59 -0
  598. chrys/foundation/patches/patcher.py +287 -0
  599. chrys/foundation/patches/staged_members.py +205 -0
  600. chrys/foundation/patches/textual_block_border.py +105 -0
  601. chrys/foundation/patches/textual_button_ansi.py +65 -0
  602. chrys/foundation/patches/textual_button_ansi_old.txt +68 -0
  603. chrys/foundation/patches/textual_callback_cache.py +248 -0
  604. chrys/foundation/patches/textual_callback_dispatch.py +109 -0
  605. chrys/foundation/patches/textual_compositor_cjk.py +566 -0
  606. chrys/foundation/patches/textual_dispatch_cache.py +150 -0
  607. chrys/foundation/patches/textual_ime_cursor_anchor.py +92 -0
  608. chrys/foundation/patches/textual_kitty_keyboard.py +268 -0
  609. chrys/foundation/patches/textual_lru_acyclic.py +241 -0
  610. chrys/foundation/patches/textual_message_pump.py +136 -0
  611. chrys/foundation/patches/textual_node_diet.py +875 -0
  612. chrys/foundation/patches/textual_one_shot_timer.py +48 -0
  613. chrys/foundation/patches/textual_option_list.py +90 -0
  614. chrys/foundation/patches/textual_precompose.py +89 -0
  615. chrys/foundation/patches/textual_pruned_tabs.py +69 -0
  616. chrys/foundation/patches/textual_reflow_reuse.py +1753 -0
  617. chrys/foundation/patches/textual_removed_node_caches.py +86 -0
  618. chrys/foundation/patches/textual_removed_screen_callbacks.py +55 -0
  619. chrys/foundation/patches/textual_selection_extract.py +48 -0
  620. chrys/foundation/patches/textual_strip_cycles.py +119 -0
  621. chrys/foundation/patches/textual_tab_selection.py +1611 -0
  622. chrys/foundation/patches/textual_timer_skip.py +70 -0
  623. chrys/foundation/patches/textual_utf8_decoder.py +106 -0
  624. chrys/foundation/patches/textual_win_sleep.py +167 -0
  625. chrys/foundation/patches/watchdog_fsevents.py +70 -0
  626. chrys/foundation/patches/watchdog_windows.py +52 -0
  627. chrys/foundation/platform/__init__.py +556 -0
  628. chrys/foundation/platform/c_api.py +32 -0
  629. chrys/foundation/platform/child_reap.py +107 -0
  630. chrys/foundation/platform/command_line.py +95 -0
  631. chrys/foundation/platform/files.py +1470 -0
  632. chrys/foundation/platform/output_capture.py +113 -0
  633. chrys/foundation/platform/paths.py +193 -0
  634. chrys/foundation/platform/process.py +1468 -0
  635. chrys/foundation/platform/pty_output.py +19 -0
  636. chrys/foundation/platform/runtime_paths.py +281 -0
  637. chrys/foundation/platform/win_console.py +129 -0
  638. chrys/foundation/reasoning_origin.py +67 -0
  639. chrys/foundation/recovery.py +16 -0
  640. chrys/foundation/retry.py +278 -0
  641. chrys/foundation/text/__init__.py +3 -0
  642. chrys/foundation/text/encoding.py +603 -0
  643. chrys/foundation/text/images.py +304 -0
  644. chrys/foundation/text/lines.py +24 -0
  645. chrys/foundation/text/mentions.py +90 -0
  646. chrys/foundation/text/model_json.py +25 -0
  647. chrys/foundation/text/reading_order.py +20 -0
  648. chrys/foundation/text/tokenizer.py +25 -0
  649. chrys/foundation/text/tool_output.py +287 -0
  650. chrys/foundation/text/yaml_io.py +62 -0
  651. chrys/foundation/tool_call_context.py +202 -0
  652. chrys/foundation/tool_execution_stamp.py +184 -0
  653. chrys/foundation/tool_invocation_order.py +48 -0
  654. chrys/foundation/tool_kinds.py +102 -0
  655. chrys/foundation/tool_result_metadata.py +287 -0
  656. chrys/foundation/trajectory/__init__.py +10 -0
  657. chrys/foundation/trajectory/context.py +481 -0
  658. chrys/foundation/trajectory/envelope.py +571 -0
  659. chrys/foundation/trajectory/event_types.py +294 -0
  660. chrys/foundation/trajectory/finalizers.py +46 -0
  661. chrys/foundation/trajectory/fingerprint.py +62 -0
  662. chrys/foundation/trajectory/ids.py +155 -0
  663. chrys/foundation/trajectory/keys.py +99 -0
  664. chrys/foundation/trajectory/lease.py +173 -0
  665. chrys/foundation/trajectory/metadata.py +72 -0
  666. chrys/foundation/trajectory/reader.py +271 -0
  667. chrys/foundation/trajectory/recovery.py +225 -0
  668. chrys/foundation/trajectory/revisions.py +152 -0
  669. chrys/foundation/trajectory/segments.py +225 -0
  670. chrys/foundation/trajectory/tools.py +117 -0
  671. chrys/foundation/trajectory/usage.py +151 -0
  672. chrys/foundation/trajectory/writer.py +972 -0
  673. chrys/foundation/trajectory_timing.py +97 -0
  674. chrys/foundation/util/__init__.py +3 -0
  675. chrys/foundation/util/async_tasks.py +29 -0
  676. chrys/foundation/util/chrys_headers.py +23 -0
  677. chrys/foundation/util/env_templates.py +104 -0
  678. chrys/foundation/util/filenames.py +96 -0
  679. chrys/foundation/util/header_charset.py +138 -0
  680. chrys/foundation/util/httpx_helpers.py +12 -0
  681. chrys/foundation/util/lock.py +236 -0
  682. chrys/foundation/util/once_close.py +46 -0
  683. chrys/foundation/util/session_ids.py +29 -0
  684. chrys/foundation/util/shell_tokens.py +88 -0
  685. chrys/foundation/util/sub_agent_context.py +38 -0
  686. chrys/foundation/util/time.py +35 -0
  687. chrys/foundation/util/unicode_scalars.py +36 -0
  688. chrys/foundation/vendor/__init__.py +43 -0
  689. chrys/foundation/vendor/ripgrep/rg-aarch64-apple-darwin +0 -0
  690. chrys/foundation/vendor/ripgrep/rg-aarch64-pc-windows-msvc.exe +0 -0
  691. chrys/foundation/vendor/ripgrep/rg-aarch64-unknown-linux-gnu +0 -0
  692. chrys/foundation/vendor/ripgrep/rg-x86_64-apple-darwin +0 -0
  693. chrys/foundation/vendor/ripgrep/rg-x86_64-pc-windows-msvc.exe +0 -0
  694. chrys/foundation/vendor/ripgrep/rg-x86_64-unknown-linux-musl +0 -0
  695. chrys/kernel/__init__.py +256 -0
  696. chrys/kernel/_content.py +1866 -0
  697. chrys/kernel/_loop_recorder.py +631 -0
  698. chrys/kernel/_null_overlay.py +1026 -0
  699. chrys/kernel/_result_ceiling.py +216 -0
  700. chrys/kernel/_serialization.py +431 -0
  701. chrys/kernel/_tool_arg_errors.py +1629 -0
  702. chrys/kernel/_tool_execution.py +1172 -0
  703. chrys/kernel/_tool_expansion.py +26 -0
  704. chrys/kernel/_types.py +2598 -0
  705. chrys/kernel/agent.py +994 -0
  706. chrys/kernel/client.py +1084 -0
  707. chrys/kernel/compaction.py +1315 -0
  708. chrys/kernel/exceptions.py +211 -0
  709. chrys/kernel/exchanges.py +683 -0
  710. chrys/kernel/identity.py +95 -0
  711. chrys/kernel/images.py +36 -0
  712. chrys/kernel/instrumentation.py +1618 -0
  713. chrys/kernel/loop.py +2308 -0
  714. chrys/kernel/middleware.py +616 -0
  715. chrys/kernel/sessions.py +604 -0
  716. chrys/kernel/tools.py +1061 -0
  717. chrys/kernel/types.py +126 -0
  718. chrys/orchestration/__init__.py +3 -0
  719. chrys/orchestration/engine/__init__.py +3 -0
  720. chrys/orchestration/engine/assembly.py +236 -0
  721. chrys/orchestration/engine/build/__init__.py +3 -0
  722. chrys/orchestration/engine/build/builder.py +1061 -0
  723. chrys/orchestration/engine/build/construction.py +408 -0
  724. chrys/orchestration/engine/build/loaded.py +139 -0
  725. chrys/orchestration/engine/engine.py +789 -0
  726. chrys/orchestration/engine/execution.py +771 -0
  727. chrys/orchestration/engine/loader.py +865 -0
  728. chrys/orchestration/engine/rollback.py +918 -0
  729. chrys/orchestration/engine/run/__init__.py +3 -0
  730. chrys/orchestration/engine/run/active_injection.py +419 -0
  731. chrys/orchestration/engine/run/attachments.py +519 -0
  732. chrys/orchestration/engine/run/bindings.py +1095 -0
  733. chrys/orchestration/engine/run/coordinator.py +1062 -0
  734. chrys/orchestration/engine/run/finalizer.py +417 -0
  735. chrys/orchestration/engine/run/input_refs.py +60 -0
  736. chrys/orchestration/engine/run/prompt_content.py +457 -0
  737. chrys/orchestration/engine/run/resume.py +299 -0
  738. chrys/orchestration/engine/run/retry.py +948 -0
  739. chrys/orchestration/engine/run/runner.py +625 -0
  740. chrys/orchestration/engine/run/runtime_skills.py +146 -0
  741. chrys/orchestration/engine/run/sub_agent_coordination.py +126 -0
  742. chrys/orchestration/engine/run/turn_hooks.py +258 -0
  743. chrys/orchestration/engine/run/turn_state.py +149 -0
  744. chrys/orchestration/engine/run/working_dir.py +50 -0
  745. chrys/orchestration/engine/session_lifecycle.py +2013 -0
  746. chrys/orchestration/engine/state/__init__.py +3 -0
  747. chrys/orchestration/engine/state/active_session.py +183 -0
  748. chrys/orchestration/engine/state/controls.py +639 -0
  749. chrys/orchestration/engine/state/current_agent.py +26 -0
  750. chrys/orchestration/engine/state/lifecycle_permits.py +395 -0
  751. chrys/orchestration/engine/state/machine.py +193 -0
  752. chrys/orchestration/engine/state/session_writer.py +434 -0
  753. chrys/orchestration/engine/trajectory.py +714 -0
  754. chrys/orchestration/engine/usage.py +118 -0
  755. chrys/orchestration/invoker/__init__.py +3 -0
  756. chrys/orchestration/invoker/acp.py +727 -0
  757. chrys/orchestration/invoker/acp_protocol.py +1115 -0
  758. chrys/orchestration/invoker/acp_spec.py +79 -0
  759. chrys/orchestration/invoker/attempts.py +994 -0
  760. chrys/orchestration/invoker/child_compaction.py +131 -0
  761. chrys/orchestration/invoker/child_history.py +99 -0
  762. chrys/orchestration/invoker/contracts.py +253 -0
  763. chrys/orchestration/invoker/evidence.py +94 -0
  764. chrys/orchestration/invoker/kernel.py +343 -0
  765. chrys/orchestration/invoker/origin.py +79 -0
  766. chrys/orchestration/invoker/resources.py +343 -0
  767. chrys/orchestration/invoker/runtime.py +299 -0
  768. chrys/orchestration/invoker/usage.py +80 -0
  769. chrys/orchestration/session_hooks.py +117 -0
  770. chrys/orchestration/session_host.py +1023 -0
  771. chrys/orchestration/session_resources.py +95 -0
  772. chrys/orchestration/session_usage.py +219 -0
  773. chrys/orchestration/startup.py +255 -0
  774. chrys/orchestration/sub_agents/__init__.py +3 -0
  775. chrys/orchestration/sub_agents/acp_policy.py +269 -0
  776. chrys/orchestration/sub_agents/kernel_policy.py +848 -0
  777. chrys/orchestration/sub_agents/registration.py +44 -0
  778. chrys/orchestration/sub_agents/shell.py +337 -0
  779. chrys/orchestration/sub_agents/tools.py +2228 -0
  780. chrys/orchestration/workflows/__init__.py +3 -0
  781. chrys/orchestration/workflows/agent_archive.py +156 -0
  782. chrys/orchestration/workflows/agent_node.py +894 -0
  783. chrys/orchestration/workflows/agent_node_build.py +687 -0
  784. chrys/orchestration/workflows/catalog.py +332 -0
  785. chrys/orchestration/workflows/coordinator.py +771 -0
  786. chrys/orchestration/workflows/hooks.py +176 -0
  787. chrys/orchestration/workflows/preview.py +322 -0
  788. chrys/orchestration/workflows/rollback.py +151 -0
  789. chrys/orchestration/workflows/runner.py +986 -0
  790. chrys/orchestration/workflows/session.py +390 -0
  791. chrys/orchestration/workflows/settings.py +97 -0
  792. chrys/orchestration/workflows/validation.py +674 -0
  793. chrys/orchestration/workflows/worker_client.py +1002 -0
  794. chrys/service/__init__.py +3 -0
  795. chrys/service/acp_client/__init__.py +65 -0
  796. chrys/service/acp_client/_transport.py +922 -0
  797. chrys/service/acp_client/client.py +1311 -0
  798. chrys/service/acp_client/errors.py +224 -0
  799. chrys/service/acp_client/protocol.py +220 -0
  800. chrys/service/acp_client/spawn.py +239 -0
  801. chrys/service/acp_client/spec.py +100 -0
  802. chrys/service/agent_middleware/__init__.py +60 -0
  803. chrys/service/agent_middleware/_metadata_keys.py +52 -0
  804. chrys/service/agent_middleware/control/__init__.py +3 -0
  805. chrys/service/agent_middleware/control/approval.py +769 -0
  806. chrys/service/agent_middleware/control/approval_hooks.py +62 -0
  807. chrys/service/agent_middleware/control/ask_user.py +207 -0
  808. chrys/service/agent_middleware/control/interrupt.py +50 -0
  809. chrys/service/agent_middleware/control/sleep.py +118 -0
  810. chrys/service/agent_middleware/control/todo.py +117 -0
  811. chrys/service/agent_middleware/events/__init__.py +3 -0
  812. chrys/service/agent_middleware/events/hook_dispatch.py +250 -0
  813. chrys/service/agent_middleware/events/hosted_tools.py +1762 -0
  814. chrys/service/agent_middleware/events/intermediate_text.py +128 -0
  815. chrys/service/agent_middleware/events/rejection_metadata.py +50 -0
  816. chrys/service/agent_middleware/events/result_persistence.py +98 -0
  817. chrys/service/agent_middleware/events/sub_agent_events.py +951 -0
  818. chrys/service/agent_middleware/events/tool_error_text.py +22 -0
  819. chrys/service/agent_middleware/events/tool_events.py +779 -0
  820. chrys/service/agent_middleware/injection.py +510 -0
  821. chrys/service/agent_middleware/reminders/__init__.py +91 -0
  822. chrys/service/agent_middleware/reminders/archive_pointer.py +154 -0
  823. chrys/service/agent_middleware/reminders/base.py +25 -0
  824. chrys/service/agent_middleware/reminders/context_usage.py +74 -0
  825. chrys/service/agent_middleware/reminders/file_change.py +63 -0
  826. chrys/service/agent_middleware/reminders/mcp.py +39 -0
  827. chrys/service/agent_middleware/reminders/profile_switch.py +106 -0
  828. chrys/service/agent_middleware/reminders/runtime_env.py +190 -0
  829. chrys/service/agent_middleware/reminders/skills.py +44 -0
  830. chrys/service/agent_middleware/reminders/sub_agents.py +35 -0
  831. chrys/service/agent_middleware/reminders/todo.py +35 -0
  832. chrys/service/agent_middleware/reminders/turn_line.py +48 -0
  833. chrys/service/agent_middleware/response_validation.py +1380 -0
  834. chrys/service/agent_middleware/system_reminder.py +1107 -0
  835. chrys/service/agent_middleware/validators.py +396 -0
  836. chrys/service/analytics/__init__.py +124 -0
  837. chrys/service/analytics/_actions.py +896 -0
  838. chrys/service/analytics/_context_evidence.py +357 -0
  839. chrys/service/analytics/_critical_path.py +179 -0
  840. chrys/service/analytics/_facts.py +919 -0
  841. chrys/service/analytics/_insights.py +769 -0
  842. chrys/service/analytics/_metric_ops.py +83 -0
  843. chrys/service/analytics/_session_projection.py +460 -0
  844. chrys/service/analytics/_timeline.py +434 -0
  845. chrys/service/analytics/_turn_graph.py +1237 -0
  846. chrys/service/analytics/_turns.py +1377 -0
  847. chrys/service/analytics/_workflows.py +171 -0
  848. chrys/service/analytics/aggregation.py +1474 -0
  849. chrys/service/analytics/classification.py +128 -0
  850. chrys/service/analytics/export.py +803 -0
  851. chrys/service/analytics/findings.py +282 -0
  852. chrys/service/analytics/math.py +79 -0
  853. chrys/service/analytics/model.py +886 -0
  854. chrys/service/analytics/reader.py +321 -0
  855. chrys/service/approval/__init__.py +3 -0
  856. chrys/service/approval/arbitration.py +102 -0
  857. chrys/service/approval/command_identity.py +109 -0
  858. chrys/service/approval/correlation.py +57 -0
  859. chrys/service/approval/grant_store.py +108 -0
  860. chrys/service/approval/judge.py +589 -0
  861. chrys/service/approval/level.py +15 -0
  862. chrys/service/approval/policy.py +209 -0
  863. chrys/service/approval/reuse.py +223 -0
  864. chrys/service/approval/reuse_binding.py +133 -0
  865. chrys/service/approval/safety_classifier.py +536 -0
  866. chrys/service/approval/turn_context.py +49 -0
  867. chrys/service/context/__init__.py +3 -0
  868. chrys/service/context/compaction/__init__.py +38 -0
  869. chrys/service/context/compaction/breaker.py +156 -0
  870. chrys/service/context/compaction/budgets.py +93 -0
  871. chrys/service/context/compaction/calibration.py +77 -0
  872. chrys/service/context/compaction/compression.py +690 -0
  873. chrys/service/context/compaction/current_turn_drop.py +458 -0
  874. chrys/service/context/compaction/events.py +201 -0
  875. chrys/service/context/compaction/exclusions.py +261 -0
  876. chrys/service/context/compaction/groups.py +361 -0
  877. chrys/service/context/compaction/last_words.py +1996 -0
  878. chrys/service/context/compaction/last_words_state.py +752 -0
  879. chrys/service/context/compaction/scoped.py +472 -0
  880. chrys/service/context/compaction/spill.py +1947 -0
  881. chrys/service/context/compaction/strategy.py +1116 -0
  882. chrys/service/context/compaction/summaries.py +319 -0
  883. chrys/service/context/compaction/turns.py +375 -0
  884. chrys/service/context/manager.py +174 -0
  885. chrys/service/context/memory_loader.py +792 -0
  886. chrys/service/context/middleware/__init__.py +3 -0
  887. chrys/service/context/middleware/usage.py +332 -0
  888. chrys/service/context/providers/__init__.py +3 -0
  889. chrys/service/context/providers/context_management.py +432 -0
  890. chrys/service/context/providers/history.py +647 -0
  891. chrys/service/context/providers/memory.py +50 -0
  892. chrys/service/context/tool_content.py +22 -0
  893. chrys/service/hooks/__init__.py +37 -0
  894. chrys/service/hooks/detached_worker.py +118 -0
  895. chrys/service/hooks/docs/README.md +14 -0
  896. chrys/service/hooks/docs/authoring.md +684 -0
  897. chrys/service/hooks/docs/configuration.md +499 -0
  898. chrys/service/hooks/docs/design.md +359 -0
  899. chrys/service/hooks/docs/project-hooks.md +348 -0
  900. chrys/service/hooks/events.py +157 -0
  901. chrys/service/hooks/loader.py +665 -0
  902. chrys/service/hooks/manager.py +807 -0
  903. chrys/service/hooks/matcher.py +111 -0
  904. chrys/service/hooks/outbox.py +294 -0
  905. chrys/service/hooks/runner.py +572 -0
  906. chrys/service/hooks/schema.py +407 -0
  907. chrys/service/llm/__init__.py +7 -0
  908. chrys/service/llm/_structured_outputs.py +192 -0
  909. chrys/service/llm/anthropic_messages/__init__.py +9 -0
  910. chrys/service/llm/anthropic_messages/client.py +254 -0
  911. chrys/service/llm/anthropic_messages/decode.py +331 -0
  912. chrys/service/llm/anthropic_messages/history.py +279 -0
  913. chrys/service/llm/anthropic_messages/request.py +323 -0
  914. chrys/service/llm/anthropic_messages/server_tools.py +319 -0
  915. chrys/service/llm/anthropic_messages/stream.py +297 -0
  916. chrys/service/llm/anthropic_messages/thinking_binding.py +156 -0
  917. chrys/service/llm/chat_completions/__init__.py +9 -0
  918. chrys/service/llm/chat_completions/client.py +369 -0
  919. chrys/service/llm/chat_completions/decode.py +338 -0
  920. chrys/service/llm/chat_completions/history.py +657 -0
  921. chrys/service/llm/chat_completions/reasoning.py +244 -0
  922. chrys/service/llm/chat_completions/request.py +192 -0
  923. chrys/service/llm/chat_completions/stream.py +546 -0
  924. chrys/service/llm/chat_completions/validation.py +308 -0
  925. chrys/service/llm/clients.py +613 -0
  926. chrys/service/llm/images.py +65 -0
  927. chrys/service/llm/mock.py +413 -0
  928. chrys/service/llm/observer.py +446 -0
  929. chrys/service/llm/one_shot.py +106 -0
  930. chrys/service/llm/openai_exceptions.py +93 -0
  931. chrys/service/llm/openai_responses/__init__.py +10 -0
  932. chrys/service/llm/openai_responses/client.py +323 -0
  933. chrys/service/llm/openai_responses/decode.py +432 -0
  934. chrys/service/llm/openai_responses/history.py +519 -0
  935. chrys/service/llm/openai_responses/hosted.py +625 -0
  936. chrys/service/llm/openai_responses/replay.py +629 -0
  937. chrys/service/llm/openai_responses/request.py +342 -0
  938. chrys/service/llm/openai_responses/stream.py +847 -0
  939. chrys/service/llm/openai_timestamps.py +57 -0
  940. chrys/service/llm/providers.py +107 -0
  941. chrys/service/llm/proxy_route.py +54 -0
  942. chrys/service/llm/raw_http_log.py +309 -0
  943. chrys/service/llm/route_facts.py +63 -0
  944. chrys/service/llm/route_sessions.py +49 -0
  945. chrys/service/llm/sdk_anthropic.py +18 -0
  946. chrys/service/llm/sdk_headers.py +47 -0
  947. chrys/service/llm/sdk_openai.py +18 -0
  948. chrys/service/llm/sdk_retry.py +59 -0
  949. chrys/service/llm/wire_client.py +246 -0
  950. chrys/service/mcp/__init__.py +3 -0
  951. chrys/service/mcp/_connection.py +235 -0
  952. chrys/service/mcp/_http_transport.py +350 -0
  953. chrys/service/mcp/_progressive.py +459 -0
  954. chrys/service/mcp/_stdio_transport.py +614 -0
  955. chrys/service/mcp/_tool_mixins.py +96 -0
  956. chrys/service/mcp/adapter.py +1031 -0
  957. chrys/service/mcp/cache.py +476 -0
  958. chrys/service/mcp/content.py +32 -0
  959. chrys/service/mcp/errors.py +169 -0
  960. chrys/service/mcp/owned.py +1330 -0
  961. chrys/service/mcp/result_limits.py +168 -0
  962. chrys/service/mcp/thinking_warning.py +95 -0
  963. chrys/service/mcp/validation.py +74 -0
  964. chrys/service/mutations/__init__.py +84 -0
  965. chrys/service/mutations/coordination.py +894 -0
  966. chrys/service/mutations/detector.py +458 -0
  967. chrys/service/mutations/git_calibrator.py +397 -0
  968. chrys/service/mutations/git_state.py +647 -0
  969. chrys/service/mutations/pipeline.py +779 -0
  970. chrys/service/mutations/scanner.py +466 -0
  971. chrys/service/mutations/snapshot_files.py +233 -0
  972. chrys/service/mutations/store.py +625 -0
  973. chrys/service/mutations/tool_names.py +15 -0
  974. chrys/service/mutations/trace.py +246 -0
  975. chrys/service/mutations/tracker.py +1289 -0
  976. chrys/service/mutations/types.py +559 -0
  977. chrys/service/mutations/workspace_changes.py +1583 -0
  978. chrys/service/profiles/__init__.py +2 -0
  979. chrys/service/profiles/agents/__init__.py +3 -0
  980. chrys/service/profiles/agents/builtins/Code.yaml +199 -0
  981. chrys/service/profiles/agents/builtins/Explore.yaml +62 -0
  982. chrys/service/profiles/agents/builtins/General.yaml +114 -0
  983. chrys/service/profiles/agents/builtins/QA.yaml +128 -0
  984. chrys/service/profiles/agents/loader.py +808 -0
  985. chrys/service/profiles/agents/registry.py +727 -0
  986. chrys/service/profiles/agents/schema.py +371 -0
  987. chrys/service/profiles/agents/serializer.py +196 -0
  988. chrys/service/profiles/coercion.py +22 -0
  989. chrys/service/profiles/models/__init__.py +3 -0
  990. chrys/service/profiles/models/env_bridge.py +157 -0
  991. chrys/service/profiles/models/loader.py +282 -0
  992. chrys/service/profiles/models/options.py +438 -0
  993. chrys/service/profiles/models/registry.py +104 -0
  994. chrys/service/profiles/models/resolver.py +286 -0
  995. chrys/service/profiles/models/schema.py +91 -0
  996. chrys/service/profiles/models/serializer.py +76 -0
  997. chrys/service/session/__init__.py +3 -0
  998. chrys/service/session/checkpoint.py +156 -0
  999. chrys/service/session/history.py +1616 -0
  1000. chrys/service/session/message_metadata.py +151 -0
  1001. chrys/service/session/persistence.py +435 -0
  1002. chrys/service/session/runtime_metadata.py +210 -0
  1003. chrys/service/session/sub_agent_logs.py +1069 -0
  1004. chrys/service/session/sub_agent_transcript.py +654 -0
  1005. chrys/service/skills/__init__.py +3 -0
  1006. chrys/service/skills/adapter.py +408 -0
  1007. chrys/service/skills/constants.py +8 -0
  1008. chrys/service/skills/loader.py +575 -0
  1009. chrys/service/skills/model.py +130 -0
  1010. chrys/service/skills/provider.py +841 -0
  1011. chrys/service/skills/runner.py +390 -0
  1012. chrys/service/state/__init__.py +3 -0
  1013. chrys/service/state/_fork.py +710 -0
  1014. chrys/service/state/_session_files.py +175 -0
  1015. chrys/service/state/_session_meta.py +552 -0
  1016. chrys/service/state/locks.py +154 -0
  1017. chrys/service/state/serializers.py +156 -0
  1018. chrys/service/state/session_catalog.py +361 -0
  1019. chrys/service/state/session_listing.py +84 -0
  1020. chrys/service/state/session_migration.py +446 -0
  1021. chrys/service/state/session_mru.py +315 -0
  1022. chrys/service/state/store.py +2132 -0
  1023. chrys/service/state/workflow.py +158 -0
  1024. chrys/service/text_blocks.py +47 -0
  1025. chrys/service/todos/__init__.py +3 -0
  1026. chrys/service/todos/reminder.py +31 -0
  1027. chrys/service/todos/tracker.py +58 -0
  1028. chrys/service/tools/__init__.py +3 -0
  1029. chrys/service/tools/approval_targets.py +92 -0
  1030. chrys/service/tools/builtins/__init__.py +3 -0
  1031. chrys/service/tools/builtins/ask_user.py +326 -0
  1032. chrys/service/tools/builtins/doc_converter/__init__.py +494 -0
  1033. chrys/service/tools/builtins/doc_converter/artifacts.py +392 -0
  1034. chrys/service/tools/builtins/doc_converter/parsers/__init__.py +3 -0
  1035. chrys/service/tools/builtins/doc_converter/parsers/_table.py +34 -0
  1036. chrys/service/tools/builtins/doc_converter/parsers/base.py +101 -0
  1037. chrys/service/tools/builtins/doc_converter/parsers/docx.py +88 -0
  1038. chrys/service/tools/builtins/doc_converter/parsers/pdf.py +144 -0
  1039. chrys/service/tools/builtins/doc_converter/parsers/pptx.py +103 -0
  1040. chrys/service/tools/builtins/doc_converter/parsers/xls.py +32 -0
  1041. chrys/service/tools/builtins/doc_converter/parsers/xlsx.py +51 -0
  1042. chrys/service/tools/builtins/doc_converter/registry.py +55 -0
  1043. chrys/service/tools/builtins/filesystem.py +1071 -0
  1044. chrys/service/tools/builtins/search.py +1265 -0
  1045. chrys/service/tools/builtins/shell.py +967 -0
  1046. chrys/service/tools/builtins/shell_command_filtering/__init__.py +5 -0
  1047. chrys/service/tools/builtins/shell_command_filtering/policy.py +60 -0
  1048. chrys/service/tools/builtins/shell_command_filtering/presets.py +152 -0
  1049. chrys/service/tools/builtins/shell_command_filtering/readonly/__init__.py +358 -0
  1050. chrys/service/tools/builtins/shell_command_filtering/readonly/commands.py +416 -0
  1051. chrys/service/tools/builtins/shell_command_filtering/readonly/git.py +408 -0
  1052. chrys/service/tools/builtins/shell_command_filtering/readonly/syntax.py +87 -0
  1053. chrys/service/tools/builtins/shell_command_filtering/tokenizer.py +214 -0
  1054. chrys/service/tools/builtins/shell_filter.py +24 -0
  1055. chrys/service/tools/builtins/sleep.py +91 -0
  1056. chrys/service/tools/builtins/todo.py +64 -0
  1057. chrys/service/tools/builtins/web/__init__.py +6 -0
  1058. chrys/service/tools/builtins/web/build.py +268 -0
  1059. chrys/service/tools/builtins/web/config.py +338 -0
  1060. chrys/service/tools/builtins/web/credentials.py +37 -0
  1061. chrys/service/tools/builtins/web/fetch/__init__.py +3 -0
  1062. chrys/service/tools/builtins/web/fetch/cache.py +68 -0
  1063. chrys/service/tools/builtins/web/fetch/convert.py +56 -0
  1064. chrys/service/tools/builtins/web/fetch/tool.py +223 -0
  1065. chrys/service/tools/builtins/web/hosted.py +61 -0
  1066. chrys/service/tools/builtins/web/http.py +330 -0
  1067. chrys/service/tools/builtins/web/progress.py +18 -0
  1068. chrys/service/tools/builtins/web/search/__init__.py +3 -0
  1069. chrys/service/tools/builtins/web/search/exa_mcp.py +146 -0
  1070. chrys/service/tools/builtins/web/search/html.py +152 -0
  1071. chrys/service/tools/builtins/web/search/json_api.py +227 -0
  1072. chrys/service/tools/builtins/web/search/providers.py +77 -0
  1073. chrys/service/tools/builtins/web/search/tool.py +218 -0
  1074. chrys/service/tools/builtins/web/search/types.py +28 -0
  1075. chrys/service/tools/builtins/web/tls_identity.py +66 -0
  1076. chrys/service/tools/kinds.py +115 -0
  1077. chrys/service/tools/names.py +47 -0
  1078. chrys/service/tools/registry.py +242 -0
  1079. chrys/service/tools/result_metadata.py +103 -0
  1080. chrys/service/tools/session_artifacts.py +196 -0
  1081. chrys/service/tools/spill.py +218 -0
  1082. chrys/service/tools/workspace_paths.py +81 -0
  1083. chrys/service/trajectory/__init__.py +11 -0
  1084. chrys/service/trajectory/approvals.py +201 -0
  1085. chrys/service/trajectory/compaction.py +420 -0
  1086. chrys/service/trajectory/fork.py +75 -0
  1087. chrys/service/trajectory/hooks.py +217 -0
  1088. chrys/service/trajectory/items.py +41 -0
  1089. chrys/service/trajectory/preparation.py +374 -0
  1090. chrys/service/trajectory/retries.py +95 -0
  1091. chrys/service/trajectory/revisions.py +129 -0
  1092. chrys/service/trajectory/segmented.py +163 -0
  1093. chrys/service/trajectory/session.py +762 -0
  1094. chrys/service/trajectory/state.py +83 -0
  1095. chrys/service/trajectory/sub_agents.py +169 -0
  1096. chrys/service/trajectory/tombstone.py +262 -0
  1097. chrys/service/trajectory/tools.py +323 -0
  1098. chrys/service/trajectory/validation.py +130 -0
  1099. chrys/service/trajectory/waits.py +161 -0
  1100. chrys/service/trajectory/workflow.py +148 -0
  1101. chrys/service/vision.py +128 -0
  1102. chrys/service/workflows/__init__.py +3 -0
  1103. chrys/service/workflows/admission.py +151 -0
  1104. chrys/service/workflows/artifacts.py +109 -0
  1105. chrys/service/workflows/asks.py +130 -0
  1106. chrys/service/workflows/builtins/__init__.py +12 -0
  1107. chrys/service/workflows/builtins/demo-workflow.manifest.json +635 -0
  1108. chrys/service/workflows/builtins/demo-workflow.py +756 -0
  1109. chrys/service/workflows/discovery.py +554 -0
  1110. chrys/service/workflows/environment.py +284 -0
  1111. chrys/service/workflows/graph.py +430 -0
  1112. chrys/service/workflows/history.py +82 -0
  1113. chrys/service/workflows/interpreter.py +107 -0
  1114. chrys/service/workflows/journal.py +334 -0
  1115. chrys/service/workflows/layout.py +56 -0
  1116. chrys/service/workflows/ledger.py +149 -0
  1117. chrys/service/workflows/model_selection.py +18 -0
  1118. chrys/service/workflows/orphans.py +209 -0
  1119. chrys/service/workflows/outcomes.py +46 -0
  1120. chrys/service/workflows/protocol.py +210 -0
  1121. chrys/service/workflows/records.py +142 -0
  1122. chrys/service/workflows/scheduler.py +770 -0
  1123. chrys/service/workflows/sdk/__init__.py +51 -0
  1124. chrys/service/workflows/sdk/_ask.py +167 -0
  1125. chrys/service/workflows/sdk/_builder.py +781 -0
  1126. chrys/service/workflows/sdk/_values.py +92 -0
  1127. chrys/service/workflows/sdk_artifact.py +74 -0
  1128. chrys/service/workflows/store.py +494 -0
  1129. chrys/service/workflows/transcript.py +102 -0
  1130. chrys/service/workflows/values.py +73 -0
  1131. chrys/service/workflows/worker_host.py +1820 -0
  1132. chrys/workflows.py +38 -0
  1133. icode_tui-0.29.1.dist-info/METADATA +104 -0
  1134. icode_tui-0.29.1.dist-info/RECORD +1138 -0
  1135. icode_tui-0.29.1.dist-info/WHEEL +4 -0
  1136. icode_tui-0.29.1.dist-info/entry_points.txt +2 -0
  1137. icode_tui-0.29.1.dist-info/licenses/LICENSE +202 -0
  1138. icode_tui-0.29.1.dist-info/licenses/NOTICE +1955 -0
@@ -0,0 +1,963 @@
1
+ # Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
2
+
3
+ """The terminal itself: what each control does to the screen, and what the terminal says back.
4
+
5
+ `TerminalEmulator` is a display-less VT220-class terminal with the xterm extensions programs rely
6
+ on today. Feed it a program's output and it keeps two screens of styled cells current; read the
7
+ active `ScreenBuffer` to draw them. It owns no I/O: replies the protocol owes the program come back
8
+ from `feed` for the caller to deliver.
9
+
10
+ Sequences are named here as the standards name them (ECMA-48, the DEC VT510 manual, xterm's
11
+ ctlseqs); each handler states the mnemonic it implements.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import base64
17
+ import binascii
18
+ import re
19
+ import socket
20
+ from collections.abc import Callable, Sequence
21
+ from dataclasses import dataclass
22
+ from enum import Enum
23
+ from functools import lru_cache
24
+ from typing import Final, NamedTuple
25
+ from urllib.parse import unquote, urlsplit
26
+
27
+ from rich.cells import cell_len, get_character_cell_size
28
+
29
+ from chrys.app.tui.terminal.emulator.buffer import ScreenBuffer
30
+ from chrys.app.tui.terminal.emulator.charsets import Charsets, CharsetSnapshot
31
+ from chrys.app.tui.terminal.emulator.parser import SequenceParser
32
+ from chrys.app.tui.terminal.emulator.pen import DEFAULT_PEN, Pen, apply_sgr
33
+
34
+ DEFAULT_HISTORY_LIMIT: Final = 10_000
35
+
36
+ _ZERO_WIDTH_JOINER: Final = "\u200d"
37
+ _TAB_INTERVAL: Final = 8
38
+ _MAX_PARAMETER: Final = 0xFFFF
39
+
40
+ # DEC private modes that are plain switches. The alternate screen and the mouse modes are not:
41
+ # several numbers drive one piece of state, so they are handled by name below.
42
+ _APPLICATION_CURSOR: Final = 1
43
+ _ORIGIN: Final = 6
44
+ _AUTOWRAP: Final = 7
45
+ _CURSOR_BLINK: Final = 12
46
+ _CURSOR_VISIBLE: Final = 25
47
+ _APPLICATION_KEYPAD: Final = 66
48
+ _FOCUS_REPORTING: Final = 1004
49
+ _ALTERNATE_SCROLL: Final = 1007
50
+ _BRACKETED_PASTE: Final = 2004
51
+ _SWITCHES: Final = frozenset(
52
+ {
53
+ _APPLICATION_CURSOR,
54
+ _ORIGIN,
55
+ _AUTOWRAP,
56
+ _CURSOR_BLINK,
57
+ _CURSOR_VISIBLE,
58
+ _APPLICATION_KEYPAD,
59
+ _FOCUS_REPORTING,
60
+ _ALTERNATE_SCROLL,
61
+ _BRACKETED_PASTE,
62
+ }
63
+ )
64
+ # xterm leaves alternate scroll off, but every terminal people use today turns the wheel into
65
+ # arrow keys on the alternate screen unless told not to, and pagers are read with that in hand.
66
+ _SWITCHES_ON_AT_RESET: Final = frozenset({_AUTOWRAP, _CURSOR_VISIBLE, _ALTERNATE_SCROLL})
67
+
68
+ _INSERT: Final = 4
69
+ _NEWLINE: Final = 20
70
+
71
+ # 47 and 1047 only switch screens; 1049 also saves the cursor going in and restores it coming out.
72
+ _ALTERNATE_SCREENS: Final = frozenset({47, 1047, 1049})
73
+ _ALTERNATE_SCREEN_SAVING_CURSOR: Final = 1049
74
+
75
+ # Of the kitty keyboard protocol's progressive enhancements only "disambiguate escape codes" is
76
+ # offered. A program reads back which flags took, so asking for more degrades cleanly.
77
+ _KITTY_DISAMBIGUATE: Final = 1
78
+ _KITTY_STACK_LIMIT: Final = 16
79
+ _XTERM_MODIFY_OTHER_KEYS: Final = 4
80
+
81
+ _DRIVE_PATH = re.compile(r"/[A-Za-z]:(/|$)")
82
+
83
+
84
+ class MouseTracking(Enum):
85
+ """Which pointer events the program asked to hear about. Values are the DEC private modes."""
86
+
87
+ OFF = 0
88
+ PRESS = 9
89
+ PRESS_RELEASE = 1000
90
+ DRAG = 1002
91
+ MOTION = 1003
92
+
93
+
94
+ class MouseEncoding(Enum):
95
+ """How pointer reports are spelled. Values are the DEC private modes."""
96
+
97
+ DEFAULT = 0
98
+ UTF8 = 1005
99
+ SGR = 1006
100
+ URXVT = 1015
101
+
102
+
103
+ class KeyProtocol(Enum):
104
+ """How keys the classic encoding cannot tell apart are spelled, once a program asks."""
105
+
106
+ LEGACY = "legacy"
107
+ MODIFY_OTHER_KEYS_1 = "modify_other_keys_1"
108
+ """xterm's ``CSI 27 ; modifiers ; code ~``, switched on with XTMODKEYS, for the keys that have
109
+ no classic spelling of their own. A key with a well-known one keeps it, less a modifier or not."""
110
+ MODIFY_OTHER_KEYS_2 = "modify_other_keys_2"
111
+ """The same spelling for every modified key, those with a well-known one included."""
112
+ KITTY = "kitty"
113
+ """The kitty keyboard protocol's ``CSI code ; modifiers u``."""
114
+
115
+
116
+ class CursorShape(Enum):
117
+ BLOCK = "block"
118
+ UNDERLINE = "underline"
119
+ BAR = "bar"
120
+
121
+
122
+ # By XTMODKEYS level. Level 3 also spells out keys held with nothing, which no program here has been
123
+ # seen to want; it is answered with the most there is.
124
+ _MODIFY_OTHER_KEYS_LEVELS: Final = (
125
+ KeyProtocol.LEGACY,
126
+ KeyProtocol.MODIFY_OTHER_KEYS_1,
127
+ KeyProtocol.MODIFY_OTHER_KEYS_2,
128
+ )
129
+ _TRACKING_MODES: Final = {tracking.value: tracking for tracking in MouseTracking if tracking.value}
130
+ _ENCODING_MODES: Final = {encoding.value: encoding for encoding in MouseEncoding if encoding.value}
131
+ _CURSOR_SHAPES: Final = (
132
+ CursorShape.BLOCK,
133
+ CursorShape.BLOCK,
134
+ CursorShape.BLOCK,
135
+ CursorShape.UNDERLINE,
136
+ CursorShape.UNDERLINE,
137
+ CursorShape.BAR,
138
+ CursorShape.BAR,
139
+ )
140
+ """By DECSCUSR parameter. Each shape comes blinking and steady, and zero is the default."""
141
+ _STEADY_CURSOR_PARAMETERS: Final = {CursorShape.BLOCK: 2, CursorShape.UNDERLINE: 4, CursorShape.BAR: 6}
142
+ """What DECRQSS reports: the cursor is drawn without blinking, whichever variant was asked for."""
143
+
144
+
145
+ @dataclass(frozen=True, slots=True)
146
+ class DirectoryChanged:
147
+ """The shell reported its working directory."""
148
+
149
+ path: str
150
+
151
+
152
+ @dataclass(frozen=True, slots=True)
153
+ class CommandSubmitted:
154
+ """The shell reported the command line it is about to run."""
155
+
156
+ command: str
157
+
158
+
159
+ type TerminalEvent = DirectoryChanged | CommandSubmitted
160
+
161
+
162
+ @dataclass(frozen=True, slots=True)
163
+ class Update:
164
+ """What one `TerminalEmulator.feed` changed."""
165
+
166
+ damaged: set[int] | None
167
+ """Rows of the active buffer to redraw, as indices into its ``rows``; ``None`` means all."""
168
+ trimmed: int
169
+ """History rows dropped from the front of the active buffer: every later row moved up by this."""
170
+ replies: str
171
+ """Bytes the protocol owes the program (status reports and the like), to write to its input."""
172
+ events: tuple[TerminalEvent, ...]
173
+
174
+
175
+ class _SavedCursor(NamedTuple):
176
+ x: int
177
+ y: int
178
+ pending_wrap: bool
179
+ pen: Pen
180
+ charsets: CharsetSnapshot
181
+ origin: bool
182
+
183
+
184
+ @lru_cache(maxsize=1024)
185
+ def _parse_parameters(parameters: str) -> tuple[int, ...]:
186
+ """Numeric parameters, omitted ones as zero. Sub-parameters mean nothing outside SGR."""
187
+ return tuple(min(int(field.partition(":")[0] or 0), _MAX_PARAMETER) for field in parameters.split(";"))
188
+
189
+
190
+ def _awaits_joined_character(cluster: str) -> bool:
191
+ """Whether the cluster ends in a zero-width joiner that has not had its character yet.
192
+
193
+ A joiner takes the one character after it, whatever that is, and a joiner taken that way is
194
+ spent: it joins nothing itself. That is how `rich.cells` measures, nonsense included.
195
+ """
196
+ return (len(cluster) - len(cluster.rstrip(_ZERO_WIDTH_JOINER))) % 2 == 1
197
+
198
+
199
+ def _decode_payload(payload: str) -> str | None:
200
+ """The text of a shell-integration report, which may travel as ``b64:`` to survive any content."""
201
+ if not payload.startswith("b64:"):
202
+ return payload
203
+ try:
204
+ return base64.b64decode(payload[4:], validate=True).decode("utf-8", "replace")
205
+ except binascii.Error, ValueError:
206
+ return None
207
+
208
+
209
+ class TerminalEmulator:
210
+ """A terminal without a display."""
211
+
212
+ def __init__(self, columns: int = 80, lines: int = 24, *, history_limit: int = DEFAULT_HISTORY_LIMIT) -> None:
213
+ self.columns = columns
214
+ self.lines = lines
215
+ self._history_limit = history_limit
216
+ self._parser = SequenceParser(self)
217
+ self._replies: list[str] = []
218
+ self._events: list[TerminalEvent] = []
219
+ self._history_dropped = 0
220
+ """History rows the program threw away (ED 3, RIS) that no `Update` has reported yet."""
221
+ self._control_sequences = self._control_sequence_table()
222
+ self._escapes = self._escape_table()
223
+ self._commands = self._operating_system_command_table()
224
+ self._reset()
225
+
226
+ def _reset(self) -> None:
227
+ self._primary = ScreenBuffer(self.columns, self.lines, history_limit=self._history_limit)
228
+ self._alternate = ScreenBuffer(self.columns, self.lines, history_limit=0)
229
+ self.buffer = self._primary
230
+ self.pen = DEFAULT_PEN
231
+ self.cursor_shape = CursorShape.BLOCK
232
+ self.mouse_tracking = MouseTracking.OFF
233
+ self.mouse_encoding = MouseEncoding.DEFAULT
234
+ self._charsets = Charsets()
235
+ self._switches = set(_SWITCHES_ON_AT_RESET)
236
+ self._insert = False
237
+ self._newline = False
238
+ self._tab_stops = set(range(_TAB_INTERVAL, self.columns, _TAB_INTERVAL))
239
+ self._saved_cursors: dict[bool, _SavedCursor] = {}
240
+ self._modify_other_keys = 0
241
+ # One stack per screen, so a full-screen program that dies without popping its flags
242
+ # does not leave the shell behind it reading keys it cannot parse.
243
+ self._kitty_flags: dict[bool, list[int]] = {False: [0], True: [0]}
244
+ self._last_printed = ""
245
+ self._anchor_pending = False
246
+
247
+ # -- state a front end reads -------------------------------------------------------------
248
+
249
+ @property
250
+ def alternate_screen(self) -> bool:
251
+ return self.buffer is self._alternate
252
+
253
+ @property
254
+ def cursor_visible(self) -> bool:
255
+ return _CURSOR_VISIBLE in self._switches
256
+
257
+ @property
258
+ def application_cursor_keys(self) -> bool:
259
+ return _APPLICATION_CURSOR in self._switches
260
+
261
+ @property
262
+ def bracketed_paste(self) -> bool:
263
+ return _BRACKETED_PASTE in self._switches
264
+
265
+ @property
266
+ def focus_reporting(self) -> bool:
267
+ return _FOCUS_REPORTING in self._switches
268
+
269
+ @property
270
+ def alternate_scroll(self) -> bool:
271
+ return _ALTERNATE_SCROLL in self._switches
272
+
273
+ @property
274
+ def key_protocol(self) -> KeyProtocol:
275
+ if self._kitty_flags[self.alternate_screen][-1] & _KITTY_DISAMBIGUATE:
276
+ return KeyProtocol.KITTY
277
+ return _MODIFY_OTHER_KEYS_LEVELS[min(self._modify_other_keys, len(_MODIFY_OTHER_KEYS_LEVELS) - 1)]
278
+
279
+ # -- driving -----------------------------------------------------------------------------
280
+
281
+ def feed(self, text: str) -> Update:
282
+ """Interpret the next piece of the program's output."""
283
+ buffer = self.buffer
284
+ cursor_before = (buffer.cursor_index, buffer.cursor.x, self.cursor_visible, self.cursor_shape)
285
+ self._parser.feed(text)
286
+ trimmed = 0
287
+ if self.buffer is self._primary:
288
+ # What the limit takes now and what the program threw away itself. Both wait for the
289
+ # primary screen to be the one on show: it is its rows that a front end numbers.
290
+ trimmed = self._history_dropped + self._primary.trim_history()
291
+ self._history_dropped = 0
292
+ if self.buffer is buffer:
293
+ cursor_after = (buffer.cursor_index, buffer.cursor.x, self.cursor_visible, self.cursor_shape)
294
+ if cursor_after != cursor_before:
295
+ # The cursor is drawn with its row, so moving it dirties the row it left too.
296
+ buffer.damage(cursor_before[0])
297
+ buffer.damage(cursor_after[0])
298
+ update = Update(self.buffer.take_damage(), trimmed, "".join(self._replies), tuple(self._events))
299
+ self._replies.clear()
300
+ self._events.clear()
301
+ return update
302
+
303
+ def resize(self, columns: int, lines: int, *, reflow: bool = True, host_repaints: bool = False) -> None:
304
+ """Change the screen size.
305
+
306
+ Args:
307
+ columns: New width.
308
+ lines: New height.
309
+ reflow: Rewrap the primary screen and its history to the new width.
310
+ host_repaints: Whoever feeds us keeps a screen of its own, reflows that, and repaints
311
+ ours with absolute positions afterwards (Windows ConPTY does). The screen origin
312
+ then stays where that host believes it is, and the first position it sends is
313
+ taken as the truth about which row the cursor is on.
314
+ """
315
+ self._tab_stops.update(range(-(-self.columns // _TAB_INTERVAL) * _TAB_INTERVAL, columns, _TAB_INTERVAL))
316
+ self.columns, self.lines = columns, lines
317
+ primary = self._primary
318
+ cursor = primary.cursor
319
+ saved = self._saved_cursors.get(False)
320
+ if saved is not None and (saved.x, saved.y) != (cursor.x, cursor.y):
321
+ saved = None
322
+ primary.resize(columns, lines, reflow=reflow, pull_history=not host_repaints)
323
+ if saved is not None:
324
+ # Saved where the cursor is, as entering the alternate screen saves it: it goes where
325
+ # the cursor went, or leaving that screen would put the shell back on the wrong row.
326
+ self._saved_cursors[False] = saved._replace(x=cursor.x, y=cursor.y, pending_wrap=cursor.pending_wrap)
327
+ self._alternate.resize(columns, lines, reflow=False, pull_history=False)
328
+ if host_repaints:
329
+ if reflow and primary.used_height <= lines:
330
+ # Everything fits on one screen, which is how the host lays it out too.
331
+ primary.anchor_cursor_row(primary.cursor_index)
332
+ self._anchor_pending = not self.alternate_screen
333
+
334
+ # -- SequenceHandler ---------------------------------------------------------------------
335
+
336
+ def print(self, text: str) -> None:
337
+ text = self._charsets.translate(text)
338
+ self._anchor_pending = False
339
+ if text.isascii() and not _awaits_joined_character(self._cell_behind_cursor()[1]):
340
+ self._last_printed = text[-1]
341
+ self._write(text)
342
+ elif cells := self._cells(text):
343
+ self._last_printed = next(cell for cell in reversed(cells) if cell)
344
+ self._write(cells)
345
+
346
+ def execute(self, control: str) -> None:
347
+ cursor = self.buffer.cursor
348
+ if control == "\n" or control in "\v\f":
349
+ self._line_feed()
350
+ elif control == "\r":
351
+ cursor.x = 0
352
+ cursor.pending_wrap = False
353
+ elif control == "\b":
354
+ cursor.x = max(cursor.x - 1, 0)
355
+ cursor.pending_wrap = False
356
+ elif control == "\t":
357
+ self._tab_forward(1)
358
+ elif control == "\x0e":
359
+ self._charsets.lock(1)
360
+ elif control == "\x0f":
361
+ self._charsets.lock(0)
362
+ # BEL and the rest have no effect here: the bell belongs to the outer terminal, and
363
+ # ringing it for an embedded program would badge the whole application.
364
+
365
+ def escape(self, intermediates: str, final: str) -> None:
366
+ if intermediates and intermediates[0] in "()*+":
367
+ self._charsets.designate("()*+".index(intermediates[0]), intermediates[1:] + final)
368
+ elif (handler := self._escapes.get(intermediates + final)) is not None:
369
+ handler()
370
+
371
+ def control_sequence(self, marker: str, parameters: str, intermediates: str, final: str) -> None:
372
+ if final == "m" and not marker and not intermediates:
373
+ self.pen = apply_sgr(self.pen, parameters)
374
+ elif (handler := self._control_sequences.get(marker + intermediates + final)) is not None:
375
+ handler(_parse_parameters(parameters))
376
+
377
+ def operating_system_command(self, payload: str) -> None:
378
+ command, _, argument = payload.partition(";")
379
+ if (handler := self._commands.get(command)) is not None:
380
+ handler(argument)
381
+
382
+ def device_control(self, payload: str) -> None:
383
+ """DECRQSS, the one device control string programs send to probe a terminal."""
384
+ if not payload.startswith("$q"):
385
+ return
386
+ setting = payload[2:]
387
+ if setting == "r":
388
+ report = f"{self.buffer.margin_top + 1};{self.buffer.margin_bottom + 1}r"
389
+ elif setting == " q":
390
+ report = f"{_STEADY_CURSOR_PARAMETERS[self.cursor_shape]} q"
391
+ else:
392
+ self._replies.append("\x1bP0$r\x1b\\")
393
+ return
394
+ self._replies.append(f"\x1bP1$r{report}\x1b\\")
395
+
396
+ # -- printing ----------------------------------------------------------------------------
397
+
398
+ def _cells(self, text: str) -> list[str]:
399
+ """Split text into cells the way the renderer will measure it.
400
+
401
+ A zero-width character joins the cell before it, a character after a zero-width joiner
402
+ joins too, and a variation selector may widen its base: exactly the rules `rich.cells`
403
+ measures by, so a row's cell count always equals the rendered width of its text.
404
+ """
405
+ cells: list[str] = []
406
+ for character in text:
407
+ base = len(cells) - 1 if cells and cells[-1] else len(cells) - 2
408
+ if base < 0:
409
+ if self._joins_cell_behind_cursor(character):
410
+ continue
411
+ width = get_character_cell_size(character)
412
+ elif _awaits_joined_character(cells[base]):
413
+ cells[base] += character
414
+ continue
415
+ elif not (width := get_character_cell_size(character)):
416
+ cells[base] += character
417
+ if base == len(cells) - 1 and cell_len(cells[base]) == 2:
418
+ cells.append("")
419
+ continue
420
+ cells.append(character)
421
+ if width == 2:
422
+ cells.append("")
423
+ return cells
424
+
425
+ def _joins_cell_behind_cursor(self, character: str) -> bool:
426
+ """Whether a character opening a run belongs to the cell already written before the cursor.
427
+
428
+ That is the case for a zero-width character, and for anything following a zero-width
429
+ joiner, when the two halves of a sequence arrive in separate runs.
430
+ """
431
+ column, behind = self._cell_behind_cursor()
432
+ if get_character_cell_size(character) and not _awaits_joined_character(behind):
433
+ return False
434
+ if behind:
435
+ # The cluster is what was printed last now, and what REP repeats.
436
+ self._last_printed = behind + character
437
+ self._rewrite_cell(column, self._last_printed)
438
+ return True
439
+
440
+ def _cell_behind_cursor(self) -> tuple[int, str]:
441
+ """The column and the text of the cell before the cursor, ``""`` where there is none.
442
+
443
+ With the last column just written the cursor is past it in all but position, so that
444
+ column is the one behind it, whether or not the next character is going to wrap.
445
+ """
446
+ cursor = self.buffer.cursor
447
+ cells = self.buffer.row(cursor.y).cells
448
+ column = cursor.x if cursor.pending_wrap else cursor.x - 1
449
+ if 0 < column < len(cells) and not cells[column]:
450
+ column -= 1
451
+ return column, cells[column] if 0 <= column < len(cells) else ""
452
+
453
+ def _rewrite_cell(self, column: int, cluster: str) -> None:
454
+ """Put ``cluster`` in the cursor's row, where the cell at ``column`` holds its beginning.
455
+
456
+ A variation selector makes a narrow character wide. What is left then is what printing
457
+ the cluster in one piece leaves: a program's output is cut wherever a read happens to
458
+ end, and that must not show on the screen.
459
+ """
460
+ columns = self.columns
461
+ cursor = self.buffer.cursor
462
+ row = self.buffer.edit(cursor.y)
463
+ pen = row.pens[column]
464
+ width = cell_len(cluster)
465
+ if width == cell_len(row.cells[column]):
466
+ # Rewritten whole, second cell and all: half a double-width cell would be blanked.
467
+ row.put(column, [cluster, *[""] * (width - 1)], pen)
468
+ elif cursor.pending_wrap:
469
+ # The last column has no room for it: it is printed again whole, to land where it
470
+ # would have, on the next row or, without autowrap, over the last two columns.
471
+ row.erase(column, columns, DEFAULT_PEN, columns)
472
+ self._write([cluster, ""], pen)
473
+ else:
474
+ if self._insert:
475
+ row.insert(cursor.x, [""], pen, columns)
476
+ row.put(column, [cluster, ""], pen)
477
+ cursor.x += 1
478
+ if cursor.x >= columns:
479
+ cursor.x = columns - 1
480
+ cursor.pending_wrap = True
481
+
482
+ def _write(self, cells: Sequence[str], pen: Pen | None = None) -> None:
483
+ """Print cells at the cursor, in the current pen unless another is given."""
484
+ buffer, columns = self.buffer, self.columns
485
+ if pen is None:
486
+ pen = self.pen
487
+ cursor = buffer.cursor
488
+ autowrap = _AUTOWRAP in self._switches
489
+ position, total = 0, len(cells)
490
+ while position < total:
491
+ if cursor.pending_wrap:
492
+ if autowrap:
493
+ self._wrap()
494
+ else:
495
+ # Nowhere to go: the last column is written over.
496
+ cursor.pending_wrap = False
497
+ end = min(position + columns - cursor.x, total)
498
+ if end < total and not cells[end]:
499
+ # A double-width character does not fit in what is left of the row.
500
+ end -= 1
501
+ if end == position:
502
+ if not cursor.x:
503
+ # A screen one column wide has no room for it anywhere.
504
+ position += 2
505
+ elif autowrap:
506
+ self._wrap()
507
+ else:
508
+ # Written over the last two columns, as a narrow one is over the last.
509
+ cursor.x -= 1
510
+ continue
511
+ row = buffer.edit(cursor.y)
512
+ if self._insert:
513
+ row.insert(cursor.x, cells[position:end], pen, columns)
514
+ else:
515
+ row.put(cursor.x, cells[position:end], pen)
516
+ cursor.x += end - position
517
+ position = end
518
+ if cursor.x >= columns:
519
+ # Whether that is a wrap is for the next character to find out: until then it only
520
+ # says which cell was printed last, which a character completing it has to know.
521
+ cursor.x = columns - 1
522
+ cursor.pending_wrap = True
523
+
524
+ def _wrap(self) -> None:
525
+ buffer = self.buffer
526
+ cursor = buffer.cursor
527
+ row = buffer.row(cursor.y)
528
+ cursor.x = 0
529
+ buffer.index(self.pen.eraser)
530
+ # Flagged only once the cursor has moved on. Scrolling, which may be how it moved, unflags
531
+ # the row it leaves behind; and below the margins on the last row it does not move at all.
532
+ row.wrapped = buffer.row(cursor.y) is not row
533
+
534
+ def _line_feed(self) -> None:
535
+ self.buffer.index(self.pen.eraser)
536
+ if self._newline:
537
+ self.buffer.cursor.x = 0
538
+
539
+ # -- cursor ------------------------------------------------------------------------------
540
+
541
+ def _move_to(self, x: int, y: int) -> None:
542
+ """Absolute positioning. Under origin mode rows count from, and stay inside, the margins."""
543
+ buffer = self.buffer
544
+ if _ORIGIN in self._switches:
545
+ y = min(buffer.margin_top + max(y, 0), buffer.margin_bottom)
546
+ buffer.cursor.y = min(max(y, 0), self.lines - 1)
547
+ buffer.cursor.x = min(max(x, 0), self.columns - 1)
548
+ buffer.cursor.pending_wrap = False
549
+
550
+ def _move_by(self, columns: int, rows: int) -> None:
551
+ """Relative movement, which stops at a margin the cursor starts inside of."""
552
+ buffer = self.buffer
553
+ cursor = buffer.cursor
554
+ if rows < 0:
555
+ limit = buffer.margin_top if cursor.y >= buffer.margin_top else 0
556
+ cursor.y = max(cursor.y + rows, limit)
557
+ elif rows:
558
+ limit = buffer.margin_bottom if cursor.y <= buffer.margin_bottom else self.lines - 1
559
+ cursor.y = min(cursor.y + rows, limit)
560
+ cursor.x = min(max(cursor.x + columns, 0), self.columns - 1)
561
+ cursor.pending_wrap = False
562
+
563
+ def _row_in_origin(self) -> int:
564
+ """The cursor row as reports and absolute positioning count it."""
565
+ buffer = self.buffer
566
+ return buffer.cursor.y - (buffer.margin_top if _ORIGIN in self._switches else 0)
567
+
568
+ def _tab_forward(self, count: int) -> None:
569
+ cursor = self.buffer.cursor
570
+ for _ in range(count):
571
+ cursor.x = min((stop for stop in self._tab_stops if stop > cursor.x), default=self.columns - 1)
572
+ cursor.x = min(cursor.x, self.columns - 1)
573
+
574
+ def _tab_backward(self, count: int) -> None:
575
+ cursor = self.buffer.cursor
576
+ for _ in range(count):
577
+ cursor.x = max((stop for stop in self._tab_stops if stop < cursor.x), default=0)
578
+ cursor.pending_wrap = False
579
+
580
+ def _save_cursor(self) -> None:
581
+ cursor = self.buffer.cursor
582
+ self._saved_cursors[self.alternate_screen] = _SavedCursor(
583
+ cursor.x, cursor.y, cursor.pending_wrap, self.pen, self._charsets.snapshot(), _ORIGIN in self._switches
584
+ )
585
+
586
+ def _restore_cursor(self) -> None:
587
+ saved = self._saved_cursors.get(self.alternate_screen)
588
+ if saved is None:
589
+ # Nothing saved: DEC specifies the power-up state, home with default rendition.
590
+ self._switches.discard(_ORIGIN)
591
+ self._move_to(0, 0)
592
+ self.pen = self.pen._replace(foreground=None, background=None, attributes=DEFAULT_PEN.attributes)
593
+ return
594
+ cursor = self.buffer.cursor
595
+ cursor.x = min(saved.x, self.columns - 1)
596
+ cursor.y = min(saved.y, self.lines - 1)
597
+ cursor.pending_wrap = saved.pending_wrap and cursor.x == self.columns - 1
598
+ self.pen = saved.pen._replace(link=self.pen.link)
599
+ self._charsets.restore(saved.charsets)
600
+ self._set_switch(_ORIGIN, saved.origin)
601
+
602
+ # -- screens and modes -------------------------------------------------------------------
603
+
604
+ def _set_switch(self, mode: int, enabled: bool) -> None:
605
+ if enabled:
606
+ self._switches.add(mode)
607
+ else:
608
+ self._switches.discard(mode)
609
+
610
+ def _set_private_mode(self, mode: int, enabled: bool) -> None:
611
+ if mode in _SWITCHES:
612
+ self._set_switch(mode, enabled)
613
+ if mode == _ORIGIN:
614
+ self._move_to(0, 0)
615
+ elif mode in _ALTERNATE_SCREENS:
616
+ self._show_alternate_screen(enabled, save_cursor=mode == _ALTERNATE_SCREEN_SAVING_CURSOR)
617
+ elif (tracking := _TRACKING_MODES.get(mode)) is not None:
618
+ if enabled or self.mouse_tracking is tracking:
619
+ self.mouse_tracking = tracking if enabled else MouseTracking.OFF
620
+ elif (encoding := _ENCODING_MODES.get(mode)) is not None:
621
+ if enabled or self.mouse_encoding is encoding:
622
+ self.mouse_encoding = encoding if enabled else MouseEncoding.DEFAULT
623
+
624
+ def _private_mode_status(self, mode: int) -> int:
625
+ """DECRPM status: 1 set, 2 reset, 0 for a mode this terminal does not have."""
626
+ if mode in _SWITCHES:
627
+ enabled = mode in self._switches
628
+ elif mode in _ALTERNATE_SCREENS:
629
+ enabled = self.alternate_screen
630
+ elif mode in _TRACKING_MODES:
631
+ enabled = self.mouse_tracking.value == mode
632
+ elif mode in _ENCODING_MODES:
633
+ enabled = self.mouse_encoding.value == mode
634
+ else:
635
+ return 0
636
+ return 1 if enabled else 2
637
+
638
+ def _show_alternate_screen(self, show: bool, *, save_cursor: bool) -> None:
639
+ if show == self.alternate_screen:
640
+ return
641
+ if show:
642
+ if save_cursor:
643
+ self._save_cursor()
644
+ primary_cursor = self._primary.cursor
645
+ self._alternate = ScreenBuffer(self.columns, self.lines, history_limit=0)
646
+ self._kitty_flags[True] = [0]
647
+ self._alternate.cursor.x, self._alternate.cursor.y = primary_cursor.x, primary_cursor.y
648
+ self.buffer = self._alternate
649
+ else:
650
+ self.buffer = self._primary
651
+ if save_cursor:
652
+ self._restore_cursor()
653
+ self.buffer.damage_everything()
654
+
655
+ def _soft_reset(self) -> None:
656
+ """DECSTR: back to sane modes without touching what is on the screen."""
657
+ self._switches.discard(_ORIGIN)
658
+ self._switches.discard(_APPLICATION_CURSOR)
659
+ self._switches.discard(_APPLICATION_KEYPAD)
660
+ self._switches.update(_SWITCHES_ON_AT_RESET)
661
+ self._insert = False
662
+ self.pen = self.pen._replace(foreground=None, background=None, attributes=DEFAULT_PEN.attributes)
663
+ self.cursor_shape = CursorShape.BLOCK
664
+ self._charsets = Charsets()
665
+ self._saved_cursors.clear()
666
+ self.buffer.set_margins(0, self.lines - 1)
667
+
668
+ def _hard_reset(self) -> None:
669
+ """RIS: the state the terminal was constructed in, history gone."""
670
+ self._history_dropped += self._primary.top
671
+ self._reset()
672
+ self._parser.reset()
673
+
674
+ # -- dispatch tables ---------------------------------------------------------------------
675
+
676
+ def _escape_table(self) -> dict[str, Callable[[], None]]:
677
+ return {
678
+ "7": self._save_cursor, # DECSC
679
+ "8": self._restore_cursor, # DECRC
680
+ "D": lambda: self.buffer.index(self.pen.eraser), # IND
681
+ "E": self._next_line, # NEL
682
+ "H": lambda: self._tab_stops.add(self.buffer.cursor.x), # HTS
683
+ "M": lambda: self.buffer.reverse_index(self.pen.eraser), # RI
684
+ "N": lambda: self._charsets.shift_once(2), # SS2
685
+ "O": lambda: self._charsets.shift_once(3), # SS3
686
+ "n": lambda: self._charsets.lock(2), # LS2
687
+ "o": lambda: self._charsets.lock(3), # LS3
688
+ "c": self._hard_reset, # RIS
689
+ "=": lambda: self._switches.add(_APPLICATION_KEYPAD), # DECKPAM
690
+ ">": lambda: self._switches.discard(_APPLICATION_KEYPAD), # DECKPNM
691
+ "#8": lambda: self.buffer.fill("E"), # DECALN
692
+ }
693
+
694
+ def _control_sequence_table(self) -> dict[str, Callable[[tuple[int, ...]], None]]:
695
+ return {
696
+ "@": self._insert_characters,
697
+ "A": lambda p: self._move_by(0, -(p[0] or 1)), # CUU
698
+ "B": lambda p: self._move_by(0, p[0] or 1), # CUD
699
+ "C": lambda p: self._move_by(p[0] or 1, 0), # CUF
700
+ "D": lambda p: self._move_by(-(p[0] or 1), 0), # CUB
701
+ "E": lambda p: self._move_by(-self.columns, p[0] or 1), # CNL
702
+ "F": lambda p: self._move_by(-self.columns, -(p[0] or 1)), # CPL
703
+ "G": lambda p: self._move_to(p[0] - 1, self._row_in_origin()), # CHA
704
+ "H": self._cursor_position,
705
+ "I": lambda p: self._tab_forward(p[0] or 1), # CHT
706
+ "J": self._erase_in_display,
707
+ "?J": self._erase_in_display, # DECSED; nothing here is protected, so it erases like ED
708
+ "K": self._erase_in_line,
709
+ "?K": self._erase_in_line, # DECSEL
710
+ "L": lambda p: self._edit_lines(self.buffer.insert_lines, p), # IL
711
+ "M": lambda p: self._edit_lines(self.buffer.delete_lines, p), # DL
712
+ "P": self._delete_characters,
713
+ "S": lambda p: self.buffer.scroll_up(p[0] or 1, self.pen.eraser), # SU
714
+ "T": lambda p: self.buffer.scroll_down(p[0] or 1, self.pen.eraser), # SD
715
+ "X": self._erase_characters,
716
+ "Z": lambda p: self._tab_backward(p[0] or 1), # CBT
717
+ "`": lambda p: self._move_to(p[0] - 1, self._row_in_origin()), # HPA
718
+ "a": lambda p: self._move_by(p[0] or 1, 0), # HPR
719
+ "b": self._repeat,
720
+ "c": self._report_primary_attributes,
721
+ ">c": self._report_secondary_attributes,
722
+ "d": self._line_position_absolute,
723
+ "e": lambda p: self._move_by(0, p[0] or 1), # VPR
724
+ "f": self._cursor_position, # HVP
725
+ "g": self._clear_tab_stops,
726
+ "h": lambda p: self._set_modes(p, enabled=True), # SM
727
+ "l": lambda p: self._set_modes(p, enabled=False), # RM
728
+ "?h": lambda p: self._set_private_modes(p, enabled=True), # DECSET
729
+ "?l": lambda p: self._set_private_modes(p, enabled=False), # DECRST
730
+ "n": self._report_status,
731
+ "?n": self._report_status_extended,
732
+ "!p": lambda _: self._soft_reset(), # DECSTR
733
+ "$p": self._report_mode,
734
+ "?$p": self._report_private_mode,
735
+ " q": self._set_cursor_shape,
736
+ "r": self._set_scrolling_region,
737
+ "s": lambda _: self._save_cursor(), # SCOSC
738
+ "t": self._window_operation,
739
+ "u": lambda _: self._restore_cursor(), # SCORC
740
+ ">m": lambda p: self._set_modify_other_keys(p, p[1] if len(p) > 1 else 0), # XTMODKEYS
741
+ ">n": lambda p: self._set_modify_other_keys(p, 0),
742
+ ">u": self._push_kitty_flags,
743
+ "<u": self._pop_kitty_flags,
744
+ "=u": self._set_kitty_flags,
745
+ "?u": lambda _: self._replies.append(f"\x1b[?{self._kitty_flags[self.alternate_screen][-1]}u"),
746
+ }
747
+
748
+ def _operating_system_command_table(self) -> dict[str, Callable[[str], None]]:
749
+ return {
750
+ # Titles (OSC 0 and 2) fall through with everything else unlisted: accepted, ignored.
751
+ "7": self._report_directory_url,
752
+ "8": self._set_hyperlink,
753
+ "2025": self._report_directory,
754
+ "2026": self._report_command,
755
+ }
756
+
757
+ # -- control sequences -------------------------------------------------------------------
758
+
759
+ def _cursor_position(self, parameters: tuple[int, ...]) -> None:
760
+ """CUP / HVP."""
761
+ row = (parameters[0] or 1) - 1
762
+ self._anchor_to_host(row)
763
+ self._move_to((parameters[1] or 1) - 1 if len(parameters) > 1 else 0, row)
764
+
765
+ def _line_position_absolute(self, parameters: tuple[int, ...]) -> None:
766
+ """VPA."""
767
+ row = (parameters[0] or 1) - 1
768
+ self._anchor_to_host(row)
769
+ self._move_to(self.buffer.cursor.x, row)
770
+
771
+ def _anchor_to_host(self, row: int) -> None:
772
+ """Believe the first row a repainting host names after a resize; see `resize`."""
773
+ if self._anchor_pending:
774
+ self._anchor_pending = False
775
+ self.buffer.anchor_cursor_row(row)
776
+
777
+ def _next_line(self) -> None:
778
+ """NEL."""
779
+ self.buffer.index(self.pen.eraser)
780
+ self.buffer.cursor.x = 0
781
+
782
+ def _erase_in_display(self, parameters: tuple[int, ...]) -> None:
783
+ """ED."""
784
+ buffer = self.buffer
785
+ cursor = buffer.cursor
786
+ eraser = self.pen.eraser
787
+ cursor.pending_wrap = False
788
+ if parameters[0] == 0:
789
+ buffer.edit(cursor.y).erase(cursor.x, self.columns, eraser, self.columns)
790
+ buffer.erase_rows(cursor.y + 1, self.lines - 1, eraser)
791
+ elif parameters[0] == 1:
792
+ buffer.erase_rows(0, cursor.y - 1, eraser)
793
+ buffer.edit(cursor.y).erase(0, cursor.x + 1, eraser, self.columns)
794
+ elif parameters[0] == 2:
795
+ buffer.erase_rows(0, self.lines - 1, eraser)
796
+ elif parameters[0] == 3:
797
+ self._history_dropped += buffer.clear_history()
798
+
799
+ def _erase_in_line(self, parameters: tuple[int, ...]) -> None:
800
+ """EL."""
801
+ cursor = self.buffer.cursor
802
+ cursor.pending_wrap = False
803
+ start, end = ((cursor.x, self.columns), (0, cursor.x + 1), (0, self.columns))[min(parameters[0], 2)]
804
+ self.buffer.edit(cursor.y).erase(start, end, self.pen.eraser, self.columns)
805
+
806
+ def _erase_characters(self, parameters: tuple[int, ...]) -> None:
807
+ """ECH."""
808
+ cursor = self.buffer.cursor
809
+ cursor.pending_wrap = False
810
+ end = min(cursor.x + (parameters[0] or 1), self.columns)
811
+ self.buffer.edit(cursor.y).erase(cursor.x, end, self.pen.eraser, self.columns)
812
+
813
+ def _insert_characters(self, parameters: tuple[int, ...]) -> None:
814
+ """ICH."""
815
+ cursor = self.buffer.cursor
816
+ cursor.pending_wrap = False
817
+ blanks = " " * min(parameters[0] or 1, self.columns - cursor.x)
818
+ self.buffer.edit(cursor.y).insert(cursor.x, blanks, self.pen.eraser, self.columns)
819
+
820
+ def _delete_characters(self, parameters: tuple[int, ...]) -> None:
821
+ """DCH."""
822
+ cursor = self.buffer.cursor
823
+ cursor.pending_wrap = False
824
+ self.buffer.edit(cursor.y).delete(cursor.x, parameters[0] or 1, self.pen.eraser, self.columns)
825
+
826
+ def _edit_lines(self, operation: Callable[[int, Pen], None], parameters: tuple[int, ...]) -> None:
827
+ """IL / DL, which leave the cursor at the start of its row."""
828
+ operation(parameters[0] or 1, self.pen.eraser)
829
+ self.buffer.cursor.x = 0
830
+ self.buffer.cursor.pending_wrap = False
831
+
832
+ def _repeat(self, parameters: tuple[int, ...]) -> None:
833
+ """REP."""
834
+ if self._last_printed:
835
+ repeated = [self._last_printed] * min(parameters[0] or 1, self.columns * self.lines)
836
+ self._write([cell for cluster in repeated for cell in (cluster, *[""] * (cell_len(cluster) - 1))])
837
+
838
+ def _clear_tab_stops(self, parameters: tuple[int, ...]) -> None:
839
+ """TBC."""
840
+ if parameters[0] == 0:
841
+ self._tab_stops.discard(self.buffer.cursor.x)
842
+ elif parameters[0] == 3:
843
+ self._tab_stops.clear()
844
+
845
+ def _set_private_modes(self, parameters: tuple[int, ...], *, enabled: bool) -> None:
846
+ """DECSET / DECRST."""
847
+ for mode in parameters:
848
+ self._set_private_mode(mode, enabled)
849
+
850
+ def _set_modes(self, parameters: tuple[int, ...], *, enabled: bool) -> None:
851
+ """SM / RM."""
852
+ for mode in parameters:
853
+ if mode == _INSERT:
854
+ self._insert = enabled
855
+ elif mode == _NEWLINE:
856
+ self._newline = enabled
857
+
858
+ def _set_scrolling_region(self, parameters: tuple[int, ...]) -> None:
859
+ """DECSTBM."""
860
+ top = (parameters[0] or 1) - 1
861
+ bottom = (parameters[1] if len(parameters) > 1 and parameters[1] else self.lines) - 1
862
+ bottom = min(bottom, self.lines - 1)
863
+ if top < bottom:
864
+ self.buffer.set_margins(top, bottom)
865
+ self._move_to(0, 0)
866
+
867
+ def _set_cursor_shape(self, parameters: tuple[int, ...]) -> None:
868
+ """DECSCUSR."""
869
+ if parameters[0] < len(_CURSOR_SHAPES):
870
+ self.cursor_shape = _CURSOR_SHAPES[parameters[0]]
871
+
872
+ def _set_modify_other_keys(self, parameters: tuple[int, ...], level: int) -> None:
873
+ if parameters[0] == _XTERM_MODIFY_OTHER_KEYS:
874
+ self._modify_other_keys = level
875
+
876
+ def _push_kitty_flags(self, parameters: tuple[int, ...]) -> None:
877
+ stack = self._kitty_flags[self.alternate_screen]
878
+ stack.append(parameters[0] & _KITTY_DISAMBIGUATE)
879
+ del stack[1:-_KITTY_STACK_LIMIT]
880
+
881
+ def _pop_kitty_flags(self, parameters: tuple[int, ...]) -> None:
882
+ stack = self._kitty_flags[self.alternate_screen]
883
+ del stack[max(len(stack) - (parameters[0] or 1), 0) :]
884
+ if not stack:
885
+ # Emptied, which the protocol says resets every flag: those set in place on the
886
+ # bottom entry, never pushed, go too.
887
+ stack.append(0)
888
+
889
+ def _set_kitty_flags(self, parameters: tuple[int, ...]) -> None:
890
+ stack = self._kitty_flags[self.alternate_screen]
891
+ flags = parameters[0] & _KITTY_DISAMBIGUATE
892
+ how = parameters[1] if len(parameters) > 1 else 1
893
+ stack[-1] = flags if how <= 1 else stack[-1] | flags if how == 2 else stack[-1] & ~flags
894
+
895
+ def _window_operation(self, parameters: tuple[int, ...]) -> None:
896
+ """XTWINOPS. Only the size report: nothing else about a window applies to a widget."""
897
+ if parameters[0] == 18:
898
+ self._replies.append(f"\x1b[8;{self.lines};{self.columns}t")
899
+
900
+ # -- reports -----------------------------------------------------------------------------
901
+
902
+ def _report_primary_attributes(self, parameters: tuple[int, ...]) -> None:
903
+ """DA1: a VT220 with ANSI color."""
904
+ if parameters[0] == 0:
905
+ self._replies.append("\x1b[?62;22c")
906
+
907
+ def _report_secondary_attributes(self, parameters: tuple[int, ...]) -> None:
908
+ """DA2: terminal type VT220, no firmware version worth testing against."""
909
+ if parameters[0] == 0:
910
+ self._replies.append("\x1b[>1;0;0c")
911
+
912
+ def _report_status(self, parameters: tuple[int, ...]) -> None:
913
+ """DSR: operating status, or the cursor position (CPR)."""
914
+ if parameters[0] == 5:
915
+ self._replies.append("\x1b[0n")
916
+ elif parameters[0] == 6:
917
+ self._replies.append(f"\x1b[{self._row_in_origin() + 1};{self.buffer.cursor.x + 1}R")
918
+
919
+ def _report_status_extended(self, parameters: tuple[int, ...]) -> None:
920
+ """DECXCPR."""
921
+ if parameters[0] == 6:
922
+ self._replies.append(f"\x1b[?{self._row_in_origin() + 1};{self.buffer.cursor.x + 1}R")
923
+
924
+ def _report_mode(self, parameters: tuple[int, ...]) -> None:
925
+ """DECRQM for an ANSI mode. The answer (DECRPM) says 1 for set, 2 for reset, 0 for unknown."""
926
+ mode = parameters[0]
927
+ states = {_INSERT: self._insert, _NEWLINE: self._newline}
928
+ status = 0 if mode not in states else 1 if states[mode] else 2
929
+ self._replies.append(f"\x1b[{mode};{status}$y")
930
+
931
+ def _report_private_mode(self, parameters: tuple[int, ...]) -> None:
932
+ """DECRQM for a DEC private mode."""
933
+ self._replies.append(f"\x1b[?{parameters[0]};{self._private_mode_status(parameters[0])}$y")
934
+
935
+ # -- operating system commands -----------------------------------------------------------
936
+
937
+ def _set_hyperlink(self, argument: str) -> None:
938
+ """OSC 8 ; params ; URI. An empty URI closes the link."""
939
+ _, _, uri = argument.partition(";")
940
+ self.pen = self.pen._replace(link=uri or None)
941
+
942
+ def _report_directory_url(self, url: str) -> None:
943
+ """OSC 7, the ``file://host/path`` convention shells use to announce their directory."""
944
+ try:
945
+ parts = urlsplit(url)
946
+ except ValueError:
947
+ # Brackets around something that is no IP address, say. Nowhere a shell can be.
948
+ return
949
+ host = (parts.hostname or "").partition(".")[0]
950
+ # A shell on the far side of ssh reports paths that mean nothing on this machine.
951
+ if parts.scheme == "file" and host in ("", "localhost", socket.gethostname().lower().partition(".")[0]):
952
+ path = unquote(parts.path)
953
+ # A drive path is written file:///C:/..., where the leading slash belongs to the URL.
954
+ self._events.append(DirectoryChanged(path[1:] if _DRIVE_PATH.match(path) else path))
955
+
956
+ def _report_directory(self, payload: str) -> None:
957
+ if path := _decode_payload(payload):
958
+ self._events.append(DirectoryChanged(path))
959
+
960
+ def _report_command(self, payload: str) -> None:
961
+ # An empty command is Enter at an empty prompt: nothing ran.
962
+ if command := _decode_payload(payload):
963
+ self._events.append(CommandSubmitted(command))