iCode-TUI 0.29.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1138) hide show
  1. chrys/__init__.py +16 -0
  2. chrys/app/__init__.py +3 -0
  3. chrys/app/acp/__init__.py +3 -0
  4. chrys/app/acp/bridge.py +664 -0
  5. chrys/app/acp/content.py +132 -0
  6. chrys/app/acp/doc/frontend-api.md +425 -0
  7. chrys/app/acp/history.py +585 -0
  8. chrys/app/acp/server.py +1796 -0
  9. chrys/app/acp/session_manager.py +1815 -0
  10. chrys/app/acp/tool_status.py +45 -0
  11. chrys/app/cli/__init__.py +3 -0
  12. chrys/app/cli/acp.py +178 -0
  13. chrys/app/cli/app.py +165 -0
  14. chrys/app/cli/approvals.py +101 -0
  15. chrys/app/cli/headless.py +223 -0
  16. chrys/app/cli/launch_cwd.py +42 -0
  17. chrys/app/cli/profiles.py +298 -0
  18. chrys/app/cli/progress.py +857 -0
  19. chrys/app/cli/run.py +425 -0
  20. chrys/app/cli/serve.py +1593 -0
  21. chrys/app/cli/tool_summary.py +225 -0
  22. chrys/app/cli/trajectory.py +201 -0
  23. chrys/app/cli/workflow.py +601 -0
  24. chrys/app/features/__init__.py +3 -0
  25. chrys/app/features/buddy/__init__.py +3 -0
  26. chrys/app/features/buddy/actions.py +68 -0
  27. chrys/app/features/buddy/animation.py +76 -0
  28. chrys/app/features/buddy/commands.py +196 -0
  29. chrys/app/features/buddy/docs/design.md +187 -0
  30. chrys/app/features/buddy/hatchery.py +80 -0
  31. chrys/app/features/buddy/lifecycle.py +41 -0
  32. chrys/app/features/buddy/model.py +265 -0
  33. chrys/app/features/buddy/pixel_renderer.py +113 -0
  34. chrys/app/features/buddy/pixel_sprites.py +279 -0
  35. chrys/app/features/buddy/portrait.py +76 -0
  36. chrys/app/features/buddy/progression.py +58 -0
  37. chrys/app/features/buddy/replies.py +191 -0
  38. chrys/app/features/buddy/sprites/alien.toml +70 -0
  39. chrys/app/features/buddy/sprites/axolotl.toml +71 -0
  40. chrys/app/features/buddy/sprites/bat.toml +69 -0
  41. chrys/app/features/buddy/sprites/bee.toml +69 -0
  42. chrys/app/features/buddy/sprites/cactus.toml +73 -0
  43. chrys/app/features/buddy/sprites/capybara.toml +70 -0
  44. chrys/app/features/buddy/sprites/cat.toml +69 -0
  45. chrys/app/features/buddy/sprites/crab.toml +70 -0
  46. chrys/app/features/buddy/sprites/dragon.toml +71 -0
  47. chrys/app/features/buddy/sprites/duck.toml +69 -0
  48. chrys/app/features/buddy/sprites/fox.toml +69 -0
  49. chrys/app/features/buddy/sprites/frog.toml +70 -0
  50. chrys/app/features/buddy/sprites/ghost.toml +70 -0
  51. chrys/app/features/buddy/sprites/goose.toml +69 -0
  52. chrys/app/features/buddy/sprites/jelly.toml +70 -0
  53. chrys/app/features/buddy/sprites/llama.toml +70 -0
  54. chrys/app/features/buddy/sprites/mushroom.toml +69 -0
  55. chrys/app/features/buddy/sprites/octopus.toml +70 -0
  56. chrys/app/features/buddy/sprites/owl.toml +70 -0
  57. chrys/app/features/buddy/sprites/panda.toml +71 -0
  58. chrys/app/features/buddy/sprites/penguin.toml +69 -0
  59. chrys/app/features/buddy/sprites/rabbit.toml +70 -0
  60. chrys/app/features/buddy/sprites/robot.toml +72 -0
  61. chrys/app/features/buddy/sprites/shark.toml +70 -0
  62. chrys/app/features/buddy/sprites/snail.toml +69 -0
  63. chrys/app/features/buddy/sprites/snake.toml +69 -0
  64. chrys/app/features/buddy/sprites/tabby.toml +69 -0
  65. chrys/app/features/buddy/sprites/turtle.toml +69 -0
  66. chrys/app/features/buddy/store.py +143 -0
  67. chrys/app/features/session_title/__init__.py +7 -0
  68. chrys/app/features/session_title/updater.py +582 -0
  69. chrys/app/installer.py +651 -0
  70. chrys/app/parsing.py +17 -0
  71. chrys/app/tui/__init__.py +3 -0
  72. chrys/app/tui/app.py +1287 -0
  73. chrys/app/tui/behaviors/__init__.py +3 -0
  74. chrys/app/tui/behaviors/click_outside_dismiss.py +90 -0
  75. chrys/app/tui/behaviors/insert_clipboard.py +73 -0
  76. chrys/app/tui/behaviors/right_click_copy.py +146 -0
  77. chrys/app/tui/binding_display.py +139 -0
  78. chrys/app/tui/buddy_messages.py +17 -0
  79. chrys/app/tui/buddy_reply.py +86 -0
  80. chrys/app/tui/chrys.tcss +549 -0
  81. chrys/app/tui/clipboard.py +109 -0
  82. chrys/app/tui/copy_messages.py +10 -0
  83. chrys/app/tui/i18n.py +230 -0
  84. chrys/app/tui/language.py +58 -0
  85. chrys/app/tui/notifications/__init__.py +12 -0
  86. chrys/app/tui/notifications/drivers.py +180 -0
  87. chrys/app/tui/notifications/service.py +126 -0
  88. chrys/app/tui/notifications/settings.py +141 -0
  89. chrys/app/tui/screens/__init__.py +3 -0
  90. chrys/app/tui/screens/agents/__init__.py +8 -0
  91. chrys/app/tui/screens/agents/agent_draft_store.py +937 -0
  92. chrys/app/tui/screens/agents/config.py +2142 -0
  93. chrys/app/tui/screens/agents/config.tcss +169 -0
  94. chrys/app/tui/screens/agents/panels/__init__.py +3 -0
  95. chrys/app/tui/screens/agents/panels/acp.py +1176 -0
  96. chrys/app/tui/screens/agents/panels/basic.py +497 -0
  97. chrys/app/tui/screens/agents/panels/compaction.py +231 -0
  98. chrys/app/tui/screens/agents/panels/config_card.py +218 -0
  99. chrys/app/tui/screens/agents/panels/instructions.py +91 -0
  100. chrys/app/tui/screens/agents/panels/markdown_report.py +76 -0
  101. chrys/app/tui/screens/agents/panels/mcp.py +2262 -0
  102. chrys/app/tui/screens/agents/panels/memory.py +684 -0
  103. chrys/app/tui/screens/agents/panels/path_entry.py +227 -0
  104. chrys/app/tui/screens/agents/panels/skills.py +806 -0
  105. chrys/app/tui/screens/agents/panels/subagents.py +652 -0
  106. chrys/app/tui/screens/agents/panels/tools.py +205 -0
  107. chrys/app/tui/screens/agents/picker.py +103 -0
  108. chrys/app/tui/screens/agents/skill_paths.py +38 -0
  109. chrys/app/tui/screens/agents/validation_messages.py +452 -0
  110. chrys/app/tui/screens/dialogs/__init__.py +35 -0
  111. chrys/app/tui/screens/dialogs/agent_load.py +834 -0
  112. chrys/app/tui/screens/dialogs/agent_load.tcss +78 -0
  113. chrys/app/tui/screens/dialogs/app_mode.py +55 -0
  114. chrys/app/tui/screens/dialogs/approval/__init__.py +15 -0
  115. chrys/app/tui/screens/dialogs/approval/approval.tcss +311 -0
  116. chrys/app/tui/screens/dialogs/approval/bodies/__init__.py +3 -0
  117. chrys/app/tui/screens/dialogs/approval/bodies/file_edit.py +210 -0
  118. chrys/app/tui/screens/dialogs/approval/bodies/sub_agent.py +82 -0
  119. chrys/app/tui/screens/dialogs/approval/body.py +110 -0
  120. chrys/app/tui/screens/dialogs/approval/dialog.py +565 -0
  121. chrys/app/tui/screens/dialogs/approval/mode.py +105 -0
  122. chrys/app/tui/screens/dialogs/ask_user.py +148 -0
  123. chrys/app/tui/screens/dialogs/ask_user.tcss +62 -0
  124. chrys/app/tui/screens/dialogs/base.py +84 -0
  125. chrys/app/tui/screens/dialogs/confirm.py +184 -0
  126. chrys/app/tui/screens/dialogs/confirm.tcss +46 -0
  127. chrys/app/tui/screens/dialogs/connection_test.py +183 -0
  128. chrys/app/tui/screens/dialogs/connection_test.tcss +92 -0
  129. chrys/app/tui/screens/dialogs/diagram.py +102 -0
  130. chrys/app/tui/screens/dialogs/diagram.tcss +24 -0
  131. chrys/app/tui/screens/dialogs/editor.py +378 -0
  132. chrys/app/tui/screens/dialogs/editor.tcss +132 -0
  133. chrys/app/tui/screens/dialogs/file_picker.py +582 -0
  134. chrys/app/tui/screens/dialogs/file_picker.tcss +78 -0
  135. chrys/app/tui/screens/dialogs/fork_session.py +219 -0
  136. chrys/app/tui/screens/dialogs/fork_session.tcss +75 -0
  137. chrys/app/tui/screens/dialogs/image_compression.py +102 -0
  138. chrys/app/tui/screens/dialogs/image_compression.tcss +28 -0
  139. chrys/app/tui/screens/dialogs/man_page.py +172 -0
  140. chrys/app/tui/screens/dialogs/man_page.tcss +39 -0
  141. chrys/app/tui/screens/dialogs/new_folder.py +277 -0
  142. chrys/app/tui/screens/dialogs/new_folder.tcss +51 -0
  143. chrys/app/tui/screens/dialogs/notice.tcss +14 -0
  144. chrys/app/tui/screens/dialogs/prompt_cache.py +144 -0
  145. chrys/app/tui/screens/dialogs/prompt_cache.tcss +38 -0
  146. chrys/app/tui/screens/dialogs/runtime_details.py +371 -0
  147. chrys/app/tui/screens/dialogs/runtime_details.tcss +49 -0
  148. chrys/app/tui/screens/dialogs/session_title.py +110 -0
  149. chrys/app/tui/screens/dialogs/session_title.tcss +43 -0
  150. chrys/app/tui/screens/dialogs/tool_view.py +207 -0
  151. chrys/app/tui/screens/dialogs/tool_view.tcss +43 -0
  152. chrys/app/tui/screens/dialogs/vision_unsupported.py +122 -0
  153. chrys/app/tui/screens/dialogs/vision_unsupported.tcss +35 -0
  154. chrys/app/tui/screens/dialogs/workflow_confirm.py +91 -0
  155. chrys/app/tui/screens/dialogs/workflow_confirm.tcss +17 -0
  156. chrys/app/tui/screens/dialogs/workflow_input.py +258 -0
  157. chrys/app/tui/screens/dialogs/workflow_input.tcss +73 -0
  158. chrys/app/tui/screens/dialogs/workflow_node.py +462 -0
  159. chrys/app/tui/screens/dialogs/workflow_node.tcss +41 -0
  160. chrys/app/tui/screens/dialogs/workflow_picker.py +46 -0
  161. chrys/app/tui/screens/dialogs/workflow_result.py +136 -0
  162. chrys/app/tui/screens/dialogs/workflow_result.tcss +14 -0
  163. chrys/app/tui/screens/diff/__init__.py +22 -0
  164. chrys/app/tui/screens/diff/rollback_modal.py +1381 -0
  165. chrys/app/tui/screens/diff/rollback_modal.tcss +248 -0
  166. chrys/app/tui/screens/diff/screen.py +1532 -0
  167. chrys/app/tui/screens/diff/screen.tcss +104 -0
  168. chrys/app/tui/screens/guides/__init__.py +7 -0
  169. chrys/app/tui/screens/guides/_docs/en/guides/configuration/agents.md +178 -0
  170. chrys/app/tui/screens/guides/_docs/en/guides/configuration/approval.md +117 -0
  171. chrys/app/tui/screens/guides/_docs/en/guides/configuration/chat-options.md +276 -0
  172. chrys/app/tui/screens/guides/_docs/en/guides/configuration/compaction.md +42 -0
  173. chrys/app/tui/screens/guides/_docs/en/guides/configuration/memory.md +48 -0
  174. chrys/app/tui/screens/guides/_docs/en/guides/configuration/models.md +158 -0
  175. chrys/app/tui/screens/guides/_docs/en/guides/configuration/settings.md +210 -0
  176. chrys/app/tui/screens/guides/_docs/en/guides/configuration/themes.md +61 -0
  177. chrys/app/tui/screens/guides/_docs/en/guides/configuration/tools.md +60 -0
  178. chrys/app/tui/screens/guides/_docs/en/guides/configuration/web-tools.md +286 -0
  179. chrys/app/tui/screens/guides/_docs/en/guides/daily-use/sessions.md +77 -0
  180. chrys/app/tui/screens/guides/_docs/en/guides/daily-use/workspaces.md +51 -0
  181. chrys/app/tui/screens/guides/_docs/en/guides/extensions/external-acp-agents.md +126 -0
  182. chrys/app/tui/screens/guides/_docs/en/guides/extensions/hooks.md +167 -0
  183. chrys/app/tui/screens/guides/_docs/en/guides/extensions/mcp.md +95 -0
  184. chrys/app/tui/screens/guides/_docs/en/guides/extensions/skills.md +175 -0
  185. chrys/app/tui/screens/guides/_docs/en/guides/running/icode-acp.md +61 -0
  186. chrys/app/tui/screens/guides/_docs/en/guides/running/icode-run.md +190 -0
  187. chrys/app/tui/screens/guides/_docs/en/guides/running/icode-serve.md +239 -0
  188. chrys/app/tui/screens/guides/_docs/en/guides/running/workflows.md +664 -0
  189. chrys/app/tui/screens/guides/_docs/en/reference/agent-profile.md +548 -0
  190. chrys/app/tui/screens/guides/_docs/en/reference/hooks.md +602 -0
  191. chrys/app/tui/screens/guides/_docs/en/reference/opentelemetry.md +177 -0
  192. chrys/app/tui/screens/guides/_docs/en/reference/settings.md +245 -0
  193. chrys/app/tui/screens/guides/_docs/en/reference/themes.md +149 -0
  194. chrys/app/tui/screens/guides/_docs/en/reference/tool-kinds-and-names.md +58 -0
  195. chrys/app/tui/screens/guides/_docs/en/reference/tui-slash-commands.md +76 -0
  196. chrys/app/tui/screens/guides/_docs/en/reference/workflows.md +1006 -0
  197. chrys/app/tui/screens/guides/_docs/en/start/getting-started.md +170 -0
  198. chrys/app/tui/screens/guides/_docs/en/start/what-is-icode.md +34 -0
  199. chrys/app/tui/screens/guides/_docs/index.yaml +100 -0
  200. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/agents.md +178 -0
  201. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/approval.md +117 -0
  202. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/chat-options.md +276 -0
  203. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/compaction.md +42 -0
  204. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/memory.md +48 -0
  205. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/models.md +158 -0
  206. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/settings.md +210 -0
  207. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/themes.md +61 -0
  208. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/tools.md +60 -0
  209. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/configuration/web-tools.md +286 -0
  210. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/daily-use/sessions.md +77 -0
  211. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/daily-use/workspaces.md +51 -0
  212. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/external-acp-agents.md +126 -0
  213. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/hooks.md +167 -0
  214. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/mcp.md +95 -0
  215. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/extensions/skills.md +175 -0
  216. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/icode-acp.md +61 -0
  217. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/icode-run.md +190 -0
  218. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/icode-serve.md +239 -0
  219. chrys/app/tui/screens/guides/_docs/zh-Hans/guides/running/workflows.md +664 -0
  220. chrys/app/tui/screens/guides/_docs/zh-Hans/reference/agent-profile.md +548 -0
  221. chrys/app/tui/screens/guides/_docs/zh-Hans/reference/hooks.md +602 -0
  222. chrys/app/tui/screens/guides/_docs/zh-Hans/reference/opentelemetry.md +177 -0
  223. chrys/app/tui/screens/guides/_docs/zh-Hans/reference/settings.md +245 -0
  224. chrys/app/tui/screens/guides/_docs/zh-Hans/reference/themes.md +149 -0
  225. chrys/app/tui/screens/guides/_docs/zh-Hans/reference/tool-kinds-and-names.md +58 -0
  226. chrys/app/tui/screens/guides/_docs/zh-Hans/reference/tui-slash-commands.md +76 -0
  227. chrys/app/tui/screens/guides/_docs/zh-Hans/reference/workflows.md +1005 -0
  228. chrys/app/tui/screens/guides/_docs/zh-Hans/start/getting-started.md +170 -0
  229. chrys/app/tui/screens/guides/_docs/zh-Hans/start/what-is-icode.md +34 -0
  230. chrys/app/tui/screens/guides/index.py +215 -0
  231. chrys/app/tui/screens/guides/reader.py +173 -0
  232. chrys/app/tui/screens/guides/screen.py +442 -0
  233. chrys/app/tui/screens/guides/screen.tcss +52 -0
  234. chrys/app/tui/screens/logs/__init__.py +7 -0
  235. chrys/app/tui/screens/logs/screen.py +504 -0
  236. chrys/app/tui/screens/logs/screen.tcss +45 -0
  237. chrys/app/tui/screens/main/__init__.py +7 -0
  238. chrys/app/tui/screens/main/action_availability.py +30 -0
  239. chrys/app/tui/screens/main/buddy_command.py +143 -0
  240. chrys/app/tui/screens/main/commands.py +1073 -0
  241. chrys/app/tui/screens/main/config_actions.py +464 -0
  242. chrys/app/tui/screens/main/copy_actions.py +117 -0
  243. chrys/app/tui/screens/main/dialog_controllers.py +826 -0
  244. chrys/app/tui/screens/main/dialog_gateway.py +166 -0
  245. chrys/app/tui/screens/main/diff_controller.py +478 -0
  246. chrys/app/tui/screens/main/engine_read_model.py +184 -0
  247. chrys/app/tui/screens/main/event_handlers.py +2014 -0
  248. chrys/app/tui/screens/main/input_flow.py +241 -0
  249. chrys/app/tui/screens/main/live_diff.py +244 -0
  250. chrys/app/tui/screens/main/main_screen_presenter.py +295 -0
  251. chrys/app/tui/screens/main/model_indicator.py +217 -0
  252. chrys/app/tui/screens/main/navigation.py +343 -0
  253. chrys/app/tui/screens/main/ports.py +571 -0
  254. chrys/app/tui/screens/main/recent_dirs.py +123 -0
  255. chrys/app/tui/screens/main/rollback_controller.py +586 -0
  256. chrys/app/tui/screens/main/runtime_info.py +71 -0
  257. chrys/app/tui/screens/main/screen.py +2265 -0
  258. chrys/app/tui/screens/main/screen.tcss +21 -0
  259. chrys/app/tui/screens/main/session_handlers.py +1127 -0
  260. chrys/app/tui/screens/main/session_title.py +239 -0
  261. chrys/app/tui/screens/main/settings_coordinator.py +365 -0
  262. chrys/app/tui/screens/main/settings_persistence.py +184 -0
  263. chrys/app/tui/screens/main/shell_mode.py +112 -0
  264. chrys/app/tui/screens/main/state.py +289 -0
  265. chrys/app/tui/screens/main/submit_state.py +11 -0
  266. chrys/app/tui/screens/main/subscriptions.py +177 -0
  267. chrys/app/tui/screens/main/suggestions.py +982 -0
  268. chrys/app/tui/screens/main/tool_action_bridge.py +75 -0
  269. chrys/app/tui/screens/main/view_adapter.py +1567 -0
  270. chrys/app/tui/screens/main/workflow_browser.py +747 -0
  271. chrys/app/tui/screens/main/workflow_content.py +248 -0
  272. chrys/app/tui/screens/main/workflow_controller.py +240 -0
  273. chrys/app/tui/screens/main/workflow_feedback.py +137 -0
  274. chrys/app/tui/screens/main/workflow_flow.py +49 -0
  275. chrys/app/tui/screens/main/workflow_run_control.py +456 -0
  276. chrys/app/tui/screens/main/workflow_session_view.py +316 -0
  277. chrys/app/tui/screens/main/workspace_actions.py +272 -0
  278. chrys/app/tui/screens/main/workspace_branch.py +194 -0
  279. chrys/app/tui/screens/menus/__init__.py +8 -0
  280. chrys/app/tui/screens/menus/languages.py +114 -0
  281. chrys/app/tui/screens/models/__init__.py +7 -0
  282. chrys/app/tui/screens/models/picker.py +147 -0
  283. chrys/app/tui/screens/models/screen.py +2043 -0
  284. chrys/app/tui/screens/models/screen.tcss +255 -0
  285. chrys/app/tui/screens/sessions/__init__.py +8 -0
  286. chrys/app/tui/screens/sessions/presenter.py +586 -0
  287. chrys/app/tui/screens/sessions/screen.py +779 -0
  288. chrys/app/tui/screens/sessions/screen.tcss +127 -0
  289. chrys/app/tui/screens/settings/__init__.py +29 -0
  290. chrys/app/tui/screens/settings/danger.py +26 -0
  291. chrys/app/tui/screens/settings/dialog.py +281 -0
  292. chrys/app/tui/screens/settings/layout.py +519 -0
  293. chrys/app/tui/screens/settings/migrate_dialog.py +253 -0
  294. chrys/app/tui/screens/settings/panes/__init__.py +3 -0
  295. chrys/app/tui/screens/settings/panes/notifications.py +248 -0
  296. chrys/app/tui/screens/settings/panes/sessions.py +148 -0
  297. chrys/app/tui/screens/settings/ports.py +55 -0
  298. chrys/app/tui/screens/settings/provenance.py +102 -0
  299. chrys/app/tui/screens/settings/rows.py +546 -0
  300. chrys/app/tui/screens/settings/settings.tcss +406 -0
  301. chrys/app/tui/screens/themes/__init__.py +8 -0
  302. chrys/app/tui/screens/themes/dialogs.py +330 -0
  303. chrys/app/tui/screens/themes/editor.py +632 -0
  304. chrys/app/tui/screens/themes/editor.tcss +268 -0
  305. chrys/app/tui/screens/themes/messages.py +95 -0
  306. chrys/app/tui/screens/themes/palette.py +694 -0
  307. chrys/app/tui/screens/themes/panel.py +113 -0
  308. chrys/app/tui/screens/themes/picker.py +203 -0
  309. chrys/app/tui/screens/themes/save.py +191 -0
  310. chrys/app/tui/support/__init__.py +3 -0
  311. chrys/app/tui/support/file_manager.py +64 -0
  312. chrys/app/tui/support/gc_freeze.py +1114 -0
  313. chrys/app/tui/support/path_shortcuts.py +50 -0
  314. chrys/app/tui/support/prompt_history.py +298 -0
  315. chrys/app/tui/support/workspace_mru.py +476 -0
  316. chrys/app/tui/terminal/__init__.py +6 -0
  317. chrys/app/tui/terminal/_pty_backend.py +114 -0
  318. chrys/app/tui/terminal/emulator/__init__.py +24 -0
  319. chrys/app/tui/terminal/emulator/buffer.py +456 -0
  320. chrys/app/tui/terminal/emulator/charsets.py +113 -0
  321. chrys/app/tui/terminal/emulator/core.py +963 -0
  322. chrys/app/tui/terminal/emulator/input.py +342 -0
  323. chrys/app/tui/terminal/emulator/parser.py +227 -0
  324. chrys/app/tui/terminal/emulator/pen.py +158 -0
  325. chrys/app/tui/terminal/launcher.py +455 -0
  326. chrys/app/tui/terminal/leaked_reports.py +44 -0
  327. chrys/app/tui/terminal/panel.py +323 -0
  328. chrys/app/tui/terminal/pty_process.py +405 -0
  329. chrys/app/tui/terminal/rendering.py +151 -0
  330. chrys/app/tui/terminal/shell.py +273 -0
  331. chrys/app/tui/terminal/shell_integration.py +199 -0
  332. chrys/app/tui/terminal/title.py +224 -0
  333. chrys/app/tui/terminal/widget.py +654 -0
  334. chrys/app/tui/theme.py +422 -0
  335. chrys/app/tui/theme_loader.py +201 -0
  336. chrys/app/tui/theme_stylesheet.py +49 -0
  337. chrys/app/tui/themes/__init__.py +3 -0
  338. chrys/app/tui/themes/document.py +120 -0
  339. chrys/app/tui/themes/preview.py +170 -0
  340. chrys/app/tui/themes/store.py +176 -0
  341. chrys/app/tui/toasts.py +69 -0
  342. chrys/app/tui/util/__init__.py +3 -0
  343. chrys/app/tui/util/context_pressure.py +146 -0
  344. chrys/app/tui/util/debug_log.py +292 -0
  345. chrys/app/tui/util/diff_entries.py +64 -0
  346. chrys/app/tui/util/formatting.py +53 -0
  347. chrys/app/tui/util/git_branch.py +326 -0
  348. chrys/app/tui/util/invocation_progress.py +57 -0
  349. chrys/app/tui/util/logo.py +32 -0
  350. chrys/app/tui/util/message_gate.py +35 -0
  351. chrys/app/tui/util/mutation_sources.py +14 -0
  352. chrys/app/tui/util/picker_text.py +27 -0
  353. chrys/app/tui/util/removal.py +47 -0
  354. chrys/app/tui/util/rich_style.py +36 -0
  355. chrys/app/tui/util/source_text.py +12 -0
  356. chrys/app/tui/util/static_update.py +88 -0
  357. chrys/app/tui/util/todo_format.py +51 -0
  358. chrys/app/tui/util/visibility.py +214 -0
  359. chrys/app/tui/widgets/__init__.py +60 -0
  360. chrys/app/tui/widgets/ask_user_controls.py +652 -0
  361. chrys/app/tui/widgets/ask_user_markdown.py +17 -0
  362. chrys/app/tui/widgets/ask_user_prompt.py +606 -0
  363. chrys/app/tui/widgets/buttons.py +52 -0
  364. chrys/app/tui/widgets/chat/__init__.py +3 -0
  365. chrys/app/tui/widgets/chat/agent_transcript_surface.py +818 -0
  366. chrys/app/tui/widgets/chat/chrome.py +296 -0
  367. chrys/app/tui/widgets/chat/compaction_card.py +353 -0
  368. chrys/app/tui/widgets/chat/context_fold.py +77 -0
  369. chrys/app/tui/widgets/chat/file_snapshot.py +91 -0
  370. chrys/app/tui/widgets/chat/image_preview.py +214 -0
  371. chrys/app/tui/widgets/chat/live_renderer.py +383 -0
  372. chrys/app/tui/widgets/chat/messages.py +1039 -0
  373. chrys/app/tui/widgets/chat/panel.py +1769 -0
  374. chrys/app/tui/widgets/chat/ports.py +245 -0
  375. chrys/app/tui/widgets/chat/renderers/__init__.py +7 -0
  376. chrys/app/tui/widgets/chat/renderers/ask_user.py +747 -0
  377. chrys/app/tui/widgets/chat/renderers/execute.py +431 -0
  378. chrys/app/tui/widgets/chat/renderers/file_edit.py +925 -0
  379. chrys/app/tui/widgets/chat/renderers/hosted_code.py +83 -0
  380. chrys/app/tui/widgets/chat/renderers/hosted_generic.py +330 -0
  381. chrys/app/tui/widgets/chat/renderers/hosted_image.py +107 -0
  382. chrys/app/tui/widgets/chat/renderers/hosted_mcp.py +43 -0
  383. chrys/app/tui/widgets/chat/renderers/hosted_search.py +167 -0
  384. chrys/app/tui/widgets/chat/renderers/hosted_shell.py +132 -0
  385. chrys/app/tui/widgets/chat/renderers/read_file.py +399 -0
  386. chrys/app/tui/widgets/chat/renderers/search.py +348 -0
  387. chrys/app/tui/widgets/chat/renderers/skill.py +564 -0
  388. chrys/app/tui/widgets/chat/renderers/sleep.py +287 -0
  389. chrys/app/tui/widgets/chat/renderers/sub_agent.py +1385 -0
  390. chrys/app/tui/widgets/chat/renderers/todo.py +114 -0
  391. chrys/app/tui/widgets/chat/renderers/web.py +69 -0
  392. chrys/app/tui/widgets/chat/replay.py +1605 -0
  393. chrys/app/tui/widgets/chat/replay_mount.py +322 -0
  394. chrys/app/tui/widgets/chat/scroll_controller.py +579 -0
  395. chrys/app/tui/widgets/chat/selection_controller.py +435 -0
  396. chrys/app/tui/widgets/chat/session_json.py +508 -0
  397. chrys/app/tui/widgets/chat/toc_model.py +156 -0
  398. chrys/app/tui/widgets/chat/tool_call.py +2478 -0
  399. chrys/app/tui/widgets/chat/tool_registry.py +753 -0
  400. chrys/app/tui/widgets/chat/tool_renderers.py +247 -0
  401. chrys/app/tui/widgets/chat/tool_view_builders.py +260 -0
  402. chrys/app/tui/widgets/chat/transcript_reader.py +57 -0
  403. chrys/app/tui/widgets/checkbox.py +24 -0
  404. chrys/app/tui/widgets/chrome/__init__.py +3 -0
  405. chrys/app/tui/widgets/chrome/app_header.py +283 -0
  406. chrys/app/tui/widgets/chrome/commands.py +95 -0
  407. chrys/app/tui/widgets/chrome/file_index.py +68 -0
  408. chrys/app/tui/widgets/chrome/file_scanner.py +670 -0
  409. chrys/app/tui/widgets/chrome/footer.py +284 -0
  410. chrys/app/tui/widgets/chrome/image_paste.py +351 -0
  411. chrys/app/tui/widgets/chrome/input_bar.py +1299 -0
  412. chrys/app/tui/widgets/chrome/status_bar.py +911 -0
  413. chrys/app/tui/widgets/chrome/suggestion_list.py +652 -0
  414. chrys/app/tui/widgets/click_affordance.py +24 -0
  415. chrys/app/tui/widgets/color_picker/__init__.py +254 -0
  416. chrys/app/tui/widgets/color_picker/controls.py +171 -0
  417. chrys/app/tui/widgets/color_picker/model.py +89 -0
  418. chrys/app/tui/widgets/dialog_buttons.py +58 -0
  419. chrys/app/tui/widgets/dialog_scroll.py +24 -0
  420. chrys/app/tui/widgets/diff_view/__init__.py +20 -0
  421. chrys/app/tui/widgets/diff_view/cells.py +59 -0
  422. chrys/app/tui/widgets/diff_view/code.py +280 -0
  423. chrys/app/tui/widgets/diff_view/compute.py +286 -0
  424. chrys/app/tui/widgets/diff_view/gutter.py +161 -0
  425. chrys/app/tui/widgets/diff_view/inline.py +77 -0
  426. chrys/app/tui/widgets/diff_view/palette.py +87 -0
  427. chrys/app/tui/widgets/diff_view/protocols.py +24 -0
  428. chrys/app/tui/widgets/diff_view/rows.py +138 -0
  429. chrys/app/tui/widgets/diff_view/unified.py +267 -0
  430. chrys/app/tui/widgets/diff_view/widget.py +209 -0
  431. chrys/app/tui/widgets/editor/__init__.py +79 -0
  432. chrys/app/tui/widgets/editor/coordinates.py +30 -0
  433. chrys/app/tui/widgets/editor/highlighter.py +139 -0
  434. chrys/app/tui/widgets/editor/keymaps.py +357 -0
  435. chrys/app/tui/widgets/editor/types.py +216 -0
  436. chrys/app/tui/widgets/editor/vim.py +1195 -0
  437. chrys/app/tui/widgets/editor/widget.py +401 -0
  438. chrys/app/tui/widgets/file_preview.py +58 -0
  439. chrys/app/tui/widgets/hatch.py +138 -0
  440. chrys/app/tui/widgets/input.py +66 -0
  441. chrys/app/tui/widgets/loading.py +125 -0
  442. chrys/app/tui/widgets/markdown/__init__.py +25 -0
  443. chrys/app/tui/widgets/markdown/_utils.py +21 -0
  444. chrys/app/tui/widgets/markdown/anchors.py +71 -0
  445. chrys/app/tui/widgets/markdown/blocks.py +115 -0
  446. chrys/app/tui/widgets/markdown/copyable.py +163 -0
  447. chrys/app/tui/widgets/markdown/diagram/__init__.py +35 -0
  448. chrys/app/tui/widgets/markdown/diagram/canvas.py +409 -0
  449. chrys/app/tui/widgets/markdown/diagram/charts.py +52 -0
  450. chrys/app/tui/widgets/markdown/diagram/diagnostics.py +101 -0
  451. chrys/app/tui/widgets/markdown/diagram/geometry.py +46 -0
  452. chrys/app/tui/widgets/markdown/diagram/layout.py +494 -0
  453. chrys/app/tui/widgets/markdown/diagram/messages.py +18 -0
  454. chrys/app/tui/widgets/markdown/diagram/model.py +275 -0
  455. chrys/app/tui/widgets/markdown/diagram/parser.py +173 -0
  456. chrys/app/tui/widgets/markdown/diagram/parsers/__init__.py +3 -0
  457. chrys/app/tui/widgets/markdown/diagram/parsers/charts.py +349 -0
  458. chrys/app/tui/widgets/markdown/diagram/parsers/common.py +403 -0
  459. chrys/app/tui/widgets/markdown/diagram/parsers/graphs.py +1072 -0
  460. chrys/app/tui/widgets/markdown/diagram/parsers/planning.py +317 -0
  461. chrys/app/tui/widgets/markdown/diagram/parsers/schedule.py +298 -0
  462. chrys/app/tui/widgets/markdown/diagram/parsers/schemas.py +241 -0
  463. chrys/app/tui/widgets/markdown/diagram/parsers/structure.py +221 -0
  464. chrys/app/tui/widgets/markdown/diagram/presentation.py +32 -0
  465. chrys/app/tui/widgets/markdown/diagram/renderers/__init__.py +3 -0
  466. chrys/app/tui/widgets/markdown/diagram/renderers/charts.py +275 -0
  467. chrys/app/tui/widgets/markdown/diagram/renderers/common.py +73 -0
  468. chrys/app/tui/widgets/markdown/diagram/renderers/planning.py +157 -0
  469. chrys/app/tui/widgets/markdown/diagram/renderers/schedule.py +202 -0
  470. chrys/app/tui/widgets/markdown/diagram/renderers/structure.py +77 -0
  471. chrys/app/tui/widgets/markdown/diagram/renderers/xy.py +385 -0
  472. chrys/app/tui/widgets/markdown/diagram/router.py +651 -0
  473. chrys/app/tui/widgets/markdown/diagram/specs/__init__.py +3 -0
  474. chrys/app/tui/widgets/markdown/diagram/specs/charts.py +94 -0
  475. chrys/app/tui/widgets/markdown/diagram/specs/planning.py +68 -0
  476. chrys/app/tui/widgets/markdown/diagram/specs/schedule.py +65 -0
  477. chrys/app/tui/widgets/markdown/diagram/specs/structure.py +23 -0
  478. chrys/app/tui/widgets/markdown/diagram/viewer.py +92 -0
  479. chrys/app/tui/widgets/markdown/links.py +47 -0
  480. chrys/app/tui/widgets/markdown/math/__init__.py +36 -0
  481. chrys/app/tui/widgets/markdown/math/layout.py +739 -0
  482. chrys/app/tui/widgets/markdown/math/markdown.py +291 -0
  483. chrys/app/tui/widgets/markdown/math/parser.py +325 -0
  484. chrys/app/tui/widgets/markdown/math/symbols.py +325 -0
  485. chrys/app/tui/widgets/markdown/navigator.py +80 -0
  486. chrys/app/tui/widgets/markdown/parser.py +1057 -0
  487. chrys/app/tui/widgets/markdown/stream.py +73 -0
  488. chrys/app/tui/widgets/markdown/toc.py +98 -0
  489. chrys/app/tui/widgets/markdown/viewer.py +146 -0
  490. chrys/app/tui/widgets/markdown/widget.py +2046 -0
  491. chrys/app/tui/widgets/marquee.py +101 -0
  492. chrys/app/tui/widgets/option_menu.py +82 -0
  493. chrys/app/tui/widgets/page_navigator.py +118 -0
  494. chrys/app/tui/widgets/select.py +196 -0
  495. chrys/app/tui/widgets/selection.py +59 -0
  496. chrys/app/tui/widgets/sidebar/__init__.py +3 -0
  497. chrys/app/tui/widgets/sidebar/buddy.py +367 -0
  498. chrys/app/tui/widgets/sidebar/context.py +482 -0
  499. chrys/app/tui/widgets/sidebar/debug.py +195 -0
  500. chrys/app/tui/widgets/sidebar/empty_state.py +11 -0
  501. chrys/app/tui/widgets/sidebar/panel.py +198 -0
  502. chrys/app/tui/widgets/sidebar/tasks.py +198 -0
  503. chrys/app/tui/widgets/sidebar/toc.py +166 -0
  504. chrys/app/tui/widgets/sidebar/toc_types.py +30 -0
  505. chrys/app/tui/widgets/syntax_theme.py +85 -0
  506. chrys/app/tui/widgets/text_area.py +258 -0
  507. chrys/app/tui/widgets/trajectory/__init__.py +8 -0
  508. chrys/app/tui/widgets/trajectory/chartkit.py +264 -0
  509. chrys/app/tui/widgets/trajectory/insights.py +1266 -0
  510. chrys/app/tui/widgets/trajectory/overview.py +1101 -0
  511. chrys/app/tui/widgets/trajectory/panel.py +983 -0
  512. chrys/app/tui/widgets/trajectory/presentation.py +311 -0
  513. chrys/app/tui/widgets/trajectory/session_info.py +124 -0
  514. chrys/app/tui/widgets/trajectory/text_view.py +124 -0
  515. chrys/app/tui/widgets/trajectory/timeline.py +380 -0
  516. chrys/app/tui/widgets/welcome.py +126 -0
  517. chrys/app/tui/widgets/workflow/__init__.py +5 -0
  518. chrys/app/tui/widgets/workflow/edges.py +89 -0
  519. chrys/app/tui/widgets/workflow/graph.py +869 -0
  520. chrys/app/tui/widgets/workflow/info.py +310 -0
  521. chrys/app/tui/widgets/workflow/node_view.py +27 -0
  522. chrys/app/tui/widgets/workflow/output.py +225 -0
  523. chrys/app/tui/widgets/workflow/panel.py +658 -0
  524. chrys/app/tui/widgets/workflow/projector.py +324 -0
  525. chrys/app/tui/widgets/workflow/records.py +69 -0
  526. chrys/app/tui/widgets/workflow/scrollbar.py +82 -0
  527. chrys/app/tui/widgets/workflow/selection.py +151 -0
  528. chrys/app/tui/widgets/workflow/source.py +65 -0
  529. chrys/app/tui/widgets/workflow/text.py +257 -0
  530. chrys/app/tui/widgets/workflow/values.py +317 -0
  531. chrys/foundation/__init__.py +0 -0
  532. chrys/foundation/branding.py +15 -0
  533. chrys/foundation/config/__init__.py +7 -0
  534. chrys/foundation/config/coercion.py +374 -0
  535. chrys/foundation/config/context.py +34 -0
  536. chrys/foundation/config/env_file.py +141 -0
  537. chrys/foundation/config/env_layers.py +370 -0
  538. chrys/foundation/config/migrations.py +521 -0
  539. chrys/foundation/config/notification_events.py +22 -0
  540. chrys/foundation/config/process_settings.py +332 -0
  541. chrys/foundation/config/runtime_pointer.py +133 -0
  542. chrys/foundation/config/settings.py +1952 -0
  543. chrys/foundation/config/settings_store.py +996 -0
  544. chrys/foundation/config/spec.py +396 -0
  545. chrys/foundation/config/user_settings.py +140 -0
  546. chrys/foundation/config/warnings.py +262 -0
  547. chrys/foundation/config/web_values.py +135 -0
  548. chrys/foundation/config/yaml_store.py +487 -0
  549. chrys/foundation/errors/__init__.py +68 -0
  550. chrys/foundation/errors/_legacy.py +117 -0
  551. chrys/foundation/errors/_text.py +13 -0
  552. chrys/foundation/errors/_walk.py +85 -0
  553. chrys/foundation/errors/classify.py +278 -0
  554. chrys/foundation/errors/display.py +274 -0
  555. chrys/foundation/errors/formatting.py +283 -0
  556. chrys/foundation/errors/kinds.py +67 -0
  557. chrys/foundation/errors/network.py +497 -0
  558. chrys/foundation/errors/provider.py +445 -0
  559. chrys/foundation/errors/route.py +85 -0
  560. chrys/foundation/events/__init__.py +8 -0
  561. chrys/foundation/events/bus.py +143 -0
  562. chrys/foundation/events/types.py +1629 -0
  563. chrys/foundation/events/workflow.py +38 -0
  564. chrys/foundation/hosted_tools.py +142 -0
  565. chrys/foundation/i18n/__init__.py +32 -0
  566. chrys/foundation/i18n/_catalogs/zh-Hans/LC_MESSAGES/chrys.mo +0 -0
  567. chrys/foundation/i18n/catalogs.py +42 -0
  568. chrys/foundation/i18n/formatting.py +202 -0
  569. chrys/foundation/i18n/locale.py +92 -0
  570. chrys/foundation/i18n/localizer.py +187 -0
  571. chrys/foundation/i18n/messages.py +268 -0
  572. chrys/foundation/io/__init__.py +3 -0
  573. chrys/foundation/io/result_content.py +237 -0
  574. chrys/foundation/models/__init__.py +3 -0
  575. chrys/foundation/models/approval_reuse.py +23 -0
  576. chrys/foundation/models/ask_user.py +468 -0
  577. chrys/foundation/models/execution.py +18 -0
  578. chrys/foundation/models/history_markers.py +115 -0
  579. chrys/foundation/models/invocations.py +63 -0
  580. chrys/foundation/models/mutation_scope.py +28 -0
  581. chrys/foundation/models/session_env.py +87 -0
  582. chrys/foundation/models/session_surface.py +29 -0
  583. chrys/foundation/models/todos.py +74 -0
  584. chrys/foundation/models/turns.py +125 -0
  585. chrys/foundation/models/workflow_session.py +89 -0
  586. chrys/foundation/models/workspace.py +59 -0
  587. chrys/foundation/net/__init__.py +3 -0
  588. chrys/foundation/net/address_class.py +57 -0
  589. chrys/foundation/net/route_probe.py +94 -0
  590. chrys/foundation/net/url.py +107 -0
  591. chrys/foundation/observability/__init__.py +3 -0
  592. chrys/foundation/observability/exporters.py +174 -0
  593. chrys/foundation/observability/gate.py +31 -0
  594. chrys/foundation/observability/setup.py +497 -0
  595. chrys/foundation/observability/sink.py +228 -0
  596. chrys/foundation/patches/__init__.py +19 -0
  597. chrys/foundation/patches/mdurl_cache.py +59 -0
  598. chrys/foundation/patches/patcher.py +287 -0
  599. chrys/foundation/patches/staged_members.py +205 -0
  600. chrys/foundation/patches/textual_block_border.py +105 -0
  601. chrys/foundation/patches/textual_button_ansi.py +65 -0
  602. chrys/foundation/patches/textual_button_ansi_old.txt +68 -0
  603. chrys/foundation/patches/textual_callback_cache.py +248 -0
  604. chrys/foundation/patches/textual_callback_dispatch.py +109 -0
  605. chrys/foundation/patches/textual_compositor_cjk.py +566 -0
  606. chrys/foundation/patches/textual_dispatch_cache.py +150 -0
  607. chrys/foundation/patches/textual_ime_cursor_anchor.py +92 -0
  608. chrys/foundation/patches/textual_kitty_keyboard.py +268 -0
  609. chrys/foundation/patches/textual_lru_acyclic.py +241 -0
  610. chrys/foundation/patches/textual_message_pump.py +136 -0
  611. chrys/foundation/patches/textual_node_diet.py +875 -0
  612. chrys/foundation/patches/textual_one_shot_timer.py +48 -0
  613. chrys/foundation/patches/textual_option_list.py +90 -0
  614. chrys/foundation/patches/textual_precompose.py +89 -0
  615. chrys/foundation/patches/textual_pruned_tabs.py +69 -0
  616. chrys/foundation/patches/textual_reflow_reuse.py +1753 -0
  617. chrys/foundation/patches/textual_removed_node_caches.py +86 -0
  618. chrys/foundation/patches/textual_removed_screen_callbacks.py +55 -0
  619. chrys/foundation/patches/textual_selection_extract.py +48 -0
  620. chrys/foundation/patches/textual_strip_cycles.py +119 -0
  621. chrys/foundation/patches/textual_tab_selection.py +1611 -0
  622. chrys/foundation/patches/textual_timer_skip.py +70 -0
  623. chrys/foundation/patches/textual_utf8_decoder.py +106 -0
  624. chrys/foundation/patches/textual_win_sleep.py +167 -0
  625. chrys/foundation/patches/watchdog_fsevents.py +70 -0
  626. chrys/foundation/patches/watchdog_windows.py +52 -0
  627. chrys/foundation/platform/__init__.py +556 -0
  628. chrys/foundation/platform/c_api.py +32 -0
  629. chrys/foundation/platform/child_reap.py +107 -0
  630. chrys/foundation/platform/command_line.py +95 -0
  631. chrys/foundation/platform/files.py +1470 -0
  632. chrys/foundation/platform/output_capture.py +113 -0
  633. chrys/foundation/platform/paths.py +193 -0
  634. chrys/foundation/platform/process.py +1468 -0
  635. chrys/foundation/platform/pty_output.py +19 -0
  636. chrys/foundation/platform/runtime_paths.py +281 -0
  637. chrys/foundation/platform/win_console.py +129 -0
  638. chrys/foundation/reasoning_origin.py +67 -0
  639. chrys/foundation/recovery.py +16 -0
  640. chrys/foundation/retry.py +278 -0
  641. chrys/foundation/text/__init__.py +3 -0
  642. chrys/foundation/text/encoding.py +603 -0
  643. chrys/foundation/text/images.py +304 -0
  644. chrys/foundation/text/lines.py +24 -0
  645. chrys/foundation/text/mentions.py +90 -0
  646. chrys/foundation/text/model_json.py +25 -0
  647. chrys/foundation/text/reading_order.py +20 -0
  648. chrys/foundation/text/tokenizer.py +25 -0
  649. chrys/foundation/text/tool_output.py +287 -0
  650. chrys/foundation/text/yaml_io.py +62 -0
  651. chrys/foundation/tool_call_context.py +202 -0
  652. chrys/foundation/tool_execution_stamp.py +184 -0
  653. chrys/foundation/tool_invocation_order.py +48 -0
  654. chrys/foundation/tool_kinds.py +102 -0
  655. chrys/foundation/tool_result_metadata.py +287 -0
  656. chrys/foundation/trajectory/__init__.py +10 -0
  657. chrys/foundation/trajectory/context.py +481 -0
  658. chrys/foundation/trajectory/envelope.py +571 -0
  659. chrys/foundation/trajectory/event_types.py +294 -0
  660. chrys/foundation/trajectory/finalizers.py +46 -0
  661. chrys/foundation/trajectory/fingerprint.py +62 -0
  662. chrys/foundation/trajectory/ids.py +155 -0
  663. chrys/foundation/trajectory/keys.py +99 -0
  664. chrys/foundation/trajectory/lease.py +173 -0
  665. chrys/foundation/trajectory/metadata.py +72 -0
  666. chrys/foundation/trajectory/reader.py +271 -0
  667. chrys/foundation/trajectory/recovery.py +225 -0
  668. chrys/foundation/trajectory/revisions.py +152 -0
  669. chrys/foundation/trajectory/segments.py +225 -0
  670. chrys/foundation/trajectory/tools.py +117 -0
  671. chrys/foundation/trajectory/usage.py +151 -0
  672. chrys/foundation/trajectory/writer.py +972 -0
  673. chrys/foundation/trajectory_timing.py +97 -0
  674. chrys/foundation/util/__init__.py +3 -0
  675. chrys/foundation/util/async_tasks.py +29 -0
  676. chrys/foundation/util/chrys_headers.py +23 -0
  677. chrys/foundation/util/env_templates.py +104 -0
  678. chrys/foundation/util/filenames.py +96 -0
  679. chrys/foundation/util/header_charset.py +138 -0
  680. chrys/foundation/util/httpx_helpers.py +12 -0
  681. chrys/foundation/util/lock.py +236 -0
  682. chrys/foundation/util/once_close.py +46 -0
  683. chrys/foundation/util/session_ids.py +29 -0
  684. chrys/foundation/util/shell_tokens.py +88 -0
  685. chrys/foundation/util/sub_agent_context.py +38 -0
  686. chrys/foundation/util/time.py +35 -0
  687. chrys/foundation/util/unicode_scalars.py +36 -0
  688. chrys/foundation/vendor/__init__.py +43 -0
  689. chrys/foundation/vendor/ripgrep/rg-aarch64-apple-darwin +0 -0
  690. chrys/foundation/vendor/ripgrep/rg-aarch64-pc-windows-msvc.exe +0 -0
  691. chrys/foundation/vendor/ripgrep/rg-aarch64-unknown-linux-gnu +0 -0
  692. chrys/foundation/vendor/ripgrep/rg-x86_64-apple-darwin +0 -0
  693. chrys/foundation/vendor/ripgrep/rg-x86_64-pc-windows-msvc.exe +0 -0
  694. chrys/foundation/vendor/ripgrep/rg-x86_64-unknown-linux-musl +0 -0
  695. chrys/kernel/__init__.py +256 -0
  696. chrys/kernel/_content.py +1866 -0
  697. chrys/kernel/_loop_recorder.py +631 -0
  698. chrys/kernel/_null_overlay.py +1026 -0
  699. chrys/kernel/_result_ceiling.py +216 -0
  700. chrys/kernel/_serialization.py +431 -0
  701. chrys/kernel/_tool_arg_errors.py +1629 -0
  702. chrys/kernel/_tool_execution.py +1172 -0
  703. chrys/kernel/_tool_expansion.py +26 -0
  704. chrys/kernel/_types.py +2598 -0
  705. chrys/kernel/agent.py +994 -0
  706. chrys/kernel/client.py +1084 -0
  707. chrys/kernel/compaction.py +1315 -0
  708. chrys/kernel/exceptions.py +211 -0
  709. chrys/kernel/exchanges.py +683 -0
  710. chrys/kernel/identity.py +95 -0
  711. chrys/kernel/images.py +36 -0
  712. chrys/kernel/instrumentation.py +1618 -0
  713. chrys/kernel/loop.py +2308 -0
  714. chrys/kernel/middleware.py +616 -0
  715. chrys/kernel/sessions.py +604 -0
  716. chrys/kernel/tools.py +1061 -0
  717. chrys/kernel/types.py +126 -0
  718. chrys/orchestration/__init__.py +3 -0
  719. chrys/orchestration/engine/__init__.py +3 -0
  720. chrys/orchestration/engine/assembly.py +236 -0
  721. chrys/orchestration/engine/build/__init__.py +3 -0
  722. chrys/orchestration/engine/build/builder.py +1061 -0
  723. chrys/orchestration/engine/build/construction.py +408 -0
  724. chrys/orchestration/engine/build/loaded.py +139 -0
  725. chrys/orchestration/engine/engine.py +789 -0
  726. chrys/orchestration/engine/execution.py +771 -0
  727. chrys/orchestration/engine/loader.py +865 -0
  728. chrys/orchestration/engine/rollback.py +918 -0
  729. chrys/orchestration/engine/run/__init__.py +3 -0
  730. chrys/orchestration/engine/run/active_injection.py +419 -0
  731. chrys/orchestration/engine/run/attachments.py +519 -0
  732. chrys/orchestration/engine/run/bindings.py +1095 -0
  733. chrys/orchestration/engine/run/coordinator.py +1062 -0
  734. chrys/orchestration/engine/run/finalizer.py +417 -0
  735. chrys/orchestration/engine/run/input_refs.py +60 -0
  736. chrys/orchestration/engine/run/prompt_content.py +457 -0
  737. chrys/orchestration/engine/run/resume.py +299 -0
  738. chrys/orchestration/engine/run/retry.py +948 -0
  739. chrys/orchestration/engine/run/runner.py +625 -0
  740. chrys/orchestration/engine/run/runtime_skills.py +146 -0
  741. chrys/orchestration/engine/run/sub_agent_coordination.py +126 -0
  742. chrys/orchestration/engine/run/turn_hooks.py +258 -0
  743. chrys/orchestration/engine/run/turn_state.py +149 -0
  744. chrys/orchestration/engine/run/working_dir.py +50 -0
  745. chrys/orchestration/engine/session_lifecycle.py +2013 -0
  746. chrys/orchestration/engine/state/__init__.py +3 -0
  747. chrys/orchestration/engine/state/active_session.py +183 -0
  748. chrys/orchestration/engine/state/controls.py +639 -0
  749. chrys/orchestration/engine/state/current_agent.py +26 -0
  750. chrys/orchestration/engine/state/lifecycle_permits.py +395 -0
  751. chrys/orchestration/engine/state/machine.py +193 -0
  752. chrys/orchestration/engine/state/session_writer.py +434 -0
  753. chrys/orchestration/engine/trajectory.py +714 -0
  754. chrys/orchestration/engine/usage.py +118 -0
  755. chrys/orchestration/invoker/__init__.py +3 -0
  756. chrys/orchestration/invoker/acp.py +727 -0
  757. chrys/orchestration/invoker/acp_protocol.py +1115 -0
  758. chrys/orchestration/invoker/acp_spec.py +79 -0
  759. chrys/orchestration/invoker/attempts.py +994 -0
  760. chrys/orchestration/invoker/child_compaction.py +131 -0
  761. chrys/orchestration/invoker/child_history.py +99 -0
  762. chrys/orchestration/invoker/contracts.py +253 -0
  763. chrys/orchestration/invoker/evidence.py +94 -0
  764. chrys/orchestration/invoker/kernel.py +343 -0
  765. chrys/orchestration/invoker/origin.py +79 -0
  766. chrys/orchestration/invoker/resources.py +343 -0
  767. chrys/orchestration/invoker/runtime.py +299 -0
  768. chrys/orchestration/invoker/usage.py +80 -0
  769. chrys/orchestration/session_hooks.py +117 -0
  770. chrys/orchestration/session_host.py +1023 -0
  771. chrys/orchestration/session_resources.py +95 -0
  772. chrys/orchestration/session_usage.py +219 -0
  773. chrys/orchestration/startup.py +255 -0
  774. chrys/orchestration/sub_agents/__init__.py +3 -0
  775. chrys/orchestration/sub_agents/acp_policy.py +269 -0
  776. chrys/orchestration/sub_agents/kernel_policy.py +848 -0
  777. chrys/orchestration/sub_agents/registration.py +44 -0
  778. chrys/orchestration/sub_agents/shell.py +337 -0
  779. chrys/orchestration/sub_agents/tools.py +2228 -0
  780. chrys/orchestration/workflows/__init__.py +3 -0
  781. chrys/orchestration/workflows/agent_archive.py +156 -0
  782. chrys/orchestration/workflows/agent_node.py +894 -0
  783. chrys/orchestration/workflows/agent_node_build.py +687 -0
  784. chrys/orchestration/workflows/catalog.py +332 -0
  785. chrys/orchestration/workflows/coordinator.py +771 -0
  786. chrys/orchestration/workflows/hooks.py +176 -0
  787. chrys/orchestration/workflows/preview.py +322 -0
  788. chrys/orchestration/workflows/rollback.py +151 -0
  789. chrys/orchestration/workflows/runner.py +986 -0
  790. chrys/orchestration/workflows/session.py +390 -0
  791. chrys/orchestration/workflows/settings.py +97 -0
  792. chrys/orchestration/workflows/validation.py +674 -0
  793. chrys/orchestration/workflows/worker_client.py +1002 -0
  794. chrys/service/__init__.py +3 -0
  795. chrys/service/acp_client/__init__.py +65 -0
  796. chrys/service/acp_client/_transport.py +922 -0
  797. chrys/service/acp_client/client.py +1311 -0
  798. chrys/service/acp_client/errors.py +224 -0
  799. chrys/service/acp_client/protocol.py +220 -0
  800. chrys/service/acp_client/spawn.py +239 -0
  801. chrys/service/acp_client/spec.py +100 -0
  802. chrys/service/agent_middleware/__init__.py +60 -0
  803. chrys/service/agent_middleware/_metadata_keys.py +52 -0
  804. chrys/service/agent_middleware/control/__init__.py +3 -0
  805. chrys/service/agent_middleware/control/approval.py +769 -0
  806. chrys/service/agent_middleware/control/approval_hooks.py +62 -0
  807. chrys/service/agent_middleware/control/ask_user.py +207 -0
  808. chrys/service/agent_middleware/control/interrupt.py +50 -0
  809. chrys/service/agent_middleware/control/sleep.py +118 -0
  810. chrys/service/agent_middleware/control/todo.py +117 -0
  811. chrys/service/agent_middleware/events/__init__.py +3 -0
  812. chrys/service/agent_middleware/events/hook_dispatch.py +250 -0
  813. chrys/service/agent_middleware/events/hosted_tools.py +1762 -0
  814. chrys/service/agent_middleware/events/intermediate_text.py +128 -0
  815. chrys/service/agent_middleware/events/rejection_metadata.py +50 -0
  816. chrys/service/agent_middleware/events/result_persistence.py +98 -0
  817. chrys/service/agent_middleware/events/sub_agent_events.py +951 -0
  818. chrys/service/agent_middleware/events/tool_error_text.py +22 -0
  819. chrys/service/agent_middleware/events/tool_events.py +779 -0
  820. chrys/service/agent_middleware/injection.py +510 -0
  821. chrys/service/agent_middleware/reminders/__init__.py +91 -0
  822. chrys/service/agent_middleware/reminders/archive_pointer.py +154 -0
  823. chrys/service/agent_middleware/reminders/base.py +25 -0
  824. chrys/service/agent_middleware/reminders/context_usage.py +74 -0
  825. chrys/service/agent_middleware/reminders/file_change.py +63 -0
  826. chrys/service/agent_middleware/reminders/mcp.py +39 -0
  827. chrys/service/agent_middleware/reminders/profile_switch.py +106 -0
  828. chrys/service/agent_middleware/reminders/runtime_env.py +190 -0
  829. chrys/service/agent_middleware/reminders/skills.py +44 -0
  830. chrys/service/agent_middleware/reminders/sub_agents.py +35 -0
  831. chrys/service/agent_middleware/reminders/todo.py +35 -0
  832. chrys/service/agent_middleware/reminders/turn_line.py +48 -0
  833. chrys/service/agent_middleware/response_validation.py +1380 -0
  834. chrys/service/agent_middleware/system_reminder.py +1107 -0
  835. chrys/service/agent_middleware/validators.py +396 -0
  836. chrys/service/analytics/__init__.py +124 -0
  837. chrys/service/analytics/_actions.py +896 -0
  838. chrys/service/analytics/_context_evidence.py +357 -0
  839. chrys/service/analytics/_critical_path.py +179 -0
  840. chrys/service/analytics/_facts.py +919 -0
  841. chrys/service/analytics/_insights.py +769 -0
  842. chrys/service/analytics/_metric_ops.py +83 -0
  843. chrys/service/analytics/_session_projection.py +460 -0
  844. chrys/service/analytics/_timeline.py +434 -0
  845. chrys/service/analytics/_turn_graph.py +1237 -0
  846. chrys/service/analytics/_turns.py +1377 -0
  847. chrys/service/analytics/_workflows.py +171 -0
  848. chrys/service/analytics/aggregation.py +1474 -0
  849. chrys/service/analytics/classification.py +128 -0
  850. chrys/service/analytics/export.py +803 -0
  851. chrys/service/analytics/findings.py +282 -0
  852. chrys/service/analytics/math.py +79 -0
  853. chrys/service/analytics/model.py +886 -0
  854. chrys/service/analytics/reader.py +321 -0
  855. chrys/service/approval/__init__.py +3 -0
  856. chrys/service/approval/arbitration.py +102 -0
  857. chrys/service/approval/command_identity.py +109 -0
  858. chrys/service/approval/correlation.py +57 -0
  859. chrys/service/approval/grant_store.py +108 -0
  860. chrys/service/approval/judge.py +589 -0
  861. chrys/service/approval/level.py +15 -0
  862. chrys/service/approval/policy.py +209 -0
  863. chrys/service/approval/reuse.py +223 -0
  864. chrys/service/approval/reuse_binding.py +133 -0
  865. chrys/service/approval/safety_classifier.py +536 -0
  866. chrys/service/approval/turn_context.py +49 -0
  867. chrys/service/context/__init__.py +3 -0
  868. chrys/service/context/compaction/__init__.py +38 -0
  869. chrys/service/context/compaction/breaker.py +156 -0
  870. chrys/service/context/compaction/budgets.py +93 -0
  871. chrys/service/context/compaction/calibration.py +77 -0
  872. chrys/service/context/compaction/compression.py +690 -0
  873. chrys/service/context/compaction/current_turn_drop.py +458 -0
  874. chrys/service/context/compaction/events.py +201 -0
  875. chrys/service/context/compaction/exclusions.py +261 -0
  876. chrys/service/context/compaction/groups.py +361 -0
  877. chrys/service/context/compaction/last_words.py +1996 -0
  878. chrys/service/context/compaction/last_words_state.py +752 -0
  879. chrys/service/context/compaction/scoped.py +472 -0
  880. chrys/service/context/compaction/spill.py +1947 -0
  881. chrys/service/context/compaction/strategy.py +1116 -0
  882. chrys/service/context/compaction/summaries.py +319 -0
  883. chrys/service/context/compaction/turns.py +375 -0
  884. chrys/service/context/manager.py +174 -0
  885. chrys/service/context/memory_loader.py +792 -0
  886. chrys/service/context/middleware/__init__.py +3 -0
  887. chrys/service/context/middleware/usage.py +332 -0
  888. chrys/service/context/providers/__init__.py +3 -0
  889. chrys/service/context/providers/context_management.py +432 -0
  890. chrys/service/context/providers/history.py +647 -0
  891. chrys/service/context/providers/memory.py +50 -0
  892. chrys/service/context/tool_content.py +22 -0
  893. chrys/service/hooks/__init__.py +37 -0
  894. chrys/service/hooks/detached_worker.py +118 -0
  895. chrys/service/hooks/docs/README.md +14 -0
  896. chrys/service/hooks/docs/authoring.md +684 -0
  897. chrys/service/hooks/docs/configuration.md +499 -0
  898. chrys/service/hooks/docs/design.md +359 -0
  899. chrys/service/hooks/docs/project-hooks.md +348 -0
  900. chrys/service/hooks/events.py +157 -0
  901. chrys/service/hooks/loader.py +665 -0
  902. chrys/service/hooks/manager.py +807 -0
  903. chrys/service/hooks/matcher.py +111 -0
  904. chrys/service/hooks/outbox.py +294 -0
  905. chrys/service/hooks/runner.py +572 -0
  906. chrys/service/hooks/schema.py +407 -0
  907. chrys/service/llm/__init__.py +7 -0
  908. chrys/service/llm/_structured_outputs.py +192 -0
  909. chrys/service/llm/anthropic_messages/__init__.py +9 -0
  910. chrys/service/llm/anthropic_messages/client.py +254 -0
  911. chrys/service/llm/anthropic_messages/decode.py +331 -0
  912. chrys/service/llm/anthropic_messages/history.py +279 -0
  913. chrys/service/llm/anthropic_messages/request.py +323 -0
  914. chrys/service/llm/anthropic_messages/server_tools.py +319 -0
  915. chrys/service/llm/anthropic_messages/stream.py +297 -0
  916. chrys/service/llm/anthropic_messages/thinking_binding.py +156 -0
  917. chrys/service/llm/chat_completions/__init__.py +9 -0
  918. chrys/service/llm/chat_completions/client.py +369 -0
  919. chrys/service/llm/chat_completions/decode.py +338 -0
  920. chrys/service/llm/chat_completions/history.py +657 -0
  921. chrys/service/llm/chat_completions/reasoning.py +244 -0
  922. chrys/service/llm/chat_completions/request.py +192 -0
  923. chrys/service/llm/chat_completions/stream.py +546 -0
  924. chrys/service/llm/chat_completions/validation.py +308 -0
  925. chrys/service/llm/clients.py +613 -0
  926. chrys/service/llm/images.py +65 -0
  927. chrys/service/llm/mock.py +413 -0
  928. chrys/service/llm/observer.py +446 -0
  929. chrys/service/llm/one_shot.py +106 -0
  930. chrys/service/llm/openai_exceptions.py +93 -0
  931. chrys/service/llm/openai_responses/__init__.py +10 -0
  932. chrys/service/llm/openai_responses/client.py +323 -0
  933. chrys/service/llm/openai_responses/decode.py +432 -0
  934. chrys/service/llm/openai_responses/history.py +519 -0
  935. chrys/service/llm/openai_responses/hosted.py +625 -0
  936. chrys/service/llm/openai_responses/replay.py +629 -0
  937. chrys/service/llm/openai_responses/request.py +342 -0
  938. chrys/service/llm/openai_responses/stream.py +847 -0
  939. chrys/service/llm/openai_timestamps.py +57 -0
  940. chrys/service/llm/providers.py +107 -0
  941. chrys/service/llm/proxy_route.py +54 -0
  942. chrys/service/llm/raw_http_log.py +309 -0
  943. chrys/service/llm/route_facts.py +63 -0
  944. chrys/service/llm/route_sessions.py +49 -0
  945. chrys/service/llm/sdk_anthropic.py +18 -0
  946. chrys/service/llm/sdk_headers.py +47 -0
  947. chrys/service/llm/sdk_openai.py +18 -0
  948. chrys/service/llm/sdk_retry.py +59 -0
  949. chrys/service/llm/wire_client.py +246 -0
  950. chrys/service/mcp/__init__.py +3 -0
  951. chrys/service/mcp/_connection.py +235 -0
  952. chrys/service/mcp/_http_transport.py +350 -0
  953. chrys/service/mcp/_progressive.py +459 -0
  954. chrys/service/mcp/_stdio_transport.py +614 -0
  955. chrys/service/mcp/_tool_mixins.py +96 -0
  956. chrys/service/mcp/adapter.py +1031 -0
  957. chrys/service/mcp/cache.py +476 -0
  958. chrys/service/mcp/content.py +32 -0
  959. chrys/service/mcp/errors.py +169 -0
  960. chrys/service/mcp/owned.py +1330 -0
  961. chrys/service/mcp/result_limits.py +168 -0
  962. chrys/service/mcp/thinking_warning.py +95 -0
  963. chrys/service/mcp/validation.py +74 -0
  964. chrys/service/mutations/__init__.py +84 -0
  965. chrys/service/mutations/coordination.py +894 -0
  966. chrys/service/mutations/detector.py +458 -0
  967. chrys/service/mutations/git_calibrator.py +397 -0
  968. chrys/service/mutations/git_state.py +647 -0
  969. chrys/service/mutations/pipeline.py +779 -0
  970. chrys/service/mutations/scanner.py +466 -0
  971. chrys/service/mutations/snapshot_files.py +233 -0
  972. chrys/service/mutations/store.py +625 -0
  973. chrys/service/mutations/tool_names.py +15 -0
  974. chrys/service/mutations/trace.py +246 -0
  975. chrys/service/mutations/tracker.py +1289 -0
  976. chrys/service/mutations/types.py +559 -0
  977. chrys/service/mutations/workspace_changes.py +1583 -0
  978. chrys/service/profiles/__init__.py +2 -0
  979. chrys/service/profiles/agents/__init__.py +3 -0
  980. chrys/service/profiles/agents/builtins/Code.yaml +199 -0
  981. chrys/service/profiles/agents/builtins/Explore.yaml +62 -0
  982. chrys/service/profiles/agents/builtins/General.yaml +114 -0
  983. chrys/service/profiles/agents/builtins/QA.yaml +128 -0
  984. chrys/service/profiles/agents/loader.py +808 -0
  985. chrys/service/profiles/agents/registry.py +727 -0
  986. chrys/service/profiles/agents/schema.py +371 -0
  987. chrys/service/profiles/agents/serializer.py +196 -0
  988. chrys/service/profiles/coercion.py +22 -0
  989. chrys/service/profiles/models/__init__.py +3 -0
  990. chrys/service/profiles/models/env_bridge.py +157 -0
  991. chrys/service/profiles/models/loader.py +282 -0
  992. chrys/service/profiles/models/options.py +438 -0
  993. chrys/service/profiles/models/registry.py +104 -0
  994. chrys/service/profiles/models/resolver.py +286 -0
  995. chrys/service/profiles/models/schema.py +91 -0
  996. chrys/service/profiles/models/serializer.py +76 -0
  997. chrys/service/session/__init__.py +3 -0
  998. chrys/service/session/checkpoint.py +156 -0
  999. chrys/service/session/history.py +1616 -0
  1000. chrys/service/session/message_metadata.py +151 -0
  1001. chrys/service/session/persistence.py +435 -0
  1002. chrys/service/session/runtime_metadata.py +210 -0
  1003. chrys/service/session/sub_agent_logs.py +1069 -0
  1004. chrys/service/session/sub_agent_transcript.py +654 -0
  1005. chrys/service/skills/__init__.py +3 -0
  1006. chrys/service/skills/adapter.py +408 -0
  1007. chrys/service/skills/constants.py +8 -0
  1008. chrys/service/skills/loader.py +575 -0
  1009. chrys/service/skills/model.py +130 -0
  1010. chrys/service/skills/provider.py +841 -0
  1011. chrys/service/skills/runner.py +390 -0
  1012. chrys/service/state/__init__.py +3 -0
  1013. chrys/service/state/_fork.py +710 -0
  1014. chrys/service/state/_session_files.py +175 -0
  1015. chrys/service/state/_session_meta.py +552 -0
  1016. chrys/service/state/locks.py +154 -0
  1017. chrys/service/state/serializers.py +156 -0
  1018. chrys/service/state/session_catalog.py +361 -0
  1019. chrys/service/state/session_listing.py +84 -0
  1020. chrys/service/state/session_migration.py +446 -0
  1021. chrys/service/state/session_mru.py +315 -0
  1022. chrys/service/state/store.py +2132 -0
  1023. chrys/service/state/workflow.py +158 -0
  1024. chrys/service/text_blocks.py +47 -0
  1025. chrys/service/todos/__init__.py +3 -0
  1026. chrys/service/todos/reminder.py +31 -0
  1027. chrys/service/todos/tracker.py +58 -0
  1028. chrys/service/tools/__init__.py +3 -0
  1029. chrys/service/tools/approval_targets.py +92 -0
  1030. chrys/service/tools/builtins/__init__.py +3 -0
  1031. chrys/service/tools/builtins/ask_user.py +326 -0
  1032. chrys/service/tools/builtins/doc_converter/__init__.py +494 -0
  1033. chrys/service/tools/builtins/doc_converter/artifacts.py +392 -0
  1034. chrys/service/tools/builtins/doc_converter/parsers/__init__.py +3 -0
  1035. chrys/service/tools/builtins/doc_converter/parsers/_table.py +34 -0
  1036. chrys/service/tools/builtins/doc_converter/parsers/base.py +101 -0
  1037. chrys/service/tools/builtins/doc_converter/parsers/docx.py +88 -0
  1038. chrys/service/tools/builtins/doc_converter/parsers/pdf.py +144 -0
  1039. chrys/service/tools/builtins/doc_converter/parsers/pptx.py +103 -0
  1040. chrys/service/tools/builtins/doc_converter/parsers/xls.py +32 -0
  1041. chrys/service/tools/builtins/doc_converter/parsers/xlsx.py +51 -0
  1042. chrys/service/tools/builtins/doc_converter/registry.py +55 -0
  1043. chrys/service/tools/builtins/filesystem.py +1071 -0
  1044. chrys/service/tools/builtins/search.py +1265 -0
  1045. chrys/service/tools/builtins/shell.py +967 -0
  1046. chrys/service/tools/builtins/shell_command_filtering/__init__.py +5 -0
  1047. chrys/service/tools/builtins/shell_command_filtering/policy.py +60 -0
  1048. chrys/service/tools/builtins/shell_command_filtering/presets.py +152 -0
  1049. chrys/service/tools/builtins/shell_command_filtering/readonly/__init__.py +358 -0
  1050. chrys/service/tools/builtins/shell_command_filtering/readonly/commands.py +416 -0
  1051. chrys/service/tools/builtins/shell_command_filtering/readonly/git.py +408 -0
  1052. chrys/service/tools/builtins/shell_command_filtering/readonly/syntax.py +87 -0
  1053. chrys/service/tools/builtins/shell_command_filtering/tokenizer.py +214 -0
  1054. chrys/service/tools/builtins/shell_filter.py +24 -0
  1055. chrys/service/tools/builtins/sleep.py +91 -0
  1056. chrys/service/tools/builtins/todo.py +64 -0
  1057. chrys/service/tools/builtins/web/__init__.py +6 -0
  1058. chrys/service/tools/builtins/web/build.py +268 -0
  1059. chrys/service/tools/builtins/web/config.py +338 -0
  1060. chrys/service/tools/builtins/web/credentials.py +37 -0
  1061. chrys/service/tools/builtins/web/fetch/__init__.py +3 -0
  1062. chrys/service/tools/builtins/web/fetch/cache.py +68 -0
  1063. chrys/service/tools/builtins/web/fetch/convert.py +56 -0
  1064. chrys/service/tools/builtins/web/fetch/tool.py +223 -0
  1065. chrys/service/tools/builtins/web/hosted.py +61 -0
  1066. chrys/service/tools/builtins/web/http.py +330 -0
  1067. chrys/service/tools/builtins/web/progress.py +18 -0
  1068. chrys/service/tools/builtins/web/search/__init__.py +3 -0
  1069. chrys/service/tools/builtins/web/search/exa_mcp.py +146 -0
  1070. chrys/service/tools/builtins/web/search/html.py +152 -0
  1071. chrys/service/tools/builtins/web/search/json_api.py +227 -0
  1072. chrys/service/tools/builtins/web/search/providers.py +77 -0
  1073. chrys/service/tools/builtins/web/search/tool.py +218 -0
  1074. chrys/service/tools/builtins/web/search/types.py +28 -0
  1075. chrys/service/tools/builtins/web/tls_identity.py +66 -0
  1076. chrys/service/tools/kinds.py +115 -0
  1077. chrys/service/tools/names.py +47 -0
  1078. chrys/service/tools/registry.py +242 -0
  1079. chrys/service/tools/result_metadata.py +103 -0
  1080. chrys/service/tools/session_artifacts.py +196 -0
  1081. chrys/service/tools/spill.py +218 -0
  1082. chrys/service/tools/workspace_paths.py +81 -0
  1083. chrys/service/trajectory/__init__.py +11 -0
  1084. chrys/service/trajectory/approvals.py +201 -0
  1085. chrys/service/trajectory/compaction.py +420 -0
  1086. chrys/service/trajectory/fork.py +75 -0
  1087. chrys/service/trajectory/hooks.py +217 -0
  1088. chrys/service/trajectory/items.py +41 -0
  1089. chrys/service/trajectory/preparation.py +374 -0
  1090. chrys/service/trajectory/retries.py +95 -0
  1091. chrys/service/trajectory/revisions.py +129 -0
  1092. chrys/service/trajectory/segmented.py +163 -0
  1093. chrys/service/trajectory/session.py +762 -0
  1094. chrys/service/trajectory/state.py +83 -0
  1095. chrys/service/trajectory/sub_agents.py +169 -0
  1096. chrys/service/trajectory/tombstone.py +262 -0
  1097. chrys/service/trajectory/tools.py +323 -0
  1098. chrys/service/trajectory/validation.py +130 -0
  1099. chrys/service/trajectory/waits.py +161 -0
  1100. chrys/service/trajectory/workflow.py +148 -0
  1101. chrys/service/vision.py +128 -0
  1102. chrys/service/workflows/__init__.py +3 -0
  1103. chrys/service/workflows/admission.py +151 -0
  1104. chrys/service/workflows/artifacts.py +109 -0
  1105. chrys/service/workflows/asks.py +130 -0
  1106. chrys/service/workflows/builtins/__init__.py +12 -0
  1107. chrys/service/workflows/builtins/demo-workflow.manifest.json +635 -0
  1108. chrys/service/workflows/builtins/demo-workflow.py +756 -0
  1109. chrys/service/workflows/discovery.py +554 -0
  1110. chrys/service/workflows/environment.py +284 -0
  1111. chrys/service/workflows/graph.py +430 -0
  1112. chrys/service/workflows/history.py +82 -0
  1113. chrys/service/workflows/interpreter.py +107 -0
  1114. chrys/service/workflows/journal.py +334 -0
  1115. chrys/service/workflows/layout.py +56 -0
  1116. chrys/service/workflows/ledger.py +149 -0
  1117. chrys/service/workflows/model_selection.py +18 -0
  1118. chrys/service/workflows/orphans.py +209 -0
  1119. chrys/service/workflows/outcomes.py +46 -0
  1120. chrys/service/workflows/protocol.py +210 -0
  1121. chrys/service/workflows/records.py +142 -0
  1122. chrys/service/workflows/scheduler.py +770 -0
  1123. chrys/service/workflows/sdk/__init__.py +51 -0
  1124. chrys/service/workflows/sdk/_ask.py +167 -0
  1125. chrys/service/workflows/sdk/_builder.py +781 -0
  1126. chrys/service/workflows/sdk/_values.py +92 -0
  1127. chrys/service/workflows/sdk_artifact.py +74 -0
  1128. chrys/service/workflows/store.py +494 -0
  1129. chrys/service/workflows/transcript.py +102 -0
  1130. chrys/service/workflows/values.py +73 -0
  1131. chrys/service/workflows/worker_host.py +1820 -0
  1132. chrys/workflows.py +38 -0
  1133. icode_tui-0.29.1.dist-info/METADATA +104 -0
  1134. icode_tui-0.29.1.dist-info/RECORD +1138 -0
  1135. icode_tui-0.29.1.dist-info/WHEEL +4 -0
  1136. icode_tui-0.29.1.dist-info/entry_points.txt +2 -0
  1137. icode_tui-0.29.1.dist-info/licenses/LICENSE +202 -0
  1138. icode_tui-0.29.1.dist-info/licenses/NOTICE +1955 -0
