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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (371) hide show
  1. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/METADATA +36 -20
  2. agentengine_sdk_python-0.8.0.dist-info/RECORD +578 -0
  3. ksadk/a2a/__init__.py +178 -11
  4. ksadk/a2a/bootstrap.py +334 -0
  5. ksadk/a2a/card.py +120 -0
  6. ksadk/a2a/context_store.py +203 -0
  7. ksadk/a2a/control_plane.py +818 -0
  8. ksadk/a2a/credential.py +99 -0
  9. ksadk/a2a/event_adapter.py +149 -0
  10. ksadk/a2a/executor.py +406 -89
  11. ksadk/a2a/external_transport.py +516 -0
  12. ksadk/a2a/identity.py +304 -0
  13. ksadk/a2a/ids.py +22 -0
  14. ksadk/a2a/langgraph.py +285 -0
  15. ksadk/a2a/resume_store.py +201 -0
  16. ksadk/a2a/routes.py +105 -0
  17. ksadk/a2a/server.py +107 -60
  18. ksadk/a2a/space_client.py +1101 -0
  19. ksadk/a2a/task_adapter.py +423 -0
  20. ksadk/a2a/task_event_dispatcher.py +167 -0
  21. ksadk/a2a/task_event_outbox.py +326 -0
  22. ksadk/a2a/task_store.py +206 -0
  23. ksadk/a2ui/__init__.py +27 -0
  24. ksadk/a2ui/core.py +186 -0
  25. ksadk/a2ui/models.py +166 -0
  26. ksadk/a2ui/renderer.py +198 -0
  27. ksadk/agui/__init__.py +9 -0
  28. ksadk/agui/a2ui_projection.py +92 -0
  29. ksadk/agui/agent.py +857 -0
  30. ksadk/agui/config.py +86 -0
  31. ksadk/agui/routes.py +55 -0
  32. ksadk/api/client.py +233 -191
  33. ksadk/builders/__init__.py +2 -0
  34. ksadk/builders/base.py +16 -14
  35. ksadk/builders/code_builder.py +318 -236
  36. ksadk/builders/container_builder.py +187 -167
  37. ksadk/builders/framework_requirements.py +29 -11
  38. ksadk/builders/ks3_uploader.py +58 -27
  39. ksadk/builders/managed_runtime_builder.py +145 -0
  40. ksadk/builders/mcp_builder.py +91 -92
  41. ksadk/builders/requirements_utils.py +1 -5
  42. ksadk/cli/__init__.py +89 -171
  43. ksadk/cli/cmd_a2a.py +254 -70
  44. ksadk/cli/cmd_agent.py +19 -5
  45. ksadk/cli/cmd_build.py +158 -29
  46. ksadk/cli/cmd_completion.py +47 -35
  47. ksadk/cli/cmd_config.py +267 -198
  48. ksadk/cli/cmd_create.py +500 -720
  49. ksadk/cli/cmd_dashboard.py +120 -45
  50. ksadk/cli/cmd_deploy.py +117 -58
  51. ksadk/cli/cmd_destroy.py +44 -14
  52. ksadk/cli/cmd_files.py +63 -24
  53. ksadk/cli/cmd_hermes.py +168 -89
  54. ksadk/cli/cmd_invoke.py +184 -122
  55. ksadk/cli/cmd_launch.py +64 -28
  56. ksadk/cli/cmd_mcp.py +98 -64
  57. ksadk/cli/cmd_model.py +14 -5
  58. ksadk/cli/cmd_openclaw.py +483 -227
  59. ksadk/cli/cmd_replay.py +71 -0
  60. ksadk/cli/cmd_run.py +33 -10
  61. ksadk/cli/cmd_status.py +80 -31
  62. ksadk/cli/cmd_version.py +20 -13
  63. ksadk/cli/cmd_web.py +89 -13
  64. ksadk/cli/deploy_utils.py +20 -20
  65. ksadk/cli/dry_run.py +5 -3
  66. ksadk/cli/error_utils.py +37 -12
  67. ksadk/cli/global_options.py +1 -2
  68. ksadk/cli/local_runtime.py +1 -2
  69. ksadk/cli/model_catalog.py +6 -2
  70. ksadk/cli/network_options.py +34 -14
  71. ksadk/cli/resource_common.py +6 -2
  72. ksadk/cli/storage.py +0 -1
  73. ksadk/cli/ui.py +5 -3
  74. ksadk/cli/workflow_common.py +51 -17
  75. ksadk/codex/__init__.py +12 -0
  76. ksadk/codex/client.py +408 -0
  77. ksadk/codex/phase.py +143 -0
  78. ksadk/codex/runtime.py +449 -0
  79. ksadk/common/aicp_env.py +0 -1
  80. ksadk/common/auth.py +2 -2
  81. ksadk/common/constants.py +1 -3
  82. ksadk/common/llm_utils.py +17 -13
  83. ksadk/compat/__init__.py +5 -0
  84. ksadk/compat/adk_compat.py +200 -0
  85. ksadk/compat/copilotkit_a2ui.py +330 -0
  86. ksadk/configs/__init__.py +12 -12
  87. ksadk/configs/env_registry.py +500 -100
  88. ksadk/configs/global_config.py +2 -1
  89. ksadk/configs/settings.py +151 -124
  90. ksadk/conversations/__init__.py +7 -5
  91. ksadk/conversations/attachment_storage.py +16 -11
  92. ksadk/conversations/attachments.py +44 -36
  93. ksadk/conversations/compaction_pipeline.py +12 -12
  94. ksadk/conversations/compaction_prompt.py +9 -5
  95. ksadk/conversations/context.py +221 -6
  96. ksadk/conversations/message_projection.py +438 -51
  97. ksadk/conversations/model_context.py +12 -6
  98. ksadk/conversations/model_options.py +0 -1
  99. ksadk/conversations/normalize.py +20 -16
  100. ksadk/conversations/reasoning_markup.py +1 -1
  101. ksadk/conversations/runtime.py +339 -5255
  102. ksadk/conversations/runtime_compaction.py +333 -0
  103. ksadk/conversations/runtime_constants.py +27 -0
  104. ksadk/conversations/runtime_governance.py +185 -0
  105. ksadk/conversations/runtime_input.py +652 -0
  106. ksadk/conversations/runtime_invocation.py +395 -0
  107. ksadk/conversations/runtime_metadata.py +475 -0
  108. ksadk/conversations/runtime_observability.py +355 -0
  109. ksadk/conversations/runtime_payloads.py +272 -0
  110. ksadk/conversations/runtime_persistence.py +440 -0
  111. ksadk/conversations/runtime_preparation.py +418 -0
  112. ksadk/conversations/runtime_resume.py +816 -0
  113. ksadk/conversations/runtime_stream_events.py +902 -0
  114. ksadk/conversations/runtime_streaming.py +547 -0
  115. ksadk/conversations/semantic_summary.py +17 -5
  116. ksadk/conversations/session_title.py +6 -3
  117. ksadk/deployment/__init__.py +15 -16
  118. ksadk/deployment/agent_access.py +12 -23
  119. ksadk/deployment/base.py +51 -38
  120. ksadk/deployment/manager.py +63 -65
  121. ksadk/deployment/providers/serverless.py +309 -202
  122. ksadk/deployment/registry.py +38 -32
  123. ksadk/deployment/state.py +11 -11
  124. ksadk/deployment/ui_config.py +1 -1
  125. ksadk/detection/__init__.py +1 -1
  126. ksadk/detection/detector.py +146 -83
  127. ksadk/detection/mcp_detector.py +9 -6
  128. ksadk/events/__init__.py +25 -0
  129. ksadk/events/parser.py +191 -0
  130. ksadk/events/replay.py +37 -0
  131. ksadk/events/runtime_event.py +264 -0
  132. ksadk/events/store.py +317 -0
  133. ksadk/harness/__init__.py +34 -0
  134. ksadk/harness/app.py +278 -0
  135. ksadk/harness/config.py +181 -0
  136. ksadk/harness/reasoner.py +105 -0
  137. ksadk/harness/runner.py +211 -0
  138. ksadk/harness/sandbox.py +141 -0
  139. ksadk/harness/tools.py +222 -0
  140. ksadk/hermes_terminal.py +23 -12
  141. ksadk/identity/resolver.py +15 -9
  142. ksadk/ids.py +32 -0
  143. ksadk/knowledge_base/adk_tool.py +1 -1
  144. ksadk/knowledge_base/client.py +23 -29
  145. ksadk/managed_runtime.py +188 -0
  146. ksadk/markdown.py +5 -2
  147. ksadk/mcp_runtime/__init__.py +9 -6
  148. ksadk/memory/__init__.py +1 -1
  149. ksadk/memory/adk/__init__.py +2 -0
  150. ksadk/memory/adk/backends/base_ltm_backend.py +2 -6
  151. ksadk/memory/adk/backends/http_ltm_backend.py +13 -21
  152. ksadk/memory/adk/backends/inmemory_ltm_backend.py +7 -13
  153. ksadk/memory/adk/backends/sdk_ltm_backend.py +26 -33
  154. ksadk/memory/adk/long_term_memory.py +31 -35
  155. ksadk/memory/adk/resilient_session_service.py +4 -5
  156. ksadk/memory/adk/short_term_memory.py +12 -21
  157. ksadk/memory/adk_tool.py +3 -2
  158. ksadk/memory/backends/base.py +19 -19
  159. ksadk/memory/backends/memory.py +24 -25
  160. ksadk/memory/backends/redis.py +31 -30
  161. ksadk/memory/langchain_tool.py +2 -1
  162. ksadk/memory/manager.py +52 -47
  163. ksadk/memory/service.py +10 -8
  164. ksadk/model_policy.py +15 -7
  165. ksadk/model_proxy/__init__.py +51 -0
  166. ksadk/model_proxy/bootstrap.py +80 -0
  167. ksadk/model_proxy/cache.py +165 -0
  168. ksadk/model_proxy/config.py +45 -0
  169. ksadk/model_proxy/demo_webui.py +115 -0
  170. ksadk/model_proxy/detect.py +145 -0
  171. ksadk/model_proxy/gate.py +68 -0
  172. ksadk/model_proxy/namespace.py +173 -0
  173. ksadk/model_proxy/protocol_proxy.py +22 -0
  174. ksadk/model_proxy/server.py +300 -0
  175. ksadk/model_proxy/transform.py +691 -0
  176. ksadk/openclaw_gateway.py +29 -14
  177. ksadk/runners/adk_runner.py +345 -120
  178. ksadk/runners/base_runner.py +46 -29
  179. ksadk/runners/codex_runner.py +132 -0
  180. ksadk/runners/deepagents_runner.py +0 -1
  181. ksadk/runners/factory.py +17 -5
  182. ksadk/runners/langchain_runner.py +29 -742
  183. ksadk/runners/langgraph_runner.py +352 -90
  184. ksadk/runners/patch_langchain.py +16 -16
  185. ksadk/runners/remote_runner.py +134 -33
  186. ksadk/runners/usage_accumulator.py +1 -0
  187. ksadk/runners/utils/langfuse.py +3 -3
  188. ksadk/runners/utils/loader.py +7 -7
  189. ksadk/runtime/__init__.py +41 -0
  190. ksadk/runtime/adapter.py +345 -0
  191. ksadk/runtime/framework_adapters.py +124 -0
  192. ksadk/runtime/preprocessing.py +118 -0
  193. ksadk/runtime/runner_adapter.py +873 -0
  194. ksadk/runtime_context.py +18 -3
  195. ksadk/runtime_state.py +0 -1
  196. ksadk/sandbox/__init__.py +4 -1
  197. ksadk/sandbox/backends/__init__.py +4 -1
  198. ksadk/sandbox/backends/e2b.py +13 -5
  199. ksadk/sandbox/backends/local_process.py +11 -3
  200. ksadk/sandbox/base.py +10 -15
  201. ksadk/sandbox/factory.py +14 -4
  202. ksadk/sandbox/registry.py +57 -6
  203. ksadk/server/api_models.py +15 -5
  204. ksadk/server/app.py +455 -3885
  205. ksadk/server/composition.py +21 -0
  206. ksadk/server/factory.py +624 -0
  207. ksadk/server/routes/__init__.py +1 -0
  208. ksadk/server/routes/checkpoint_resolution.py +116 -0
  209. ksadk/server/routes/common.py +713 -0
  210. ksadk/server/routes/control.py +327 -0
  211. ksadk/server/routes/dependencies.py +98 -0
  212. ksadk/server/routes/misc.py +448 -0
  213. ksadk/server/routes/models.py +443 -0
  214. ksadk/server/routes/openai_compat.py +207 -0
  215. ksadk/server/routes/projection.py +795 -0
  216. ksadk/server/routes/routers.py +52 -0
  217. ksadk/server/routes/run.py +726 -0
  218. ksadk/server/routes/sessions.py +649 -0
  219. ksadk/server/routes/streaming.py +223 -0
  220. ksadk/server/routes/workspace.py +381 -0
  221. ksadk/server/static/assets/{ArtifactsPanel-D-SSn2ee.js → ArtifactsPanel-Cjnqt-8W.js} +1 -1
  222. ksadk/server/static/assets/CodeBlock-BqyqZfuB.js +9 -0
  223. ksadk/server/static/assets/MathMessageMarkdown-BpH7jiFM.js +8 -0
  224. ksadk/server/static/assets/MermaidBlock-DWuxETaJ.js +303 -0
  225. ksadk/server/static/assets/NativeTerminalPanel-MDTopWMO.js +1 -0
  226. ksadk/server/static/assets/{arc-BA13sHc9.js → arc-CFKwSk_B.js} +1 -1
  227. ksadk/server/static/assets/architecture-7EHR7CIX-CynqOWjZ.js +1 -0
  228. ksadk/server/static/assets/{architectureDiagram-3BPJPVTR-DzJzgVoL.js → architectureDiagram-3BPJPVTR-Dej8jX8c.js} +1 -1
  229. ksadk/server/static/assets/{blockDiagram-GPEHLZMM-UTdVcZhn.js → blockDiagram-GPEHLZMM-1F64jqvy.js} +1 -1
  230. ksadk/server/static/assets/{c4Diagram-AAUBKEIU-Dix0GD4m.js → c4Diagram-AAUBKEIU-9q7AUAKb.js} +1 -1
  231. ksadk/server/static/assets/channel-iUGv2N24.js +1 -0
  232. ksadk/server/static/assets/{chunk-2J33WTMH-C2JxuZ42.js → chunk-2J33WTMH-6qgwJm7Q.js} +1 -1
  233. ksadk/server/static/assets/{chunk-4BX2VUAB-Be57QPgD.js → chunk-4BX2VUAB-CsWIQRhc.js} +1 -1
  234. ksadk/server/static/assets/{chunk-55IACEB6-WjSuy4nv.js → chunk-55IACEB6-BK8paA20.js} +1 -1
  235. ksadk/server/static/assets/{chunk-727SXJPM-BOovP-b5.js → chunk-727SXJPM-B-JEhy7h.js} +1 -1
  236. ksadk/server/static/assets/{chunk-AQP2D5EJ-DOmZNmWj.js → chunk-AQP2D5EJ-DYBvMHAa.js} +1 -1
  237. ksadk/server/static/assets/{chunk-FMBD7UC4-DzwlodkU.js → chunk-FMBD7UC4-BGhvzDYt.js} +1 -1
  238. ksadk/server/static/assets/{chunk-ND2GUHAM-DMqAikEU.js → chunk-ND2GUHAM-D98vPd-E.js} +1 -1
  239. ksadk/server/static/assets/chunk-QZHKN3VN-YQIEXeF6.js +1 -0
  240. ksadk/server/static/assets/classDiagram-4FO5ZUOK-CDAUuoXK.js +1 -0
  241. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-CDAUuoXK.js +1 -0
  242. ksadk/server/static/assets/{cose-bilkent-S5V4N54A-Zhy2GBnP.js → cose-bilkent-S5V4N54A-CX3yotHq.js} +1 -1
  243. ksadk/server/static/assets/{dagre-BM42HDAG-CbKrtIUz.js → dagre-BM42HDAG-C7TguAWo.js} +1 -1
  244. ksadk/server/static/assets/{diagram-2AECGRRQ-BRApgQRK.js → diagram-2AECGRRQ-Dd17_kws.js} +1 -1
  245. ksadk/server/static/assets/{diagram-5GNKFQAL-D0xbnDiK.js → diagram-5GNKFQAL-DVIClIil.js} +1 -1
  246. ksadk/server/static/assets/{diagram-KO2AKTUF-CVugVI9L.js → diagram-KO2AKTUF-Df_3gYQa.js} +1 -1
  247. ksadk/server/static/assets/{diagram-LMA3HP47-DozUGgee.js → diagram-LMA3HP47-BhLk6ZtA.js} +1 -1
  248. ksadk/server/static/assets/{diagram-OG6HWLK6-ZOYsKlru.js → diagram-OG6HWLK6-D4GNGvuu.js} +1 -1
  249. ksadk/server/static/assets/{dist-DmFaq_Iy.js → dist-6CVKuTAe.js} +1 -1
  250. ksadk/server/static/assets/{dist-8EEyGgDx.js → dist-BUHRxvDG.js} +1 -1
  251. ksadk/server/static/assets/{dist-B0NP_Z0z.js → dist-BZ9n9AjO.js} +1 -1
  252. ksadk/server/static/assets/{dist-DMezvBGJ.js → dist-BibcG1V9.js} +1 -1
  253. ksadk/server/static/assets/{dist-C3HIIaN5.js → dist-Bs-jQcRX.js} +1 -1
  254. ksadk/server/static/assets/{dist-BrCcQ47T.js → dist-C2xjE7cO.js} +1 -1
  255. ksadk/server/static/assets/{dist-Dp0k7bWI.js → dist-C7-3jInC.js} +1 -1
  256. ksadk/server/static/assets/{dist-D-xOD048.js → dist-C99THtIA.js} +1 -1
  257. ksadk/server/static/assets/{dist-CkEKJFtk.js → dist-CGcp2ex8.js} +1 -1
  258. ksadk/server/static/assets/{dist-DyOkZmze.js → dist-CVQRZjPu.js} +1 -1
  259. ksadk/server/static/assets/{dist-BssVmjIo.js → dist-Cf23NY4L.js} +1 -1
  260. ksadk/server/static/assets/{dist-BIJVDMuB.js → dist-CgF3fj15.js} +1 -1
  261. ksadk/server/static/assets/{dist-2hInLS_J.js → dist-Cwt7koer.js} +1 -1
  262. ksadk/server/static/assets/{dist-DyWsb78Q.js → dist-D82mtD2s.js} +1 -1
  263. ksadk/server/static/assets/{dist-D-JMdWPQ.js → dist-DF06ORQo.js} +1 -1
  264. ksadk/server/static/assets/{dist-BmlMPsCc.js → dist-DHn6nBzr.js} +1 -1
  265. ksadk/server/static/assets/{dist-gX6GOLKq.js → dist-Djgl2wVZ.js} +1 -1
  266. ksadk/server/static/assets/{dist-DpKtt4Bo.js → dist-DlzCRy0S.js} +1 -1
  267. ksadk/server/static/assets/{dist-8_SfK1IP.js → dist-DqNnNFrw.js} +1 -1
  268. ksadk/server/static/assets/{dist-D57wnGWd.js → dist-DyvYLMW9.js} +1 -1
  269. ksadk/server/static/assets/{dist-CPBtqRTW.js → dist-eoW6tZXA.js} +1 -1
  270. ksadk/server/static/assets/{dist-DL_NKc_z.js → dist-ivOya1LD.js} +1 -1
  271. ksadk/server/static/assets/{erDiagram-TEJ5UH35-Bk-9bgE5.js → erDiagram-TEJ5UH35-CnfRcY2M.js} +1 -1
  272. ksadk/server/static/assets/eventmodeling-FCH6USID-ChYO-ijM.js +1 -0
  273. ksadk/server/static/assets/{flowDiagram-I6XJVG4X-Dn9kZVuZ.js → flowDiagram-I6XJVG4X-DGaJjGNJ.js} +1 -1
  274. ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BGxXlJqm.js → ganttDiagram-6RSMTGT7-DpCoB2g9.js} +1 -1
  275. ksadk/server/static/assets/gitGraph-WXDBUCRP-BN_0Fk-j.js +1 -0
  276. ksadk/server/static/assets/{gitGraphDiagram-PVQCEYII-C7Q0X-wi.js → gitGraphDiagram-PVQCEYII-9IKVFBVT.js} +1 -1
  277. ksadk/server/static/assets/index-QSwCHuB8.js +177 -0
  278. ksadk/server/static/assets/index-jOPmTOZH.css +2 -0
  279. ksadk/server/static/assets/info-J43DQDTF-Cp6o3vId.js +1 -0
  280. ksadk/server/static/assets/{infoDiagram-5YYISTIA-BO_CSqq5.js → infoDiagram-5YYISTIA-CYIIsb8y.js} +1 -1
  281. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-eYLPLd46.js → ishikawaDiagram-YF4QCWOH-DLOHGvWw.js} +1 -1
  282. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-De9geSb0.js → journeyDiagram-JHISSGLW-BIzKeejT.js} +1 -1
  283. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-Cs80kQto.js → kanban-definition-UN3LZRKU-BWHoFSw2.js} +1 -1
  284. ksadk/server/static/assets/{katex-DSrCFD8n.js → katex-DATssjp1.js} +1 -1
  285. ksadk/server/static/assets/{linear-CN9sbKTF.js → linear-CvMBw9cA.js} +1 -1
  286. ksadk/server/static/assets/{mermaid-parser.core-DlgSBU8l.js → mermaid-parser.core-mGAcEwcq.js} +2 -2
  287. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-9afz7Oug.js → mindmap-definition-RKZ34NQL-CSAQxEAe.js} +1 -1
  288. ksadk/server/static/assets/packet-YPE3B663-wQzct3cT.js +1 -0
  289. ksadk/server/static/assets/pie-LRSECV5Y-BgTQiv-9.js +1 -0
  290. ksadk/server/static/assets/{pieDiagram-4H26LBE5-DPv_VstN.js → pieDiagram-4H26LBE5-By-ra07M.js} +1 -1
  291. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-o77wZ1XK.js → quadrantDiagram-W4KKPZXB-CxIR4JXI.js} +1 -1
  292. ksadk/server/static/assets/radar-GUYGQ44K-BIq5fXsn.js +1 -0
  293. ksadk/server/static/assets/{requirementDiagram-4Y6WPE33-BYVw81VC.js → requirementDiagram-4Y6WPE33-DpW90iu3.js} +1 -1
  294. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-B7nZFMKT.js → sankeyDiagram-5OEKKPKP-DpGa6EAV.js} +1 -1
  295. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-BGRRrFw5.js → sequenceDiagram-3UESZ5HK-NTKNGGe5.js} +1 -1
  296. ksadk/server/static/assets/{stateDiagram-AJRCARHV-BUfHCoPs.js → stateDiagram-AJRCARHV-DGpd9yFG.js} +1 -1
  297. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-B3o579IQ.js +1 -0
  298. ksadk/server/static/assets/{timeline-definition-PNZ67QCA-FbCTGrf9.js → timeline-definition-PNZ67QCA-jqoYs3jQ.js} +1 -1
  299. ksadk/server/static/assets/treeView-BLDUP644-Dqi-E0EM.js +1 -0
  300. ksadk/server/static/assets/treemap-LRROVOQU-ClQP9CJ9.js +1 -0
  301. ksadk/server/static/assets/{vennDiagram-CIIHVFJN-DWgwtPsJ.js → vennDiagram-CIIHVFJN-l3Z7-wbP.js} +1 -1
  302. ksadk/server/static/assets/wardley-L42UT6IY-8XLvnNIs.js +1 -0
  303. ksadk/server/static/assets/{wardleyDiagram-YWT4CUSO-2x8G-tTM.js → wardleyDiagram-YWT4CUSO-BR54GPmr.js} +1 -1
  304. ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-Drma3AYl.js → xychartDiagram-2RQKCTM6-SkiH8GsK.js} +1 -1
  305. ksadk/server/static/index.html +2 -2
  306. ksadk/server/terminal_sessions.py +111 -58
  307. ksadk/sessions/__init__.py +69 -19
  308. ksadk/sessions/base.py +31 -11
  309. ksadk/sessions/continuity.py +2 -3
  310. ksadk/sessions/in_memory.py +46 -2
  311. ksadk/sessions/local_service.py +148 -17
  312. ksadk/sessions/postgres_service.py +143 -25
  313. ksadk/sessions/resilient.py +36 -8
  314. ksadk/skills/models.py +17 -5
  315. ksadk/skills/package_store.py +5 -2
  316. ksadk/skills/runtime/agent.py +20 -5
  317. ksadk/skills/runtime/artifacts.py +1 -5
  318. ksadk/skills/runtime/backends/e2b.py +10 -4
  319. ksadk/skills/runtime/backends/local.py +15 -3
  320. ksadk/skills/runtime/base.py +1 -2
  321. ksadk/skills/runtime/executor.py +10 -3
  322. ksadk/skills/runtime/factory.py +4 -2
  323. ksadk/skills/runtime/loader.py +1 -2
  324. ksadk/skills/runtime/registry.py +15 -10
  325. ksadk/skills/runtime/request.py +3 -1
  326. ksadk/skills/service_client.py +38 -17
  327. ksadk/skills/tool_defs.py +15 -10
  328. ksadk/terminal_exec_policy.py +3 -4
  329. ksadk/tools/gateway.py +92 -15
  330. ksadk/tools/result_budget.py +12 -4
  331. ksadk/toolsets/__init__.py +134 -36
  332. ksadk/toolsets/platform.py +21 -5
  333. ksadk/toolsets/sandbox.py +82 -16
  334. ksadk/toolsets/skills.py +200 -39
  335. ksadk/toolsets/web.py +123 -26
  336. ksadk/toolsets/workspace.py +212 -101
  337. ksadk/tracing/exporters/inmemory_exporter.py +62 -51
  338. ksadk/tracing/exporters/langfuse_exporter.py +91 -76
  339. ksadk/tracing/setup.py +69 -53
  340. ksadk/tracing/span_utils.py +5 -2
  341. ksadk/tui/loop.py +78 -30
  342. ksadk/tui/stream_render.py +1 -0
  343. ksadk/ui_config.py +5 -3
  344. ksadk/version.py +1 -1
  345. ksadk_runtime_common/schemas/runtime_event_v1.json +95 -0
  346. agentengine_sdk_python-0.7.0.dist-info/RECORD +0 -482
  347. ksadk/server/static/assets/CodeBlock-P16VOLYi.js +0 -9
  348. ksadk/server/static/assets/MathMessageMarkdown-CdGiQWHR.js +0 -8
  349. ksadk/server/static/assets/MermaidBlock-IOcal8cm.js +0 -303
  350. ksadk/server/static/assets/NativeTerminalPanel-DGSDWsei.js +0 -1
  351. ksadk/server/static/assets/architecture-7EHR7CIX-BvUlsA6l.js +0 -1
  352. ksadk/server/static/assets/channel-CTuydARX.js +0 -1
  353. ksadk/server/static/assets/chunk-QZHKN3VN-BnCydivS.js +0 -1
  354. ksadk/server/static/assets/classDiagram-4FO5ZUOK-BNHg45Be.js +0 -1
  355. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-BNHg45Be.js +0 -1
  356. ksadk/server/static/assets/eventmodeling-FCH6USID-DsAgWnlf.js +0 -1
  357. ksadk/server/static/assets/gitGraph-WXDBUCRP-BIao9Yjg.js +0 -1
  358. ksadk/server/static/assets/index-C00Lwg3L.js +0 -165
  359. ksadk/server/static/assets/index-DAURbCsP.css +0 -1
  360. ksadk/server/static/assets/info-J43DQDTF-CyipqGxU.js +0 -1
  361. ksadk/server/static/assets/packet-YPE3B663-CGkyRxlg.js +0 -1
  362. ksadk/server/static/assets/pie-LRSECV5Y-M0uWAf4n.js +0 -1
  363. ksadk/server/static/assets/radar-GUYGQ44K-Bdbm24-x.js +0 -1
  364. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-8xBN_gLM.js +0 -1
  365. ksadk/server/static/assets/treeView-BLDUP644-npMo0ak1.js +0 -1
  366. ksadk/server/static/assets/treemap-LRROVOQU-CHdzrrdo.js +0 -1
  367. ksadk/server/static/assets/wardley-L42UT6IY-BEQz7wyy.js +0 -1
  368. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/WHEEL +0 -0
  369. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/entry_points.txt +0 -0
  370. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/licenses/LICENSE +0 -0
  371. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/top_level.txt +0 -0
