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,857 @@
1
+ # Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
2
+
3
+ """Streaming progress for headless text mode: one stderr line per meaningful event.
4
+
5
+ Progress goes to stderr and the final answer to stdout, so a redirected answer
6
+ stays exactly what it was. Lines are only ever appended — nothing animates or
7
+ moves the cursor — and Rich decides colour alone, so a log file or a pipe with
8
+ ``FORCE_COLOR`` reads the same as a terminal. Granularity is one line per
9
+ event, never per token.
10
+
11
+ Every dynamic string is display text from a model, a tool, a remote agent or
12
+ the user's configuration: it is sanitized before it reaches the console.
13
+
14
+ Progress never costs the run its answer: a stderr that fails or whose reader
15
+ has gone ends progress, and the answer still reaches stdout.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import contextlib
21
+ import errno
22
+ import logging
23
+ import os
24
+ import sys
25
+ from collections.abc import AsyncIterator, Callable, Iterable, Mapping
26
+ from dataclasses import dataclass, field
27
+ from datetime import datetime
28
+ from typing import IO, Any, Final
29
+
30
+ from rich.console import Console
31
+ from rich.text import Text
32
+
33
+ from chrys.app.cli.tool_summary import (
34
+ ToolSummary,
35
+ clamp,
36
+ failure_reason,
37
+ hosted_status_is_terminal,
38
+ is_todo_tool,
39
+ summarize_tool,
40
+ tool_failed,
41
+ )
42
+ from chrys.app.tui.util.context_pressure import context_pressure_message
43
+ from chrys.foundation.errors.display import DISPLAY_WITH_HINT
44
+ from chrys.foundation.events.bus import EventBus
45
+ from chrys.foundation.events.types import (
46
+ AGENT_LOAD_PHASE_MCP,
47
+ AGENT_LOAD_STATUS_RUNNING,
48
+ WORKFLOW_NOTICE_DATA_DROPPED,
49
+ WORKFLOW_OUTPUT_EMIT,
50
+ AgentLoadFinished,
51
+ AgentLoadProgress,
52
+ CompactionFinished,
53
+ CompactionStarted,
54
+ Event,
55
+ InvocationAborted,
56
+ InvocationCascadeAborted,
57
+ InvocationCompactionCommitted,
58
+ InvocationCompactionFinished,
59
+ InvocationCompactionStarted,
60
+ InvocationContextPressure,
61
+ InvocationEvent,
62
+ InvocationMessage,
63
+ InvocationPaused,
64
+ InvocationPresentationAttemptAccepted,
65
+ InvocationPresentationAttemptRejected,
66
+ InvocationProgress,
67
+ InvocationResumed,
68
+ InvocationRetryAttempt,
69
+ InvocationStarted,
70
+ InvocationToolCallArgsUpdated,
71
+ InvocationToolCallProgress,
72
+ InvocationToolCallResult,
73
+ InvocationToolCallStart,
74
+ InvocationToolCallStatusUpdated,
75
+ TodoListUpdated,
76
+ ToolCompacted,
77
+ Warning,
78
+ WorkflowLoopIteration,
79
+ WorkflowNodeOutput,
80
+ WorkflowNodeStateChanged,
81
+ WorkflowRunAccepted,
82
+ WorkflowRunNotice,
83
+ WorkflowRunStarted,
84
+ )
85
+ from chrys.foundation.i18n import MessageRef
86
+ from chrys.foundation.i18n.formatting import sanitize_legacy_scalar, sanitize_terminal_block
87
+ from chrys.foundation.models.invocations import InvocationOrigin
88
+ from chrys.foundation.models.todos import TodoItem
89
+ from chrys.foundation.platform.files import surrogate_safe_text
90
+ from chrys.foundation.util.session_ids import session_short_id
91
+
92
+ logger = logging.getLogger(__name__)
93
+
94
+ QUIET_NOTICES: Final = frozenset({WORKFLOW_NOTICE_DATA_DROPPED})
95
+ """Notice codes retained in run events and records but omitted from CLI progress."""
96
+
97
+ _MESSAGE_LIMIT: Final = 240
98
+ _DIM: Final = "dim"
99
+ _OK: Final = "green"
100
+ _FAIL: Final = "red"
101
+ _NOTE: Final = "yellow"
102
+ _ACCENT: Final = "cyan"
103
+
104
+ _NAMED_EVENTS: Final = (
105
+ InvocationStarted,
106
+ InvocationMessage,
107
+ InvocationPresentationAttemptAccepted,
108
+ InvocationPresentationAttemptRejected,
109
+ InvocationToolCallStart,
110
+ InvocationToolCallArgsUpdated,
111
+ InvocationToolCallStatusUpdated,
112
+ InvocationToolCallProgress,
113
+ InvocationToolCallResult,
114
+ InvocationProgress,
115
+ InvocationCompactionStarted,
116
+ InvocationCompactionFinished,
117
+ InvocationCompactionCommitted,
118
+ InvocationRetryAttempt,
119
+ InvocationPaused,
120
+ InvocationResumed,
121
+ InvocationCascadeAborted,
122
+ InvocationAborted,
123
+ )
124
+ """Invocation events that carry the agent's display name."""
125
+
126
+ type Render = Callable[[MessageRef], str]
127
+ type Part = str | tuple[str, str]
128
+ type CallKey = tuple[str, int, str]
129
+
130
+
131
+ class _ProgressConsole(Console):
132
+ """A stderr console whose broken pipe is an ordinary write failure.
133
+
134
+ Rich's default policy points *stdout* at devnull and exits, which would drop the answer still to come.
135
+ """
136
+
137
+ def on_broken_pipe(self) -> None:
138
+ self.quiet = True
139
+ raise BrokenPipeError(errno.EPIPE, "The progress stream was closed.")
140
+
141
+
142
+ def progress_console() -> Console:
143
+ """The stderr console progress writes to; built per command so a replaced ``sys.stderr`` is honoured."""
144
+ return _ProgressConsole(file=sys.stderr, highlight=False, markup=False, emoji=False, soft_wrap=True)
145
+
146
+
147
+ def _discard(stream: IO[str]) -> None:
148
+ """Point a stream whose reader has gone at devnull, so later writes and the exit-time flush succeed."""
149
+ try:
150
+ descriptor = stream.fileno()
151
+ devnull = os.open(os.devnull, os.O_WRONLY)
152
+ except OSError, ValueError:
153
+ return
154
+ try:
155
+ os.dup2(devnull, descriptor)
156
+ except OSError:
157
+ pass
158
+ finally:
159
+ os.close(devnull)
160
+
161
+
162
+ def guarded(handle: Callable[[Event], None]) -> Callable[[Event], None]:
163
+ """*handle* as a stream callback: a display failure is logged and dropped, never costing the run its answer."""
164
+
165
+ def on_event(event: Event) -> None:
166
+ try:
167
+ handle(event)
168
+ except Exception:
169
+ logger.exception("Headless progress could not show %s", type(event).__name__)
170
+
171
+ return on_event
172
+
173
+
174
+ def format_duration(seconds: float) -> str:
175
+ """``0.4s``, ``12.3s``, ``2m 14s``, ``1h 05m``."""
176
+ seconds = max(seconds, 0.0)
177
+ if round(seconds, 1) < 60:
178
+ return f"{seconds:.1f}s"
179
+ total = round(seconds)
180
+ if total < 3600:
181
+ return f"{total // 60}m {total % 60:02d}s"
182
+ return f"{total // 3600}h {total % 3600 // 60:02d}m"
183
+
184
+
185
+ def format_tokens(count: int) -> str:
186
+ """``950``, ``41k``, ``1.2M``."""
187
+ if count < 1000:
188
+ return str(count)
189
+ # Choose the unit after rounding: 999,600 is 1.0M, not 1000k.
190
+ thousands = round(count / 1000)
191
+ if thousands < 1000:
192
+ return f"{thousands}k"
193
+ return f"{count / 1_000_000:.1f}M"
194
+
195
+
196
+ def display_path(path: str) -> str:
197
+ """A path for display: surrogates neutralized, the home directory shortened to ``~``."""
198
+ text = surrogate_safe_text(path)
199
+ home = surrogate_safe_text(os.path.expanduser("~"))
200
+ if home not in ("", "~") and os.path.normcase(text).startswith(os.path.normcase(home)):
201
+ rest = text[len(home) :]
202
+ if not rest or rest[0] in (os.sep, "/"):
203
+ text = f"~{rest}"
204
+ return sanitize_legacy_scalar(text)
205
+
206
+
207
+ def _scalar(value: str, limit: int = _MESSAGE_LIMIT) -> str:
208
+ return clamp(_unclamped(value), limit)
209
+
210
+
211
+ def _short_id(value: str) -> str:
212
+ # Restored and persisted ids are not guaranteed hexadecimal; the raw id stays the identity elsewhere.
213
+ return _unclamped(session_short_id(value))
214
+
215
+
216
+ def _unclamped(value: str) -> str:
217
+ """One display line kept whole: warnings, notices and emits are already bounded by their producers."""
218
+ return sanitize_legacy_scalar(surrogate_safe_text(value)).strip()
219
+
220
+
221
+ def _error_lines(error: str) -> list[str]:
222
+ """A node error's non-empty lines, sanitized for the terminal; its cause often sits below the first."""
223
+ return [line for line in sanitize_terminal_block(surrogate_safe_text(error)).splitlines() if line.strip()]
224
+
225
+
226
+ class ProgressWriter:
227
+ """Appends styled lines to the console and remembers what the last line was.
228
+
229
+ The memory serves two rules: a tool's end line is compact only when it
230
+ directly follows that tool's start line, and a warning is printed once per
231
+ ``(code, message)`` across everything this command has already reported.
232
+
233
+ A ``quiet`` writer (``--quiet``) prints only the warnings: every mode renders
234
+ the same events, so quiet output never misses a warning progress would show.
235
+ """
236
+
237
+ def __init__(
238
+ self, console: Console, *, reported_warnings: Iterable[tuple[str, str]] = (), quiet: bool = False
239
+ ) -> None:
240
+ self._console = console
241
+ self._last_start: CallKey | None = None
242
+ self._warnings = set(reported_warnings)
243
+ self._quiet = quiet
244
+ self._failed = False
245
+
246
+ def line(self, *parts: Part, start_of: CallKey | None = None) -> None:
247
+ if not self._quiet:
248
+ self._print(parts)
249
+ self._last_start = start_of
250
+
251
+ def always(self, *parts: Part) -> None:
252
+ """A line every mode prints, quiet included: warnings and the run's captured output."""
253
+ self._print(parts)
254
+ self._last_start = None
255
+
256
+ def _print(self, parts: Iterable[Part]) -> None:
257
+ if self._failed:
258
+ return
259
+ text = Text()
260
+ for part in parts:
261
+ if isinstance(part, tuple):
262
+ text.append(part[0], style=part[1])
263
+ else:
264
+ text.append(part)
265
+ text.rstrip()
266
+ try:
267
+ self._console.print(text)
268
+ except (OSError, ValueError) as exc:
269
+ # A failing or closed stderr ends progress, never the run.
270
+ self._failed = True
271
+ if isinstance(exc, BrokenPipeError):
272
+ _discard(self._console.file)
273
+ logger.debug("Headless progress stopped writing", exc_info=True)
274
+
275
+ def follows_start_of(self, key: CallKey) -> bool:
276
+ return self._last_start == key
277
+
278
+ def mark_reported(self, code: str, message: str) -> None:
279
+ self._warnings.add((code, message))
280
+
281
+ def unreported(self, warnings: Iterable[Warning]) -> list[Warning]:
282
+ """The warnings not reported yet, now marked reported."""
283
+ fresh: list[Warning] = []
284
+ for warning in warnings:
285
+ key = (warning.code, warning.message)
286
+ if key not in self._warnings:
287
+ self._warnings.add(key)
288
+ fresh.append(warning)
289
+ return fresh
290
+
291
+ def warning(self, message: str, *, lead: str = "") -> None:
292
+ self.always(lead, ("Warning: ", _NOTE), _unclamped(message))
293
+
294
+
295
+ @dataclass(slots=True)
296
+ class _Call:
297
+ summary: ToolSummary
298
+ tool_name: str
299
+ tool_kind: str
300
+ hosted_family: str
301
+ started: datetime
302
+ lead: str
303
+ nested: bool
304
+ silent: bool
305
+ ended: bool = False
306
+ failure_shown: bool = False
307
+
308
+
309
+ @dataclass(slots=True)
310
+ class _Provisional:
311
+ segments: dict[str, str] = field(default_factory=dict)
312
+
313
+
314
+ def _depth(origin: InvocationOrigin) -> int:
315
+ depth = 0
316
+ current: InvocationOrigin | None = origin
317
+ while current is not None:
318
+ if current.kind == "sub_agent":
319
+ depth += 1
320
+ current = current.parent
321
+ return depth
322
+
323
+
324
+ class InvocationActivity:
325
+ """Renders ``Invocation*`` facts: tools, sub-agents, retries, compaction, pressure and commentary.
326
+
327
+ ``prefix(origin)`` returns the lead every line for that origin starts with
328
+ (``""`` for a chat turn, ``" [node] "`` for a workflow node), or ``None``
329
+ when the origin is not this command's to show.
330
+ """
331
+
332
+ def __init__(self, writer: ProgressWriter, *, prefix: Callable[[InvocationOrigin], str | None], render: Render):
333
+ self._writer = writer
334
+ self._prefix = prefix
335
+ self._render = render
336
+ self._calls: dict[CallKey, _Call] = {}
337
+ self._names: dict[str, str] = {}
338
+ self._provisional: dict[tuple[str, int, str], _Provisional] = {}
339
+ self.top_level_calls = 0
340
+
341
+ def handle(self, event: InvocationEvent) -> None:
342
+ origin = event.origin
343
+ prefix = self._prefix(origin)
344
+ if prefix is None:
345
+ return
346
+ name = self._agent_name(event)
347
+ nested = _depth(origin) > 0
348
+ lead = f"{prefix}{' ' * _depth(origin)}↳ {name} " if nested else prefix
349
+ match event:
350
+ case InvocationStarted():
351
+ return
352
+ case InvocationToolCallStart():
353
+ self._tool_start(event, lead, nested=nested)
354
+ case InvocationToolCallArgsUpdated():
355
+ self._tool_args(event)
356
+ case InvocationToolCallStatusUpdated():
357
+ self._tool_status(event)
358
+ case InvocationToolCallResult():
359
+ self._tool_result(event)
360
+ case InvocationMessage():
361
+ self._message(event, lead, nested=nested)
362
+ case InvocationPresentationAttemptAccepted():
363
+ self._accept(event, lead, nested=nested)
364
+ case InvocationPresentationAttemptRejected():
365
+ self._provisional.pop((origin.invocation_id, origin.attempt, event.attempt_id), None)
366
+ case InvocationRetryAttempt():
367
+ self._retry(event, lead)
368
+ case InvocationPaused():
369
+ reason = self._display(event.last_error_display, event.last_error, event.last_error_hint)
370
+ self._writer.line(lead, ("paused", _NOTE), f": {reason}" if reason else "")
371
+ case InvocationResumed():
372
+ self._writer.line(lead, ("resumed", _DIM))
373
+ case InvocationAborted():
374
+ self._writer.line(lead, ("✗ ", _FAIL), _scalar(event.last_error) or "failed")
375
+ case InvocationCascadeAborted():
376
+ self._writer.line(lead, ("cancelled", _DIM))
377
+ case InvocationCompactionStarted():
378
+ self._status(lead, "Compacting conversation…", nested=nested)
379
+ case InvocationCompactionFinished():
380
+ if event.outcome == "canceled":
381
+ self._status(lead, "Compaction interrupted", nested=nested)
382
+ elif event.outcome == "failed":
383
+ self._compaction_failed(lead, event.failure_reason)
384
+ case InvocationCompactionCommitted():
385
+ self._status(lead, "Conversation compacted", nested=nested)
386
+ case InvocationContextPressure():
387
+ message = self._render(context_pressure_message(event.reason, source=event.source))
388
+ self._writer.warning(message, lead=lead)
389
+ case _:
390
+ return
391
+
392
+ def _agent_name(self, event: InvocationEvent) -> str:
393
+ # Registered from any event that names its agent (InvocationStarted first), so an event without the field
394
+ # (context pressure) still resolves through its origin.
395
+ invocation_id = event.origin.invocation_id
396
+ if isinstance(event, _NAMED_EVENTS) and event.agent_name.strip():
397
+ self._names[invocation_id] = _scalar(event.agent_name, 60)
398
+ return self._names.get(invocation_id, "sub-agent")
399
+
400
+ def _status(self, lead: str, text: str, *, nested: bool) -> None:
401
+ if nested:
402
+ self._writer.line(lead, (text[0].lower() + text[1:], _DIM))
403
+ else:
404
+ self._writer.line(lead, ("• ", _ACCENT), text)
405
+
406
+ def _compaction_failed(self, lead: str, reason: str) -> None:
407
+ cause = _scalar(reason, 120)
408
+ self._writer.warning(f"compaction failed ({cause})" if cause else "compaction failed", lead=lead)
409
+
410
+ def _display(self, reference: MessageRef | None, fallback: str, hint: MessageRef | None = None) -> str:
411
+ if reference is None:
412
+ return _scalar(fallback)
413
+ message = self._render(reference)
414
+ if hint is not None:
415
+ message = self._render(DISPLAY_WITH_HINT.bind(message=message, hint=self._render(hint)))
416
+ return _scalar(message)
417
+
418
+ # -- tools -------------------------------------------------------------------------------
419
+
420
+ @staticmethod
421
+ def _key(origin: InvocationOrigin, call_id: str) -> CallKey:
422
+ # Call ids repeat across invocations and across a workflow node's attempts.
423
+ return (origin.invocation_id, origin.attempt, call_id)
424
+
425
+ def _tool_start(self, event: InvocationToolCallStart, lead: str, *, nested: bool) -> None:
426
+ key = self._key(event.origin, event.call_id)
427
+ summary = summarize_tool(event.tool_name, event.tool_kind, event.args, hosted_family=event.hosted_family)
428
+ call = self._calls.get(key)
429
+ if call is not None:
430
+ # ACP re-emits a start whenever a call's title, kind or input changes, and once more before its
431
+ # result: the open call keeps its first timestamp and only learns the newer description.
432
+ call.summary = summary
433
+ call.tool_name, call.tool_kind = event.tool_name, event.tool_kind
434
+ call.hosted_family = event.hosted_family or call.hosted_family
435
+ return
436
+ silent = is_todo_tool(event.tool_name, event.tool_kind)
437
+ self._calls[key] = _Call(
438
+ summary=summary,
439
+ tool_name=event.tool_name,
440
+ tool_kind=event.tool_kind,
441
+ hosted_family=event.hosted_family,
442
+ started=event.timestamp,
443
+ lead=lead,
444
+ nested=nested,
445
+ silent=silent,
446
+ )
447
+ if silent:
448
+ return
449
+ if not nested:
450
+ self.top_level_calls += 1
451
+ self._writer.line(lead, ("→ ", _ACCENT), *self._describe(summary), start_of=key)
452
+
453
+ def _tool_args(self, event: InvocationToolCallArgsUpdated) -> None:
454
+ call = self._calls.get(self._key(event.origin, event.call_id))
455
+ if call is None:
456
+ return
457
+ call.summary = summarize_tool(
458
+ event.tool_name or call.tool_name,
459
+ event.tool_kind or call.tool_kind,
460
+ event.args,
461
+ hosted_family=event.hosted_family or call.hosted_family,
462
+ )
463
+
464
+ def _tool_status(self, event: InvocationToolCallStatusUpdated) -> None:
465
+ call = self._calls.get(self._key(event.origin, event.call_id))
466
+ status = event.status or event.provider_status
467
+ if call is None or call.ended or not hosted_status_is_terminal(status):
468
+ return
469
+ result_text = event.metadata.get("result_text")
470
+ self._end(
471
+ call,
472
+ self._key(event.origin, event.call_id),
473
+ result=result_text if isinstance(result_text, str) else "",
474
+ metadata=event.metadata,
475
+ provider_status=status,
476
+ ended_at=event.timestamp,
477
+ duration_ms=0,
478
+ )
479
+
480
+ def _tool_result(self, event: InvocationToolCallResult) -> None:
481
+ key = self._key(event.origin, event.call_id)
482
+ call = self._calls.pop(key, None)
483
+ if call is None:
484
+ return
485
+ if not call.ended:
486
+ self._end(
487
+ call,
488
+ key,
489
+ result=event.result,
490
+ metadata=event.metadata,
491
+ provider_status=event.provider_status,
492
+ ended_at=event.timestamp,
493
+ duration_ms=event.duration_ms,
494
+ )
495
+ return
496
+ # A hosted call already ended on its terminal status; its result only matters when it contradicts a
497
+ # success that was shown.
498
+ failed = tool_failed(
499
+ event.result,
500
+ event.metadata,
501
+ tool_name=call.tool_name,
502
+ tool_kind=call.tool_kind,
503
+ hosted_family=call.hosted_family or event.hosted_family,
504
+ provider_status=event.provider_status,
505
+ )
506
+ if failed and not call.failure_shown:
507
+ reason = failure_reason(event.result, event.metadata, hosted_family=call.hosted_family)
508
+ self._end_line(call, failed=True, reason=reason, elapsed=None, compact=False)
509
+
510
+ def _end(
511
+ self,
512
+ call: _Call,
513
+ key: CallKey,
514
+ *,
515
+ result: str,
516
+ metadata: dict[str, object],
517
+ provider_status: str,
518
+ ended_at: datetime,
519
+ duration_ms: int,
520
+ ) -> None:
521
+ call.ended = True
522
+ failed = tool_failed(
523
+ result,
524
+ metadata,
525
+ tool_name=call.tool_name,
526
+ tool_kind=call.tool_kind,
527
+ hosted_family=call.hosted_family,
528
+ provider_status=provider_status,
529
+ )
530
+ if not failed and (call.silent or call.nested):
531
+ return
532
+ elapsed = duration_ms / 1000 if duration_ms > 0 else (ended_at - call.started).total_seconds()
533
+ reason = failure_reason(result, metadata, hosted_family=call.hosted_family) if failed else ""
534
+ compact = not call.nested and self._writer.follows_start_of(key)
535
+ self._end_line(call, failed=failed, reason=reason, elapsed=elapsed, compact=compact)
536
+
537
+ def _end_line(self, call: _Call, *, failed: bool, reason: str, elapsed: float | None, compact: bool) -> None:
538
+ call.failure_shown = call.failure_shown or failed
539
+ mark = ("✗ ", _FAIL) if failed else ("✓ ", _OK)
540
+ tail: list[Part] = []
541
+ if failed:
542
+ tail.append(reason)
543
+ if elapsed is not None:
544
+ tail.append((format_duration(elapsed), _DIM))
545
+ joined: list[Part] = []
546
+ for index, part in enumerate(tail):
547
+ if index:
548
+ joined.append((" · ", _DIM))
549
+ joined.append(part)
550
+ indent = "" if call.nested else " "
551
+ if compact:
552
+ self._writer.line(call.lead, indent, mark, *joined)
553
+ return
554
+ separator: list[Part] = [(" · ", _DIM)] if joined else []
555
+ self._writer.line(call.lead, indent, mark, *self._describe(call.summary), *separator, *joined)
556
+
557
+ @staticmethod
558
+ def _describe(summary: ToolSummary) -> list[Part]:
559
+ if not summary.detail:
560
+ return [summary.label]
561
+ return [f"{summary.label.ljust(5)} ", summary.detail]
562
+
563
+ # -- commentary --------------------------------------------------------------------------
564
+
565
+ def _message(self, event: InvocationMessage, lead: str, *, nested: bool) -> None:
566
+ if nested or not event.is_intermediate:
567
+ # Child prose stays private, and the final answer goes to stdout.
568
+ return
569
+ if event.presentation is not None:
570
+ origin = event.origin
571
+ buffer = self._provisional.setdefault(
572
+ (origin.invocation_id, origin.attempt, event.presentation.attempt_id), _Provisional()
573
+ )
574
+ buffer.segments[event.presentation.segment_id] = event.text
575
+ return
576
+ self._commentary(lead, event.text)
577
+
578
+ def _accept(self, event: InvocationPresentationAttemptAccepted, lead: str, *, nested: bool) -> None:
579
+ origin = event.origin
580
+ buffer = self._provisional.pop((origin.invocation_id, origin.attempt, event.attempt_id), None)
581
+ if buffer is None or nested:
582
+ return
583
+ for segment_id in event.segment_ids:
584
+ text = buffer.segments.pop(segment_id, None)
585
+ if text is not None:
586
+ self._commentary(lead, text)
587
+
588
+ def _commentary(self, lead: str, text: str) -> None:
589
+ body = sanitize_terminal_block(surrogate_safe_text(text)).strip("\n")
590
+ if not body.strip():
591
+ return
592
+ for line in body.splitlines():
593
+ self._writer.line(lead, line)
594
+
595
+ # -- retries -----------------------------------------------------------------------------
596
+
597
+ def _retry(self, event: InvocationRetryAttempt, lead: str) -> None:
598
+ message = self._display(event.display_message, event.message, event.display_hint).rstrip(" .")
599
+ attempt = f" (attempt {event.attempt}/{event.max_attempts})" if event.max_attempts else ""
600
+ wait = f"Retrying in {event.delay_seconds}s{attempt}"
601
+ self._writer.line(lead, ("↻ ", _NOTE), f"{message}. {wait}" if message else wait)
602
+
603
+
604
+ @dataclass(frozen=True, slots=True)
605
+ class RunContext:
606
+ """What the ready line names beyond the agent: the model profile and the working directory."""
607
+
608
+ model: str = ""
609
+ workdir: str = ""
610
+
611
+
612
+ def _warning_text(warning: Warning, render: Render) -> str:
613
+ return render(warning.display_message) if warning.display_message is not None else warning.message
614
+
615
+
616
+ def _todo_line(items: list[TodoItem]) -> str:
617
+ if not items:
618
+ return "Todo list cleared"
619
+ completed = sum(1 for item in items if item.status == "completed")
620
+ head = f"Todo {completed}/{len(items)}"
621
+ active = next((item for item in items if item.status == "in_progress"), None)
622
+ if active is not None:
623
+ return f"{head} · → {_scalar(active.active_form or active.content, 160)}"
624
+ pending = next((item for item in items if item.status == "pending"), None)
625
+ if pending is not None:
626
+ return f"{head} · next: {_scalar(pending.content, 160)}"
627
+ return f"{head} · all done"
628
+
629
+
630
+ class TurnProgress:
631
+ """Progress for ``icode run``: startup, the turn's activity, and a closing summary."""
632
+
633
+ def __init__(self, writer: ProgressWriter, *, render: Render, context: Callable[[], RunContext]) -> None:
634
+ self._writer = writer
635
+ self._render = render
636
+ self._context = context
637
+ self._activity = InvocationActivity(writer, prefix=self._prefix, render=render)
638
+ self._ready = False
639
+ self._restored_name = ""
640
+
641
+ @staticmethod
642
+ def _prefix(origin: InvocationOrigin) -> str | None:
643
+ return "" if origin.root.kind == "turn" else None
644
+
645
+ def restoring(self, selector: str) -> None:
646
+ self._writer.line(("• ", _ACCENT), f"Restoring session {_scalar(selector, 80)}…")
647
+
648
+ @contextlib.asynccontextmanager
649
+ async def observe_restore(self, bus: EventBus) -> AsyncIterator[None]:
650
+ """Show MCP connections and warnings while a restore runs before the turn's own event stream."""
651
+
652
+ async def on_progress(event: AgentLoadProgress) -> None:
653
+ self._load_progress(event)
654
+
655
+ async def on_warning(event: Warning) -> None:
656
+ self.warnings([event])
657
+
658
+ async def on_finished(event: AgentLoadFinished) -> None:
659
+ self._restored_name = event.display_name or event.agent_profile
660
+
661
+ await bus.subscribe(AgentLoadProgress, on_progress)
662
+ await bus.subscribe(Warning, on_warning)
663
+ await bus.subscribe(AgentLoadFinished, on_finished)
664
+ try:
665
+ yield
666
+ finally:
667
+ await bus.unsubscribe(AgentLoadProgress, on_progress)
668
+ await bus.unsubscribe(Warning, on_warning)
669
+ await bus.unsubscribe(AgentLoadFinished, on_finished)
670
+
671
+ def restored(self, session_id: str) -> None:
672
+ self._ready = True
673
+ parts = [f"Restored session {_short_id(session_id)}"]
674
+ if self._restored_name:
675
+ parts.append(_scalar(self._restored_name, 80))
676
+ parts.extend(self._context_parts())
677
+ self._writer.line(("• ", _ACCENT), " · ".join(parts))
678
+
679
+ def warnings(self, warnings: Iterable[Warning]) -> None:
680
+ for warning in self._writer.unreported(warnings):
681
+ self._writer.warning(_warning_text(warning, self._render))
682
+
683
+ def handle(self, event: Event) -> None:
684
+ match event:
685
+ case InvocationEvent():
686
+ self._activity.handle(event)
687
+ case AgentLoadProgress():
688
+ self._load_progress(event)
689
+ case AgentLoadFinished():
690
+ if not self._ready:
691
+ self._ready = True
692
+ parts = [f"{_scalar(event.display_name or event.agent_profile, 80) or 'Agent'} ready"]
693
+ parts.extend(self._context_parts(event.session_id or ""))
694
+ self._writer.line(("• ", _ACCENT), " · ".join(parts))
695
+ case Warning():
696
+ self.warnings([event])
697
+ case CompactionStarted():
698
+ self._writer.line(("• ", _ACCENT), "Compacting conversation…")
699
+ case CompactionFinished():
700
+ # A generated note is not a committed compaction yet: phase-4 ToolCompacted reports that.
701
+ if event.outcome == "canceled":
702
+ self._writer.line(("• ", _ACCENT), "Compaction interrupted")
703
+ elif event.outcome == "failed":
704
+ cause = _scalar(event.failure_reason, 120)
705
+ self._writer.warning(f"compaction failed ({cause})" if cause else "compaction failed")
706
+ case ToolCompacted(phase="phase4"):
707
+ tokens = ""
708
+ if event.tokens_before > 0:
709
+ tokens = f" · {format_tokens(event.tokens_before)} → {format_tokens(event.tokens_after)} tokens"
710
+ self._writer.line(("• ", _ACCENT), f"Conversation compacted{tokens}")
711
+ case TodoListUpdated():
712
+ self._writer.line(_todo_line(event.items))
713
+ case _:
714
+ return
715
+
716
+ def _load_progress(self, event: AgentLoadProgress) -> None:
717
+ # Only a named server's own start: the collective event has no name, and a failure is reported by the
718
+ # adapter's mcp.connect_failed warning.
719
+ if event.phase != AGENT_LOAD_PHASE_MCP or event.status != AGENT_LOAD_STATUS_RUNNING:
720
+ return
721
+ server = _scalar(event.subject or event.server_name, 80)
722
+ if server:
723
+ self._writer.line(("• ", _ACCENT), f"Connecting MCP server {server}…")
724
+
725
+ def _context_parts(self, session_id: str = "") -> list[str]:
726
+ context = self._context()
727
+ parts: list[str] = []
728
+ if context.model:
729
+ parts.append(_scalar(context.model, 80))
730
+ if session_id:
731
+ parts.append(f"session {_short_id(session_id)}")
732
+ if context.workdir:
733
+ parts.append(display_path(context.workdir))
734
+ return parts
735
+
736
+ def succeeded(self, *, duration: float, session_id: str) -> None:
737
+ count = self._activity.top_level_calls
738
+ calls = f"{count} tool call" if count == 1 else f"{count} tool calls"
739
+ parts = ["Done", format_duration(duration), calls]
740
+ if session_id:
741
+ parts.append(f"session {_short_id(session_id)}")
742
+ self._writer.line()
743
+ self._writer.line(("✓ ", _OK), " · ".join(parts))
744
+
745
+
746
+ class WorkflowProgress:
747
+ """Progress for ``icode workflow run``: the run header, node states, agent-node activity and a summary."""
748
+
749
+ def __init__(self, writer: ProgressWriter, *, render: Render) -> None:
750
+ self._writer = writer
751
+ self._render = render
752
+ self._nodes: dict[str, str] = {}
753
+ self._running: dict[tuple[str, int], datetime] = {}
754
+ self._session_id = ""
755
+ self._activity = InvocationActivity(writer, prefix=self._prefix, render=render)
756
+
757
+ def _prefix(self, origin: InvocationOrigin) -> str | None:
758
+ node_id = self._nodes.get(origin.root.invocation_id) if origin.root.kind == "workflow_node" else None
759
+ return None if node_id is None else f" [{node_id}] "
760
+
761
+ def starting(self, workflow_id: str, *, checking: bool = False) -> None:
762
+ verb = "Checking" if checking else "Starting"
763
+ self._writer.line(("• ", _ACCENT), f"{verb} workflow {_scalar(workflow_id, 80)}…")
764
+
765
+ def warnings(self, warnings: Iterable[Warning]) -> None:
766
+ for warning in self._writer.unreported(warnings):
767
+ self._writer.warning(_warning_text(warning, self._render))
768
+
769
+ def reported(self, code: str, message: str) -> None:
770
+ """Record a warning the command printed itself, so the run's stream does not repeat it."""
771
+ self._writer.mark_reported(code, message)
772
+
773
+ def handle(self, event: Event) -> None:
774
+ match event:
775
+ case InvocationEvent():
776
+ self._activity.handle(event)
777
+ case WorkflowRunAccepted():
778
+ self._session_id = event.session_id or ""
779
+ case WorkflowRunStarted():
780
+ self._header(event)
781
+ case WorkflowNodeStateChanged():
782
+ self._node_state(event)
783
+ case WorkflowNodeOutput(kind=kind) if kind == WORKFLOW_OUTPUT_EMIT:
784
+ self._writer.line(f" [{_scalar(event.node_id, 80)}] {_unclamped(event.summary_text)}")
785
+ case WorkflowLoopIteration():
786
+ self._writer.line(
787
+ ("↻ ", _NOTE),
788
+ f"[{_scalar(event.loop_id, 80)}] iteration {event.iteration}: {_scalar(event.verdict, 40)}",
789
+ )
790
+ case WorkflowRunNotice(code=code) if code not in QUIET_NOTICES:
791
+ self._writer.line(("Notice: ", _NOTE), _unclamped(event.message))
792
+ case Warning():
793
+ self.warnings([event])
794
+ case _:
795
+ return
796
+
797
+ def _header(self, event: WorkflowRunStarted) -> None:
798
+ workflow_id = _scalar(event.workflow_id, 80)
799
+ title = _scalar(event.title, 120)
800
+ name = f"{title} ({workflow_id})" if title and title != workflow_id else workflow_id
801
+ parts = [f"Workflow {name}", f"run {_short_id(event.run_id)}"]
802
+ if self._session_id:
803
+ parts.append(f"session {_short_id(self._session_id)}")
804
+ self._writer.line((" · ".join(parts), "bold"))
805
+
806
+ def _node_state(self, event: WorkflowNodeStateChanged) -> None:
807
+ node = f"[{_scalar(event.node_id, 80)}]"
808
+ error_lines = _error_lines(event.error)
809
+ error = error_lines[0].strip() if error_lines else ""
810
+ attempt_key = (event.activation_id, event.attempt)
811
+ match event.state:
812
+ case "running":
813
+ if event.invocation_id:
814
+ self._nodes[event.invocation_id] = _scalar(event.node_id, 80)
815
+ self._running[attempt_key] = event.timestamp
816
+ attempt = f" (attempt {event.attempt})" if event.attempt > 1 else ""
817
+ self._writer.line(("▸ ", _ACCENT), f"{node} running{attempt}")
818
+ case "completed":
819
+ started = self._running.pop(attempt_key, None)
820
+ duration: list[Part] = []
821
+ if started is not None:
822
+ duration = [(" · ", _DIM), (format_duration((event.timestamp - started).total_seconds()), _DIM)]
823
+ self._writer.line(("✓ ", _OK), f"{node} completed", *duration)
824
+ case "failed":
825
+ self._running.pop(attempt_key, None)
826
+ self._writer.line(("✗ ", _FAIL), f"{node} failed", f": {error}" if error else "")
827
+ self._error_detail(error_lines)
828
+ case "retrying" | "awaiting_retry":
829
+ self._running.pop(attempt_key, None)
830
+ state = event.state.replace("_", " ")
831
+ self._writer.line(("↻ ", _NOTE), f"{node} {state}", f": {error}" if error else "")
832
+ self._error_detail(error_lines)
833
+ case "skipped" | "cancelled":
834
+ self._running.pop(attempt_key, None)
835
+ self._writer.line(("· ", _DIM), f"{node} {event.state}")
836
+ case _:
837
+ return
838
+
839
+ def _error_detail(self, error_lines: list[str]) -> None:
840
+ for line in error_lines[1:]:
841
+ self._writer.line(" ", line.rstrip())
842
+
843
+ def captured(self, diagnostics: Mapping[str, Any]) -> None:
844
+ """The run's captured load output and output outside nodes, and a failure to read it; it precedes the summary."""
845
+ for key, label in (("load", "Load output"), ("native", "Output outside nodes")):
846
+ captured = diagnostics.get(key, {})
847
+ if captured.get("text"):
848
+ self._writer.always(f"{label}:")
849
+ for line in sanitize_terminal_block(surrogate_safe_text(captured["text"])).splitlines():
850
+ self._writer.always(line)
851
+ if captured.get("truncated") or captured.get("dropped_bytes"):
852
+ self._writer.always(f"{label}: some output was omitted by the capture limit.")
853
+ if diagnostics.get("error"):
854
+ self._writer.always(_unclamped(diagnostics["error"]))
855
+
856
+ def succeeded(self, *, duration: float) -> None:
857
+ self._writer.line(("✓ ", _OK), "Workflow completed", (" · ", _DIM), format_duration(duration))