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,994 @@
1
+ # Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
2
+
3
+ """Kernel pass attempts, task ownership and synchronous retry ports.
4
+
5
+ Local calls keep the kernel wire retry lane; stored calls use the existing
6
+ whole-run retry loop. Caller policy, presentation and pass lifecycle arrive
7
+ through explicit ports. No caller shell or disk writer is owned here.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import asyncio
13
+ import contextlib
14
+ import logging
15
+ import time
16
+ from dataclasses import dataclass
17
+ from enum import Enum
18
+ from typing import TYPE_CHECKING, Any, Protocol, TypedDict, TypeGuard, cast
19
+
20
+ from chrys.foundation.errors import clean_error_message, is_retryable
21
+ from chrys.foundation.retry import (
22
+ HistorySnapshot,
23
+ RetryAttemptInfo,
24
+ StreamRetryLoop,
25
+ StreamStall,
26
+ StreamStallExhausted,
27
+ restore_message_properties,
28
+ snapshot_message_properties,
29
+ )
30
+ from chrys.foundation.trajectory.context import TRAJECTORY_CONTEXT_KWARG, trajectory_scope
31
+ from chrys.foundation.trajectory.envelope import Link, MeasurementSource, measurement
32
+ from chrys.foundation.trajectory.event_types import EventType as TrajectoryEventType
33
+ from chrys.foundation.trajectory.event_types import ModelRunEndReason, RetryMode, RetryReason
34
+ from chrys.foundation.trajectory.ids import new_analytics_id
35
+ from chrys.kernel import is_retry_boundary_update, resolve_storage_mode_and_handles, wire_progress_scope
36
+ from chrys.service.agent_middleware.response_validation import (
37
+ RetryableResponseValidationError,
38
+ hosted_commits_from_error,
39
+ )
40
+ from chrys.service.context.providers.history import PRE_OUTPUT_HISTORY_LEN_STATE_KEY
41
+
42
+ if TYPE_CHECKING:
43
+ from collections.abc import AsyncIterator, Awaitable, Callable, Coroutine, Mapping, Sequence
44
+
45
+ from chrys.foundation.trajectory.context import TrajectoryContext
46
+ from chrys.kernel import Agent, AgentResponse, AgentResponseUpdate, AgentSession, Message, ResponseStream
47
+ from chrys.kernel.loop import StallExhaustedAction
48
+ from chrys.kernel.middleware import ChatMiddleware, FunctionMiddleware
49
+
50
+ logger = logging.getLogger(__name__)
51
+
52
+
53
+ class AgentRunKwargs(TypedDict, total=False):
54
+ """Keyword arguments forwarded unchanged to ``Agent.run``."""
55
+
56
+ session: AgentSession
57
+ middleware: Sequence[ChatMiddleware | FunctionMiddleware]
58
+ options: Mapping[str, Any]
59
+ compaction_strategy: Any
60
+ tokenizer: Any
61
+ client_kwargs: Mapping[str, Any]
62
+
63
+
64
+ def is_string_keyed_dict(value: object) -> TypeGuard[dict[str, Any]]:
65
+ """Narrow internal kwargs dictionaries whose producers use string keys."""
66
+ return isinstance(value, dict)
67
+
68
+
69
+ def _validation_retry_exemption(exc: BaseException) -> RetryAttemptInfo | None:
70
+ """Expose the middleware-owned stored validation budget to the retry loop.
71
+
72
+ The exemption is bounded because response validation terminally gives up
73
+ when its carried retry budget or identical-reason guard is reached.
74
+ """
75
+ if not isinstance(exc, RetryableResponseValidationError):
76
+ return None
77
+ exemption = exc.exemption
78
+ return RetryAttemptInfo(
79
+ reason=str(exc),
80
+ attempt=exemption.attempt,
81
+ max_attempts=exemption.max_attempts,
82
+ delay_seconds=exemption.delay_seconds,
83
+ )
84
+
85
+
86
+ def has_live_continuation_token(run_kwargs: Mapping[str, object] | None) -> bool:
87
+ """True when the retry-owned options still reference a live background response."""
88
+ if run_kwargs is None:
89
+ return False
90
+ options = run_kwargs.get("options")
91
+ return is_string_keyed_dict(options) and options.get("continuation_token") is not None
92
+
93
+
94
+ def drop_continuation_token(run_kwargs: AgentRunKwargs) -> None:
95
+ """Forget the retry-owned options' background response, so the next request starts a new one."""
96
+ options = run_kwargs.get("options")
97
+ if is_string_keyed_dict(options):
98
+ options.pop("continuation_token", None)
99
+
100
+
101
+ def continuation_token_observer_for(run_kwargs: AgentRunKwargs) -> Callable[[Any], None]:
102
+ """Mirror the kernel's live continuation token into the retry-owned options.
103
+
104
+ A transient failure while polling a stored background response must
105
+ resume that response on the next whole-run attempt — the token has to
106
+ live in the options the retry loop re-issues, not only in the kernel's
107
+ attempt-local copy. Writes go through ``run_kwargs`` at call time so a
108
+ handle-stripping restore that replaces the options dict cannot orphan
109
+ the observer.
110
+ """
111
+
112
+ def _observe(token: Any) -> None:
113
+ if token is None:
114
+ drop_continuation_token(run_kwargs)
115
+ return
116
+ raw_options = run_kwargs.get("options")
117
+ options = raw_options if is_string_keyed_dict(raw_options) else None
118
+ if options is None:
119
+ options = {}
120
+ run_kwargs["options"] = options
121
+ options["continuation_token"] = token
122
+
123
+ return _observe
124
+
125
+
126
+ def _run_retry_reason(exc: BaseException) -> str:
127
+ """Closed reason code for a whole-run retry decision."""
128
+ if isinstance(exc, StreamStall | StreamStallExhausted):
129
+ return RetryReason.STREAM_STALL
130
+ if isinstance(exc, RetryableResponseValidationError):
131
+ return RetryReason.VALIDATION_REJECTED
132
+ return RetryReason.TRANSIENT_ERROR
133
+
134
+
135
+ class WireRetryPolicyAdapter:
136
+ """Adapt caller configuration and callbacks to the kernel WireRetryPolicy.
137
+
138
+ Budgets and backoff accept values (the TurnBindings's per-run snapshot) or
139
+ readers (the child controller's live configuration). No budget or stall
140
+ action is defaulted across those policies. An absent pre-retry callback
141
+ means no injection transaction; an absent hosted probe remains None.
142
+ """
143
+
144
+ def __init__(
145
+ self,
146
+ *,
147
+ max_retries: int | Callable[[], int],
148
+ stall_timeout_seconds: float | None,
149
+ stall_max_retries: int | Callable[[], int],
150
+ stall_exhausted_action: StallExhaustedAction,
151
+ backoff_schedule: tuple[int, ...] | Callable[[], tuple[int, ...]],
152
+ interrupted: Callable[[], bool],
153
+ interruptible_sleep: Callable[[int], Awaitable[bool]],
154
+ publish_retry: Callable[[str, int, int, int, BaseException], Awaitable[None]],
155
+ prepare_retry: Callable[[], None] | None = None,
156
+ hosted_commits_in_flight: Callable[[], tuple[str, ...]] | None = None,
157
+ ) -> None:
158
+ self._max_retries = max_retries
159
+ self.stall_timeout_seconds = stall_timeout_seconds
160
+ self._stall_max_retries = stall_max_retries
161
+ self.stall_exhausted_action = stall_exhausted_action
162
+ self.backoff_schedule = backoff_schedule
163
+ self.interrupted = interrupted
164
+ self.interruptible_sleep = interruptible_sleep
165
+ self.publish_retry = publish_retry
166
+ self.prepare_retry = prepare_retry
167
+ # Describes only the current wire attempt, never the pass total.
168
+ self.hosted_commits_in_flight = hosted_commits_in_flight
169
+
170
+ @property
171
+ def max_retries(self) -> int:
172
+ return self._max_retries if isinstance(self._max_retries, int) else self._max_retries()
173
+
174
+ @max_retries.setter
175
+ def max_retries(self, value: int) -> None:
176
+ self._max_retries = value
177
+
178
+ @property
179
+ def stall_max_retries(self) -> int:
180
+ return self._stall_max_retries if isinstance(self._stall_max_retries, int) else self._stall_max_retries()
181
+
182
+ @stall_max_retries.setter
183
+ def stall_max_retries(self, value: int) -> None:
184
+ self._stall_max_retries = value
185
+
186
+ def backoff_seconds(self, attempt: int) -> int:
187
+ backoff = self.backoff_schedule if isinstance(self.backoff_schedule, tuple) else self.backoff_schedule()
188
+ if not backoff:
189
+ return 0
190
+ return backoff[min(attempt, len(backoff) - 1)]
191
+
192
+ def is_retryable(self, exc: BaseException) -> bool:
193
+ return is_retryable(exc)
194
+
195
+ def is_interrupted(self) -> bool:
196
+ return self.interrupted()
197
+
198
+ async def sleep(self, seconds: int) -> bool:
199
+ return await self.interruptible_sleep(seconds)
200
+
201
+ async def on_retry(
202
+ self,
203
+ message: str,
204
+ attempt: int,
205
+ max_attempts: int,
206
+ delay_seconds: int,
207
+ exc: BaseException,
208
+ ) -> None:
209
+ await self.publish_retry(message, attempt, max_attempts, delay_seconds, exc)
210
+
211
+ def before_retry(self) -> None:
212
+ if self.prepare_retry is not None:
213
+ self.prepare_retry()
214
+
215
+
216
+ class RetryParticipant(Protocol):
217
+ """Caller transaction participating in a whole pass and retry rollback."""
218
+
219
+ def begin_retry(self) -> None: ...
220
+ def end_retry(self) -> None: ...
221
+ def restore_for_retry(self) -> None: ...
222
+
223
+
224
+ class InterruptProbe(Protocol):
225
+ """Read the caller's synchronous interrupt latch."""
226
+
227
+ @property
228
+ def is_interrupted(self) -> bool: ...
229
+
230
+
231
+ class StreamObserver(Protocol):
232
+ """Observe raw updates and publish caller projection before finalize."""
233
+
234
+ def on_update(self, update: AgentResponseUpdate) -> None: ...
235
+ async def on_retry_boundary(self) -> None: ...
236
+ async def before_finalize(self) -> None: ...
237
+
238
+
239
+ @dataclass(frozen=True, slots=True)
240
+ class RestoreAndFallback:
241
+ """Restore the exhausted stream, reject its presentation, then block once."""
242
+
243
+ reject_stream: Callable[[str], Awaitable[None]]
244
+
245
+
246
+ @dataclass(frozen=True, slots=True)
247
+ class KeepAndRaise:
248
+ """Keep the exhausted attempt for the shell's pause projection."""
249
+
250
+
251
+ type StallExhaustionPolicy = RestoreAndFallback | KeepAndRaise
252
+
253
+
254
+ class RetryBoundaryPolicy(Enum):
255
+ """Keep the caller's scheduling observation at an empty retry boundary."""
256
+
257
+ SKIP = "skip"
258
+ OBSERVE = "observe"
259
+
260
+
261
+ class BlockingCallTiming(Enum):
262
+ """Task context in which the blocking run awaitable is constructed."""
263
+
264
+ IN_ATTEMPT_TASK = "in_attempt_task"
265
+ BEFORE_ATTEMPT_TASK = "before_attempt_task"
266
+
267
+
268
+ @dataclass(frozen=True, slots=True)
269
+ class AttemptRecipe:
270
+ """Conversation-bound policy choices; no shared controller callbacks.
271
+
272
+ history_state is consumed by the shell to construct HistoryRollback;
273
+ AttemptRunner does not read it.
274
+ """
275
+
276
+ stall_exhaustion: StallExhaustionPolicy
277
+ stall_error: Callable[[float], StreamStall]
278
+ retry_boundary: RetryBoundaryPolicy
279
+ blocking_call_timing: BlockingCallTiming
280
+ history_state: Callable[[], dict[str, Any]]
281
+ before_attempt: Callable[[], None] | None
282
+
283
+
284
+ @dataclass(frozen=True, slots=True)
285
+ class WireRecipe:
286
+ """The main pass snapshot or child live-reader wire-policy recipe."""
287
+
288
+ max_retries: int | Callable[[], int]
289
+ stall_timeout_seconds: float
290
+ stall_max_retries: int | Callable[[], int]
291
+ stall_exhausted_action: StallExhaustedAction
292
+ backoff_schedule: tuple[int, ...] | Callable[[], tuple[int, ...]]
293
+ interrupted: Callable[[], bool]
294
+ interruptible_sleep: Callable[[int], Awaitable[bool]]
295
+ publish_retry: Callable[[str, int, int, int, BaseException], Awaitable[None]]
296
+ prepare_retry: Callable[[], None] | None
297
+ hosted_commits_in_flight: Callable[[], tuple[str, ...]] | None
298
+
299
+ def build(self) -> WireRetryPolicyAdapter:
300
+ return WireRetryPolicyAdapter(
301
+ max_retries=self.max_retries,
302
+ stall_timeout_seconds=self.stall_timeout_seconds,
303
+ stall_max_retries=self.stall_max_retries,
304
+ stall_exhausted_action=self.stall_exhausted_action,
305
+ backoff_schedule=self.backoff_schedule,
306
+ interrupted=self.interrupted,
307
+ interruptible_sleep=self.interruptible_sleep,
308
+ publish_retry=self.publish_retry,
309
+ prepare_retry=self.prepare_retry,
310
+ hosted_commits_in_flight=self.hosted_commits_in_flight,
311
+ )
312
+
313
+
314
+ @dataclass(slots=True)
315
+ class AttemptTaskHandle:
316
+ """Sole L0 attempt task reference; Turn flags belong to the Turn shell."""
317
+
318
+ task: asyncio.Task[AgentResponse[Any]] | None = None
319
+
320
+ @property
321
+ def active(self) -> bool:
322
+ return self.task is not None and not self.task.done()
323
+
324
+ def cancel(self) -> None:
325
+ if self.task is not None and not self.task.done():
326
+ self.task.cancel()
327
+
328
+
329
+ class HistoryRollback:
330
+ """Synchronous history rollback plus an opaque caller-state participant.
331
+
332
+ Snapshot/restore callbacks run inline, without exception interception or
333
+ await gaps. In particular a failed caller restore stops before after_restore,
334
+ retry notification, sleep or another model attempt.
335
+ """
336
+
337
+ def __init__(
338
+ self,
339
+ session: AgentSession,
340
+ *,
341
+ snapshot_caller: Callable[[], object],
342
+ restore_caller: Callable[[object], None],
343
+ history_state: Callable[[], dict[str, Any]],
344
+ ) -> None:
345
+ self._session = session
346
+ self._snapshot_caller = snapshot_caller
347
+ self._restore_caller = restore_caller
348
+ self._history_state = history_state
349
+
350
+ def snapshot(self) -> HistorySnapshot:
351
+ """Snapshot messages and compressed block count for rollback.
352
+
353
+ Used before an ``agent.run()`` that may need retry. Captures
354
+ ``compressed_msgs`` length alongside messages so ``restore``
355
+ can undo cross-turn compressions (``_compress_state``) that run
356
+ mid-loop, and each message's ``additional_properties`` so in-place
357
+ annotations from the rolled-back attempt (exclusion flags,
358
+ summarized-by markers) can be reverted exactly.
359
+ """
360
+ history_state = self._history_state()
361
+ messages = list(history_state.get("messages", []))
362
+ return HistorySnapshot(
363
+ messages=messages,
364
+ compressed_count=len(history_state.get("compressed_msgs", [])),
365
+ service_session_id=self._session.service_session_id or "",
366
+ message_properties=snapshot_message_properties(messages),
367
+ pre_output_history_len=history_state.get(PRE_OUTPUT_HISTORY_LEN_STATE_KEY),
368
+ caller_state=self._snapshot_caller(),
369
+ )
370
+
371
+ def restore(self, snapshot: HistorySnapshot) -> None:
372
+ """Restore history from a snapshot, including compressed block rollback.
373
+
374
+ Undoes ``_compress_state`` list replacements, removes orphaned
375
+ ``CompressedBlock`` entries added during the rolled-back run, and
376
+ restores each message's ``additional_properties`` exactly as captured
377
+ — clearing the rolled-back attempt's compaction marks while keeping
378
+ marks that legitimately predate the attempt.
379
+
380
+ Used for retry rollback.
381
+ """
382
+ history_state = self._history_state()
383
+ restored_messages = list(snapshot.messages)
384
+ history_state["messages"] = restored_messages
385
+ self._session.service_session_id = snapshot.service_session_id or None
386
+
387
+ # Undo compressed block additions from the rolled-back run.
388
+ compressed: list = history_state.get("compressed_msgs", [])
389
+ if len(compressed) > snapshot.compressed_count:
390
+ del compressed[snapshot.compressed_count :]
391
+
392
+ if snapshot.pre_output_history_len is None:
393
+ history_state.pop(PRE_OUTPUT_HISTORY_LEN_STATE_KEY, None)
394
+ else:
395
+ history_state[PRE_OUTPUT_HISTORY_LEN_STATE_KEY] = snapshot.pre_output_history_len
396
+ restore_message_properties(restored_messages, snapshot.message_properties)
397
+ self._restore_caller(snapshot.caller_state)
398
+
399
+
400
+ class ModelRunTrace:
401
+ """Attempt trace observer over the foundation sink port, never a writer owner.
402
+
403
+ The caller supplies first-run causal links. The terminal is queued with
404
+ emit_soon and deliberately never awaits the sink acknowledgement.
405
+ """
406
+
407
+ def __init__(
408
+ self, *, interrupted: Callable[[], bool], service_side: Callable[[], bool], committed: Callable[[], bool]
409
+ ) -> None:
410
+ self.context: TrajectoryContext | None = None
411
+ self.first_run_links: tuple[Link, ...] = ()
412
+ self._interrupted = interrupted
413
+ self._service_side = service_side
414
+ self._committed = committed
415
+ self.reset()
416
+
417
+ def reset(self) -> None:
418
+ self._trajectory_last_run_id = None
419
+ self._run_attempts = 0
420
+ self._trajectory_retry_run_id = None
421
+
422
+ @contextlib.asynccontextmanager
423
+ async def run(self, run_kwargs: AgentRunKwargs, *, stream: bool) -> AsyncIterator[None]:
424
+ """Bind one ``model.run`` operation around one ``agent.run`` attempt.
425
+
426
+ The narrowed context rides two ways: ambiently (for tool middleware
427
+ and side calls, bound before the attempt task is created so the
428
+ task inherits it) and explicitly in ``client_kwargs`` for the
429
+ kernel loop, whose lazy provider streams resolve outside this
430
+ context.
431
+ """
432
+ base = self.context
433
+ if base is None:
434
+ yield
435
+ return
436
+ retry_run_id = self._trajectory_retry_run_id
437
+ run_id = retry_run_id or new_analytics_id()
438
+ context = base.with_run(run_id)
439
+ raw_client_kwargs = run_kwargs.get("client_kwargs")
440
+ run_kwargs["client_kwargs"] = {
441
+ **(raw_client_kwargs if raw_client_kwargs is not None else {}),
442
+ TRAJECTORY_CONTEXT_KWARG: context,
443
+ }
444
+ attempt_index = self._run_attempts
445
+ self._run_attempts += 1
446
+ previous_run_id = self._trajectory_last_run_id
447
+ started_ns = time.monotonic_ns()
448
+ sink = context.sink
449
+
450
+ def _finished_draft(outcome: str) -> Any:
451
+ return context.draft(
452
+ TrajectoryEventType.MODEL_RUN_FINISHED,
453
+ operation_id=run_id,
454
+ parent_operation_id=base.innermost_model_operation_id,
455
+ payload={
456
+ "outcome": outcome,
457
+ "duration_ms": max(0, (time.monotonic_ns() - started_ns) // 1_000_000),
458
+ },
459
+ measurements={"/payload/duration_ms": measurement(MeasurementSource.MONOTONIC_CLOCK, method_version=1)},
460
+ )
461
+
462
+ self._trajectory_last_run_id = run_id
463
+ links = self.first_run_links if attempt_index == 0 else ()
464
+
465
+ def _started_draft() -> Any:
466
+ return context.draft(
467
+ TrajectoryEventType.MODEL_RUN_STARTED,
468
+ operation_id=run_id,
469
+ parent_operation_id=base.innermost_model_operation_id,
470
+ payload={
471
+ "attempt_index": attempt_index,
472
+ "stream": stream,
473
+ "service_side_storage": self._service_side(),
474
+ "previous_run_operation_id": previous_run_id,
475
+ },
476
+ links=links,
477
+ )
478
+
479
+ if retry_run_id is not None:
480
+ self._trajectory_retry_run_id = None
481
+ try:
482
+ await sink.emit(
483
+ context.draft(
484
+ TrajectoryEventType.RETRY_STARTED,
485
+ operation_id=run_id,
486
+ payload={
487
+ "retry_mode": RetryMode.RUN,
488
+ "next_operation_id": run_id,
489
+ "previous_operation_id": previous_run_id,
490
+ },
491
+ )
492
+ )
493
+ except asyncio.CancelledError:
494
+ # retry.started already names this run. Entry never reaches
495
+ # the body, so queue its complete interrupted lifecycle before
496
+ # preserving cancellation, without waiting on another ack.
497
+ try:
498
+ sink.emit_soon(_started_draft())
499
+ sink.emit_soon(_finished_draft(ModelRunEndReason.INTERRUPTED))
500
+ except Exception:
501
+ logger.debug("Trajectory interrupted retry settlement failed", exc_info=True)
502
+ raise
503
+ except Exception:
504
+ logger.debug("Trajectory retry.started emit failed", exc_info=True)
505
+
506
+ try:
507
+ await sink.emit(_started_draft())
508
+ except asyncio.CancelledError:
509
+ # The writer commits the opening line before its shielded ack can
510
+ # be cancelled. Context-manager entry will not reach the body
511
+ # below, so settle the operation here before preserving cancel.
512
+ try:
513
+ sink.emit_soon(_finished_draft(ModelRunEndReason.INTERRUPTED))
514
+ except Exception:
515
+ logger.debug("Trajectory model.run.finished emit failed", exc_info=True)
516
+ raise
517
+ except Exception:
518
+ logger.debug("Trajectory model.run.started emit failed", exc_info=True)
519
+ outcome = ModelRunEndReason.COMPLETED
520
+ with trajectory_scope(context):
521
+ try:
522
+ yield
523
+ except asyncio.CancelledError:
524
+ outcome = ModelRunEndReason.INTERRUPTED
525
+ raise
526
+ except BaseException:
527
+ outcome = ModelRunEndReason.INTERRUPTED if self._interrupted() else ModelRunEndReason.FAILED
528
+ raise
529
+ finally:
530
+ draft = _finished_draft(outcome)
531
+ try:
532
+ # Queued, never awaited. The model has already answered by
533
+ # the time this runs and its task is gone, so an ack that
534
+ # waits out a slow writer holds the answer unpublished for
535
+ # that long — and a Stop pressed in that window finds
536
+ # nothing to cancel, marks the turn interrupted, and the
537
+ # finished answer is dropped. Cancellation cannot await
538
+ # here either: the scope is already unwinding. The line
539
+ # takes its sequence here, so this span is only left open
540
+ # when the process does not outlive the queue — and the
541
+ # turn still gets its terminal from the recorder's close.
542
+ sink.emit_soon(draft)
543
+ except Exception:
544
+ logger.debug("Trajectory model.run.finished emit failed", exc_info=True)
545
+
546
+ async def retry_scheduled(
547
+ self, *, exc: BaseException, delay_seconds: int, fallback_to_blocking: bool = False
548
+ ) -> None:
549
+ """Record a service-side whole-run retry decision (``retry.scheduled``)."""
550
+ context = self.context
551
+ if context is None:
552
+ return
553
+ retry_run_id = new_analytics_id()
554
+ self._trajectory_retry_run_id = retry_run_id
555
+ committed = self._committed()
556
+ try:
557
+ await context.sink.emit(
558
+ context.draft(
559
+ TrajectoryEventType.RETRY_SCHEDULED,
560
+ operation_id=retry_run_id,
561
+ payload={
562
+ "reason_code": _run_retry_reason(exc),
563
+ "delay_ms": max(0, int(delay_seconds * 1000)),
564
+ "retry_mode": RetryMode.RUN,
565
+ "previous_operation_id": self._trajectory_last_run_id,
566
+ "committed_work_present": committed,
567
+ "fallback_to_blocking": fallback_to_blocking,
568
+ },
569
+ )
570
+ )
571
+ except Exception:
572
+ logger.debug("Trajectory retry.scheduled emit failed", exc_info=True)
573
+
574
+
575
+ class AttemptRunner:
576
+ """Drive one kernel pass through caller-supplied synchronous/async ports.
577
+
578
+ Construction does not run hooks or mutate history. The shell enters this
579
+ runner only inside its pass try, after its once-per-pass hooks and barriers.
580
+ """
581
+
582
+ def __init__(
583
+ self,
584
+ *,
585
+ agent: Agent,
586
+ session: AgentSession,
587
+ handle: AttemptTaskHandle,
588
+ rollback: HistoryRollback,
589
+ retry_participant: RetryParticipant | None,
590
+ interrupt: InterruptProbe,
591
+ trace: ModelRunTrace,
592
+ stream_observer: Callable[[], StreamObserver] | None,
593
+ publish_retry: Callable[[str, int, int, int, BaseException], Awaitable[None]],
594
+ interruptible_sleep: Callable[[int], Awaitable[bool]],
595
+ max_retries: Callable[[], int],
596
+ backoff_schedule: Callable[[], tuple[int, ...]],
597
+ stream_timeout: Callable[[], float],
598
+ committed_count: Callable[[], int],
599
+ hosted_commits: Callable[[], tuple[str, ...]],
600
+ recipe: AttemptRecipe,
601
+ ) -> None:
602
+ self._agent = agent
603
+ self._session = session
604
+ self.handle = handle
605
+ self._rollback = rollback
606
+ self._retry_participant = retry_participant
607
+ self._interrupt = interrupt
608
+ self.trace = trace
609
+ self._stream_observer = stream_observer
610
+ self._publish_retry_notice = publish_retry
611
+ self._interruptible_sleep = interruptible_sleep
612
+ self._effective_max_retries = max_retries
613
+ self._backoff_schedule = backoff_schedule
614
+ self._stream_timeout = stream_timeout
615
+ self._committed_count = committed_count
616
+ self._hosted_commits_probe = hosted_commits
617
+ self._stall_exhaustion = recipe.stall_exhaustion
618
+ self._stall_error = recipe.stall_error
619
+ self._retry_boundary = recipe.retry_boundary
620
+ self._blocking_call_timing = recipe.blocking_call_timing
621
+ self._before_attempt = recipe.before_attempt
622
+
623
+ async def run(
624
+ self, message: list[Message], run_kwargs: AgentRunKwargs, *, stream: bool, service_side: bool
625
+ ) -> AgentResponse[Any]:
626
+ if stream:
627
+ if service_side:
628
+ return await self._stream_with_retry(message, run_kwargs)
629
+ if self._retry_participant is not None:
630
+ self._retry_participant.begin_retry()
631
+ try:
632
+ return await self._stream_single_attempt(message, run_kwargs, watchdog=False)
633
+ finally:
634
+ if self._retry_participant is not None:
635
+ self._retry_participant.end_retry()
636
+ return await self._run_blocking(message, run_kwargs, service_side=service_side)
637
+
638
+ def _may_retry_attempt(self, exc: BaseException, run_kwargs: Mapping[str, object] | None = None) -> bool:
639
+ """Whole-run retry gate: answered tool work must never re-execute.
640
+
641
+ Locally answered results are counted by the loop recorder. Provider-
642
+ hosted calls (hosted MCP, hosted shell) execute inside the failed
643
+ exchange itself and never reach the recorder — their evidence rides
644
+ on the raised validation error (the middleware swallows the invalid
645
+ response before the kernel loop sees it) or, for stalls and transport
646
+ drops mid-stream, on the middleware's run-scoped observation probe.
647
+ A live continuation token exempts the gate: the retry then resumes
648
+ the already-created background response instead of re-creating the
649
+ request, so nothing hosted runs twice.
650
+ """
651
+ if self._committed_count():
652
+ return False
653
+ hosted = hosted_commits_from_error(exc)
654
+ if not hosted:
655
+ hosted = self._hosted_commits_probe()
656
+ if hosted and not has_live_continuation_token(run_kwargs):
657
+ logger.warning(
658
+ "Not retrying failed attempt: provider-hosted tool call(s) already executed (%s)",
659
+ ", ".join(hosted),
660
+ )
661
+ return False
662
+ return True
663
+
664
+ def _restore_service_retry_inputs(self, run_kwargs: AgentRunKwargs) -> None:
665
+ """Replay failed-call injections and discard every stale service handle."""
666
+ raw_options = run_kwargs.get("options")
667
+ options = raw_options if is_string_keyed_dict(raw_options) else {}
668
+ if options.get("continuation_token") is None and self._retry_participant is not None:
669
+ # A live token means the retry resumes the already-created
670
+ # background response: the create that consumed the injections
671
+ # succeeded, so the consumed transaction (and its retained-message
672
+ # mirror for the terminal weave) must stay intact. Replaying here
673
+ # would strand the batch — polls skip consumption, then the
674
+ # terminal commit would destroy the held replay.
675
+ self._retry_participant.restore_for_retry()
676
+ self._session.service_session_id = None
677
+ raw_client_kwargs = run_kwargs.get("client_kwargs")
678
+ client_kwargs = raw_client_kwargs if is_string_keyed_dict(raw_client_kwargs) else {}
679
+ try:
680
+ force_stateless = bool(self._agent.client.FORCES_STATELESS)
681
+ except AttributeError:
682
+ force_stateless = False
683
+ resolution = resolve_storage_mode_and_handles(
684
+ options,
685
+ stores_by_default=True,
686
+ client_kwargs=client_kwargs,
687
+ force_stateless=force_stateless,
688
+ )
689
+ if raw_options is not None:
690
+ run_kwargs["options"] = resolution.options_without_handles
691
+ run_kwargs["client_kwargs"] = resolution.client_kwargs_without_handles
692
+
693
+ async def _run_agent(self, input: list[Message], run_kwargs: AgentRunKwargs) -> AgentResponse[Any]:
694
+ """Run agent.run() in a child task so interrupt() can cancel it.
695
+
696
+ Using a child task isolates cancellation: the parent task (and its
697
+ post-processing) continues normally even when the child is cancelled.
698
+ """
699
+
700
+ if self._before_attempt is not None:
701
+ self._before_attempt()
702
+
703
+ # Keep the run call and await inside the attempt task. AgentTelemetryLayer
704
+ # sets and resets non-streaming ContextVars inside its returned coroutine,
705
+ # so either call-timing mode below preserves their asyncio context.
706
+ async def _call() -> AgentResponse[Any]:
707
+ return await self._agent.run(input, stream=False, **run_kwargs)
708
+
709
+ async with self.trace.run(run_kwargs, stream=False):
710
+ if self._blocking_call_timing is BlockingCallTiming.BEFORE_ATTEMPT_TASK:
711
+ # Preserve the child's original call timing, including sync
712
+ # exceptions from run(). Kernel telemetry binds its ContextVars
713
+ # inside this coroutine, not while constructing it.
714
+ run = self._agent.run(input, stream=False, **run_kwargs)
715
+ self.handle.task = asyncio.create_task(cast("Coroutine[Any, Any, AgentResponse[Any]]", run))
716
+ else:
717
+ self.handle.task = asyncio.create_task(_call())
718
+ try:
719
+ return await self.handle.task
720
+ finally:
721
+ self.handle.task = None
722
+
723
+ async def _run_blocking(
724
+ self, message: list[Message], run_kwargs: AgentRunKwargs, *, service_side: bool
725
+ ) -> AgentResponse[Any]:
726
+ """Run locally with per-wire retry, or stored mode with outer retry."""
727
+ if self._retry_participant is not None:
728
+ self._retry_participant.begin_retry()
729
+ try:
730
+ if service_side:
731
+ loop = StreamRetryLoop(
732
+ max_retries=self._effective_max_retries(),
733
+ backoff_schedule=self._backoff_schedule(),
734
+ is_retryable=is_retryable,
735
+ snapshot_history=self._rollback.snapshot,
736
+ restore_history=self._rollback.restore,
737
+ publish_retry_attempt=self._publish_retry_attempt,
738
+ is_interrupted=lambda: self._interrupt.is_interrupted,
739
+ interruptible_sleep=self._interruptible_sleep,
740
+ clean_error_message=clean_error_message,
741
+ after_restore=lambda: self._restore_service_retry_inputs(run_kwargs),
742
+ may_retry=lambda exc: self._may_retry_attempt(exc, run_kwargs),
743
+ retry_exemption=_validation_retry_exemption,
744
+ )
745
+ result = await loop.run(lambda: self._run_agent(message, run_kwargs))
746
+ else:
747
+ result = await self._run_agent(message, run_kwargs)
748
+ finally:
749
+ if self._retry_participant is not None:
750
+ self._retry_participant.end_retry()
751
+
752
+ return result
753
+
754
+ async def _stream_with_retry(
755
+ self,
756
+ current_input: list[Message],
757
+ run_kwargs: AgentRunKwargs,
758
+ ) -> AgentResponse[Any]:
759
+ """Execute a streaming agent.run() with retry on transient errors.
760
+
761
+ Delegates the generic retry/backoff/rollback machinery to
762
+ :class:`StreamRetryLoop`. On exhausted stall retries falls back
763
+ to a non-streaming request for this turn. ``StreamRetryLoop``
764
+ captures its own history snapshot on entry, so callers no longer
765
+ pre-snapshot.
766
+ """
767
+ loop = StreamRetryLoop(
768
+ max_retries=self._effective_max_retries(),
769
+ backoff_schedule=self._backoff_schedule(),
770
+ is_retryable=is_retryable,
771
+ snapshot_history=self._rollback.snapshot,
772
+ restore_history=self._rollback.restore,
773
+ publish_retry_attempt=self._publish_retry_attempt,
774
+ is_interrupted=lambda: self._interrupt.is_interrupted,
775
+ interruptible_sleep=self._interruptible_sleep,
776
+ clean_error_message=clean_error_message,
777
+ restore_on_stall_exhaustion=isinstance(self._stall_exhaustion, RestoreAndFallback),
778
+ after_restore=lambda: self._restore_service_retry_inputs(run_kwargs),
779
+ may_retry=lambda exc: self._may_retry_attempt(exc, run_kwargs),
780
+ retry_exemption=_validation_retry_exemption,
781
+ )
782
+
783
+ async def _attempt() -> AgentResponse[Any]:
784
+ return await self._stream_single_attempt(current_input, run_kwargs)
785
+
786
+ if self._retry_participant is not None:
787
+ self._retry_participant.begin_retry()
788
+ try:
789
+ try:
790
+ return await loop.run(_attempt)
791
+ except StreamStallExhausted as exc:
792
+ if isinstance(self._stall_exhaustion, KeepAndRaise):
793
+ raise
794
+ # The retry loop restored failed stream state before this
795
+ # blocking replacement, including any consumed injection.
796
+ await self._stall_exhaustion.reject_stream("Streaming stalled; using blocking fallback")
797
+ await self.trace.retry_scheduled(exc=exc, delay_seconds=0, fallback_to_blocking=True)
798
+ return await self._run_agent(current_input, run_kwargs)
799
+ finally:
800
+ if self._retry_participant is not None:
801
+ self._retry_participant.end_retry()
802
+
803
+ async def _publish_retry_attempt(
804
+ self,
805
+ message: str,
806
+ attempt: int,
807
+ max_attempts: int,
808
+ delay_seconds: int,
809
+ exc: BaseException,
810
+ ) -> None:
811
+ """Record the whole-run retry before invoking the caller notice port."""
812
+ await self.trace.retry_scheduled(exc=exc, delay_seconds=delay_seconds)
813
+ await self._publish_retry_notice(message, attempt, max_attempts, delay_seconds, exc)
814
+
815
+ async def _stream_single_attempt(
816
+ self,
817
+ current_input: list[Message],
818
+ run_kwargs: AgentRunKwargs,
819
+ *,
820
+ watchdog: bool = True,
821
+ ) -> AgentResponse[Any]:
822
+ """Consume one stream in its owning task and finalize under the watchdog.
823
+
824
+ Retry boundaries are observed before ordinary updates. The caller's
825
+ final text projection runs after iterator completion, before finalize.
826
+ """
827
+ import time as _time
828
+
829
+ if self._before_attempt is not None:
830
+ self._before_attempt()
831
+
832
+ # Tracks the last point at which our per-chunk watchdog (re)armed
833
+ # its timer. Updated after each successful ``__anext__()``, on each
834
+ # progress report, and after the stream is fully drained (before
835
+ # the finalize wait). Used below to distinguish a genuine stall — our
836
+ # timer actually elapsed — from a ``TimeoutError`` bubbling up
837
+ # from inside the stream/transport (e.g. httpx or SDK internals),
838
+ # which would otherwise be mis-labelled "Stream stalled" and
839
+ # retried with the wrong error message.
840
+ last_wait_start = _time.monotonic()
841
+
842
+ async def _watched[T](awaitable: Awaitable[T]) -> T:
843
+ # Idle timing: work a pull waits on before its next chunk (a
844
+ # compaction pass and its LAST_WORDS side call) reports progress,
845
+ # and every report restarts the stall timer. The pull stays in
846
+ # this task: its telemetry ContextVars are set and reset here.
847
+ timeout = self._stream_timeout()
848
+ if timeout <= 0:
849
+ # wait_for stalls a non-positive timeout even when the pull
850
+ # would finish without suspending; asyncio.timeout does not.
851
+ return await asyncio.wait_for(awaitable, timeout=timeout)
852
+ event_loop = asyncio.get_running_loop()
853
+ watching = True
854
+ async with asyncio.timeout(timeout) as deadline:
855
+
856
+ def _on_progress() -> None:
857
+ nonlocal last_wait_start
858
+ # A task the pull spawned can report after the pull
859
+ # settled, or while the stall is already cancelling it.
860
+ if watching and not deadline.expired():
861
+ last_wait_start = _time.monotonic()
862
+ deadline.reschedule(event_loop.time() + timeout)
863
+
864
+ try:
865
+ with wire_progress_scope(_on_progress):
866
+ return await awaitable
867
+ finally:
868
+ watching = False
869
+
870
+ async def _iterate_and_finalize() -> AgentResponse[Any]:
871
+ nonlocal last_wait_start
872
+
873
+ # ``agent.run(stream=True)`` is invoked INSIDE this task so the
874
+ # OTel telemetry ContextVars (e.g.
875
+ # ``INNER_RESPONSE_TELEMETRY_CAPTURED_FIELDS``) are set and
876
+ # reset in the same asyncio context — the telemetry cleanup
877
+ # hook fires when the stream is consumed, which happens here.
878
+ # The streaming layer keeps the set-in-call / reset-in-finalizer
879
+ # shape; same-task creation remains the correct pattern here.
880
+ # Matches the blocking path (see ``_run_agent``).
881
+ stream: ResponseStream[AgentResponseUpdate, AgentResponse[Any]] = self._agent.run(
882
+ current_input, stream=True, **run_kwargs
883
+ )
884
+ completed = False
885
+
886
+ try:
887
+ observer = self._stream_observer() if self._stream_observer is not None else None
888
+
889
+ # Per-chunk watchdog: each __anext__() gets the stall timeout
890
+ # individually, so the timer resets whenever a chunk arrives
891
+ # (or the work before it reports progress).
892
+ # This measures stream-idle time (matching httpx read-timeout
893
+ # semantics) — tool executions between chunks don't count.
894
+ #
895
+ # When a ``function_call`` chunk is observed, the tool loop
896
+ # synchronously invokes the tool inside the NEXT ``__anext__()``
897
+ # call — so that await blocks for the entire tool duration.
898
+ # Tools (especially sub-agents) can legitimately run for
899
+ # minutes, which would trip a short stream-idle timeout. To
900
+ # avoid a false "Stream stalled" during tool execution we
901
+ # suspend the watchdog until the post-tool chunk arrives.
902
+ # User-initiated cancellation still works — it flows through
903
+ # the caller control port, which cancels the owned task handle.
904
+ expecting_tool_result = False
905
+ aiter = stream.__aiter__()
906
+ while True:
907
+ try:
908
+ if not watchdog or expecting_tool_result:
909
+ update = await aiter.__anext__()
910
+ else:
911
+ update = await _watched(aiter.__anext__())
912
+ except StopAsyncIteration:
913
+ break
914
+ last_wait_start = _time.monotonic()
915
+
916
+ if is_retry_boundary_update(update):
917
+ if observer is not None:
918
+ await observer.on_retry_boundary()
919
+ if self._retry_boundary is RetryBoundaryPolicy.SKIP:
920
+ continue
921
+
922
+ if observer is not None:
923
+ observer.on_update(update)
924
+ has_function_call = False
925
+ has_function_result = False
926
+ text_chunk = ""
927
+ for content in update.contents or []:
928
+ if content.type == "text" and content.text:
929
+ text_chunk += content.text
930
+ elif content.type == "function_call" and not content.informational_only:
931
+ has_function_call = True
932
+ elif content.type == "function_result":
933
+ has_function_result = True
934
+ if has_function_result or text_chunk:
935
+ expecting_tool_result = False
936
+ if has_function_call:
937
+ expecting_tool_result = True
938
+
939
+ await asyncio.sleep(0) # yield to event loop between chunks
940
+
941
+ if observer is not None:
942
+ await observer.before_finalize()
943
+
944
+ # Also guard finalize with the same per-chunk timeout — it
945
+ # shouldn't block once the iterator exhausts, but it's still
946
+ # an await on the same underlying stream.
947
+ last_wait_start = _time.monotonic()
948
+ if not watchdog:
949
+ response = await stream.get_final_response()
950
+ else:
951
+ response = await _watched(stream.get_final_response())
952
+ completed = True
953
+ return response
954
+ except TimeoutError:
955
+ if not watchdog:
956
+ raise
957
+ # ``asyncio.TimeoutError`` is the builtin ``TimeoutError`` on
958
+ # Python 3.11+, so a ``TimeoutError`` raised from *inside*
959
+ # ``__anext__()`` / ``get_final_response()`` (httpx or SDK
960
+ # internals) would look identical to our own ``wait_for``
961
+ # firing. Only treat it as a stall when the current watchdog
962
+ # window actually elapsed (with a small margin for scheduling
963
+ # jitter); otherwise re-raise so ``is_retryable`` classifies
964
+ # the original exception with its real message.
965
+ idle = _time.monotonic() - last_wait_start
966
+ if idle + 0.5 < self._stream_timeout():
967
+ raise
968
+ raise self._stall_error(self._stream_timeout()) from None
969
+ finally:
970
+ # A stream left before its final response is closed here, in
971
+ # this task: its cleanup hooks (the OTel ContextVar resets
972
+ # among them) must run before the attempt ends, in the context
973
+ # ``.run(stream=True)`` was called in. A pull that failed ran
974
+ # them already and one that was cancelled closed the stream; a
975
+ # cancel between chunks, a failing observer or a stall while
976
+ # finalizing leaves it open, and garbage collection would close
977
+ # only the bare generators under it, never its hooks.
978
+ if not completed:
979
+ try:
980
+ await stream.aclose()
981
+ except Exception:
982
+ logger.debug("Failed to close an abandoned attempt stream", exc_info=True)
983
+
984
+ # Wrap the iteration in a child task so ``interrupt()`` can cancel
985
+ # it. Without this, the streaming path has no task handle to
986
+ # cancel and user-initiated interrupts cannot stop an in-flight
987
+ # stream/tool — including long-running sub-agents. (The blocking
988
+ # path already does this in :meth:`_run_agent`.)
989
+ async with self.trace.run(run_kwargs, stream=True):
990
+ self.handle.task = asyncio.create_task(_iterate_and_finalize())
991
+ try:
992
+ return await self.handle.task
993
+ finally:
994
+ self.handle.task = None