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,1023 @@
1
+ # Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
2
+
3
+ """Headless session host for running Chrys agents outside the TUI."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import asyncio
8
+ import contextlib
9
+ import os
10
+ from collections.abc import AsyncIterator, Callable
11
+ from dataclasses import dataclass, field, replace
12
+ from pathlib import Path
13
+ from typing import Literal
14
+
15
+ from chrys.foundation.config.settings import Settings
16
+ from chrys.foundation.config.settings_store import LoadedSettings
17
+ from chrys.foundation.events.bus import EventBus
18
+ from chrys.foundation.events.types import (
19
+ AgentLoadFailed,
20
+ AgentLoadFinished,
21
+ AgentLoadProgress,
22
+ AgentLoadStarted,
23
+ AgentRuntimeUpdated,
24
+ AgentThinking,
25
+ ApprovalModeUpdated,
26
+ ApprovalRequest,
27
+ ApprovalReviewed,
28
+ CompactionFinished,
29
+ CompactionStarted,
30
+ ContextCompressed,
31
+ Error,
32
+ Event,
33
+ InvocationAborted,
34
+ InvocationCascadeAborted,
35
+ InvocationCompactionCommitted,
36
+ InvocationCompactionFinished,
37
+ InvocationCompactionStarted,
38
+ InvocationContextPressure,
39
+ InvocationEvent,
40
+ InvocationMessage,
41
+ InvocationPaused,
42
+ InvocationPresentationAttemptAccepted,
43
+ InvocationPresentationAttemptRejected,
44
+ InvocationProgress,
45
+ InvocationResumed,
46
+ InvocationRetryAttempt,
47
+ InvocationStarted,
48
+ InvocationToolCallArgsUpdated,
49
+ InvocationToolCallProgress,
50
+ InvocationToolCallResult,
51
+ InvocationToolCallStart,
52
+ InvocationToolCallStatusUpdated,
53
+ QuestionToUser,
54
+ SessionRestore,
55
+ SessionRestored,
56
+ SessionSaved,
57
+ StateChanged,
58
+ TodoListUpdated,
59
+ ToolCompacted,
60
+ UsageUpdate,
61
+ UserInjectResult,
62
+ UserInterrupt,
63
+ UserMessage,
64
+ Warning,
65
+ WorkflowCancelRequest,
66
+ WorkflowNodeStateChanged,
67
+ WorkflowRunAccepted,
68
+ WorkflowRunFinished,
69
+ WorkflowRunRejected,
70
+ WorkflowRunRequest,
71
+ )
72
+ from chrys.foundation.events.workflow import WORKFLOW_RUN_EVENTS
73
+ from chrys.foundation.i18n import DisplayBlock, DisplaySequence, MessageRef, msg
74
+ from chrys.foundation.models.session_surface import SessionSurface
75
+ from chrys.foundation.models.workflow_session import (
76
+ WorkflowDraft,
77
+ WorkflowSessionSelection,
78
+ WorkflowTarget,
79
+ WorkspaceSnapshot,
80
+ )
81
+ from chrys.foundation.models.workspace import Workspace
82
+ from chrys.foundation.platform import get_platform
83
+ from chrys.foundation.trajectory.ids import new_analytics_id
84
+ from chrys.foundation.util.session_ids import SESSION_SHORT_ID_LEN, session_short_id
85
+ from chrys.orchestration.engine.assembly import assemble_agent_engine
86
+ from chrys.orchestration.engine.engine import AgentEngine
87
+ from chrys.orchestration.workflows.catalog import WorkflowCatalog
88
+ from chrys.orchestration.workflows.preview import PreparedWorkflow
89
+ from chrys.orchestration.workflows.runner import WorkflowRunResult
90
+ from chrys.service.approval.policy import ApprovalMode
91
+ from chrys.service.profiles.agents.registry import AgentProfileRegistry
92
+ from chrys.service.profiles.agents.schema import AgentProfile, MCPServerConfig
93
+ from chrys.service.profiles.models.registry import ModelProfileRegistry
94
+ from chrys.service.state.store import JsonFileStateStore, StateStore
95
+ from chrys.service.workflows.discovery import Discovery
96
+ from chrys.service.workflows.outcomes import REASON_DEADLINE_EXCEEDED
97
+
98
+ _AGENT_PROFILE_NOT_FOUND = msg(
99
+ "session_host.agent_profile_not_found",
100
+ fallback="Agent profile not found: {name}",
101
+ )
102
+ _AGENT_PROFILE_NOT_FOUND_WITH_AVAILABLE = msg(
103
+ "session_host.agent_profile_not_found_with_available",
104
+ fallback="Agent profile not found: {name}. Available profiles: {available}",
105
+ )
106
+ _SESSION_NOT_FOUND = msg(
107
+ "session_host.session_not_found",
108
+ fallback="Session not found: {session_id}",
109
+ )
110
+ _SESSION_NOT_FOUND_WITH_RECENT = msg(
111
+ "session_host.session_not_found_with_recent",
112
+ fallback="Session not found: {session_id}. Recent sessions: {recent}",
113
+ )
114
+ _SESSION_ID_AMBIGUOUS = msg(
115
+ "session_host.session_id_ambiguous",
116
+ fallback="Session id '{session_id}' is ambiguous.",
117
+ )
118
+ _NO_FINAL_RESPONSE = msg(
119
+ "session_host.no_final_response",
120
+ fallback="Agent run ended without a final response.",
121
+ )
122
+ _HEADLESS_INTERACTION_REQUIRED = msg(
123
+ "session_host.headless_interaction_required",
124
+ fallback="Agent requested user input in headless mode: {detail}",
125
+ multiline=True,
126
+ )
127
+
128
+ _HEADLESS_RUN_EVENT_TYPES = (
129
+ AgentLoadFailed,
130
+ AgentLoadFinished,
131
+ AgentLoadProgress,
132
+ AgentLoadStarted,
133
+ InvocationMessage,
134
+ AgentRuntimeUpdated,
135
+ AgentThinking,
136
+ ApprovalModeUpdated,
137
+ ApprovalRequest,
138
+ ApprovalReviewed,
139
+ CompactionFinished,
140
+ CompactionStarted,
141
+ ContextCompressed,
142
+ InvocationContextPressure,
143
+ Error,
144
+ QuestionToUser,
145
+ InvocationPresentationAttemptAccepted,
146
+ InvocationPresentationAttemptRejected,
147
+ InvocationRetryAttempt,
148
+ SessionRestored,
149
+ SessionSaved,
150
+ StateChanged,
151
+ InvocationAborted,
152
+ InvocationCascadeAborted,
153
+ InvocationCompactionCommitted,
154
+ InvocationCompactionFinished,
155
+ InvocationCompactionStarted,
156
+ InvocationStarted,
157
+ InvocationPaused,
158
+ InvocationProgress,
159
+ InvocationResumed,
160
+ InvocationToolCallArgsUpdated,
161
+ InvocationToolCallProgress,
162
+ InvocationToolCallResult,
163
+ InvocationToolCallStart,
164
+ InvocationToolCallStatusUpdated,
165
+ TodoListUpdated,
166
+ ToolCompacted,
167
+ UsageUpdate,
168
+ UserInjectResult,
169
+ Warning,
170
+ WorkflowRunAccepted,
171
+ WorkflowRunRejected,
172
+ *WORKFLOW_RUN_EVENTS,
173
+ )
174
+
175
+
176
+ def allows_headless_event(event: Event) -> bool:
177
+ """Keep the existing headless boundary after execution families converge."""
178
+ if not isinstance(event, _HEADLESS_RUN_EVENT_TYPES):
179
+ return False
180
+ if not isinstance(event, InvocationEvent):
181
+ return True
182
+ if event.origin.root.kind == "workflow_node":
183
+ return False
184
+ return event.origin.kind == "turn" or not isinstance(
185
+ event, InvocationMessage | InvocationPresentationAttemptAccepted | InvocationPresentationAttemptRejected
186
+ )
187
+
188
+
189
+ def allows_workflow_node_event(event: Event) -> bool:
190
+ """Whether a workflow agent node's own activity may reach a headless workflow stream that asked for it.
191
+
192
+ The complement of what :func:`allows_headless_event` drops for workflow-rooted invocations: tool calls,
193
+ retries, compaction and sub-agent lifecycle, and the node's own prose. A node's sub-agents keep their prose
194
+ private, as a chat turn's do.
195
+ """
196
+ if not isinstance(event, InvocationEvent) or not isinstance(event, _HEADLESS_RUN_EVENT_TYPES):
197
+ return False
198
+ if event.origin.root.kind != "workflow_node":
199
+ return False
200
+ return event.origin.kind == "workflow_node" or not isinstance(
201
+ event, InvocationMessage | InvocationPresentationAttemptAccepted | InvocationPresentationAttemptRejected
202
+ )
203
+
204
+
205
+ @dataclass(frozen=True)
206
+ class HeadlessRunResult:
207
+ """Final response from one headless agent turn; ``events`` stays empty unless the caller asked to keep them."""
208
+
209
+ text: str
210
+ session_id: str
211
+ events: list[Event] = field(default_factory=list)
212
+
213
+
214
+ class HeadlessRunError(RuntimeError):
215
+ """Raised when a headless agent turn ends with an engine error."""
216
+
217
+ def __init__(self, event: Error) -> None:
218
+ self.event = event
219
+ super().__init__(event.message or event.code or "Headless agent run failed")
220
+
221
+
222
+ @dataclass(frozen=True)
223
+ class EndTurn:
224
+ """A turn completed normally."""
225
+
226
+ kind: Literal["end_turn"] = "end_turn"
227
+ final_text: str = ""
228
+
229
+
230
+ @dataclass(frozen=True)
231
+ class Cancelled:
232
+ """A turn was cancelled by the caller."""
233
+
234
+ kind: Literal["cancelled"] = "cancelled"
235
+ reason: str = ""
236
+
237
+
238
+ @dataclass(frozen=True)
239
+ class Errored:
240
+ """A turn ended with an engine error."""
241
+
242
+ kind: Literal["error"] = "error"
243
+ error: Error = field(default_factory=Error)
244
+
245
+
246
+ TurnOutcome = EndTurn | Cancelled | Errored
247
+
248
+
249
+ class WorkflowRunRejectedError(RuntimeError):
250
+ """The engine refused the workflow run request; ``event`` carries the reason code."""
251
+
252
+ def __init__(self, event: WorkflowRunRejected) -> None:
253
+ super().__init__(f"{event.error}: {event.message}")
254
+ self.event = event
255
+
256
+
257
+ class WorkflowRunTimeoutError(TimeoutError):
258
+ """The workflow deadline expired before a run could be accepted."""
259
+
260
+
261
+ class AgentProfileNotFoundError(KeyError):
262
+ """Raised when an agent profile selector can't be resolved against the registry."""
263
+
264
+ def __init__(self, message: str, *, display_message: MessageRef | None = None) -> None:
265
+ self.display_message = display_message
266
+ super().__init__(message)
267
+
268
+
269
+ class SessionNotFoundError(KeyError):
270
+ """Raised when a session id (full or short) doesn't match any stored session."""
271
+
272
+ def __init__(self, message: str, *, display_message: MessageRef | None = None) -> None:
273
+ self.display_message = display_message
274
+ super().__init__(message)
275
+
276
+
277
+ class AmbiguousSessionIdError(ValueError):
278
+ """Raised when a session-id prefix matches more than one stored session."""
279
+
280
+ def __init__(self, message: str, *, display_message: MessageRef | None = None) -> None:
281
+ self.display_message = display_message
282
+ super().__init__(message)
283
+
284
+
285
+ def _normalize_cwd(cwd: str | None) -> str:
286
+ raw = (cwd or "").strip()
287
+ if not raw:
288
+ return ""
289
+ return os.path.abspath(os.path.expanduser(raw))
290
+
291
+
292
+ class ChrysSessionHost:
293
+ """Owns one event bus, one engine, and one session for non-TUI callers.
294
+
295
+ ``start()`` is an explicit, idempotent lifecycle boundary. Callers that
296
+ need startup/restore events should use ``iter_run_events()``, which
297
+ auto-starts before publishing the user turn. ``run_until_final()`` is only
298
+ a convenience wrapper over that streaming primitive.
299
+ """
300
+
301
+ def __init__(
302
+ self,
303
+ *,
304
+ profile_name: str = "",
305
+ session_id: str | None = None,
306
+ settings: Settings | None = None,
307
+ loaded_settings: LoadedSettings | None = None,
308
+ event_bus: EventBus | None = None,
309
+ agent_registry: AgentProfileRegistry | None = None,
310
+ model_registry: ModelProfileRegistry | None = None,
311
+ state_store: StateStore | None = None,
312
+ approval_mode: ApprovalMode | None = ApprovalMode.BYPASS,
313
+ cwd: str | None = None,
314
+ workspace: Workspace | None = None,
315
+ mcp_overlay: list[MCPServerConfig] | None = None,
316
+ allow_user_interaction: bool = False,
317
+ on_successful_turn: Callable[[], None] | None = None,
318
+ on_turn_started: Callable[[], None] | None = None,
319
+ surface: SessionSurface | None = None,
320
+ ) -> None:
321
+ self._profile_name = profile_name.strip()
322
+ self._restore_session_id = (session_id or "").strip() or None
323
+ if not self._profile_name and not self._restore_session_id:
324
+ error_message = "profile_name is required."
325
+ raise ValueError(error_message)
326
+ self._bus = event_bus or EventBus()
327
+ self._agent_registry = agent_registry or AgentProfileRegistry()
328
+ self._model_registry = model_registry or ModelProfileRegistry()
329
+ self._state_store = state_store or JsonFileStateStore()
330
+ self._cwd = _normalize_cwd(cwd)
331
+ self._workflow_catalog: WorkflowCatalog | None = None
332
+ self._workflow_selection: WorkflowSessionSelection | None = None
333
+ self._workspace = workspace
334
+ self._mcp_overlay = list(mcp_overlay or [])
335
+ self._allow_user_interaction = allow_user_interaction
336
+ self._started = False
337
+ self._run_lock = asyncio.Lock()
338
+ self._cancel_requested = False
339
+ self._last_turn_outcome: TurnOutcome | None = None
340
+ self._engine = assemble_agent_engine(
341
+ self._bus,
342
+ settings,
343
+ loaded_settings=loaded_settings,
344
+ agent_registry=self._agent_registry,
345
+ model_registry=self._model_registry,
346
+ state_store=self._state_store,
347
+ initial_approval_mode=approval_mode,
348
+ mcp_overlay=self._mcp_overlay,
349
+ initial_workspace=workspace or (Workspace.from_cwd(self._cwd) if self._cwd else None),
350
+ on_successful_turn=on_successful_turn,
351
+ on_turn_started=on_turn_started,
352
+ allow_user_interaction=allow_user_interaction,
353
+ surface=surface,
354
+ )
355
+ self._approval_mode = self._engine.session.approval_mode
356
+
357
+ @property
358
+ def event_bus(self) -> EventBus:
359
+ """Event bus used by this host."""
360
+ return self._bus
361
+
362
+ @property
363
+ def engine(self) -> AgentEngine:
364
+ """Engine owned by this host."""
365
+ return self._engine
366
+
367
+ @property
368
+ def _settings(self) -> Settings:
369
+ """The engine's current settings, not a copy taken at construction.
370
+
371
+ Held nowhere here on purpose: the engine owns the handle, and a second
372
+ field would answer with whatever was true when this host was built —
373
+ stale from the first reload or session restore onwards.
374
+ """
375
+ return self._engine.settings
376
+
377
+ @property
378
+ def session_id(self) -> str | None:
379
+ """Current canonical session id, or ``None`` before start."""
380
+ return self._engine.session_id
381
+
382
+ @property
383
+ def approval_mode(self) -> ApprovalMode:
384
+ """Initial approval mode used when the engine was built."""
385
+ return self._approval_mode
386
+
387
+ @property
388
+ def vision_enabled(self) -> bool:
389
+ """Whether the currently built runtime model accepts image input."""
390
+ return self._engine.runtime_details.model.vision
391
+
392
+ @property
393
+ def last_turn_outcome(self) -> TurnOutcome | None:
394
+ """Structured outcome for the most recently completed streamed turn."""
395
+ return self._last_turn_outcome
396
+
397
+ async def start(self) -> None:
398
+ """Start a fresh session or restore the configured existing session.
399
+
400
+ Idempotent. Callers that need to observe startup/restore events through
401
+ ``iter_run_events()`` should let the first run drive startup so the
402
+ event stream is already subscribed before load events are published.
403
+ """
404
+ if self._started:
405
+ return
406
+ self._load_registries_if_needed()
407
+ if self._restore_session_id:
408
+ await self.restore_session(self._restore_session_id)
409
+ else:
410
+ if not self._profile_name:
411
+ error_message = "profile_name is required to start a new session."
412
+ raise ValueError(error_message)
413
+ profile = self._resolve_profile(self._profile_name)
414
+ self._profile_name = profile.name
415
+ await self._engine.start(profile)
416
+ self._started = True
417
+
418
+ async def restore_session(self, session_id: str) -> None:
419
+ """Restore a saved session into this host."""
420
+ target_session_id = await self._resolve_restore_session_id(session_id)
421
+ if not target_session_id:
422
+ error_message = "session_id is required to restore a session."
423
+ raise ValueError(error_message)
424
+ self._load_registries_if_needed()
425
+ if self._profile_name:
426
+ self._profile_name = self._resolve_profile(self._profile_name).name
427
+ await self._engine.prepare()
428
+ # Promote a requested short id to the canonical id used by restore events and future idempotent starts.
429
+ self._restore_session_id = target_session_id
430
+ loop = asyncio.get_running_loop()
431
+ restored: asyncio.Future[SessionRestored] = loop.create_future()
432
+
433
+ async def _on_restored(event: SessionRestored) -> None:
434
+ if event.session_id == target_session_id and not restored.done():
435
+ restored.set_result(event)
436
+
437
+ async def _on_error(event: Error) -> None:
438
+ if event.session_id == target_session_id and not restored.done():
439
+ restored.set_exception(HeadlessRunError(event))
440
+
441
+ await self._bus.subscribe(SessionRestored, _on_restored)
442
+ await self._bus.subscribe(Error, _on_error)
443
+ try:
444
+ await self._bus.publish(
445
+ SessionRestore(
446
+ session_id=target_session_id,
447
+ primary_cwd=self._cwd,
448
+ profile_name=self._profile_name,
449
+ working_dirs=(
450
+ [d.path for d in self._workspace.working_dirs if not d.is_primary]
451
+ if self._workspace is not None
452
+ else None
453
+ ),
454
+ ),
455
+ raise_handler_errors=True,
456
+ )
457
+ await restored
458
+ finally:
459
+ await self._bus.unsubscribe(SessionRestored, _on_restored)
460
+ await self._bus.unsubscribe(Error, _on_error)
461
+ if restored.done() and not restored.cancelled():
462
+ # A failing restore publishes its Error *and* re-raises out of
463
+ # the publish above, so the future's copy of the failure goes
464
+ # unawaited; retrieve it or asyncio logs it as never-retrieved.
465
+ restored.exception()
466
+ self._started = True
467
+
468
+ async def iter_run_events(self, message: str | UserMessage) -> AsyncIterator[Event]:
469
+ """Run one user turn and yield backend events until the engine run finishes."""
470
+ async for event in self._iter_turn_events(message, raise_for_outcome=True):
471
+ yield event
472
+
473
+ async def iter_turn_events(self, message: str | UserMessage) -> AsyncIterator[Event]:
474
+ """Run one user turn and yield events, recording a structured terminal outcome."""
475
+ async for event in self._iter_turn_events(message, raise_for_outcome=False):
476
+ yield event
477
+
478
+ async def _iter_turn_events(self, message: str | UserMessage, *, raise_for_outcome: bool) -> AsyncIterator[Event]:
479
+ if self._run_lock.locked():
480
+ error_message = "Concurrent turns are not supported for a ChrysSessionHost."
481
+ raise RuntimeError(error_message)
482
+ async with self._run_lock:
483
+ self._last_turn_outcome = None
484
+ self._cancel_requested = False
485
+ async for event in self._iter_run_events_locked(message, raise_for_outcome=raise_for_outcome):
486
+ yield event
487
+
488
+ async def cancel_current_turn(self) -> None:
489
+ """Request cancellation for the active turn, if any."""
490
+ self._cancel_requested = True
491
+ await self._bus.publish(UserInterrupt(session_id=self.session_id))
492
+
493
+ async def _iter_run_events_locked(
494
+ self,
495
+ message: str | UserMessage,
496
+ *,
497
+ raise_for_outcome: bool,
498
+ ) -> AsyncIterator[Event]:
499
+ """Run one user turn while the host run lock is held."""
500
+ final: InvocationMessage | None = None
501
+ error: Error | None = None
502
+ question: QuestionToUser | None = None
503
+ next_event_task: asyncio.Task[Event] | None = None
504
+ start_task: asyncio.Task[None] | None = None
505
+ run_task: asyncio.Task[None] | None = None
506
+
507
+ def _record_event(event: Event) -> None:
508
+ nonlocal final, error, question
509
+ # Only the terminal state is kept: a caller that wants the turn's
510
+ # events reads them from this iterator as they stream.
511
+ if (
512
+ isinstance(event, InvocationMessage)
513
+ and event.origin.kind == "turn"
514
+ and event.is_final
515
+ and not event.is_intermediate
516
+ ):
517
+ final = event
518
+ elif isinstance(event, Error):
519
+ error = event
520
+ elif isinstance(event, QuestionToUser):
521
+ question = event
522
+
523
+ async with self._bus.stream(*_HEADLESS_RUN_EVENT_TYPES) as stream:
524
+ ask_user_interrupted = False
525
+
526
+ async def _publish_user_turn() -> None:
527
+ nonlocal run_task
528
+ user_event = self._coerce_user_message(message)
529
+ await self._bus.publish(user_event)
530
+ run_task = asyncio.create_task(self._engine.wait_for_run_task())
531
+
532
+ async def _event_task_ready_after_checkpoint() -> bool:
533
+ if next_event_task is not None and next_event_task.done():
534
+ return True
535
+ # EventBus.publish queues stream events synchronously; this checkpoint lets those tasks win first.
536
+ # Revisit if the bus later grows asynchronous fan-out.
537
+ await asyncio.sleep(0)
538
+ return next_event_task is not None and next_event_task.done()
539
+
540
+ next_event_task = asyncio.create_task(stream.__anext__())
541
+ if self._started:
542
+ await _publish_user_turn()
543
+ else:
544
+ start_task = asyncio.create_task(self.start())
545
+
546
+ try:
547
+ while True:
548
+ wait_tasks: set[asyncio.Task[object]] = {next_event_task}
549
+ if start_task is not None:
550
+ wait_tasks.add(start_task)
551
+ if run_task is not None:
552
+ wait_tasks.add(run_task)
553
+ done, _pending = await asyncio.wait(wait_tasks, return_when=asyncio.FIRST_COMPLETED)
554
+
555
+ if next_event_task in done:
556
+ event = next_event_task.result()
557
+ next_event_task = asyncio.create_task(stream.__anext__())
558
+ if not self._event_belongs_to_session(event, self.session_id, self._restore_session_id):
559
+ continue
560
+ if not allows_headless_event(event):
561
+ continue
562
+ _record_event(event)
563
+ yield event
564
+ if (
565
+ isinstance(event, QuestionToUser)
566
+ and not self._allow_user_interaction
567
+ and not ask_user_interrupted
568
+ ):
569
+ ask_user_interrupted = True
570
+ await self._bus.publish(UserInterrupt(session_id=self.session_id))
571
+ continue
572
+
573
+ if start_task is not None and start_task in done:
574
+ if await _event_task_ready_after_checkpoint():
575
+ continue
576
+ await start_task
577
+ start_task = None
578
+ await _publish_user_turn()
579
+ continue
580
+
581
+ if run_task is not None and run_task in done:
582
+ if await _event_task_ready_after_checkpoint():
583
+ continue
584
+ await run_task
585
+ outcome = self._resolve_run_outcome(final=final, error=error, question=question)
586
+ self._last_turn_outcome = outcome
587
+ if raise_for_outcome:
588
+ self._raise_for_run_outcome(outcome)
589
+ return
590
+ finally:
591
+ if start_task is not None:
592
+ if start_task.done():
593
+ self._observe_task_exception(start_task)
594
+ else:
595
+ start_task.cancel()
596
+ with contextlib.suppress(asyncio.CancelledError):
597
+ await start_task
598
+ if run_task is not None:
599
+ if run_task.done():
600
+ self._observe_task_exception(run_task)
601
+ else:
602
+ try:
603
+ self._cancel_requested = True
604
+ await self._bus.publish(UserInterrupt(session_id=self.session_id))
605
+ await asyncio.shield(run_task)
606
+ finally:
607
+ self._observe_or_defer_task_exception(run_task)
608
+ if next_event_task is not None and not next_event_task.done():
609
+ next_event_task.cancel()
610
+ with contextlib.suppress(asyncio.CancelledError):
611
+ await next_event_task
612
+
613
+ async def run_until_final(
614
+ self,
615
+ message: str | UserMessage,
616
+ *,
617
+ timeout: float | None = None,
618
+ keep_events: bool = False,
619
+ on_event: Callable[[Event], None] | None = None,
620
+ ) -> HeadlessRunResult:
621
+ """Run one user turn and return the final assistant response.
622
+
623
+ The turn's streamed events are retained in ``HeadlessRunResult.events``
624
+ only with *keep_events*: a long turn streams many deltas nobody reads.
625
+ *on_event* sees every event as it streams, before the next one is read.
626
+ """
627
+
628
+ async def _run() -> HeadlessRunResult:
629
+ events: list[Event] = []
630
+ final: InvocationMessage | None = None
631
+ async for event in self.iter_run_events(message):
632
+ if on_event is not None:
633
+ on_event(event)
634
+ if keep_events:
635
+ events.append(event)
636
+ if (
637
+ isinstance(event, InvocationMessage)
638
+ and event.origin.kind == "turn"
639
+ and event.is_final
640
+ and not event.is_intermediate
641
+ ):
642
+ final = event
643
+ if final is None:
644
+ error_message = "Agent run ended without a final response."
645
+ raise RuntimeError(error_message)
646
+ return HeadlessRunResult(text=final.text, session_id=self.session_id or "", events=events)
647
+
648
+ if timeout is None:
649
+ return await _run()
650
+ return await asyncio.wait_for(_run(), timeout=timeout)
651
+
652
+ # -- workflows -----------------------------------------------------------------------
653
+
654
+ def _project_workspace(self) -> Workspace:
655
+ """The workspace inherited by a new workflow session, copied at admission."""
656
+ return self._engine.workspace or self._workspace or Workspace.from_cwd(self._cwd)
657
+
658
+ @property
659
+ def workflow_session_id(self) -> str:
660
+ return self._workflow_selection.session_id if self._workflow_selection is not None else ""
661
+
662
+ @property
663
+ def workflow_catalog(self) -> WorkflowCatalog:
664
+ config_dir, project_cwd = (
665
+ get_platform().config_dir,
666
+ Path(
667
+ self._workflow_selection.workspace.primary_cwd
668
+ if self._workflow_selection is not None
669
+ else self._project_workspace().primary_cwd
670
+ ),
671
+ )
672
+ catalog = self._workflow_catalog
673
+ if catalog is None or (catalog.config_dir, catalog.project_cwd) != (config_dir, project_cwd):
674
+ catalog = self._workflow_catalog = WorkflowCatalog(config_dir=config_dir, project_cwd=project_cwd)
675
+ return catalog
676
+
677
+ @property
678
+ def workflow_session_dir(self) -> Path | None:
679
+ return self._state_store.session_dir(self.workflow_session_id) if self.workflow_session_id else None
680
+
681
+ async def load_workflow_session(self, session_id: str) -> None:
682
+ """Select workflow history and workspace without constructing a chat agent."""
683
+ normalized = session_id.strip().replace("-", "").lower()
684
+ meta = await self._state_store.load_session_meta(normalized, strict=True)
685
+ if meta is None or normalized != (
686
+ session_short_id(meta.session_id).lower()
687
+ if len(normalized) <= SESSION_SHORT_ID_LEN
688
+ else meta.session_id.replace("-", "").lower()
689
+ ):
690
+ raise ValueError(f"No workflow session {session_id!r} exists.")
691
+ if meta.kind != "workflow":
692
+ raise ValueError(f"Session {session_id!r} is a Chat session, not a Workflow session.")
693
+ if not meta.run_count:
694
+ raise ValueError(f"Workflow session {session_id!r} has no runs.")
695
+ state = await self._state_store.load_workflow_session(meta.session_id)
696
+ if state is None:
697
+ raise ValueError(f"No workflow session {session_id!r} exists.")
698
+ self._workflow_selection = state.selection(meta.session_id)
699
+
700
+ def list_workflows(self) -> Discovery:
701
+ """Active and shadowed workflows, plus unreadable files."""
702
+ return self.workflow_catalog.discover()
703
+
704
+ def workflow_target(
705
+ self, workflow_id: str, *, new_session: bool = False, workspace: Workspace | None = None
706
+ ) -> WorkflowTarget:
707
+ """Capture a draft or the selected session; subsequent browsing cannot retarget it."""
708
+ if not new_session and self._workflow_selection is not None:
709
+ if workspace is not None or workflow_id != self._workflow_selection.workflow_id:
710
+ raise WorkflowRunRejectedError(
711
+ WorkflowRunRejected(
712
+ error="spec_changed",
713
+ message="Use a new workflow draft to select another definition or workspace.",
714
+ )
715
+ )
716
+ return self._workflow_selection
717
+ return WorkflowDraft(workflow_id, WorkspaceSnapshot.capture(workspace or self._project_workspace()))
718
+
719
+ async def preview_workflow(
720
+ self, target: WorkflowTarget, *, timeout: float | None = None, trust: bool = False
721
+ ) -> PreparedWorkflow:
722
+ """Prepare the exact target; ``trust`` explicitly authorizes execution of unconfirmed source."""
723
+ catalog = WorkflowCatalog(config_dir=get_platform().config_dir, project_cwd=Path(target.workspace.primary_cwd))
724
+ preview = await catalog.preview(
725
+ target.workflow_id,
726
+ timeout=timeout,
727
+ trust=trust,
728
+ expected_identity=target.identity if isinstance(target, WorkflowSessionSelection) else None,
729
+ )
730
+ return PreparedWorkflow(target, preview)
731
+
732
+ def confirm_workflow(self, prepared: PreparedWorkflow) -> None:
733
+ """Confirm a prepared definition independently of the current browsing selection."""
734
+ catalog = WorkflowCatalog(
735
+ config_dir=get_platform().config_dir, project_cwd=Path(prepared.target.workspace.primary_cwd)
736
+ )
737
+ catalog.confirm(prepared.preview)
738
+
739
+ async def iter_workflow_events(
740
+ self,
741
+ target: WorkflowTarget | PreparedWorkflow,
742
+ *,
743
+ input_text: str = "",
744
+ timeout: float = 0.0,
745
+ include_node_activity: bool = False,
746
+ ) -> AsyncIterator[Event]:
747
+ """Run one workflow and yield backend events until its ``WorkflowRunFinished``.
748
+
749
+ Raises :class:`WorkflowRunRejectedError` when the engine refuses the
750
+ request. Replies to other requests (a retransmitted request id is
751
+ answered with its original reply) are not this run's and are not
752
+ yielded. Closing the iterator early (``contextlib.aclosing``) cancels
753
+ the run and waits for it; so does a cancellation that lands while the
754
+ request is still being published.
755
+
756
+ ``timeout`` is in seconds and covers admission and execution. Expiry before acceptance
757
+ raises :class:`WorkflowRunTimeoutError`; an accepted run finishes as
758
+ cancelled with reason ``deadline_exceeded``. Both paths await cleanup.
759
+
760
+ With ``include_node_activity`` the stream also carries this run's agent-node activity
761
+ (:func:`allows_workflow_node_event`), admitted only for invocations one of this run's
762
+ ``WorkflowNodeStateChanged`` events announced: the runner binds an activation's invocation id before
763
+ publishing ``running``, so a same-session invocation from any other run is never yielded.
764
+ """
765
+ if self._run_lock.locked():
766
+ error_message = "Concurrent runs are not supported for a ChrysSessionHost."
767
+ raise RuntimeError(error_message)
768
+ async with self._run_lock, self._bus.stream(*_HEADLESS_RUN_EVENT_TYPES) as stream:
769
+ self._load_registries_if_needed()
770
+ await self._engine.prepare()
771
+ request_id = new_analytics_id()
772
+ request = (
773
+ target.run_request(input_text=input_text, request_id=request_id, timeout=timeout)
774
+ if isinstance(target, PreparedWorkflow)
775
+ else WorkflowRunRequest(target=target, input_text=input_text, request_id=request_id, timeout=timeout)
776
+ )
777
+ run_id: str | None = None
778
+ execution_session_id: str | None = None
779
+ owned_invocations: set[str] = set()
780
+ rejected = False
781
+ finished = False
782
+ timed_out = False
783
+
784
+ async def enforce_deadline() -> None:
785
+ nonlocal timed_out
786
+ await asyncio.sleep(timeout)
787
+ workflows = self._engine.workflows
788
+ if workflows.active_request_id == request_id:
789
+ timed_out = workflows.cancel_active(reason=REASON_DEADLINE_EXCEEDED)
790
+
791
+ # Own a timer independently of iterator consumption: never put a timeout context across yield.
792
+ deadline_task = (
793
+ asyncio.create_task(enforce_deadline(), name=f"chrys.workflow.deadline.{request_id}")
794
+ if timeout > 0
795
+ else None
796
+ )
797
+ try:
798
+ # Published inside the try: a subscriber behind the coordinator can hold the publish open after
799
+ # the run was admitted, and a cancellation landing there must still give the run up below.
800
+ await self._bus.publish(request)
801
+ async for event in stream:
802
+ node_activity = include_node_activity and allows_workflow_node_event(event)
803
+ if not node_activity and not allows_headless_event(event):
804
+ continue
805
+ if isinstance(event, WorkflowRunAccepted | WorkflowRunRejected) and event.request_id != request_id:
806
+ continue # the reply to someone else's request, a replayed acceptance included
807
+ if isinstance(event, WorkflowRunRejected):
808
+ rejected = True
809
+ if timed_out:
810
+ raise WorkflowRunTimeoutError("Workflow run timed out during admission.")
811
+ raise WorkflowRunRejectedError(event)
812
+ if isinstance(event, WorkflowRunAccepted):
813
+ run_id = event.run_id
814
+ execution_session_id = event.session_id
815
+ self._workflow_selection = event.selection
816
+ elif isinstance(event, Warning) and event.request_id:
817
+ if event.request_id != request_id:
818
+ continue
819
+ elif run_id is None or event.session_id != execution_session_id:
820
+ continue
821
+ if (
822
+ isinstance(
823
+ event,
824
+ WORKFLOW_RUN_EVENTS,
825
+ )
826
+ and event.run_id != run_id
827
+ ):
828
+ continue
829
+ if isinstance(event, WorkflowNodeStateChanged) and event.invocation_id:
830
+ owned_invocations.add(event.invocation_id)
831
+ if (
832
+ node_activity
833
+ and isinstance(event, InvocationEvent)
834
+ and event.origin.root.invocation_id not in owned_invocations
835
+ ):
836
+ continue
837
+ yield event
838
+ if isinstance(event, WorkflowRunFinished):
839
+ finished = True
840
+ return
841
+ finally:
842
+ if deadline_task is not None:
843
+ deadline_task.cancel()
844
+ with contextlib.suppress(asyncio.CancelledError):
845
+ await deadline_task
846
+ # Only this request's run is ours to give up; a refused request leaves the active run alone.
847
+ workflows = self._engine.workflows
848
+ if workflows.active_request_id == request_id:
849
+ if not finished and not rejected:
850
+ await asyncio.shield(workflows.abandon_active())
851
+ await asyncio.shield(workflows.wait_idle())
852
+
853
+ async def run_workflow_until_final(
854
+ self,
855
+ target: WorkflowTarget | PreparedWorkflow,
856
+ *,
857
+ input_text: str = "",
858
+ timeout: float = 0.0,
859
+ ) -> WorkflowRunResult:
860
+ """Run one workflow to its terminal outcome and return the full result (outputs included)."""
861
+ run_id = ""
862
+ async for event in self.iter_workflow_events(
863
+ target,
864
+ input_text=input_text,
865
+ timeout=timeout,
866
+ ):
867
+ if isinstance(event, WorkflowRunAccepted):
868
+ run_id = event.run_id
869
+ result = self._engine.workflows.result(run_id)
870
+ if result is None:
871
+ error_message = "Workflow run ended without a recorded result."
872
+ raise RuntimeError(error_message)
873
+ return result
874
+
875
+ async def cancel_workflow(self) -> None:
876
+ """Request cancellation of the active workflow run, if any."""
877
+ run_id = self._engine.workflows.active_run_id
878
+ if run_id is not None:
879
+ await self._bus.publish(WorkflowCancelRequest(run_id=run_id, session_id=self.workflow_session_id or None))
880
+
881
+ async def shutdown(self) -> None:
882
+ """Shutdown the owned engine and release session resources."""
883
+ await self._engine.shutdown()
884
+ self._started = False
885
+
886
+ def _load_registries_if_needed(self) -> None:
887
+ if not self._agent_registry.list_names():
888
+ self._agent_registry.load_all()
889
+ if not self._model_registry.list_ids():
890
+ self._model_registry.load_all()
891
+
892
+ def _resolve_profile(self, name: str) -> AgentProfile:
893
+ profile = self._agent_registry.resolve_selector(name)
894
+ if profile is not None:
895
+ return profile
896
+ available_names = sorted(self._agent_registry.list_names())
897
+ available = ", ".join(available_names)
898
+ error_message = f"Agent profile not found: {name}"
899
+ if available_names:
900
+ error_message = f"{error_message}. Available profiles: {available}"
901
+ display_message = _AGENT_PROFILE_NOT_FOUND_WITH_AVAILABLE.bind(
902
+ name=name,
903
+ available=DisplaySequence(available_names),
904
+ )
905
+ else:
906
+ display_message = _AGENT_PROFILE_NOT_FOUND.bind(name=name)
907
+ raise AgentProfileNotFoundError(error_message, display_message=display_message)
908
+
909
+ async def _resolve_restore_session_id(self, session_id: str) -> str:
910
+ raw = (session_id or "").strip()
911
+ if not raw:
912
+ return ""
913
+ sessions = await self._state_store.list_sessions()
914
+ normalized = raw.replace("-", "").lower()
915
+ if len(normalized) <= SESSION_SHORT_ID_LEN:
916
+ matches = [meta.session_id for meta in sessions if session_short_id(meta.session_id).lower() == normalized]
917
+ else:
918
+ matches = [
919
+ meta.session_id
920
+ for meta in sessions
921
+ if meta.session_id == raw or meta.session_id.replace("-", "").lower() == normalized
922
+ ]
923
+ if len(matches) == 1:
924
+ return matches[0]
925
+ if len(matches) > 1:
926
+ error_message = f"Session id '{raw}' is ambiguous."
927
+ raise AmbiguousSessionIdError(
928
+ error_message,
929
+ display_message=_SESSION_ID_AMBIGUOUS.bind(session_id=raw),
930
+ )
931
+ recent_ids = [session_short_id(meta.session_id) for meta in sessions[:5]]
932
+ recent = ", ".join(recent_ids)
933
+ error_message = f"Session not found: {raw}"
934
+ if recent_ids:
935
+ error_message = f"{error_message}. Recent sessions: {recent}"
936
+ display_message = _SESSION_NOT_FOUND_WITH_RECENT.bind(
937
+ session_id=raw,
938
+ recent=DisplaySequence(recent_ids),
939
+ )
940
+ else:
941
+ display_message = _SESSION_NOT_FOUND.bind(session_id=raw)
942
+ raise SessionNotFoundError(error_message, display_message=display_message)
943
+
944
+ @staticmethod
945
+ def _event_belongs_to_session(
946
+ event: Event,
947
+ session_id: str | None,
948
+ restore_session_id: str | None = None,
949
+ ) -> bool:
950
+ if not event.session_id:
951
+ # Headless streams ignore global/sessionless events so unrelated errors cannot terminate this run.
952
+ return False
953
+ return event.session_id == session_id or (
954
+ restore_session_id is not None and event.session_id == restore_session_id
955
+ )
956
+
957
+ @staticmethod
958
+ def _observe_task_exception(task: asyncio.Task[object]) -> None:
959
+ """Mark a completed helper task's exception as retrieved."""
960
+ if task.done() and not task.cancelled():
961
+ task.exception()
962
+
963
+ @classmethod
964
+ def _observe_or_defer_task_exception(cls, task: asyncio.Task[object]) -> None:
965
+ """Observe a helper task now, or once it finishes after caller cancellation."""
966
+ if task.done():
967
+ cls._observe_task_exception(task)
968
+ return
969
+ task.add_done_callback(cls._observe_task_exception)
970
+
971
+ def _raise_for_run_outcome(self, outcome: TurnOutcome) -> None:
972
+ if isinstance(outcome, Errored):
973
+ raise HeadlessRunError(outcome.error)
974
+ if isinstance(outcome, Cancelled):
975
+ error_message = outcome.reason or "Agent run was cancelled."
976
+ raise RuntimeError(error_message)
977
+
978
+ def _resolve_run_outcome(
979
+ self,
980
+ *,
981
+ final: InvocationMessage | None,
982
+ error: Error | None,
983
+ question: QuestionToUser | None,
984
+ ) -> TurnOutcome:
985
+ if question is not None and not self._allow_user_interaction:
986
+ # Headless runs have no one to answer, so a question terminates the turn.
987
+ # Interactive hosts (e.g. ACP) answer ask_user inline, so the run continues
988
+ # to a real final/error outcome and the question must not fail the turn.
989
+ return Errored(error=self._question_to_error(question))
990
+ if self._cancel_requested:
991
+ return Cancelled(reason="Agent run was cancelled.")
992
+ if error is not None:
993
+ return Errored(error=error)
994
+ if final is not None:
995
+ return EndTurn(final_text=final.text)
996
+ return Errored(
997
+ error=Error(
998
+ code="no_final_response",
999
+ message="Agent run ended without a final response.",
1000
+ display_message=_NO_FINAL_RESPONSE.bind(),
1001
+ session_id=self.session_id,
1002
+ )
1003
+ )
1004
+
1005
+ def _coerce_user_message(self, message: str | UserMessage) -> UserMessage:
1006
+ if isinstance(message, UserMessage):
1007
+ return replace(message, session_id=self.session_id)
1008
+ return UserMessage(text=message, session_id=self.session_id)
1009
+
1010
+ @staticmethod
1011
+ def _question_to_error(event: QuestionToUser) -> Error:
1012
+ details = [question.question.strip() for question in event.questions if question.question.strip()]
1013
+ detail = (
1014
+ " ".join(f"{index}. {question}" for index, question in enumerate(details, start=1))
1015
+ if details
1016
+ else "The agent requested user input."
1017
+ )
1018
+ return Error(
1019
+ code="headless_interaction_required",
1020
+ message=f"Agent requested user input in headless mode: {detail}",
1021
+ display_message=_HEADLESS_INTERACTION_REQUIRED.bind(detail=DisplayBlock(detail)),
1022
+ session_id=event.session_id,
1023
+ )