agent-log-server 0.2.122__py3-none-android_24_arm64_v8a.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 (256) hide show
  1. agent_log_server-0.2.122.dist-info/METADATA +186 -0
  2. agent_log_server-0.2.122.dist-info/RECORD +256 -0
  3. agent_log_server-0.2.122.dist-info/WHEEL +5 -0
  4. agent_log_server-0.2.122.dist-info/entry_points.txt +4 -0
  5. agent_log_server-0.2.122.dist-info/licenses/LICENSE +649 -0
  6. agent_log_server-0.2.122.dist-info/top_level.txt +6 -0
  7. agent_log_server_rs/DEVELOPER_MESSAGE_TEMPLATE.md +517 -0
  8. agent_log_server_rs/__init__.py +1 -0
  9. agent_log_server_rs/adapter_protocol.py +548 -0
  10. agent_log_server_rs/adapters/__init__.py +1 -0
  11. agent_log_server_rs/adapters/embedded_adapter.py +157 -0
  12. agent_log_server_rs/adapters/extension_adapter.py +1925 -0
  13. agent_log_server_rs/adapters/rpc_common.py +137 -0
  14. agent_log_server_rs/bin/als-server +0 -0
  15. agent_log_server_rs/bin/als-server.manifest.json +10 -0
  16. agent_log_server_rs/bootstrap.py +706 -0
  17. agent_log_server_rs/codec.py +85 -0
  18. agent_log_server_rs/extension_cli.py +520 -0
  19. agent_log_server_rs/rust/crates/als-server/src/static/app.js +151 -0
  20. agent_log_server_rs/rust/crates/als-server/src/static/appserver.css +240 -0
  21. agent_log_server_rs/rust/crates/als-server/src/static/appserver.js +208 -0
  22. agent_log_server_rs/rust/crates/als-server/src/static/codex_agent.css +4024 -0
  23. agent_log_server_rs/rust/crates/als-server/src/static/codex_agent.ts +2610 -0
  24. agent_log_server_rs/rust/crates/als-server/src/static/codexas-icon.svg +5 -0
  25. agent_log_server_rs/rust/crates/als-server/src/static/dist/codex_agent.js +26 -0
  26. agent_log_server_rs/rust/crates/als-server/src/static/dist/codex_agent.js.map +7 -0
  27. agent_log_server_rs/rust/crates/als-server/src/static/images/green-right-arrow.png +0 -0
  28. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/approvals/ui.ts +949 -0
  29. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/assistant_stream.ts +165 -0
  30. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/boot/init_flow.ts +341 -0
  31. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/boot/input_flow.ts +592 -0
  32. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/client_identity.ts +10 -0
  33. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/composer/runtime.ts +1065 -0
  34. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/conversation/runtime.ts +345 -0
  35. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/conversation_drawer/actions.ts +523 -0
  36. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/conversation_drawer/index.ts +100 -0
  37. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/conversation_drawer/list.ts +966 -0
  38. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/conversation_drawer.ts +1 -0
  39. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/conversation_modal.ts +147 -0
  40. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/diff/rendering.ts +857 -0
  41. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/events/router.ts +779 -0
  42. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/events/socket.ts +140 -0
  43. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/host/runtime.ts +357 -0
  44. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/layout/viewport.ts +95 -0
  45. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/layout/widescreen.ts +168 -0
  46. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/markdown.ts +576 -0
  47. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/orchestrator/rpc_flow.ts +73 -0
  48. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/orchestrator/session_flow.ts +191 -0
  49. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/path_label.ts +76 -0
  50. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/plan/runtime.ts +446 -0
  51. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/plan_modal.ts +102 -0
  52. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/plan_overlay.ts +235 -0
  53. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/project_modal.ts +1398 -0
  54. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/render/utils.ts +456 -0
  55. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/request_cards/runtime.ts +236 -0
  56. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/rpc/conversations/client.ts +1021 -0
  57. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/rpc/conversations/contract.ts +406 -0
  58. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/rpc/namespaces.ts +18 -0
  59. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/rpc/registry.ts +113 -0
  60. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/rpc/settings/client.ts +662 -0
  61. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/rpc/settings/contract.ts +49 -0
  62. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/rpc/transport.ts +463 -0
  63. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/rpc/ui/client.ts +588 -0
  64. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/rpc/ui/contract.ts +50 -0
  65. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/runtime_footer.ts +373 -0
  66. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/settings/runtime_helpers.ts +20 -0
  67. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/settings/save_flow.ts +332 -0
  68. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/settings/ui_flow.ts +1148 -0
  69. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/shared_types.ts +61 -0
  70. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/shell_render.ts +377 -0
  71. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/shell_semantic.ts +606 -0
  72. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/subagents/collapsible.ts +224 -0
  73. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/terminal_ansi.ts +134 -0
  74. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/timeline/live_items.ts +594 -0
  75. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/timeline/replay.ts +820 -0
  76. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/timeline/rows.ts +290 -0
  77. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/timeline/virtualizer.ts +1123 -0
  78. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/timeline_sticky_headers.ts +492 -0
  79. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/tool_render.ts +789 -0
  80. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/transcript_card_metadata.ts +228 -0
  81. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/transcript_cards.ts +780 -0
  82. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/transcript_config.ts +10 -0
  83. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/transcript_loader.ts +511 -0
  84. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/transcript_metrics.ts +28 -0
  85. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/transcript_stream.ts +561 -0
  86. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/transcript_stream_url.ts +7 -0
  87. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/vendor_modules.d.ts +5 -0
  88. agent_log_server_rs/rust/crates/als-server/src/static/js/codex_agent/vendor_runtime.ts +106 -0
  89. agent_log_server_rs/rust/crates/als-server/src/static/js/console_bridge.js +229 -0
  90. agent_log_server_rs/rust/crates/als-server/src/static/js/socketio_runtime.js +27 -0
  91. agent_log_server_rs/rust/crates/als-server/src/static/modals/rollout_picker.ts +46 -0
  92. agent_log_server_rs/rust/crates/als-server/src/static/modals/settings_modal.ts +63 -0
  93. agent_log_server_rs/rust/crates/als-server/src/static/modals/settings_schema.ts +3666 -0
  94. agent_log_server_rs/rust/crates/als-server/src/static/modals/warning_modal.ts +65 -0
  95. agent_log_server_rs/rust/crates/als-server/src/static/style.css +194 -0
  96. agent_log_server_rs/rust/crates/als-server/src/static/ui/extension_settings.ts +493 -0
  97. agent_log_server_rs/rust/crates/als-server/src/static/ui/splash_settings.ts +41 -0
  98. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-cyrillic-400-italic.woff2 +0 -0
  99. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-cyrillic-400-normal.woff2 +0 -0
  100. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-cyrillic-500-italic.woff2 +0 -0
  101. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-cyrillic-500-normal.woff2 +0 -0
  102. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-cyrillic-600-italic.woff2 +0 -0
  103. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-cyrillic-600-normal.woff2 +0 -0
  104. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-cyrillic-700-italic.woff2 +0 -0
  105. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-cyrillic-700-normal.woff2 +0 -0
  106. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-cyrillic-ext-400-italic.woff2 +0 -0
  107. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-cyrillic-ext-400-normal.woff2 +0 -0
  108. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-cyrillic-ext-500-italic.woff2 +0 -0
  109. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-cyrillic-ext-500-normal.woff2 +0 -0
  110. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-cyrillic-ext-600-italic.woff2 +0 -0
  111. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-cyrillic-ext-600-normal.woff2 +0 -0
  112. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-cyrillic-ext-700-italic.woff2 +0 -0
  113. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-cyrillic-ext-700-normal.woff2 +0 -0
  114. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-greek-400-italic.woff2 +0 -0
  115. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-greek-400-normal.woff2 +0 -0
  116. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-greek-500-italic.woff2 +0 -0
  117. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-greek-500-normal.woff2 +0 -0
  118. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-greek-600-italic.woff2 +0 -0
  119. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-greek-600-normal.woff2 +0 -0
  120. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-greek-700-italic.woff2 +0 -0
  121. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-greek-700-normal.woff2 +0 -0
  122. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-latin-400-italic.woff2 +0 -0
  123. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-latin-400-normal.woff2 +0 -0
  124. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-latin-500-italic.woff2 +0 -0
  125. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-latin-500-normal.woff2 +0 -0
  126. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-latin-600-italic.woff2 +0 -0
  127. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-latin-600-normal.woff2 +0 -0
  128. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-latin-700-italic.woff2 +0 -0
  129. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-latin-700-normal.woff2 +0 -0
  130. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-latin-ext-400-italic.woff2 +0 -0
  131. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-latin-ext-400-normal.woff2 +0 -0
  132. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-latin-ext-500-italic.woff2 +0 -0
  133. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-latin-ext-500-normal.woff2 +0 -0
  134. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-latin-ext-600-italic.woff2 +0 -0
  135. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-latin-ext-600-normal.woff2 +0 -0
  136. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-latin-ext-700-italic.woff2 +0 -0
  137. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-latin-ext-700-normal.woff2 +0 -0
  138. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-vietnamese-400-italic.woff2 +0 -0
  139. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-vietnamese-400-normal.woff2 +0 -0
  140. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-vietnamese-500-italic.woff2 +0 -0
  141. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-vietnamese-500-normal.woff2 +0 -0
  142. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-vietnamese-600-italic.woff2 +0 -0
  143. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-vietnamese-600-normal.woff2 +0 -0
  144. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-vietnamese-700-italic.woff2 +0 -0
  145. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono/jetbrains-mono-vietnamese-700-normal.woff2 +0 -0
  146. agent_log_server_rs/rust/crates/als-server/src/static/vendor/fonts/jetbrains-mono.css +481 -0
  147. agent_log_server_rs/rust/crates/als-server/src/static/vendor/socket.io/socket.io.min.js +7 -0
  148. agent_log_server_rs/rust/crates/als-server/src/static/vendor/socket.io-msgpack-parser/socket.io-msgpack-parser.js +2 -0
  149. agent_log_server_rs/rust/crates/als-server/src/static/vendor/socket.io-msgpack-parser/socket.io-msgpack-parser.js.map +7 -0
  150. agent_log_server_rs/rust/crates/als-server/src/static/vendor/tree-sitter-bash/highlights.scm +56 -0
  151. agent_log_server_rs/rust/crates/als-server/src/static/vendor/tree-sitter-bash/tree-sitter-bash.wasm +0 -0
  152. agent_log_server_rs/rust/crates/als-server/src/static/vendor/web-tree-sitter/tree-sitter.js +3278 -0
  153. agent_log_server_rs/rust/crates/als-server/src/static/vendor/web-tree-sitter/tree-sitter.wasm +0 -0
  154. agent_log_server_rs/rust/crates/als-server/src/static/vendor/web-tree-sitter/web-tree-sitter.js +4007 -0
  155. agent_log_server_rs/rust/crates/als-server/src/static/vendor/web-tree-sitter/web-tree-sitter.wasm +0 -0
  156. agent_log_server_rs/shellspec/extension_adapter.yaml +15 -0
  157. agent_log_server_rs/tests/test_bootstrap.py +435 -0
  158. agent_log_server_rs/tests/test_codec.py +97 -0
  159. agent_log_server_rs/tests/test_codex_ext_client_resume.py +37 -0
  160. agent_log_server_rs/tests/test_codex_ext_router_atomic_diff.py +221 -0
  161. agent_log_server_rs/tests/test_codex_ext_runtime_protocol.py +109 -0
  162. agent_log_server_rs/tests/test_codex_ext_transport_resume.py +190 -0
  163. agent_log_server_rs/tests/test_extension_adapter_dispatch.py +170 -0
  164. agent_log_server_rs/tests/test_kb_and_codex_search.py +415 -0
  165. agent_log_server_rs/tests/test_mcp_socketio_serializer.py +53 -0
  166. agent_log_server_rs/tests/test_release_binary_wheel.py +119 -0
  167. als_deprecated/__init__.py +2 -0
  168. als_deprecated/__main__.py +5 -0
  169. als_deprecated/agent_log_subsystem.py +276 -0
  170. als_deprecated/agent_pty_http_shim_lifecycle.py +171 -0
  171. als_deprecated/appserver_routes.py +1930 -0
  172. als_deprecated/appserver_socketio.py +1223 -0
  173. als_deprecated/ask_user_interactions.py +415 -0
  174. als_deprecated/conversation_store.py +279 -0
  175. als_deprecated/conversation_todos.py +271 -0
  176. als_deprecated/conversations_rpc_contract.py +849 -0
  177. als_deprecated/extension_api.py +1041 -0
  178. als_deprecated/extension_cli.py +347 -0
  179. als_deprecated/host_routes.py +493 -0
  180. als_deprecated/index.py +30 -0
  181. als_deprecated/ipc_auth.py +36 -0
  182. als_deprecated/markdown_sections.py +141 -0
  183. als_deprecated/mcp_agent_pty_http_shim.py +30 -0
  184. als_deprecated/page_routes.py +71 -0
  185. als_deprecated/pending_context.py +671 -0
  186. als_deprecated/prompt_context.py +165 -0
  187. als_deprecated/repo_memory_delta.py +126 -0
  188. als_deprecated/rpc_stdio_mirror.py +134 -0
  189. als_deprecated/server.py +2004 -0
  190. als_deprecated/settings_ui_rpc_contract.py +311 -0
  191. als_deprecated/shellspec/agent_log.yaml +13 -0
  192. als_deprecated/shellspec/mcp_agent_pty.yaml +54 -0
  193. als_deprecated/socketio_config.py +53 -0
  194. als_deprecated/te2_assets/console_bridge.js +208 -0
  195. als_deprecated/te2_assets/framework_shells_README.md +448 -0
  196. als_deprecated/te2_assets/proxy_shell_wrapper_README.md +18 -0
  197. als_deprecated/te2_mcp_config.py +83 -0
  198. als_deprecated/te2_runtime.py +35 -0
  199. als_deprecated/te2_sync.py +78 -0
  200. als_deprecated/templates/template.html +46 -0
  201. als_deprecated/transcript_card_metadata.py +211 -0
  202. als_deprecated/typing_helpers.py +26 -0
  203. als_deprecated/web_pages.py +770 -0
  204. extensions/__init__.py +3107 -0
  205. extensions/codex_ext/__init__.py +1 -0
  206. extensions/codex_ext/client.py +1843 -0
  207. extensions/codex_ext/dependencies.py +143 -0
  208. extensions/codex_ext/devins_contract.py +25 -0
  209. extensions/codex_ext/manifest.json +138 -0
  210. extensions/codex_ext/mcp_contract.py +183 -0
  211. extensions/codex_ext/plan_utils.py +74 -0
  212. extensions/codex_ext/rollout_import.py +204 -0
  213. extensions/codex_ext/router.py +3772 -0
  214. extensions/codex_ext/runtime_protocol.py +1699 -0
  215. extensions/codex_ext/settings_schema.json +172 -0
  216. extensions/codex_ext/shellspec/app_server.yaml +19 -0
  217. extensions/codex_ext/transport.py +1412 -0
  218. extensions/codex_ext/ui/request_cards/codex_ext_request_card.js +882 -0
  219. extensions/copilot_sdk/VENDORED_SDK.md +17 -0
  220. extensions/copilot_sdk/__init__.py +2 -0
  221. extensions/copilot_sdk/_vendor/__init__.py +1 -0
  222. extensions/copilot_sdk/_vendor/copilot/__init__.py +80 -0
  223. extensions/copilot_sdk/_vendor/copilot/_diagnostics.py +29 -0
  224. extensions/copilot_sdk/_vendor/copilot/_jsonrpc.py +442 -0
  225. extensions/copilot_sdk/_vendor/copilot/_sdk_protocol_version.py +19 -0
  226. extensions/copilot_sdk/_vendor/copilot/_telemetry.py +48 -0
  227. extensions/copilot_sdk/_vendor/copilot/client.py +3157 -0
  228. extensions/copilot_sdk/_vendor/copilot/generated/__init__.py +0 -0
  229. extensions/copilot_sdk/_vendor/copilot/generated/rpc.py +7149 -0
  230. extensions/copilot_sdk/_vendor/copilot/generated/session_events.py +5009 -0
  231. extensions/copilot_sdk/_vendor/copilot/py.typed +1 -0
  232. extensions/copilot_sdk/_vendor/copilot/session.py +2415 -0
  233. extensions/copilot_sdk/_vendor/copilot/session_fs_provider.py +223 -0
  234. extensions/copilot_sdk/_vendor/copilot/tools.py +327 -0
  235. extensions/copilot_sdk/client.py +3871 -0
  236. extensions/copilot_sdk/dependencies.py +108 -0
  237. extensions/copilot_sdk/devins_contract.py +25 -0
  238. extensions/copilot_sdk/file_change_preview.py +80 -0
  239. extensions/copilot_sdk/fws_pipe_process.py +382 -0
  240. extensions/copilot_sdk/manifest.json +135 -0
  241. extensions/copilot_sdk/mcp_contract.py +63 -0
  242. extensions/copilot_sdk/plan_utils.py +56 -0
  243. extensions/copilot_sdk/protocol_adapter.py +342 -0
  244. extensions/copilot_sdk/protocol_registry.py +149 -0
  245. extensions/copilot_sdk/router.py +1789 -0
  246. extensions/copilot_sdk/schemas/api.schema.json +1063 -0
  247. extensions/copilot_sdk/schemas/session-events.schema.json +6369 -0
  248. extensions/copilot_sdk/settings_schema.json +165 -0
  249. extensions/copilot_sdk/shellspec/copilot_cli.yaml +20 -0
  250. extensions/copilot_sdk/te2_runtime.py +78 -0
  251. extensions/copilot_sdk/ui/request_cards/copilot_request_card.js +449 -0
  252. extensions/extensions.json +19 -0
  253. extensions/message_card_contracts.py +213 -0
  254. extensions/tool_card_contracts.py +102 -0
  255. mcp_agent_pty_http_shim.py +321 -0
  256. mcp_agent_pty_server.py +2931 -0
