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,865 @@
1
+ # Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
2
+
3
+ """Engine-internal companion module for ``AgentEngine`` build and restart orchestration."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import asyncio
8
+ import contextlib
9
+ import copy
10
+ import logging
11
+ from collections.abc import Callable
12
+ from datetime import UTC, datetime
13
+ from typing import TYPE_CHECKING
14
+
15
+ from chrys.foundation.branding import APP_DISPLAY_NAME
16
+ from chrys.foundation.errors import clean_error_message
17
+ from chrys.foundation.errors.display import display_fields
18
+ from chrys.foundation.events.types import (
19
+ AgentLoadFailed,
20
+ AgentLoadFinished,
21
+ AgentLoadProgress,
22
+ AgentLoadStarted,
23
+ ApprovalModeUpdated,
24
+ ProfileSwitched,
25
+ SessionReady,
26
+ Warning,
27
+ WorkspaceUpdated,
28
+ )
29
+ from chrys.foundation.i18n import msg
30
+ from chrys.foundation.models.workspace import Workspace
31
+ from chrys.foundation.platform import safe_getcwd
32
+ from chrys.foundation.trajectory.event_types import ProfileKind
33
+ from chrys.orchestration.engine.build import construction
34
+ from chrys.orchestration.engine.build.construction import BuildAgentFn, StagedBuild
35
+ from chrys.orchestration.engine.build.loaded import AgentManifest, CompletedBuild, ReplacedBuild
36
+ from chrys.orchestration.engine.state.machine import Trigger
37
+ from chrys.orchestration.engine.trajectory import TrajectoryRecorder
38
+ from chrys.orchestration.invoker.resources import PreparedAgent
39
+ from chrys.orchestration.session_hooks import SessionHookFactory
40
+ from chrys.service.agent_middleware.reminders.archive_pointer import CATALOG_POINTER_RECORD_COUNT_STATE_KEY
41
+ from chrys.service.mcp.cache import MCPConnectionCache
42
+ from chrys.service.mutations.workspace_changes import WorkspaceChangeTracker
43
+ from chrys.service.profiles.models.schema import API_STYLE_RESPONSES
44
+ from chrys.service.session.runtime_metadata import SessionRuntimeMetadata
45
+ from chrys.service.trajectory.session import SessionStartInfo
46
+
47
+ if TYPE_CHECKING:
48
+ from chrys.foundation.config.settings_store import LoadedSettings, SettingsHandle
49
+ from chrys.foundation.events.bus import EventBus
50
+ from chrys.foundation.events.types import RuntimeSkillDetails
51
+ from chrys.orchestration.engine.run.turn_hooks import TurnHookDispatcher
52
+ from chrys.orchestration.engine.run.turn_state import TurnRuntimeState
53
+ from chrys.orchestration.engine.state.active_session import ActiveSession
54
+ from chrys.orchestration.engine.state.current_agent import CurrentAgent
55
+ from chrys.orchestration.engine.state.lifecycle_permits import LifecyclePermits
56
+ from chrys.orchestration.engine.state.machine import EngineStateMachine
57
+ from chrys.orchestration.engine.state.session_writer import RecoveryCheckpoints
58
+ from chrys.orchestration.engine.usage import UsagePublisher
59
+ from chrys.service.approval.turn_context import TurnContextHolder
60
+ from chrys.service.hooks.manager import HookManager
61
+ from chrys.service.profiles.agents.registry import AgentProfileRegistry
62
+ from chrys.service.profiles.agents.schema import AgentProfile, MCPServerConfig
63
+ from chrys.service.profiles.models.registry import ModelProfileRegistry
64
+ from chrys.service.profiles.models.schema import ModelProfile
65
+ from chrys.service.session.history import SessionHistoryManager
66
+ from chrys.service.session.persistence import SessionPersistence
67
+ from chrys.service.trajectory.session import SessionTrajectory
68
+
69
+
70
+ logger = logging.getLogger(__name__)
71
+
72
+
73
+ _CONSTRUCTION_SERVICE_SESSION_INCOMPATIBLE = msg(
74
+ "construction.service_session_incompatible",
75
+ fallback=(
76
+ "The previous OpenAI Responses service session is not compatible with the active agent profile, workspace, "
77
+ "model profile, service endpoint, or storage is disabled. {app_name} will continue from local history only."
78
+ ),
79
+ )
80
+
81
+
82
+ def _is_openai_responses_profile(profile: ModelProfile | None) -> bool:
83
+ """Return True for profiles that use OpenAI Responses transport."""
84
+ return profile is not None and profile.provider == "openai" and profile.api_style == API_STYLE_RESPONSES
85
+
86
+
87
+ def _responses_service_session_profiles_match(
88
+ old_profile: ModelProfile | None, new_profile: ModelProfile | None
89
+ ) -> bool:
90
+ """Return True when two profiles can safely share a Responses service session id."""
91
+ return (
92
+ _is_openai_responses_profile(old_profile)
93
+ and _is_openai_responses_profile(new_profile)
94
+ and old_profile is not None
95
+ and new_profile is not None
96
+ and old_profile.model_id == new_profile.model_id
97
+ )
98
+
99
+
100
+ def _workspace_signature(workspace: Workspace | None) -> tuple[str, tuple[str, ...]]:
101
+ """Return the session-relevant workspace identity for service-session reuse."""
102
+ if workspace is None:
103
+ return ("", ())
104
+ return (workspace.primary_cwd, tuple(working_dir.path for working_dir in workspace.working_dirs))
105
+
106
+
107
+ def _workspace_primary_cwd(workspace: Workspace | None) -> str:
108
+ """Return the workspace primary cwd used for project-scoped config."""
109
+ return workspace.primary_cwd if workspace is not None else ""
110
+
111
+
112
+ def _can_reuse_responses_service_session(
113
+ *,
114
+ old_agent_profile_fingerprint: str,
115
+ new_agent_profile_fingerprint: str,
116
+ old_model_profile_fingerprint: str,
117
+ new_model_profile_fingerprint: str,
118
+ old_model_profile: ModelProfile | None,
119
+ new_model_profile: ModelProfile | None,
120
+ old_model_base_url: str,
121
+ new_model_base_url: str,
122
+ old_workspace: Workspace | None,
123
+ new_workspace: Workspace | None,
124
+ old_storage_enabled: bool,
125
+ new_storage_enabled: bool,
126
+ ) -> bool:
127
+ """Return True when a Responses service session can safely survive a rebuild."""
128
+ return bool(
129
+ _responses_service_session_profiles_match(old_model_profile, new_model_profile)
130
+ and old_agent_profile_fingerprint
131
+ and old_agent_profile_fingerprint == new_agent_profile_fingerprint
132
+ and old_model_profile_fingerprint
133
+ and old_model_profile_fingerprint == new_model_profile_fingerprint
134
+ and old_model_base_url
135
+ and old_model_base_url == new_model_base_url
136
+ and _workspace_signature(old_workspace) == _workspace_signature(new_workspace)
137
+ and old_storage_enabled
138
+ and new_storage_enabled
139
+ )
140
+
141
+
142
+ async def _close_replaced_hook_manager(manager: HookManager | None) -> None:
143
+ """Close a hook manager that no longer belongs to the live executor."""
144
+ if manager is None:
145
+ return
146
+ try:
147
+ await manager.drain_session()
148
+ except Exception:
149
+ logger.exception("Error draining replaced hook manager")
150
+
151
+
152
+ def _preserved_history_state(raw: dict | None) -> dict | None:
153
+ """Deep copy of the predecessor's history state, installed as the successor's live history.
154
+
155
+ The copy owns every layer (messages, contents lists, content objects) so the
156
+ successor's registries and anchors can never alias the predecessor's live
157
+ objects. Falsy/empty history returns ``None`` — no carryover to preserve.
158
+ """
159
+ return copy.deepcopy(raw) if raw else None
160
+
161
+
162
+ class AgentLoader:
163
+ """Construct candidates, install their values, and release displaced build resources."""
164
+
165
+ def __init__(
166
+ self,
167
+ *,
168
+ bus: EventBus,
169
+ persistence: SessionPersistence,
170
+ agent_registry: AgentProfileRegistry | None,
171
+ model_registry: ModelProfileRegistry | None,
172
+ settings_handle: SettingsHandle,
173
+ session: ActiveSession,
174
+ current: CurrentAgent,
175
+ permits: LifecyclePermits,
176
+ checkpoints: RecoveryCheckpoints,
177
+ usage_publisher: UsagePublisher,
178
+ hooks: TurnHookDispatcher,
179
+ history: SessionHistoryManager,
180
+ turn_state: TurnRuntimeState,
181
+ workspace_change_tracker: WorkspaceChangeTracker,
182
+ trajectory_recorder: TrajectoryRecorder,
183
+ fsm: EngineStateMachine,
184
+ turn_context: TurnContextHolder,
185
+ mcp_cache: MCPConnectionCache,
186
+ mcp_overlay: list[MCPServerConfig] | None,
187
+ allow_user_interaction: bool,
188
+ build_agent_fn: BuildAgentFn,
189
+ register_current_engine: Callable[[], None],
190
+ ) -> None:
191
+ self._bus = bus
192
+ self._persistence = persistence
193
+ self._agent_registry = agent_registry
194
+ self._model_registry = model_registry
195
+ self._settings_handle = settings_handle
196
+ self._session = session
197
+ self._current = current
198
+ self._permits = permits
199
+ self._checkpoints = checkpoints
200
+ self._usage_publisher = usage_publisher
201
+ self._hooks = hooks
202
+ self._history = history
203
+ self._turn_state = turn_state
204
+ self._workspace_change_tracker = workspace_change_tracker
205
+ self._trajectory_recorder = trajectory_recorder
206
+ self._fsm = fsm
207
+ self._mcp_cache = mcp_cache
208
+ self._turn_context = turn_context
209
+ self._injection_notify_tasks: set[asyncio.Task[None]] = set()
210
+ self._mcp_overlay = list(mcp_overlay or [])
211
+ self._allow_user_interaction = allow_user_interaction
212
+ self._build_agent_fn = build_agent_fn
213
+ self._register_current_engine = register_current_engine
214
+
215
+ def stage(
216
+ self,
217
+ *,
218
+ loaded: LoadedSettings,
219
+ agent_profile: AgentProfile,
220
+ workspace: Workspace | None,
221
+ hook_manager: HookManager | None,
222
+ ) -> StagedBuild:
223
+ """Assemble the settings, workspace, and coordinator candidate."""
224
+ return construction.stage_build(
225
+ session=self._session,
226
+ persistence=self._persistence,
227
+ loaded=loaded,
228
+ agent_profile=agent_profile,
229
+ workspace=workspace,
230
+ hook_manager=hook_manager,
231
+ )
232
+
233
+ async def stage_hook_manager(
234
+ self,
235
+ *,
236
+ operation: str,
237
+ staged_loaded: LoadedSettings | None,
238
+ workspace: Workspace | None,
239
+ ) -> HookManager | None:
240
+ """Construct the hook candidate when the caller's replacement decision requires it."""
241
+ old_hook_manager = self._session.hook_manager
242
+ hook_manager = old_hook_manager
243
+ if old_hook_manager is None or operation == "workspace_change":
244
+ # Project hooks are primary-cwd scoped, so the candidate is built for
245
+ # the root the staged workspace would install, not the live one.
246
+ build_settings = (staged_loaded if staged_loaded is not None else self._settings_handle.loaded).settings
247
+ hook_manager = await self.build_hook_manager(
248
+ project_root=_workspace_primary_cwd(workspace),
249
+ project_hooks_enabled=build_settings.project_hooks_enabled,
250
+ )
251
+ return hook_manager
252
+
253
+ async def build(
254
+ self, profile: AgentProfile, staged: StagedBuild, *, preserved_history: dict | None = None
255
+ ) -> CompletedBuild:
256
+ """Construct a complete candidate without installing it."""
257
+ return await construction.build_agent(
258
+ self._profile_with_mcp_overlay(profile),
259
+ staged=staged,
260
+ preserved_history=preserved_history,
261
+ build_agent_fn=self._build_agent_fn,
262
+ session=self._session,
263
+ settings_handle=self._settings_handle,
264
+ persistence=self._persistence,
265
+ bus=self._bus,
266
+ agent_registry=self._agent_registry,
267
+ model_registry=self._model_registry,
268
+ workspace_change_tracker=self._workspace_change_tracker,
269
+ turn_state=self._turn_state,
270
+ turn_context=self._turn_context,
271
+ mcp_cache=self._mcp_cache,
272
+ allow_user_interaction=self._allow_user_interaction,
273
+ checkpoints=self._checkpoints,
274
+ usage_publisher=self._usage_publisher,
275
+ fire_pre_compact=self._hooks.fire_pre_compact,
276
+ publish_load_progress=self.publish_load_progress,
277
+ hold_injection_notification=self.hold_injection_notification,
278
+ )
279
+
280
+ def _workspace_cwd(self) -> str:
281
+ """Return the engine workspace cwd, falling back only for legacy unstarted engines."""
282
+ if self._session.workspace is not None:
283
+ return self._session.workspace.primary_cwd
284
+ return safe_getcwd()
285
+
286
+ def _workspace_working_dirs(self) -> list[str]:
287
+ """Return all workspace root paths for event payloads."""
288
+ if self._session.workspace is None:
289
+ return []
290
+ return [working_dir.path for working_dir in self._session.workspace.working_dirs]
291
+
292
+ def trajectory_session_start_info(self) -> SessionStartInfo:
293
+ """Resolved lazily, at the recorder's first event — after the build set the fingerprints."""
294
+ return SessionStartInfo(
295
+ primary_cwd=_workspace_primary_cwd(self._session.workspace),
296
+ agent_profile_fingerprint=self._current.manifest.agent_profile_fingerprint,
297
+ model_profile_fingerprint=self._current.manifest.model_profile_fingerprint,
298
+ )
299
+
300
+ async def build_hook_manager(
301
+ self, *, project_root: str, project_hooks_enabled: bool, session_id: str | None = None
302
+ ) -> HookManager | None:
303
+ return await SessionHookFactory(self._bus)(
304
+ project_root=project_root,
305
+ project_hooks_enabled=project_hooks_enabled,
306
+ session_id=session_id or self._session.session_id,
307
+ )
308
+
309
+ def start_outbox_recovery(self) -> None:
310
+ """Start durable outbox recovery for the current hook manager if needed."""
311
+ if self._session.hook_manager is None or self._session.outbox_recovery_task is not None:
312
+ return
313
+ # Background-recover the outbox only after the session guard is held.
314
+ # That prevents a failed/conflicting startup from spawning work, and
315
+ # shutdown explicitly observes/cancels this task before draining hooks.
316
+ self._session.outbox_recovery_task = asyncio.create_task(self._session.hook_manager.recover_outbox())
317
+
318
+ async def cancel_outbox_recovery(self) -> None:
319
+ """Observe or cancel the active outbox recovery task before replacing managers."""
320
+ task = self._session.outbox_recovery_task
321
+ if task is None:
322
+ return
323
+ if task.done():
324
+ try:
325
+ task.result()
326
+ except asyncio.CancelledError:
327
+ pass
328
+ except Exception:
329
+ logger.exception("Outbox recovery task failed")
330
+ else:
331
+ task.cancel()
332
+ with contextlib.suppress(asyncio.CancelledError):
333
+ await task
334
+ self._session.outbox_recovery_task = None
335
+
336
+ async def commit_hook_manager_replacement(
337
+ self,
338
+ old_hook_manager: HookManager | None,
339
+ ) -> None:
340
+ """Retire the old hook manager after a replacement executor is installed."""
341
+ await self.cancel_outbox_recovery()
342
+ await _close_replaced_hook_manager(old_hook_manager)
343
+ self.start_outbox_recovery()
344
+
345
+ async def settle_staged_hook_manager(
346
+ self,
347
+ *,
348
+ old: HookManager | None,
349
+ staged: HookManager | None,
350
+ ) -> None:
351
+ """After a failed build, retire whichever hook manager lost.
352
+
353
+ Pre-commit failure: the staged candidate never went live — drain it and
354
+ leave the old manager, its outbox and its recovery task untouched.
355
+ Post-commit failure: the candidate is installed and the build it belongs
356
+ to is what the engine keeps, so retire the old manager exactly as the
357
+ success path would have.
358
+ """
359
+ if staged is old:
360
+ return
361
+ if self._session.hook_manager is staged:
362
+ await self.commit_hook_manager_replacement(old)
363
+ else:
364
+ await _close_replaced_hook_manager(staged)
365
+
366
+ async def load(
367
+ self,
368
+ profile: AgentProfile,
369
+ *,
370
+ operation: str = "startup",
371
+ staged_loaded: LoadedSettings | None = None,
372
+ workspace: Workspace | None,
373
+ hook_manager: HookManager | None,
374
+ trajectory: SessionTrajectory,
375
+ old_hook_manager: HookManager | None,
376
+ ) -> None:
377
+ """Build and install the agent from a profile.
378
+
379
+ ``staged_loaded`` is a settings candidate the build should be configured
380
+ from instead of the live handle — a settings reload or workspace change
381
+ retrying after a failed first build passes the re-derived load here, and
382
+ it goes live only when the build commits. ``workspace`` is the same thing
383
+ for the workspace: a no-executor workspace change passes the new root here
384
+ rather than mutating the live one, so a failed build keeps the workspace
385
+ the live settings and hooks still describe.
386
+ """
387
+ self._permits.begin_agent_load()
388
+ try:
389
+ await self._bus.publish(
390
+ AgentLoadStarted(
391
+ operation=operation,
392
+ to_profile=profile.name,
393
+ to_display_name=profile.display_name or profile.name,
394
+ session_id=self._session.session_id,
395
+ ),
396
+ )
397
+
398
+ staged = self.stage(
399
+ loaded=staged_loaded if staged_loaded is not None else self._settings_handle.loaded,
400
+ agent_profile=profile,
401
+ workspace=workspace,
402
+ hook_manager=hook_manager,
403
+ )
404
+ completed = await self.build(profile, staged)
405
+ replaced = self.install(completed)
406
+ await self.release(replaced)
407
+ self._fsm.try_transition(Trigger.START)
408
+ self._register_current_engine()
409
+ except Exception as exc:
410
+ await self.settle_staged_hook_manager(old=old_hook_manager, staged=hook_manager)
411
+ self._permits.finish_agent_load()
412
+ await self.publish_load_failed(operation=operation, profile=profile, exc=exc)
413
+ raise
414
+ except BaseException:
415
+ await self.settle_staged_hook_manager(old=old_hook_manager, staged=hook_manager)
416
+ self._permits.finish_agent_load()
417
+ raise
418
+
419
+ # The build committed, so the profiles this session opened with are the
420
+ # ones it keeps: pin them before a switch can rewrite what the recorder
421
+ # reads at its first event.
422
+ trajectory.pin_session_start_info()
423
+
424
+ if hook_manager is not old_hook_manager:
425
+ await self.commit_hook_manager_replacement(old_hook_manager)
426
+ else:
427
+ self.start_outbox_recovery()
428
+
429
+ self._permits.finish_agent_load()
430
+ await self._bus.publish(
431
+ AgentLoadFinished(
432
+ operation=operation,
433
+ agent_profile=profile.name,
434
+ display_name=profile.display_name or profile.name,
435
+ session_id=self._session.session_id,
436
+ ),
437
+ )
438
+
439
+ sub_agent_tool_names = (
440
+ self._current.loaded.sub_agent_tools.tool_names()
441
+ if self._current.loaded is not None and self._current.loaded.sub_agent_tools
442
+ else []
443
+ )
444
+ await self._bus.publish(
445
+ SessionReady(
446
+ agent_profile=profile.name,
447
+ display_name=profile.display_name,
448
+ model_profile_id=self._current.manifest.active_profile.id
449
+ if self._current.manifest.active_profile
450
+ else "",
451
+ max_context_tokens=self._current.manifest.active_profile.max_context_tokens
452
+ if self._current.manifest.active_profile
453
+ else 0,
454
+ session_id=self._session.session_id,
455
+ tool_names=list(self._current.manifest.tool_names),
456
+ tool_kinds=dict(self._current.manifest.tool_kinds),
457
+ skill_names=list(self._current.manifest.skill_names),
458
+ sub_agent_tool_names=sub_agent_tool_names,
459
+ memory_files=list(self._current.manifest.memory_files),
460
+ runtime_details=copy.deepcopy(self._current.manifest.runtime_details),
461
+ primary_cwd=self._workspace_cwd(),
462
+ working_dirs=self._workspace_working_dirs(),
463
+ ),
464
+ )
465
+ await self._bus.publish(
466
+ ApprovalModeUpdated(mode=self._session.approval_mode.value, session_id=self._session.session_id)
467
+ )
468
+
469
+ # Fire ``session_start`` hooks after the engine is fully ready and
470
+ # the UI has its ``SessionReady`` event. Hooks are observers here:
471
+ # they cannot deny startup or mutate engine state, although a
472
+ # blocking hook still runs inline at this post-ready boundary.
473
+ # Profile-switch/rebuild operations skip this so users don't see a
474
+ # hook fire every time the same session reloads infrastructure.
475
+ if self._session.hook_manager is not None and operation in {"startup", "new_session", "reset"}:
476
+ from chrys.service.hooks.events import HookEvent
477
+
478
+ await self._session.hook_manager.fire(
479
+ HookEvent.SESSION_START,
480
+ {"session_id": self._session.session_id, "profile": profile.name, "cwd": self._workspace_cwd()},
481
+ scope="session",
482
+ )
483
+
484
+ async def publish_load_progress(
485
+ self,
486
+ *,
487
+ phase: str,
488
+ message: str,
489
+ server_name: str = "",
490
+ current: int = 0,
491
+ total: int = 0,
492
+ failed: int = 0,
493
+ status: str = "",
494
+ subject: str = "",
495
+ detail: str = "",
496
+ ) -> None:
497
+ """Publish an agent load progress event."""
498
+ await self._bus.publish(
499
+ AgentLoadProgress(
500
+ phase=phase,
501
+ message=message,
502
+ server_name=server_name,
503
+ current=current,
504
+ total=total,
505
+ failed=failed,
506
+ status=status,
507
+ subject=subject,
508
+ detail=detail,
509
+ session_id=self._session.session_id,
510
+ ),
511
+ )
512
+
513
+ async def publish_load_failed(
514
+ self,
515
+ *,
516
+ operation: str,
517
+ profile: AgentProfile,
518
+ exc: Exception,
519
+ ) -> None:
520
+ """Publish an agent load failure event."""
521
+ display_message, display_hint = display_fields(exc)
522
+ await self._bus.publish(
523
+ AgentLoadFailed(
524
+ operation=operation,
525
+ agent_profile=profile.name,
526
+ display_name=profile.display_name or profile.name,
527
+ message=clean_error_message(exc),
528
+ session_id=self._session.session_id,
529
+ display_message=display_message,
530
+ display_hint=display_hint,
531
+ ),
532
+ )
533
+
534
+ async def reload(
535
+ self,
536
+ new_profile: AgentProfile,
537
+ workspace: Workspace | None = None,
538
+ *,
539
+ operation: str = "switch",
540
+ staged_loaded: LoadedSettings | None = None,
541
+ ) -> None:
542
+ """Restart the agent with a new profile/workspace while preserving history.
543
+
544
+ ``staged_loaded`` is a settings candidate the build should be configured
545
+ from instead of the live handle — a settings reload or workspace change
546
+ passes the re-derived load here, and it goes live only when the build
547
+ commits. A pre-commit failure therefore leaves the settings the previous
548
+ executor was built from untouched; a post-commit failure keeps the new
549
+ ones, because they are what the installed executor runs with.
550
+ """
551
+ old_profile_name = self._session.agent_profile.name if self._session.agent_profile else ""
552
+ old_display_name = self._session.agent_profile.display_name if self._session.agent_profile else ""
553
+ old_model_profile = self._current.manifest.active_profile
554
+ old_model_base_url = self._current.manifest.runtime_details.model.base_url
555
+ old_agent_profile_fingerprint = self._current.manifest.agent_profile_fingerprint
556
+ old_model_profile_fingerprint = self._current.manifest.model_profile_fingerprint
557
+ old_service_session_id = (
558
+ self._current.loaded.bindings.backend.service_session_id if self._current.loaded is not None else ""
559
+ )
560
+ old_service_session_storage_enabled = (
561
+ self._current.loaded.bindings.backend.service_session_storage_enabled
562
+ if self._current.loaded is not None
563
+ else False
564
+ )
565
+ old_workspace = self._session.workspace
566
+ old_hook_manager = self._session.hook_manager
567
+ staged_hook_manager = old_hook_manager
568
+ new_loaded = staged_loaded if staged_loaded is not None else self._settings_handle.loaded
569
+ # Project hooks are primary-cwd scoped and gated by ``project.hooks_enabled``:
570
+ # a new primary cwd or a flipped gate rebuilds the manager for the root
571
+ # the build is about to install (the live one when the workspace stays).
572
+ reload_hook_manager = (
573
+ workspace is not None and _workspace_primary_cwd(old_workspace) != _workspace_primary_cwd(workspace)
574
+ ) or new_loaded.settings.project_hooks_enabled != self._settings_handle.loaded.settings.project_hooks_enabled
575
+ hook_manager_root = _workspace_primary_cwd(workspace if workspace is not None else old_workspace)
576
+ hook_manager_replacement_committed = False
577
+
578
+ preserved_state: dict | None = None
579
+ self._permits.begin_agent_load()
580
+ try:
581
+ await self._bus.publish(
582
+ AgentLoadStarted(
583
+ operation=operation,
584
+ from_profile=old_profile_name,
585
+ to_profile=new_profile.name,
586
+ from_display_name=old_display_name or old_profile_name,
587
+ to_display_name=new_profile.display_name or new_profile.name,
588
+ session_id=self._session.session_id,
589
+ ),
590
+ )
591
+
592
+ if self._current.loaded is not None:
593
+ raw = self._current.loaded.bindings.backend.history_state
594
+ preserved_state = _preserved_history_state(raw)
595
+ if self._current.loaded is not None:
596
+ catalog_pointer_record_count = (
597
+ self._current.loaded.reminder_middleware.sources.archive_pointer.record_count_state()
598
+ )
599
+ if catalog_pointer_record_count is not None:
600
+ preserved_state = preserved_state or {}
601
+ preserved_state[CATALOG_POINTER_RECORD_COUNT_STATE_KEY] = catalog_pointer_record_count
602
+
603
+ old_pending_switch = (
604
+ self._current.loaded.reminder_middleware.sources.profile_switch.snapshot_pending_switch()
605
+ if self._current.loaded is not None
606
+ else None
607
+ )
608
+ is_consecutive = old_pending_switch is not None
609
+
610
+ if preserved_state is not None and new_profile.name != old_profile_name:
611
+ switches = preserved_state.setdefault("agent_profile_switches", [])
612
+ new_label = new_profile.display_name or new_profile.name
613
+ if is_consecutive and switches:
614
+ switches[-1]["to"] = new_profile.name
615
+ switches[-1]["to_display"] = new_label
616
+ switches[-1]["timestamp"] = datetime.now(UTC).isoformat()
617
+ if switches[-1]["from"] == new_profile.name:
618
+ switches.pop()
619
+ else:
620
+ switches.append(
621
+ {
622
+ "from": old_profile_name,
623
+ "to": new_profile.name,
624
+ "from_display": old_display_name or old_profile_name,
625
+ "to_display": new_label,
626
+ "at_message_index": len(preserved_state.get("messages", [])),
627
+ "timestamp": datetime.now(UTC).isoformat(),
628
+ },
629
+ )
630
+
631
+ if reload_hook_manager:
632
+ staged_hook_manager = await self.build_hook_manager(
633
+ project_root=hook_manager_root,
634
+ project_hooks_enabled=new_loaded.settings.project_hooks_enabled,
635
+ )
636
+
637
+ staged = self.stage(
638
+ loaded=staged_loaded if staged_loaded is not None else self._settings_handle.loaded,
639
+ agent_profile=new_profile,
640
+ workspace=workspace if workspace is not None else old_workspace,
641
+ hook_manager=staged_hook_manager,
642
+ )
643
+ # The commit inside ``_build_agent`` records ``new_profile`` with the
644
+ # executor it configured — and installs ``preserved_state`` into that
645
+ # executor in the same synchronous block. Doing either here, after the
646
+ # awaited post-commit steps, would let a cancellation strand the new
647
+ # executor under the old profile's name or publish it with an empty
648
+ # history for the next save to persist.
649
+ completed = await self.build(new_profile, staged, preserved_history=preserved_state)
650
+ replaced = self.install(completed)
651
+ await self.release(replaced)
652
+ if staged_hook_manager is not old_hook_manager:
653
+ await self.commit_hook_manager_replacement(old_hook_manager)
654
+ hook_manager_replacement_committed = True
655
+ if old_service_session_id and self._current.loaded is not None:
656
+ if _can_reuse_responses_service_session(
657
+ old_agent_profile_fingerprint=old_agent_profile_fingerprint,
658
+ new_agent_profile_fingerprint=self._current.manifest.agent_profile_fingerprint,
659
+ old_model_profile_fingerprint=old_model_profile_fingerprint,
660
+ new_model_profile_fingerprint=self._current.manifest.model_profile_fingerprint,
661
+ old_model_profile=old_model_profile,
662
+ new_model_profile=self._current.manifest.active_profile,
663
+ old_model_base_url=old_model_base_url,
664
+ new_model_base_url=self._current.manifest.runtime_details.model.base_url,
665
+ old_workspace=old_workspace,
666
+ new_workspace=self._session.workspace,
667
+ old_storage_enabled=old_service_session_storage_enabled,
668
+ new_storage_enabled=self._current.loaded.bindings.backend.service_session_storage_enabled,
669
+ ):
670
+ self._current.loaded.bindings.backend.service_session_id = old_service_session_id
671
+ else:
672
+ self._current.loaded.bindings.backend.service_session_id = ""
673
+ await self._bus.publish(
674
+ Warning(
675
+ code="service_session_incompatible",
676
+ message=(
677
+ "The previous OpenAI Responses service session is not compatible with "
678
+ "the active agent profile, workspace, model profile, service endpoint, "
679
+ "or storage is disabled. "
680
+ f"{APP_DISPLAY_NAME} will continue from local history only."
681
+ ),
682
+ display_message=_CONSTRUCTION_SERVICE_SESSION_INCOMPATIBLE.bind(
683
+ app_name=APP_DISPLAY_NAME,
684
+ ),
685
+ session_id=self._session.session_id,
686
+ )
687
+ )
688
+ if new_profile.name != old_profile_name and self._current.loaded is not None:
689
+ new_label = new_profile.display_name or new_profile.name
690
+ set_profile_switch = self._current.loaded.reminder_middleware.sources.profile_switch.set_profile_switch
691
+ if is_consecutive and old_pending_switch is not None:
692
+ if old_pending_switch["from"] != new_label:
693
+ set_profile_switch(old_pending_switch["from"], new_label)
694
+ else:
695
+ old_label = old_display_name or old_profile_name
696
+ set_profile_switch(old_label, new_label)
697
+
698
+ if preserved_state is not None:
699
+ last_usage = preserved_state.get("last_usage")
700
+ if isinstance(last_usage, dict):
701
+ preserved_meta = SessionRuntimeMetadata.from_state_dict(preserved_state)
702
+ self._session.runtime_meta.last_usage_details = preserved_meta.last_usage_details
703
+ # Route through the ordered chain so a sub-agent UsageUpdate
704
+ # still queued from the prior build can't overtake this
705
+ # post-restart snapshot.
706
+ self._usage_publisher.enqueue_usage_event(self._usage_publisher.make_usage_event())
707
+ except Exception as exc:
708
+ # Nothing was staged onto the engine, so there is nothing to roll
709
+ # back: a pre-commit failure left every live field untouched, and a
710
+ # post-commit failure keeps the committed build. Only the losing
711
+ # hook manager still needs retiring.
712
+ if not hook_manager_replacement_committed:
713
+ await self.settle_staged_hook_manager(old=old_hook_manager, staged=staged_hook_manager)
714
+ self._permits.finish_agent_load()
715
+ await self.publish_load_failed(operation=operation, profile=new_profile, exc=exc)
716
+ raise
717
+ except BaseException:
718
+ if not hook_manager_replacement_committed:
719
+ await self.settle_staged_hook_manager(old=old_hook_manager, staged=staged_hook_manager)
720
+ self._permits.finish_agent_load()
721
+ raise
722
+
723
+ message_count = len(preserved_state.get("messages", [])) if preserved_state else 0
724
+ switched_sub_agent_names = (
725
+ self._current.loaded.sub_agent_tools.tool_names()
726
+ if self._current.loaded is not None and self._current.loaded.sub_agent_tools
727
+ else []
728
+ )
729
+ self._permits.finish_agent_load()
730
+ await self._bus.publish(
731
+ AgentLoadFinished(
732
+ operation=operation,
733
+ agent_profile=new_profile.name,
734
+ display_name=new_profile.display_name or new_profile.name,
735
+ session_id=self._session.session_id,
736
+ ),
737
+ )
738
+ await self._bus.publish(
739
+ ProfileSwitched(
740
+ from_profile=old_profile_name,
741
+ to_profile=new_profile.name,
742
+ from_display_name=old_display_name or old_profile_name,
743
+ to_display_name=new_profile.display_name or new_profile.name,
744
+ message_count=message_count,
745
+ model_profile_id=self._current.manifest.active_profile.id
746
+ if self._current.manifest.active_profile
747
+ else "",
748
+ max_context_tokens=self._current.manifest.active_profile.max_context_tokens
749
+ if self._current.manifest.active_profile
750
+ else 0,
751
+ session_id=self._session.session_id,
752
+ tool_names=list(self._current.manifest.tool_names),
753
+ skill_names=list(self._current.manifest.skill_names),
754
+ sub_agent_tool_names=switched_sub_agent_names,
755
+ memory_files=list(self._current.manifest.memory_files),
756
+ runtime_details=copy.deepcopy(self._current.manifest.runtime_details),
757
+ ),
758
+ )
759
+ await self._trajectory_recorder.profile_switched(
760
+ kind=ProfileKind.AGENT,
761
+ from_fingerprint=old_agent_profile_fingerprint,
762
+ to_fingerprint=self._current.manifest.agent_profile_fingerprint,
763
+ )
764
+ await self._trajectory_recorder.profile_switched(
765
+ kind=ProfileKind.MODEL,
766
+ from_fingerprint=old_model_profile_fingerprint,
767
+ to_fingerprint=self._current.manifest.model_profile_fingerprint,
768
+ )
769
+ if workspace is not None:
770
+ await self._bus.publish(
771
+ WorkspaceUpdated(
772
+ primary_cwd=workspace.primary_cwd,
773
+ working_dirs=[d.path for d in workspace.working_dirs],
774
+ reference_files=workspace.reference_files,
775
+ session_id=self._session.session_id,
776
+ ),
777
+ )
778
+
779
+ def install(self, completed: CompletedBuild) -> ReplacedBuild:
780
+ """Install the completed candidate before releasing the previous owner."""
781
+ old_loaded = self._current.loaded
782
+ old_coordinator = self._session.mutation_coordinator
783
+ staged = completed.staged
784
+ # The commit: everything the build was configured from goes live in one
785
+ # synchronous block together with the executor it produced — no await
786
+ # between the first assignment and the last, so a concurrent reader sees
787
+ # the old build entirely or the new one entirely, never a mixture.
788
+ self._settings_handle.install_prepared(completed.settings)
789
+ self._session.install_build(
790
+ staged, mutation_tracker=completed.mutation_tracker, todo_tracker=completed.todo_tracker
791
+ )
792
+ self._current.loaded = completed.loaded
793
+ self._current.manifest = completed.manifest
794
+ self._history.bind(completed.loaded.bindings.backend.history_state)
795
+ self._workspace_change_tracker.apply_retarget(completed.workspace_retarget)
796
+ self._permits.advance_build_generation()
797
+ return ReplacedBuild(
798
+ loaded=old_loaded,
799
+ coordinator=old_coordinator if old_coordinator is not staged.mutation_coordinator else None,
800
+ )
801
+
802
+ async def release(self, replaced: ReplacedBuild) -> None:
803
+ old_prepared = replaced.loaded.prepared if replaced.loaded is not None else None
804
+ old_coordinator = replaced.coordinator
805
+ cancelled: asyncio.CancelledError | None = None
806
+ if old_coordinator is not None:
807
+ # Settings turned coordination off (or the session re-targeted): the
808
+ # replaced registry file is stamped closed only now that the build is
809
+ # committed, so peers keep warning about us until we truly stop.
810
+ try:
811
+ await construction._close_coordinator(old_coordinator, reason="replaced")
812
+ except asyncio.CancelledError as exc:
813
+ cancelled = exc
814
+ try:
815
+ await self._cleanup_replaced_build_resources(old_prepared)
816
+ except asyncio.CancelledError as exc:
817
+ cancelled = exc
818
+ if cancelled is not None:
819
+ raise cancelled
820
+
821
+ def apply_skill_refresh(
822
+ self, *, skill_names: list[str], skill_sources: dict[str, list[str]], skill_details: list[RuntimeSkillDetails]
823
+ ) -> AgentManifest:
824
+ """Install a new manifest containing the refreshed skill catalog."""
825
+ manifest = self._current.manifest.with_skill_refresh(
826
+ skill_names=skill_names, skill_sources=skill_sources, skill_details=skill_details
827
+ )
828
+ self._current.manifest = manifest
829
+ return manifest
830
+
831
+ def hold_injection_notification(self, task: asyncio.Task[None]) -> None:
832
+ """Hold the latest detached injection delivery task."""
833
+ self._injection_notify_tasks.add(task)
834
+ task.add_done_callback(self._injection_notify_tasks.discard)
835
+
836
+ def _profile_with_mcp_overlay(self, profile: AgentProfile) -> AgentProfile:
837
+ """Return a per-session profile copy with ephemeral MCP servers appended."""
838
+ if not self._mcp_overlay:
839
+ return profile
840
+ effective = copy.deepcopy(profile)
841
+ effective.tools.mcp.extend(copy.deepcopy(self._mcp_overlay))
842
+ return effective
843
+
844
+ async def _cleanup_replaced_build_resources(self, old_prepared: PreparedAgent | None) -> None:
845
+ """Drain the displaced owner; borrowed engine fields have no close authority.
846
+
847
+ Prepared keeps its cleanup task reachable and completes every release even
848
+ if this post-install waiter is cancelled repeatedly.
849
+ """
850
+ if old_prepared is not None and old_prepared is not (
851
+ self._current.loaded.prepared if self._current.loaded is not None else None
852
+ ):
853
+ await old_prepared.aclose()
854
+
855
+ async def release_current(self) -> None:
856
+ """Close the installed owner and then clear the live resource slot."""
857
+ loaded = self._current.loaded
858
+ if loaded is not None:
859
+ await loaded.aclose()
860
+ self._current.loaded = None
861
+
862
+ async def close(self) -> None:
863
+ """Close and replace the engine-level MCP connection cache."""
864
+ await self._mcp_cache.close_all()
865
+ self._mcp_cache = MCPConnectionCache()