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
@@ -15,11 +15,11 @@ from dataclasses import dataclass
15
15
  from importlib.metadata import PackageNotFoundError
16
16
  from importlib.metadata import version as _pkg_version
17
17
  from pathlib import Path
18
- from typing import Any, AsyncIterator, Dict, Mapping, Optional
18
+ from typing import Any, AsyncIterator, Dict, Mapping, Optional, cast
19
19
 
20
- from google.genai import types
21
20
  from opentelemetry import trace
22
21
 
22
+ from ksadk.compat.adk_compat import genai_types as types
23
23
  from ksadk.conversations.attachments import classify_attachment_kind, read_attachment_uri_bytes
24
24
  from ksadk.conversations.model_context import supports_native_image_input
25
25
  from ksadk.runners.base_runner import BaseRunner
@@ -31,22 +31,36 @@ logger = logging.getLogger(__name__)
31
31
  tracer = trace.get_tracer(__name__)
32
32
 
33
33
 
34
+ def _part_metadata_flag(part: Any, key: str) -> bool:
35
+ """读取 A2A→GenAI Part 保留下来的扩展 metadata 标记。"""
36
+ metadata = getattr(part, "part_metadata", None)
37
+ if isinstance(metadata, Mapping):
38
+ return bool(metadata.get(key))
39
+ getter = getattr(metadata, "get", None)
40
+ if callable(getter):
41
+ try:
42
+ return bool(getter(key))
43
+ except (KeyError, TypeError, ValueError):
44
+ return False
45
+ return False
46
+
47
+
34
48
  class ADKRunner(BaseRunner):
35
49
  """ADK 框架运行时"""
36
50
 
37
51
  def __init__(self, detection_result: Any, project_dir: str):
38
52
  super().__init__(detection_result, project_dir)
39
- self._runner = None
40
- self._session_service = None
53
+ self._runner: Any = None
54
+ self._session_service: Any = None
41
55
  # Map external session_ids (e.g. from run_interactive or web) to ADK internal session IDs
42
56
  self._session_map: Dict[str, str] = {}
43
57
  # Fallback default session
44
58
  self._default_session_id: Optional[str] = None
45
59
  # Memory integration
46
- self._short_term_memory = None
47
- self._long_term_memory = None
60
+ self._short_term_memory: Any = None
61
+ self._long_term_memory: Any = None
48
62
  # Knowledge base integration
49
- self._knowledge_base = None
63
+ self._knowledge_base: Any = None
50
64
  # Keep runtime toolsets alive for the lifetime of the runner.
51
65
  self._runtime_toolsets: list[Any] = []
52
66
  # ADK resumability state
@@ -55,7 +69,7 @@ class ADKRunner(BaseRunner):
55
69
  # P1.1 sub-issue: guard invocation_map read-modify-write so concurrent
56
70
  # invocations on the same session don't lose each other's mappings.
57
71
  self._invocation_map_lock = asyncio.Lock()
58
- self._module = None
72
+ self._module: Any = None
59
73
  self._adk_resume_min_version = os.environ.get(
60
74
  "GOOGLE_ADK_RESUME_MIN_VERSION", "1.16.0"
61
75
  ).strip()
@@ -98,7 +112,9 @@ class ADKRunner(BaseRunner):
98
112
  import inspect as _inspect
99
113
  import json as _stdlib_json
100
114
 
101
- import google.adk.models.lite_llm as adk_lite_llm
115
+ from ksadk.compat.adk_compat import lite_llm_module
116
+
117
+ adk_lite_llm = lite_llm_module()
102
118
 
103
119
  _original_fn = adk_lite_llm._message_to_generate_content_response
104
120
  if getattr(_original_fn, "__ksadk_json_patch__", False):
@@ -106,9 +122,7 @@ class ADKRunner(BaseRunner):
106
122
  # Determine which kwargs the original function actually accepts,
107
123
  # so the patch is compatible across ADK versions that may have
108
124
  # added or removed `model_version` / `thought_parts`.
109
- _orig_params = set(
110
- _inspect.signature(_original_fn).parameters.keys()
111
- )
125
+ _orig_params = set(_inspect.signature(_original_fn).parameters.keys())
112
126
 
113
127
  def _patched_message_to_generate_content_response(
114
128
  message, *, is_partial=False, model_version=None, thought_parts=None
@@ -129,8 +143,8 @@ class ADKRunner(BaseRunner):
129
143
  "ADKRunner: caught JSONDecodeError in args parsing, "
130
144
  "returning empty response"
131
145
  )
132
- from google.adk.models import LlmResponse
133
- from google.genai import types as _genai_types
146
+ from ksadk.compat.adk_compat import LlmResponse
147
+ from ksadk.compat.adk_compat import genai_types as _genai_types
134
148
 
