agentengine-sdk-python 0.7.0__py3-none-any.whl → 0.8.0__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 (371) hide show
  1. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/METADATA +36 -20
  2. agentengine_sdk_python-0.8.0.dist-info/RECORD +578 -0
  3. ksadk/a2a/__init__.py +178 -11
  4. ksadk/a2a/bootstrap.py +334 -0
  5. ksadk/a2a/card.py +120 -0
  6. ksadk/a2a/context_store.py +203 -0
  7. ksadk/a2a/control_plane.py +818 -0
  8. ksadk/a2a/credential.py +99 -0
  9. ksadk/a2a/event_adapter.py +149 -0
  10. ksadk/a2a/executor.py +406 -89
  11. ksadk/a2a/external_transport.py +516 -0
  12. ksadk/a2a/identity.py +304 -0
  13. ksadk/a2a/ids.py +22 -0
  14. ksadk/a2a/langgraph.py +285 -0
  15. ksadk/a2a/resume_store.py +201 -0
  16. ksadk/a2a/routes.py +105 -0
  17. ksadk/a2a/server.py +107 -60
  18. ksadk/a2a/space_client.py +1101 -0
  19. ksadk/a2a/task_adapter.py +423 -0
  20. ksadk/a2a/task_event_dispatcher.py +167 -0
  21. ksadk/a2a/task_event_outbox.py +326 -0
  22. ksadk/a2a/task_store.py +206 -0
  23. ksadk/a2ui/__init__.py +27 -0
  24. ksadk/a2ui/core.py +186 -0
  25. ksadk/a2ui/models.py +166 -0
  26. ksadk/a2ui/renderer.py +198 -0
  27. ksadk/agui/__init__.py +9 -0
  28. ksadk/agui/a2ui_projection.py +92 -0
  29. ksadk/agui/agent.py +857 -0
  30. ksadk/agui/config.py +86 -0
  31. ksadk/agui/routes.py +55 -0
  32. ksadk/api/client.py +233 -191
  33. ksadk/builders/__init__.py +2 -0
  34. ksadk/builders/base.py +16 -14
  35. ksadk/builders/code_builder.py +318 -236
  36. ksadk/builders/container_builder.py +187 -167
  37. ksadk/builders/framework_requirements.py +29 -11
  38. ksadk/builders/ks3_uploader.py +58 -27
  39. ksadk/builders/managed_runtime_builder.py +145 -0
  40. ksadk/builders/mcp_builder.py +91 -92
  41. ksadk/builders/requirements_utils.py +1 -5
  42. ksadk/cli/__init__.py +89 -171
  43. ksadk/cli/cmd_a2a.py +254 -70
  44. ksadk/cli/cmd_agent.py +19 -5
  45. ksadk/cli/cmd_build.py +158 -29
  46. ksadk/cli/cmd_completion.py +47 -35
  47. ksadk/cli/cmd_config.py +267 -198
  48. ksadk/cli/cmd_create.py +500 -720
  49. ksadk/cli/cmd_dashboard.py +120 -45
  50. ksadk/cli/cmd_deploy.py +117 -58
  51. ksadk/cli/cmd_destroy.py +44 -14
  52. ksadk/cli/cmd_files.py +63 -24
  53. ksadk/cli/cmd_hermes.py +168 -89
  54. ksadk/cli/cmd_invoke.py +184 -122
  55. ksadk/cli/cmd_launch.py +64 -28
  56. ksadk/cli/cmd_mcp.py +98 -64
  57. ksadk/cli/cmd_model.py +14 -5
  58. ksadk/cli/cmd_openclaw.py +483 -227
  59. ksadk/cli/cmd_replay.py +71 -0
  60. ksadk/cli/cmd_run.py +33 -10
  61. ksadk/cli/cmd_status.py +80 -31
  62. ksadk/cli/cmd_version.py +20 -13
  63. ksadk/cli/cmd_web.py +89 -13
  64. ksadk/cli/deploy_utils.py +20 -20
  65. ksadk/cli/dry_run.py +5 -3
  66. ksadk/cli/error_utils.py +37 -12
  67. ksadk/cli/global_options.py +1 -2
  68. ksadk/cli/local_runtime.py +1 -2
  69. ksadk/cli/model_catalog.py +6 -2
  70. ksadk/cli/network_options.py +34 -14
  71. ksadk/cli/resource_common.py +6 -2
  72. ksadk/cli/storage.py +0 -1
  73. ksadk/cli/ui.py +5 -3
  74. ksadk/cli/workflow_common.py +51 -17
  75. ksadk/codex/__init__.py +12 -0
  76. ksadk/codex/client.py +408 -0
  77. ksadk/codex/phase.py +143 -0
  78. ksadk/codex/runtime.py +449 -0
  79. ksadk/common/aicp_env.py +0 -1
  80. ksadk/common/auth.py +2 -2
  81. ksadk/common/constants.py +1 -3
  82. ksadk/common/llm_utils.py +17 -13
  83. ksadk/compat/__init__.py +5 -0
  84. ksadk/compat/adk_compat.py +200 -0
  85. ksadk/compat/copilotkit_a2ui.py +330 -0
  86. ksadk/configs/__init__.py +12 -12
  87. ksadk/configs/env_registry.py +500 -100
  88. ksadk/configs/global_config.py +2 -1
  89. ksadk/configs/settings.py +151 -124
  90. ksadk/conversations/__init__.py +7 -5
  91. ksadk/conversations/attachment_storage.py +16 -11
  92. ksadk/conversations/attachments.py +44 -36
  93. ksadk/conversations/compaction_pipeline.py +12 -12
  94. ksadk/conversations/compaction_prompt.py +9 -5
  95. ksadk/conversations/context.py +221 -6
  96. ksadk/conversations/message_projection.py +438 -51
  97. ksadk/conversations/model_context.py +12 -6
  98. ksadk/conversations/model_options.py +0 -1
  99. ksadk/conversations/normalize.py +20 -16
  100. ksadk/conversations/reasoning_markup.py +1 -1
  101. ksadk/conversations/runtime.py +339 -5255
  102. ksadk/conversations/runtime_compaction.py +333 -0
  103. ksadk/conversations/runtime_constants.py +27 -0
  104. ksadk/conversations/runtime_governance.py +185 -0
  105. ksadk/conversations/runtime_input.py +652 -0
  106. ksadk/conversations/runtime_invocation.py +395 -0
  107. ksadk/conversations/runtime_metadata.py +475 -0
  108. ksadk/conversations/runtime_observability.py +355 -0
  109. ksadk/conversations/runtime_payloads.py +272 -0
  110. ksadk/conversations/runtime_persistence.py +440 -0
  111. ksadk/conversations/runtime_preparation.py +418 -0
  112. ksadk/conversations/runtime_resume.py +816 -0
  113. ksadk/conversations/runtime_stream_events.py +902 -0
  114. ksadk/conversations/runtime_streaming.py +547 -0
  115. ksadk/conversations/semantic_summary.py +17 -5
  116. ksadk/conversations/session_title.py +6 -3
  117. ksadk/deployment/__init__.py +15 -16
  118. ksadk/deployment/agent_access.py +12 -23
  119. ksadk/deployment/base.py +51 -38
  120. ksadk/deployment/manager.py +63 -65
  121. ksadk/deployment/providers/serverless.py +309 -202
  122. ksadk/deployment/registry.py +38 -32
  123. ksadk/deployment/state.py +11 -11
  124. ksadk/deployment/ui_config.py +1 -1
  125. ksadk/detection/__init__.py +1 -1
  126. ksadk/detection/detector.py +146 -83
  127. ksadk/detection/mcp_detector.py +9 -6
  128. ksadk/events/__init__.py +25 -0
  129. ksadk/events/parser.py +191 -0
  130. ksadk/events/replay.py +37 -0
  131. ksadk/events/runtime_event.py +264 -0
  132. ksadk/events/store.py +317 -0
  133. ksadk/harness/__init__.py +34 -0
  134. ksadk/harness/app.py +278 -0
  135. ksadk/harness/config.py +181 -0
  136. ksadk/harness/reasoner.py +105 -0
  137. ksadk/harness/runner.py +211 -0
  138. ksadk/harness/sandbox.py +141 -0
  139. ksadk/harness/tools.py +222 -0
  140. ksadk/hermes_terminal.py +23 -12
  141. ksadk/identity/resolver.py +15 -9
  142. ksadk/ids.py +32 -0
  143. ksadk/knowledge_base/adk_tool.py +1 -1
  144. ksadk/knowledge_base/client.py +23 -29
  145. ksadk/managed_runtime.py +188 -0
  146. ksadk/markdown.py +5 -2
  147. ksadk/mcp_runtime/__init__.py +9 -6
  148. ksadk/memory/__init__.py +1 -1
  149. ksadk/memory/adk/__init__.py +2 -0
  150. ksadk/memory/adk/backends/base_ltm_backend.py +2 -6
  151. ksadk/memory/adk/backends/http_ltm_backend.py +13 -21
  152. ksadk/memory/adk/backends/inmemory_ltm_backend.py +7 -13
  153. ksadk/memory/adk/backends/sdk_ltm_backend.py +26 -33
  154. ksadk/memory/adk/long_term_memory.py +31 -35
  155. ksadk/memory/adk/resilient_session_service.py +4 -5
  156. ksadk/memory/adk/short_term_memory.py +12 -21
  157. ksadk/memory/adk_tool.py +3 -2
  158. ksadk/memory/backends/base.py +19 -19
  159. ksadk/memory/backends/memory.py +24 -25
  160. ksadk/memory/backends/redis.py +31 -30
  161. ksadk/memory/langchain_tool.py +2 -1
  162. ksadk/memory/manager.py +52 -47
  163. ksadk/memory/service.py +10 -8
  164. ksadk/model_policy.py +15 -7
  165. ksadk/model_proxy/__init__.py +51 -0
  166. ksadk/model_proxy/bootstrap.py +80 -0
  167. ksadk/model_proxy/cache.py +165 -0
  168. ksadk/model_proxy/config.py +45 -0
  169. ksadk/model_proxy/demo_webui.py +115 -0
  170. ksadk/model_proxy/detect.py +145 -0
  171. ksadk/model_proxy/gate.py +68 -0
  172. ksadk/model_proxy/namespace.py +173 -0
  173. ksadk/model_proxy/protocol_proxy.py +22 -0
  174. ksadk/model_proxy/server.py +300 -0
  175. ksadk/model_proxy/transform.py +691 -0
  176. ksadk/openclaw_gateway.py +29 -14
  177. ksadk/runners/adk_runner.py +345 -120
  178. ksadk/runners/base_runner.py +46 -29
  179. ksadk/runners/codex_runner.py +132 -0
  180. ksadk/runners/deepagents_runner.py +0 -1
  181. ksadk/runners/factory.py +17 -5
  182. ksadk/runners/langchain_runner.py +29 -742
  183. ksadk/runners/langgraph_runner.py +352 -90
  184. ksadk/runners/patch_langchain.py +16 -16
  185. ksadk/runners/remote_runner.py +134 -33
  186. ksadk/runners/usage_accumulator.py +1 -0
  187. ksadk/runners/utils/langfuse.py +3 -3
  188. ksadk/runners/utils/loader.py +7 -7
  189. ksadk/runtime/__init__.py +41 -0
  190. ksadk/runtime/adapter.py +345 -0
  191. ksadk/runtime/framework_adapters.py +124 -0
  192. ksadk/runtime/preprocessing.py +118 -0
  193. ksadk/runtime/runner_adapter.py +873 -0
  194. ksadk/runtime_context.py +18 -3
  195. ksadk/runtime_state.py +0 -1
  196. ksadk/sandbox/__init__.py +4 -1
  197. ksadk/sandbox/backends/__init__.py +4 -1
  198. ksadk/sandbox/backends/e2b.py +13 -5
  199. ksadk/sandbox/backends/local_process.py +11 -3
  200. ksadk/sandbox/base.py +10 -15
  201. ksadk/sandbox/factory.py +14 -4
  202. ksadk/sandbox/registry.py +57 -6
  203. ksadk/server/api_models.py +15 -5
  204. ksadk/server/app.py +455 -3885
  205. ksadk/server/composition.py +21 -0
  206. ksadk/server/factory.py +624 -0
  207. ksadk/server/routes/__init__.py +1 -0
  208. ksadk/server/routes/checkpoint_resolution.py +116 -0
  209. ksadk/server/routes/common.py +713 -0
  210. ksadk/server/routes/control.py +327 -0
  211. ksadk/server/routes/dependencies.py +98 -0
  212. ksadk/server/routes/misc.py +448 -0
  213. ksadk/server/routes/models.py +443 -0
  214. ksadk/server/routes/openai_compat.py +207 -0
  215. ksadk/server/routes/projection.py +795 -0
  216. ksadk/server/routes/routers.py +52 -0
  217. ksadk/server/routes/run.py +726 -0
  218. ksadk/server/routes/sessions.py +649 -0
  219. ksadk/server/routes/streaming.py +223 -0
  220. ksadk/server/routes/workspace.py +381 -0
  221. ksadk/server/static/assets/{ArtifactsPanel-D-SSn2ee.js → ArtifactsPanel-Cjnqt-8W.js} +1 -1
  222. ksadk/server/static/assets/CodeBlock-BqyqZfuB.js +9 -0
  223. ksadk/server/static/assets/MathMessageMarkdown-BpH7jiFM.js +8 -0
  224. ksadk/server/static/assets/MermaidBlock-DWuxETaJ.js +303 -0
  225. ksadk/server/static/assets/NativeTerminalPanel-MDTopWMO.js +1 -0
  226. ksadk/server/static/assets/{arc-BA13sHc9.js → arc-CFKwSk_B.js} +1 -1
  227. ksadk/server/static/assets/architecture-7EHR7CIX-CynqOWjZ.js +1 -0
  228. ksadk/server/static/assets/{architectureDiagram-3BPJPVTR-DzJzgVoL.js → architectureDiagram-3BPJPVTR-Dej8jX8c.js} +1 -1
  229. ksadk/server/static/assets/{blockDiagram-GPEHLZMM-UTdVcZhn.js → blockDiagram-GPEHLZMM-1F64jqvy.js} +1 -1
  230. ksadk/server/static/assets/{c4Diagram-AAUBKEIU-Dix0GD4m.js → c4Diagram-AAUBKEIU-9q7AUAKb.js} +1 -1
  231. ksadk/server/static/assets/channel-iUGv2N24.js +1 -0
  232. ksadk/server/static/assets/{chunk-2J33WTMH-C2JxuZ42.js → chunk-2J33WTMH-6qgwJm7Q.js} +1 -1
  233. ksadk/server/static/assets/{chunk-4BX2VUAB-Be57QPgD.js → chunk-4BX2VUAB-CsWIQRhc.js} +1 -1
  234. ksadk/server/static/assets/{chunk-55IACEB6-WjSuy4nv.js → chunk-55IACEB6-BK8paA20.js} +1 -1
  235. ksadk/server/static/assets/{chunk-727SXJPM-BOovP-b5.js → chunk-727SXJPM-B-JEhy7h.js} +1 -1
  236. ksadk/server/static/assets/{chunk-AQP2D5EJ-DOmZNmWj.js → chunk-AQP2D5EJ-DYBvMHAa.js} +1 -1
  237. ksadk/server/static/assets/{chunk-FMBD7UC4-DzwlodkU.js → chunk-FMBD7UC4-BGhvzDYt.js} +1 -1
  238. ksadk/server/static/assets/{chunk-ND2GUHAM-DMqAikEU.js → chunk-ND2GUHAM-D98vPd-E.js} +1 -1
  239. ksadk/server/static/assets/chunk-QZHKN3VN-YQIEXeF6.js +1 -0
  240. ksadk/server/static/assets/classDiagram-4FO5ZUOK-CDAUuoXK.js +1 -0
  241. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-CDAUuoXK.js +1 -0
  242. ksadk/server/static/assets/{cose-bilkent-S5V4N54A-Zhy2GBnP.js → cose-bilkent-S5V4N54A-CX3yotHq.js} +1 -1
  243. ksadk/server/static/assets/{dagre-BM42HDAG-CbKrtIUz.js → dagre-BM42HDAG-C7TguAWo.js} +1 -1
  244. ksadk/server/static/assets/{diagram-2AECGRRQ-BRApgQRK.js → diagram-2AECGRRQ-Dd17_kws.js} +1 -1
  245. ksadk/server/static/assets/{diagram-5GNKFQAL-D0xbnDiK.js → diagram-5GNKFQAL-DVIClIil.js} +1 -1
  246. ksadk/server/static/assets/{diagram-KO2AKTUF-CVugVI9L.js → diagram-KO2AKTUF-Df_3gYQa.js} +1 -1
  247. ksadk/server/static/assets/{diagram-LMA3HP47-DozUGgee.js → diagram-LMA3HP47-BhLk6ZtA.js} +1 -1
  248. ksadk/server/static/assets/{diagram-OG6HWLK6-ZOYsKlru.js → diagram-OG6HWLK6-D4GNGvuu.js} +1 -1
  249. ksadk/server/static/assets/{dist-DmFaq_Iy.js → dist-6CVKuTAe.js} +1 -1
  250. ksadk/server/static/assets/{dist-8EEyGgDx.js → dist-BUHRxvDG.js} +1 -1
  251. ksadk/server/static/assets/{dist-B0NP_Z0z.js → dist-BZ9n9AjO.js} +1 -1
  252. ksadk/server/static/assets/{dist-DMezvBGJ.js → dist-BibcG1V9.js} +1 -1
  253. ksadk/server/static/assets/{dist-C3HIIaN5.js → dist-Bs-jQcRX.js} +1 -1
  254. ksadk/server/static/assets/{dist-BrCcQ47T.js → dist-C2xjE7cO.js} +1 -1
  255. ksadk/server/static/assets/{dist-Dp0k7bWI.js → dist-C7-3jInC.js} +1 -1
  256. ksadk/server/static/assets/{dist-D-xOD048.js → dist-C99THtIA.js} +1 -1
  257. ksadk/server/static/assets/{dist-CkEKJFtk.js → dist-CGcp2ex8.js} +1 -1
  258. ksadk/server/static/assets/{dist-DyOkZmze.js → dist-CVQRZjPu.js} +1 -1
  259. ksadk/server/static/assets/{dist-BssVmjIo.js → dist-Cf23NY4L.js} +1 -1
  260. ksadk/server/static/assets/{dist-BIJVDMuB.js → dist-CgF3fj15.js} +1 -1
  261. ksadk/server/static/assets/{dist-2hInLS_J.js → dist-Cwt7koer.js} +1 -1
  262. ksadk/server/static/assets/{dist-DyWsb78Q.js → dist-D82mtD2s.js} +1 -1
  263. ksadk/server/static/assets/{dist-D-JMdWPQ.js → dist-DF06ORQo.js} +1 -1
  264. ksadk/server/static/assets/{dist-BmlMPsCc.js → dist-DHn6nBzr.js} +1 -1
  265. ksadk/server/static/assets/{dist-gX6GOLKq.js → dist-Djgl2wVZ.js} +1 -1
  266. ksadk/server/static/assets/{dist-DpKtt4Bo.js → dist-DlzCRy0S.js} +1 -1
  267. ksadk/server/static/assets/{dist-8_SfK1IP.js → dist-DqNnNFrw.js} +1 -1
  268. ksadk/server/static/assets/{dist-D57wnGWd.js → dist-DyvYLMW9.js} +1 -1
  269. ksadk/server/static/assets/{dist-CPBtqRTW.js → dist-eoW6tZXA.js} +1 -1
  270. ksadk/server/static/assets/{dist-DL_NKc_z.js → dist-ivOya1LD.js} +1 -1
  271. ksadk/server/static/assets/{erDiagram-TEJ5UH35-Bk-9bgE5.js → erDiagram-TEJ5UH35-CnfRcY2M.js} +1 -1
  272. ksadk/server/static/assets/eventmodeling-FCH6USID-ChYO-ijM.js +1 -0
  273. ksadk/server/static/assets/{flowDiagram-I6XJVG4X-Dn9kZVuZ.js → flowDiagram-I6XJVG4X-DGaJjGNJ.js} +1 -1
  274. ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BGxXlJqm.js → ganttDiagram-6RSMTGT7-DpCoB2g9.js} +1 -1
  275. ksadk/server/static/assets/gitGraph-WXDBUCRP-BN_0Fk-j.js +1 -0
  276. ksadk/server/static/assets/{gitGraphDiagram-PVQCEYII-C7Q0X-wi.js → gitGraphDiagram-PVQCEYII-9IKVFBVT.js} +1 -1
  277. ksadk/server/static/assets/index-QSwCHuB8.js +177 -0
  278. ksadk/server/static/assets/index-jOPmTOZH.css +2 -0
  279. ksadk/server/static/assets/info-J43DQDTF-Cp6o3vId.js +1 -0
  280. ksadk/server/static/assets/{infoDiagram-5YYISTIA-BO_CSqq5.js → infoDiagram-5YYISTIA-CYIIsb8y.js} +1 -1
  281. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-eYLPLd46.js → ishikawaDiagram-YF4QCWOH-DLOHGvWw.js} +1 -1
  282. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-De9geSb0.js → journeyDiagram-JHISSGLW-BIzKeejT.js} +1 -1
  283. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-Cs80kQto.js → kanban-definition-UN3LZRKU-BWHoFSw2.js} +1 -1
  284. ksadk/server/static/assets/{katex-DSrCFD8n.js → katex-DATssjp1.js} +1 -1
  285. ksadk/server/static/assets/{linear-CN9sbKTF.js → linear-CvMBw9cA.js} +1 -1
  286. ksadk/server/static/assets/{mermaid-parser.core-DlgSBU8l.js → mermaid-parser.core-mGAcEwcq.js} +2 -2
  287. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-9afz7Oug.js → mindmap-definition-RKZ34NQL-CSAQxEAe.js} +1 -1
  288. ksadk/server/static/assets/packet-YPE3B663-wQzct3cT.js +1 -0
  289. ksadk/server/static/assets/pie-LRSECV5Y-BgTQiv-9.js +1 -0
  290. ksadk/server/static/assets/{pieDiagram-4H26LBE5-DPv_VstN.js → pieDiagram-4H26LBE5-By-ra07M.js} +1 -1
  291. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-o77wZ1XK.js → quadrantDiagram-W4KKPZXB-CxIR4JXI.js} +1 -1
  292. ksadk/server/static/assets/radar-GUYGQ44K-BIq5fXsn.js +1 -0
  293. ksadk/server/static/assets/{requirementDiagram-4Y6WPE33-BYVw81VC.js → requirementDiagram-4Y6WPE33-DpW90iu3.js} +1 -1
  294. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-B7nZFMKT.js → sankeyDiagram-5OEKKPKP-DpGa6EAV.js} +1 -1
  295. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-BGRRrFw5.js → sequenceDiagram-3UESZ5HK-NTKNGGe5.js} +1 -1
  296. ksadk/server/static/assets/{stateDiagram-AJRCARHV-BUfHCoPs.js → stateDiagram-AJRCARHV-DGpd9yFG.js} +1 -1
  297. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-B3o579IQ.js +1 -0
  298. ksadk/server/static/assets/{timeline-definition-PNZ67QCA-FbCTGrf9.js → timeline-definition-PNZ67QCA-jqoYs3jQ.js} +1 -1
  299. ksadk/server/static/assets/treeView-BLDUP644-Dqi-E0EM.js +1 -0
  300. ksadk/server/static/assets/treemap-LRROVOQU-ClQP9CJ9.js +1 -0
  301. ksadk/server/static/assets/{vennDiagram-CIIHVFJN-DWgwtPsJ.js → vennDiagram-CIIHVFJN-l3Z7-wbP.js} +1 -1
  302. ksadk/server/static/assets/wardley-L42UT6IY-8XLvnNIs.js +1 -0
  303. ksadk/server/static/assets/{wardleyDiagram-YWT4CUSO-2x8G-tTM.js → wardleyDiagram-YWT4CUSO-BR54GPmr.js} +1 -1
  304. ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-Drma3AYl.js → xychartDiagram-2RQKCTM6-SkiH8GsK.js} +1 -1
  305. ksadk/server/static/index.html +2 -2
  306. ksadk/server/terminal_sessions.py +111 -58
  307. ksadk/sessions/__init__.py +69 -19
  308. ksadk/sessions/base.py +31 -11
  309. ksadk/sessions/continuity.py +2 -3
  310. ksadk/sessions/in_memory.py +46 -2
  311. ksadk/sessions/local_service.py +148 -17
  312. ksadk/sessions/postgres_service.py +143 -25
  313. ksadk/sessions/resilient.py +36 -8
  314. ksadk/skills/models.py +17 -5
  315. ksadk/skills/package_store.py +5 -2
  316. ksadk/skills/runtime/agent.py +20 -5
  317. ksadk/skills/runtime/artifacts.py +1 -5
  318. ksadk/skills/runtime/backends/e2b.py +10 -4
  319. ksadk/skills/runtime/backends/local.py +15 -3
  320. ksadk/skills/runtime/base.py +1 -2
  321. ksadk/skills/runtime/executor.py +10 -3
  322. ksadk/skills/runtime/factory.py +4 -2
  323. ksadk/skills/runtime/loader.py +1 -2
  324. ksadk/skills/runtime/registry.py +15 -10
  325. ksadk/skills/runtime/request.py +3 -1
  326. ksadk/skills/service_client.py +38 -17
  327. ksadk/skills/tool_defs.py +15 -10
  328. ksadk/terminal_exec_policy.py +3 -4
  329. ksadk/tools/gateway.py +92 -15
  330. ksadk/tools/result_budget.py +12 -4
  331. ksadk/toolsets/__init__.py +134 -36
  332. ksadk/toolsets/platform.py +21 -5
  333. ksadk/toolsets/sandbox.py +82 -16
  334. ksadk/toolsets/skills.py +200 -39
  335. ksadk/toolsets/web.py +123 -26
  336. ksadk/toolsets/workspace.py +212 -101
  337. ksadk/tracing/exporters/inmemory_exporter.py +62 -51
  338. ksadk/tracing/exporters/langfuse_exporter.py +91 -76
  339. ksadk/tracing/setup.py +69 -53
  340. ksadk/tracing/span_utils.py +5 -2
  341. ksadk/tui/loop.py +78 -30
  342. ksadk/tui/stream_render.py +1 -0
  343. ksadk/ui_config.py +5 -3
  344. ksadk/version.py +1 -1
  345. ksadk_runtime_common/schemas/runtime_event_v1.json +95 -0
  346. agentengine_sdk_python-0.7.0.dist-info/RECORD +0 -482
  347. ksadk/server/static/assets/CodeBlock-P16VOLYi.js +0 -9
  348. ksadk/server/static/assets/MathMessageMarkdown-CdGiQWHR.js +0 -8
  349. ksadk/server/static/assets/MermaidBlock-IOcal8cm.js +0 -303
  350. ksadk/server/static/assets/NativeTerminalPanel-DGSDWsei.js +0 -1
  351. ksadk/server/static/assets/architecture-7EHR7CIX-BvUlsA6l.js +0 -1
  352. ksadk/server/static/assets/channel-CTuydARX.js +0 -1
  353. ksadk/server/static/assets/chunk-QZHKN3VN-BnCydivS.js +0 -1
  354. ksadk/server/static/assets/classDiagram-4FO5ZUOK-BNHg45Be.js +0 -1
  355. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-BNHg45Be.js +0 -1
  356. ksadk/server/static/assets/eventmodeling-FCH6USID-DsAgWnlf.js +0 -1
  357. ksadk/server/static/assets/gitGraph-WXDBUCRP-BIao9Yjg.js +0 -1
  358. ksadk/server/static/assets/index-C00Lwg3L.js +0 -165
  359. ksadk/server/static/assets/index-DAURbCsP.css +0 -1
  360. ksadk/server/static/assets/info-J43DQDTF-CyipqGxU.js +0 -1
  361. ksadk/server/static/assets/packet-YPE3B663-CGkyRxlg.js +0 -1
  362. ksadk/server/static/assets/pie-LRSECV5Y-M0uWAf4n.js +0 -1
  363. ksadk/server/static/assets/radar-GUYGQ44K-Bdbm24-x.js +0 -1
  364. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-8xBN_gLM.js +0 -1
  365. ksadk/server/static/assets/treeView-BLDUP644-npMo0ak1.js +0 -1
  366. ksadk/server/static/assets/treemap-LRROVOQU-CHdzrrdo.js +0 -1
  367. ksadk/server/static/assets/wardley-L42UT6IY-BEQz7wyy.js +0 -1
  368. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/WHEEL +0 -0
  369. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/entry_points.txt +0 -0
  370. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/licenses/LICENSE +0 -0
  371. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,816 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import json