@@ -0,0 +1,849 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import uuid
5
+ from collections.abc import Callable
6
+ from dataclasses import dataclass
7
+ from typing import Literal, TypeAlias, cast
8
+
9
+ from fastapi import HTTPException
10
+
11
+ from als_deprecated.typing_helpers import (
12
+ ObjectList,
13
+ ObjectMap,
14
+ RequestId,
15
+ coerce_object_list,
16
+ coerce_object_map,
17
+ )
18
+
19
+ CONVERSATIONS_RPC_NAMESPACE = "/rpc/conversations"
20
+ CONVERSATION_SEND_METHOD = "conversation.send"
21
+ CONVERSATION_INTERRUPT_METHOD = "conversation.interrupt"
22
+ CONVERSATION_COMPACT_METHOD = "conversation.compact"
23
+ CONVERSATION_REPLAY_GET_CHUNK_METHOD = "conversation.replay.getChunk"
24
+ CONVERSATION_APPROVAL_RESPONSE_METHOD = "conversation.approval.respond"
25
+ CONVERSATION_SHELL_EXEC_METHOD = "conversation.shell.exec"
26
+ CONVERSATION_GET_METHOD = "conversation.get"
27
+ CONVERSATION_LIST_METHOD = "conversation.list"
28
+ CONVERSATION_CREATE_METHOD = "conversation.create"
29
+ CONVERSATION_SELECT_METHOD = "conversation.select"
30
+ CONVERSATION_UPDATE_METHOD = "conversation.update"
31
+ CONVERSATION_DELETE_METHOD = "conversation.delete"
32
+ CONVERSATION_PINS_SET_METHOD = "conversation.pins.set"
33
+ CONVERSATION_DRAFT_SET_METHOD = "conversation.draft.set"
34
+ CONVERSATIONS_RPC_NOTIFICATION_METHOD_BY_EVENT_TYPE: dict[str, str] = {
35
+ "activity": "conversation.activity",
36
+ "approval": "conversation.approval.request",
37
+ "approval_handoff": "conversation.approval.handoff",
38
+ "assistant_delta": "conversation.message.delta",
39
+ "assistant_end": "conversation.message.final",
40
+ "assistant_finalize": "conversation.message.final",
41
+ "command_result": "conversation.command.result",
42
+ "context_compacted": "conversation.context.compacted",
43
+ "diff": "conversation.diff",
44
+ "diff_declined": "conversation.diff.declined",
45
+ "draft_update": "conversation.draft.updated",
46
+ "error": "conversation.error",
47
+ "mention_insert": "conversation.mention.inserted",
48
+ "message": "conversation.user.message",
49
+ "meta_updated": "conversation.meta.updated",
50
+ "mode": "conversation.mode.changed",
51
+ "plan": "conversation.plan",
52
+ "plan_state": "conversation.plan.state",
53
+ "plan_update": "conversation.plan.update",
54
+ "preview_updated": "conversation.preview.updated",
55
+ "reasoning_delta": "conversation.reasoning.delta",
56
+ "reasoning_end": "conversation.reasoning.final",
57
+ "reasoning_finalize": "conversation.reasoning.final",
58
+ "shell_begin": "conversation.command.begin",
59
+ "shell_delta": "conversation.command.delta",
60
+ "shell_end": "conversation.command.end",
61
+ "status": "conversation.status",
62
+ "subagent_end": "conversation.subagent.end",
63
+ "subagent_start": "conversation.subagent.start",
64
+ "thought": "conversation.thought",
65
+ "toast": "conversation.toast",
66
+ "token_count": "conversation.token.updated",
67
+ "tool_interaction": "conversation.tool.interaction",
68
+ "tool_begin": "conversation.tool.begin",
69
+ "tool_delta": "conversation.tool.delta",
70
+ "tool_end": "conversation.tool.end",
71
+ "search": "conversation.search",
72
+ "view": "conversation.view",
73
+ "warning": "conversation.warning",
74
+ }
75
+
76
+ ConversationsRpcMethod: TypeAlias = Literal[
77
+ "conversation.get",
78
+ "conversation.list",
79
+ "conversation.create",
80
+ "conversation.select",
81
+ "conversation.update",
82
+ "conversation.delete",
83
+ "conversation.pins.set",
84
+ "conversation.draft.set",
85
+ "conversation.send",
86
+ "conversation.interrupt",
87
+ "conversation.compact",
88
+ "conversation.replay.getChunk",
89
+ "conversation.approval.respond",
90
+ "conversation.shell.exec",
91
+ ]
92
+ SanitizeConversationId: TypeAlias = Callable[[str], str]
93
+
94
+
95
+ class ConversationsRpcProtocolError(Exception):
96
+ def __init__(
97
+ self,
98
+ request_id: RequestId,
99
+ *,
100
+ code: int,
101
+ message: str,
102
+ data: ObjectMap | None = None,
103
+ ) -> None:
104
+ super().__init__(message)
105
+ self.request_id = request_id
106
+ self.code = code
107
+ self.message = message
108
+ self.data = data or {}
109
+
110
+
111
+ @dataclass(frozen=True)
112
+ class JsonRpcSuccessResponse:
113
+ request_id: RequestId
114
+ result: ObjectMap
115
+
116
+ def to_json(self) -> ObjectMap:
117
+ return {
118
+ "jsonrpc": "2.0",
119
+ "id": self.request_id,
120
+ "result": self.result,
121
+ }
122
+
123
+
124
+ @dataclass(frozen=True)
125
+ class JsonRpcErrorResponse:
126
+ request_id: RequestId
127
+ code: int
128
+ message: str
129
+ data: ObjectMap | None = None
130
+
131
+ def to_json(self) -> ObjectMap:
132
+ error: ObjectMap = {
133
+ "code": self.code,
134
+ "message": self.message,
135
+ }
136
+ if self.data:
137
+ error["data"] = self.data
138
+ return {
139
+ "jsonrpc": "2.0",
140
+ "id": self.request_id,
141
+ "error": error,
142
+ }
143
+
144
+
145
+ @dataclass(frozen=True)
146
+ class ParsedConversationsRpcRequest:
147
+ request_id: RequestId
148
+ method: ConversationsRpcMethod
149
+ params: ObjectMap
150
+
151
+
152
+ @dataclass(frozen=True)
153
+ class ConversationSendParams:
154
+ conversation_id: str
155
+ text: str
156
+
157
+
158
+ @dataclass(frozen=True)
159
+ class ConversationGetParams:
160
+ conversation_id: str | None
161
+
162
+
163
+ @dataclass(frozen=True)
164
+ class ConversationCreateParams:
165
+ settings: ObjectMap | None
166
+
167
+ def to_json(self) -> ObjectMap:
168
+ payload: ObjectMap = {}
169
+ if self.settings:
170
+ payload["settings"] = self.settings
171
+ return payload
172
+
173
+
174
+ @dataclass(frozen=True)
175
+ class ConversationSelectParams:
176
+ conversation_id: str
177
+ view: Literal["splash", "conversation"] | None = None
178
+
179
+ def to_json(self) -> ObjectMap:
180
+ payload: ObjectMap = {"conversation_id": self.conversation_id}
181
+ if self.view is not None:
182
+ payload["view"] = self.view
183
+ return payload
184
+
185
+
186
+ @dataclass(frozen=True)
187
+ class ConversationUpdateParams:
188
+ conversation_id: str | None
189
+ settings: ObjectMap | None = None
190
+ thread_id: str | None = None
191
+
192
+ def to_json(self) -> ObjectMap:
193
+ payload: ObjectMap = {}
194
+ if self.conversation_id:
195
+ payload["conversation_id"] = self.conversation_id
196
+ if self.settings is not None:
197
+ payload["settings"] = self.settings
198
+ if self.thread_id:
199
+ payload["thread_id"] = self.thread_id
200
+ return payload
201
+
202
+
203
+ @dataclass(frozen=True)
204
+ class ConversationDeleteParams:
205
+ conversation_id: str
206
+
207
+
208
+ @dataclass(frozen=True)
209
+ class ConversationPinsSetParams:
210
+ pinned_conversations: list[str]
211
+
212
+ def to_json(self) -> ObjectMap:
213
+ return {"pinned_conversations": list(self.pinned_conversations)}
214
+
215
+
216
+ @dataclass(frozen=True)
217
+ class ConversationDraftSetParams:
218
+ conversation_id: str | None
219
+ draft: str
220
+
221
+ def to_json(self) -> ObjectMap:
222
+ payload: ObjectMap = {"draft": self.draft}
223
+ if self.conversation_id:
224
+ payload["conversation_id"] = self.conversation_id
225
+ return payload
226
+
227
+
228
+ @dataclass(frozen=True)
229
+ class ConversationControlParams:
230
+ conversation_id: str
231
+
232
+ def to_json(self) -> ObjectMap:
233
+ return {"conversation_id": self.conversation_id}
234
+
235
+
236
+ @dataclass(frozen=True)
237
+ class ConversationApprovalResponseParams:
238
+ payload: ObjectMap
239
+
240
+
241
+ @dataclass(frozen=True)
242
+ class ConversationShellExecParams:
243
+ payload: ObjectMap
244
+
245
+
246
+ @dataclass(frozen=True)
247
+ class ReplayChunkCursor:
248
+ offset: int
249
+
250
+ def to_json(self) -> ObjectMap:
251
+ return {"offset": self.offset}
252
+
253
+
254
+ @dataclass(frozen=True)
255
+ class ConversationReplayGetChunkParams:
256
+ conversation_id: str | None
257
+ cursor: ReplayChunkCursor
258
+ max_entries: int
259
+ max_bytes: int
260
+ include_internal: bool
261
+ format_name: Literal["jsonl"] = "jsonl"
262
+
263
+
264
+ @dataclass(frozen=True)
265
+ class ConversationSendResult:
266
+ conversation_id: str
267
+ accepted: bool
268
+ payload: ObjectMap
269
+
270
+ def to_json(self) -> ObjectMap:
271
+ normalized = dict(self.payload)
272
+ normalized.setdefault("conversation_id", self.conversation_id)
273
+ normalized["accepted"] = self.accepted
274
+ return normalized
275
+
276
+
277
+ @dataclass(frozen=True)
278
+ class ConversationControlResult:
279
+ ok: bool
280
+ error: str | None
281
+ payload: ObjectMap
282
+
283
+ def to_json(self) -> ObjectMap:
284
+ normalized = dict(self.payload)
285
+ normalized.setdefault("ok", self.ok)
286
+ if self.error is not None:
287
+ normalized.setdefault("error", self.error)
288
+ return normalized
289
+
290
+
291
+ @dataclass(frozen=True)
292
+ class ReplayRangeData:
293
+ conversation_id: str | None
294
+ items: ObjectList
295
+ offset: int
296
+ total_count: int
297
+
298
+
299
+ @dataclass(frozen=True)
300
+ class ReplayJsonlChunk:
301
+ jsonl: str
302
+ item_count: int
303
+
304
+
305
+ @dataclass(frozen=True)
306
+ class ReplayChunkFrame:
307
+ format_name: Literal["jsonl"]
308
+ offset: int
309
+ item_count: int
310
+ total_count: int
311
+ chunk_index: int
312
+ complete: bool
313
+ next_cursor: ReplayChunkCursor | None
314
+ jsonl: str
315
+
316
+ def to_json(self) -> ObjectMap:
317
+ return {
318
+ "format": self.format_name,
319
+ "offset": self.offset,
320
+ "item_count": self.item_count,
321
+ "total_count": self.total_count,
322
+ "chunk_index": self.chunk_index,
323
+ "complete": self.complete,
324
+ "next_cursor": None if self.next_cursor is None else self.next_cursor.to_json(),
325
+ "jsonl": self.jsonl,
326
+ }
327
+
328
+
329
+ @dataclass(frozen=True)
330
+ class ReplayChunkResult:
331
+ conversation_id: str | None
332
+ replay_id: str
333
+ frame: ReplayChunkFrame
334
+
335
+ def to_json(self) -> ObjectMap:
336
+ return {
337
+ "conversation_id": self.conversation_id,
338
+ "replay_id": self.replay_id,
339
+ "frame": self.frame.to_json(),
340
+ }
341
+
342
+
343
+ def coerce_request_id(value: object) -> RequestId:
344
+ if isinstance(value, bool):
345
+ return None
346
+ return value if isinstance(value, (str, int)) else None
347
+
348
+
349
+ def conversation_rpc_notification_method(evt_type: object) -> str | None:
350
+ normalized = str(evt_type or "").strip().lower()
351
+ if not normalized:
352
+ return None
353
+ return CONVERSATIONS_RPC_NOTIFICATION_METHOD_BY_EVENT_TYPE.get(normalized)
354
+
355
+
356
+ def build_jsonrpc_success_response(
357
+ request_id: RequestId,
358
+ result: ObjectMap,
359
+ ) -> JsonRpcSuccessResponse:
360
+ return JsonRpcSuccessResponse(request_id=request_id, result=result)
361
+
362
+
363
+ def build_jsonrpc_error_response(
364
+ request_id: RequestId,
365
+ *,
366
+ code: int,
367
+ message: str,
368
+ data: ObjectMap | None = None,
369
+ ) -> JsonRpcErrorResponse:
370
+ return JsonRpcErrorResponse(
371
+ request_id=request_id,
372
+ code=code,
373
+ message=message,
374
+ data=data,
375
+ )
376
+
377
+
378
+ def build_jsonrpc_error_response_from_http_exception(
379
+ request_id: RequestId,
380
+ *,
381
+ method: str,
382
+ exc: HTTPException,
383
+ ) -> JsonRpcErrorResponse:
384
+ status_code = int(getattr(exc, "status_code", 500) or 500)
385
+ detail = exc.detail
386
+ message = detail if isinstance(detail, str) and detail.strip() else "Request failed"
387
+ if status_code == 400:
388
+ code = -32602
389
+ error_code = "INVALID_REQUEST"
390
+ elif status_code == 404:
391
+ code = -32004
392
+ error_code = "NOT_FOUND"
393
+ elif status_code == 409:
394
+ code = -32009
395
+ error_code = "CONFLICT"
396
+ else:
397
+ code = -32603
398
+ error_code = "INTERNAL_ERROR"
399
+ return build_jsonrpc_error_response(
400
+ request_id,
401
+ code=code,
402
+ message=message,
403
+ data={
404
+ "code": error_code,
405
+ "status_code": status_code,
406
+ "method": method,
407
+ },
408
+ )
409
+
410
+
411
+ def parse_conversations_rpc_request(payload: object) -> ParsedConversationsRpcRequest:
412
+ request_id = coerce_request_id(payload.get("id") if isinstance(payload, dict) else None)
413
+ if not isinstance(payload, dict):
414
+ raise ConversationsRpcProtocolError(
415
+ request_id,
416
+ code=-32600,
417
+ message="Invalid request",
418
+ data={"code": "INVALID_REQUEST", "reason": "Payload must be an object"},
419
+ )
420
+
421
+ payload_map = coerce_object_map(payload)
422
+ if payload_map.get("jsonrpc") != "2.0":
423
+ raise ConversationsRpcProtocolError(
424
+ request_id,
425
+ code=-32600,
426
+ message="Invalid request",
427
+ data={"code": "INVALID_REQUEST", "reason": "jsonrpc must be '2.0'"},
428
+ )
429
+
430
+ method_value = payload_map.get("method")
431
+ if not isinstance(method_value, str) or not method_value.strip():
432
+ raise ConversationsRpcProtocolError(
433
+ request_id,
434
+ code=-32600,
435
+ message="Invalid request",
436
+ data={"code": "INVALID_REQUEST", "reason": "method is required"},
437
+ )
438
+
439
+ params_value = payload_map.get("params", {})
440
+ if params_value is None:
441
+ params_value = {}
442
+ if not isinstance(params_value, dict):
443
+ raise ConversationsRpcProtocolError(
444
+ request_id,
445
+ code=-32602,
446
+ message="Invalid params",
447
+ data={"code": "INVALID_REQUEST", "reason": "params must be an object"},
448
+ )
449
+
450
+ method = method_value.strip()
451
+ if method == CONVERSATION_GET_METHOD:
452
+ parsed_method: ConversationsRpcMethod = CONVERSATION_GET_METHOD
453
+ elif method == CONVERSATION_LIST_METHOD:
454
+ parsed_method = CONVERSATION_LIST_METHOD
455
+ elif method == CONVERSATION_CREATE_METHOD:
456
+ parsed_method = CONVERSATION_CREATE_METHOD
457
+ elif method == CONVERSATION_SELECT_METHOD:
458
+ parsed_method = CONVERSATION_SELECT_METHOD
459
+ elif method == CONVERSATION_UPDATE_METHOD:
460
+ parsed_method = CONVERSATION_UPDATE_METHOD
461
+ elif method == CONVERSATION_DELETE_METHOD:
462
+ parsed_method = CONVERSATION_DELETE_METHOD
463
+ elif method == CONVERSATION_PINS_SET_METHOD:
464
+ parsed_method = CONVERSATION_PINS_SET_METHOD
465
+ elif method == CONVERSATION_DRAFT_SET_METHOD:
466
+ parsed_method = CONVERSATION_DRAFT_SET_METHOD
467
+ elif method == CONVERSATION_SEND_METHOD:
468
+ parsed_method = CONVERSATION_SEND_METHOD
469
+ elif method == CONVERSATION_INTERRUPT_METHOD:
470
+ parsed_method = CONVERSATION_INTERRUPT_METHOD
471
+ elif method == CONVERSATION_COMPACT_METHOD:
472
+ parsed_method = CONVERSATION_COMPACT_METHOD
473
+ elif method == CONVERSATION_REPLAY_GET_CHUNK_METHOD:
474
+ parsed_method = CONVERSATION_REPLAY_GET_CHUNK_METHOD
475
+ elif method == CONVERSATION_APPROVAL_RESPONSE_METHOD:
476
+ parsed_method = CONVERSATION_APPROVAL_RESPONSE_METHOD
477
+ elif method == CONVERSATION_SHELL_EXEC_METHOD:
478
+ parsed_method = CONVERSATION_SHELL_EXEC_METHOD
479
+ else:
480
+ raise ConversationsRpcProtocolError(
481
+ request_id,
482
+ code=-32601,
483
+ message="Method not found",
484
+ data={"code": "NOT_FOUND", "method": method},
485
+ )
486
+
487
+ return ParsedConversationsRpcRequest(
488
+ request_id=request_id,
489
+ method=parsed_method,
490
+ params=coerce_object_map(params_value),
491
+ )
492
+
493
+
494
+ def parse_conversation_send_params(
495
+ params: ObjectMap,
496
+ *,
497
+ sanitize_conversation_id: SanitizeConversationId,
498
+ ) -> ConversationSendParams:
499
+ conversation_id = _require_sanitized_conversation_id(
500
+ params.get("conversation_id"),
501
+ sanitize_conversation_id=sanitize_conversation_id,
502
+ detail="conversation_id and text required",
503
+ )
504
+ text_value = params.get("text")
505
+ text = text_value if isinstance(text_value, str) else ""
506
+ if not text:
507
+ raise HTTPException(status_code=400, detail="conversation_id and text required")
508
+ return ConversationSendParams(conversation_id=conversation_id, text=text)
509
+
510
+
511
+ def parse_conversation_control_params(
512
+ params: ObjectMap,
513
+ *,
514
+ sanitize_conversation_id: SanitizeConversationId,
515
+ ) -> ConversationControlParams:
516
+ conversation_id = _require_sanitized_conversation_id(
517
+ params.get("conversation_id"),
518
+ sanitize_conversation_id=sanitize_conversation_id,
519
+ detail="Missing required field: conversation_id",
520
+ )
521
+ return ConversationControlParams(conversation_id=conversation_id)
522
+
523
+
524
+ def parse_conversation_get_params(
525
+ params: ObjectMap,
526
+ *,
527
+ sanitize_conversation_id: SanitizeConversationId,
528
+ active_conversation_id: str | None,
529
+ ) -> ConversationGetParams:
530
+ conversation_id_raw = params.get("conversation_id")
531
+ if isinstance(conversation_id_raw, str) and conversation_id_raw.strip():
532
+ return ConversationGetParams(
533
+ conversation_id=sanitize_conversation_id(conversation_id_raw.strip()),
534
+ )
535
+ if isinstance(active_conversation_id, str) and active_conversation_id.strip():
536
+ return ConversationGetParams(
537
+ conversation_id=sanitize_conversation_id(active_conversation_id.strip()),
538
+ )
539
+ return ConversationGetParams(conversation_id=None)
540
+
541
+
542
+ def parse_conversation_approval_response_params(
543
+ params: ObjectMap,
544
+ ) -> ConversationApprovalResponseParams:
545
+ request_id_value = params.get("request_id", params.get("requestId", params.get("id")))
546
+ request_id = str(request_id_value or "").strip()
547
+ if not request_id:
548
+ raise HTTPException(status_code=400, detail="Missing request_id")
549
+ return ConversationApprovalResponseParams(payload=dict(params))
550
+
551
+
552
+ def parse_conversation_shell_exec_params(
553
+ params: ObjectMap,
554
+ *,
555
+ sanitize_conversation_id: SanitizeConversationId,
556
+ ) -> ConversationShellExecParams:
557
+ conversation_id = _require_sanitized_conversation_id(
558
+ params.get("conversation_id"),
559
+ sanitize_conversation_id=sanitize_conversation_id,
560
+ detail="Missing required field: conversation_id",
561
+ )
562
+ command = params.get("command")
563
+ if not isinstance(command, str) or not command.strip():
564
+ raise HTTPException(status_code=400, detail="No command provided")
565
+ payload = dict(params)
566
+ payload["conversation_id"] = conversation_id
567
+ payload["command"] = command
568
+ return ConversationShellExecParams(payload=payload)
569
+
570
+
571
+ def parse_conversation_create_params(params: ObjectMap) -> ConversationCreateParams:
572
+ settings_value = params.get("settings")
573
+ settings = coerce_object_map(settings_value) if isinstance(settings_value, dict) else None
574
+ return ConversationCreateParams(settings=settings or None)
575
+
576
+
577
+ def parse_conversation_select_params(
578
+ params: ObjectMap,
579
+ *,
580
+ sanitize_conversation_id: SanitizeConversationId,
581
+ ) -> ConversationSelectParams:
582
+ conversation_id = _require_sanitized_conversation_id(
583
+ params.get("conversation_id", params.get("id")),
584
+ sanitize_conversation_id=sanitize_conversation_id,
585
+ detail="Missing conversation_id",
586
+ )
587
+ view_value = params.get("view")
588
+ view: Literal["splash", "conversation"] | None = None
589
+ if isinstance(view_value, str):
590
+ normalized_view = view_value.strip().lower()
591
+ if normalized_view in {"splash", "conversation"}:
592
+ view = cast(Literal["splash", "conversation"], normalized_view)
593
+ return ConversationSelectParams(conversation_id=conversation_id, view=view)
594
+
595
+
596
+ def parse_conversation_update_params(
597
+ params: ObjectMap,
598
+ *,
599
+ sanitize_conversation_id: SanitizeConversationId,
600
+ ) -> ConversationUpdateParams:
601
+ conversation_id_raw = params.get("conversation_id")
602
+ conversation_id = (
603
+ sanitize_conversation_id(conversation_id_raw.strip())
604
+ if isinstance(conversation_id_raw, str) and conversation_id_raw.strip()
605
+ else None
606
+ )
607
+ settings_value = params.get("settings")
608
+ settings = coerce_object_map(settings_value) if isinstance(settings_value, dict) else None
609
+ thread_id_value = params.get("thread_id")
610
+ thread_id = thread_id_value.strip() if isinstance(thread_id_value, str) and thread_id_value.strip() else None
611
+ return ConversationUpdateParams(
612
+ conversation_id=conversation_id,
613
+ settings=settings,
614
+ thread_id=thread_id,
615
+ )
616
+
617
+
618
+ def parse_conversation_delete_params(
619
+ params: ObjectMap,
620
+ *,
621
+ sanitize_conversation_id: SanitizeConversationId,
622
+ ) -> ConversationDeleteParams:
623
+ conversation_id = _require_sanitized_conversation_id(
624
+ params.get("conversation_id"),
625
+ sanitize_conversation_id=sanitize_conversation_id,
626
+ detail="Missing conversation_id",
627
+ )
628
+ return ConversationDeleteParams(conversation_id=conversation_id)
629
+
630
+
631
+ def parse_conversation_pins_set_params(
632
+ params: ObjectMap,
633
+ *,
634
+ sanitize_conversation_id: SanitizeConversationId,
635
+ ) -> ConversationPinsSetParams:
636
+ requested = params.get("pinned_conversations")
637
+ if not isinstance(requested, list):
638
+ raise HTTPException(status_code=400, detail="pinned_conversations must be a list")
639
+ pinned: list[str] = []
640
+ for item in requested:
641
+ if not isinstance(item, str) or not item.strip():
642
+ continue
643
+ convo_id = sanitize_conversation_id(item.strip())
644
+ if convo_id and convo_id not in pinned:
645
+ pinned.append(convo_id)
646
+ return ConversationPinsSetParams(pinned_conversations=pinned)
647
+
648
+
649
+ def parse_conversation_draft_set_params(
650
+ params: ObjectMap,
651
+ *,
652
+ sanitize_conversation_id: SanitizeConversationId,
653
+ ) -> ConversationDraftSetParams:
654
+ conversation_id_raw = params.get("conversation_id")
655
+ conversation_id = (
656
+ sanitize_conversation_id(conversation_id_raw.strip())
657
+ if isinstance(conversation_id_raw, str) and conversation_id_raw.strip()
658
+ else None
659
+ )
660
+ draft_value = params.get("draft", "")
661
+ draft = draft_value if isinstance(draft_value, str) else ""
662
+ return ConversationDraftSetParams(conversation_id=conversation_id, draft=draft)
663
+
664
+
665
+ def parse_conversation_replay_get_chunk_params(
666
+ params: ObjectMap,
667
+ *,
668
+ sanitize_conversation_id: SanitizeConversationId,
669
+ active_conversation_id: str | None,
670
+ ) -> ConversationReplayGetChunkParams:
671
+ conversation_id_raw = params.get("conversation_id")
672
+ conversation_id = (
673
+ sanitize_conversation_id(conversation_id_raw.strip())
674
+ if isinstance(conversation_id_raw, str) and conversation_id_raw.strip()
675
+ else None
676
+ )
677
+ if conversation_id is None and isinstance(active_conversation_id, str) and active_conversation_id.strip():
678
+ conversation_id = sanitize_conversation_id(active_conversation_id.strip())
679
+
680
+ cursor_value = params.get("cursor") or {}
681
+ if not isinstance(cursor_value, dict):
682
+ raise HTTPException(status_code=400, detail="cursor must be an object")
683
+ cursor_map = coerce_object_map(cursor_value)
684
+ offset = _parse_int(cursor_map.get("offset", 0), detail="cursor.offset must be an integer")
685
+ max_entries = min(max(_parse_int(params.get("max_entries", 500), detail="max_entries must be an integer"), 1), 500)
686
+ max_bytes = max(_parse_int(params.get("max_bytes", 524288), detail="max_bytes must be an integer"), 1)
687
+
688
+ format_name = str(params.get("format", "jsonl") or "jsonl").strip().lower()
689
+ if format_name != "jsonl":
690
+ raise HTTPException(status_code=400, detail="Only format=jsonl is supported")
691
+
692
+ return ConversationReplayGetChunkParams(
693
+ conversation_id=conversation_id,
694
+ cursor=ReplayChunkCursor(offset=offset),
695
+ max_entries=max_entries,
696
+ max_bytes=max_bytes,
697
+ include_internal=(params.get("include_internal") is True),
698
+ )
699
+
700
+
701
+ def normalize_conversation_send_result(
702
+ result: object,
703
+ *,
704
+ conversation_id: str,
705
+ ) -> ConversationSendResult:
706
+ payload = (
707
+ coerce_object_map(result)
708
+ if isinstance(result, dict)
709
+ else {"ok": False, "error": "Invalid send result"}
710
+ )
711
+ return ConversationSendResult(
712
+ conversation_id=conversation_id,
713
+ accepted=(payload.get("ok") is True),
714
+ payload=payload,
715
+ )
716
+
717
+
718
+ def normalize_conversation_control_result(
719
+ result: object,
720
+ *,
721
+ invalid_error: str,
722
+ ) -> ConversationControlResult:
723
+ payload = (
724
+ coerce_object_map(result)
725
+ if isinstance(result, dict)
726
+ else {"ok": False, "error": invalid_error}
727
+ )
728
+ error_value = payload.get("error")
729
+ return ConversationControlResult(
730
+ ok=(payload.get("ok") is True),
731
+ error=error_value if isinstance(error_value, str) else None,
732
+ payload=payload,
733
+ )
734
+
735
+
736
+ def normalize_replay_range_data(range_data: object) -> ReplayRangeData:
737
+ range_map = coerce_object_map(range_data)
738
+ conversation_id_value = range_map.get("conversation_id")
739
+ return ReplayRangeData(
740
+ conversation_id=conversation_id_value if isinstance(conversation_id_value, str) else None,
741
+ items=coerce_object_list(range_map.get("items")),
742
+ offset=_parse_int(range_map.get("offset"), detail="Invalid replay offset", default=0),
743
+ total_count=_parse_int(range_map.get("total"), detail="Invalid replay total", default=0),
744
+ )
745
+
746
+
747
+ def encode_replay_items_jsonl(
748
+ items: ObjectList,
749
+ *,
750
+ max_bytes: int,
751
+ ) -> ReplayJsonlChunk:
752
+ jsonl_parts: list[str] = []
753
+ kept_item_count = 0
754
+ encoded_bytes = 0
755
+ for item in items:
756
+ line = json.dumps(item, ensure_ascii=False, separators=(",", ":")) + "\n"
757
+ line_size = len(line.encode("utf-8"))
758
+ if kept_item_count > 0 and encoded_bytes + line_size > max_bytes:
759
+ break
760
+ jsonl_parts.append(line)
761
+ kept_item_count += 1
762
+ encoded_bytes += line_size
763
+
764
+ if not jsonl_parts and items:
765
+ jsonl_parts.append(json.dumps(items[0], ensure_ascii=False, separators=(",", ":")) + "\n")
766
+ kept_item_count = 1
767
+
768
+ return ReplayJsonlChunk(
769
+ jsonl="".join(jsonl_parts),
770
+ item_count=kept_item_count,
771
+ )
772
+
773
+
774
+ def build_empty_replay_chunk_result(
775
+ *,
776
+ conversation_id: str | None,
777
+ ) -> ReplayChunkResult:
778
+ return ReplayChunkResult(
779
+ conversation_id=conversation_id,
780
+ replay_id=_next_replay_id(),
781
+ frame=ReplayChunkFrame(
782
+ format_name="jsonl",
783
+ offset=0,
784
+ item_count=0,
785
+ total_count=0,
786
+ chunk_index=0,
787
+ complete=True,
788
+ next_cursor=None,
789
+ jsonl="",
790
+ ),
791
+ )
792
+
793
+
794
+ def build_replay_chunk_result(
795
+ *,
796
+ params: ConversationReplayGetChunkParams,
797
+ range_data: ReplayRangeData,
798
+ jsonl_chunk: ReplayJsonlChunk,
799
+ ) -> ReplayChunkResult:
800
+ next_offset = range_data.offset + jsonl_chunk.item_count
801
+ complete = next_offset >= range_data.total_count
802
+ return ReplayChunkResult(
803
+ conversation_id=range_data.conversation_id,
804
+ replay_id=_next_replay_id(),
805
+ frame=ReplayChunkFrame(
806
+ format_name=params.format_name,
807
+ offset=range_data.offset,
808
+ item_count=jsonl_chunk.item_count,
809
+ total_count=range_data.total_count,
810
+ chunk_index=range_data.offset // max(params.max_entries, 1),
811
+ complete=complete,
812
+ next_cursor=None if complete else ReplayChunkCursor(offset=next_offset),
813
+ jsonl=jsonl_chunk.jsonl,
814
+ ),
815
+ )
816
+
817
+
818
+ def _next_replay_id() -> str:
819
+ return f"replay_{uuid.uuid4().hex[:12]}"
820
+
821
+
822
+ def _require_sanitized_conversation_id(
823
+ value: object,
824
+ *,
825
+ sanitize_conversation_id: SanitizeConversationId,
826
+ detail: str,
827
+ ) -> str:
828
+ if not isinstance(value, str) or not value.strip():
829
+ raise HTTPException(status_code=400, detail=detail)
830
+ return sanitize_conversation_id(value.strip())
831
+
832
+
833
+ def _parse_int(
834
+ value: object,
835
+ *,
836
+ detail: str,
837
+ default: int | None = None,
838
+ ) -> int:
839
+ candidate = default if value is None else value
840
+ if isinstance(candidate, bool):
841
+ raise HTTPException(status_code=400, detail=detail)
842
+ if isinstance(candidate, int):
843
+ return candidate
844
+ if isinstance(candidate, str):
845
+ try:
846
+ return int(candidate)
847
+ except ValueError as exc:
848
+ raise HTTPException(status_code=400, detail=detail) from exc
849
+ raise HTTPException(status_code=400, detail=detail)