135
149
  return LlmResponse(
136
150
  content=_genai_types.Content(role="model", parts=[]),
@@ -149,15 +163,11 @@ class ADKRunner(BaseRunner):
149
163
  for i, part in enumerate(result.content.parts):
150
164
  if part.function_call and part.function_call.args is None:
151
165
  part.function_call.args = {}
152
- if (
153
- part.function_call
154
- and not (part.function_call.name or "").strip()
155
- ):
166
+ if part.function_call and not (part.function_call.name or "").strip():
156
167
  phantom_indices.add(i)
157
168
  if phantom_indices:
158
169
  result.content.parts = [
159
- p for i, p in enumerate(result.content.parts)
160
- if i not in phantom_indices
170
+ p for i, p in enumerate(result.content.parts) if i not in phantom_indices
161
171
  ]
162
172
  return result
163
173
 
@@ -180,7 +190,6 @@ class ADKRunner(BaseRunner):
180
190
  except Exception:
181
191
  pass
182
192
 
183
-
184
193
  def _apply_mcp_result_patch(self):
185
194
  """Patch ADK McpTool to convert CallToolResult to dict.
186
195
 
@@ -192,17 +201,17 @@ class ADKRunner(BaseRunner):
192
201
  for the old version.
193
202
  """
194
203
  try:
195
- from google.adk.tools.mcp_tool.mcp_tool import McpTool
204
+ from ksadk.compat.adk_compat import McpTool
196
205
 
197
206
  _original_run_async = McpTool._run_async_impl
198
207
  if getattr(_original_run_async, "__ksadk_mcp_result_patch__", False):
199
208
  return
200
209
 
201
- async def _patched_run_async_impl(
202
- self, *, args, tool_context, credential
203
- ):
210
+ async def _patched_run_async_impl(self, *, args, tool_context, credential):
204
211
  response = await _original_run_async(
205
- self, args=args, tool_context=tool_context,
212
+ self,
213
+ args=args,
214
+ tool_context=tool_context,
206
215
  credential=credential,
207
216
  )
208
217
  # If response is a Pydantic model (e.g. CallToolResult),
@@ -224,7 +233,6 @@ class ADKRunner(BaseRunner):
224
233
  except Exception as exc:
225
234
  logger.debug("ADKRunner: MCP result patch failed: %s", exc)
226
235
 
227
-
228
236
  def _init_short_term_memory(self):
229
237
  """从环境变量初始化短期记忆
230
238
 
@@ -268,8 +276,7 @@ class ADKRunner(BaseRunner):
268
276
  def describe_checkpoint_capability(self) -> dict[str, Any]:
269
277
  resumable = getattr(self, "_resumable", False)
270
278
  stm_backend = (
271
- getattr(self._short_term_memory, "backend", None)
272
- if self._short_term_memory else None
279
+ getattr(self._short_term_memory, "backend", None) if self._short_term_memory else None
273
280
  )
274
281
  if resumable:
275
282
  backend = "adk_invocation"
@@ -312,8 +319,7 @@ class ADKRunner(BaseRunner):
312
319
  capabilities = super().get_runtime_capabilities()
313
320
  resumable = getattr(self, "_resumable", False)
314
321
  stm_backend = (
315
- getattr(self._short_term_memory, "backend", None)
316
- if self._short_term_memory else None
322
+ getattr(self._short_term_memory, "backend", None) if self._short_term_memory else None
317
323
  )
318
324
  is_durable = stm_backend is not None and stm_backend != "local"
319
325
  if resumable:
@@ -352,9 +358,11 @@ class ADKRunner(BaseRunner):
352
358
  "Supported": True,
353
359
  "Type": "native_session" if has_native_session else "semantic_replay",
354
360
  "Level": "semantic",
355
- "Reason": "ADK native session can continue conversation context"
356
- if has_native_session
357
- else "conversation transcript can be replayed",
361
+ "Reason": (
362
+ "ADK native session can continue conversation context"
363
+ if has_native_session
364
+ else "conversation transcript can be replayed"
365
+ ),
358
366
  }
359
367
  capabilities["ResumeRun"]["Reason"] = capabilities["Checkpoint"]["Reason"]
360
368
  return capabilities
@@ -362,8 +370,8 @@ class ADKRunner(BaseRunner):
362
370
  @dataclass
363
371
  class _ResolvabilityResult:
364
372
  enabled: bool
365
- source: str # "agent_module" | "env" | "auto_persistent_session" | "default"
366
- app: Any # User module exported App object (if any)
373
+ source: str # "agent_module" | "env" | "auto_persistent_session" | "default"
374
+ app: Any # User module exported App object (if any)
367
375
 
368
376
  def _resolve_resumability(self) -> "_ResolvabilityResult":
369
377
  """从环境变量或 agent 模块推断是否启用 ADK 可恢复性。"""
@@ -371,13 +379,15 @@ class ADKRunner(BaseRunner):
371
379
  module = getattr(self, "_module", None)
372
380
  if module is not None:
373
381
  try:
374
- from google.adk.apps import App
382
+ from ksadk.compat.adk_compat import App
383
+
375
384
  for attr_name in ("app", "application"):
376
385
  candidate = getattr(module, attr_name, None)
377
386
  if isinstance(candidate, App) and candidate.resumability_config:
378
387
  if candidate.resumability_config.is_resumable:
379
388
  return self._ResolvabilityResult(
380
- enabled=True, source="agent_module", app=candidate)
389
+ enabled=True, source="agent_module", app=candidate
390
+ )
381
391
  except ImportError:
382
392
  pass
383
393
 
@@ -388,12 +398,12 @@ class ADKRunner(BaseRunner):
388
398
 
389
399
  # Priority 3: persistent session backend auto-enable
390
400
  stm_backend = (
391
- getattr(self._short_term_memory, "backend", None)
392
- if self._short_term_memory else None
401
+ getattr(self._short_term_memory, "backend", None) if self._short_term_memory else None
393
402
  )
394
403
  if stm_backend and stm_backend != "local":
395
404
  return self._ResolvabilityResult(
396
- enabled=True, source="auto_persistent_session", app=None)
405
+ enabled=True, source="auto_persistent_session", app=None
406
+ )
397
407
 
398
408
  return self._ResolvabilityResult(enabled=False, source="default", app=None)
399
409
 
@@ -417,6 +427,7 @@ class ADKRunner(BaseRunner):
417
427
  return False, "google-adk version unknown, cannot guarantee invocation_id support"
418
428
  try:
419
429
  from packaging.version import Version
430
+
420
431
  if Version(adk_ver) < Version(self._adk_resume_min_version):
421
432
  return (
422
433
  False,
@@ -430,7 +441,7 @@ class ADKRunner(BaseRunner):
430
441
 
431
442
  def _build_runner(self) -> None:
432
443
  """构造 ADK Runner,优先使用 App 对象以启用 ResumabilityConfig。"""
433
- from google.adk.runners import Runner
444
+ from ksadk.compat.adk_compat import Runner
434
445
 
435
446
  resumable = self._resolve_resumability()
436
447
  resumability_enabled = resumable.enabled
@@ -438,9 +449,7 @@ class ADKRunner(BaseRunner):
438
449
  # 版本兼容性检查:低于最低版本时强制关闭恢复
439
450
  resume_compatible, resume_reason = self._check_adk_resume_compatibility()
440
451
  if not resume_compatible and resumable.enabled:
441
- logger.warning(
442
- "ADKRunner: resumability disabled — %s", resume_reason
443
- )
452
+ logger.warning("ADKRunner: resumability disabled — %s", resume_reason)
444
453
  resumable = self._ResolvabilityResult(enabled=False, source="version_check", app=None)
445
454
  resumability_enabled = False
446
455
  self._resume_disabled_reason = resume_reason
@@ -452,7 +461,8 @@ class ADKRunner(BaseRunner):
452
461
  )
453
462
  elif resumable.enabled:
454
463
  try:
455
- from google.adk.apps import App, ResumabilityConfig
464
+ from ksadk.compat.adk_compat import App, ResumabilityConfig
465
+
456
466
  app = App(
457
467
  name=self._agent.name,
458
468
  root_agent=self._agent,
@@ -491,7 +501,8 @@ class ADKRunner(BaseRunner):
491
501
  if resumability_enabled:
492
502
  stm_backend = (
493
503
  getattr(self._short_term_memory, "backend", None)
494
- if self._short_term_memory else None
504
+ if self._short_term_memory
505
+ else None
495
506
  )
496
507
  logger.info(
497
508
  "ADKRunner: resumability enabled (source=%s, backend=%s)",
@@ -519,10 +530,7 @@ class ADKRunner(BaseRunner):
519
530
 
520
531
  agent_name = self._agent.name if self._agent else "default"
521
532
  ltm = LongTermMemory.from_env(app_name=agent_name)
522
- logger.info(
523
- f"LongTermMemory initialized: backend={backend}, "
524
- f"app_name={agent_name}"
525
- )
533
+ logger.info(f"LongTermMemory initialized: backend={backend}, " f"app_name={agent_name}")
526
534
  return ltm
527
535
  except Exception as e:
528
536
  logger.warning(f"Failed to init LongTermMemory: {e}.")
@@ -546,8 +554,7 @@ class ADKRunner(BaseRunner):
546
554
 
547
555
  kb = KnowledgeBaseClient.from_env()
548
556
  logger.info(
549
- f"KnowledgeBase initialized: dataset_id={kb.dataset_id}, "
550
- f"region={kb.region}"
557
+ f"KnowledgeBase initialized: dataset_id={kb.dataset_id}, " f"region={kb.region}"
551
558
  )
552
559
  return kb
553
560
  except ImportError:
@@ -582,7 +589,7 @@ class ADKRunner(BaseRunner):
582
589
  def _inject_load_memory_tool(self):
583
590
  """自动注入 load_memory 工具到 Agent"""
584
591
  try:
585
- from google.adk.tools import load_memory
592
+ from ksadk.compat.adk_compat import load_memory
586
593
 
587
594
  added = self._append_tools_by_name([load_memory])
588
595
  if added:
@@ -810,7 +817,7 @@ class ADKRunner(BaseRunner):
810
817
 
811
818
  @staticmethod
812
819
  def _tool_name(tool: Any) -> str:
813
- return getattr(tool, "name", None) or getattr(tool, "__name__", "")
820
+ return str(getattr(tool, "name", None) or getattr(tool, "__name__", ""))
814
821
 
815
822
  def _append_tools_by_name(self, tools: list[Any]) -> list[str]:
816
823
  if not hasattr(self._agent, "tools"):
@@ -821,9 +828,7 @@ class ADKRunner(BaseRunner):
821
828
  self._agent.tools = []
822
829
 
823
830
  existing_names = {
824
- self._tool_name(tool)
825
- for tool in self._agent.tools
826
- if self._tool_name(tool)
831
+ self._tool_name(tool) for tool in self._agent.tools if self._tool_name(tool)
827
832
  }
828
833
  added_names: list[str] = []
829
834
  for tool in tools:
@@ -845,9 +850,7 @@ class ADKRunner(BaseRunner):
845
850
  self._agent.tools = []
846
851
 
847
852
  existing_keys = {
848
- getattr(tool, key_attr)
849
- for tool in self._agent.tools
850
- if getattr(tool, key_attr, None)
853
+ getattr(tool, key_attr) for tool in self._agent.tools if getattr(tool, key_attr, None)
851
854
  }
852
855
  added_keys: list[str] = []
853
856
  for toolset in toolsets:
@@ -879,11 +882,13 @@ class ADKRunner(BaseRunner):
879
882
  name = detail.get("input")
880
883
  if location not in (("name",), ["name"]) or not isinstance(name, str):
881
884
  continue
882
- if "valid identifier" not in message.lower():
885
+ # adk 1.34 报 "...valid identifier";adk 2.x 报 "...valid Python identifier"。
886
+ # 放宽匹配以同时覆盖两种措辞,保证两版都给出 actionable hint。
887
+ msg_lower = message.lower()
888
+ if "identifier" not in msg_lower or "valid" not in msg_lower:
883
889
  continue
884
890
  safe_name = "".join(
885
- char if char.isascii() and (char.isalnum() or char == "_") else "_"
886
- for char in name
891
+ char if char.isascii() and (char.isalnum() or char == "_") else "_" for char in name
887
892
  )
888
893
  if not safe_name or safe_name[0].isdigit():
889
894
  safe_name = f"agent_{safe_name}"
@@ -960,7 +965,7 @@ class ADKRunner(BaseRunner):
960
965
  self._inject_search_knowledge_tool()
961
966
 
962
967
  # 初始化 SessionService
963
- from google.adk.sessions import InMemorySessionService
968
+ from ksadk.compat.adk_compat import InMemorySessionService
964
969
 
965
970
  if self._short_term_memory:
966
971
  self._session_service = self._short_term_memory.session_service
@@ -983,10 +988,7 @@ class ADKRunner(BaseRunner):
983
988
  os.getenv("OPENAI_MODEL_NAME") or os.getenv("MODEL_NAME")
984
989
  )
985
990
  self._default_model_reference = self._discover_model_reference(self._agent)
986
- self._active_model_name = (
987
- self._default_model_reference
988
- or self._default_model_name
989
- )
991
+ self._active_model_name = self._default_model_reference or self._default_model_name
990
992
 
991
993
  def _discover_model_reference(self, agent: Any) -> Optional[str]:
992
994
  visited: set[int] = set()
@@ -1058,18 +1060,14 @@ class ADKRunner(BaseRunner):
1058
1060
  def prepare_for_request(self, model: str | None) -> None:
1059
1061
  normalized = self.sync_process_model_env(model)
1060
1062
  if normalized is None:
1061
- default_model_name = (
1062
- getattr(self, "_default_model_name", None)
1063
- or self.normalize_requested_model(
1064
- os.getenv("OPENAI_MODEL_NAME") or os.getenv("MODEL_NAME")
1065
- )
1063
+ default_model_name = getattr(
1064
+ self, "_default_model_name", None
1065
+ ) or self.normalize_requested_model(
1066
+ os.getenv("OPENAI_MODEL_NAME") or os.getenv("MODEL_NAME")
1066
1067
  )
1067
1068
  if default_model_name:
1068
1069
  self.sync_process_model_env(default_model_name)
1069
- target_reference = (
1070
- getattr(self, "_default_model_reference", None)
1071
- or default_model_name
1072
- )
1070
+ target_reference = getattr(self, "_default_model_reference", None) or default_model_name
1073
1071
  if target_reference and self._agent is not None:
1074
1072
  current_reference = self._discover_model_reference(self._agent)
1075
1073
  if current_reference != target_reference:
@@ -1095,14 +1093,13 @@ class ADKRunner(BaseRunner):
1095
1093
  return
1096
1094
  self._active_model_name = target_reference
1097
1095
 
1098
- def _prepare_trace_metadata(self, session_id: str):
1096
+ def _prepare_trace_metadata(self, session_id: Optional[str]):
1099
1097
  """准备 Trace 元数据 (Tags, UserID, etc.)"""
1100
1098
  from ksadk.tracing.span_utils import prepare_trace_metadata
1101
- return prepare_trace_metadata(
1102
- detection_result=getattr(self, "detection_result", None)
1103
- )
1104
1099
 
1105
- async def _ensure_session(self, external_session_id: str = None) -> str:
1100
+ return prepare_trace_metadata(detection_result=getattr(self, "detection_result", None))
1101
+
1102
+ async def _ensure_session(self, external_session_id: Optional[str] = None) -> str:
1106
1103
  """Get or create ADK session ID based on external ID
1107
1104
 
1108
1105
  When ShortTermMemory is configured, uses its create_session method
@@ -1121,11 +1118,13 @@ class ADKRunner(BaseRunner):
1121
1118
  session_id=external_session_id,
1122
1119
  )