@@ -4,25 +4,26 @@ 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 get_langfuse_callbacks, get_langfuse_metadata, 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
 
@@ -40,7 +41,7 @@ class LangGraphRunner(BaseRunner):
40
41
  self._loaded_model_name = self.normalize_requested_model(
41
42
  os.getenv("OPENAI_MODEL_NAME") or os.getenv("MODEL_NAME")
42
43
  )
43
-
44
+
44
45
  if not hasattr(self._agent, "invoke"):
45
46
  raise TypeError("加载的对象不是有效的 LangGraph CompiledGraph")
46
47
 
@@ -57,10 +58,10 @@ class LangGraphRunner(BaseRunner):
57
58
 
58
59
  def describe_checkpoint_capability(self) -> dict[str, Any]:
59
60
  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:
61
+ checkpointer = getattr(agent, "checkpointer", None)
62
+ if checkpointer is None:
63
+ checkpointer = getattr(agent, "_checkpointer", None)
64
+ if checkpointer is None:
64
65
  return {
65
66
  "Supported": False,
66
67
  "Backend": "none",
@@ -70,7 +71,16 @@ class LangGraphRunner(BaseRunner):
70
71
  "Reason": "LangGraph graph has no configured checkpointer",
71
72
  }
72
73
 
73
- backend = str(os.getenv("KSADK_CHECKPOINT_BACKEND") or "").strip().lower()
74
+ checkpointer_type = type(checkpointer)
75
+ type_name = f"{checkpointer_type.__module__}.{checkpointer_type.__name__}".lower()
76
+ if "memory" in type_name or "inmemory" in type_name:
77
+ backend = "memory"
78
+ elif "sqlite" in type_name:
79
+ backend = "sqlite"
80
+ elif "postgres" in type_name:
81
+ backend = "postgres"
82
+ else:
83
+ backend = str(os.getenv("KSADK_CHECKPOINT_BACKEND") or "").strip().lower()
74
84
  if backend == "local":
