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
@@ -4,28 +4,33 @@ LangGraphRunner - LangGraph 框架运行时
4
4
  直接透传 LangGraph 原生能力,最小化封装
5
5
  """
6
6
 
7
+ import base64
8
+ import inspect
7
9
  import os
8
- import uuid
9
10
  import re
11
+ import uuid
10
12
  from typing import Any, AsyncIterator, Dict, Mapping
11
- import base64
12
- from pathlib import Path
13
13
 
14
- from ksadk.runners.base_runner import BaseRunner
15
- from ksadk.runners.usage_accumulator import accumulate_usage
16
- from ksadk.sessions.continuity import LangGraphSessionAdapter
17
- from ksadk.runners.utils import get_langfuse_callbacks, get_langfuse_metadata, load_agent_module
18
14
  from langgraph.types import Command
15
+
19
16
  from ksadk.conversations.attachments import classify_attachment_kind, read_attachment_uri_bytes
20
17
  from ksadk.conversations.reasoning_markup import ReasoningMarkupParser, strip_reasoning_markup
18
+ from ksadk.runners.base_runner import BaseRunner
19
+ from ksadk.runners.usage_accumulator import accumulate_usage
20
+ from ksadk.runners.utils import load_agent_module
21
+ from ksadk.sessions.continuity import LangGraphSessionAdapter
21
22
 
22
23
 
23
24
  class LangGraphRunner(BaseRunner):
24
25
  """LangGraph 框架运行时
25
-
26
+
26
27
  透传原生 LangGraph 功能,支持任意 State 格式
