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
chrys/kernel/agent.py ADDED
@@ -0,0 +1,994 @@
1
+ # Copyright (c) Microsoft. All rights reserved.
2
+ # Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
3
+ # Contains code adapted from Microsoft Agent Framework (MIT License; see NOTICE).
4
+
5
+ """Agent orchestration over session providers and the model/tool loop.
6
+
7
+ ``_AgentCore`` prepares history, instructions and tools, calls the client,
8
+ aggregates an ``AgentResponse``, and runs the session providers before and
9
+ after the run. ``Agent`` adds telemetry and forwards chat/function middleware
10
+ to the client stack::
11
+
12
+ Agent = AgentTelemetryLayer over _AgentCore
13
+ client = ToolLoopLayer(ChatMiddlewareLayer(wire client))
14
+ session/providers = kernel.sessions
15
+
16
+ The MCP adapter supplies flat ``FunctionTool`` lists. Compaction strategy and
17
+ tokenizer are supplied per run. History persistence runs after the complete
18
+ agent response, and both finalizers write ``SessionContext._response``;
19
+ providers observe it through the read-only ``response`` property.
20
+
21
+ ``AgentTelemetryLayer`` sets and resets non-streaming ContextVars inside the
22
+ returned coroutine, so it can be driven from another task. Streaming spans
23
+ activate around each pull. Agent spans identify their provider as ``chrys``;
24
+ chat spans identify the model provider.
25
+
26
+ Kernel imports stay within this package, allowed dependencies and foundation.
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ from collections.abc import Mapping, MutableMapping, Sequence
32
+ from contextlib import AbstractAsyncContextManager, AsyncExitStack
33
+ from copy import deepcopy
34
+ from functools import partial
35
+ from typing import TYPE_CHECKING, Any, ClassVar, Literal, Self, TypedDict, overload
36
+ from uuid import uuid4
37
+
38
+ from ._types import (
39
+ AgentResponse,
40
+ ChatResponseUpdate,
41
+ ResponseStream,
42
+ _build_agent_response_from_chat_response,
43
+ map_chat_to_agent_update,
44
+ normalize_messages,
45
+ )
46
+ from .client import (
47
+ CONVERSATION_HANDLE_KEYS,
48
+ collect_conversation_handles,
49
+ continuation_token_response_id,
50
+ conversation_handle_value,
51
+ resolve_storage_mode_and_handles,
52
+ strip_invalidated_conversation_handles,
53
+ )
54
+ from .exceptions import AgentInvalidResponseException
55
+ from .instrumentation import AgentTelemetryLayer
56
+ from .middleware import split_middleware
57
+ from .sessions import (
58
+ AgentSession,
59
+ HistoryProvider,
60
+ InMemoryHistoryProvider,
61
+ ServiceFallbackHistoryProvider,
62
+ SessionContext,
63
+ is_local_history_conversation_id,
64
+ )
65
+
66
+ # Normalize foreign tools at admission so the run pipeline invokes FunctionTool.
67
+ from .tools import normalize_tools
68
+
69
+ if TYPE_CHECKING:
70
+ from collections.abc import Awaitable, Callable
71
+
72
+ from ._types import (
73
+ AgentResponseUpdate,
74
+ AgentRunInputs,
75
+ ChatResponse,
76
+ Message,
77
+ ToolTypes,
78
+ )
79
+ from .middleware import ChatMiddleware, FunctionMiddleware
80
+ from .sessions import ContextProvider
81
+
82
+
83
+ def _get_tool_name(tool: Any) -> str | None:
84
+ """Return the name of a function-tool declaration or tool-like object."""
85
+ if isinstance(tool, Mapping):
86
+ func = tool.get("function", None)
87
+ if func and isinstance(func, Mapping):
88
+ name = func.get("name")
89
+ return name if isinstance(name, str) else None
90
+ return None
91
+ name = getattr(tool, "name", None)
92
+ return name if isinstance(name, str) else None
93
+
94
+
95
+ def _tool_identity(tool: Any) -> Any:
96
+ """Return the original tool identity shared by clones of a foreign tool."""
97
+ return getattr(tool, "_foreign_invocation_state_source", tool)
98
+
99
+
100
+ def _append_unique_tools(
101
+ existing_tools: list[Any],
102
+ new_tools: Sequence[Any],
103
+ *,
104
+ duplicate_error_message: str | None = None,
105
+ ) -> list[Any]:
106
+ """Append tools while preserving the run-pipeline duplicate-name contract.
107
+
108
+ Appends in place and returns the same list. Nameless tools always append;
109
+ the same object (or clones of the same foreign source) seen twice is
110
+ skipped; a *different* object under an existing name raises ``ValueError``.
111
+ """
112
+ seen_by_name: dict[str, Any] = {}
113
+ for tool_item in existing_tools:
114
+ if tool_name := _get_tool_name(tool_item):
115
+ seen_by_name[tool_name] = tool_item
116
+
117
+ for tool_item in new_tools:
118
+ tool_name = _get_tool_name(tool_item)
119
+ if tool_name is None:
120
+ existing_tools.append(tool_item)
121
+ continue
122
+
123
+ existing_tool = seen_by_name.get(tool_name)
124
+ if existing_tool is None:
125
+ seen_by_name[tool_name] = tool_item
126
+ existing_tools.append(tool_item)
127
+ continue
128
+
129
+ if existing_tool is tool_item or _tool_identity(existing_tool) is _tool_identity(tool_item):
130
+ continue
131
+
132
+ message = duplicate_error_message or "Tool names must be unique."
133
+ raise ValueError(f"Duplicate tool name '{tool_name}'. {message}")
134
+
135
+ return existing_tools
136
+
137
+
138
+ def _merge_options(base: dict[str, Any], override: dict[str, Any]) -> dict[str, Any]:
139
+ """Merge base and override chat options.
140
+
141
+ Returns a new dict; ``None`` override values are skipped, ``tools`` are
142
+ unioned via :func:`_append_unique_tools`, ``logit_bias``/``metadata`` are
143
+ dict-merged, ``instructions`` are newline-concatenated, everything else is
144
+ replaced. ``None`` is treated as "unset" throughout: the merged result is
145
+ stripped of any remaining ``None`` values in a final pass so unset options
146
+ are never forwarded (e.g. an unset ``store`` is left
147
+ for the service to default).
148
+ """
149
+ result = dict(base)
150
+
151
+ for key, value in override.items():
152
+ if value is None:
153
+ continue
154
+ if key == "tools" and (result.get("tools") or value):
155
+ base_tools = normalize_tools(result.get("tools"))
156
+ override_tools = normalize_tools(value)
157
+ result["tools"] = _append_unique_tools(
158
+ list(base_tools),
159
+ override_tools,
160
+ duplicate_error_message="Tool names must be unique.",
161
+ )
162
+ elif key == "logit_bias" and result.get("logit_bias"):
163
+ result["logit_bias"] = {**result["logit_bias"], **value}
164
+ elif key == "metadata" and result.get("metadata"):
165
+ result["metadata"] = {**result["metadata"], **value}
166
+ elif key == "instructions" and result.get("instructions"):
167
+ result["instructions"] = f"{result['instructions']}\n{value}"
168
+ else:
169
+ result[key] = value
170
+ return {key: value for key, value in result.items() if value is not None}
171
+
172
+
173
+ class _RunContext(TypedDict):
174
+ """Session, request options and invocation arguments prepared for one run."""
175
+
176
+ session: AgentSession | None
177
+ session_context: SessionContext
178
+ input_messages: Sequence[Message]
179
+ session_messages: Sequence[Message]
180
+ agent_name: str
181
+ chat_options: MutableMapping[str, Any]
182
+ compaction_strategy: Any
183
+ tokenizer: Any
184
+ client_kwargs: Mapping[str, Any]
185
+ function_invocation_kwargs: Mapping[str, Any]
186
+
187
+
188
+ class _AgentCore:
189
+ """Prepare requests, drive the client, and finalize responses through session providers.
190
+
191
+ The core terminates the cooperative MRO beneath ``AgentTelemetryLayer``.
192
+ Streaming preparation is lazy: ``ResponseStream.from_awaitable`` wraps the
193
+ mapped tool-loop stream, whose transform and result hooks propagate
194
+ conversation state and persist the completed response.
195
+ """
196
+
197
+ # AgentTelemetryLayer reads this before the core constructor runs, so it
198
+ # must stay a ClassVar. It supplies the agent span's gen_ai.provider.name.
199
+ AGENT_PROVIDER_NAME: ClassVar[str] = "chrys"
200
+
201
+ def __init__(
202
+ self,
203
+ client: Any,
204
+ instructions: str | None = None,
205
+ *,
206
+ id: str | None = None, # Public agent identifier; intentionally shadows the builtin.
207
+ name: str | None = None,
208
+ description: str | None = None,
209
+ tools: ToolTypes | Callable[..., Any] | Sequence[ToolTypes | Callable[..., Any]] | None = None,
210
+ context_providers: Sequence[ContextProvider] | None = None,
211
+ middleware: ChatMiddleware | FunctionMiddleware | Sequence[ChatMiddleware | FunctionMiddleware] | None = None,
212
+ additional_properties: MutableMapping[str, Any] | None = None,
213
+ ) -> None:
214
+ """Initialize agent defaults and provider state around the client stack.
215
+
216
+ Compaction strategy and tokenizer default to ``None`` here; run-level
217
+ values take precedence in ``_prepare_run_context``.
218
+ """
219
+ # Store middleware as supplied; run() re-reads it on every call so
220
+ # changes between runs take effect. Validating it here rejects an
221
+ # unsupported value at construction rather than on the first run.
222
+ split_middleware(middleware)
223
+ self.id = id if id is not None else str(uuid4())
224
+ self.name = name
225
+ self.description = description
226
+ self.context_providers: list[ContextProvider] = list(context_providers or [])
227
+ self.middleware: ChatMiddleware | FunctionMiddleware | Sequence[ChatMiddleware | FunctionMiddleware] | None = (
228
+ middleware
229
+ )
230
+ self.additional_properties: MutableMapping[str, Any] = additional_properties or {}
231
+
232
+ self.client = client
233
+ self.compaction_strategy: Any = None
234
+ self.tokenizer: Any = None
235
+
236
+ # Normalize flat tools and resolve the model through client delegation.
237
+ # AgentTelemetryLayer also reads this dict when building span attributes.
238
+ initial_tools = _append_unique_tools([], normalize_tools(tools))
239
+ default_options: dict[str, Any] = {
240
+ "instructions": instructions,
241
+ "tool_choice": "auto",
242
+ "tools": initial_tools,
243
+ "model": getattr(client, "model", None),
244
+ }
245
+ self.default_options: dict[str, Any] = {k: v for k, v in default_options.items() if v is not None}
246
+ self._async_exit_stack = AsyncExitStack()
247
+ self._update_agent_name_and_description()
248
+ super().__init__()
249
+
250
+ def _update_agent_name_and_description(self) -> None:
251
+ """Forward agent identity to clients that expose the optional update hook."""
252
+ update_fn = getattr(self.client, "_update_agent_name_and_description", None)
253
+ if callable(update_fn):
254
+ update_fn(self.name, self.description)
255
+
256
+ async def __aenter__(self) -> Self:
257
+ """Enter the client context, when supported, and own its cleanup through the exit stack."""
258
+ if isinstance(self.client, AbstractAsyncContextManager):
259
+ await self._async_exit_stack.enter_async_context(self.client)
260
+ return self
261
+
262
+ async def __aexit__(
263
+ self,
264
+ exc_type: type[BaseException] | None,
265
+ exc_val: BaseException | None,
266
+ exc_tb: Any,
267
+ ) -> None:
268
+ """Close client resources registered on the exit stack."""
269
+ await self._async_exit_stack.aclose()
270
+
271
+ def create_session(self, *, session_id: str | None = None) -> AgentSession:
272
+ """Create a session with a supplied or generated identifier."""
273
+ return AgentSession(session_id=session_id)
274
+
275
+ def get_session(self, service_session_id: str, *, session_id: str | None = None) -> AgentSession:
276
+ """Create a local session referring to an existing service conversation."""
277
+ return AgentSession(session_id=session_id, service_session_id=service_session_id)
278
+
279
+ def _get_agent_name(self) -> str:
280
+ """Use UnnamedAgent when no display name is configured."""
281
+ return self.name or "UnnamedAgent"
282
+
283
+ def run(
284
+ self,
285
+ messages: AgentRunInputs | None = None,
286
+ *,
287
+ stream: bool = False,
288
+ session: AgentSession | None = None,
289
+ tools: ToolTypes | Callable[..., Any] | Sequence[ToolTypes | Callable[..., Any]] | None = None,
290
+ options: Mapping[str, Any] | None = None,
291
+ compaction_strategy: Any = None,
292
+ tokenizer: Any = None,
293
+ function_invocation_kwargs: Mapping[str, Any] | None = None,
294
+ client_kwargs: Mapping[str, Any] | None = None,
295
+ ) -> Awaitable[AgentResponse[Any]] | ResponseStream[AgentResponseUpdate, AgentResponse[Any]]:
296
+ """Build a lazy run that prepares context before calling the client.
297
+
298
+ ``stream=False`` returns an un-awaited coroutine; ``stream=True`` returns
299
+ ``ResponseStream.from_awaitable(...)`` synchronously. Neither mode runs
300
+ providers or calls the client until the result is driven.
301
+ """
302
+
303
+ async def _prepare() -> _RunContext:
304
+ return await self._prepare_run_context(
305
+ messages=messages,
306
+ session=session,
307
+ tools=tools,
308
+ options=options,
309
+ compaction_strategy=compaction_strategy,
310
+ tokenizer=tokenizer,
311
+ function_invocation_kwargs=function_invocation_kwargs,
312
+ client_kwargs=client_kwargs,
313
+ )
314
+
315
+ if not stream:
316
+
317
+ async def _run_non_streaming() -> AgentResponse[Any]:
318
+ ctx = await _prepare()
319
+ response = await self._call_chat_client(ctx, stream=False)
320
+ return await self._parse_non_streaming_response(ctx, response)
321
+
322
+ return _run_non_streaming()
323
+
324
+ async def _run_streaming() -> ResponseStream[AgentResponseUpdate, AgentResponse[Any]]:
325
+ ctx = await _prepare()
326
+ stream_response = self._call_chat_client(ctx, stream=True)
327
+ return self._parse_streaming_response(ctx, stream_response)
328
+
329
+ return ResponseStream.from_awaitable(_run_streaming())
330
+
331
+ def _call_chat_client(self, context: _RunContext, *, stream: bool) -> Any:
332
+ """Call the client with the prepared messages, options and invocation arguments."""
333
+ return self.client.get_response(
334
+ messages=context["session_messages"],
335
+ stream=stream,
336
+ options=context["chat_options"],
337
+ compaction_strategy=context["compaction_strategy"],
338
+ tokenizer=context["tokenizer"],
339
+ function_invocation_kwargs=context["function_invocation_kwargs"],
340
+ client_kwargs=context["client_kwargs"],
341
+ )
342
+
343
+ async def _prepare_run_context(
344
+ self,
345
+ *,
346
+ messages: AgentRunInputs | None,
347
+ session: AgentSession | None,
348
+ tools: ToolTypes | Callable[..., Any] | Sequence[ToolTypes | Callable[..., Any]] | None,
349
+ options: Mapping[str, Any] | None,
350
+ compaction_strategy: Any,
351
+ tokenizer: Any,
352
+ function_invocation_kwargs: Mapping[str, Any] | None,
353
+ client_kwargs: Mapping[str, Any] | None,
354
+ ) -> _RunContext:
355
+ """Combine agent defaults, provider contributions and run options into a request."""
356
+ opts = dict(options) if options else {}
357
+ existing_additional_args: dict[str, Any] = opts.pop("additional_function_arguments", None) or {}
358
+
359
+ # Get tools from options or named parameter (named param takes precedence)
360
+ tools_ = tools if tools is not None else opts.pop("tools", None)
361
+
362
+ input_messages = normalize_messages(messages)
363
+
364
+ # Combine agent-level defaults with runtime options up front so the
365
+ # store/conversation_id decisions below read from a single place
366
+ # using _merge_options with the same precedence as the
367
+ # actual client call (runtime wins; unset/None falls back to the agent
368
+ # default).
369
+ effective_options = _merge_options(self.default_options, opts)
370
+
371
+ # `store` in runtime or agent options takes precedence over the
372
+ # client's default storage behavior (the STORES_BY_DEFAULT getattr
373
+ # resolves through the layered clients' __getattr__ delegation). An
374
+ # explicit store=False forces local history injection even when the
375
+ # client stores server-side by default; store=None/unset falls back to
376
+ # STORES_BY_DEFAULT: under the merge's None-strip an explicit None
377
+ # means "unset", not "falsy False".
378
+ try:
379
+ stores_by_default = bool(self.client.STORES_BY_DEFAULT)
380
+ except AttributeError:
381
+ stores_by_default = False
382
+ try:
383
+ force_stateless = bool(self.client.FORCES_STATELESS)
384
+ except AttributeError:
385
+ force_stateless = False
386
+ storage_resolution = resolve_storage_mode_and_handles(
387
+ effective_options,
388
+ stores_by_default=stores_by_default,
389
+ client_kwargs=client_kwargs,
390
+ force_stateless=force_stateless,
391
+ )
392
+ service_stores_history = storage_resolution.service_side
393
+ # One normalized, capability- and invalidation-aware reflection view
394
+ # of the effective request. Forced-stateless clients see the
395
+ # resolver's handle-free copy here so local history remains eligible;
396
+ # the provider wire view is rebuilt later from the original options.
397
+ # handles the tool loop withheld are removed outright, because the
398
+ # service transcript behind them still holds stripped or
399
+ # never-answered calls — re-sending one would combine a poisoned
400
+ # remote transcript with the local fallback replay. The same view
401
+ # feeds the eager-shadow decision here and the history providers
402
+ # below, so no consumer can mistake a withheld handle for a live one.
403
+ reflected_options = storage_resolution.options_without_handles if force_stateless else effective_options
404
+ if session is not None and session.invalidated_service_session_ids:
405
+ reflected_options = strip_invalidated_conversation_handles(
406
+ reflected_options, session.invalidated_service_session_ids
407
+ )
408
+ # Resolve the request-level continuation handles from that view
409
+ # (a live session id still takes precedence below). Every
410
+ # CONVERSATION_HANDLE_KEYS spelling counts, top-level and inside
411
+ # extra_body — each one reaches the wire as service-side
412
+ # continuation state, so any of them means the service owns history.
413
+ request_handles = collect_conversation_handles(reflected_options)
414
+ # Auto-inject local history when no loading HistoryProvider owns it.
415
+ # Non-history and persist-only providers must not suppress prior turns.
416
+ # NOTE: appends permanently to self.context_providers — agent state,
417
+ # not run state.
418
+ if (
419
+ session is not None
420
+ and not any(
421
+ provider.load_messages for provider in self.context_providers if isinstance(provider, HistoryProvider)
422
+ )
423
+ and not request_handles
424
+ ):
425
+ if service_stores_history:
426
+ # Service-side storage keeps the transcript only behind the
427
+ # service handle, and the exhaustion tail may have to discard
428
+ # that handle. Shadow every turn locally from the start —
429
+ # waiting until invalidation would permanently lose the turns
430
+ # that succeeded through service storage before it. The
431
+ # handle-gated fallback persists through the ordinary
432
+ # after_run pass but loads only once the handle is gone, so
433
+ # a live service conversation never sees duplicated history.
434
+ self.context_providers.append(ServiceFallbackHistoryProvider())
435
+ elif not session.service_session_id:
436
+ self.context_providers.append(InMemoryHistoryProvider())
437
+
438
+ active_session = session
439
+ if active_session is None and self.context_providers:
440
+ active_session = AgentSession()
441
+
442
+ session_context, chat_options = await self._prepare_session_and_messages(
443
+ session=active_session,
444
+ input_messages=input_messages,
445
+ # Providers reflect on the effective request, not the raw run
446
+ # arguments: agent-default continuation handles must be visible
447
+ # to history-provider load gates exactly like run-level ones,
448
+ # and an invalidated handle must not look live to them — a
449
+ # provider that trusts it would skip local replay while the
450
+ # wire choke point strips the handle, sending the request with
451
+ # neither remote nor local history. Hand over the sanitized
452
+ # merged snapshot.
453
+ options=reflected_options,
454
+ )
455
+ default_additional_args = chat_options.pop("additional_function_arguments", None)
456
+ if isinstance(default_additional_args, Mapping):
457
+ existing_additional_args = {
458
+ **dict(default_additional_args),
459
+ **existing_additional_args,
460
+ }
461
+
462
+ agent_name = self._get_agent_name()
463
+ base_tools = normalize_tools(chat_options.pop("tools", None))
464
+
465
+ # Combine configured, provider and runtime tools under the same
466
+ # duplicate-name rules. All inputs normalize to flat FunctionTools.
467
+ final_tools = list(base_tools)
468
+ _append_unique_tools(final_tools, normalize_tools(tools_))
469
+
470
+ effective_function_invocation_kwargs = (
471
+ dict(function_invocation_kwargs) if function_invocation_kwargs is not None else {}
472
+ )
473
+ additional_function_arguments = {**effective_function_invocation_kwargs, **existing_additional_args}
474
+
475
+ model = opts.pop("model", None)
476
+
477
+ # Build options dict from run() options merged with provided options.
478
+ # ORDER MATTERS for conversation_id: when a session exists, the key is
479
+ # NOT popped from opts above, so an options-level conversation_id
480
+ # overrides the session-derived value via the trailing **opts spread.
481
+ run_opts: dict[str, Any] = {
482
+ "conversation_id": active_session.service_session_id
483
+ if active_session
484
+ else opts.pop("conversation_id", None),
485
+ "allow_multiple_tool_calls": opts.pop("allow_multiple_tool_calls", None),
486
+ "frequency_penalty": opts.pop("frequency_penalty", None),
487
+ "logit_bias": opts.pop("logit_bias", None),
488
+ "max_tokens": opts.pop("max_tokens", None),
489
+ "metadata": opts.pop("metadata", None),
490
+ "presence_penalty": opts.pop("presence_penalty", None),
491
+ "response_format": opts.pop("response_format", None),
492
+ "seed": opts.pop("seed", None),
493
+ "stop": opts.pop("stop", None),
494
+ "store": opts.pop("store", None),
495
+ "temperature": opts.pop("temperature", None),
496
+ "tool_choice": opts.pop("tool_choice", None),
497
+ "tools": final_tools or None,
498
+ "top_p": opts.pop("top_p", None),
499
+ "user": opts.pop("user", None),
500
+ **opts, # Remaining options are provider-specific
501
+ }
502
+ if model is not None:
503
+ run_opts["model"] = model
504
+ # _merge_options strips unset (None) options, so e.g. an unset `store`
505
+ # is not forwarded and the service decides its own default (the
506
+ # explicit pre-filter folded into the merge's final pass).
507
+ co = _merge_options(chat_options, run_opts)
508
+ # Single wire choke point for invalidated handles: run options, agent
509
+ # defaults, and provider chat_options all land in the merged view, so
510
+ # a caller repeating a handle the tool loop withheld is suppressed
511
+ # here regardless of which layer supplied it — under every
512
+ # CONVERSATION_HANDLE_KEYS spelling, top-level or nested in
513
+ # extra_body (provider SDKs merge extra_body over the named
514
+ # parameters, so a nested spelling reaches the wire all the same).
515
+ # Suppression is removal-only: poisoned keys are dropped, never
516
+ # rewritten, except that conversation_id falls back to the session's
517
+ # own live handle when one exists — the fallback provider already
518
+ # skipped its replay on seeing that handle, so bare removal would
519
+ # send neither a handle nor local history.
520
+ if active_session is not None and active_session.invalidated_service_session_ids:
521
+ invalidated = active_session.invalidated_service_session_ids
522
+ for key in CONVERSATION_HANDLE_KEYS:
523
+ handle = conversation_handle_value(co.get(key))
524
+ if handle is None or handle not in invalidated:
525
+ continue
526
+ if key == "conversation_id":
527
+ live_handle = active_session.service_session_id
528
+ if (
529
+ isinstance(live_handle, str)
530
+ and live_handle
531
+ and not is_local_history_conversation_id(live_handle)
532
+ and live_handle not in invalidated
533
+ ):
534
+ co[key] = live_handle
535
+ continue
536
+ co.pop(key)
537
+ token_response_id = continuation_token_response_id(co.get("continuation_token"))
538
+ if token_response_id is not None and token_response_id in invalidated:
539
+ # The token would short-circuit the request into retrieving
540
+ # the poisoned response outright — resurfacing the very
541
+ # stripped calls the invalidation withheld, with the request
542
+ # messages ignored. Removal-only: tokens are
543
+ # response-specific, no fall-back applies.
544
+ co.pop("continuation_token")
545
+ extra_body = co.get("extra_body")
546
+ if isinstance(extra_body, Mapping):
547
+ poisoned_keys = [
548
+ key
549
+ for key in CONVERSATION_HANDLE_KEYS
550
+ if (nested := conversation_handle_value(extra_body.get(key))) is not None and nested in invalidated
551
+ ]
552
+ if poisoned_keys:
553
+ # Copy-on-write: the mapping may be a caller-owned object
554
+ # shared beyond this run.
555
+ clean_extra_body = dict(extra_body)
556
+ for key in poisoned_keys:
557
+ del clean_extra_body[key]
558
+ co["extra_body"] = clean_extra_body
559
+
560
+ # Build session_messages from session context: context messages + input messages
561
+ session_messages: list[Message] = session_context.get_messages(include_input=True)
562
+
563
+ effective_client_kwargs = dict(client_kwargs) if client_kwargs is not None else {}
564
+ if active_session is not None:
565
+ effective_client_kwargs["session"] = active_session
566
+ # Append provider middleware after existing client middleware.
567
+ # split_middleware rejects unsupported middleware with TypeError.
568
+ provider_middleware = session_context.get_middleware()
569
+ if provider_middleware:
570
+ provider_split = split_middleware(provider_middleware)
571
+ provider_function_chat_middleware: list[Any] = [*provider_split.function, *provider_split.chat]
572
+ if provider_function_chat_middleware:
573
+ existing_middleware = effective_client_kwargs.get("middleware")
574
+ if isinstance(existing_middleware, Sequence) and not isinstance(existing_middleware, (str, bytes)):
575
+ effective_client_kwargs["middleware"] = [
576
+ *existing_middleware,
577
+ *provider_function_chat_middleware,
578
+ ]
579
+ elif existing_middleware is not None:
580
+ effective_client_kwargs["middleware"] = [
581
+ existing_middleware,
582
+ *provider_function_chat_middleware,
583
+ ]
584
+ else:
585
+ effective_client_kwargs["middleware"] = provider_function_chat_middleware
586
+
587
+ return {
588
+ "session": active_session,
589
+ "session_context": session_context,
590
+ "input_messages": input_messages,
591
+ "session_messages": session_messages,
592
+ "agent_name": agent_name,
593
+ "chat_options": co,
594
+ "compaction_strategy": compaction_strategy or self.compaction_strategy,
595
+ "tokenizer": tokenizer or self.tokenizer,
596
+ "client_kwargs": effective_client_kwargs,
597
+ "function_invocation_kwargs": additional_function_arguments,
598
+ }
599
+
600
+ async def _prepare_session_and_messages(
601
+ self,
602
+ *,
603
+ session: AgentSession | None,
604
+ input_messages: list[Message] | None = None,
605
+ options: dict[str, Any] | None = None,
606
+ ) -> tuple[SessionContext, dict[str, Any]]:
607
+ """Copy agent defaults and collect provider context before the run."""
608
+ # Shallow-copy the tools list, deepcopy every other default.
609
+ chat_options_tools: list[Any] | None = None
610
+ if self.default_options:
611
+ chat_options: dict[str, Any] = {}
612
+ for key, value in self.default_options.items():
613
+ if key == "tools":
614
+ chat_options_tools = list(value) if value else []
615
+ chat_options[key] = chat_options_tools
616
+ else:
617
+ chat_options[key] = deepcopy(value)
618
+ else:
619
+ chat_options = {}
620
+
621
+ provider_session = session
622
+ if provider_session is None and self.context_providers:
623
+ provider_session = AgentSession()
624
+
625
+ session_context = SessionContext(
626
+ session_id=provider_session.session_id if provider_session else None,
627
+ service_session_id=provider_session.service_session_id if provider_session else None,
628
+ input_messages=input_messages or [],
629
+ options=options or {},
630
+ )
631
+
632
+ # Run before_run providers in forward order; HistoryProviders that opt
633
+ # out of message loading are skipped.
634
+ for provider in self.context_providers:
635
+ if isinstance(provider, HistoryProvider) and not provider.load_messages:
636
+ continue
637
+ if provider_session is None:
638
+ raise RuntimeError("Provider session must be available when context providers are configured.")
639
+ await provider.before_run(
640
+ agent=self, # type: ignore[arg-type]
641
+ session=provider_session,
642
+ context=session_context,
643
+ state=provider_session.state.setdefault(provider.source_id, {}),
644
+ )
645
+
646
+ # Merge provider-contributed tools into chat_options.
647
+ if session_context.tools:
648
+ if chat_options_tools is not None:
649
+ chat_options_tools.extend(session_context.tools)
650
+ else:
651
+ chat_options["tools"] = list(session_context.tools)
652
+
653
+ # Merge provider-contributed instructions into chat_options.
654
+ if session_context.instructions:
655
+ combined_instructions = "\n".join(session_context.instructions)
656
+ if "instructions" in chat_options:
657
+ chat_options["instructions"] = f"{chat_options['instructions']}\n{combined_instructions}"
658
+ else:
659
+ chat_options["instructions"] = combined_instructions
660
+
661
+ return session_context, chat_options
662
+
663
+ async def _parse_non_streaming_response(
664
+ self,
665
+ context: _RunContext,
666
+ response: ChatResponse,
667
+ ) -> AgentResponse[Any]:
668
+ """Build the agent response, update conversation state, and run the after-providers pass."""
669
+ if not response:
670
+ raise AgentInvalidResponseException("Chat client did not return a response.")
671
+
672
+ for message in response.messages:
673
+ if message.author_name is None:
674
+ message.author_name = context["agent_name"]
675
+
676
+ session = context["session"]
677
+ if response._chrys_service_state_invalidated:
678
+ # The tool loop withheld this run's continuation handles and
679
+ # already cleared the session's service handle in place; the
680
+ # discarded service transcript was the only history store, so
681
+ # install the local fallback before the after-providers pass
682
+ # persists this run's messages through it.
683
+ self._install_local_history_fallback(session)
684
+ if (
685
+ session
686
+ and response.conversation_id
687
+ and not is_local_history_conversation_id(response.conversation_id)
688
+ and session.service_session_id != response.conversation_id
689
+ ):
690
+ session.service_session_id = response.conversation_id
691
+
692
+ agent_response = _build_agent_response_from_chat_response(
693
+ response,
694
+ response_format=context["chat_options"].get("response_format"),
695
+ )
696
+ session_context = context["session_context"]
697
+ # Providers observe the exact object returned to the caller. Response-
698
+ # level fields are shared run results and should be treated as read-only
699
+ # unless a provider intentionally coordinates a mutation.
700
+ session_context._response = agent_response
701
+ await self._run_after_providers(session=session, context=session_context)
702
+ return agent_response
703
+
704
+ def _parse_streaming_response(
705
+ self,
706
+ context: _RunContext,
707
+ stream_response: ResponseStream[ChatResponseUpdate, ChatResponse],
708
+ ) -> ResponseStream[AgentResponseUpdate, AgentResponse[Any]]:
709
+ """Map the tool-loop stream into agent updates and finalize session state.
710
+
711
+ The hook chain keeps streamed updates and the final response consistent:
712
+ ``map(...)`` assembles the response, the transform hook propagates the
713
+ conversation id, and the result hook updates history after assembly.
714
+ ``run()`` wraps this chain in ``from_awaitable``. Bypassing it can leave
715
+ final state out of sync with the updates already delivered to the caller.
716
+
717
+ The update transform uses ``self.name``, which may be ``None``. The result
718
+ hook backfills missing authors with the non-empty ``_get_agent_name()``;
719
+ both name sources are intentional.
720
+ """
721
+
722
+ async def _post_hook(response: AgentResponse[Any]) -> None:
723
+ # Update session with conversation_id derived from streaming raw
724
+ # updates. Using response_id here can break function-call
725
+ # continuation for APIs where response IDs are not valid
726
+ # conversation handles.
727
+ conversation_id = self._extract_conversation_id_from_streaming_response(response)
728
+
729
+ for message in response.messages:
730
+ if message.author_name is None:
731
+ message.author_name = context["agent_name"]
732
+
733
+ session = context["session"]
734
+ if response._chrys_service_state_invalidated:
735
+ # The tool loop withheld this run's continuation handles (its
736
+ # exhaustion strip left the service-side transcript holding
737
+ # unanswered calls); the raw updates still carry the stale id
738
+ # and the eager transform already wrote it mid-stream, so
739
+ # clear instead of restoring. Install the local fallback
740
+ # first: the after-providers pass below persists this run's
741
+ # messages through it, so the next run replays them instead
742
+ # of starting from the bare new user message.
743
+ self._install_local_history_fallback(session)
744
+ if session is not None:
745
+ # The raw-scan id recovered above is the withheld handle;
746
+ # record it so a caller-supplied conversation_id repeating
747
+ # it gets suppressed instead of re-sent with the replay.
748
+ for handle in (conversation_id, session.service_session_id):
749
+ if isinstance(handle, str) and handle and not is_local_history_conversation_id(handle):
750
+ session.invalidated_service_session_ids.add(handle)
751
+ session.service_session_id = None
752
+ elif (
753
+ session
754
+ and conversation_id
755
+ and not is_local_history_conversation_id(conversation_id)
756
+ and session.service_session_id != conversation_id
757
+ ):
758
+ session.service_session_id = conversation_id
759
+
760
+ session_context = context["session_context"]
761
+ # Agent owns the backing field; providers see the completed
762
+ # response through the read-only SessionContext.response property.
763
+ session_context._response = response
764
+ await self._run_after_providers(session=session, context=session_context)
765
+
766
+ def _propagate_conversation_id(update: AgentResponseUpdate) -> AgentResponseUpdate:
767
+ """Propagate the conversation id to the session as updates arrive."""
768
+ session = context["session"]
769
+ if session is None:
770
+ return update
771
+ raw = update.raw_representation
772
+ if not isinstance(raw, ChatResponseUpdate):
773
+ return update
774
+ conversation_id = raw.conversation_id
775
+ if (
776
+ isinstance(conversation_id, str)
777
+ and conversation_id
778
+ and not is_local_history_conversation_id(conversation_id)
779
+ # Exhaustion-tail updates defer to the tail's own verdict
780
+ # (post-hook restore or invalidation): mirroring them eagerly
781
+ # would survive a mid-tail abandonment as a stale pointer to a
782
+ # service transcript holding stripped, never-answered calls.
783
+ and not raw.__dict__.get("_chrys_exhaustion_tail_update", False)
784
+ and session.service_session_id != conversation_id
785
+ ):
786
+ session.service_session_id = conversation_id
787
+ return update
788
+
789
+ async def _finalizer(updates: Sequence[AgentResponseUpdate]) -> AgentResponse[Any]:
790
+ # Mapped updates span every tool-loop iteration, so copy the
791
+ # inner loop's normalized aggregate and final-call usage instead
792
+ # of re-summing raw cumulative usage snapshots here.
793
+ response = AgentResponse.from_updates(
794
+ updates,
795
+ output_format_type=context["chat_options"].get("response_format"),
796
+ )
797
+ chat_response = await stream_response.get_final_response()
798
+ response.usage_details = chat_response.usage_details
799
+ response.latest_usage_details = chat_response.latest_usage_details
800
+ # The tool loop is the sole reconstruction authority: reuse the
801
+ # tool loop's assembled Message objects instead of the local
802
+ # ``from_updates`` re-merge, which would rebuild multi-fragment
803
+ # function calls as new Content objects and drop kernel-stamped
804
+ # call provenance and folded result metadata. Element identity is
805
+ # preserved; the fresh list container means an after-provider
806
+ # mutating this response's list cannot corrupt the loop's.
807
+ response.messages = list(chat_response.messages)
808
+ if chat_response._chrys_service_state_invalidated:
809
+ # ``AgentResponse.from_updates`` restored ``response_id`` from
810
+ # the mapped updates even though the loop's finalizer withheld
811
+ # it; mirror the invalidation so the post-hook and the caller
812
+ # see no stale continuation handle.
813
+ response.response_id = None
814
+ response._chrys_service_state_invalidated = True
815
+ return response
816
+
817
+ stream = stream_response.map(
818
+ transform=partial(
819
+ map_chat_to_agent_update,
820
+ agent_name=self.name,
821
+ ),
822
+ finalizer=_finalizer,
823
+ )
824
+ return stream.with_transform_hook(_propagate_conversation_id).with_result_hook(_post_hook)
825
+
826
+ @staticmethod
827
+ def _extract_conversation_id_from_streaming_response(
828
+ response: AgentResponse[Any],
829
+ ) -> str | None:
830
+ """Read the latest non-empty conversation id from the raw streaming updates."""
831
+ raw = response.raw_representation
832
+ if raw is None:
833
+ return None
834
+
835
+ raw_items: list[Any] = list(raw) if isinstance(raw, list) else [raw]
836
+ for item in reversed(raw_items):
837
+ if isinstance(item, Mapping):
838
+ value = item.get("conversation_id")
839
+ if isinstance(value, str) and value:
840
+ return value
841
+ continue
842
+
843
+ value = getattr(item, "conversation_id", None)
844
+ if isinstance(value, str) and value:
845
+ return value
846
+
847
+ return None
848
+
849
+ def _install_local_history_fallback(self, session: AgentSession | None) -> None:
850
+ """Give an invalidated service-stored session a local history source.
851
+
852
+ Safety net behind the ``_prepare_run_context`` shadow install: any
853
+ service-stored run normally carries the handle-gated fallback from
854
+ run start, but if an invalidation arrives on a run whose preparation
855
+ did not select it, installing before the after-providers pass still
856
+ lets the standard ``after_run`` persist this run's input/output for
857
+ replay once no live handle exists. Sessions that already have a
858
+ loading HistoryProvider (including a previously installed fallback)
859
+ keep their existing history owner.
860
+ """
861
+ if session is None:
862
+ return
863
+ if any(provider.load_messages for provider in self.context_providers if isinstance(provider, HistoryProvider)):
864
+ return
865
+ self.context_providers.append(ServiceFallbackHistoryProvider())
866
+
867
+ async def _run_after_providers(
868
+ self,
869
+ *,
870
+ session: AgentSession | None,
871
+ context: SessionContext,
872
+ ) -> None:
873
+ """Run providers in reverse order with their session state and the completed response."""
874
+ provider_session = session
875
+ if provider_session is None and self.context_providers:
876
+ provider_session = AgentSession()
877
+
878
+ for provider in reversed(self.context_providers):
879
+ if provider_session is None:
880
+ raise RuntimeError("Provider session must be available when context providers are configured.")
881
+ await provider.after_run(
882
+ agent=self, # type: ignore[arg-type]
883
+ session=provider_session,
884
+ context=context,
885
+ state=provider_session.state.setdefault(provider.source_id, {}),
886
+ )
887
+
888
+
889
+ class Agent(AgentTelemetryLayer, _AgentCore):
890
+ """Production agent with telemetry over the orchestration core.
891
+
892
+ Chat and function middleware are split and forwarded to the client stack.
893
+ ``AgentTelemetryLayer`` sets and resets non-streaming ContextVars inside
894
+ the returned coroutine, so creation and execution may occur in different
895
+ tasks. Streaming spans activate around each pull and close before their
896
+ ContextVars are reset.
897
+ """
898
+
899
+ @overload
900
+ def run(
901
+ self,
902
+ messages: AgentRunInputs | None = None,
903
+ *,
904
+ stream: Literal[False] = False,
905
+ session: AgentSession | None = None,
906
+ middleware: ChatMiddleware | FunctionMiddleware | Sequence[ChatMiddleware | FunctionMiddleware] | None = None,
907
+ tools: ToolTypes | Callable[..., Any] | Sequence[ToolTypes | Callable[..., Any]] | None = None,
908
+ options: Mapping[str, Any] | None = None,
909
+ compaction_strategy: Any = None,
910
+ tokenizer: Any = None,
911
+ function_invocation_kwargs: Mapping[str, Any] | None = None,
912
+ client_kwargs: Mapping[str, Any] | None = None,
913
+ ) -> Awaitable[AgentResponse[Any]]: ...
914
+
915
+ @overload
916
+ def run(
917
+ self,
918
+ messages: AgentRunInputs | None = None,
919
+ *,
920
+ stream: Literal[True],
921
+ session: AgentSession | None = None,
922
+ middleware: ChatMiddleware | FunctionMiddleware | Sequence[ChatMiddleware | FunctionMiddleware] | None = None,
923
+ tools: ToolTypes | Callable[..., Any] | Sequence[ToolTypes | Callable[..., Any]] | None = None,
924
+ options: Mapping[str, Any] | None = None,
925
+ compaction_strategy: Any = None,
926
+ tokenizer: Any = None,
927
+ function_invocation_kwargs: Mapping[str, Any] | None = None,
928
+ client_kwargs: Mapping[str, Any] | None = None,
929
+ ) -> ResponseStream[AgentResponseUpdate, AgentResponse[Any]]: ...
930
+
931
+ @overload
932
+ def run(
933
+ self,
934
+ messages: AgentRunInputs | None = None,
935
+ *,
936
+ stream: bool,
937
+ session: AgentSession | None = None,
938
+ middleware: ChatMiddleware | FunctionMiddleware | Sequence[ChatMiddleware | FunctionMiddleware] | None = None,
939
+ tools: ToolTypes | Callable[..., Any] | Sequence[ToolTypes | Callable[..., Any]] | None = None,
940
+ options: Mapping[str, Any] | None = None,
941
+ compaction_strategy: Any = None,
942
+ tokenizer: Any = None,
943
+ function_invocation_kwargs: Mapping[str, Any] | None = None,
944
+ client_kwargs: Mapping[str, Any] | None = None,
945
+ ) -> Awaitable[AgentResponse[Any]] | ResponseStream[AgentResponseUpdate, AgentResponse[Any]]: ...
946
+
947
+ def run(
948
+ self,
949
+ messages: AgentRunInputs | None = None,
950
+ *,
951
+ stream: bool = False,
952
+ session: AgentSession | None = None,
953
+ middleware: ChatMiddleware | FunctionMiddleware | Sequence[ChatMiddleware | FunctionMiddleware] | None = None,
954
+ tools: ToolTypes | Callable[..., Any] | Sequence[ToolTypes | Callable[..., Any]] | None = None,
955
+ options: Mapping[str, Any] | None = None,
956
+ compaction_strategy: Any = None,
957
+ tokenizer: Any = None,
958
+ function_invocation_kwargs: Mapping[str, Any] | None = None,
959
+ client_kwargs: Mapping[str, Any] | None = None,
960
+ ) -> Awaitable[AgentResponse[Any]] | ResponseStream[AgentResponseUpdate, AgentResponse[Any]]:
961
+ """Combine constructor and run middleware, then delegate through telemetry.
962
+
963
+ Re-read ``self.middleware`` on every call so dynamic changes take effect.
964
+ The order is ``[ctor-function, ctor-chat, run-function, run-chat]``; a
965
+ non-empty combination overwrites ``client_kwargs["middleware"]``.
966
+ The ``middleware`` keyword itself must not be forwarded:
967
+ ``AgentTelemetryLayer.run`` would pass it to ``_AgentCore.run``, which
968
+ accepts middleware only through ``client_kwargs``.
969
+ """
970
+ ctor_split = split_middleware(self.middleware)
971
+ run_split = split_middleware(middleware)
972
+ combined_function_chat_middleware: list[Any] = [
973
+ *ctor_split.function,
974
+ *ctor_split.chat,
975
+ *run_split.function,
976
+ *run_split.chat,
977
+ ]
978
+ effective_client_kwargs = dict(client_kwargs) if client_kwargs is not None else {}
979
+ if combined_function_chat_middleware:
980
+ effective_client_kwargs["middleware"] = combined_function_chat_middleware
981
+ effective_function_invocation_kwargs = (
982
+ dict(function_invocation_kwargs) if function_invocation_kwargs is not None else {}
983
+ )
984
+ return super().run(
985
+ messages,
986
+ stream=stream,
987
+ session=session,
988
+ tools=tools,
989
+ options=options,
990
+ compaction_strategy=compaction_strategy,
991
+ tokenizer=tokenizer,
992
+ function_invocation_kwargs=effective_function_invocation_kwargs,
993
+ client_kwargs=effective_client_kwargs,
994
+ )