5
+ import time
6
+ import uuid
7
+ from typing import Any, Callable, Mapping, Sequence
8
+
9
+ from ksadk.conversations.context import (
10
+ canonical_event_type,
11
+ )
12
+ from ksadk.conversations.model_options import normalize_model_options
13
+ from ksadk.conversations.run_kinds import (
14
+ RUN_MODE_UNKNOWN,
15
+ validate_run_mode,
16
+ )
17
+ from ksadk.conversations.runtime_persistence import append_conversation_event
18
+ from ksadk.sessions import SessionEvent
19
+ from ksadk.tools.gateway import (
20
+ build_tool_receipt_idempotency_key,
21
+ )
22
+
23
+
24
+ def _has_pending_approval(events: Sequence[SessionEvent]) -> bool:
25
+ pending = 0
26
+ for event in events:
27
+ event_type = canonical_event_type(
28
+ event.event_type,
29
+ author=event.author,
30
+ role=str((event.content or {}).get("role") or ""),
31
+ )
32
+ if event_type == "approval_request":
33
+ pending += 1
34
+ elif event_type == "approval_response" and pending > 0:
35
+ pending -= 1
36
+ return pending > 0
37
+
38
+
39
+ def _approval_request_id_from_event(event: SessionEvent) -> str:
40
+ metadata = event.metadata or {}
41
+ interrupt_info = metadata.get("interrupt_info")
42
+ if isinstance(interrupt_info, Mapping):
43
+ value = interrupt_info.get("approval_request_id") or interrupt_info.get("id")
44
+ if value:
45
+ return str(value)
46
+ return str(event.id or "")
47
+
48
+
49
+ def _pending_approval_events(events: Sequence[SessionEvent]) -> list[SessionEvent]:
50
+ pending: list[SessionEvent] = []
51
+ for event in events:
52
+ event_type = canonical_event_type(
53
+ event.event_type,
54
+ author=event.author,
55
+ role=str((event.content or {}).get("role") or ""),
56
+ )
57
+ if event_type == "approval_request":
58
+ pending.append(event)
59
+ continue
60
+ if event_type != "approval_response" or not pending:
61
+ continue
62
+ resume_input = (event.metadata or {}).get("resume_input")
63
+ response_id = ""
64
+ if isinstance(resume_input, Mapping):
65
+ response_id = str(
66
+ resume_input.get("approval_request_id")
67
+ or resume_input.get("interrupt_id")
68
+ or resume_input.get("id")
69
+ or ""
70
+ )
71
+ if response_id:
72
+ pending = [
73
+ item for item in pending if _approval_request_id_from_event(item) != response_id
74
+ ]
75
+ else:
76
+ pending.pop()
77
+ return pending
78
+
79
+
80
+ def _approval_request_events(events: Sequence[SessionEvent]) -> list[SessionEvent]:
81
+ return [
82
+ event
83
+ for event in events
84
+ if canonical_event_type(
85
+ event.event_type,
86
+ author=event.author,
87
+ role=str((event.content or {}).get("role") or ""),
88
+ )
89
+ == "approval_request"
90
+ ]
91
+
92
+
93
+ def _approval_resume_run_mode(
94
+ events: Sequence[SessionEvent],
95
+ resume_input: Mapping[str, Any],
96
+ *,
97
+ fallback: str,
98
+ ) -> str:
99
+ target_ids = {
100
+ str(value)
101
+ for value in (
102
+ resume_input.get("approval_request_id"),
103
+ resume_input.get("interrupt_id"),
104
+ resume_input.get("id"),
105
+ )
106
+ if value
107
+ }
108
+ approval_events = _pending_approval_events(events) or _approval_request_events(events)
109
+ for approval_event in reversed(approval_events):
110
+ approval_id = _approval_request_id_from_event(approval_event)
111
+ if target_ids and approval_id not in target_ids:
112
+ continue
113
+ approval_invocation_id = str(approval_event.invocation_id or "")
114
+ if not approval_invocation_id:
115
+ continue
116
+ for event in reversed(events):
117
+ if event.event_type != "run_status" or event.invocation_id != approval_invocation_id:
118
+ continue
119
+ metadata = event.metadata or {}
120
+ state_delta = event.state_delta or {}
121
+ active_run = state_delta.get("active_run") if isinstance(state_delta, Mapping) else None
122
+ state_mode = active_run.get("run_mode") if isinstance(active_run, Mapping) else None
123
+ mode = validate_run_mode(str(metadata.get("run_mode") or state_mode or ""))
124
+ if mode != RUN_MODE_UNKNOWN:
125
+ return str(mode)
126
+ break
127
+ return str(validate_run_mode(fallback))
128
+
129
+
130
+ def _parse_approval_arguments(value: Any) -> dict[str, Any]:
131
+ if isinstance(value, Mapping):
132
+ return dict(value)
133
+ if isinstance(value, str) and value.strip():
134
+ try:
135
+ parsed = json.loads(value)
136
+ except json.JSONDecodeError:
137
+ return {}
138
+ return dict(parsed) if isinstance(parsed, Mapping) else {}
139
+ return {}
140
+
141
+
142
+ def _approval_decision_from_resume(resume_input: Mapping[str, Any]) -> dict[str, Any]:
143
+ if "approve" in resume_input:
144
+ approved = bool(resume_input.get("approve"))
145
+ elif "approved" in resume_input:
146
+ approved = bool(resume_input.get("approved"))
147
+ else:
148
+ approved = bool(
149
+ resume_input.get("value", {}).get("approved")
150
+ if isinstance(resume_input.get("value"), Mapping)
151
+ else True
152
+ )
153
+ decision = {
154
+ "approved": approved,
155
+ "approval_request_id": str(
156
+ resume_input.get("approval_request_id")
157
+ or resume_input.get("interrupt_id")
158
+ or resume_input.get("id")
159
+ or ""
160
+ ),
161
+ }
162
+ if resume_input.get("reason") is not None:
163
+ decision["reason"] = str(resume_input.get("reason") or "")
164
+ return decision
165
+
166
+
167
+ def _consecutive_approval_denials_from_events(events: Sequence[SessionEvent]) -> int:
168
+ denials = 0
169
+ for event in reversed(events):
170
+ event_type = canonical_event_type(
171
+ event.event_type,
172
+ author=event.author,
173
+ role=str((event.content or {}).get("role") or ""),
174
+ )
175
+ if event_type != "approval_response":
176
+ continue
177
+ resume_input = (event.metadata or {}).get("resume_input")
178
+ if not isinstance(resume_input, Mapping):
179
+ break
180
+ decision = resume_input.get("approval")
181
+ if not isinstance(decision, Mapping):
182
+ decision = _approval_decision_from_resume(resume_input)
183
+ if bool(decision.get("approved") or decision.get("approve")):
184
+ break
185
+ denials += 1
186
+ return denials
187
+
188
+
189
+ def _normalize_approval_resume_input(
190
+ resume_input: Mapping[str, Any],
191
+ events: Sequence[SessionEvent],
192
+ *,
193
+ include_resolved: bool = False,
194
+ ) -> dict[str, Any]:
195
+ normalized = dict(resume_input)
196
+ if not _is_approval_resume_input(normalized):
197
+ return normalized
198
+
199
+ approval_request_id = str(
200
+ normalized.get("approval_request_id") or normalized.get("interrupt_id") or ""
201
+ )
202
+ pending_events = (
203
+ _approval_request_events(events) if include_resolved else _pending_approval_events(events)
204
+ )
205
+ matched_event = None
206
+ for event in reversed(pending_events):
207
+ if not approval_request_id or _approval_request_id_from_event(event) == approval_request_id:
208
+ matched_event = event
209
+ break
210
+ if matched_event is None:
211
+ return normalized
212
+
213
+ interrupt_info = (matched_event.metadata or {}).get("interrupt_info")
214
+ if not isinstance(interrupt_info, Mapping):
215
+ return normalized
216
+ if not (interrupt_info.get("tool_name") or normalized.get("tool_name")):
217
+ return normalized
218
+
219
+ decision = _approval_decision_from_resume(normalized)
220
+ tool_args = _parse_approval_arguments(
221
+ interrupt_info.get("arguments")
222
+ or interrupt_info.get("tool_args")
223
+ or interrupt_info.get("args")
224
+ or {}
225
+ )
226
+ tool_args["approval"] = decision
227
+
228
+ if not normalized.get("approval_request_id"):
229
+ request_id = _approval_request_id_from_event(matched_event)
230
+ if request_id:
231
+ normalized["approval_request_id"] = request_id
232
+ decision["approval_request_id"] = request_id
233
+ if interrupt_info.get("tool_name") and not normalized.get("tool_name"):
234
+ normalized["tool_name"] = str(interrupt_info.get("tool_name"))
235
+ if interrupt_info.get("run_id") and not normalized.get("run_id"):
236
+ normalized["run_id"] = str(interrupt_info.get("run_id"))
237
+ normalized["approval"] = decision
238
+ normalized["tool_args"] = tool_args
239
+ return normalized
240
+
241
+
242
+ def _builtin_tool_callable(tool_name: str):
243
+ name = str(tool_name or "").strip()
244
+ if not name:
245
+ return None
246
+ if name in {
247
+ "write_workspace_file",
248
+ "write_workspace_files",
249
+ "delete_workspace_file",
250
+ }:
251
+ from ksadk.toolsets import workspace
252
+
253
+ return getattr(workspace, name, None)
254
+ if name == "execute_skills":
255
+ from ksadk.toolsets.skills import execute_skills
256
+
257
+ return execute_skills
258
+ if name in {"run_command", "run_code"}:
259
+ from ksadk.toolsets import sandbox
260
+
261
+ return getattr(sandbox, name, None)
262
+ return None
263
+
264
+
265
+ def _tool_receipt_metadata(
266
+ *,
267
+ session_id: str,
268
+ run_id: str,
269
+ tool_name: str,
270
+ tool_args: Mapping[str, Any],
271
+ tool_call_id: str | None = None,
272
+ checkpoint_id: str | None = None,
273
+ framework: str | None = None,
274
+ framework_ref: Mapping[str, Any] | None = None,
275
+ status: str = "completed",
276
+ ) -> dict[str, Any]:
277
+ idempotency_key = build_tool_receipt_idempotency_key(
278
+ session_id=session_id,
279
+ run_id=run_id,
280
+ checkpoint_id=checkpoint_id,
281
+ tool_call_id=tool_call_id,
282
+ tool_name=tool_name,
283
+ tool_args=tool_args,
284
+ )
285
+ return {
286
+ "receipt_id": f"tr_{idempotency_key.removeprefix('tool_receipt:')[:24]}",
287
+ "idempotency_key": idempotency_key,
288
+ "tool_name": tool_name,
289
+ "tool_call_id": tool_call_id or run_id,
290
+ "run_id": run_id,
291
+ "checkpoint_id": checkpoint_id or "",
292
+ "framework": framework or "",
293
+ "framework_ref": dict(framework_ref or {}),
294
+ "status": status,
295
+ "created_at": time.time(),
296
+ }
297
+
298
+
299
+ def _tool_receipt_status_from_output(output: Any) -> str:
300
+ if not isinstance(output, Mapping):
301
+ return "failed"
302
+ status = str(output.get("status") or "").strip().lower()
303
+ if status == "accepted_not_extracted":
304
+ return "completed"
305
+ return "completed" if output.get("ok") is not False else "failed"
306
+
307
+
308
+ def _tool_resume_run_id(resume_input: Mapping[str, Any]) -> str:
309
+ return str(
310
+ resume_input.get("run_id")
311
+ or resume_input.get("call_id")
312
+ or resume_input.get("approval_request_id")
313
+ or resume_input.get("interrupt_id")
314
+ or ""
315
+ )
316
+
317
+
318
+ def _tool_receipt_idempotency_key_for_resume(
319
+ *,
320
+ session_id: str,
321
+ resume_input: Mapping[str, Any],
322
+ ) -> str | None:
323
+ tool_name = str(resume_input.get("tool_name") or "").strip()
324
+ if not tool_name:
325
+ return None
326
+ tool_args = resume_input.get("tool_args")
327
+ if not isinstance(tool_args, Mapping):
328
+ return None
329
+ run_id = _tool_resume_run_id(resume_input)
330
+ if not run_id:
331
+ return None
332
+ idempotency_key = build_tool_receipt_idempotency_key(
333
+ session_id=session_id,
334
+ run_id=run_id,
335
+ tool_call_id=run_id,
336
+ tool_name=tool_name,
337
+ tool_args=dict(tool_args),
338
+ )
339
+ return str(idempotency_key) if idempotency_key else None
340
+
341
+
342
+ def _find_tool_receipt_event_by_key(
343
+ events: Sequence[SessionEvent],
344
+ idempotency_key: str,
345
+ ) -> SessionEvent | None:
346
+ for event in reversed(events):
347
+ if event.event_type != "tool_result":
348
+ continue
349
+ metadata = event.metadata or {}
350
+ receipt = metadata.get("tool_receipt")
351
+ if not isinstance(receipt, Mapping):
352
+ continue
353
+ if str(receipt.get("idempotency_key") or "") == idempotency_key:
354
+ return event
355
+ return None
356
+
357
+
358
+ def _latest_checkpoint_metadata_for_run(
359
+ events: Sequence[SessionEvent],
360
+ run_id: str,
361
+ ) -> dict[str, Any]:
362
+ normalized_run_id = str(run_id or "").strip()
363
+ if not normalized_run_id:
364
+ return {}
365
+ for event in reversed(events):
366
+ if event.event_type != "run_checkpoint":
367
+ continue
368
+ metadata = event.metadata or {}
369
+ if str(metadata.get("run_id") or "").strip() != normalized_run_id:
370
+ continue
371
+ checkpoint_id = str(metadata.get("checkpoint_id") or "").strip()
372
+ if not checkpoint_id:
373
+ continue
374
+ framework_ref = metadata.get("framework_ref")
375
+ return {
376
+ "checkpoint_id": checkpoint_id,
377
+ "framework": str(metadata.get("framework") or ""),
378
+ "framework_ref": dict(framework_ref) if isinstance(framework_ref, Mapping) else {},
379
+ }
380
+ return {}
381
+
382
+
383
+ async def _execute_approved_builtin_tool_resume(
384
+ *,
385
+ session_id: str,
386
+ invocation_id: str,
387
+ resume_input: Mapping[str, Any],
388
+ session_service_provider: Callable[[], Any],
389
+ existing_events: Sequence[SessionEvent] | None = None,
390
+ ) -> dict[str, Any] | None:
391
+ approval = resume_input.get("approval")
392
+ if not isinstance(approval, Mapping) or not bool(approval.get("approved")):
393
+ return None
394
+ tool_name = str(resume_input.get("tool_name") or "").strip()
395
+ tool_func = _builtin_tool_callable(tool_name)
396
+ if tool_func is None:
397
+ return None
398
+
399
+ tool_args = resume_input.get("tool_args")
400
+ if not isinstance(tool_args, Mapping):
401
+ return None
402
+ call_args = dict(tool_args)
403
+ run_id = _tool_resume_run_id(resume_input)
404
+ service = session_service_provider()
405
+ if existing_events is None:
406
+ existing_events = await service.get_events(session_id)
407
+ checkpoint_metadata = _latest_checkpoint_metadata_for_run(existing_events, run_id)
408
+ receipt = _tool_receipt_metadata(
409
+ session_id=session_id,
410
+ run_id=run_id,
411
+ tool_call_id=run_id,
412
+ tool_name=tool_name,
413
+ tool_args=call_args,
414
+ checkpoint_id=checkpoint_metadata.get("checkpoint_id"),
415
+ framework=checkpoint_metadata.get("framework"),
416
+ framework_ref=checkpoint_metadata.get("framework_ref"),
417
+ )
418
+ existing_event = _find_tool_receipt_event_by_key(
419
+ existing_events,
420
+ receipt["idempotency_key"],
421
+ )
422
+ if existing_event is not None:
423
+ existing_metadata = existing_event.metadata or {}
424
+ output = existing_metadata.get("tool_output", "")
425
+ if isinstance(output, Mapping):
426
+ output = {**dict(output), "replayed": True}
427
+ replayed_receipt = {
428
+ **dict((existing_metadata.get("tool_receipt") or receipt)),
429
+ "replayed": True,
430
+ "replayed_from_event_id": existing_event.id,
431
+ }
432
+ await append_conversation_event(
433
+ session_id=session_id,
434
+ author="tool",
435
+ role="user",
436
+ text=str(output),
437
+ invocation_id=invocation_id,
438
+ event_type="tool_result",
439
+ session_service_provider=session_service_provider,
440
+ metadata={
441
+ "tool_name": tool_name,
442
+ "tool_args": call_args,
443
+ "tool_output": output,
444
+ "run_id": run_id,
445
+ "approval_request_id": resume_input.get("approval_request_id")
446
+ or resume_input.get("interrupt_id"),
447
+ "tool_receipt": replayed_receipt,
448
+ "replayed": True,
449
+ },
450
+ )
451
+ return {
452
+ "type": "function_call_output",
453
+ "call_id": run_id,
454
+ "output": output,
455
+ }
456
+
457
+ try:
458
+ if tool_name in {"run_command", "run_code"}:
459
+ output = await asyncio.to_thread(tool_func, **call_args)
460
+ else:
461
+ output = tool_func(**call_args)
462
+ except Exception as exc:
463
+ output = {"ok": False, "error_type": type(exc).__name__, "error_message": str(exc)}
464
+
465
+ receipt["status"] = _tool_receipt_status_from_output(output)
466
+ await append_conversation_event(
467
+ session_id=session_id,
468
+ author="tool",
469
+ role="user",
470
+ text=str(output),
471
+ invocation_id=invocation_id,
472
+ event_type="tool_result",
473
+ session_service_provider=session_service_provider,
474
+ metadata={
475
+ "tool_name": tool_name,
476
+ "tool_args": call_args,
477
+ "tool_output": output,
478
+ "run_id": run_id,
479
+ "approval_request_id": resume_input.get("approval_request_id")
480
+ or resume_input.get("interrupt_id"),
481
+ "tool_receipt": receipt,
482
+ },
483
+ )
484
+ return {
485
+ "type": "function_call_output",
486
+ "call_id": run_id,
487
+ "output": output,
488
+ }
489
+
490
+
491
+ def _is_approval_resume_input(resume_input: Mapping[str, Any]) -> bool:
492
+ return str(resume_input.get("type") or "").strip() in {
493
+ "mcp_approval_response",
494
+ "ksadk_resume",
495
+ "ksadk.approval_response",
496
+ }
497
+
498
+
499
+ def _is_checkpoint_resume_input(resume_input: Mapping[str, Any]) -> bool:
500
+ return str(resume_input.get("type") or "").strip() == "agentengine.resume_checkpoint"
501
+
502
+
503
+ def _failed_status_for_resume(resume_input: Mapping[str, Any] | None) -> str:
504
+ """checkpoint resume 失败时返回 resume_failed,否则返回 failed。
505
+
506
+ 仅 checkpoint resume 的失败才写 resume_failed(独立终态,触发 SSE [DONE]
507
+ 并让前端展示"恢复失败");approval/ksadk_resume 等其他 resume 失败仍写 failed。
508
+ """
509
+ if resume_input is not None and _is_checkpoint_resume_input(resume_input):
510
+ return "resume_failed"
511
+ return "failed"
512
+
513
+
514
+ def _normalize_checkpoint_resume_input(resume_input: Mapping[str, Any]) -> dict[str, Any]:
515
+ run_id = str(resume_input.get("run_id") or "").strip()
516
+ if not run_id:
517
+ raise ValueError("Checkpoint resume requires run_id")
518
+
519
+ checkpoint_id = str(resume_input.get("checkpoint_id") or "").strip()
520
+ if not checkpoint_id:
521
+ raise ValueError("Checkpoint resume requires checkpoint_id")
522
+
523
+ framework = str(resume_input.get("framework") or "langgraph").strip() or "langgraph"
524
+ raw_framework_ref = resume_input.get("framework_ref")
525
+ framework_ref = dict(raw_framework_ref) if isinstance(raw_framework_ref, Mapping) else {}
526
+ raw_framework_detail = framework_ref.get(framework)
527
+ framework_detail = (
528
+ dict(raw_framework_detail) if isinstance(raw_framework_detail, Mapping) else {}
529
+ )
530
+ framework_detail.setdefault("checkpoint_id", checkpoint_id)
531
+ if resume_input.get("thread_id") and not framework_detail.get("thread_id"):
532
+ framework_detail["thread_id"] = str(resume_input.get("thread_id"))
533
+ framework_ref[framework] = framework_detail
534
+
535
+ resume_attempt_id = str(resume_input.get("resume_attempt_id") or "").strip()
536
+ if not resume_attempt_id:
537
+ resume_attempt_id = f"resume_{uuid.uuid4().hex}"
538
+
539
+ return {
540
+ "type": "agentengine.resume_checkpoint",
541
+ "run_id": run_id,
542
+ "checkpoint_id": checkpoint_id,
543
+ "resume_attempt_id": resume_attempt_id,
544
+ "framework": framework,
545
+ "framework_ref": framework_ref,
546
+ "metadata": dict(resume_input.get("metadata") or resume_input.get("Metadata") or {}),
547
+ "checkpoint_metadata": dict(
548
+ resume_input.get("checkpoint_metadata")
549
+ or resume_input.get("CheckpointMetadata")
550
+ or resume_input.get("metadata")
551
+ or resume_input.get("Metadata")
552
+ or {}
553
+ ),
554
+ "resume_instruction_enabled": bool(
555
+ resume_input.get("resume_instruction_enabled")
556
+ or resume_input.get("ResumeInstructionEnabled")
557
+ ),
558
+ "resume_instruction": str(
559
+ resume_input.get("resume_instruction") or resume_input.get("ResumeInstruction") or ""
560
+ ).strip(),
561
+ }
562
+
563
+
564
+ def _agentengine_resume_metadata(resume_input: Mapping[str, Any] | None) -> dict[str, Any]:
565
+ if not resume_input or not _is_checkpoint_resume_input(resume_input):
566
+ return {}
567
+ return {
568
+ "agentengine": {
569
+ "action": "resume_checkpoint",
570
+ "run_id": str(resume_input.get("run_id") or ""),
571
+ "checkpoint_id": str(resume_input.get("checkpoint_id") or ""),
572
+ "resume_attempt_id": str(resume_input.get("resume_attempt_id") or ""),
573
+ "framework": str(resume_input.get("framework") or ""),
574
+ "framework_ref": dict(resume_input.get("framework_ref") or {}),
575
+ }
576
+ }
577
+
578
+
579
+ def _extract_agentengine_metadata(result: Mapping[str, Any] | None) -> dict[str, Any]:
580
+ if not result:
581
+ return {}
582
+ metadata = result.get("metadata")
583
+ if not isinstance(metadata, Mapping):
584
+ return {}
585
+ agentengine = metadata.get("agentengine")
586
+ if not isinstance(agentengine, Mapping):
587
+ return {}
588
+ return {"agentengine": dict(agentengine)}
589
+
590
+
591
+ def _checkpoint_event_args_from_agentengine_metadata(
592
+ agentengine_metadata: Mapping[str, Any] | None,
593
+ *,
594
+ fallback_run_id: str,
595
+ ) -> dict[str, Any] | None:
596
+ if not isinstance(agentengine_metadata, Mapping):
597
+ return None
598
+ framework = str(agentengine_metadata.get("framework") or "langgraph").strip() or "langgraph"
599
+ raw_framework_ref = agentengine_metadata.get("framework_ref")
600
+ if not isinstance(raw_framework_ref, Mapping):
601
+ return None
602
+ framework_ref = dict(raw_framework_ref)
603
+ raw_framework_detail = framework_ref.get(framework)
604
+ if not isinstance(raw_framework_detail, Mapping):
605
+ return None
606
+ framework_detail = dict(raw_framework_detail)
607
+ checkpoint_id = str(framework_detail.get("checkpoint_id") or "").strip()
608
+ if not checkpoint_id:
609
+ return None
610
+ run_id = str(agentengine_metadata.get("run_id") or fallback_run_id or "").strip()
611
+ if not run_id:
612
+ return None
613
+ phase = str(agentengine_metadata.get("phase") or "").strip()
614
+ display_metadata = {
615
+ key: value
616
+ for key, value in agentengine_metadata.items()
617
+ if key
618
+ not in {
619
+ "run_id",
620
+ "checkpoint_id",
621
+ "framework",
622
+ "framework_ref",
623
+ "phase",
624
+ }
625
+ }
626
+ return {
627
+ "run_id": run_id,
628
+ "checkpoint_id": checkpoint_id,
629
+ "framework": framework,
630
+ "framework_ref": framework_ref,
631
+ "phase": phase,
632
+ "metadata": display_metadata,
633
+ }
634
+
635
+
636
+ def _merge_agentengine_metadata(
637
+ *metadata_items: Mapping[str, Any] | None,
638
+ ) -> dict[str, Any]:
639
+ merged: dict[str, Any] = {}
640
+ for metadata in metadata_items:
641
+ if not isinstance(metadata, Mapping):
642
+ continue
643
+ agentengine = metadata.get("agentengine")
644
+ if not isinstance(agentengine, Mapping):
645
+ continue
646
+ next_agentengine = dict(agentengine)
647
+ merged.update(next_agentengine)
648
+ framework_ref = agentengine.get("framework_ref")
649
+ if isinstance(framework_ref, Mapping):
650
+ merged_framework_ref = merged.get("framework_ref")
651
+ existing_framework_ref = (
652
+ merged_framework_ref if isinstance(merged_framework_ref, Mapping) else {}
653
+ )
654
+ merged["framework_ref"] = {
655
+ **dict(existing_framework_ref),
656
+ **dict(framework_ref),
657
+ }
658
+ return {"agentengine": merged} if merged else {}
659
+
660
+
661
+ def _format_resume_response_text(resume_input: Mapping[str, Any]) -> str:
662
+ item_type = str(resume_input.get("type") or "resume")
663
+ if item_type == "mcp_approval_response":
664
+ approval_request_id = str(resume_input.get("approval_request_id") or "")
665
+ approve = resume_input.get("approve")
666
+ reason = str(resume_input.get("reason") or "").strip()
667
+ parts = [f"mcp_approval_response approval_request_id={approval_request_id}"]
668
+ if approve is not None:
669
+ parts.append(f"approve={bool(approve)}")
670
+ if reason:
671
+ parts.append(f"reason={reason}")
672
+ return " ".join(parts)
673
+
674
+ if item_type == "function_call_output":
675
+ output = resume_input.get("output", "")
676
+ if isinstance(output, (dict, list)):
677
+ output_text = json.dumps(output, ensure_ascii=False, sort_keys=True)
678
+ else:
679
+ output_text = str(output)
680
+ return (
681
+ f"function_call_output call_id={resume_input.get('call_id') or ''} output={output_text}"
682
+ )
683
+
684
+ return f"{item_type} {json.dumps(dict(resume_input), ensure_ascii=False, sort_keys=True)}"
685
+
686
+
687
+ def _stringify_responses_item_value(value: Any) -> str:
688
+ if value is None:
689
+ return ""
690
+ if isinstance(value, str):
691
+ return value
692
+ if isinstance(value, (dict, list)):
693
+ return json.dumps(value, ensure_ascii=False, indent=2)
694
+ return str(value)
695
+
696
+
697
+ def _responses_output_item_text(item: Mapping[str, Any]) -> str:
698
+ for text_field in ("output_text", "text", "summary_text", "delta"):
699
+ value = item.get(text_field)
700
+ if isinstance(value, str) and value:
701
+ return value
702
+ summary = item.get("summary")
703
+ if isinstance(summary, Sequence) and not isinstance(summary, (str, bytes, bytearray)):
704
+ parts: list[str] = []
705
+ for part in summary:
706
+ if isinstance(part, str):
707
+ parts.append(part)
708
+ elif isinstance(part, Mapping):
709
+ text = part.get("text") or part.get("summary_text")
710
+ if isinstance(text, str):
711
+ parts.append(text)
712
+ if parts:
713
+ return "".join(parts)
714
+ content = item.get("content")
715
+ if isinstance(content, Sequence) and not isinstance(content, (str, bytes, bytearray)):
716
+ parts = []
717
+ for part in content:
718
+ if isinstance(part, str):
719
+ parts.append(part)
720
+ elif isinstance(part, Mapping):
721
+ text = part.get("text")
722
+ if isinstance(text, str):
723
+ parts.append(text)
724
+ return "".join(parts)
725
+ if isinstance(content, str):
726
+ return content
727
+ return ""
728
+
729
+
730
+ def _semantic_events_from_responses_output(output: Sequence[Any]) -> list[dict[str, Any]]:
731
+ events: list[dict[str, Any]] = []
732
+ tool_names: dict[str, str] = {}
733
+ for raw_item in output:
734
+ if not isinstance(raw_item, Mapping):
735
+ continue
736
+ item = dict(raw_item)
737
+ item_type = str(item.get("type") or "").strip()
738
+ item_id = str(item.get("id") or item.get("item_id") or "")
739
+ call_id = str(item.get("call_id") or "")
740
+ if item_type == "function_call":
741
+ name = str(item.get("name") or item.get("tool_name") or "tool")
742
+ args = _stringify_responses_item_value(
743
+ item.get("arguments")
744
+ if "arguments" in item
745
+ else item.get("args", item.get("input"))
746
+ )
747
+ if item_id:
748
+ tool_names[item_id] = name
749
+ if call_id:
750
+ tool_names[call_id] = name
751
+ events.append(
752
+ {
753
+ "type": "tool_call",
754
+ "name": name,
755
+ "args": args,
756
+ "run_id": call_id or item_id or None,
757
+ }
758
+ )
759
+ continue
760
+ if item_type == "function_call_output":
761
+ name = (
762
+ tool_names.get(call_id)
763
+ or tool_names.get(item_id)
764
+ or str(item.get("name") or "tool")
765
+ )
766
+ output_text = _stringify_responses_item_value(
767
+ item.get("output") if "output" in item else item.get("result", item.get("content"))
768
+ )
769
+ events.append(
770
+ {
771
+ "type": "tool_result",
772
+ "name": name,
773
+ "output": output_text,
774
+ "run_id": call_id or item_id or None,
775
+ }
776
+ )
777
+ continue
778
+ if item_type in {"reasoning", "reasoning_summary", "reasoning_summary_text"}:
779
+ text = _responses_output_item_text(item)
780
+ if text:
781
+ events.append({"type": "thinking", "delta": text})
782
+ return events
783
+
784
+
785
+ def _model_options_disable_reasoning(model_options: Mapping[str, Any] | None) -> bool:
786
+ normalized = normalize_model_options(model_options)
787
+ reasoning = normalized.get("reasoning")
788
+ if isinstance(reasoning, Mapping):
789
+ effort = str(reasoning.get("effort") or "").strip().lower()
790
+ if effort in {"none", "off", "disabled", "disable", "false", "0"}:
791
+ return True
792
+ max_reasoning_tokens = normalized.get("max_reasoning_tokens")
793
+ if max_reasoning_tokens is not None:
794
+ try:
795
+ return int(max_reasoning_tokens) <= 0
796
+ except (TypeError, ValueError):
797
+ pass
798
+ thinking = normalized.get("thinking")
799
+ if isinstance(thinking, Mapping):
800
+ raw_type = str(thinking.get("type") or thinking.get("status") or "").strip().lower()
801
+ return raw_type in {"disabled", "disable", "off", "none", "false", "0"}
802
+ if isinstance(thinking, bool):
803
+ return not thinking
804
+ raw_thinking = str(thinking or "").strip().lower()
805
+ return raw_thinking in {"disabled", "disable", "off", "none", "false", "0"}
806
+
807
+
808
+ def _filter_responses_reasoning_output(output: Sequence[Any]) -> list[Any]:
809
+ filtered: list[Any] = []
810
+ for raw_item in output:
811
+ if isinstance(raw_item, Mapping):
812
+ item_type = str(raw_item.get("type") or "").strip()
813
+ if item_type in {"reasoning", "reasoning_summary", "reasoning_summary_text"}:
814
+ continue
815
+ filtered.append(raw_item)
816
+ return filtered