27
28
  """
28
29
 
30
+ # ToolGateway approvals can arise after an otherwise terminal tool call;
31
+ # this runner opts into the runtime's semantic follow-up continuation.
32
+ supports_gateway_approval_semantic_resume = True
33
+
29
34
  def load_agent(self) -> None:
30
35
  self._load_agent(force_reload=False)
31
36
 
@@ -40,7 +45,7 @@ class LangGraphRunner(BaseRunner):
40
45
  self._loaded_model_name = self.normalize_requested_model(
41
46
  os.getenv("OPENAI_MODEL_NAME") or os.getenv("MODEL_NAME")
42
47
  )
43
-
48
+
44
49
  if not hasattr(self._agent, "invoke"):
45
50
  raise TypeError("加载的对象不是有效的 LangGraph CompiledGraph")
46
51
 
@@ -57,10 +62,10 @@ class LangGraphRunner(BaseRunner):
57
62
 
58
63
  def describe_checkpoint_capability(self) -> dict[str, Any]:
59
64
  agent = getattr(self, "_agent", None)
60
- has_checkpointer = bool(
61
- getattr(agent, "checkpointer", None) or getattr(agent, "_checkpointer", None)
62
- )
63
- if not has_checkpointer:
65
+ checkpointer = getattr(agent, "checkpointer", None)
66
+ if checkpointer is None:
67
+ checkpointer = getattr(agent, "_checkpointer", None)
68
+ if checkpointer is None:
64
69
  return {
65
70
  "Supported": False,
66
71
  "Backend": "none",
@@ -70,7 +75,16 @@ class LangGraphRunner(BaseRunner):
70
75
  "Reason": "LangGraph graph has no configured checkpointer",
71
76
  }
72
77
 
73
- backend = str(os.getenv("KSADK_CHECKPOINT_BACKEND") or "").strip().lower()
78
+ checkpointer_type = type(checkpointer)
79
+ type_name = f"{checkpointer_type.__module__}.{checkpointer_type.__name__}".lower()
80
+ if "memory" in type_name or "inmemory" in type_name:
81
+ backend = "memory"
82
+ elif "sqlite" in type_name:
83
+ backend = "sqlite"
84
+ elif "postgres" in type_name:
85
+ backend = "postgres"
86
+ else:
87
+ backend = str(os.getenv("KSADK_CHECKPOINT_BACKEND") or "").strip().lower()
74
88
  if backend == "local":
75
89
  backend = "sqlite"
76
90
  if not backend:
@@ -87,7 +101,9 @@ class LangGraphRunner(BaseRunner):
87
101
  scope = "pod_local"
88
102
  durable = True
89
103
  shared = False
90
- reason = "SQLite checkpoint is durable for local web debugging but is not shared across pods"
104
+ reason = (
105
+ "SQLite checkpoint is durable for local web debugging but is not shared across pods"
106
+ )
91
107
  elif backend in {"memory", "inmemory"}:
92
108
  backend = "memory"
93
109
  scope = "process_local"
@@ -109,26 +125,17 @@ class LangGraphRunner(BaseRunner):
109
125
  capabilities = super().get_runtime_capabilities()
110
126
  capabilities["SessionContinuity"] = {
111
127
  "Supported": True,
112
- "Type": "checkpoint"
113
- if capabilities["Checkpoint"].get("Supported")
114
- else "semantic_replay",
115
- "Level": "runtime"
116
- if capabilities["Checkpoint"].get("Supported")
117
- else "semantic",
128
+ "Type": (
129
+ "checkpoint" if capabilities["Checkpoint"].get("Supported") else "semantic_replay"
130
+ ),
131
+ "Level": "runtime" if capabilities["Checkpoint"].get("Supported") else "semantic",
118
132
  "Reason": "",
119
133
  }
120
134
  return capabilities
121
135
 
122
136
  def _get_config(self, session_id: str) -> dict:
123
137
  """获取运行配置"""
124
- config = {"configurable": {"thread_id": session_id}}
125
-
126
- langfuse_callbacks = get_langfuse_callbacks()
127
- if langfuse_callbacks:
128
- config["callbacks"] = langfuse_callbacks
129
- config["metadata"] = get_langfuse_metadata(session_id)
130
-
131
- return config
138
+ return {"configurable": {"thread_id": session_id}}
132
139
 
133
140
  @staticmethod
134
141
  def _extract_langgraph_checkpoint_ref(payload: Dict[str, Any]) -> dict[str, Any]:
@@ -154,7 +161,9 @@ class LangGraphRunner(BaseRunner):
154
161
 
155
162
  thread_id = str(checkpoint_ref.get("thread_id") or session_id or "").strip()
156
163
  if not thread_id:
157
- raise ValueError("checkpoint_resume requires session_id or framework_ref.langgraph.thread_id")
164
+ raise ValueError(
165
+ "checkpoint_resume requires session_id or framework_ref.langgraph.thread_id"
166
+ )
158
167
 
159
168
  next_config = dict(config)
160
169
  configurable = dict(next_config.get("configurable") or {})
@@ -164,6 +173,50 @@ class LangGraphRunner(BaseRunner):
164
173
  next_config["configurable"] = configurable
165
174
  return next_config
166
175
 
176
+ @staticmethod
177
+ def _checkpoint_resume_input(
178
+ value: Any,
179
+ *,
180
+ payload_provided: bool,
181
+ interrupt_id: str,
182
+ ) -> Command | None:
183
+ if not payload_provided:
184
+ return None
185
+ resume_value = {interrupt_id: value} if interrupt_id else value
186
+ return Command(resume=resume_value)
187
+
188
+ @staticmethod
189
+ def _is_gateway_approval_semantic_resume(value: Any) -> bool:
190
+ """Whether ``value`` came from a completed ToolGateway approval.
191
+
192
+ Native LangGraph ``interrupt()`` values must keep using
193
+ ``Command(resume=...)``. In contrast, a ToolGateway can return an
194
+ ``approval_required`` result from an otherwise normal tool call; the
195
+ graph then reaches its terminal node before KsADK shows the approval
196
+ card. Its approved tool result needs a new semantic graph turn.
197
+ """
198
+
199
+ return bool(
200
+ isinstance(value, Mapping)
201
+ and value.get("_ksadk_gateway_approval_resume") is True
202
+ and str(value.get("type") or "") == "function_call_output"
203
+ )
204
+
205
+ @staticmethod
206
+ def _gateway_approval_follow_up_input() -> str:
207
+ """A neutral prompt for the post-approval semantic continuation.
208
+
209
+ The actual approval response and durable tool result are already in
210
+ the session history. Do not repeat their contents here: project hooks
211
+ commonly route by input keywords, and copying a workspace path back
212
+ into a synthetic user message can accidentally trigger another tool.
213
+ """
214
+
215
+ return (
216
+ "系统已完成此前获批的操作。请基于会话记录中的真实结果,直接向用户说明完成情况;"
217
+ "不要重试,也不要再次要求确认。"
218
+ )
219
+
167
220
  @staticmethod
168
221
  def _checkpoint_ref_from_state(state: Any) -> dict[str, Any]:
169
222
  state_config = None
@@ -180,17 +233,15 @@ class LangGraphRunner(BaseRunner):
180
233
  checkpoint_id = str(configurable.get("checkpoint_id") or "").strip()
181
234
  if not thread_id or not checkpoint_id:
182
235
  return {}
183
- next_nodes_raw = state.get("next") if isinstance(state, dict) else getattr(state, "next", None)
236
+ next_nodes_raw = (
237
+ state.get("next") if isinstance(state, dict) else getattr(state, "next", None)
238
+ )
184
239
  next_nodes: list[str] = []
185
240
  if isinstance(next_nodes_raw, str):
186
241
  if next_nodes_raw.strip():
187
242
  next_nodes = [next_nodes_raw.strip()]
188
243
  elif isinstance(next_nodes_raw, (list, tuple, set)):
189
- next_nodes = [
190
- str(item).strip()
191
- for item in next_nodes_raw
192
- if str(item or "").strip()
193
- ]
244
+ next_nodes = [str(item).strip() for item in next_nodes_raw if str(item or "").strip()]
194
245
  return {
195
246
  "langgraph": {
196
247
  "thread_id": thread_id,
@@ -273,7 +324,11 @@ class LangGraphRunner(BaseRunner):
273
324
  def _ambient_context_text(payload: Dict[str, Any]) -> str:
274
325
  sections: list[str] = []
275
326
  kb_context = payload.get("kb_context") or {}
276
- kb_text = str(kb_context.get("formatted_text") or "").strip() if isinstance(kb_context, dict) else ""
327
+ kb_text = (
328
+ str(kb_context.get("formatted_text") or "").strip()
329
+ if isinstance(kb_context, dict)
330
+ else ""
331
+ )
277
332
  if kb_text:
278
333
  sections.append(f"Knowledge base context:\n{kb_text}")
279
334
 
@@ -325,6 +380,14 @@ class LangGraphRunner(BaseRunner):
325
380
  prepared = prepare_state(dict(normalized_payload), session_context)
326
381
  if not isinstance(prepared, dict):
327
382
  raise TypeError("ksadk_prepare_state(payload, session_context) must return a dict")
383
+ # A custom state hook owns application state, but must not accidentally
384
+ # erase protocol namespaces that framework middleware consumes. In
385
+ # particular CopilotKit reads ``ag-ui`` to decide whether to inject the
386
+ # official A2UI tool, and reads ``copilotkit.actions`` for frontend
387
+ # actions. Keep these namespaces narrow and transport-owned.
388
+ for namespace in ("ag-ui", "copilotkit"):
389
+ if namespace in normalized_payload:
390
+ prepared.setdefault(namespace, normalized_payload[namespace])
328
391
  return prepared
329
392
 
330
393
  def _to_state(self, payload: Dict[str, Any], history: list) -> Dict[str, Any]:
@@ -338,7 +401,7 @@ class LangGraphRunner(BaseRunner):
338
401
  if "input" in normalized_payload and "messages" not in normalized_payload:
339
402
  from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
340
403
 
341
- messages = []
404
+ messages: list[Any] = []
342
405
  if system_text:
343
406
  messages.append(SystemMessage(content=system_text))
344
407
  for msg in history:
@@ -442,11 +505,11 @@ class LangGraphRunner(BaseRunner):
442
505
  )
443
506
  continue
444
507
 
445
- file_uri = attachment.get("file_uri")
446
- if not file_uri:
508
+ attachment_uri = attachment.get("file_uri")
509
+ if not attachment_uri:
447
510
  continue
448
511
 
449
- raw = read_attachment_uri_bytes(file_uri)
512
+ raw = read_attachment_uri_bytes(str(attachment_uri))
450
513
  if not raw:
451
514
  continue
452
515
 
@@ -536,7 +599,7 @@ class LangGraphRunner(BaseRunner):
536
599
 
537
600
  async def invoke(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
538
601
  """调用 LangGraph 图
