agentengine-sdk-python 0.7.0__py3-none-any.whl → 0.8.1__py3-none-any.whl

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