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,951 @@
1
+ # Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
2
+
3
+ """Sub-agent event middleware — publishes InvocationToolCallStart/InvocationToolCallResult events."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import asyncio
8
+ import contextlib
9
+ import time
10
+ from datetime import UTC, datetime
11
+ from typing import TYPE_CHECKING, Any
12
+ from uuid import uuid4
13
+
14
+ from chrys.foundation.events.types import (
15
+ InvocationMessage,
16
+ InvocationPresentationAttemptAccepted,
17
+ InvocationPresentationAttemptRejected,
18
+ InvocationProgress,
19
+ InvocationToolCallArgsUpdated,
20
+ InvocationToolCallProgress,
21
+ InvocationToolCallResult,
22
+ InvocationToolCallStart,
23
+ InvocationToolCallStatusUpdated,
24
+ ProvisionalPresentation,
25
+ )
26
+ from chrys.foundation.hosted_tools import HOSTED_TOOL_DEFAULT_KIND_BY_FAMILY, HostedToolStatus
27
+ from chrys.foundation.io.result_content import extract_result_images, extract_result_text
28
+ from chrys.foundation.platform.paths import resolve_workspace_path
29
+ from chrys.foundation.tool_call_context import resolve_tool_call_context
30
+ from chrys.foundation.tool_execution_stamp import write_execution_stamp
31
+ from chrys.foundation.tool_kinds import KIND_SHELL, KIND_SUB_AGENT, get_tool_kind
32
+ from chrys.foundation.tool_result_metadata import (
33
+ TOOL_ERROR_KIND_METADATA_KEY,
34
+ TOOL_ERRORED_METADATA_KEY,
35
+ tool_payload_observation,
36
+ tool_result_metadata_failure_state,
37
+ )
38
+ from chrys.foundation.trajectory.context import bind_tool_operation, reset_tool_operation
39
+ from chrys.foundation.trajectory.event_types import ToolOutcome
40
+ from chrys.foundation.trajectory_timing import build_trajectory_timing, stamp_trajectory_timing
41
+ from chrys.foundation.util.sub_agent_context import (
42
+ SUB_AGENT_RESULT_COMMIT_CALLBACK_KEY,
43
+ SUB_AGENT_TRANSCRIPT_FINAL_TEXT_METADATA_KEY,
44
+ SubAgentParentResultMetadata,
45
+ sub_agent_parent_call_id,
46
+ sub_agent_parent_result_metadata,
47
+ )
48
+ from chrys.kernel._result_ceiling import preview_result_ceiling
49
+ from chrys.kernel.middleware import FunctionMiddleware
50
+ from chrys.service.agent_middleware._metadata_keys import (
51
+ _APPROVAL_REJECTED_KEY,
52
+ _SHORT_ID_LEN,
53
+ _SLEEP_INTERRUPTED_KEY,
54
+ _SLEEP_SKIPPED_KEY,
55
+ )
56
+ from chrys.service.agent_middleware.events.hook_dispatch import (
57
+ append_extra_context_to_result,
58
+ apply_before_tool_hooks,
59
+ final_tool_args,
60
+ fire_after_tool_hooks,
61
+ get_call_id,
62
+ get_provider_call_id,
63
+ get_tool_invocation_order,
64
+ is_mutable_tool_args,
65
+ set_call_id,
66
+ set_tool_invocation_order,
67
+ )
68
+ from chrys.service.agent_middleware.events.hosted_tools import (
69
+ FinalTextOp,
70
+ HostedPresentationBridge,
71
+ HostedToolArgsOp,
72
+ HostedToolProgressOp,
73
+ HostedToolResultOp,
74
+ HostedToolStartOp,
75
+ HostedToolStatusOp,
76
+ IntermediateTextOp,
77
+ PresentationAttemptAcceptedOp,
78
+ PresentationAttemptRejectedOp,
79
+ PresentationSinkOperation,
80
+ next_hosted_run_generation,
81
+ )
82
+ from chrys.service.agent_middleware.events.intermediate_text import IntermediateTextBuffer
83
+ from chrys.service.agent_middleware.events.rejection_metadata import rejection_result_metadata, rejection_source
84
+ from chrys.service.agent_middleware.events.result_persistence import (
85
+ RESULT_SLEEP_INTERRUPTED_METADATA_KEY,
86
+ RESULT_SLEEP_SKIPPED_METADATA_KEY,
87
+ RESULT_SUB_AGENT_AUDIT_COMPLETE_METADATA_KEY,
88
+ RESULT_SUB_AGENT_INVOCATION_ID_METADATA_KEY,
89
+ RESULT_SUB_AGENT_LOG_FILE_METADATA_KEY,
90
+ write_result_carriage,
91
+ )
92
+ from chrys.service.agent_middleware.events.tool_error_text import tool_card_error_text
93
+ from chrys.service.mutations.pipeline import (
94
+ abort_mutation_tracking,
95
+ finalize_mutation_tracking,
96
+ prepare_mutation_tracking,
97
+ )
98
+ from chrys.service.mutations.tool_names import _FILE_TOOLS, _IMPLICIT_WRITE_TOOLS
99
+ from chrys.service.tools.result_metadata import tool_result_metadata
100
+ from chrys.service.trajectory.preparation import (
101
+ PreparationOutcome,
102
+ PreparationScope,
103
+ PreparationTrace,
104
+ preparation_lock,
105
+ )
106
+ from chrys.service.trajectory.tools import ToolOperationTrace, tool_operation_id, tool_outcome
107
+
108
+ if TYPE_CHECKING:
109
+ from collections.abc import Awaitable, Callable, Sequence
110
+
111
+ from chrys.foundation.events.bus import EventBus
112
+ from chrys.foundation.models.invocations import InvocationOrigin
113
+ from chrys.kernel import Message
114
+ from chrys.kernel.middleware import FunctionInvocationContext
115
+ from chrys.service.hooks.manager import HookManager
116
+ from chrys.service.mutations.coordination import MutationCoordinator
117
+ from chrys.service.mutations.tracker import MutationTracker
118
+ from chrys.service.session.sub_agent_logs import SubAgentLogStats
119
+
120
+
121
+ class SubAgentStatsMiddleware(FunctionMiddleware):
122
+ """Record sub-agent inner tool completions without publishing events."""
123
+
124
+ def __init__(self, stats: SubAgentLogStats) -> None:
125
+ self._stats = stats
126
+
127
+ async def process(
128
+ self,
129
+ context: FunctionInvocationContext,
130
+ call_next: Callable[[], Awaitable[None]],
131
+ ) -> None:
132
+ cancelled = False
133
+ try:
134
+ await call_next()
135
+ except asyncio.CancelledError:
136
+ cancelled = True
137
+ raise
138
+ finally:
139
+ if not cancelled:
140
+ self._stats.record_tool_call()
141
+
142
+
143
+ class SubAgentEventMiddleware(FunctionMiddleware):
144
+ """Publishes InvocationToolCallStart/InvocationToolCallResult events for sub-agent tool invocations.
145
+
146
+ Lightweight variant of ToolEventMiddleware for sub-agents. Created fresh per ``_invoke()``
147
+ call with a unique ``invocation_id``, then passed as per-run middleware to
148
+ ``agent.run()``.
149
+
150
+ A per-invocation ``IntermediateTextBuffer`` preserves the same ordering as
151
+ the main agent: pending assistant text is delivered before the first tool
152
+ start in its batch. Sub-agent transcripts do not persist batch IDs.
153
+
154
+ Tracks cumulative tool call count and publishes ``InvocationProgress``
155
+ after each tool result so the TUI can display live stats.
156
+
157
+ When a ``mutation_tracker`` is provided, records file mutations (write_file,
158
+ edit_file) and shell command changes so that ``/diff`` picks up sub-agent
159
+ modifications.
160
+ """
161
+
162
+ def __init__(
163
+ self,
164
+ event_bus: EventBus,
165
+ agent_name: str,
166
+ invocation_id: str,
167
+ mutation_tracker: MutationTracker | None = None,
168
+ hook_manager: HookManager | None = None,
169
+ profile_name: str = "",
170
+ session_id: str | None = None,
171
+ workspace_cwd: str = "",
172
+ serialize_implicit_windows: bool = False,
173
+ stats: SubAgentLogStats | None = None,
174
+ mutation_coordinator: MutationCoordinator | None = None,
175
+ tool_result_ceiling_tokens: int | None = None,
176
+ intermediate_buffer: IntermediateTextBuffer | None = None,
177
+ *,
178
+ origin: InvocationOrigin,
179
+ ) -> None:
180
+ self._bus = event_bus
181
+ self._origin = origin
182
+ self._agent_name = agent_name
183
+ self._invocation_id = invocation_id
184
+ self._mutation_tracker = mutation_tracker
185
+ self._mutation_coordinator = mutation_coordinator
186
+ self._hook_manager = hook_manager
187
+ self._profile_name = profile_name
188
+ self._session_id = session_id
189
+ self._workspace_cwd = workspace_cwd
190
+ self._serialize_implicit_windows = serialize_implicit_windows
191
+ self._stats = stats
192
+ self._tool_result_ceiling_tokens = tool_result_ceiling_tokens
193
+ self._intermediate_buffer = intermediate_buffer
194
+ self._tool_call_count = 0
195
+ self._tool_invocation_order = 0
196
+ self._total_tokens = 0
197
+ self._total_usage_tokens = 0
198
+ self._progress_tasks: set[asyncio.Task[None]] = set()
199
+ self._progress_publish_tail: asyncio.Task[None] | None = None
200
+ self._hosted_run_generation = 0
201
+ self._hosted_bridge: HostedPresentationBridge | None = None
202
+
203
+ def bind_origin(self, origin: InvocationOrigin) -> None:
204
+ """Bind the next pass; each tool call and hosted bridge keeps the origin it started under."""
205
+ self._origin = origin
206
+
207
+ def begin_hosted_pass(self) -> HostedPresentationBridge:
208
+ """Create the presentation bridge owned by the next controller pass."""
209
+ self._hosted_run_generation = next_hosted_run_generation()
210
+ origin = self._origin
211
+ self._hosted_bridge = HostedPresentationBridge(
212
+ lambda operation: self._publish_hosted_operation(operation, origin),
213
+ run_generation=self._hosted_run_generation,
214
+ before_response=self.release_intermediate_text,
215
+ )
216
+ return self._hosted_bridge
217
+
218
+ async def reconcile_hosted_response(self, messages: Sequence[Message]) -> None:
219
+ """Reconcile the accepted full-pass transcript through the current bridge.
220
+
221
+ The controller calls this once after the pass completes, so no more
222
+ tool starts can arrive: barriers for calls that never entered the
223
+ tool pipeline (unknown tool, pre-pipeline argument rejection) must
224
+ be released here or the hosted events behind them stay buffered
225
+ forever. The text such a call streamed is released first, so it
226
+ precedes what the reconcile publishes.
227
+ """
228
+ await self.release_intermediate_text()
229
+ if self._hosted_bridge is not None:
230
+ await self._hosted_bridge.reconcile_accepted(messages, final=True)
231
+
232
+ async def release_intermediate_text(self) -> None:
233
+ """Publish intermediate text that no tool start has released yet.
234
+
235
+ A call that never enters the tool pipeline (unknown tool, pre-pipeline
236
+ argument rejection) starts no tool, so the text streamed with it stays
237
+ buffered until the next response starts or the pass ends.
238
+ """
239
+ await self._release_intermediate_text(None)
240
+
241
+ async def finish_intermediate_text(self) -> None:
242
+ """Release what is still buffered as the pass ends; see ``IntermediateTextBuffer.finish``."""
243
+ if self._intermediate_buffer is not None:
244
+ await self._intermediate_buffer.finish(self._publish_intermediate_text)
245
+
246
+ async def _release_intermediate_text(self, preamble: PreparationTrace | None) -> None:
247
+ if self._intermediate_buffer is not None:
248
+ await self._intermediate_buffer.release(self._publish_intermediate_text, preamble)
249
+
250
+ async def _publish_intermediate_text(self, text: str) -> None:
251
+ await self._bus.publish(
252
+ InvocationMessage(
253
+ is_final=False,
254
+ is_intermediate=True,
255
+ origin=self._origin,
256
+ agent_name=self._agent_name,
257
+ text=text,
258
+ session_id=self._session_id,
259
+ )
260
+ )
261
+
262
+ async def reject_hosted_attempt(
263
+ self,
264
+ reason: str,
265
+ *,
266
+ status: str = HostedToolStatus.INTERRUPTED,
267
+ ) -> None:
268
+ """Terminalize running hosted cards at a controller boundary."""
269
+ if self._hosted_bridge is not None:
270
+ await self._hosted_bridge.attempt_rejected(reason, status=status)
271
+
272
+ @staticmethod
273
+ def _artifact_descriptors(operation: HostedToolResultOp) -> list[dict[str, Any]]:
274
+ descriptors: list[dict[str, Any]] = []
275
+ for artifact in operation.view.artifacts:
276
+ # "path" must stay a real URI: consumers turn it into links, and a
277
+ # bare hosted filename (OpenAI hosted_file) is not addressable.
278
+ descriptor: dict[str, Any] = {
279
+ "id": artifact.file_id or artifact.vector_store_id or artifact.id or "",
280
+ "name": artifact.name or "",
281
+ "path": artifact.uri or "",
282
+ "mime": artifact.media_type or "",
283
+ }
284
+ size = artifact.additional_properties.get("size")
285
+ if isinstance(size, int) and not isinstance(size, bool):
286
+ descriptor["size"] = size
287
+ descriptors.append({key: value for key, value in descriptor.items() if value != ""})
288
+ return descriptors
289
+
290
+ async def _publish_hosted_operation(self, operation: PresentationSinkOperation, origin: InvocationOrigin) -> None:
291
+ """Map one hosted presentation operation to sub-agent events."""
292
+ if isinstance(operation, IntermediateTextOp):
293
+ presentation = None
294
+ if operation.provisional:
295
+ segment_id = operation.segment_ids[0] if operation.segment_ids else ""
296
+ presentation = ProvisionalPresentation(operation.attempt_id, segment_id)
297
+ await self._bus.publish(
298
+ InvocationMessage(
299
+ is_final=False,
300
+ is_intermediate=True,
301
+ origin=origin,
302
+ agent_name=self._agent_name,
303
+ text=operation.text,
304
+ presentation=presentation,
305
+ session_id=self._session_id,
306
+ )
307
+ )
308
+ return
309
+ if isinstance(operation, PresentationAttemptAcceptedOp):
310
+ await self._bus.publish(
311
+ InvocationPresentationAttemptAccepted(
312
+ origin=origin,
313
+ agent_name=self._agent_name,
314
+ attempt_id=operation.attempt_id,
315
+ segment_ids=tuple(
316
+ segment_id for segment in operation.segments for segment_id in segment.segment_ids
317
+ ),
318
+ session_id=self._session_id,
319
+ )
320
+ )
321
+ return
322
+ if isinstance(operation, PresentationAttemptRejectedOp):
323
+ await self._bus.publish(
324
+ InvocationPresentationAttemptRejected(
325
+ origin=origin,
326
+ agent_name=self._agent_name,
327
+ attempt_id=operation.attempt_id,
328
+ session_id=self._session_id,
329
+ )
330
+ )
331
+ return
332
+ if isinstance(operation, FinalTextOp):
333
+ return
334
+ view = operation.view
335
+ common: dict[str, Any] = {
336
+ "agent_name": self._agent_name,
337
+ "tool_name": view.tool_name,
338
+ "call_id": operation.presentation_id,
339
+ "provider_hosted": True,
340
+ "hosted_family": view.family,
341
+ "provider": view.provider,
342
+ "session_id": self._session_id,
343
+ }
344
+ if isinstance(operation, HostedToolStartOp):
345
+ await self._bus.publish(
346
+ InvocationToolCallStart(
347
+ origin=origin,
348
+ **common,
349
+ tool_kind=HOSTED_TOOL_DEFAULT_KIND_BY_FAMILY.get(view.family, ""),
350
+ args=view.arguments,
351
+ provider_item_type=view.provider_item_type,
352
+ provider_call_id=view.provider_call_id,
353
+ provider_status=view.provider_status,
354
+ )
355
+ )
356
+ elif isinstance(operation, HostedToolArgsOp):
357
+ await self._bus.publish(
358
+ InvocationToolCallArgsUpdated(
359
+ origin=origin,
360
+ **common,
361
+ tool_kind=HOSTED_TOOL_DEFAULT_KIND_BY_FAMILY.get(view.family, ""),
362
+ args=view.arguments,
363
+ provider_item_type=view.provider_item_type,
364
+ provider_call_id=view.provider_call_id,
365
+ provider_status=view.provider_status,
366
+ )
367
+ )
368
+ elif isinstance(operation, HostedToolProgressOp):
369
+ await self._bus.publish(
370
+ InvocationToolCallProgress(
371
+ origin=origin,
372
+ **common,
373
+ lines=view.result_text.splitlines(),
374
+ image_contents=view.image_contents,
375
+ snapshot_metadata=view.metadata,
376
+ provider_item_type=view.provider_item_type,
377
+ provider_call_id=view.provider_call_id,
378
+ provider_status=view.provider_status,
379
+ )
380
+ )
381
+ elif isinstance(operation, HostedToolStatusOp):
382
+ metadata = dict(view.metadata)
383
+ if view.result_text:
384
+ metadata["result_text"] = view.result_text
385
+ if view.provider_item_type:
386
+ metadata["provider_item_type"] = view.provider_item_type
387
+ if view.provider_call_id:
388
+ metadata["provider_call_id"] = view.provider_call_id
389
+ await self._bus.publish(
390
+ InvocationToolCallStatusUpdated(
391
+ origin=origin,
392
+ **common,
393
+ status=view.status,
394
+ provider_status=view.provider_status,
395
+ metadata=metadata,
396
+ )
397
+ )
398
+ elif isinstance(operation, HostedToolResultOp):
399
+ await self._bus.publish(
400
+ InvocationToolCallResult(
401
+ origin=origin,
402
+ **common,
403
+ result=view.result_text,
404
+ image_contents=view.image_contents,
405
+ metadata=view.metadata,
406
+ artifacts=self._artifact_descriptors(operation),
407
+ provider_item_type=view.provider_item_type,
408
+ provider_call_id=view.provider_call_id,
409
+ provider_status=view.provider_status,
410
+ )
411
+ )
412
+
413
+ def record_usage(self, total_tokens: int, total_usage_tokens: int) -> None:
414
+ """Record sub-agent usage and publish progress from the usage callback."""
415
+ if self._stats is not None:
416
+ self._stats.record_usage(total_tokens, total_usage_tokens)
417
+ self._total_tokens = total_tokens
418
+ self._total_usage_tokens = total_usage_tokens
419
+ # ``record_usage`` is sync (called from ``UsageTrackingMiddleware.
420
+ # _fire_callback``). Skip the publish when there's no running loop —
421
+ # callers driving this from a non-async context (test fixture, future
422
+ # threadpool result hook) should not crash; the cumulative state has
423
+ # already been recorded on ``self``.
424
+ try:
425
+ asyncio.get_running_loop()
426
+ except RuntimeError:
427
+ return
428
+ self._schedule_progress_publish()
429
+
430
+ def _schedule_progress_publish(self) -> None:
431
+ """Schedule a InvocationProgress publish chained behind the previous one.
432
+
433
+ Chaining matches the parent ``_track_usage_publish`` pattern in
434
+ ``chrys.service.usage`` — without it, two close-together publishes can be
435
+ delivered out of order under a slow subscriber, regressing the displayed
436
+ token/tool-call counters. Tasks read ``self._*`` live, so a stale
437
+ ``tool_call_count`` snapshot can never undo a fresher one.
438
+ """
439
+ previous = self._progress_publish_tail
440
+
441
+ async def _publish_after_previous() -> None:
442
+ if previous is not None:
443
+ await asyncio.gather(previous, return_exceptions=True)
444
+ await self._publish_progress()
445
+
446
+ task = asyncio.create_task(_publish_after_previous())
447
+ self._progress_publish_tail = task
448
+ self._progress_tasks.add(task)
449
+ task.add_done_callback(self._progress_tasks.discard)
450
+ task.add_done_callback(self._maybe_clear_tail)
451
+ task.add_done_callback(_observe_task_exception)
452
+
453
+ def _maybe_clear_tail(self, task: asyncio.Task[None]) -> None:
454
+ if self._progress_publish_tail is task:
455
+ self._progress_publish_tail = None
456
+
457
+ async def flush_progress(self) -> None:
458
+ """Wait for any usage-triggered progress publishes to finish.
459
+
460
+ Loops until no tasks remain — a ``record_usage`` that fires while the
461
+ gather is suspended (e.g. a late streaming finalization) adds a task
462
+ to ``_progress_tasks`` that a single snapshot would miss.
463
+ """
464
+ while self._progress_tasks:
465
+ tasks = tuple(self._progress_tasks)
466
+ await asyncio.gather(*tasks, return_exceptions=True)
467
+ self._progress_tasks.difference_update(tasks)
468
+
469
+ async def _publish_progress(self) -> None:
470
+ await self._bus.publish(
471
+ InvocationProgress(
472
+ origin=self._origin,
473
+ agent_name=self._agent_name,
474
+ tool_call_count=self._tool_call_count,
475
+ total_tokens=self._total_tokens,
476
+ total_usage_tokens=self._total_usage_tokens,
477
+ session_id=self._session_id,
478
+ )
479
+ )
480
+
481
+ async def process(
482
+ self,
483
+ context: FunctionInvocationContext,
484
+ call_next: Callable[[], Awaitable[None]],
485
+ ) -> None:
486
+ # Bound before the first await, for the reason spelled out in
487
+ # ``ToolEventMiddleware.process``: the kernel hands the operation over
488
+ # at dispatch, and everything below can be cancelled before the start
489
+ # marker is written.
490
+ trajectory = ToolOperationTrace.open(context.metadata if isinstance(context.metadata, dict) else {})
491
+ preamble = (
492
+ PreparationTrace.open(
493
+ scope=PreparationScope.TOOL_PREAMBLE,
494
+ phase="tool_dispatch",
495
+ parent_operation_id=trajectory.context.innermost_model_operation_id,
496
+ target_operation_id=trajectory.operation_id,
497
+ context=trajectory.context,
498
+ )
499
+ if trajectory is not None
500
+ else None
501
+ )
502
+ origin = self._origin
503
+ try:
504
+ if preamble is not None:
505
+ await preamble.started()
506
+ await self._process(context, call_next, trajectory, preamble, origin)
507
+ except Exception:
508
+ if preamble is not None:
509
+ preamble.finished_soon(outcome=PreparationOutcome.FAILED)
510
+ self._abandon_operation(context, trajectory, preamble, ToolOutcome.ERRORED)
511
+ raise
512
+ except BaseException:
513
+ if preamble is not None:
514
+ preamble.finished_soon(outcome=PreparationOutcome.INTERRUPTED)
515
+ self._abandon_operation(context, trajectory, preamble, ToolOutcome.INTERRUPTED)
516
+ raise
517
+
518
+ def _abandon_operation(
519
+ self,
520
+ context: FunctionInvocationContext,
521
+ trajectory: ToolOperationTrace | None,
522
+ preamble: PreparationTrace | None,
523
+ outcome: str,
524
+ ) -> None:
525
+ """Close an operation that never reached its own start marker (a no-op once it has)."""
526
+ if trajectory is None:
527
+ return
528
+ metadata = context.metadata if isinstance(context.metadata, dict) else {}
529
+ args = context.arguments if is_mutable_tool_args(context.arguments) else {}
530
+ trajectory.abandoned_soon(
531
+ tool_name=context.function.name,
532
+ tool_kind=get_tool_kind(context.function) or "",
533
+ batch_index=None,
534
+ invocation_order=get_tool_invocation_order(context),
535
+ arguments=args,
536
+ invocation_metadata=metadata,
537
+ tool_context=resolve_tool_call_context(context.function, args) or None,
538
+ outcome=outcome,
539
+ preamble_operation_id=(
540
+ preamble.operation_id if preamble is not None and preamble.start_committed else None
541
+ ),
542
+ )
543
+
544
+ async def _process(
545
+ self,
546
+ context: FunctionInvocationContext,
547
+ call_next: Callable[[], Awaitable[None]],
548
+ trajectory: ToolOperationTrace | None,
549
+ preamble: PreparationTrace | None,
550
+ origin: InvocationOrigin,
551
+ ) -> None:
552
+ tool_order = get_tool_invocation_order(context)
553
+ if tool_order is None:
554
+ tool_order = self._tool_invocation_order
555
+ self._tool_invocation_order = max(self._tool_invocation_order, tool_order + 1)
556
+ set_tool_invocation_order(context, tool_order)
557
+
558
+ await self._release_intermediate_text(preamble)
559
+
560
+ provider_call_id = get_provider_call_id(context)
561
+ call_id = get_call_id(context) or uuid4().hex[:_SHORT_ID_LEN]
562
+ tool_name = context.function.name
563
+ args = context.arguments if is_mutable_tool_args(context.arguments) else {}
564
+ # Stash the Chrys call_id in metadata so downstream middleware
565
+ # references the same id as the published
566
+ # InvocationToolCallStart/InvocationToolCallResult events.
567
+ set_call_id(context, call_id)
568
+
569
+ tool_kind = get_tool_kind(context.function) or ""
570
+ blocked = await apply_before_tool_hooks(
571
+ manager=self._hook_manager,
572
+ context=context,
573
+ session_id=self._session_id,
574
+ profile_name=self._profile_name,
575
+ tool_name=tool_name,
576
+ kind=tool_kind,
577
+ call_id=call_id,
578
+ args=args,
579
+ workspace_cwd=self._workspace_cwd,
580
+ target_operation_id=trajectory.operation_id if trajectory is not None else None,
581
+ )
582
+ args = context.arguments if is_mutable_tool_args(context.arguments) else {}
583
+
584
+ async def _blocked_call_next() -> None:
585
+ return None
586
+
587
+ # Serialize concurrent mutations to the same file (same rationale
588
+ # as ToolEventMiddleware — framework uses asyncio.gather()).
589
+ file_lock_path = _file_lock_path(tool_name, args, self._workspace_cwd)
590
+ implicit_window = _uses_implicit_window(tool_name, tool_kind)
591
+ if blocked:
592
+ await self._process_tool_call(
593
+ context, _blocked_call_next, call_id, provider_call_id, tool_name, args, trajectory, preamble, origin
594
+ )
595
+ elif self._serialize_implicit_windows and implicit_window and self._mutation_tracker is not None:
596
+ async with preparation_lock(self._mutation_tracker.get_implicit_window_lock(), preamble):
597
+ await self._process_tool_call(
598
+ context, call_next, call_id, provider_call_id, tool_name, args, trajectory, preamble, origin
599
+ )
600
+ elif file_lock_path and self._mutation_tracker is not None:
601
+ async with preparation_lock(self._mutation_tracker.get_file_lock(file_lock_path), preamble):
602
+ await self._process_tool_call(
603
+ context, call_next, call_id, provider_call_id, tool_name, args, trajectory, preamble, origin
604
+ )
605
+ else:
606
+ await self._process_tool_call(
607
+ context, call_next, call_id, provider_call_id, tool_name, args, trajectory, preamble, origin
608
+ )
609
+
610
+ async def _process_tool_call(
611
+ self,
612
+ context: FunctionInvocationContext,
613
+ call_next: Callable[[], Awaitable[None]],
614
+ call_id: str,
615
+ provider_call_id: str,
616
+ tool_name: str,
617
+ args: dict,
618
+ trajectory: ToolOperationTrace | None,
619
+ preamble: PreparationTrace | None,
620
+ origin: InvocationOrigin,
621
+ ) -> None:
622
+ """Core tool-call processing for sub-agents."""
623
+ tool_kind = get_tool_kind(context.function) or ""
624
+ is_shell = tool_kind == KIND_SHELL
625
+ is_sub_agent = tool_kind == KIND_SUB_AGENT
626
+
627
+ # Pre-execution mutation tracking
628
+ _meta = context.metadata if isinstance(context.metadata, dict) else {}
629
+ pre_rejected = bool(_meta.get(_APPROVAL_REJECTED_KEY))
630
+ mut_ctx = None
631
+ if not pre_rejected:
632
+ mut_ctx = await prepare_mutation_tracking(
633
+ self._mutation_tracker,
634
+ tool_name,
635
+ args,
636
+ call_id,
637
+ is_shell,
638
+ workspace_cwd=self._workspace_cwd,
639
+ coordinator=self._mutation_coordinator,
640
+ )
641
+
642
+ await self._bus.publish(
643
+ InvocationToolCallStart(
644
+ origin=origin,
645
+ agent_name=self._agent_name,
646
+ tool_name=tool_name,
647
+ tool_kind=tool_kind,
648
+ args=args,
649
+ call_id=call_id,
650
+ session_id=self._session_id,
651
+ )
652
+ )
653
+ if self._hosted_bridge is not None:
654
+ await self._hosted_bridge.local_call_start_published(provider_call_id)
655
+
656
+ # A delegation from this invocation (a workflow node's, or a nested sub-agent's) binds its
657
+ # child to this call the same way ToolEventMiddleware binds a chat turn's: the child reads
658
+ # the parent call ids from these ContextVars and writes its invocation id back for the result.
659
+ sub_agent_token = None
660
+ sub_agent_metadata_token = None
661
+ sub_agent_metadata: SubAgentParentResultMetadata | None = None
662
+ if is_sub_agent:
663
+ raw_commit = _meta.get(SUB_AGENT_RESULT_COMMIT_CALLBACK_KEY)
664
+ sub_agent_token = sub_agent_parent_call_id.set(call_id)
665
+ sub_agent_metadata = SubAgentParentResultMetadata(
666
+ parent_provider_call_id=provider_call_id,
667
+ parent_event_call_id=call_id,
668
+ commit_interrupted_result=raw_commit if callable(raw_commit) else None,
669
+ )
670
+ sub_agent_metadata_token = sub_agent_parent_result_metadata.set(sub_agent_metadata)
671
+
672
+ started_at = datetime.now(UTC)
673
+ start = time.monotonic()
674
+ error_text = ""
675
+ errored = False
676
+ raised_error_kind: str | None = None
677
+ cancelled = False
678
+ shell_metadata_token = None
679
+ shell_metadata: dict[str, object] | None = None
680
+ generic_metadata: dict[str, object] = {}
681
+ generic_metadata_token = tool_result_metadata.set(generic_metadata)
682
+ payload_observation: dict[str, object] = {}
683
+ payload_observation_token = tool_payload_observation.set(payload_observation)
684
+ if is_shell:
685
+ from chrys.service.tools.builtins.shell import shell_result_metadata
686
+
687
+ shell_metadata = {}
688
+ shell_metadata_token = shell_result_metadata.set(shell_metadata)
689
+ tool_operation_token = bind_tool_operation(
690
+ trajectory.operation_id if trajectory is not None else tool_operation_id(_meta)
691
+ )
692
+ try:
693
+ if preamble is not None:
694
+ await preamble.finished(outcome=PreparationOutcome.HANDOFF)
695
+ # Inside the block that closes the operation: the start marker
696
+ # awaits its write ack, and an interrupt landing there would
697
+ # otherwise leave the operation open forever. Nothing between the
698
+ # trace and here awaits, so the marker still precedes the call.
699
+ if trajectory is not None:
700
+ await trajectory.started(
701
+ tool_name=tool_name,
702
+ tool_kind=tool_kind,
703
+ batch_index=None,
704
+ invocation_order=get_tool_invocation_order(context),
705
+ arguments=args,
706
+ invocation_metadata=_meta,
707
+ # Resolved again after the call for the persisted carriage;
708
+ # here it describes the call as dispatched, alongside the
709
+ # argument fingerprint of those same arguments.
710
+ tool_context=resolve_tool_call_context(context.function, args) or None,
711
+ preamble_operation_id=(
712
+ preamble.operation_id if preamble is not None and preamble.start_committed else None
713
+ ),
714
+ )
715
+ await call_next()
716
+ except Exception as exc:
717
+ errored = True
718
+ raised_error_kind = type(exc).__name__
719
+ error_text = tool_card_error_text(exc)
720
+ raise
721
+ except BaseException:
722
+ cancelled = True
723
+ raise
724
+ finally:
725
+ reset_tool_operation(tool_operation_token)
726
+ if shell_metadata_token is not None:
727
+ from chrys.service.tools.builtins.shell import shell_result_metadata
728
+
729
+ shell_result_metadata.reset(shell_metadata_token)
730
+ tool_result_metadata.reset(generic_metadata_token)
731
+ tool_payload_observation.reset(payload_observation_token)
732
+ if sub_agent_token is not None:
733
+ sub_agent_parent_call_id.reset(sub_agent_token)
734
+ if sub_agent_metadata_token is not None:
735
+ sub_agent_parent_result_metadata.reset(sub_agent_metadata_token)
736
+
737
+ if not isinstance(context.metadata, dict):
738
+ context.metadata = {}
739
+ rejected = bool(context.metadata.get(_APPROVAL_REJECTED_KEY, False))
740
+ finished_at = datetime.now(UTC)
741
+ elapsed_ms = int((time.monotonic() - start) * 1000)
742
+ # A rejected delegation reports no duration to the UI — it never
743
+ # ran — but the operation was open for the whole approval wait.
744
+ duration_ms = 0 if rejected else elapsed_ms
745
+ stamp_trajectory_timing(
746
+ context.metadata,
747
+ build_trajectory_timing(
748
+ started_at=finished_at if rejected else started_at,
749
+ finished_at=finished_at,
750
+ duration_ms=duration_ms,
751
+ ),
752
+ overwrite=True,
753
+ )
754
+
755
+ if cancelled and trajectory is not None:
756
+ # Unwinding from CancelledError: close without awaiting the ack.
757
+ trajectory.finished_soon(outcome=ToolOutcome.INTERRUPTED, duration_ms=elapsed_ms)
758
+ if cancelled and mut_ctx is not None:
759
+ # Cancelled tasks raise CancelledError at their next await,
760
+ # so the async finalize below is unreachable — release the
761
+ # trace wrapper and close the coordination window
762
+ # synchronously or the registry advertises a live command
763
+ # to peers until shutdown. The command may have written
764
+ # before the interrupt, so implicit detection is truncated.
765
+ abort_mutation_tracking(mut_ctx, self._mutation_coordinator, tracker=self._mutation_tracker)
766
+ if not cancelled:
767
+ try:
768
+ result_text = error_text or extract_result_text(context.result)
769
+ _meta = context.metadata
770
+ # Refresh to the final (approval-modified) args — everything
771
+ # below (after-tool hooks, persisted provenance) must see the
772
+ # arguments the tool actually ran with, same as the main
773
+ # ToolEventMiddleware.
774
+ args = final_tool_args(context, args)
775
+ if rejected and mut_ctx is not None:
776
+ # Rejected at approval, post-prepare: finalize is
777
+ # deliberately skipped (the tool never executed), so
778
+ # release the trace wrapper and close the observation
779
+ # window here.
780
+ abort_mutation_tracking(mut_ctx, self._mutation_coordinator)
781
+ if self._stats is not None:
782
+ self._stats.record_tool_call()
783
+ self._tool_call_count += 1
784
+
785
+ # Post-execution mutation tracking (skip for rejected tools)
786
+ metadata: dict[str, Any] = {}
787
+ metadata.update(generic_metadata)
788
+ if shell_metadata:
789
+ metadata.update(shell_metadata)
790
+ if not rejected and mut_ctx is not None:
791
+ # Authorship gate for coordination claims: structured
792
+ # "Error: ..." results (tool_error / record_tool_failure)
793
+ # mean the write may not have happened — the observed
794
+ # delta could be a peer racing our window. ``errored``
795
+ # only covers raised exceptions; the metadata failure
796
+ # state covers the no-raise failure paths.
797
+ tool_failed = errored or tool_result_metadata_failure_state(metadata) is True
798
+ try:
799
+ mut_result = await finalize_mutation_tracking(
800
+ self._mutation_tracker,
801
+ mut_ctx,
802
+ call_id,
803
+ coordinator=self._mutation_coordinator,
804
+ tool_errored=tool_failed,
805
+ )
806
+ except BaseException:
807
+ # An interrupt landing DURING finalize (git
808
+ # calibration, scans, and trace parsing are executor
809
+ # hops) bypasses both the cancel-path abort above
810
+ # (``cancelled`` only covers call_next) and
811
+ # finalize's own release — clean up synchronously.
812
+ # Idempotent after a partial finalize. The command
813
+ # ran; its aborted detection is truncated.
814
+ abort_mutation_tracking(mut_ctx, self._mutation_coordinator, tracker=self._mutation_tracker)
815
+ raise
816
+ if mut_result.file_snapshot is not None:
817
+ metadata["file_snapshot"] = mut_result.file_snapshot
818
+ if mut_result.file_operation is not None:
819
+ metadata["file_mutation_op"] = mut_result.file_operation
820
+ if mut_result.file_bytes_changed is not None:
821
+ metadata["file_bytes_changed"] = mut_result.file_bytes_changed
822
+ if mut_result.file_hashes is not None:
823
+ metadata["file_mutation_hashes"] = mut_result.file_hashes
824
+ if mut_result.shell_snapshots:
825
+ metadata["shell_file_snapshots"] = mut_result.shell_snapshots
826
+ if rejected:
827
+ metadata.update(rejection_result_metadata(_meta))
828
+ if isinstance(_meta, dict) and _meta.get(_SLEEP_SKIPPED_KEY, False):
829
+ metadata[RESULT_SLEEP_SKIPPED_METADATA_KEY] = True
830
+ if isinstance(_meta, dict) and _meta.get(_SLEEP_INTERRUPTED_KEY, False):
831
+ metadata[RESULT_SLEEP_INTERRUPTED_METADATA_KEY] = True
832
+ if sub_agent_metadata is not None:
833
+ if sub_agent_metadata.sub_agent_invocation_id:
834
+ metadata[RESULT_SUB_AGENT_INVOCATION_ID_METADATA_KEY] = (
835
+ sub_agent_metadata.sub_agent_invocation_id
836
+ )
837
+ if sub_agent_metadata.sub_agent_log_file:
838
+ metadata[RESULT_SUB_AGENT_LOG_FILE_METADATA_KEY] = sub_agent_metadata.sub_agent_log_file
839
+ if sub_agent_metadata.sub_agent_audit_complete:
840
+ metadata[RESULT_SUB_AGENT_AUDIT_COMPLETE_METADATA_KEY] = True
841
+ if sub_agent_metadata.transcript_final_text is not None:
842
+ metadata[SUB_AGENT_TRANSCRIPT_FINAL_TEXT_METADATA_KEY] = (
843
+ sub_agent_metadata.transcript_final_text
844
+ )
845
+ if errored:
846
+ metadata[TOOL_ERRORED_METADATA_KEY] = True
847
+ structured_failed = tool_result_metadata_failure_state(metadata) is True
848
+ source = rejection_source(_meta) if rejected else ""
849
+ # Same carriage as ToolEventMiddleware: the kernel folds the
850
+ # carried metadata/provenance onto the right Contents at
851
+ # construction — no post-run positional re-association.
852
+ tool_context = resolve_tool_call_context(context.function, args)
853
+ write_result_carriage(context, metadata=metadata, tool_context=tool_context or None)
854
+
855
+ hook_decision = await fire_after_tool_hooks(
856
+ manager=self._hook_manager,
857
+ session_id=self._session_id,
858
+ profile_name=self._profile_name,
859
+ tool_name=tool_name,
860
+ kind=tool_kind,
861
+ call_id=call_id,
862
+ args=args,
863
+ result_text=result_text,
864
+ duration_ms=duration_ms,
865
+ errored=errored,
866
+ failed=structured_failed,
867
+ approval_rejected=rejected,
868
+ rejection_source=source,
869
+ workspace_cwd=self._workspace_cwd,
870
+ target_operation_id=trajectory.operation_id if trajectory is not None else None,
871
+ )
872
+ context.result, result_text = append_extra_context_to_result(
873
+ context.result,
874
+ result_text,
875
+ hook_decision.extra_context,
876
+ )
877
+ structured_error_kind = metadata.get(TOOL_ERROR_KIND_METADATA_KEY)
878
+ if not isinstance(context.metadata, dict):
879
+ context.metadata = {}
880
+ write_execution_stamp(
881
+ context.metadata,
882
+ context.arguments,
883
+ outcome="error" if errored or structured_failed else "ok",
884
+ error_kind=(
885
+ raised_error_kind
886
+ or (structured_error_kind if isinstance(structured_error_kind, str) else None)
887
+ ),
888
+ )
889
+ result_images = extract_result_images(context.result)
890
+ await self._bus.publish(
891
+ InvocationToolCallResult(
892
+ origin=origin,
893
+ agent_name=self._agent_name,
894
+ tool_name=tool_name,
895
+ call_id=call_id,
896
+ result=result_text,
897
+ image_contents=result_images,
898
+ duration_ms=duration_ms,
899
+ metadata=metadata,
900
+ session_id=self._session_id,
901
+ )
902
+ )
903
+ if trajectory is not None:
904
+ # Bounded the way the kernel will bound it — see the
905
+ # same call in ``tool_events``.
906
+ await trajectory.payload_observed(
907
+ result_text=preview_result_ceiling(
908
+ result_text,
909
+ self._tool_result_ceiling_tokens,
910
+ observation=payload_observation,
911
+ ),
912
+ image_count=len(result_images),
913
+ observation=payload_observation,
914
+ )
915
+ await trajectory.finished(
916
+ outcome=tool_outcome(
917
+ cancelled=False, rejected=rejected, errored=errored, result_metadata=metadata
918
+ ),
919
+ duration_ms=elapsed_ms,
920
+ result_metadata=metadata,
921
+ error_kind=raised_error_kind,
922
+ )
923
+ await self._publish_progress()
924
+ except BaseException:
925
+ # Interrupted while closing out a call that already ran:
926
+ # the operation is open, and only this path can close it.
927
+ if trajectory is not None:
928
+ trajectory.finished_soon(outcome=ToolOutcome.INTERRUPTED, duration_ms=elapsed_ms)
929
+ raise
930
+
931
+
932
+ def _file_lock_path(tool_name: str, args: dict[str, Any], workspace_cwd: str = "") -> str:
933
+ """Return a file path for write/edit serialization, or empty when absent."""
934
+ if tool_name not in _FILE_TOOLS:
935
+ return ""
936
+ path = args.get("path")
937
+ if not isinstance(path, str) or not path:
938
+ return ""
939
+ return resolve_workspace_path(path, base_cwd=workspace_cwd or None)
940
+
941
+
942
+ def _uses_implicit_window(tool_name: str, tool_kind: str) -> bool:
943
+ """Return whether a tool needs serialized implicit mutation observation."""
944
+ return tool_kind == KIND_SHELL or tool_name in _IMPLICIT_WRITE_TOOLS
945
+
946
+
947
+ def _observe_task_exception(task: asyncio.Task[None]) -> None:
948
+ if task.cancelled():
949
+ return
950
+ with contextlib.suppress(Exception):
951
+ task.exception()