539
-
602
+
540
603
  支持两种输入格式:
541
604
  1. 简化格式: {"input": "hello"} - 自动转换为 messages
542
605
  2. 原生格式: {"messages": [...]} 或自定义 State - 直接透传
@@ -549,11 +612,22 @@ class LangGraphRunner(BaseRunner):
549
612
  session_id = payload.pop("session_id", None) or str(uuid.uuid4())[:8]
550
613
  is_resume = payload.pop("resume", False)
551
614
  is_checkpoint_resume = bool(payload.pop("checkpoint_resume", False))
615
+ resume_payload_provided = bool(payload.pop("resume_payload_provided", False))
616
+ resume_interrupt_id = str(payload.pop("resume_interrupt_id", "") or "")
617
+ resume_value = payload.get("input")
618
+ is_gateway_approval_resume = bool(
619
+ is_resume and self._is_gateway_approval_semantic_resume(resume_value)
620
+ )
621
+ if is_gateway_approval_resume:
622
+ # ``Command(resume=...)`` only works for a graph that actually
623
+ # yielded LangGraph's native interrupt. ToolGateway approvals are
624
+ # intercepted after a normal tool result, when the graph already
625
+ # ended, so restart a normal turn from the persisted transcript.
626
+ payload["input"] = self._gateway_approval_follow_up_input()
627
+ resume_value = payload["input"]
552
628
  checkpoint_ref = self._extract_langgraph_checkpoint_ref(payload)
553
629
  history = payload.pop("history", [])
554
630
  native_context = self.build_native_context(payload.get("platform_context"))
555
- normalized_payload = self._strip_platform_context_fields(payload)
556
-
557
631
  config = self._get_config(session_id)
558
632
  if is_checkpoint_resume:
559
633
  config = self._apply_checkpoint_resume_config(
@@ -561,28 +635,38 @@ class LangGraphRunner(BaseRunner):
561
635
  session_id=session_id,
562
636
  checkpoint_ref=checkpoint_ref,
563
637
  )
564
-
638
+
565
639
  # 判断输入格式 / resume
566
640
  if is_checkpoint_resume:
567
- state = None
641
+ state = resume_value
642
+ elif is_resume and not is_gateway_approval_resume:
643
+ # ``Command(resume=...)`` is delivered to the graph's suspended
644
+ # interrupt. A custom prepare-state hook is for new user input;
645
+ # applying it here can rewrite an approval decision into ordinary
646
+ # graph state and turn an approved HITL action into a rejection.
647
+ state = resume_value
568
648
  elif self._has_prepare_state_hook():