@@ -0,0 +1,1866 @@
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
+ """Chrys-owned content types."""
6
+
7
+ from __future__ import annotations
8
+
9
+ import base64
10
+ import json
11
+ import logging
12
+ from collections.abc import Iterable, Mapping, MutableMapping, Sequence
13
+ from copy import deepcopy
14
+ from typing import Any, ClassVar, Final, Literal, TypeGuard, TypeVar, cast
15
+
16
+ from typing_extensions import TypedDict
17
+
18
+ from chrys.foundation.hosted_tools import PRESENTATION_TEXT_SEGMENT_ID_KEY, HostedToolFamily
19
+ from chrys.foundation.reasoning_origin import REASONING_ORIGIN_KEY
20
+ from chrys.foundation.text.model_json import model_json
21
+
22
+ from .exceptions import AdditionItemMismatch, ContentError
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+ OPENAI_OUTPUT_MESSAGE_ENVELOPE_KEY: Final[str] = "openai.responses.output_message_envelope"
27
+ # What tells the output items of a Responses stream apart on the function
28
+ # calls it sends, each whole: its position and its item id. Two items are two
29
+ # calls even under one call id, so their calls never merge.
30
+ _RESPONSES_OUTPUT_ITEM_KEYS: Final = ("output_index", "fc_id")
31
+ _ANTHROPIC_REDACTED_THINKING_KEY = "anthropic_redacted_thinking"
32
+
33
+
34
+ # region Content Parsing Utilities
35
+ def _parse_content_list(contents_data: Sequence[Any]) -> list[Content]:
36
+ """Parse a list of content data into appropriate Content objects.
37
+
38
+ Args:
39
+ contents_data: List of content data (strings, dicts, or already constructed objects)
40
+
41
+ Returns:
42
+ List of Content objects with unknown types logged and ignored
43
+ """
44
+ contents: list[Content] = []
45
+ for content_data in contents_data:
46
+ if content_data is None:
47
+ continue
48
+ if isinstance(content_data, Content):
49
+ contents.append(content_data)
50
+ continue
51
+ if isinstance(content_data, str):
52
+ contents.append(Content.from_text(text=content_data))
53
+ continue
54
+ try:
55
+ contents.append(Content.from_dict(content_data))
56
+ except ContentError as exc:
57
+ logger.warning(f"Skipping unknown content type or invalid content: {exc}")
58
+
59
+ return contents
60
+
61
+
62
+ # region Internal Helper functions for unified Content
63
+
64
+
65
+ def detect_media_type_from_base64(
66
+ *,
67
+ data_bytes: bytes | None = None,
68
+ data_str: str | None = None,
69
+ data_uri: str | None = None,
70
+ ) -> str | None:
71
+ """Detect media type from base64-encoded data by examining magic bytes.
72
+
73
+ This function examines the binary signature (magic bytes) at the start of the data
74
+ to identify common media types. It's reliable for binary formats like images, audio,
75
+ video, and documents, but cannot detect text-based formats like JSON or plain text.
76
+
77
+ Args:
78
+ data_bytes: Raw binary data.
79
+ data_str: Base64-encoded data (without data URI prefix).
80
+ data_uri: Full data URI string (e.g., "data:image/png;base64,iVBORw0KGgo...").
81
+ This will look at the actual data to determine the media_type and not at the URI prefix.
82
+ Will also not compare those two values.
83
+
84
+ Returns:
85
+ The detected media type (e.g., 'image/png', 'audio/wav', 'application/pdf')
86
+ or None if the format is not recognized.
87
+
88
+ Raises:
89
+ ValueError: If not exactly 1 of data_bytes, data_str, or data_uri is provided, or if base64 decoding fails.
90
+
91
+ Examples:
92
+ .. code-block:: python
93
+
94
+ from chrys.kernel._content import detect_media_type_from_base64
95
+
96
+ # Detect from base64 string
97
+ base64_data = "iVBORw0KGgo..."
98
+ media_type = detect_media_type_from_base64(base64_data)
99
+ # Returns: "image/png"
100
+
101
+ # Works with data URIs too
102
+ data_uri = "data:image/png;base64,iVBORw0KGgo..."
103
+ media_type = detect_media_type_from_base64(data_uri)
104
+ # Returns: "image/png"
105
+ """
106
+ data: bytes | None = None
107
+ if data_bytes is not None:
108
+ data = data_bytes
109
+ if data_uri is not None:
110
+ if data is not None:
111
+ raise ValueError("Provide exactly one of data_bytes, data_str, or data_uri.")
112
+ # Remove data URI prefix if present
113
+ if not data_uri.startswith("data:") or "," not in data_uri:
114
+ raise ValueError("Invalid data URI format.")
115
+ prefix, data_str = data_uri.split(",", 1)
116
+ if not prefix.endswith(";base64"):
117
+ raise ValueError("Data URI must use base64 encoding.")
118
+ if data_str is not None:
119
+ if data is not None:
120
+ raise ValueError("Provide exactly one of data_bytes, data_str, or data_uri.")
121
+ try:
122
+ data = base64.b64decode(data_str)
123
+ except Exception as exc:
124
+ raise ValueError("Invalid base64 data provided.") from exc
125
+ if data is None:
126
+ raise ValueError("Provide exactly one of data_bytes, data_str, or data_uri.")
127
+
128
+ # Check magic bytes for common formats
129
+ # Images
130
+ if data.startswith(b"\x89PNG\r\n\x1a\n"):
131
+ return "image/png"
132
+ if data.startswith(b"\xff\xd8\xff"):
133
+ return "image/jpeg"
134
+ if data.startswith((b"GIF87a", b"GIF89a")):
135
+ return "image/gif"
136
+ if data.startswith(b"RIFF") and len(data) > 11 and data[8:12] == b"WEBP":
137
+ return "image/webp"
138
+ if data.startswith(b"BM"):
139
+ return "image/bmp"
140
+ if data.startswith((b"<svg", b"<?xml")):
141
+ return "image/svg+xml"
142
+
143
+ # Documents
144
+ if data.startswith(b"%PDF-"):
145
+ return "application/pdf"
146
+
147
+ # Audio
148
+ if data.startswith(b"RIFF") and len(data) > 11 and data[8:12] == b"WAVE":
149
+ return "audio/wav"
150
+ if data.startswith((b"ID3", b"\xff\xfb", b"\xff\xf3")):
151
+ return "audio/mpeg"
152
+ if data.startswith(b"OggS"):
153
+ return "audio/ogg"
154
+ if data.startswith(b"fLaC"):
155
+ return "audio/flac"
156
+
157
+ return None
158
+
159
+
160
+ def _get_data_bytes_as_str(content: Content) -> str | None:
161
+ """Extract base64 data string from data URI.
162
+
163
+ Args:
164
+ content: The Content instance to extract data from.
165
+
166
+ Returns:
167
+ The base64-encoded data as a string, or None if not a data content type.
168
+
169
+ Raises:
170
+ ContentError: If the URI is not a valid data URI.
171
+ """
172
+ if content.type not in ("data", "uri"):
173
+ return None
174
+
175
+ uri = content.uri
176
+ if not uri:
177
+ return None
178
+
179
+ if not uri.startswith("data:"):
180
+ return None
181
+
182
+ if ";base64," not in uri:
183
+ raise ContentError("Data URI must use base64 encoding")
184
+
185
+ _, data = uri.split(";base64,", 1)
186
+ return data # type: ignore[return-value, no-any-return]
187
+
188
+
189
+ def _get_data_bytes(content: Content) -> bytes | None: # pyright: ignore[reportUnusedFunction]
190
+ """Extract and decode binary data from data URI.
191
+
192
+ Args:
193
+ content: The Content instance to extract data from.
194
+
195
+ Returns:
196
+ The decoded binary data, or None if not a data content type.
197
+
198
+ Raises:
199
+ ContentError: If the URI is not a valid data URI or decoding fails.
200
+ """
201
+ data_str = _get_data_bytes_as_str(content)
202
+ if data_str is None:
203
+ return None
204
+
205
+ try:
206
+ return base64.b64decode(data_str)
207
+ except Exception as e:
208
+ raise ContentError(f"Failed to decode base64 data: {e}") from e
209
+
210
+
211
+ KNOWN_URI_SCHEMAS: Final[set[str]] = {"http", "https", "ftp", "ftps", "file", "s3", "gs", "azure", "blob"}
212
+
213
+
214
+ def _validate_uri(uri: str, media_type: str | None) -> dict[str, Any]:
215
+ """Validate URI format and return validation result.
216
+
217
+ Args:
218
+ uri: The URI to validate.
219
+ media_type: Optional media type associated with the URI.
220
+
221
+ Returns:
222
+ If valid, returns a dict, with "type" key indicating "data" or "uri", along with the uri and media_type.
223
+ """
224
+ if not uri:
225
+ raise ContentError("URI cannot be empty")
226
+
227
+ # Check for data URI
228
+ if uri.startswith("data:"):
229
+ if "," not in uri:
230
+ raise ContentError("Data URI must contain a comma separating metadata and data")
231
+ prefix, _ = uri.split(",", 1)
232
+ if ";" in prefix:
233
+ parts = prefix.split(";")
234
+ if len(parts) < 2:
235
+ raise ContentError("Invalid data URI format")
236
+ # Check encoding
237
+ encoding = parts[-1]
238
+ if encoding not in ("base64", ""):
239
+ raise ContentError(f"Unsupported data URI encoding: {encoding}")
240
+ if media_type is None:
241
+ # attempt to extract:
242
+ media_type = parts[0][5:] # Remove 'data:'
243
+ return {"type": "data", "uri": uri, "media_type": media_type}
244
+
245
+ # Check for common URI schemes
246
+ if ":" in uri:
247
+ scheme = uri.split(":", 1)[0].lower()
248
+ if not media_type:
249
+ logger.warning("Using URI without media type is not recommended.")
250
+ if scheme not in KNOWN_URI_SCHEMAS:
251
+ logger.info(f"Unknown URI scheme: {scheme}, allowed schemes are {KNOWN_URI_SCHEMAS}.")
252
+ return {"type": "uri", "uri": uri, "media_type": media_type}
253
+
254
+ # No scheme found
255
+ raise ContentError("URI must contain a scheme (e.g., http://, data:, file://)")
256
+
257
+
258
+ def _serialize_value(value: Any, exclude_none: bool) -> Any:
259
+ """Recursively serialize a value for to_dict."""
260
+ if value is None:
261
+ return None
262
+ if isinstance(value, Content):
263
+ return value.to_dict(exclude_none=exclude_none)
264
+ if isinstance(value, Sequence) and not isinstance(value, (str, bytes, bytearray)):
265
+ return [_serialize_value(item, exclude_none) for item in cast(Iterable[Any], value)]
266
+ if isinstance(value, Mapping):
267
+ return {k: _serialize_value(v, exclude_none) for k, v in value.items()} # type: ignore[reportUnknownVariableType]
268
+ if hasattr(value, "to_dict"):
269
+ return value.to_dict() # type: ignore[call-arg]
270
+ return value
271
+
272
+
273
+ def _restore_compaction_annotation_in_additional_properties(
274
+ additional_properties: MutableMapping[str, Any] | None,
275
+ *,
276
+ allow_none: bool = False,
277
+ ) -> dict[str, Any] | None:
278
+ if additional_properties is None:
279
+ return None if allow_none else {}
280
+
281
+ return dict(additional_properties)
282
+
283
+
284
+ # region Constants and types
285
+ KNOWN_MEDIA_TYPES = [
286
+ "application/json",
287
+ "application/octet-stream",
288
+ "application/pdf",
289
+ "application/xml",
290
+ "audio/mpeg",
291
+ "audio/mp3",
292
+ "audio/ogg",
293
+ "audio/wav",
294
+ "image/apng",
295
+ "image/avif",
296
+ "image/bmp",
297
+ "image/gif",
298
+ "image/jpeg",
299
+ "image/png",
300
+ "image/svg+xml",
301
+ "image/tiff",
302
+ "image/webp",
303
+ "text/css",
304
+ "text/csv",
305
+ "text/html",
306
+ "text/javascript",
307
+ "text/plain",
308
+ "text/plain;charset=UTF-8",
309
+ "text/xml",
310
+ ]
311
+
312
+ # region Unified Content Types
313
+
314
+ ContentType = Literal[
315
+ "text",
316
+ "text_reasoning",
317
+ "data",
318
+ "uri",
319
+ "error",
320
+ "function_call",
321
+ "function_result",
322
+ "usage",
323
+ "hosted_file",
324
+ "hosted_vector_store",
325
+ "code_interpreter_tool_call",
326
+ "code_interpreter_tool_result",
327
+ "image_generation_tool_call",
328
+ "image_generation_tool_result",
329
+ "mcp_server_tool_call",
330
+ "mcp_server_tool_result",
331
+ "search_tool_call",
332
+ "search_tool_result",
333
+ "shell_tool_call",
334
+ "shell_tool_result",
335
+ "shell_command_output",
336
+ "hosted_tool_call",
337
+ "hosted_tool_result",
338
+ ]
339
+
340
+
341
+ class TextSpanRegion(TypedDict, total=False):
342
+ """TypedDict representation of a text span region annotation."""
343
+
344
+ type: Literal["text_span"]
345
+ start_index: int
346
+ end_index: int
347
+
348
+
349
+ class Annotation(TypedDict, total=False):
350
+ """TypedDict representation of an annotation."""
351
+
352
+ type: Literal["citation"]
353
+ title: str
354
+ url: str
355
+ file_id: str
356
+ tool_name: str
357
+ snippet: str | None
358
+ annotated_regions: Sequence[TextSpanRegion]
359
+ additional_properties: dict[str, Any]
360
+ raw_representation: Any
361
+
362
+
363
+ ContentT = TypeVar("ContentT", bound="Content")
364
+
365
+ # endregion
366
+
367
+
368
+ class UsageDetails(TypedDict, total=False, extra_items=int): # type: ignore[call-arg]
369
+ """A dictionary representing usage details.
370
+
371
+ This is a non-closed dictionary, so any specific provider fields can be added as needed.
372
+ Whenever they can be mapped to standard fields, they will be.
373
+
374
+ Keys:
375
+ input_token_count: The number of input tokens used.
376
+ output_token_count: The number of output tokens generated.
377
+ total_token_count: The total number of tokens (input + output).
378
+ context_input_token_count: The final request's context-window input occupancy.
379
+ context_input_token_estimate: A provider-derived estimate of final input occupancy.
380
+ context_input_token_floor: A provider-derived lower bound for final input occupancy.
381
+ cache_creation_input_token_count: The number of input tokens written to a provider-managed cache.
382
+ cache_read_input_token_count: The number of input tokens served from a provider-managed cache.
383
+ reasoning_output_token_count: The number of output tokens used for reasoning.
384
+
385
+ """
386
+
387
+ input_token_count: int | None
388
+ output_token_count: int | None
389
+ total_token_count: int | None
390
+ context_input_token_count: int | None
391
+ context_input_token_estimate: int | None
392
+ context_input_token_floor: int | None
393
+ cache_creation_input_token_count: int | None
394
+ cache_read_input_token_count: int | None
395
+ reasoning_output_token_count: int | None
396
+
397
+
398
+ def _is_content_list(value: list[Any]) -> TypeGuard[list[Content]]:
399
+ """Return whether every item is a Chrys ``Content`` instance."""
400
+ return all(isinstance(item, Content) for item in value)
401
+
402
+
403
+ def add_usage_details(usage1: UsageDetails | None, usage2: UsageDetails | None) -> UsageDetails:
404
+ """Add two UsageDetails dictionaries by summing all numeric values.
405
+
406
+ If any of the two usage details contains a key with a non-int value, it will be skipped,
407
+ even if the other contains a int-value on that key.
408
+
409
+ Args:
410
+ usage1: First usage details dictionary.
411
+ usage2: Second usage details dictionary.
412
+
413
+ Returns:
414
+ A new UsageDetails dictionary with summed values.
415
+
416
+ Examples:
417
+ .. code-block:: python
418
+
419
+ from chrys.kernel._content import UsageDetails, add_usage_details
420
+
421
+ usage1 = UsageDetails(input_token_count=5, output_token_count=10)
422
+ usage2 = UsageDetails(input_token_count=3, output_token_count=6)
423
+ combined = add_usage_details(usage1, usage2)
424
+ # Result: {'input_token_count': 8, 'output_token_count': 16}
425
+ """
426
+ if usage1 is None:
427
+ return usage2 or UsageDetails()
428
+ if usage2 is None:
429
+ return usage1
430
+
431
+ result = UsageDetails()
432
+ # Combine all keys from both dictionaries
433
+ all_keys = set(usage1.keys()) | set(usage2.keys())
434
+ for key in all_keys:
435
+ if not isinstance((val1 := usage1.get(key, 0)), (int | None)) or not isinstance(
436
+ (val2 := usage2.get(key, 0)), (int | None)
437
+ ):
438
+ logger.warning("Non `int` value found in usage details, skipping.")
439
+ continue
440
+ result[key] = (val1 or 0) + (val2 or 0) # type: ignore[literal-required]
441
+ return result
442
+
443
+
444
+ def normalize_stream_usage(usages: Sequence[Mapping[str, Any]]) -> UsageDetails | None:
445
+ """Collapse cumulative usage snapshots from one streamed model call.
446
+
447
+ Streaming providers may emit more than one cumulative usage payload for a
448
+ single request. Summing those snapshots over-counts the request; the
449
+ latest non-null value for each key is the authoritative per-call value.
450
+
451
+ Aggregation *across distinct model calls* remains the responsibility of
452
+ the caller via :func:`add_usage_details`.
453
+ """
454
+ if not usages:
455
+ return None
456
+
457
+ merged = UsageDetails()
458
+ for usage in usages:
459
+ for key, value in usage.items():
460
+ if value is not None:
461
+ merged[key] = value # type: ignore[literal-required,typeddict-item]
462
+ return merged
463
+
464
+
465
+ # region Content Class
466
+ class Content:
467
+ """Unified content container covering all content variants.
468
+
469
+ This class provides a single unified type that handles all content variants.
470
+ Use the class methods like `Content.from_text()`, `Content.from_data()`,
471
+ `Content.from_uri()`, etc. to create instances.
472
+ """
473
+
474
+ _SHALLOW_COPY_FIELDS: ClassVar[set[str]] = {"raw_representation"}
475
+ __hash__ = None
476
+
477
+ def __init__(
478
+ self,
479
+ type: ContentType,
480
+ *,
481
+ # Text content fields
482
+ text: str | None = None,
483
+ protected_data: str | None = None,
484
+ # Data/URI content fields
485
+ uri: str | None = None,
486
+ media_type: str | None = None,
487
+ # Error content fields
488
+ message: str | None = None,
489
+ error_code: str | None = None,
490
+ error_details: str | None = None,
491
+ # Usage content fields
492
+ usage_details: UsageDetails | None = None,
493
+ # Function call/result fields
494
+ call_id: str | None = None,
495
+ name: str | None = None,
496
+ arguments: str | Mapping[str, Any] | None = None,
497
+ informational_only: bool = False,
498
+ exception: str | None = None,
499
+ result: Any = None,
500
+ items: Sequence[Content] | None = None,
501
+ # Hosted file/vector store fields
502
+ file_id: str | None = None,
503
+ vector_store_id: str | None = None,
504
+ # Code interpreter tool fields
505
+ inputs: list[Content] | None = None,
506
+ outputs: list[Content] | Any | None = None,
507
+ # Image generation tool fields
508
+ image_id: str | None = None,
509
+ # Shell tool fields
510
+ commands: list[str] | None = None,
511
+ timeout_ms: int | None = None,
512
+ max_output_length: int | None = None,
513
+ status: str | None = None,
514
+ # Shell command output fields
515
+ stdout: str | None = None,
516
+ stderr: str | None = None,
517
+ exit_code: int | None = None,
518
+ timed_out: bool | None = None,
519
+ # MCP server tool fields
520
+ tool_name: str | None = None,
521
+ server_name: str | None = None,
522
+ output: Any = None,
523
+ # Server-issued item identity (e.g. Responses reasoning ``rs_*`` ids)
524
+ id: str | None = None,
525
+ # Provider-hosted tool fields
526
+ provider_hosted: bool = False,
527
+ hosted_family: str | None = None,
528
+ hosted_provider: str | None = None,
529
+ provider_item_type: str | None = None,
530
+ provider_item_id: str | None = None,
531
+ provider_phase: str | None = None,
532
+ provider_status: str | None = None,
533
+ retry_safety: str | None = None,
534
+ # Common fields
535
+ annotations: Sequence[Annotation] | None = None,
536
+ additional_properties: MutableMapping[str, Any] | None = None,
537
+ raw_representation: Any | None = None,
538
+ ) -> None:
539
+ """Create a content instance.
540
+
541
+ Prefer using the classmethod constructors like `Content.from_text()` instead of calling __init__ directly.
542
+ """
543
+ self.type = type
544
+ self.annotations = annotations
545
+ self.additional_properties: dict[str, Any] = (
546
+ _restore_compaction_annotation_in_additional_properties(additional_properties) or {}
547
+ )
548
+ self.raw_representation = raw_representation
549
+
550
+ # Set all content-specific attributes
551
+ self.text = text
552
+ self.protected_data = protected_data
553
+ self.uri = uri
554
+ self.media_type = media_type
555
+ self.message = message
556
+ self.error_code = error_code
557
+ self.error_details = error_details
558
+ self.usage_details = usage_details
559
+ self.call_id = call_id
560
+ self.name = name
561
+ self.arguments = arguments
562
+ self.informational_only = informational_only or type == "mcp_server_tool_call"
563
+ self.exception = exception
564
+ self.result = result
565
+ self.items = items
566
+ self.file_id = file_id
567
+ self.vector_store_id = vector_store_id
568
+ self.inputs = inputs
569
+ self.outputs = outputs
570
+ self.image_id = image_id
571
+ self.commands = commands
572
+ self.timeout_ms = timeout_ms
573
+ self.max_output_length = max_output_length
574
+ self.status = status
575
+ self.stdout = stdout
576
+ self.stderr = stderr
577
+ self.exit_code = exit_code
578
+ self.timed_out = timed_out
579
+ self.tool_name = tool_name
580
+ self.server_name = server_name
581
+ self.output = output
582
+ self.id = id
583
+ self.provider_hosted = provider_hosted
584
+ self.hosted_family = hosted_family
585
+ self.hosted_provider = hosted_provider
586
+ self.provider_item_type = provider_item_type
587
+ self.provider_item_id = provider_item_id
588
+ self.provider_phase = provider_phase
589
+ self.provider_status = provider_status
590
+ self.retry_safety = retry_safety
591
+
592
+ def __deepcopy__(self, memo: dict[int, Any]) -> Content:
593
+ """Create a deep copy, preserving ``_SHALLOW_COPY_FIELDS`` by reference.
594
+
595
+ Fields listed in ``_SHALLOW_COPY_FIELDS`` may contain LLM SDK objects
596
+ (e.g., proto/gRPC responses) that are not safe to deep-copy.
597
+ """
598
+ cls = type(self)
599
+ result = cls.__new__(cls)
600
+ memo[id(self)] = result
601
+ shallow = cls._SHALLOW_COPY_FIELDS
602
+ for k, v in self.__dict__.items():
603
+ if k in shallow:
604
+ object.__setattr__(result, k, v)
605
+ else:
606
+ object.__setattr__(result, k, deepcopy(v, memo))
607
+ return result
608
+
609
+ @classmethod
610
+ def from_text(
611
+ cls: type[ContentT],
612
+ text: str,
613
+ *,
614
+ annotations: Sequence[Annotation] | None = None,
615
+ additional_properties: MutableMapping[str, Any] | None = None,
616
+ raw_representation: Any = None,
617
+ ) -> ContentT:
618
+ """Create text content."""
619
+ return cls(
620
+ "text",
621
+ text=text,
622
+ annotations=annotations,
623
+ additional_properties=additional_properties,
624
+ raw_representation=raw_representation,
625
+ )
626
+
627
+ @classmethod
628
+ def from_text_reasoning(
629
+ cls: type[ContentT],
630
+ *,
631
+ id: str | None = None,
632
+ text: str | None = None,
633
+ protected_data: str | None = None,
634
+ annotations: Sequence[Annotation] | None = None,
635
+ additional_properties: MutableMapping[str, Any] | None = None,
636
+ raw_representation: Any = None,
637
+ ) -> ContentT:
638
+ """Create text reasoning content."""
639
+ return cls(
640
+ "text_reasoning",
641
+ id=id,
642
+ text=text,
643
+ protected_data=protected_data,
644
+ annotations=annotations,
645
+ additional_properties=additional_properties,
646
+ raw_representation=raw_representation,
647
+ )
648
+
649
+ @classmethod
650
+ def from_data(
651
+ cls: type[ContentT],
652
+ data: bytes,
653
+ media_type: str,
654
+ *,
655
+ annotations: Sequence[Annotation] | None = None,
656
+ additional_properties: MutableMapping[str, Any] | None = None,
657
+ raw_representation: Any = None,
658
+ ) -> ContentT:
659
+ r"""Create data content from raw binary data.
660
+
661
+ Use this to create content from binary data (images, audio, documents, etc.).
662
+ The data will be automatically base64-encoded into a data URI.
663
+
664
+ Args:
665
+ data: Raw binary data as bytes. This should be the actual binary data,
666
+ not a base64-encoded string. If you have a base64 string,
667
+ decode it first: base64.b64decode(base64_string)
668
+ media_type: The MIME type of the data (e.g., "image/png", "application/pdf").
669
+ If you don't know the media type and have base64 data, you can detect it in some cases:
670
+
671
+ .. code-block:: python
672
+
673
+ from chrys.kernel._content import detect_media_type_from_base64, Content
674
+
675
+ media_type = detect_media_type_from_base64(base64_string)
676
+ if media_type is None:
677
+ raise ValueError("Could not detect media type")
678
+ data_bytes = base64.b64decode(base64_string)
679
+ content = Content.from_data(data=data_bytes, media_type=media_type)
680
+
681
+ Keyword Args:
682
+ annotations: Optional annotations associated with the content.
683
+ additional_properties: Optional additional properties.
684
+ raw_representation: Optional raw representation from an underlying implementation.
685
+
686
+ Returns:
687
+ A Content instance with type="data".
688
+
689
+ Raises:
690
+ TypeError: If data is not bytes.
691
+
692
+ Examples:
693
+ .. code-block:: python
694
+
695
+ from chrys.kernel._content import Content, detect_media_type_from_base64
696
+ import base64
697
+
698
+ # Create from raw binary data with known media type
699
+ image_bytes = b"\x89PNG\r\n\x1a\n..."
700
+ content = Content.from_data(data=image_bytes, media_type="image/png")
701
+
702
+ # If you have a base64 string and need to detect media type
703
+ base64_string = "iVBORw0KGgo..."
704
+ media_type = detect_media_type_from_base64(base64_string)
705
+ if media_type is None:
706
+ raise ValueError("Unknown media type")
707
+ image_bytes = base64.b64decode(base64_string)
708
+ content = Content.from_data(data=image_bytes, media_type=media_type)
709
+ """
710
+ try:
711
+ encoded_data = base64.b64encode(data).decode("utf-8")
712
+ except TypeError as e:
713
+ raise TypeError(
714
+ "Could not encode data to base64. Ensure 'data' is of type bytes.Or another b64encode compatible type."
715
+ ) from e
716
+ return cls(
717
+ "data",
718
+ uri=f"data:{media_type};base64,{encoded_data}",
719
+ media_type=media_type,
720
+ annotations=annotations,
721
+ additional_properties=additional_properties,
722
+ raw_representation=raw_representation,
723
+ )
724
+
725
+ @classmethod
726
+ def from_uri(
727
+ cls: type[ContentT],
728
+ uri: str,
729
+ *,
730
+ media_type: str | None = None,
731
+ annotations: Sequence[Annotation] | None = None,
732
+ additional_properties: MutableMapping[str, Any] | None = None,
733
+ raw_representation: Any = None,
734
+ ) -> ContentT:
735
+ """Create content from a URI, can be both data URI or external URI.
736
+
737
+ Use this when you already have a properly formed data URI
738
+ (e.g., "data:image/png;base64,iVBORw0KGgo...").
739
+ Or when you receive a link to a online resource (e.g., "https://example.com/image.png").
740
+
741
+ Args:
742
+ uri: A URI string,
743
+ that either includes the media type and base64-encoded data,
744
+ or a valid URL to an external resource.
745
+
746
+ Keyword Args:
747
+ media_type: The MIME type of the data (e.g., "image/png", "application/pdf").
748
+ This is optional but recommended for external URIs.
749
+ annotations: Optional annotations associated with the content.
750
+ additional_properties: Optional additional properties.
751
+ raw_representation: Optional raw representation from an underlying implementation.
752
+
753
+ Returns:
754
+ A Content instance with type="data" for data URIs or type="uri" for external URIs.
755
+
756
+ Raises:
757
+ ContentError: If the URI is not valid.
758
+
759
+ Examples:
760
+ .. code-block:: python
761
+
762
+ from chrys.kernel._content import Content
763
+
764
+ # Create from a data URI
765
+ content = Content.from_uri(uri="data:image/png;base64,iVBORw0KGgo...", media_type="image/png")
766
+ assert content.type == "data"
767
+
768
+ # Create from an external URI
769
+ content = Content.from_uri(uri="https://example.com/image.png", media_type="image/png")
770
+ assert content.type == "uri"
771
+
772
+ # When receiving a raw already encode data string, you can do this:
773
+ raw_base64_string = "iVBORw0KGgo..."
774
+ content = Content.from_uri(
775
+ uri=f"data:{(detect_media_type_from_base64(data_str=raw_base64_string) or 'image/png')};base64,{
776
+ raw_base64_string
777
+ }"
778
+ )
779
+ """
780
+ return cls(
781
+ **_validate_uri(uri, media_type),
782
+ annotations=annotations,
783
+ additional_properties=additional_properties,
784
+ raw_representation=raw_representation,
785
+ )
786
+
787
+ @classmethod
788
+ def from_error(
789
+ cls: type[ContentT],
790
+ *,
791
+ message: str | None = None,
792
+ error_code: str | None = None,
793
+ error_details: str | None = None,
794
+ annotations: Sequence[Annotation] | None = None,
795
+ additional_properties: MutableMapping[str, Any] | None = None,
796
+ raw_representation: Any = None,
797
+ ) -> ContentT:
798
+ """Create error content."""
799
+ return cls(
800
+ "error",
801
+ message=message,
802
+ error_code=error_code,
803
+ error_details=error_details,
804
+ annotations=annotations,
805
+ additional_properties=additional_properties,
806
+ raw_representation=raw_representation,
807
+ )
808
+
809
+ @classmethod
810
+ def from_function_call(
811
+ cls: type[ContentT],
812
+ call_id: str,
813
+ name: str,
814
+ *,
815
+ arguments: str | Mapping[str, Any] | None = None,
816
+ informational_only: bool = False,
817
+ exception: str | None = None,
818
+ annotations: Sequence[Annotation] | None = None,
819
+ additional_properties: MutableMapping[str, Any] | None = None,
820
+ raw_representation: Any = None,
821
+ ) -> ContentT:
822
+ """Create function call content.
823
+
824
+ ``informational_only=True`` preserves a provider-hosted call in the
825
+ transcript without authorizing the local tool loop to execute it. The
826
+ flag is serialized only when true so older sessions retain their wire
827
+ shape; sessions containing a true flag require a build that supports
828
+ this field and are not downgrade-compatible with older Chrys builds.
829
+ """
830
+ return cls(
831
+ "function_call",
832
+ call_id=call_id,
833
+ name=name,
834
+ arguments=arguments,
835
+ informational_only=informational_only,
836
+ exception=exception,
837
+ annotations=annotations,
838
+ additional_properties=additional_properties,
839
+ raw_representation=raw_representation,
840
+ )
841
+
842
+ @classmethod
843
+ def from_function_result(
844
+ cls: type[ContentT],
845
+ call_id: str | None,
846
+ *,
847
+ result: Any = None,
848
+ exception: str | None = None,
849
+ annotations: Sequence[Annotation] | None = None,
850
+ additional_properties: MutableMapping[str, Any] | None = None,
851
+ raw_representation: Any = None,
852
+ ) -> ContentT:
853
+ """Create function result content.
854
+
855
+ All tool output is represented uniformly as Content items in the
856
+ ``items`` field. The ``result`` field is populated with the concatenated
857
+ text from text items for backwards compatibility.
858
+
859
+ Args:
860
+ call_id: The ID of the function call this result corresponds to.
861
+
862
+ Keyword Args:
863
+ result: The tool output. Accepts a ``list[Content]`` (the canonical
864
+ form produced by :meth:`~FunctionTool.parse_result`), a plain
865
+ ``str``, or any other value (which is stringified).
866
+ exception: The exception message if the function call failed.
867
+ annotations: Optional annotations for the content.
868
+ additional_properties: Optional additional properties.
869
+ raw_representation: Optional raw representation from the provider.
870
+ """
871
+ if isinstance(result, list):
872
+ if _is_content_list(result):
873
+ items_list: list[Content] = list(result)
874
+ else:
875
+ items_list = [Content.from_text(str(result))] # type: ignore[reportUnknownArgumentType]
876
+ elif isinstance(result, str):
877
+ items_list = [Content.from_text(result)]
878
+ elif result is not None:
879
+ try:
880
+ text = model_json(result, default=str)
881
+ except TypeError, ValueError:
882
+ text = str(result)
883
+ items_list = [Content.from_text(text)]
884
+ else:
885
+ items_list = [Content.from_text("")]
886
+
887
+ text_parts = [c.text for c in items_list if c.type == "text" and c.text]
888
+ text_result = "\n".join(text_parts) if text_parts else ""
889
+
890
+ return cls(
891
+ "function_result",
892
+ call_id=call_id,
893
+ result=text_result,
894
+ items=items_list,
895
+ exception=exception,
896
+ annotations=annotations,
897
+ additional_properties=additional_properties,
898
+ raw_representation=raw_representation,
899
+ )
900
+
901
+ @classmethod
902
+ def from_search_tool_call(
903
+ cls: type[ContentT],
904
+ call_id: str,
905
+ *,
906
+ tool_name: str,
907
+ arguments: str | Mapping[str, Any] | None = None,
908
+ status: str | None = None,
909
+ hosted_family: str = HostedToolFamily.SEARCH,
910
+ hosted_provider: str | None = None,
911
+ provider_item_type: str | None = None,
912
+ provider_item_id: str | None = None,
913
+ provider_phase: str | None = None,
914
+ provider_status: str | None = None,
915
+ retry_safety: str | None = None,
916
+ annotations: Sequence[Annotation] | None = None,
917
+ additional_properties: MutableMapping[str, Any] | None = None,
918
+ raw_representation: Any = None,
919
+ ) -> ContentT:
920
+ """Create search tool call content."""
921
+ return cls(
922
+ "search_tool_call",
923
+ call_id=call_id,
924
+ tool_name=tool_name,
925
+ arguments=arguments,
926
+ status=status,
927
+ provider_hosted=True,
928
+ hosted_family=hosted_family,
929
+ hosted_provider=hosted_provider,
930
+ provider_item_type=provider_item_type,
931
+ provider_item_id=provider_item_id,
932
+ provider_phase=provider_phase,
933
+ provider_status=provider_status if provider_status is not None else status,
934
+ retry_safety=retry_safety,
935
+ annotations=annotations,
936
+ additional_properties=additional_properties,
937
+ raw_representation=raw_representation,
938
+ )
939
+
940
+ @classmethod
941
+ def from_search_tool_result(
942
+ cls: type[ContentT],
943
+ call_id: str,
944
+ *,
945
+ tool_name: str,
946
+ result: Any = None,
947
+ items: Sequence[Content] | None = None,
948
+ status: str | None = None,
949
+ hosted_family: str = HostedToolFamily.SEARCH,
950
+ hosted_provider: str | None = None,
951
+ provider_item_type: str | None = None,
952
+ provider_item_id: str | None = None,
953
+ provider_phase: str | None = None,
954
+ provider_status: str | None = None,
955
+ retry_safety: str | None = None,
956
+ annotations: Sequence[Annotation] | None = None,
957
+ additional_properties: MutableMapping[str, Any] | None = None,
958
+ raw_representation: Any = None,
959
+ ) -> ContentT:
960
+ """Create search tool result content."""
961
+ return cls(
962
+ "search_tool_result",
963
+ call_id=call_id,
964
+ tool_name=tool_name,
965
+ result=result,
966
+ items=list(items) if items is not None else None,
967
+ status=status,
968
+ provider_hosted=True,
969
+ hosted_family=hosted_family,
970
+ hosted_provider=hosted_provider,
971
+ provider_item_type=provider_item_type,
972
+ provider_item_id=provider_item_id,
973
+ provider_phase=provider_phase,
974
+ provider_status=provider_status if provider_status is not None else status,
975
+ retry_safety=retry_safety,
976
+ annotations=annotations,
977
+ additional_properties=additional_properties,
978
+ raw_representation=raw_representation,
979
+ )
980
+
981
+ @classmethod
982
+ def from_hosted_tool_call(
983
+ cls: type[ContentT],
984
+ call_id: str | None,
985
+ *,
986
+ tool_name: str,
987
+ arguments: Any = None,
988
+ status: str | None = None,
989
+ hosted_family: str = HostedToolFamily.GENERIC,
990
+ hosted_provider: str | None = None,
991
+ provider_item_type: str | None = None,
992
+ provider_item_id: str | None = None,
993
+ provider_phase: str | None = None,
994
+ provider_status: str | None = None,
995
+ retry_safety: str | None = None,
996
+ annotations: Sequence[Annotation] | None = None,
997
+ additional_properties: MutableMapping[str, Any] | None = None,
998
+ raw_representation: Any = None,
999
+ ) -> ContentT:
1000
+ """Create generic provider-hosted tool call content."""
1001
+ return cls(
1002
+ "hosted_tool_call",
1003
+ call_id=call_id,
1004
+ tool_name=tool_name,
1005
+ arguments=arguments,
1006
+ status=status,
1007
+ provider_hosted=True,
1008
+ hosted_family=hosted_family,
1009
+ hosted_provider=hosted_provider,
1010
+ provider_item_type=provider_item_type,
1011
+ provider_item_id=provider_item_id,
1012
+ provider_phase=provider_phase,
1013
+ provider_status=provider_status if provider_status is not None else status,
1014
+ retry_safety=retry_safety,
1015
+ annotations=annotations,
1016
+ additional_properties=additional_properties,
1017
+ raw_representation=raw_representation,
1018
+ )
1019
+
1020
+ @classmethod
1021
+ def from_hosted_tool_result(
1022
+ cls: type[ContentT],
1023
+ call_id: str | None,
1024
+ *,
1025
+ tool_name: str | None = None,
1026
+ result: Any = None,
1027
+ items: Sequence[Content] | None = None,
1028
+ status: str | None = None,
1029
+ hosted_family: str = HostedToolFamily.GENERIC,
1030
+ hosted_provider: str | None = None,
1031
+ provider_item_type: str | None = None,
1032
+ provider_item_id: str | None = None,
1033
+ provider_phase: str | None = None,
1034
+ provider_status: str | None = None,
1035
+ retry_safety: str | None = None,
1036
+ annotations: Sequence[Annotation] | None = None,
1037
+ additional_properties: MutableMapping[str, Any] | None = None,
1038
+ raw_representation: Any = None,
1039
+ ) -> ContentT:
1040
+ """Create generic provider-hosted tool result content."""
1041
+ return cls(
1042
+ "hosted_tool_result",
1043
+ call_id=call_id,
1044
+ tool_name=tool_name,
1045
+ result=result,
1046
+ items=list(items) if items is not None else None,
1047
+ status=status,
1048
+ provider_hosted=True,
1049
+ hosted_family=hosted_family,
1050
+ hosted_provider=hosted_provider,
1051
+ provider_item_type=provider_item_type,
1052
+ provider_item_id=provider_item_id,
1053
+ provider_phase=provider_phase,
1054
+ provider_status=provider_status if provider_status is not None else status,
1055
+ retry_safety=retry_safety,
1056
+ annotations=annotations,
1057
+ additional_properties=additional_properties,
1058
+ raw_representation=raw_representation,
1059
+ )
1060
+
1061
+ @classmethod
1062
+ def from_usage(
1063
+ cls: type[ContentT],
1064
+ usage_details: UsageDetails,
1065
+ *,
1066
+ annotations: Sequence[Annotation] | None = None,
1067
+ additional_properties: MutableMapping[str, Any] | None = None,
1068
+ raw_representation: Any = None,
1069
+ ) -> ContentT:
1070
+ """Create usage content."""
1071
+ return cls(
1072
+ "usage",
1073
+ usage_details=usage_details,
1074
+ annotations=annotations,
1075
+ additional_properties=additional_properties,
1076
+ raw_representation=raw_representation,
1077
+ )
1078
+
1079
+ @classmethod
1080
+ def from_hosted_file(
1081
+ cls: type[ContentT],
1082
+ file_id: str,
1083
+ *,
1084
+ media_type: str | None = None,
1085
+ name: str | None = None,
1086
+ annotations: Sequence[Annotation] | None = None,
1087
+ additional_properties: MutableMapping[str, Any] | None = None,
1088
+ raw_representation: Any = None,
1089
+ ) -> ContentT:
1090
+ """Create hosted file content."""
1091
+ return cls(
1092
+ "hosted_file",
1093
+ file_id=file_id,
1094
+ media_type=media_type,
1095
+ name=name,
1096
+ annotations=annotations,
1097
+ additional_properties=additional_properties,
1098
+ raw_representation=raw_representation,
1099
+ )
1100
+
1101
+ @classmethod
1102
+ def from_hosted_vector_store(
1103
+ cls: type[ContentT],
1104
+ vector_store_id: str,
1105
+ *,
1106
+ annotations: Sequence[Annotation] | None = None,
1107
+ additional_properties: MutableMapping[str, Any] | None = None,
1108
+ raw_representation: Any = None,
1109
+ ) -> ContentT:
1110
+ """Create hosted vector store content."""
1111
+ return cls(
1112
+ "hosted_vector_store",
1113
+ vector_store_id=vector_store_id,
1114
+ annotations=annotations,
1115
+ additional_properties=additional_properties,
1116
+ raw_representation=raw_representation,
1117
+ )
1118
+
1119
+ @classmethod
1120
+ def from_code_interpreter_tool_call(
1121
+ cls: type[ContentT],
1122
+ *,
1123
+ call_id: str | None = None,
1124
+ inputs: Sequence[Content] | None = None,
1125
+ hosted_provider: str | None = None,
1126
+ provider_item_type: str | None = None,
1127
+ provider_item_id: str | None = None,
1128
+ provider_phase: str | None = None,
1129
+ provider_status: str | None = None,
1130
+ retry_safety: str | None = None,
1131
+ annotations: Sequence[Annotation] | None = None,
1132
+ additional_properties: MutableMapping[str, Any] | None = None,
1133
+ raw_representation: Any = None,
1134
+ ) -> ContentT:
1135
+ """Create code interpreter tool call content."""
1136
+ return cls(
1137
+ "code_interpreter_tool_call",
1138
+ call_id=call_id,
1139
+ inputs=list(inputs) if inputs is not None else None,
1140
+ provider_hosted=True,
1141
+ hosted_family=HostedToolFamily.CODE,
1142
+ hosted_provider=hosted_provider,
1143
+ provider_item_type=provider_item_type,
1144
+ provider_item_id=provider_item_id,
1145
+ provider_phase=provider_phase,
1146
+ provider_status=provider_status,
1147
+ retry_safety=retry_safety,
1148
+ annotations=annotations,
1149
+ additional_properties=additional_properties,
1150
+ raw_representation=raw_representation,
1151
+ )
1152
+
1153
+ @classmethod
1154
+ def from_code_interpreter_tool_result(
1155
+ cls: type[ContentT],
1156
+ *,
1157
+ call_id: str | None = None,
1158
+ outputs: Sequence[Content] | None = None,
1159
+ hosted_provider: str | None = None,
1160
+ provider_item_type: str | None = None,
1161
+ provider_item_id: str | None = None,
1162
+ provider_phase: str | None = None,
1163
+ provider_status: str | None = None,
1164
+ retry_safety: str | None = None,
1165
+ annotations: Sequence[Annotation] | None = None,
1166
+ additional_properties: MutableMapping[str, Any] | None = None,
1167
+ raw_representation: Any = None,
1168
+ ) -> ContentT:
1169
+ """Create code interpreter tool result content."""
1170
+ return cls(
1171
+ "code_interpreter_tool_result",
1172
+ call_id=call_id,
1173
+ outputs=list(outputs) if outputs is not None else None,
1174
+ provider_hosted=True,
1175
+ hosted_family=HostedToolFamily.CODE,
1176
+ hosted_provider=hosted_provider,
1177
+ provider_item_type=provider_item_type,
1178
+ provider_item_id=provider_item_id,
1179
+ provider_phase=provider_phase,
1180
+ provider_status=provider_status,
1181
+ retry_safety=retry_safety,
1182
+ annotations=annotations,
1183
+ additional_properties=additional_properties,
1184
+ raw_representation=raw_representation,
1185
+ )
1186
+
1187
+ @classmethod
1188
+ def from_image_generation_tool_call(
1189
+ cls: type[ContentT],
1190
+ *,
1191
+ image_id: str | None = None,
1192
+ hosted_provider: str | None = None,
1193
+ provider_item_type: str | None = None,
1194
+ provider_item_id: str | None = None,
1195
+ provider_phase: str | None = None,
1196
+ provider_status: str | None = None,
1197
+ retry_safety: str | None = None,
1198
+ annotations: Sequence[Annotation] | None = None,
1199
+ additional_properties: MutableMapping[str, Any] | None = None,
1200
+ raw_representation: Any = None,
1201
+ ) -> ContentT:
1202
+ """Create image generation tool call content."""
1203
+ return cls(
1204
+ "image_generation_tool_call",
1205
+ image_id=image_id,
1206
+ provider_hosted=True,
1207
+ hosted_family=HostedToolFamily.IMAGE,
1208
+ hosted_provider=hosted_provider,
1209
+ provider_item_type=provider_item_type,
1210
+ provider_item_id=provider_item_id,
1211
+ provider_phase=provider_phase,
1212
+ provider_status=provider_status,
1213
+ retry_safety=retry_safety,
1214
+ annotations=annotations,
1215
+ additional_properties=additional_properties,
1216
+ raw_representation=raw_representation,
1217
+ )
1218
+
1219
+ @classmethod
1220
+ def from_image_generation_tool_result(
1221
+ cls: type[ContentT],
1222
+ *,
1223
+ image_id: str | None = None,
1224
+ outputs: Any = None,
1225
+ hosted_provider: str | None = None,
1226
+ provider_item_type: str | None = None,
1227
+ provider_item_id: str | None = None,
1228
+ provider_phase: str | None = None,
1229
+ provider_status: str | None = None,
1230
+ retry_safety: str | None = None,
1231
+ annotations: Sequence[Annotation] | None = None,
1232
+ additional_properties: MutableMapping[str, Any] | None = None,
1233
+ raw_representation: Any = None,
1234
+ ) -> ContentT:
1235
+ """Create image generation tool result content."""
1236
+ return cls(
1237
+ "image_generation_tool_result",
1238
+ image_id=image_id,
1239
+ outputs=outputs,
1240
+ provider_hosted=True,
1241
+ hosted_family=HostedToolFamily.IMAGE,
1242
+ hosted_provider=hosted_provider,
1243
+ provider_item_type=provider_item_type,
1244
+ provider_item_id=provider_item_id,
1245
+ provider_phase=provider_phase,
1246
+ provider_status=provider_status,
1247
+ retry_safety=retry_safety,
1248
+ annotations=annotations,
1249
+ additional_properties=additional_properties,
1250
+ raw_representation=raw_representation,
1251
+ )
1252
+
1253
+ @classmethod
1254
+ def from_shell_tool_call(
1255
+ cls: type[ContentT],
1256
+ *,
1257
+ call_id: str | None = None,
1258
+ commands: list[str] | None = None,
1259
+ timeout_ms: int | None = None,
1260
+ max_output_length: int | None = None,
1261
+ status: str | None = None,
1262
+ hosted_provider: str | None = None,
1263
+ provider_item_type: str | None = None,
1264
+ provider_item_id: str | None = None,
1265
+ provider_phase: str | None = None,
1266
+ provider_status: str | None = None,
1267
+ retry_safety: str | None = None,
1268
+ annotations: Sequence[Annotation] | None = None,
1269
+ additional_properties: MutableMapping[str, Any] | None = None,
1270
+ raw_representation: Any = None,
1271
+ ) -> ContentT:
1272
+ """Create shell tool call content.
1273
+
1274
+ This content represents the model's request to run one or more shell
1275
+ commands. It is request metadata, not command output.
1276
+
1277
+ Keyword Args:
1278
+ call_id: The unique identifier for this tool call.
1279
+ commands: The list of commands to execute.
1280
+ timeout_ms: The timeout in milliseconds for the shell command execution.
1281
+ max_output_length: The maximum output length in characters.
1282
+ status: The status of the shell call (e.g., "in_progress", "completed", "incomplete").
1283
+ annotations: Optional annotations for this content.
1284
+ additional_properties: Optional additional properties.
1285
+ raw_representation: The raw provider-specific representation.
1286
+ """
1287
+ return cls(
1288
+ "shell_tool_call",
1289
+ call_id=call_id,
1290
+ commands=commands,
1291
+ timeout_ms=timeout_ms,
1292
+ max_output_length=max_output_length,
1293
+ status=status,
1294
+ provider_hosted=True,
1295
+ hosted_family=HostedToolFamily.SHELL,
1296
+ hosted_provider=hosted_provider,
1297
+ provider_item_type=provider_item_type,
1298
+ provider_item_id=provider_item_id,
1299
+ provider_phase=provider_phase,
1300
+ provider_status=provider_status if provider_status is not None else status,
1301
+ retry_safety=retry_safety,
1302
+ annotations=annotations,
1303
+ additional_properties=additional_properties,
1304
+ raw_representation=raw_representation,
1305
+ )
1306
+
1307
+ @classmethod
1308
+ def from_shell_tool_result(
1309
+ cls: type[ContentT],
1310
+ *,
1311
+ call_id: str | None = None,
1312
+ outputs: Sequence[Content] | None = None,
1313
+ max_output_length: int | None = None,
1314
+ hosted_provider: str | None = None,
1315
+ provider_item_type: str | None = None,
1316
+ provider_item_id: str | None = None,
1317
+ provider_phase: str | None = None,
1318
+ provider_status: str | None = None,
1319
+ retry_safety: str | None = None,
1320
+ annotations: Sequence[Annotation] | None = None,
1321
+ additional_properties: MutableMapping[str, Any] | None = None,
1322
+ raw_representation: Any = None,
1323
+ ) -> ContentT:
1324
+ """Create shell tool result content.
1325
+
1326
+ This content represents the aggregate result for a shell tool call.
1327
+ Use :meth:`from_shell_command_output` to build each per-command output
1328
+ item and pass those objects via ``outputs``.
1329
+
1330
+ Keyword Args:
1331
+ call_id: The function call ID for which this is the result.
1332
+ outputs: The list of shell command output Content objects.
1333
+ max_output_length: The maximum output length in characters.
1334
+ annotations: Optional annotations for this content.
1335
+ additional_properties: Optional additional properties.
1336
+ raw_representation: The raw provider-specific representation.
1337
+ """
1338
+ return cls(
1339
+ "shell_tool_result",
1340
+ call_id=call_id,
1341
+ outputs=list(outputs) if outputs is not None else None,
1342
+ max_output_length=max_output_length,
1343
+ provider_hosted=True,
1344
+ hosted_family=HostedToolFamily.SHELL,
1345
+ hosted_provider=hosted_provider,
1346
+ provider_item_type=provider_item_type,
1347
+ provider_item_id=provider_item_id,
1348
+ provider_phase=provider_phase,
1349
+ provider_status=provider_status,
1350
+ retry_safety=retry_safety,
1351
+ annotations=annotations,
1352
+ additional_properties=additional_properties,
1353
+ raw_representation=raw_representation,
1354
+ )
1355
+
1356
+ @classmethod
1357
+ def from_shell_command_output(
1358
+ cls: type[ContentT],
1359
+ *,
1360
+ stdout: str | None = None,
1361
+ stderr: str | None = None,
1362
+ exit_code: int | None = None,
1363
+ timed_out: bool | None = None,
1364
+ additional_properties: MutableMapping[str, Any] | None = None,
1365
+ raw_representation: Any = None,
1366
+ ) -> ContentT:
1367
+ """Create shell command output content for one command execution.
1368
+
1369
+ Keyword Args:
1370
+ stdout: The standard output of the command.
1371
+ stderr: The standard error output of the command.
1372
+ exit_code: The exit code of the command, or None if the command timed out.
1373
+ timed_out: Whether the command execution timed out.
1374
+ additional_properties: Optional additional properties.
1375
+ raw_representation: The raw provider-specific representation.
1376
+ """
1377
+ return cls(
1378
+ "shell_command_output",
1379
+ stdout=stdout,
1380
+ stderr=stderr,
1381
+ exit_code=exit_code,
1382
+ timed_out=timed_out,
1383
+ additional_properties=additional_properties,
1384
+ raw_representation=raw_representation,
1385
+ )
1386
+
1387
+ @classmethod
1388
+ def from_mcp_server_tool_call(
1389
+ cls: type[ContentT],
1390
+ call_id: str,
1391
+ tool_name: str,
1392
+ *,
1393
+ server_name: str | None = None,
1394
+ arguments: str | Mapping[str, Any] | None = None,
1395
+ hosted_provider: str | None = None,
1396
+ provider_item_type: str | None = None,
1397
+ provider_item_id: str | None = None,
1398
+ provider_phase: str | None = None,
1399
+ provider_status: str | None = None,
1400
+ retry_safety: str | None = None,
1401
+ annotations: Sequence[Annotation] | None = None,
1402
+ additional_properties: MutableMapping[str, Any] | None = None,
1403
+ raw_representation: Any = None,
1404
+ ) -> ContentT:
1405
+ """Create MCP server tool call content."""
1406
+ return cls(
1407
+ "mcp_server_tool_call",
1408
+ call_id=call_id,
1409
+ tool_name=tool_name,
1410
+ server_name=server_name,
1411
+ arguments=arguments,
1412
+ informational_only=True,
1413
+ provider_hosted=True,
1414
+ hosted_family=HostedToolFamily.MCP,
1415
+ hosted_provider=hosted_provider,
1416
+ provider_item_type=provider_item_type,
1417
+ provider_item_id=provider_item_id,
1418
+ provider_phase=provider_phase,
1419
+ provider_status=provider_status,
1420
+ retry_safety=retry_safety,
1421
+ annotations=annotations,
1422
+ additional_properties=additional_properties,
1423
+ raw_representation=raw_representation,
1424
+ )
1425
+
1426
+ @classmethod
1427
+ def from_mcp_server_tool_result(
1428
+ cls: type[ContentT],
1429
+ call_id: str,
1430
+ *,
1431
+ output: Any = None,
1432
+ hosted_provider: str | None = None,
1433
+ provider_item_type: str | None = None,
1434
+ provider_item_id: str | None = None,
1435
+ provider_phase: str | None = None,
1436
+ provider_status: str | None = None,
1437
+ retry_safety: str | None = None,
1438
+ annotations: Sequence[Annotation] | None = None,
1439
+ additional_properties: MutableMapping[str, Any] | None = None,
1440
+ raw_representation: Any = None,
1441
+ ) -> ContentT:
1442
+ """Create MCP server tool result content."""
1443
+ return cls(
1444
+ "mcp_server_tool_result",
1445
+ call_id=call_id,
1446
+ output=output,
1447
+ provider_hosted=True,
1448
+ hosted_family=HostedToolFamily.MCP,
1449
+ hosted_provider=hosted_provider,
1450
+ provider_item_type=provider_item_type,
1451
+ provider_item_id=provider_item_id,
1452
+ provider_phase=provider_phase,
1453
+ provider_status=provider_status,
1454
+ retry_safety=retry_safety,
1455
+ annotations=annotations,
1456
+ additional_properties=additional_properties,
1457
+ raw_representation=raw_representation,
1458
+ )
1459
+
1460
+ def to_dict(self, *, exclude_none: bool = True, exclude: set[str] | None = None) -> dict[str, Any]:
1461
+ """Serialize the content to a dictionary."""
1462
+ fields_to_capture = (
1463
+ "text",
1464
+ "protected_data",
1465
+ "uri",
1466
+ "media_type",
1467
+ "message",
1468
+ "error_code",
1469
+ "error_details",
1470
+ "usage_details",
1471
+ "call_id",
1472
+ "name",
1473
+ "arguments",
1474
+ "informational_only",
1475
+ "exception",
1476
+ "result",
1477
+ "items",
1478
+ "file_id",
1479
+ "vector_store_id",
1480
+ "inputs",
1481
+ "outputs",
1482
+ "image_id",
1483
+ "commands",
1484
+ "timeout_ms",
1485
+ "max_output_length",
1486
+ "status",
1487
+ "stdout",
1488
+ "stderr",
1489
+ "exit_code",
1490
+ "timed_out",
1491
+ "tool_name",
1492
+ "server_name",
1493
+ "output",
1494
+ "id",
1495
+ "provider_hosted",
1496
+ "hosted_family",
1497
+ "hosted_provider",
1498
+ "provider_item_type",
1499
+ "provider_item_id",
1500
+ "provider_phase",
1501
+ "provider_status",
1502
+ "retry_safety",
1503
+ "additional_properties",
1504
+ )
1505
+
1506
+ exclude = exclude or set()
1507
+ result: dict[str, Any] = {"type": self.type}
1508
+
1509
+ for field in fields_to_capture:
1510
+ value = getattr(self, field, None)
1511
+ if field in exclude:
1512
+ continue
1513
+ # Keep old/ordinary session payloads byte-shape compatible. Only
1514
+ # informational function calls need this explicit marker; hosted
1515
+ # MCP calls are inherently informational by content type.
1516
+ if field == "informational_only" and (self.type != "function_call" or not value):
1517
+ continue
1518
+ if field == "provider_hosted" and not value:
1519
+ continue
1520
+ if (
1521
+ field
1522
+ in {
1523
+ "hosted_family",
1524
+ "hosted_provider",
1525
+ "provider_item_type",
1526
+ "provider_item_id",
1527
+ "provider_phase",
1528
+ "provider_status",
1529
+ "retry_safety",
1530
+ }
1531
+ and value is None
1532
+ ):
1533
+ continue
1534
+ if exclude_none and value is None:
1535
+ continue
1536
+ result[field] = _serialize_value(value, exclude_none)
1537
+
1538
+ if "annotations" not in exclude and self.annotations is not None:
1539
+ result["annotations"] = [
1540
+ {
1541
+ key: _serialize_value(value, exclude_none)
1542
+ for key, value in annotation.items()
1543
+ if key != "raw_representation"
1544
+ }
1545
+ for annotation in self.annotations
1546
+ ]
1547
+
1548
+ return result
1549
+
1550
+ def __eq__(self, other: object) -> bool:
1551
+ """Check if two Content instances are equal by comparing their dict representations."""
1552
+ if not isinstance(other, Content):
1553
+ return False
1554
+ return self.to_dict(exclude_none=False) == other.to_dict(exclude_none=False)
1555
+
1556
+ def __str__(self) -> str:
1557
+ """Return a string representation of the Content."""
1558
+ if self.type == "error":
1559
+ if self.error_code:
1560
+ return f"Error {self.error_code}: {self.message or ''}"
1561
+ return self.message or "Unknown error"
1562
+ if self.type == "text":
1563
+ return self.text or ""
1564
+ return f"Content(type={self.type})"
1565
+
1566
+ @classmethod
1567
+ def from_dict(cls: type[ContentT], data: Mapping[str, Any]) -> ContentT:
1568
+ """Create a Content instance from a mapping."""
1569
+ if not (content_type := data.get("type")):
1570
+ raise ValueError("Content mapping requires 'type'")
1571
+ remaining = dict(data)
1572
+ remaining.pop("type", None)
1573
+ annotations = remaining.pop("annotations", None)
1574
+ additional_properties = remaining.pop("additional_properties", None)
1575
+ raw_representation = remaining.pop("raw_representation", None)
1576
+
1577
+ # Special handling for DataContent with data and media_type
1578
+ if content_type == "data" and "data" in remaining and "media_type" in remaining:
1579
+ # Use from_data() to properly create the DataContent with URI
1580
+ return cls.from_data(remaining["data"], remaining["media_type"])
1581
+
1582
+ # Handle list of Content objects (e.g., inputs in code_interpreter_tool_call)
1583
+ if (input_items := remaining.get("inputs")) and isinstance(input_items, list):
1584
+ remaining["inputs"] = [cls.from_dict(item) if isinstance(item, dict) else item for item in input_items] # type: ignore[reportUnknownVariableType]
1585
+ if (output_items := remaining.get("outputs")) and isinstance(output_items, list):
1586
+ remaining["outputs"] = [cls.from_dict(item) if isinstance(item, dict) else item for item in output_items] # type: ignore[reportUnknownVariableType]
1587
+ if (content_items := remaining.get("items")) and isinstance(content_items, list):
1588
+ remaining["items"] = [cls.from_dict(item) if isinstance(item, dict) else item for item in content_items] # type: ignore[reportUnknownVariableType]
1589
+
1590
+ return cls(
1591
+ type=content_type,
1592
+ annotations=annotations,
1593
+ additional_properties=additional_properties,
1594
+ raw_representation=raw_representation,
1595
+ **remaining,
1596
+ )
1597
+
1598
+ def __add__(self, other: Content) -> Content:
1599
+ """Concatenate or merge two Content instances."""
1600
+ if not isinstance(other, Content):
1601
+ raise TypeError(f"Incompatible type: Cannot add Content with {type(other).__name__}")
1602
+
1603
+ if self.type != other.type:
1604
+ raise TypeError(f"Cannot add Content of type '{self.type}' with type '{other.type}'")
1605
+
1606
+ if self.type == "text":
1607
+ return self._add_text_content(other)
1608
+ if self.type == "text_reasoning":
1609
+ return self._add_text_reasoning_content(other)
1610
+ if self.type == "function_call":
1611
+ return self._add_function_call_content(other)
1612
+ if self.type == "usage":
1613
+ return self._add_usage_content(other)
1614
+ raise ContentError(f"Addition not supported for content type: {self.type}")
1615
+
1616
+ def _add_text_content(self, other: Content) -> Content:
1617
+ """Add two TextContent instances."""
1618
+ if self.text is None or other.text is None:
1619
+ raise ContentError("Cannot add text content when either text value is None")
1620
+ if self.additional_properties.get(OPENAI_OUTPUT_MESSAGE_ENVELOPE_KEY) != other.additional_properties.get(
1621
+ OPENAI_OUTPUT_MESSAGE_ENVELOPE_KEY
1622
+ ):
1623
+ raise AdditionItemMismatch("Cannot merge text contents from different OpenAI Responses output messages")
1624
+ return Content(
1625
+ "text",
1626
+ text=self.text + other.text,
1627
+ annotations=_combine_annotations(self.annotations, other.annotations),
1628
+ additional_properties=_combine_additional_props(self.additional_properties, other.additional_properties),
1629
+ raw_representation=_combine_raw_representations(self.raw_representation, other.raw_representation),
1630
+ )
1631
+
1632
+ def _add_text_reasoning_content(self, other: Content) -> Content:
1633
+ """Add two TextReasoningContent instances."""
1634
+ # Ensure we do not silently merge contents with conflicting ids
1635
+ if self.id and other.id and self.id != other.id:
1636
+ raise AdditionItemMismatch(
1637
+ f"Cannot add text_reasoning content with different ids: {self.id!r} != {other.id!r}"
1638
+ )
1639
+ combined_id = self.id or other.id
1640
+
1641
+ # Reasoning captured under different wire dialects must never merge:
1642
+ # each side's replay format is provider state, not concatenable text.
1643
+ if self.additional_properties.get("openai_reasoning_format") != other.additional_properties.get(
1644
+ "openai_reasoning_format"
1645
+ ):
1646
+ raise AdditionItemMismatch("Cannot merge reasoning contents with different wire formats")
1647
+ if self.additional_properties.get(_ANTHROPIC_REDACTED_THINKING_KEY) != other.additional_properties.get(
1648
+ _ANTHROPIC_REDACTED_THINKING_KEY
1649
+ ):
1650
+ raise AdditionItemMismatch("Cannot merge redacted and ordinary Anthropic reasoning contents")
1651
+ # Each side replays only to the endpoint that issued it.
1652
+ if self.additional_properties.get(REASONING_ORIGIN_KEY) != other.additional_properties.get(
1653
+ REASONING_ORIGIN_KEY
1654
+ ):
1655
+ raise AdditionItemMismatch("Cannot merge reasoning contents from different endpoints")
1656
+
1657
+ # Concatenate text, handling None values
1658
+ self_text = self.text or "" # type: ignore[attr-defined]
1659
+ other_text = other.text or "" # type: ignore[attr-defined]
1660
+ if (
1661
+ self_text
1662
+ and other_text
1663
+ and ("reasoning_text" in self.additional_properties) != ("reasoning_text" in other.additional_properties)
1664
+ ):
1665
+ raise AdditionItemMismatch("Cannot merge reasoning text with a reasoning summary")
1666
+ combined_text = self_text + other_text if (self_text or other_text) else None
1667
+
1668
+ # Handle protected_data replacement. Two id-less sides that BOTH carry
1669
+ # distinct opaque payloads cannot merge by replacement — that silently
1670
+ # drops the earlier payload. Equal non-empty ids keep later-wins: the
1671
+ # ids already matched above, and a same-id pair is the Responses
1672
+ # snapshot→final refinement contract, where the later payload is the
1673
+ # terminal one.
1674
+ if (
1675
+ self.protected_data is not None
1676
+ and other.protected_data is not None
1677
+ and self.protected_data != other.protected_data
1678
+ and not (self.id and other.id)
1679
+ ):
1680
+ raise AdditionItemMismatch("Cannot merge reasoning contents that both carry distinct protected payloads")
1681
+ protected_data = other.protected_data if other.protected_data is not None else self.protected_data # type: ignore[attr-defined]
1682
+
1683
+ return Content(
1684
+ "text_reasoning",
1685
+ id=combined_id,
1686
+ text=combined_text,
1687
+ protected_data=protected_data,
1688
+ annotations=_combine_annotations(self.annotations, other.annotations),
1689
+ additional_properties=_combine_additional_props(self.additional_properties, other.additional_properties),
1690
+ raw_representation=_combine_raw_representations(self.raw_representation, other.raw_representation),
1691
+ )
1692
+
1693
+ def _add_function_call_content(self, other: Content) -> Content:
1694
+ """Add two FunctionCallContent instances."""
1695
+ other_call_id = other.call_id
1696
+ self_call_id = self.call_id
1697
+ if other_call_id and self_call_id != other_call_id:
1698
+ raise ContentError("Cannot add function calls with different call_ids")
1699
+ for key in _RESPONSES_OUTPUT_ITEM_KEYS:
1700
+ mine, theirs = self.additional_properties.get(key), other.additional_properties.get(key)
1701
+ if mine is not None and theirs is not None and mine != theirs:
1702
+ raise AdditionItemMismatch("Cannot merge function calls from different OpenAI Responses output items")
1703
+
1704
+ self_arguments = self.arguments
1705
+ other_arguments = other.arguments
1706
+
1707
+ if not self_arguments:
1708
+ arguments: str | Mapping[str, Any] | None = other_arguments
1709
+ elif not other_arguments:
1710
+ arguments = self_arguments
1711
+ elif isinstance(self_arguments, str) and isinstance(other_arguments, str):
1712
+ arguments = self_arguments + other_arguments
1713
+ elif isinstance(self_arguments, dict) and isinstance(other_arguments, dict):
1714
+ arguments = {**self_arguments, **other_arguments}
1715
+ else:
1716
+ raise TypeError("Incompatible argument types")
1717
+
1718
+ return Content(
1719
+ "function_call",
1720
+ call_id=self_call_id,
1721
+ name=self.name or other.name,
1722
+ arguments=arguments,
1723
+ informational_only=self.informational_only or other.informational_only,
1724
+ exception=self.exception or other.exception,
1725
+ additional_properties=_combine_additional_props(self.additional_properties, other.additional_properties),
1726
+ raw_representation=_combine_raw_representations(self.raw_representation, other.raw_representation),
1727
+ )
1728
+
1729
+ def _add_usage_content(self, other: Content) -> Content:
1730
+ """Add two UsageContent instances by combining their usage details."""
1731
+ return Content(
1732
+ "usage",
1733
+ usage_details=add_usage_details(self.usage_details, other.usage_details),
1734
+ additional_properties=_combine_additional_props(self.additional_properties, other.additional_properties),
1735
+ raw_representation=_combine_raw_representations(self.raw_representation, other.raw_representation),
1736
+ )
1737
+
1738
+ def has_top_level_media_type(self, top_level_media_type: Literal["application", "audio", "image", "text"]) -> bool:
1739
+ """Check if content has a specific top-level media type.
1740
+
1741
+ Works with data, uri, and hosted_file content types.
1742
+
1743
+ Args:
1744
+ top_level_media_type: The top-level media type to check for.
1745
+
1746
+ Returns:
1747
+ True if the content's media type matches the specified top-level type.
1748
+
1749
+ Raises:
1750
+ ContentError: If the content type doesn't support media types.
1751
+
1752
+ Examples:
1753
+ .. code-block:: python
1754
+
1755
+ from chrys.kernel._content import Content
1756
+
1757
+ image = Content.from_uri(uri="data:image/png;base64,abc123", media_type="image/png")
1758
+ print(image.has_top_level_media_type("image")) # True
1759
+ print(image.has_top_level_media_type("audio")) # False
1760
+ """
1761
+ if self.media_type is None:
1762
+ raise ContentError("no media_type found")
1763
+
1764
+ slash_index = self.media_type.find("/")
1765
+ span = self.media_type[:slash_index] if slash_index >= 0 else self.media_type
1766
+ span = span.strip()
1767
+ return span.lower() == top_level_media_type.lower()
1768
+
1769
+ def parse_arguments(self) -> Mapping[str, Any] | None:
1770
+ """Parse arguments from function_call, mcp_server_tool_call, or search_tool_call content.
1771
+
1772
+ If arguments cannot be parsed as JSON or the result is not a dict,
1773
+ they are returned as a dictionary with a single key "raw".
1774
+
1775
+ Returns:
1776
+ Parsed arguments as a dictionary, or None if no arguments.
1777
+
1778
+ Raises:
1779
+ ContentError: If the content type doesn't support arguments.
1780
+
1781
+ Examples:
1782
+ .. code-block:: python
1783
+
1784
+ from chrys.kernel._content import Content
1785
+
1786
+ func_call = Content.from_function_call(
1787
+ call_id="call_123",
1788
+ name="send_email",
1789
+ arguments='{"to": "user@example.com"}',
1790
+ )
1791
+ args = func_call.parse_arguments()
1792
+ print(args) # {"to": "user@example.com"}
1793
+ """
1794
+ if self.arguments is None:
1795
+ return None
1796
+
1797
+ if not self.arguments:
1798
+ return {}
1799
+
1800
+ if isinstance(self.arguments, str):
1801
+ # If arguments are a string, try to parse it as JSON
1802
+ try:
1803
+ loaded = json.loads(self.arguments)
1804
+ if isinstance(loaded, dict):
1805
+ return loaded
1806
+ return {"raw": loaded}
1807
+ except json.JSONDecodeError, TypeError:
1808
+ return {"raw": self.arguments}
1809
+ return self.arguments
1810
+
1811
+
1812
+ def _combine_additional_props(
1813
+ self_additional_properties: dict[str, Any], other_additional_properties: dict[str, Any]
1814
+ ) -> dict[str, Any]:
1815
+ """Combine additional properties for addition operations."""
1816
+ combined = {
1817
+ **other_additional_properties,
1818
+ **self_additional_properties,
1819
+ }
1820
+ left_segment_ids = self_additional_properties.get(PRESENTATION_TEXT_SEGMENT_ID_KEY)
1821
+ right_segment_ids = other_additional_properties.get(PRESENTATION_TEXT_SEGMENT_ID_KEY)
1822
+ if left_segment_ids is not None and right_segment_ids is not None:
1823
+ combined[PRESENTATION_TEXT_SEGMENT_ID_KEY] = tuple(
1824
+ dict.fromkeys(
1825
+ segment_id
1826
+ for value in (left_segment_ids, right_segment_ids)
1827
+ for segment_id in (
1828
+ value
1829
+ if isinstance(value, Sequence) and not isinstance(value, (str, bytes, bytearray))
1830
+ else (value,)
1831
+ )
1832
+ if isinstance(segment_id, str) and segment_id
1833
+ )
1834
+ )
1835
+ return combined
1836
+
1837
+
1838
+ def _combine_raw_representations(
1839
+ self_repr: Any,
1840
+ other_repr: Any,
1841
+ ) -> Any:
1842
+ """Combine raw representations for addition operations."""
1843
+ if self_repr is None:
1844
+ return other_repr
1845
+ if other_repr is None:
1846
+ return self_repr
1847
+ self_list = self_repr if isinstance(self_repr, list) else [self_repr] # type: ignore[reportUnknownVariableType]
1848
+ other_list = other_repr if isinstance(other_repr, list) else [other_repr] # type: ignore[reportUnknownVariableType]
1849
+ return self_list + other_list # type: ignore[reportUnknownVariableType]
1850
+
1851
+
1852
+ def _combine_annotations(
1853
+ self_annotations: Sequence[Annotation] | None,
1854
+ other_annotations: Sequence[Annotation] | None,
1855
+ ) -> Sequence[Annotation] | None:
1856
+ """Combine annotations for addition operations."""
1857
+ if self_annotations is None:
1858
+ return other_annotations
1859
+ if other_annotations is None:
1860
+ return self_annotations
1861
+ return [*self_annotations, *other_annotations]
1862
+
1863
+
1864
+ def _text_for_join(content: Content) -> Any:
1865
+ """Leave validation of the nullable field to ``str.join``, preserving its ``TypeError``."""
1866
+ return content.text