75
85
  backend = "sqlite"
76
86
  if not backend:
@@ -87,7 +97,9 @@ class LangGraphRunner(BaseRunner):
87
97
  scope = "pod_local"
88
98
  durable = True
89
99
  shared = False
90
- reason = "SQLite checkpoint is durable for local web debugging but is not shared across pods"
100
+ reason = (
101
+ "SQLite checkpoint is durable for local web debugging but is not shared across pods"
102
+ )
91
103
  elif backend in {"memory", "inmemory"}:
92
104
  backend = "memory"
93
105
  scope = "process_local"
@@ -109,25 +121,23 @@ class LangGraphRunner(BaseRunner):
109
121
  capabilities = super().get_runtime_capabilities()
110
122
  capabilities["SessionContinuity"] = {
111
123
  "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",
124
+ "Type": (
125
+ "checkpoint" if capabilities["Checkpoint"].get("Supported") else "semantic_replay"
126
+ ),
127
+ "Level": "runtime" if capabilities["Checkpoint"].get("Supported") else "semantic",
118
128
  "Reason": "",
119
129
  }
120
130
  return capabilities
121
131
 
122
132
  def _get_config(self, session_id: str) -> dict:
123
133
  """获取运行配置"""
124
- config = {"configurable": {"thread_id": session_id}}
125
-
134
+ config: dict[str, Any] = {"configurable": {"thread_id": session_id}}
135
+
126
136
  langfuse_callbacks = get_langfuse_callbacks()