569
- state = self._prepare_state_with_hook(payload, session_id, history, is_resume=is_resume)
570
- elif is_resume:
571
- if "input" in normalized_payload and len(normalized_payload) == 1:
572
- state = normalized_payload["input"]
573
- else:
574
- state = normalized_payload
649
+ state = self._prepare_state_with_hook(
650
+ payload,
651
+ session_id,
652
+ history,
653
+ is_resume=is_gateway_approval_resume,
654
+ )
575
655
  else:
576
656
  state = self._to_state(payload, history)
577
657
 
578
658
  try:
579
659
  if is_checkpoint_resume:
580
660
  result = await self._invoke_graph(
581
- None,
661
+ self._checkpoint_resume_input(
662
+ state,
663
+ payload_provided=resume_payload_provided,
664
+ interrupt_id=resume_interrupt_id,
665
+ ),
582
666
  config=config,
583
667
  context=native_context,
584
668
  )
585
- elif is_resume:
669
+ elif is_resume and not is_gateway_approval_resume:
586
670
  result = await self._invoke_graph(
587
671
  Command(resume=state),
588
672
  config=config,
@@ -606,7 +690,7 @@ class LangGraphRunner(BaseRunner):
606
690
  if metadata:
607
691
  output["metadata"] = {**(output.get("metadata") or {}), **metadata}
608
692
  return output
609
-
693
+
610
694
  except Exception as e:
611
695
  if "Interrupt" in type(e).__name__:
612
696
  interrupt_info = self._get_interrupt_info(self._agent.get_state(config))
@@ -614,7 +698,11 @@ class LangGraphRunner(BaseRunner):
614
698
  "type": "interrupt",
615
699
  "interrupt_info": interrupt_info,
616
700
  "session_id": session_id,
617
- "output": interrupt_info.get("message", "需要用户确认") if isinstance(interrupt_info, dict) else "需要用户确认",
701
+ "output": (
702
+ interrupt_info.get("message", "需要用户确认")
703
+ if isinstance(interrupt_info, dict)
704
+ else "需要用户确认"
705
+ ),
618
706
  }
619
707
  raise
620
708
 
@@ -623,16 +711,21 @@ class LangGraphRunner(BaseRunner):
623
711
  if isinstance(result, dict):
624
712
  # 自定义 output 字段是业务显式出参,优先于内部 messages state。
625
713
  if "output" in result:
626
- return result["output"]
714
+ return str(result["output"])
627
715
  # LangGraph 示例常用 answer 作为最终业务回答字段。
628
716
  if "answer" in result:
629
- return result["answer"]
717
+ return str(result["answer"])
630
718
  # 标准 messages 格式
631
719
  if "messages" in result:
632
720
  messages = result["messages"]
633
721
  if messages:
634
722
  last = messages[-1]
635
- return last.get("content", str(last)) if isinstance(last, dict) else getattr(last, "content", str(last))
723
+ content = (
724
+ last.get("content", str(last))
725
+ if isinstance(last, dict)
726
+ else getattr(last, "content", str(last))
727
+ )
728
+ return str(content)
636
729
  return str(result) if result else ""
637
730
 
638
731
  def _get_interrupt_info(self, state) -> dict:
@@ -642,12 +735,18 @@ class LangGraphRunner(BaseRunner):
642
735
  if hasattr(task, "interrupts") and task.interrupts:
643
736
  for intr in task.interrupts:
644
737
  if hasattr(intr, "value"):
645
- return intr.value
738
+ value = intr.value
739
+ info = dict(value) if isinstance(value, Mapping) else {"value": value}
740
+ interrupt_id = str(getattr(intr, "id", "") or "")
741
+ if interrupt_id:
742
+ info.setdefault("approval_request_id", interrupt_id)
743
+ return info
646
744
  return {}
647
745
 
648
746
  async def _stream_checkpoint_resume_updates(
649
747
  self,
650
748
  *,
749
+ stream_input: Any,
651
750
  config: dict[str, Any],
652
751
  context: dict[str, Any] | None,
653
752
  ) -> AsyncIterator[Dict[str, Any]]:
@@ -664,11 +763,17 @@ class LangGraphRunner(BaseRunner):
664
763
 
665
764
  latest_output: Any = None
666
765
  emitted_update = False
667
- async for update in self._agent.astream(None, **kwargs):
766
+ tool_names: dict[str, str] = {}
767
+ async for update in self._agent.astream(stream_input, **kwargs):
668
768
  emitted_update = True
669
769
  latest_output = update
670
770
  if isinstance(update, Mapping):
671
771
  for node_name, node_output in update.items():