1123
1120
  else:
1121
+ if self._session_service is None:
1122
+ raise RuntimeError("ADK session service is not initialized")
1124
1123
  session = await self._session_service.create_session(
1125
1124
  app_name=self._agent.name, user_id="ksadk_user"
1126
1125
  )
1127
1126
  self._session_map[external_session_id] = session.id
1128
- return session.id
1127
+ return str(session.id)
1129
1128
 
1130
1129
  # Case 2: No external ID (use default singleton)
1131
1130
  if self._default_session_id is None:
@@ -1135,11 +1134,13 @@ class ADKRunner(BaseRunner):
1135
1134
  user_id="ksadk_user",
1136
1135
  )
1137
1136
  else:
1137
+ if self._session_service is None:
1138
+ raise RuntimeError("ADK session service is not initialized")
1138
1139
  session = await self._session_service.create_session(
1139
1140
  app_name=self._agent.name, user_id="ksadk_user"
1140
1141
  )
1141
1142
  self._default_session_id = session.id
1142
- return self._default_session_id
1143
+ return str(self._default_session_id)
1143
1144
 
1144
1145
  async def save_session_to_long_term_memory(
1145
1146
  self, session_id: str, user_id: str = "ksadk_user"
@@ -1323,8 +1324,11 @@ class ADKRunner(BaseRunner):
1323
1324
  usage_metadata.get("total_token_count") or (input_tokens + output_tokens)
1324
1325
  )