127
137
  if langfuse_callbacks:
128
138
  config["callbacks"] = langfuse_callbacks
129
139
  config["metadata"] = get_langfuse_metadata(session_id)
130
-
140
+
131
141
  return config
132
142
 
133
143
  @staticmethod
@@ -154,7 +164,9 @@ class LangGraphRunner(BaseRunner):
154
164
 
155
165
  thread_id = str(checkpoint_ref.get("thread_id") or session_id or "").strip()
156
166
  if not thread_id:
157
- raise ValueError("checkpoint_resume requires session_id or framework_ref.langgraph.thread_id")
167
+ raise ValueError(
168
+ "checkpoint_resume requires session_id or framework_ref.langgraph.thread_id"
169
+ )
158
170
 
159
171
  next_config = dict(config)
160
172
  configurable = dict(next_config.get("configurable") or {})
@@ -164,6 +176,18 @@ class LangGraphRunner(BaseRunner):
164
176
  next_config["configurable"] = configurable
165
177
  return next_config
166
178
 
179
+ @staticmethod
180
+ def _checkpoint_resume_input(
181
+ value: Any,
182
+ *,
183
+ payload_provided: bool,
184
+ interrupt_id: str,
185
+ ) -> Command | None:
186
+ if not payload_provided:
187
+ return None
188
+ resume_value = {interrupt_id: value} if interrupt_id else value
189
+ return Command(resume=resume_value)
190
+
167
191
  @staticmethod
168
192
  def _checkpoint_ref_from_state(state: Any) -> dict[str, Any]:
169
193
  state_config = None
@@ -180,17 +204,15 @@ class LangGraphRunner(BaseRunner):
180
204
  checkpoint_id = str(configurable.get("checkpoint_id") or "").strip()
181
205
  if not thread_id or not checkpoint_id:
182
206
  return {}
183
- next_nodes_raw = state.get("next") if isinstance(state, dict) else getattr(state, "next", None)
207
+ next_nodes_raw = (
208
+ state.get("next") if isinstance(state, dict) else getattr(state, "next", None)
209
+ )
184
210
  next_nodes: list[str] = []
185
211
  if isinstance(next_nodes_raw, str):
186
212
  if next_nodes_raw.strip():
187
213
  next_nodes = [next_nodes_raw.strip()]
188
214
  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
- ]
215
+ next_nodes = [str(item).strip() for item in next_nodes_raw if str(item or "").strip()]
194
216
  return {
195
217
  "langgraph": {
196
218
  "thread_id": thread_id,
@@ -273,7 +295,11 @@ class LangGraphRunner(BaseRunner):
273
295
  def _ambient_context_text(payload: Dict[str, Any]) -> str:
274
296
  sections: list[str] = []
275
297
  kb_context = payload.get("kb_context") or {}
276
- kb_text = str(kb_context.get("formatted_text") or "").strip() if isinstance(kb_context, dict) else ""
298
+ kb_text = (
299
+ str(kb_context.get("formatted_text") or "").strip()
300
+ if isinstance(kb_context, dict)
301
+ else ""
302
+ )
277
303
  if kb_text:
278
304
  sections.append(f"Knowledge base context:\n{kb_text}")
279
305
 
@@ -325,6 +351,14 @@ class LangGraphRunner(BaseRunner):
325
351
  prepared = prepare_state(dict(normalized_payload), session_context)
326
352
  if not isinstance(prepared, dict):
327
353
  raise TypeError("ksadk_prepare_state(payload, session_context) must return a dict")
354
+ # A custom state hook owns application state, but must not accidentally
355
+ # erase protocol namespaces that framework middleware consumes. In
356
+ # particular CopilotKit reads ``ag-ui`` to decide whether to inject the
357
+ # official A2UI tool, and reads ``copilotkit.actions`` for frontend
358
+ # actions. Keep these namespaces narrow and transport-owned.
359
+ for namespace in ("ag-ui", "copilotkit"):
360
+ if namespace in normalized_payload:
361
+ prepared.setdefault(namespace, normalized_payload[namespace])
328
362
  return prepared
329
363
 
330
364
  def _to_state(self, payload: Dict[str, Any], history: list) -> Dict[str, Any]:
@@ -338,7 +372,7 @@ class LangGraphRunner(BaseRunner):
338
372
  if "input" in normalized_payload and "messages" not in normalized_payload:
339
373
  from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
340
374
 
341
- messages = []
375
+ messages: list[Any] = []
342
376
  if system_text:
343
377
  messages.append(SystemMessage(content=system_text))
344
378
  for msg in history:
@@ -442,11 +476,11 @@ class LangGraphRunner(BaseRunner):
442
476
  )
443
477
  continue
444
478
 
445
- file_uri = attachment.get("file_uri")
446
- if not file_uri:
479
+ attachment_uri = attachment.get("file_uri")
480
+ if not attachment_uri:
447
481
  continue
448
482
 
449
- raw = read_attachment_uri_bytes(file_uri)
483
+ raw = read_attachment_uri_bytes(str(attachment_uri))
450
484
  if not raw:
451
485
  continue
452
486
 
@@ -536,7 +570,7 @@ class LangGraphRunner(BaseRunner):
536
570
 
537
571
  async def invoke(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
538
572
  """调用 LangGraph 图
539
-
573
+
540
574
  支持两种输入格式:
541
575
  1. 简化格式: {"input": "hello"} - 自动转换为 messages
542
576
  2. 原生格式: {"messages": [...]} 或自定义 State - 直接透传
@@ -549,11 +583,12 @@ class LangGraphRunner(BaseRunner):
549
583
  session_id = payload.pop("session_id", None) or str(uuid.uuid4())[:8]
550
584
  is_resume = payload.pop("resume", False)
551
585
  is_checkpoint_resume = bool(payload.pop("checkpoint_resume", False))
586
+ resume_payload_provided = bool(payload.pop("resume_payload_provided", False))
587
+ resume_interrupt_id = str(payload.pop("resume_interrupt_id", "") or "")
588
+ resume_value = payload.get("input")
552
589
  checkpoint_ref = self._extract_langgraph_checkpoint_ref(payload)
553
590
  history = payload.pop("history", [])
554
591
  native_context = self.build_native_context(payload.get("platform_context"))
555
- normalized_payload = self._strip_platform_context_fields(payload)
556
-
557
592
  config = self._get_config(session_id)
558
593
  if is_checkpoint_resume:
559
594
  config = self._apply_checkpoint_resume_config(
@@ -561,24 +596,29 @@ class LangGraphRunner(BaseRunner):
561
596
  session_id=session_id,
562
597
  checkpoint_ref=checkpoint_ref,
563
598
  )
564
-
599
+
565
600
  # 判断输入格式 / resume
566
601
  if is_checkpoint_resume:
567
- state = None
568
- elif self._has_prepare_state_hook():
569
- state = self._prepare_state_with_hook(payload, session_id, history, is_resume=is_resume)
602
+ state = resume_value
570
603
  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
604
+ # ``Command(resume=...)`` is delivered to the graph's suspended
605
+ # interrupt. A custom prepare-state hook is for new user input;
606
+ # applying it here can rewrite an approval decision into ordinary
607
+ # graph state and turn an approved HITL action into a rejection.
608
+ state = resume_value
609
+ elif self._has_prepare_state_hook():
610
+ state = self._prepare_state_with_hook(payload, session_id, history)
575
611
  else:
576
612
  state = self._to_state(payload, history)
577
613
 
578
614
  try:
579
615
  if is_checkpoint_resume:
580
616
  result = await self._invoke_graph(
581
- None,
617
+ self._checkpoint_resume_input(
618
+ state,
619
+ payload_provided=resume_payload_provided,
620
+ interrupt_id=resume_interrupt_id,
621
+ ),
582
622
  config=config,
583
623
  context=native_context,
584
624
  )
@@ -606,7 +646,7 @@ class LangGraphRunner(BaseRunner):
606
646
  if metadata:
607
647
  output["metadata"] = {**(output.get("metadata") or {}), **metadata}
608
648
  return output
609
-
649
+
610
650
  except Exception as e:
611
651
  if "Interrupt" in type(e).__name__:
612
652
  interrupt_info = self._get_interrupt_info(self._agent.get_state(config))
@@ -614,7 +654,11 @@ class LangGraphRunner(BaseRunner):
614
654
  "type": "interrupt",
615
655
  "interrupt_info": interrupt_info,
616
656
  "session_id": session_id,
617
- "output": interrupt_info.get("message", "需要用户确认") if isinstance(interrupt_info, dict) else "需要用户确认",
657
+ "output": (
658
+ interrupt_info.get("message", "需要用户确认")
659
+ if isinstance(interrupt_info, dict)
660
+ else "需要用户确认"
661
+ ),
618
662
  }
619
663
  raise
620
664
 
@@ -623,16 +667,21 @@ class LangGraphRunner(BaseRunner):
623
667
  if isinstance(result, dict):
624
668
  # 自定义 output 字段是业务显式出参,优先于内部 messages state。
625
669
  if "output" in result:
626
- return result["output"]
670
+ return str(result["output"])
627
671
  # LangGraph 示例常用 answer 作为最终业务回答字段。
628
672
  if "answer" in result:
629
- return result["answer"]
673
+ return str(result["answer"])
630
674
  # 标准 messages 格式
631
675
  if "messages" in result:
632
676
  messages = result["messages"]
633
677
  if messages:
634
678
  last = messages[-1]
635
- return last.get("content", str(last)) if isinstance(last, dict) else getattr(last, "content", str(last))
679
+ content = (
680
+ last.get("content", str(last))
681
+ if isinstance(last, dict)
682
+ else getattr(last, "content", str(last))
683
+ )
684
+ return str(content)
636
685
  return str(result) if result else ""
637
686
 
638
687
  def _get_interrupt_info(self, state) -> dict:
@@ -642,12 +691,18 @@ class LangGraphRunner(BaseRunner):
642
691
  if hasattr(task, "interrupts") and task.interrupts:
643
692
  for intr in task.interrupts:
644
693
  if hasattr(intr, "value"):
645
- return intr.value
694
+ value = intr.value
695
+ info = dict(value) if isinstance(value, Mapping) else {"value": value}
696
+ interrupt_id = str(getattr(intr, "id", "") or "")
697
+ if interrupt_id:
698
+ info.setdefault("approval_request_id", interrupt_id)
699
+ return info
646
700
  return {}
647
701
 
648
702
  async def _stream_checkpoint_resume_updates(
649
703
  self,
650
704
  *,
705
+ stream_input: Any,
651
706
  config: dict[str, Any],
652
707
  context: dict[str, Any] | None,
653
708
  ) -> AsyncIterator[Dict[str, Any]]:
@@ -664,11 +719,17 @@ class LangGraphRunner(BaseRunner):
664
719
 
665
720
  latest_output: Any = None
666
721
  emitted_update = False
667
- async for update in self._agent.astream(None, **kwargs):
722
+ tool_names: dict[str, str] = {}
723
+ async for update in self._agent.astream(stream_input, **kwargs):
668
724
  emitted_update = True
669
725
  latest_output = update
670
726
  if isinstance(update, Mapping):
671
727
  for node_name, node_output in update.items():
728
+ for tool_event in self._tool_events_from_graph_update(
729
+ node_output,
730
+ tool_names=tool_names,
731
+ ):
732
+ yield tool_event
672
733
  yield {
673
734
  "type": "graph_update",
674
735
  "node": str(node_name),
@@ -703,6 +764,76 @@ class LangGraphRunner(BaseRunner):
703
764
  if metadata:
704
765
  yield {"type": "checkpoint", "metadata": metadata}
705
766
 
767
+ @staticmethod
768
+ def _tool_events_from_graph_update(
769
+ node_output: Any,
770
+ *,
771
+ tool_names: dict[str, str],
772
+ ) -> list[dict[str, Any]]:
773
+ """Preserve LangGraph update-mode tool messages as structured runner events."""
774
+
775
+ if isinstance(node_output, Mapping):
776
+ messages = node_output.get("messages")
777
+ else:
778
+ messages = getattr(node_output, "messages", None)
779
+ if not isinstance(messages, (list, tuple)):
780
+ return []
781
+
782
+ events: list[dict[str, Any]] = []
783
+ for message in messages:
784
+ if isinstance(message, Mapping):
785
+ tool_calls = message.get("tool_calls")
786
+ tool_call_id = message.get("tool_call_id")
787
+ message_name = message.get("name")
788
+ message_content = message.get("content")
789
+ message_status = message.get("status")
790
+ else:
791
+ tool_calls = getattr(message, "tool_calls", None)
792
+ tool_call_id = getattr(message, "tool_call_id", None)
793
+ message_name = getattr(message, "name", None)
794
+ message_content = getattr(message, "content", None)
795
+ message_status = getattr(message, "status", None)
796
+
797
+ if isinstance(tool_calls, (list, tuple)):
798
+ for tool_call in tool_calls:
799
+ if isinstance(tool_call, Mapping):
800
+ call_id = str(tool_call.get("id") or tool_call.get("tool_call_id") or "")
801
+ name = str(tool_call.get("name") or "tool")
802
+ args = tool_call.get("args")
803
+ else:
804
+ call_id = str(
805
+ getattr(tool_call, "id", None)
806
+ or getattr(tool_call, "tool_call_id", None)
807
+ or ""
808
+ )
809
+ name = str(getattr(tool_call, "name", None) or "tool")
810
+ args = getattr(tool_call, "args", None)
811
+ if call_id:
812
+ tool_names[call_id] = name
813
+ events.append(
814
+ {
815
+ "type": "tool_call",
816
+ "tool_call_id": call_id or name,
817
+ "tool_name": name,
818
+ "tool_args": args,
819
+ }
820
+ )
821
+
822
+ if tool_call_id:
823
+ call_id = str(tool_call_id)
824
+ name = str(message_name or tool_names.get(call_id) or "tool")
825
+ status = str(message_status or "").lower()
826
+ events.append(
827
+ {
828
+ "type": "tool_result",
829
+ "tool_call_id": call_id,
830
+ "tool_name": name,
831
+ "tool_output": message_content,
832
+ "error": message_content if status in {"error", "failed"} else None,
833
+ }
834
+ )
835
+ return events
836
+
706
837
  async def stream(self, input_data: Dict[str, Any]) -> AsyncIterator[Dict[str, Any]]:
707
838
  """流式调用 LangGraph 图"""
708
839
  payload = dict(input_data)
@@ -711,10 +842,11 @@ class LangGraphRunner(BaseRunner):
711
842
  history = payload.pop("history", [])
712
843
  is_resume = payload.pop("resume", False)
713
844
  is_checkpoint_resume = bool(payload.pop("checkpoint_resume", False))
845
+ resume_payload_provided = bool(payload.pop("resume_payload_provided", False))
846
+ resume_interrupt_id = str(payload.pop("resume_interrupt_id", "") or "")
847
+ resume_value = payload.get("input")
714
848
  checkpoint_ref = self._extract_langgraph_checkpoint_ref(payload)
715
849
  native_context = self.build_native_context(payload.get("platform_context"))
716
- normalized_payload = self._strip_platform_context_fields(payload)
717
-
718
850
  invoke_payload = dict(payload)
719
851
  invoke_payload["session_id"] = session_id
720
852
  if history:
@@ -723,7 +855,9 @@ class LangGraphRunner(BaseRunner):
723
855
  invoke_payload["resume"] = True
724
856
  if is_checkpoint_resume:
725
857
  invoke_payload["checkpoint_resume"] = True
726
-
858
+ invoke_payload["resume_payload_provided"] = resume_payload_provided
859
+ invoke_payload["resume_interrupt_id"] = resume_interrupt_id
860
+
727
861
  config = self._get_config(session_id)
728
862
  if is_checkpoint_resume:
729
863
  config = self._apply_checkpoint_resume_config(
@@ -733,14 +867,13 @@ class LangGraphRunner(BaseRunner):
733
867
  )
734
868
 
735
869
  if is_checkpoint_resume:
736
- state = None
737
- elif self._has_prepare_state_hook():
738
- state = self._prepare_state_with_hook(payload, session_id, history, is_resume=is_resume)
870
+ state = resume_value
739
871
  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
872
+ # Keep the interrupt value intact for ``Command(resume=...)``;
873
+ # prepare-state hooks only shape fresh user turns.
874
+ state = resume_value
875
+ elif self._has_prepare_state_hook():
876
+ state = self._prepare_state_with_hook(payload, session_id, history)
744
877
  else:
745
878
  state = self._to_state(payload, history)
746
879
 
@@ -799,6 +932,11 @@ class LangGraphRunner(BaseRunner):
799
932
  if is_checkpoint_resume and callable(getattr(self._agent, "astream", None)):
800
933
  try:
801
934
  async for chunk in self._stream_checkpoint_resume_updates(
935
+ stream_input=self._checkpoint_resume_input(
936
+ state,
937
+ payload_provided=resume_payload_provided,
938
+ interrupt_id=resume_interrupt_id,
939
+ ),
802
940
  config=config,
803
941
  context=native_context,
804
942
  ):
@@ -826,9 +964,23 @@ class LangGraphRunner(BaseRunner):
826
964
  return
827
965
 
828
966
  try:
829
- stream_input = None if is_checkpoint_resume else (Command(resume=state) if is_resume else state)
967
+ stream_input = (
968
+ self._checkpoint_resume_input(
969
+ state,
970
+ payload_provided=resume_payload_provided,
971
+ interrupt_id=resume_interrupt_id,
972
+ )
973
+ if is_checkpoint_resume
974
+ else (Command(resume=state) if is_resume else state)
975
+ )
976
+ # stream_mode 含 "custom" 才会产生 on_custom_stream 事件(custom writer);
977
+ # 保留默认 "values" 以兼容既有 on_chain_end/graph_update 消费。
830
978
  stream_kwargs = {"version": "v2", "config": config}
831
- if native_context and self._callable_accepts_keyword(self._agent.astream_events, "context"):
979
+ if self._callable_accepts_keyword(self._agent.astream_events, "stream_mode"):
980
+ stream_kwargs["stream_mode"] = ["values", "custom"]
981
+ if native_context and self._callable_accepts_keyword(
982
+ self._agent.astream_events, "context"
983
+ ):
832
984
  stream_kwargs["context"] = native_context
833
985
  async for event in self._agent.astream_events(stream_input, **stream_kwargs):
834
986
  event_kind = event.get("event", "")
@@ -855,7 +1007,7 @@ class LangGraphRunner(BaseRunner):
855
1007
  reasoning = getattr(chunk, "reasoning_content", None)
856
1008
  if not reasoning and hasattr(chunk, "additional_kwargs"):
857
1009
  reasoning = chunk.additional_kwargs.get("reasoning_content")
858
-
1010
+
859
1011
  if reasoning:
860
1012
  accumulated_reasoning += reasoning
861
1013
  yield {"delta": reasoning, "type": "thinking"}
@@ -865,9 +1017,9 @@ class LangGraphRunner(BaseRunner):
865
1017
  content = self._filter_tool_tags(chunk.content)
866
1018
  if isinstance(content, str):
867
1019
  if accumulated_reasoning and content.startswith(accumulated_reasoning):
868
- content = content[len(accumulated_reasoning):]
1020
+ content = content[len(accumulated_reasoning) :]
869
1021
  elif reasoning and content.startswith(reasoning):
870
- content = content[len(reasoning):]
1022
+ content = content[len(reasoning) :]
871
1023
  if content:
872
1024
  for part in inline_reasoning_parser.feed(content):
873
1025
  if not part.text or not part.text.strip():
@@ -888,6 +1040,59 @@ class LangGraphRunner(BaseRunner):
888
1040
  if run_key not in stream_usage_run_keys:
889
1041
  record_model_usage(event, last_usage or usage)
890
1042
 
1043
+ elif event_kind == "on_chain_stream":
1044
+ # node 内 get_stream_writer() 写入的自定义数据,经 stream_mode 含
1045
+ # "custom" 时,astream_events 包成 on_chain_stream,chunk 为
1046
+ # (mode, value) tuple:("custom", value) 是 writer 透传内容,
1047
+ # ("values", state) 是 state 快照(忽略,终态走 on_chain_end)。
1048
+ # 编排方常用 custom writer 把"调远端 agent/子图"的流式增量透传出来。
1049
+ chunk = event.get("data", {}).get("chunk")
1050
+ if not (
1051
+ isinstance(chunk, tuple) and len(chunk) == 2 and chunk[0] == "custom"
1052
+ ):
1053
+ continue
1054
+ data = chunk[1]
1055
+ if isinstance(data, str):
1056
+ accumulated_text += data
1057
+ yield {"delta": data, "type": "text"}
1058
+ continue
1059
+ if isinstance(data, Mapping):
1060
+ custom_type = str(data.get("type") or "text")
1061
+ if custom_type in ("tool_call", "tool_result"):
1062
+ # 结构化工具事件:透传完整 payload(tool_name/tool_args/
1063
+ # tool_output 等),不计入正文,供 UI 渲染工具卡片。
1064
+ out = {"type": custom_type}
1065
+ out.update({k: v for k, v in data.items() if k != "type"})
1066
+ yield out
1067
+ continue
1068
+ custom_delta = ""
1069
+ for key in ("delta", "text", "content", "output", "data"):
1070
+ value = data.get(key)
1071
+ if isinstance(value, str) and value:
1072
+ custom_delta = value
1073
+ break
1074
+ if not custom_delta:
1075
+ continue
1076
+ replace = bool(data.get("replace"))
1077
+ if custom_type == "thinking":
1078
+ accumulated_reasoning = (
1079
+ custom_delta
1080
+ if replace
1081
+ else accumulated_reasoning + custom_delta
1082
+ )
1083
+ else:
1084
+ accumulated_text = (
1085
+ custom_delta if replace else accumulated_text + custom_delta
1086
+ )
1087
+ out = {"delta": custom_delta, "type": custom_type}
1088
+ if replace:
1089
+ out["replace"] = True
1090
+ yield out
1091
+ continue
1092
+ if data is not None:
1093
+ accumulated_text += str(data)
1094
+ yield {"delta": str(data), "type": "text"}
1095
+
891
1096
  elif event_kind == "on_tool_start":
892
1097
  emitted_non_text_event = True
893
1098
  yield {
@@ -896,23 +1101,35 @@ class LangGraphRunner(BaseRunner):
896
1101
  "tool_args": event.get("data", {}).get("input", {}),
897
1102
  "run_id": event.get("run_id"),
898
1103
  }
899
-
1104
+
900
1105
  elif event_kind == "on_tool_end":
901
1106
  emitted_non_text_event = True
902
1107
  tool_output = event.get("data", {}).get("output", "")
1108
+ # LangGraph returns a ToolMessage here for normal tools.
1109
+ # Preserve its content instead of serializing the repr,
1110
+ # otherwise structured output such as A2UI envelopes becomes
1111
+ # unparsable. Keep the callback run_id below: it is paired
1112
+ # with the preceding ``on_tool_start`` event on this stream.
1113
+ normalized_output = getattr(tool_output, "content", tool_output)
1114
+ if isinstance(tool_output, Mapping) and "content" in tool_output:
1115
+ normalized_output = tool_output["content"]
903
1116
  yield {
904
1117
  "type": "tool_result",
905
1118
  "tool_name": event.get("name", "unknown"),
906
1119
  "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 ""),
1120
+ "tool_output": normalized_output,
908
1121
  "run_id": event.get("run_id"),
909
1122
  }
910
-
1123
+
911
1124
  elif event_kind == "on_chain_end":
912
1125
  output = event.get("data", {}).get("output", {})
913
1126
  if isinstance(output, dict) and "__interrupt__" in output:
914
1127
  emitted_non_text_event = True
915
- yield {"type": "interrupt", "interrupt_info": output["__interrupt__"], "session_id": session_id}
1128
+ yield {
1129
+ "type": "interrupt",
1130
+ "interrupt_info": output["__interrupt__"],
1131
+ "session_id": session_id,
1132
+ }
916
1133
  return
917
1134
  extracted_output = self._extract_output(output)
918
1135
  if extracted_output:
@@ -922,10 +1139,41 @@ class LangGraphRunner(BaseRunner):
922
1139
 
923
1140
  except Exception as e:
924
1141
  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}
1142
+ yield {
1143
+ "type": "interrupt",
1144
+ "interrupt_info": self._get_interrupt_info(self._agent.get_state(config)),
1145
+ "session_id": session_id,
1146
+ }
926
1147
  return
927
1148
  raise
928
1149
 
1150
+ # goal-18(ksadk-web 人机交互):图因审批门(HITL)在流式中静默暂停时,
1151
+ # 这里把审批详情(action_requests)作为 approval 事件冒出,供 UI 渲染审批卡。
1152
+ # 此前流式路径只在 checkpoint 标 resumable,UI 拿不到"该批哪个工具/什么参数/允许哪些决定"。
1153
+ # 注:get_state 在部分 agent 上是 async,统一按 awaitable 处理;取不到则跳过,不破坏事件流。
1154
+ pending_approval = None
1155
+ try:
1156
+ _get_state = getattr(self._agent, "aget_state", None) or getattr(
1157
+ self._agent, "get_state", None
1158
+ )
1159
+ if _get_state is not None:
1160
+ _maybe_state = _get_state(config)
1161
+ if inspect.isawaitable(_maybe_state):
1162
+ _maybe_state = await _maybe_state
1163
+ pending_approval = self._get_interrupt_info(_maybe_state)
1164
+ except Exception:
1165
+ pending_approval = None
1166
+ if pending_approval:
1167
+ yield {
1168
+ "type": "approval",
1169
+ "interrupt_info": pending_approval,
1170
+ "session_id": session_id,
1171
+ }
1172
+ metadata = await self._latest_checkpoint_metadata(config)
1173
+ if metadata:
1174
+ yield {"type": "checkpoint", "metadata": metadata}
1175
+ return
1176
+
929
1177
  for part in inline_reasoning_parser.flush():
930
1178
  if not part.text or not part.text.strip():
931
1179
  continue
@@ -940,35 +1188,49 @@ class LangGraphRunner(BaseRunner):
940
1188
  if final_output_text:
941
1189
  final_chunk = {"output": final_output_text, "type": "final"}
942
1190
  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
1191
+ last_usage = (
1192
+ latest_model_usage() or final_output_last_usage or latest_stream_usage or usage
1193
+ )
944
1194
  if usage:
945
1195
  final_chunk["usage"] = usage
946
1196
  if last_usage:
947
1197
  final_chunk.setdefault("metadata", {})["last_usage"] = last_usage
948
1198
  yield final_chunk
949
1199
  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"}
1200
+ result = await self.invoke({**invoke_payload, "_ksadk_force_graph_invoke": True})
1201
+ fallback_chunk: dict[str, Any] = {
1202
+ "output": result.get("output", ""),
1203
+ "type": "final",
1204
+ }
954
1205
  usage = self._extract_usage(result)
955
1206
  if usage:
956
- final_chunk["usage"] = usage
1207
+ fallback_chunk["usage"] = usage
957
1208
  last_usage = self._extract_last_usage(result)
958
1209
  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}
1210
+ fallback_chunk.setdefault("metadata", {})["last_usage"] = last_usage
1211
+ yield fallback_chunk
1212
+ checkpoint_metadata = result.get("metadata") if isinstance(result, dict) else None
1213
+ if isinstance(checkpoint_metadata, dict) and checkpoint_metadata.get("agentengine"):
1214
+ yield {"type": "checkpoint", "metadata": checkpoint_metadata}
964
1215
  return
965
1216
  else:
966
1217
  final_chunk = {"output": accumulated_text, "type": "final"}
967
1218
  state_usage = await self._latest_state_usage(config)
968
- usage = accumulated_model_usage() or state_usage or final_output_usage or latest_stream_usage
1219
+ usage = (
1220
+ accumulated_model_usage()
1221
+ or state_usage
1222
+ or final_output_usage
1223
+ or latest_stream_usage
1224
+ )
969
1225
  if usage:
970
1226
  final_chunk["usage"] = usage
971
- last_usage = latest_model_usage() or state_usage or final_output_last_usage or latest_stream_usage or usage
1227
+ last_usage = (
1228
+ latest_model_usage()
1229
+ or state_usage
1230
+ or final_output_last_usage
1231
+ or latest_stream_usage
1232
+ or usage
1233
+ )
972
1234
  final_chunk.setdefault("metadata", {})["last_usage"] = last_usage
973
1235
  yield final_chunk
974
1236
 
@@ -980,6 +1242,6 @@ class LangGraphRunner(BaseRunner):
980
1242
  """过滤 <tool_call> 标签"""
981
1243
  if not isinstance(content, str):
982
1244
  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)
1245
+ content = re.sub(r"<tool_call>.*?</tool_call>", "", content, flags=re.DOTALL)
1246
+ content = re.sub(r"</?(?:tool_call|arg_key|arg_value)>", "", content)
985
1247
  return content