772
+ for tool_event in self._tool_events_from_graph_update(
773
+ node_output,
774
+ tool_names=tool_names,
775
+ ):
776
+ yield tool_event
672
777
  yield {
673
778
  "type": "graph_update",
674
779
  "node": str(node_name),
@@ -703,6 +808,76 @@ class LangGraphRunner(BaseRunner):
703
808
  if metadata:
704
809
  yield {"type": "checkpoint", "metadata": metadata}
705
810
 
811
+ @staticmethod
812
+ def _tool_events_from_graph_update(
813
+ node_output: Any,
814
+ *,
815
+ tool_names: dict[str, str],
816
+ ) -> list[dict[str, Any]]:
817
+ """Preserve LangGraph update-mode tool messages as structured runner events."""
818
+
819
+ if isinstance(node_output, Mapping):
820
+ messages = node_output.get("messages")
821
+ else:
822
+ messages = getattr(node_output, "messages", None)
823
+ if not isinstance(messages, (list, tuple)):
824
+ return []
825
+
826
+ events: list[dict[str, Any]] = []
827
+ for message in messages:
828
+ if isinstance(message, Mapping):
829
+ tool_calls = message.get("tool_calls")
830
+ tool_call_id = message.get("tool_call_id")
831
+ message_name = message.get("name")
832
+ message_content = message.get("content")
833
+ message_status = message.get("status")
834
+ else:
835
+ tool_calls = getattr(message, "tool_calls", None)
836
+ tool_call_id = getattr(message, "tool_call_id", None)
837
+ message_name = getattr(message, "name", None)
838
+ message_content = getattr(message, "content", None)
839
+ message_status = getattr(message, "status", None)
840
+
841
+ if isinstance(tool_calls, (list, tuple)):
842
+ for tool_call in tool_calls:
843
+ if isinstance(tool_call, Mapping):
844
+ call_id = str(tool_call.get("id") or tool_call.get("tool_call_id") or "")
845
+ name = str(tool_call.get("name") or "tool")
846
+ args = tool_call.get("args")
847
+ else:
848
+ call_id = str(
849
+ getattr(tool_call, "id", None)
850
+ or getattr(tool_call, "tool_call_id", None)
851
+ or ""
852
+ )
853
+ name = str(getattr(tool_call, "name", None) or "tool")
854
+ args = getattr(tool_call, "args", None)
855
+ if call_id:
856
+ tool_names[call_id] = name
857
+ events.append(
858
+ {
859
+ "type": "tool_call",
860
+ "tool_call_id": call_id or name,
861
+ "tool_name": name,
862
+ "tool_args": args,
863
+ }
864
+ )
865
+
866
+ if tool_call_id:
867
+ call_id = str(tool_call_id)
868
+ name = str(message_name or tool_names.get(call_id) or "tool")
869
+ status = str(message_status or "").lower()
870
+ events.append(
871
+ {
872
+ "type": "tool_result",
873
+ "tool_call_id": call_id,
874
+ "tool_name": name,
875
+ "tool_output": message_content,
876
+ "error": message_content if status in {"error", "failed"} else None,
877
+ }
878
+ )
879
+ return events
880
+
706
881
  async def stream(self, input_data: Dict[str, Any]) -> AsyncIterator[Dict[str, Any]]:
707
882
  """流式调用 LangGraph 图"""
708
883
  payload = dict(input_data)
@@ -711,19 +886,30 @@ class LangGraphRunner(BaseRunner):
711
886
  history = payload.pop("history", [])
712
887
  is_resume = payload.pop("resume", False)
713
888
  is_checkpoint_resume = bool(payload.pop("checkpoint_resume", False))
889
+ resume_payload_provided = bool(payload.pop("resume_payload_provided", False))
890
+ resume_interrupt_id = str(payload.pop("resume_interrupt_id", "") or "")
891
+ resume_value = payload.get("input")
892
+ is_gateway_approval_resume = bool(
893
+ is_resume and self._is_gateway_approval_semantic_resume(resume_value)
894
+ )
895
+ if is_gateway_approval_resume:
896
+ # See ``invoke``: the graph did not suspend at a native interrupt,
897
+ # so use the durable transcript to run the post-tool answer turn.
898
+ payload["input"] = self._gateway_approval_follow_up_input()
899
+ resume_value = payload["input"]
714
900
  checkpoint_ref = self._extract_langgraph_checkpoint_ref(payload)
715
901
  native_context = self.build_native_context(payload.get("platform_context"))
716
- normalized_payload = self._strip_platform_context_fields(payload)
717
-
718
902
  invoke_payload = dict(payload)
719
903
  invoke_payload["session_id"] = session_id
720
904
  if history:
721
905
  invoke_payload["history"] = history
722
- if is_resume:
906
+ if is_resume and not is_gateway_approval_resume:
723
907
  invoke_payload["resume"] = True
724
908
  if is_checkpoint_resume:
725
909
  invoke_payload["checkpoint_resume"] = True
726
-
910
+ invoke_payload["resume_payload_provided"] = resume_payload_provided
911
+ invoke_payload["resume_interrupt_id"] = resume_interrupt_id
912
+
727
913
  config = self._get_config(session_id)
728
914
  if is_checkpoint_resume:
729
915
  config = self._apply_checkpoint_resume_config(
@@ -733,14 +919,18 @@ class LangGraphRunner(BaseRunner):
733
919
  )
734
920
 
735
921
  if is_checkpoint_resume:
736
- state = None
922
+ state = resume_value
923
+ elif is_resume and not is_gateway_approval_resume:
924
+ # Keep the interrupt value intact for ``Command(resume=...)``;
925
+ # prepare-state hooks only shape fresh user turns.
926
+ state = resume_value
737
927
  elif self._has_prepare_state_hook():
738
- state = self._prepare_state_with_hook(payload, session_id, history, is_resume=is_resume)
739
- elif is_resume:
740
- if "input" in normalized_payload and len(normalized_payload) == 1:
741
- state = normalized_payload["input"]
742
- else:
743
- state = normalized_payload
928
+ state = self._prepare_state_with_hook(
929
+ payload,
930
+ session_id,
931
+ history,
932
+ is_resume=is_gateway_approval_resume,
933
+ )
744
934
  else:
745
935
  state = self._to_state(payload, history)
746
936
 
@@ -799,6 +989,11 @@ class LangGraphRunner(BaseRunner):
799
989
  if is_checkpoint_resume and callable(getattr(self._agent, "astream", None)):
800
990
  try:
801
991
  async for chunk in self._stream_checkpoint_resume_updates(
992
+ stream_input=self._checkpoint_resume_input(
993
+ state,
994
+ payload_provided=resume_payload_provided,
995
+ interrupt_id=resume_interrupt_id,
996
+ ),
802
997
  config=config,
803
998
  context=native_context,
804
999
  ):
@@ -826,9 +1021,25 @@ class LangGraphRunner(BaseRunner):
826
1021
  return
827
1022
 
828
1023
  try:
829
- stream_input = None if is_checkpoint_resume else (Command(resume=state) if is_resume else state)
1024
+ stream_input = (
1025
+ self._checkpoint_resume_input(
1026
+ state,
1027
+ payload_provided=resume_payload_provided,
1028
+ interrupt_id=resume_interrupt_id,
1029
+ )
1030
+ if is_checkpoint_resume
1031
+ else (
1032
+ Command(resume=state) if is_resume and not is_gateway_approval_resume else state
1033
+ )
1034
+ )
1035
+ # stream_mode 含 "custom" 才会产生 on_custom_stream 事件(custom writer);
1036
+ # 保留默认 "values" 以兼容既有 on_chain_end/graph_update 消费。
830
1037
  stream_kwargs = {"version": "v2", "config": config}
831
- if native_context and self._callable_accepts_keyword(self._agent.astream_events, "context"):
1038
+ if self._callable_accepts_keyword(self._agent.astream_events, "stream_mode"):
1039
+ stream_kwargs["stream_mode"] = ["values", "custom"]
1040
+ if native_context and self._callable_accepts_keyword(
1041
+ self._agent.astream_events, "context"
1042
+ ):
832
1043
  stream_kwargs["context"] = native_context
833
1044
  async for event in self._agent.astream_events(stream_input, **stream_kwargs):
834
1045
  event_kind = event.get("event", "")
@@ -855,7 +1066,7 @@ class LangGraphRunner(BaseRunner):
855
1066
  reasoning = getattr(chunk, "reasoning_content", None)
856
1067
  if not reasoning and hasattr(chunk, "additional_kwargs"):
857
1068
  reasoning = chunk.additional_kwargs.get("reasoning_content")
858
-
1069
+
859
1070
  if reasoning:
860
1071
  accumulated_reasoning += reasoning
861
1072
  yield {"delta": reasoning, "type": "thinking"}
@@ -865,9 +1076,9 @@ class LangGraphRunner(BaseRunner):
865
1076
  content = self._filter_tool_tags(chunk.content)
866
1077
  if isinstance(content, str):
867
1078
  if accumulated_reasoning and content.startswith(accumulated_reasoning):
868
- content = content[len(accumulated_reasoning):]
1079
+ content = content[len(accumulated_reasoning) :]
869
1080
  elif reasoning and content.startswith(reasoning):
870
- content = content[len(reasoning):]
1081
+ content = content[len(reasoning) :]
871
1082
  if content:
872
1083
  for part in inline_reasoning_parser.feed(content):
873
1084
  if not part.text or not part.text.strip():
@@ -888,6 +1099,58 @@ class LangGraphRunner(BaseRunner):
888
1099
  if run_key not in stream_usage_run_keys:
889
1100
  record_model_usage(event, last_usage or usage)
890
1101
 
1102
+ elif event_kind == "on_chain_stream":
1103
+ # node 内 get_stream_writer() 写入的自定义数据,经 stream_mode 含
1104
+ # "custom" 时,astream_events 包成 on_chain_stream,chunk 为
1105
+ # (mode, value) tuple:("custom", value) 是 writer 透传内容,
1106
+ # ("values", state) 是 state 快照(忽略,终态走 on_chain_end)。
1107
+ # 编排方常用 custom writer 把"调远端 agent/子图"的流式增量透传出来。
1108
+ chunk = event.get("data", {}).get("chunk")
1109
+ if not (isinstance(chunk, tuple) and len(chunk) == 2 and chunk[0] == "custom"):
1110
+ continue
1111
+ data = chunk[1]
1112
+ if isinstance(data, str):
1113
+ accumulated_text += data
1114
+ yield {"delta": data, "type": "text"}
1115
+ continue
1116
+ if isinstance(data, Mapping):
1117
+ custom_type = str(data.get("type") or "text")
1118
+ if custom_type in ("tool_call", "tool_result"):
1119
+ # 结构化工具事件:透传完整 payload(tool_name/tool_args/
1120
+ # tool_output 等),不计入正文,供 UI 渲染工具卡片。
1121
+ out = {"type": custom_type}
1122
+ out.update({k: v for k, v in data.items() if k != "type"})
1123
+ yield out
1124
+ continue
1125
+ custom_delta = ""
1126
+ for key in ("delta", "text", "content", "output", "data"):
1127
+ value = data.get(key)
1128
+ if isinstance(value, str) and value:
1129
+ custom_delta = value
1130
+ break
1131
+ if not custom_delta:
1132
+ continue
1133
+ replace = bool(data.get("replace"))
1134
+ if custom_type == "thinking":
1135
+ accumulated_reasoning = (
1136
+ custom_delta if replace else accumulated_reasoning + custom_delta
1137
+ )
1138
+ else:
1139
+ accumulated_text = (
1140
+ custom_delta if replace else accumulated_text + custom_delta
1141
+ )
1142
+ custom_event: dict[str, Any] = {
1143
+ "delta": custom_delta,
1144
+ "type": custom_type,
1145
+ }
1146
+ if replace:
1147
+ custom_event["replace"] = True
1148
+ yield custom_event
1149
+ continue
1150
+ if data is not None:
1151
+ accumulated_text += str(data)
1152
+ yield {"delta": str(data), "type": "text"}
1153
+
891
1154
  elif event_kind == "on_tool_start":
892
1155
  emitted_non_text_event = True
893
1156
  yield {
@@ -896,23 +1159,35 @@ class LangGraphRunner(BaseRunner):
896
1159
  "tool_args": event.get("data", {}).get("input", {}),
897
1160
  "run_id": event.get("run_id"),
898
1161
  }
899
-
1162
+
900
1163
  elif event_kind == "on_tool_end":
901
1164
  emitted_non_text_event = True
902
1165
  tool_output = event.get("data", {}).get("output", "")
1166
+ # LangGraph returns a ToolMessage here for normal tools.
1167
+ # Preserve its content instead of serializing the repr,
1168
+ # otherwise structured output such as A2UI envelopes becomes
1169
+ # unparsable. Keep the callback run_id below: it is paired
1170
+ # with the preceding ``on_tool_start`` event on this stream.
1171
+ normalized_output = getattr(tool_output, "content", tool_output)
1172
+ if isinstance(tool_output, Mapping) and "content" in tool_output:
1173
+ normalized_output = tool_output["content"]
903
1174
  yield {
904
1175
  "type": "tool_result",
905
1176
  "tool_name": event.get("name", "unknown"),
906
1177
  "tool_args": event.get("data", {}).get("input", {}),
907
- "tool_output": tool_output if isinstance(tool_output, dict) else (str(tool_output) if tool_output else ""),
1178
+ "tool_output": normalized_output,
908
1179
  "run_id": event.get("run_id"),
909
1180
  }
910
-
1181
+
911
1182
  elif event_kind == "on_chain_end":
912
1183
  output = event.get("data", {}).get("output", {})
913
1184
  if isinstance(output, dict) and "__interrupt__" in output:
914
1185
  emitted_non_text_event = True
915
- yield {"type": "interrupt", "interrupt_info": output["__interrupt__"], "session_id": session_id}
1186
+ yield {
1187
+ "type": "interrupt",
1188
+ "interrupt_info": output["__interrupt__"],
1189
+ "session_id": session_id,
1190
+ }
916
1191
  return
917
1192
  extracted_output = self._extract_output(output)
918
1193
  if extracted_output:
@@ -922,10 +1197,41 @@ class LangGraphRunner(BaseRunner):
922
1197
 
923
1198
  except Exception as e:
924
1199
  if "Interrupt" in type(e).__name__:
925
- yield {"type": "interrupt", "interrupt_info": self._get_interrupt_info(self._agent.get_state(config)), "session_id": session_id}
1200
+ yield {
1201
+ "type": "interrupt",
1202
+ "interrupt_info": self._get_interrupt_info(self._agent.get_state(config)),
1203
+ "session_id": session_id,
1204
+ }
926
1205
  return
927
1206
  raise
928
1207
 
1208
+ # goal-18(ksadk-web 人机交互):图因审批门(HITL)在流式中静默暂停时,
1209
+ # 这里把审批详情(action_requests)作为 approval 事件冒出,供 UI 渲染审批卡。
1210
+ # 此前流式路径只在 checkpoint 标 resumable,UI 拿不到"该批哪个工具/什么参数/允许哪些决定"。
1211
+ # 注:get_state 在部分 agent 上是 async,统一按 awaitable 处理;取不到则跳过,不破坏事件流。
1212
+ pending_approval = None
1213
+ try:
1214
+ _get_state = getattr(self._agent, "aget_state", None) or getattr(
1215
+ self._agent, "get_state", None
1216
+ )
1217
+ if _get_state is not None:
1218
+ _maybe_state = _get_state(config)
1219
+ if inspect.isawaitable(_maybe_state):
1220
+ _maybe_state = await _maybe_state
1221
+ pending_approval = self._get_interrupt_info(_maybe_state)
1222
+ except Exception:
1223
+ pending_approval = None
1224
+ if pending_approval:
1225
+ yield {
1226
+ "type": "approval",
1227
+ "interrupt_info": pending_approval,
1228
+ "session_id": session_id,
1229
+ }
1230
+ metadata = await self._latest_checkpoint_metadata(config)
1231
+ if metadata:
1232
+ yield {"type": "checkpoint", "metadata": metadata}
1233
+ return
1234
+
929
1235
  for part in inline_reasoning_parser.flush():
930
1236
  if not part.text or not part.text.strip():
931
1237
  continue
@@ -940,35 +1246,49 @@ class LangGraphRunner(BaseRunner):
940
1246
  if final_output_text:
941
1247
  final_chunk = {"output": final_output_text, "type": "final"}
942
1248
  usage = accumulated_model_usage() or final_output_usage or latest_stream_usage
943
- last_usage = latest_model_usage() or final_output_last_usage or latest_stream_usage or usage
1249
+ last_usage = (
1250
+ latest_model_usage() or final_output_last_usage or latest_stream_usage or usage
1251
+ )
944
1252
  if usage:
945
1253
  final_chunk["usage"] = usage
946
1254
  if last_usage:
947
1255
  final_chunk.setdefault("metadata", {})["last_usage"] = last_usage
948
1256
  yield final_chunk
949
1257
  elif not emitted_non_text_event:
950
- result = await self.invoke(
951
- {**invoke_payload, "_ksadk_force_graph_invoke": True}
952
- )
953
- final_chunk = {"output": result.get("output", ""), "type": "final"}
1258
+ result = await self.invoke({**invoke_payload, "_ksadk_force_graph_invoke": True})
1259
+ fallback_chunk: dict[str, Any] = {
1260
+ "output": result.get("output", ""),
1261
+ "type": "final",
1262
+ }
954
1263
  usage = self._extract_usage(result)
955
1264
  if usage:
956
- final_chunk["usage"] = usage
1265
+ fallback_chunk["usage"] = usage
957
1266
  last_usage = self._extract_last_usage(result)
958
1267
  if last_usage:
959
- final_chunk.setdefault("metadata", {})["last_usage"] = last_usage
960
- yield final_chunk
961
- metadata = result.get("metadata") if isinstance(result, dict) else None
962
- if isinstance(metadata, dict) and metadata.get("agentengine"):
963
- yield {"type": "checkpoint", "metadata": metadata}
1268
+ fallback_chunk.setdefault("metadata", {})["last_usage"] = last_usage
1269
+ yield fallback_chunk
1270
+ checkpoint_metadata = result.get("metadata") if isinstance(result, dict) else None
1271
+ if isinstance(checkpoint_metadata, dict) and checkpoint_metadata.get("agentengine"):
1272
+ yield {"type": "checkpoint", "metadata": checkpoint_metadata}
964
1273
  return
965
1274
  else:
966
1275
  final_chunk = {"output": accumulated_text, "type": "final"}
967
1276
  state_usage = await self._latest_state_usage(config)
968
- usage = accumulated_model_usage() or state_usage or final_output_usage or latest_stream_usage
1277
+ usage = (
1278
+ accumulated_model_usage()
1279
+ or state_usage
1280
+ or final_output_usage
1281
+ or latest_stream_usage
1282
+ )
969
1283
  if usage:
970
1284
  final_chunk["usage"] = usage
971
- last_usage = latest_model_usage() or state_usage or final_output_last_usage or latest_stream_usage or usage
1285
+ last_usage = (
1286
+ latest_model_usage()
1287
+ or state_usage
1288
+ or final_output_last_usage
1289
+ or latest_stream_usage
1290
+ or usage
1291
+ )
972
1292
  final_chunk.setdefault("metadata", {})["last_usage"] = last_usage
973
1293
  yield final_chunk
974
1294
 
@@ -980,6 +1300,6 @@ class LangGraphRunner(BaseRunner):
980
1300
  """过滤 <tool_call> 标签"""
981
1301
  if not isinstance(content, str):
982
1302
  return content
983
- content = re.sub(r'<tool_call>.*?</tool_call>', '', content, flags=re.DOTALL)
984
- content = re.sub(r'</?(?:tool_call|arg_key|arg_value)>', '', content)
1303
+ content = re.sub(r"<tool_call>.*?</tool_call>", "", content, flags=re.DOTALL)
1304
+ content = re.sub(r"</?(?:tool_call|arg_key|arg_value)>", "", content)
985
1305
  return content