1325
1326
  if not (
1326
- input_tokens or output_tokens or total_tokens
1327
- or input_token_details or output_token_details
1327
+ input_tokens
1328
+ or output_tokens
1329
+ or total_tokens
1330
+ or input_token_details
1331
+ or output_token_details
1328
1332
  ):
1329
1333
  return {}
1330
1334
  return {
@@ -1339,7 +1343,6 @@ class ADKRunner(BaseRunner):
1339
1343
  def _extract_event_usage(cls, event: Any) -> dict[str, Any]:
1340
1344
  return cls._normalize_usage_metadata(getattr(event, "usage_metadata", None))
1341
1345
 
1342
-
1343
1346
  # --- ADK invocation_id & checkpoint helpers ---
1344
1347
 
1345
1348
  async def _get_max_checkpoint_seq_for_run(
@@ -1380,9 +1383,9 @@ class ADKRunner(BaseRunner):
1380
1383
  return max_seq
1381
1384
  except Exception as exc:
1382
1385
  logger.warning(
1383
- "ADKRunner: failed to query max checkpoint_seq "
1384
- "for run_id=%s: %s",
1385
- run_id, exc,
1386
+ "ADKRunner: failed to query max checkpoint_seq " "for run_id=%s: %s",
1387
+ run_id,
1388
+ exc,
1386
1389
  )
1387
1390
  return 0
1388
1391
 
@@ -1406,7 +1409,8 @@ class ADKRunner(BaseRunner):
1406
1409
  captured_adk_invocation_id = ""
1407
1410
  # 从已有 checkpoint 的最大 seq 继续,避免恢复模式下 ID 碰撞被去重丢弃
1408
1411
  checkpoint_seq = await self._get_max_checkpoint_seq_for_run(
1409
- session_id=session_id, run_id=effective_run_id,
1412
+ session_id=session_id,
1413
+ run_id=effective_run_id,
1410
1414
  )
1411
1415
  async for event in events_async:
1412
1416
  if not first_event_captured and hasattr(event, "invocation_id") and event.invocation_id:
@@ -1513,7 +1517,6 @@ class ADKRunner(BaseRunner):
1513
1517
  except Exception:
1514
1518
  return None
1515
1519
 
1516
-
1517
1520
  def _is_resumable_boundary(self, event: Any) -> bool:
1518
1521
  """判断 ADK event 是否为可恢复边界。"""
1519
1522
  # 工具调用请求
@@ -1527,6 +1530,83 @@ class ADKRunner(BaseRunner):
1527
1530
  return True
1528
1531
  return False
1529
1532
 
1533
+ def _extract_approval_signals(self, event: Any) -> list[dict[str, Any]]:
1534
+ """从 ADK event 提取原生审批信号,翻译成统一 interrupt_info(供 runtime:4645 消费)。
1535
+
1536
+ 覆盖 ADK 两套原生 HITL 机制,经 ``adk_compat`` 能力探测,兼容 1.34.x → 2.x:
1537
+
1538
+ 1. **tool-confirmation**(``actions.requested_tool_confirmations``):工具执行前
1539
+ yes/no 审批,1.34+ 均有。→ ``kind="tool"``。
1540
+ 2. **workflow HITL**(v2.0+):``event.interrupted=True`` + ``RequestInput`` 信号
1541
+ (message/payload/response_schema)。→ ``kind="input"``。
1542
+
1543
+ 命中任一即返回 ``[{"approval_request_id","tool_name","args","kind","message"}, ...]``,
1544
+ 否则返回 ``[]``。不引入对 v2.0 API 的硬依赖——字段缺失时跳过,不报错。
1545
+ """
1546
+ signals: list[dict[str, Any]] = []
1547
+ actions = getattr(event, "actions", None)
1548
+
1549
+ # 1) tool-confirmation:requested_tool_confirmations(1.34+,经 getattr 容错)
1550
+ if actions is not None:
1551
+ confirmations = getattr(actions, "requested_tool_confirmations", None)
1552
+ if confirmations:
1553
+ for conf in confirmations:
1554
+ if conf is None:
1555
+ continue
1556
+ conf_id = str(
1557
+ getattr(conf, "id", "") or getattr(conf, "approval_request_id", "") or ""
1558
+ )
1559
+ if not conf_id:
1560
+ continue
1561
+ signals.append(
1562
+ {
1563
+ "approval_request_id": conf_id,
1564
+ "tool_name": str(
1565
+ getattr(conf, "tool_name", "")
1566
+ or getattr(conf, "name", "")
1567
+ or "tool"
1568
+ ),
1569
+ "args": dict(getattr(conf, "args", None) or {}),
1570
+ "kind": "tool",
1571
+ "message": str(
1572
+ getattr(conf, "message", "") or getattr(conf, "hint", "") or ""
1573
+ ),
1574
+ }
1575
+ )
1576
+
1577
+ # 2) workflow HITL(v2.0+):event.interrupted + RequestInput 信号
1578
+ # 仅当 adk_compat 探测到 v2.0+ 时检查,避免在 1.34 上访问不存在字段。
1579
+ try:
1580
+ from ksadk.compat.adk_compat import adk_version_at_least
1581
+
1582
+ if adk_version_at_least("2.0.0") and getattr(event, "interrupted", False):
1583
+ # RequestInput 信号可能挂在 actions 或 event 上(字段名跨小版本未冻结,
1584
+ # 用 getattr 容错,缺失即视为纯 interrupt 无 payload)。
1585
+ req_input = None
1586
+ if actions is not None:
1587
+ req_input = getattr(actions, "requested_input", None) or getattr(
1588
+ actions, "request_input", None
1589
+ )
1590
+ if req_input is None:
1591
+ req_input = getattr(event, "requested_input", None)
1592
+ interrupt_id = "adk-hitl-" + str(getattr(event, "invocation_id", "") or "unknown")
1593
+ signals.append(
1594
+ {
1595
+ "approval_request_id": interrupt_id,
1596
+ "tool_name": "RequestInput",
1597
+ "args": dict(getattr(req_input, "payload", None) or {}),
1598
+ "kind": "input",
1599
+ "message": str(
1600
+ getattr(req_input, "message", "") or "ADK workflow 请求人工输入"
1601
+ ),
1602
+ }
1603
+ )
1604
+ except Exception:
1605
+ # 能力探测失败时不阻塞主流(审批 surface 是 best-effort,不降级为报错)。
1606
+ pass
1607
+
1608
+ return signals
1609
+
1530
1610
  async def _maybe_write_checkpoint(
1531
1611
  self,
1532
1612
  *,
@@ -1573,9 +1653,10 @@ class ADKRunner(BaseRunner):
1573
1653
  metadata=metadata,
1574
1654
  )
1575
1655
  logger.debug(
1576
- "ADKRunner: wrote checkpoint adk-ckpt-%d at boundary "
1577
- "(session=%s, invocation_id=%s)",
1578
- checkpoint_seq, session_id, adk_invocation_id,
1656
+ "ADKRunner: wrote checkpoint adk-ckpt-%d at boundary " "(session=%s, invocation_id=%s)",
1657
+ checkpoint_seq,
1658
+ session_id,
1659
+ adk_invocation_id,
1579
1660
  )
1580
1661
 
1581
1662
  def _extract_checkpoint_metadata(self, event: Any) -> dict[str, Any]:
@@ -1602,8 +1683,7 @@ class ADKRunner(BaseRunner):
1602
1683
 
1603
1684
  # 是否可恢复
1604
1685
  stm_backend = (
1605
- getattr(self._short_term_memory, "backend", None)
1606
- if self._short_term_memory else None
1686
+ getattr(self._short_term_memory, "backend", None) if self._short_term_memory else None
1607
1687
  )
1608
1688
  shared_across_pods = stm_backend == "database"
1609
1689
  platform_resumable = self._resumable and shared_across_pods
@@ -1655,9 +1735,9 @@ class ADKRunner(BaseRunner):
1655
1735
  )
1656
1736
  if not adk_invocation_id:
1657
1737
  logger.error(
1658
- "Resume requested but ADK invocation_id not found for "
1659
- "session=%s ksadk_inv=%s",
1660
- session_id, ksadk_invocation_id,
1738
+ "Resume requested but ADK invocation_id not found for " "session=%s ksadk_inv=%s",
1739
+ session_id,
1740
+ ksadk_invocation_id,
1661
1741
  )
1662
1742
  raise ValueError(
1663
1743
  f"checkpoint_not_resumable: ADK invocation_id not found for "
@@ -1665,7 +1745,7 @@ class ADKRunner(BaseRunner):
1665
1745
  f"The checkpoint data may have been lost or the invocation was "
1666
1746
  f"never persisted."
1667
1747
  )
1668
- return adk_invocation_id
1748
+ return str(adk_invocation_id)
1669
1749
 
1670
1750
  async def _prepare_run_events(
1671
1751
  self,
@@ -1691,9 +1771,7 @@ class ADKRunner(BaseRunner):
1691
1771
  else:
1692
1772
  new_message = None
1693
1773
 
1694
- ksadk_invocation_id = str(
1695
- input_data.get("invocation_id") or input_data.get("run_id") or ""
1696
- )
1774
+ ksadk_invocation_id = str(input_data.get("invocation_id") or input_data.get("run_id") or "")
1697
1775
 
1698
1776
  checkpoint_run_id = (
1699
1777
  str(input_data.get("run_id") or "").strip()
@@ -1725,6 +1803,8 @@ class ADKRunner(BaseRunner):
1725
1803
  run_kwargs["new_message"] = new_message
1726
1804
  run_kwargs["state_delta"] = self._build_state_delta(input_data) or None
1727
1805
 
1806
+ if self._runner is None:
1807
+ raise RuntimeError("ADK runner is not initialized")
1728
1808
  events_async = self._runner.run_async(**run_kwargs)
1729
1809
 
1730
1810
  if ksadk_invocation_id and self._resumable:
@@ -1740,7 +1820,7 @@ class ADKRunner(BaseRunner):
1740
1820
  session_id=session_id,
1741
1821
  ksadk_invocation_id=ksadk_invocation_id,
1742
1822
  )
1743
- return wrapped_async
1823
+ return cast(AsyncIterator[Any], wrapped_async)
1744
1824
 
1745
1825
  async def invoke(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
1746
1826
  """调用 ADK Agent"""
@@ -1764,9 +1844,7 @@ class ADKRunner(BaseRunner):
1764
1844
  # Set input.value for Langfuse top-level input display
1765
1845
  span.set_attribute("input.value", user_input)
1766
1846
  truncated_input = (
1767
- user_input[:200]
1768
- if isinstance(user_input, str)
1769
- else str(user_input)[:200]
1847
+ user_input[:200] if isinstance(user_input, str) else str(user_input)[:200]
1770
1848
  )
1771
1849
  span.set_attribute("user.input", truncated_input)
1772
1850
 
@@ -1819,7 +1897,7 @@ class ADKRunner(BaseRunner):
1819
1897
  # final_response may hold only intermediate fragments.
1820
1898
  last_event_text = ""
1821
1899
  if last_event is not None and hasattr(last_event, "content") and last_event.content:
1822
- for part in (last_event.content.parts or []):
1900
+ for part in last_event.content.parts or []:
1823
1901
  is_thought = getattr(part, "thought", False)
1824
1902
  if hasattr(part, "text") and part.text and not is_thought:
1825
1903
  last_event_text += part.text
@@ -1835,7 +1913,10 @@ class ADKRunner(BaseRunner):
1835
1913
  # Set output.value for Langfuse top-level output display
1836
1914
  span.set_attribute("output.value", final_response[:5000] if final_response else "")
1837
1915
  span.set_attribute("agent.output", final_response[:500] if final_response else "")
1838
- result = {"output": final_response, "events": events_list}
1916
+ result: dict[str, Any] = {
1917
+ "output": final_response,
1918
+ "events": events_list,
1919
+ }
1839
1920
  if usage:
1840
1921
  result["usage"] = usage
1841
1922
  # last_usage = 最后一次 LLM 调用快照(input_tokens = 当前上下文窗口占用)
@@ -1847,7 +1928,7 @@ class ADKRunner(BaseRunner):
1847
1928
 
1848
1929
  使用 StreamingMode.SSE 启用真正的流式 token 输出
1849
1930
  """
1850
- from google.adk.agents.run_config import RunConfig, StreamingMode
1931
+ from ksadk.compat.adk_compat import RunConfig, StreamingMode
1851
1932
 
1852
1933
  # 判断是否为恢复调用 — 提前判断以避免将 resume_input dict 当作 string 处理
1853
1934
  is_resume = bool(input_data.get("checkpoint_resume"))
@@ -1868,9 +1949,7 @@ class ADKRunner(BaseRunner):
1868
1949
  # Set input.value for Langfuse top-level input display
1869
1950
  span.set_attribute("input.value", user_input)
1870
1951
  truncated_input = (
1871
- user_input[:200]
1872
- if isinstance(user_input, str)
1873
- else str(user_input)[:200]
1952
+ user_input[:200] if isinstance(user_input, str) else str(user_input)[:200]
1874
1953
  )
1875
1954
  span.set_attribute("user.input", truncated_input)
1876
1955
 
@@ -1899,6 +1978,13 @@ class ADKRunner(BaseRunner):
1899
1978
  accumulated_text = ""
1900
1979
  usage: dict[str, Any] = {}
1901
1980
  last_usage: dict[str, Any] = {}
1981
+ # handoff/sub-agent(如 RemoteA2aAgent)回复事件 partial=None,不进上面
1982
+ # partial 分支;且远端 agent 经 A2A 流式返回时,ADK 会产出多个"累积快照"
1983
+ # 事件(后一个含前一个内容)。按 author 记录已输出快照,只补发增量去重。
1984
+ sub_agent_snapshots: dict[str, str] = {}
1985
+ sub_agent_thought_snapshots: dict[str, str] = {}
1986
+ sub_agent_last_output_kind: dict[str, str] = {}
1987
+ top_agent_name = getattr(self._agent, "name", None)
1902
1988
 
1903
1989
  async for event in wrapped_async:
1904
1990
  event_usage = self._extract_event_usage(event)
@@ -1908,15 +1994,137 @@ class ADKRunner(BaseRunner):
1908
1994
  # Only yield text delta if event is partial to avoid duplication of final summary
1909
1995
  if hasattr(event, "content") and event.content and getattr(event, "partial", False):
1910
1996
  if hasattr(event.content, "parts"):
1997
+ author = getattr(event, "author", None)
1998
+ is_sub_agent = bool(author and top_agent_name and author != top_agent_name)
1999
+ author_key = str(author)
1911
2000
  for part in event.content.parts:
1912
2001
  if hasattr(part, "text") and part.text:
1913
2002
  is_thought = getattr(part, "thought", False)
1914
- accumulated_text += part.text
2003
+ replace_snapshot = bool(
2004
+ is_sub_agent
2005
+ and _part_metadata_flag(part, "ksadk_output_snapshot")
2006
+ )
2007
+ output_delta = part.text
2008
+ # 思考内容只作为 thinking delta 流出,不计入最终输出,
2009
+ # 否则最终回复会把思考过程再重复一遍(与 invoke() 语义对齐)。
2010
+ if is_thought and is_sub_agent:
2011
+ if sub_agent_last_output_kind.get(author_key) == "text":
2012
+ # 正文之后的 thought 是一个新的 reasoning segment。
2013
+ sub_agent_thought_snapshots[author_key] = ""
2014
+ previous_thought = sub_agent_thought_snapshots.get(
2015
+ author_key, ""
2016
+ )
2017
+ if part.text.startswith(previous_thought):
2018
+ output_delta = part.text[len(previous_thought) :]
2019
+ sub_agent_thought_snapshots[author_key] = part.text
2020
+ else:
2021
+ sub_agent_thought_snapshots[author_key] = (
2022
+ previous_thought + part.text
2023
+ )
2024
+ elif not is_thought:
2025
+ accumulated_text = (
2026
+ part.text
2027
+ if replace_snapshot
2028
+ else accumulated_text + part.text
2029
+ )
2030
+ # sub-agent(RemoteA2aAgent)增量也累积进快照,供后续
2031
+ # completed 全文消息(同一份结果)在 handoff 分支去重。
2032
+ if is_sub_agent:
2033
+ sub_agent_snapshots[author_key] = (
2034
+ part.text
2035
+ if replace_snapshot
2036
+ else sub_agent_snapshots.get(author_key, "") + part.text
2037
+ )
1915
2038
  # 标记思考内容,前端可以选择是否展示
1916
- yield {
1917
- "delta": part.text,
1918
- "type": "thinking" if is_thought else "text",
1919
- }
2039
+ if output_delta:
2040
+ output_chunk: dict[str, Any] = {
2041
+ "delta": output_delta,
2042
+ "type": "thinking" if is_thought else "text",
2043
+ }
2044
+ if replace_snapshot and not is_thought:
2045
+ output_chunk["replace"] = True
2046
+ yield output_chunk
2047
+ if is_sub_agent:
2048
+ sub_agent_last_output_kind[author_key] = (
2049
+ "thinking" if is_thought else "text"
2050
+ )
2051
+ # handoff/sub-agent 回复:partial 为 None/False,上面分支跳过,这里补上。
2052
+ elif hasattr(event, "content") and event.content:
2053
+ author = getattr(event, "author", None)
2054
+ if (
2055
+ author
2056
+ and top_agent_name
2057
+ and author != top_agent_name
2058
+ and hasattr(event.content, "parts")
2059
+ ):
2060
+ author_key = str(author)
2061
+ for part in event.content.parts:
2062
+ if (
2063
+ hasattr(part, "text")
2064
+ and part.text
2065
+ and getattr(part, "thought", False)
2066
+ ):
2067
+ # A2A's terminal artifact is converted by ADK to
2068
+ # partial=False. Once that sub-agent has already
2069
+ # emitted response text, a trailing thought here is
2070
+ # a terminal snapshot/replacement (not a new
2071
+ # interleaved reasoning step). Showing it makes
2072
+ # the UI appear to end at "thinking" and hides the
2073
+ # just-emitted final response.
2074
+ if sub_agent_last_output_kind.get(author_key) == "text":
2075
+ continue
2076
+ # RemoteA2aAgent 将 last_chunk=True 映射成
2077
+ # partial=False;这可能是一个此前 partial thought
2078
+ # 的终态快照。只补发新增内容,避免正文之后再显示一遍
2079
+ # 相同的思考块;若此前没有 partial thought,仍完整透传。
2080
+ previous_thought = sub_agent_thought_snapshots.get(
2081
+ author_key, ""
2082
+ )
2083
+ if part.text.startswith(previous_thought):
2084
+ thought_delta = part.text[len(previous_thought) :]
2085
+ sub_agent_thought_snapshots[author_key] = part.text
2086
+ elif previous_thought.startswith(part.text):
2087
+ thought_delta = ""
2088
+ else:
2089
+ thought_delta = part.text
2090
+ sub_agent_thought_snapshots[author_key] = (
2091
+ previous_thought + part.text
2092
+ )
2093
+ if thought_delta:
2094
+ yield {"delta": thought_delta, "type": "thinking"}
2095
+ snapshot = ""
2096
+ replace_snapshot = False
2097
+ for part in event.content.parts:
2098
+ if hasattr(part, "text") and part.text and not getattr(
2099
+ part, "thought", False
2100
+ ):
2101
+ snapshot += part.text
2102
+ replace_snapshot = replace_snapshot or _part_metadata_flag(
2103
+ part,
2104
+ "ksadk_output_snapshot",
2105
+ )
2106
+ if snapshot:
2107
+ prev = sub_agent_snapshots.get(author_key, "")
2108
+ if replace_snapshot:
2109
+ delta = snapshot
2110
+ sub_agent_snapshots[author_key] = snapshot
2111
+ accumulated_text = snapshot
2112
+ elif snapshot.startswith(prev):
2113
+ # 累计快照(如 completed 全文):只补发超出已累积的部分;
2114
+ # 若增量已发全,delta 为空,避免 completed 再渲染一遍。
2115
+ delta = snapshot[len(prev) :]
2116
+ sub_agent_snapshots[author_key] = snapshot
2117
+ else:
2118
+ # final 增量(小块):追加进累积,不覆盖。
2119
+ delta = snapshot
2120
+ sub_agent_snapshots[author_key] = prev + snapshot
2121
+ if delta:
2122
+ if not replace_snapshot:
2123
+ accumulated_text += delta
2124
+ output_chunk = {"delta": delta, "type": "text"}
2125
+ if replace_snapshot:
2126
+ output_chunk["replace"] = True
2127
+ yield output_chunk
1920
2128
 
1921
2129
  # 处理工具调用事件 — ADK 通过 event.content.parts[].function_call
1922
2130
  # 发出工具调用(即 event.get_function_calls()),而非
@@ -1935,6 +2143,7 @@ class ADKRunner(BaseRunner):
1935
2143
  if not isinstance(fc_args, dict):
1936
2144
  try:
1937
2145
  import json as _json
2146
+
1938
2147
  fc_args = _json.loads(fc_args) if isinstance(fc_args, str) else {}
1939
2148
  except Exception:
1940
2149
  fc_args = {}
@@ -1958,6 +2167,21 @@ class ADKRunner(BaseRunner):
1958
2167
  "tool_args": getattr(tool_call, "input", {}),
1959
2168
  }
1960
2169
 
2170
+ # ADK 原生审批 surface(tool-confirmation + v2.0 workflow HITL):
2171
+ # 翻译成统一 interrupt chunk,经 runtime:4645 既有 approval_request 通道
2172
+ # 消费 → 审批卡。能力探测兼容 1.34.x→2.x,缺字段则跳过。
2173
+ emitted_approval_ids: set[str] = set()
2174
+ for signal in self._extract_approval_signals(event):
2175
+ aid = signal.get("approval_request_id", "")
2176
+ if not aid or aid in emitted_approval_ids:
2177
+ continue
2178
+ emitted_approval_ids.add(aid)
2179
+ yield {
2180
+ "type": "interrupt",
2181
+ "interrupt_info": signal,
2182
+ "session_id": session_id,
2183
+ }
2184
+
1961
2185
  # 处理工具返回结果 — ADK 通过 event.content.parts[].function_response
1962
2186
  # 发出工具执行结果。当工具执行完毕后 ADK 会产生一个包含
1963
2187
  # function_response 的事件,此处将其转为 tool_result 语义事件,
@@ -1971,6 +2195,7 @@ class ADKRunner(BaseRunner):
1971
2195
  if not isinstance(fr_output, dict):
1972
2196
  try:
1973
2197
  import json as _json2
2198
+
1974
2199
  if isinstance(fr_output, str):
1975
2200
  fr_output = _json2.loads(fr_output)
1976
2201
  else: