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
ksadk/a2a/executor.py CHANGED
@@ -1,78 +1,288 @@
1
- """A2A executor that adapts a KsADK runner to the A2A server contract."""
1
+ """A2ARuntimeExecutor — 把 ksadk runner 桥接进 A2A 请求生命周期 (goal-05)。
2
+
3
+ 契约 §7.2:A2A ``context_id`` ↔ Runtime ``session_id``;``canceled`` ↔
4
+ ``RuntimeAdapter.cancel(invocation_id)``。executor 不在此自造 cancel,而是委托
5
+ :class:`~ksadk.a2a.task_adapter.A2ARuntimeTaskAdapter`(其内部走 RuntimeAdapter.cancel,
6
+ G0.3 已冻结)。
7
+
8
+ wire 对象在 a2a-sdk 1.1.0 是 protobuf(``a2a_pb2``);文本用 ``Part(text=...)``,
9
+ 消息用 ``Message(role=Role.ROLE_AGENT, parts=[...])``。
10
+ """
2
11
 
3
12
  from __future__ import annotations
4
13
 
14
+ import inspect
5
15
  import json
6
16
  import logging
7
- from collections.abc import AsyncIterator
8
- from typing import Any
17
+ from typing import Any, Literal
9
18
 
10
19
  from a2a.server.agent_execution import AgentExecutor, RequestContext
11
20
  from a2a.server.events import EventQueue
12
21
  from a2a.server.tasks import TaskUpdater
13
- from a2a.types import Part, TextPart
22
+ from a2a.types import Part, Task, TaskState, TaskStatus
23
+ from a2a.utils.errors import TaskNotCancelableError
24
+
25
+ from ksadk.a2a.resume_store import A2AResumePayloadKind
26
+ from ksadk.events import EventType, RuntimeEvent
27
+ from ksadk.runtime import CancelResult, RunHandle
14
28
 
15
29
  logger = logging.getLogger(__name__)
16
30
 
31
+ #: ADK v2 A2A 集成扩展标记 URI(adk.dev/a2a/a2a-extension)。
32
+ #: 在 Task/status metadata 里塞入该 key 且值非空时,ADK ``RemoteA2aAgent`` 走
33
+ #: ``_handle_a2a_response_v2``——处理每个 ``artifact_update``(含 append=True 增量),
34
+ #: 不再像 v1 那样只放行首块+末块、丢弃中间增量(remote_a2a_agent.py:546/762)。
35
+ #: ksadk 自研 executor 必须带这个标记,否则编排侧 sub-agent 流式被压成"一次性"。
36
+ ADK_V2_INTEGRATION_EXTENSION_URI = "https://google.github.io/adk-docs/a2a/a2a-extension/"
17
37
 
18
- class KsAgentExecutor(AgentExecutor):
19
- """Execute a KsADK runner inside the A2A request lifecycle."""
38
+ #: 扩展标记值(与 google.adk ``A2aAgentExecutor._get_invocation_metadata`` 对齐)。
39
+ ADK_V2_INTEGRATION_METADATA: dict[str, Any] = {
40
+ ADK_V2_INTEGRATION_EXTENSION_URI: {"adk_agent_executor_v2": True}
41
+ }
42
+
43
+ _OUTPUT_SNAPSHOT_METADATA = {"ksadk_output_snapshot": True}
44
+ _ArtifactKind = Literal["text", "thinking"]
20
45
 
21
- def __init__(self, runner: Any, prefer_stream: bool = True) -> None:
22
- self.runner = runner
23
- self.prefer_stream = prefer_stream
24
46
 
25
- async def execute(
47
+ def _thought_part(text: str) -> Part:
48
+ """构造可被 ADK RemoteA2aAgent 识别为 thought 的 A2A Part。"""
49
+ return Part(text=text, metadata={"adk_thought": True})
50
+
51
+
52
+ class _ArtifactStreamEmitter:
53
+ """按连续类型分段输出 A2A artifact,并正确终止每个 artifact stream。"""
54
+
55
+ def __init__(self, updater: TaskUpdater, task_id: str) -> None:
56
+ self._updater = updater
57
+ self._task_id = task_id
58
+ self._active_kind: _ArtifactKind | None = None
59
+ self._segments: dict[_ArtifactKind, int] = {"text": 0, "thinking": 0}
60
+ self._emitted: dict[tuple[_ArtifactKind, int], int] = {}
61
+ self._pending: tuple[_ArtifactKind, str, bool, int] | None = None
62
+
63
+ async def push(
26
64
  self,
27
- context: RequestContext,
28
- event_queue: EventQueue,
65
+ kind: _ArtifactKind,
66
+ text: str,
67
+ *,
68
+ replace_snapshot: bool = False,
69
+ ) -> None:
70
+ if self._pending is not None:
71
+ switched = self._pending[0] != kind
72
+ await self._emit(self._pending, last_chunk=switched)
73
+ if self._active_kind != kind:
74
+ self._segments[kind] += 1
75
+ self._active_kind = kind
76
+ self._pending = (kind, text, replace_snapshot, self._segments[kind])
77
+
78
+ async def close(self) -> None:
79
+ if self._pending is None:
80
+ return
81
+ await self._emit(self._pending, last_chunk=True)
82
+ self._pending = None
83
+
84
+ async def _emit(
85
+ self,
86
+ pending: tuple[_ArtifactKind, str, bool, int],
87
+ *,
88
+ last_chunk: bool,
89
+ ) -> None:
90
+ kind, text, replace_snapshot, segment = pending
91
+ key = (kind, segment)
92
+ self._emitted[key] = self._emitted.get(key, 0) + 1
93
+ is_reasoning = kind == "thinking"
94
+ base_id = (
95
+ f"{self._task_id}-reasoning"
96
+ if is_reasoning
97
+ else f"{self._task_id}-response"
98
+ )
99
+ artifact_id = base_id if segment == 1 else f"{base_id}-{segment}"
100
+ part = (
101
+ _thought_part(text)
102
+ if is_reasoning
103
+ else Part(
104
+ text=text,
105
+ metadata=(
106
+ dict(_OUTPUT_SNAPSHOT_METADATA)
107
+ if replace_snapshot
108
+ else None
109
+ ),
110
+ )
111
+ )
112
+ await self._updater.add_artifact(
113
+ parts=[part],
114
+ artifact_id=artifact_id,
115
+ name="reasoning" if is_reasoning else "response",
116
+ append=False if replace_snapshot else self._emitted[key] > 1,
117
+ last_chunk=last_chunk,
118
+ )
119
+
120
+
121
+ async def _enqueue_initial_task(context: RequestContext, event_queue: EventQueue) -> None:
122
+ """先入队初始 ``Task`` 对象,再发状态更新(a2a-sdk 1.1.0 生命周期要求:
123
+ TaskStatusUpdateEvent 之前必须已有 Task)。``current_task`` 为续跑任务时直接用,
124
+ 否则新建 submitted 任务。Task metadata 带 ADK v2 扩展标记,让 RemoteA2aAgent
125
+ 走 v2 handler 保留全部流式增量。
126
+ """
127
+ task = getattr(context, "current_task", None)
128
+ if task is None:
129
+ task = Task(
130
+ id=context.task_id,
131
+ context_id=context.context_id,
132
+ status=TaskStatus(state=TaskState.TASK_STATE_SUBMITTED),
133
+ metadata=dict(ADK_V2_INTEGRATION_METADATA),
134
+ )
135
+ await event_queue.enqueue_event(task)
136
+
137
+
138
+ class _InputRequired(Exception):
139
+ """runner 请求用户输入的信号(内部用于跳出 execute,task 停 input-required)。"""
140
+
141
+
142
+ class _RunCanceled(Exception):
143
+ """Runtime 已取消本次执行,executor 不得再发 completed。"""
144
+
145
+
146
+ class A2ARuntimeExecutor(AgentExecutor):
147
+ """在 A2A 请求生命周期内执行 ksadk runner。
148
+
149
+ - sync: ``runner.invoke``。
150
+ - streaming: ``runner.stream``,逐 chunk 发 artifact。
151
+ - cancel: 委托 ``task_adapter.cancel_task``(内部走 RuntimeAdapter.cancel)。
152
+ """
153
+
154
+ def __init__(
155
+ self,
156
+ runner: Any,
157
+ task_adapter: Any = None,
158
+ prefer_stream: bool = True,
159
+ include_reasoning: bool = False,
29
160
  ) -> None:
161
+ self.runner = runner
162
+ self.task_adapter = task_adapter
163
+ self.prefer_stream = prefer_stream
164
+ self.include_reasoning = include_reasoning
165
+
166
+ async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
167
+ current_task = getattr(context, "current_task", None)
168
+ durable_context_id = str(
169
+ getattr(current_task, "context_id", "") or context.context_id or "unknown-context"
170
+ )
30
171
  updater = TaskUpdater(
31
172
  event_queue=event_queue,
32
173
  task_id=context.task_id or "unknown-task",
33
- context_id=context.context_id or "unknown-context",
174
+ context_id=durable_context_id,
175
+ )
176
+ is_resume = (
177
+ current_task is not None
178
+ and getattr(getattr(current_task, "status", None), "state", None)
179
+ == TaskState.TASK_STATE_INPUT_REQUIRED
34
180
  )
181
+ interaction_response: Any = None
182
+ if self.task_adapter is not None:
183
+ # Third-party/local adapters written before durable context mapping
184
+ # do not necessarily provide this optional lifecycle hook.
185
+ prepare_context = getattr(self.task_adapter, "prepare_context", None)
186
+ if callable(prepare_context):
187
+ await prepare_context(context)
188
+ if is_resume and self.task_adapter is not None:
189
+ interaction_response = self.task_adapter.answer_from_context(context)
190
+ # Invalid resume tokens/decisions are request errors. Validate before emitting
191
+ # working so the durable Task remains input-required and retryable.
192
+ await self.task_adapter.validate_resume_task(
193
+ context.task_id or "",
194
+ context,
195
+ answer=interaction_response,
196
+ )
35
197
 
198
+ handle: RunHandle | None = None
36
199
  try:
37
- await updater.start_work()
200
+ # §7.2:当前任务处于 input-required 时,本条消息是续跑回包(checkpoint/resume)。
201
+ if not is_resume:
202
+ await _enqueue_initial_task(context, event_queue)
203
+ # start_work 不透传 metadata;直接 update_status 以带 ADK v2 扩展标记
204
+ # (RemoteA2aAgent 读 task/status metadata 决定走 v2 全增量 handler)。
205
+ await updater.update_status(
206
+ TaskState.TASK_STATE_WORKING,
207
+ metadata=dict(ADK_V2_INTEGRATION_METADATA),
208
+ )
38
209
  runner_input = self._build_runner_input(context)
39
- output = await self._run_runner(context, updater, runner_input)
210
+ if is_resume:
211
+ if self.task_adapter is None:
212
+ raise RuntimeError("runtime task adapter is required to resume A2A task")
213
+ handle = await self.task_adapter.resume_task(
214
+ context.task_id or "",
215
+ context,
216
+ answer=interaction_response,
217
+ )
218
+ output = await self._run_runtime(context, updater, handle)
219
+ elif self.task_adapter is not None:
220
+ handle = await self.task_adapter.start_task(
221
+ task_id=str(context.task_id or ""),
222
+ context=context,
223
+ input_data=runner_input.get("input"),
224
+ )
225
+ output = await self._run_runtime(context, updater, handle)
226
+ else:
227
+ output = await self._run_runner(context, updater, runner_input)
228
+ # completed 携带全文消息:非流式消费端与 text.completed 投影(§ event_adapter
229
+ # message_to_event final)依赖它拿最终结果;流式消费端的重复由 adk_runner
230
+ # 在 handoff 分支对"增量累积"去重解决(不在此处删消息)。
40
231
  completion_message = (
41
- updater.new_agent_message(parts=[Part(root=TextPart(text=output))])
42
- if output
43
- else None
232
+ updater.new_agent_message(parts=[Part(text=output)]) if output else None
44
233
  )
45
234
  await updater.complete(message=completion_message)
46
- except Exception as exc:
47
- logger.exception("A2A task execution failed")
48
- error_text = self._coerce_text(exc)
235
+ await self._forget_task(context, handle)
236
+ except _InputRequired:
237
+ # runner 请求输入:task 已停在 input-required(附 resume token),不 complete。
238
+ logger.info("A2A task %s 进入 input-required", context.task_id)
239
+ except _RunCanceled:
240
+ logger.info("A2A task %s 已由 runtime 取消", context.task_id)
241
+ await self._forget_task(context, handle)
242
+ except Exception as exc: # noqa: BLE001
243
+ logger.error("A2A task execution failed (%s)", type(exc).__name__)
49
244
  await updater.failed(
50
- message=updater.new_agent_message(
51
- parts=[Part(root=TextPart(text=error_text))]
52
- )
245
+ message=updater.new_agent_message(parts=[Part(text="A2A task execution failed")])
53
246
  )
247
+ await self._forget_task(context, handle)
54
248
 
55
- async def cancel(
56
- self,
57
- context: RequestContext,
58
- event_queue: EventQueue,
59
- ) -> None:
249
+ async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
250
+ # §7.4:cancel 统一由 adapter 提供。有 RuntimeAdapter → 尊重其 CancelResult,
251
+ # 只有底层真取消(CANCELLED)才把协议 Task 置 canceled;其余状态如实抛
252
+ # TaskNotCancelableError 拒绝,不伪造终态。无 adapter 时底层状态未知,
253
+ # 同样拒绝取消。
254
+ current_task = getattr(context, "current_task", None)
60
255
  updater = TaskUpdater(
61
256
  event_queue=event_queue,
62
257
  task_id=context.task_id or "unknown-task",
63
- context_id=context.context_id or "unknown-context",
258
+ context_id=str(
259
+ getattr(current_task, "context_id", "") or context.context_id or "unknown-context"
260
+ ),
64
261
  )
65
- await updater.cancel(
66
- message=updater.new_agent_message(
67
- parts=[Part(root=TextPart(text="Request canceled"))]
262
+ if self.task_adapter is None:
263
+ raise TaskNotCancelableError(message="runtime task adapter is not configured")
264
+ result = await self.task_adapter.cancel_task(context.task_id or "", context)
265
+ # 只有底层真的接受了取消(已中断活跃 turn,或已登记 pending cancel)才把
266
+ # 协议 Task 置 canceled;其他结果如实拒绝,包括未来新增的 UNSUPPORTED。
267
+ if result not in (
268
+ CancelResult.INTERRUPTED_ACTIVE_TURN,
269
+ CancelResult.PENDING_CANCEL_RECORDED,
270
+ ):
271
+ result_value = getattr(result, "value", result)
272
+ raise TaskNotCancelableError(
273
+ message=f"underlying runtime cancel did not apply: {result_value}"
68
274
  )
275
+ await updater.cancel(
276
+ message=updater.new_agent_message(parts=[Part(text="Request canceled")])
69
277
  )
278
+ clear_resume_state = getattr(self.task_adapter, "clear_resume_state", None)
279
+ if callable(clear_resume_state):
280
+ result = clear_resume_state(context.task_id or "", context)
281
+ if inspect.isawaitable(result):
282
+ await result
70
283
 
71
284
  async def _run_runner(
72
- self,
73
- context: RequestContext,
74
- updater: TaskUpdater,
75
- runner_input: dict[str, Any],
285
+ self, context: RequestContext, updater: TaskUpdater, runner_input: dict[str, Any]
76
286
  ) -> str:
77
287
  stream = getattr(self.runner, "stream", None)
78
288
  if self.prefer_stream and callable(stream):
@@ -82,7 +292,7 @@ class KsAgentExecutor(AgentExecutor):
82
292
  text = self._coerce_text(result)
83
293
  if text:
84
294
  await updater.add_artifact(
85
- parts=[Part(root=TextPart(text=text))],
295
+ parts=[Part(text=text)],
86
296
  artifact_id=f"{context.task_id}-response",
87
297
  name="response",
88
298
  last_chunk=True,
@@ -97,85 +307,53 @@ class KsAgentExecutor(AgentExecutor):
97
307
  runner_input: dict[str, Any],
98
308
  ) -> str:
99
309
  output_text = ""
100
- emitted_chunks = 0
101
- artifact_id = f"{context.task_id}-response"
310
+ artifacts = _ArtifactStreamEmitter(updater, str(context.task_id))
102
311
 
103
- async for chunk, last_chunk in self._with_last_flag(stream(runner_input)):
312
+ async for chunk in stream(runner_input):
104
313
  chunk_type = chunk.get("type") if isinstance(chunk, dict) else None
314
+ if chunk_type == "input_required":
315
+ raise RuntimeError(
316
+ "runner emitted input_required without a RuntimeAdapter execution path"
317
+ )
105
318
  if chunk_type == "final":
106
319
  final_text = (
107
- self._coerce_text(chunk.get("output"))
108
- if isinstance(chunk, dict)
109
- else ""
320
+ self._coerce_text(chunk.get("output")) if isinstance(chunk, dict) else ""
110
321
  )
111
322
  if not final_text:
112
323
  continue
113
324
  if not output_text:
114
325
  output_text = final_text
326
+ await artifacts.push("text", final_text)
115
327
  elif final_text.startswith(output_text):
116
328
  suffix = final_text[len(output_text) :]
117
329
  output_text = final_text
118
330
  if suffix:
119
- emitted_chunks += 1
120
- await updater.add_artifact(
121
- parts=[Part(root=TextPart(text=suffix))],
122
- artifact_id=artifact_id,
123
- name="response",
124
- append=bool(emitted_chunks > 1),
125
- last_chunk=last_chunk,
126
- )
331
+ await artifacts.push("text", suffix)
127
332
  continue
128
333
  else:
129
334
  output_text = final_text
130
- await updater.add_artifact(
131
- parts=[Part(root=TextPart(text=final_text))],
132
- artifact_id=artifact_id,
133
- name="response",
134
- append=False,
135
- last_chunk=last_chunk,
136
- )
335
+ await artifacts.push("text", final_text, replace_snapshot=True)
137
336
  continue
138
337
 
139
338
  text = self._coerce_text(chunk)
140
339
  if not text:
141
340
  continue
341
+ if chunk_type == "thinking":
342
+ if self.include_reasoning:
343
+ await artifacts.push("thinking", text)
344
+ continue
142
345
  output_text += text
143
- emitted_chunks += 1
144
- await updater.add_artifact(
145
- parts=[Part(root=TextPart(text=text))],
146
- artifact_id=artifact_id,
147
- name="response",
148
- append=bool(emitted_chunks > 1),
149
- last_chunk=last_chunk,
150
- )
346
+ await artifacts.push("text", text)
151
347
 
348
+ await artifacts.close()
152
349
  return output_text
153
350
 
154
- async def _with_last_flag(
155
- self,
156
- iterator: AsyncIterator[Any],
157
- ) -> AsyncIterator[tuple[Any, bool]]:
158
- try:
159
- previous = await anext(iterator)
160
- except StopAsyncIteration:
161
- return
162
-
163
- while True:
164
- try:
165
- current = await anext(iterator)
166
- except StopAsyncIteration:
167
- yield previous, True
168
- return
169
- yield previous, False
170
- previous = current
171
-
172
- @staticmethod
173
- def _build_runner_input(context: RequestContext) -> dict[str, Any]:
174
- metadata = dict(context.metadata)
351
+ def _build_runner_input(self, context: RequestContext) -> dict[str, Any]:
352
+ metadata = dict(getattr(context, "metadata", None) or {})
175
353
  state = metadata.get("state", {})
176
354
  if not isinstance(state, dict):
177
355
  state = {}
178
-
356
+ # §7.2: A2A context_id ↔ Runtime session_id。
179
357
  return {
180
358
  "input": context.get_user_input(),
181
359
  "task_id": context.task_id,
@@ -186,6 +364,142 @@ class KsAgentExecutor(AgentExecutor):
186
364
  "metadata": metadata,
187
365
  }
188
366
 
367
+ async def _run_runtime(
368
+ self,
369
+ context: RequestContext,
370
+ updater: TaskUpdater,
371
+ handle: RunHandle,
372
+ ) -> str:
373
+ output_text = ""
374
+ artifacts = _ArtifactStreamEmitter(updater, str(context.task_id))
375
+ reasoning_text = ""
376
+ input_required = False
377
+ input_prompt = "Input required"
378
+ checkpoint_id: str | None = None
379
+ call_id: str | None = None
380
+ payload_kind: A2AResumePayloadKind = "hitl_answer"
381
+
382
+ async for event in self.task_adapter.stream_task(handle):
383
+ if not isinstance(event, RuntimeEvent):
384
+ raise TypeError("RuntimeAdapter.stream must yield RuntimeEvent")
385
+ if event.event_type == EventType.RUN_FAILED:
386
+ raise RuntimeError(self._coerce_text(event.payload.get("error")))
387
+ if event.event_type == EventType.RUN_CANCELED:
388
+ await artifacts.close()
389
+ if not self._cancel_was_accepted(context, handle):
390
+ await updater.cancel(
391
+ message=updater.new_agent_message(parts=[Part(text="Request canceled")])
392
+ )
393
+ raise _RunCanceled()
394
+ if event.event_type == EventType.APPROVAL_REQUESTED:
395
+ input_required = True
396
+ payload_kind = "approval_decision"
397
+ call_id = (
398
+ str(event.payload.get("call_id") or event.payload.get("approval_id") or "")
399
+ or None
400
+ )
401
+ detail = event.payload.get("detail")
402
+ if isinstance(detail, dict):
403
+ input_prompt = self._coerce_text(
404
+ detail.get("prompt") or detail.get("message") or input_prompt
405
+ )
406
+ continue
407
+ if event.event_type == EventType.CHECKPOINT_CREATED:
408
+ checkpoint_id = str(event.payload.get("checkpoint_id") or "") or None
409
+ continue
410
+ if event.event_type == EventType.RUN_INTERRUPTED:
411
+ input_required = True
412
+ input_prompt = self._coerce_text(
413
+ event.payload.get("prompt") or event.payload.get("message") or input_prompt
414
+ )
415
+ continue
416
+ if event.event_type not in {
417
+ EventType.TEXT_DELTA,
418
+ EventType.TEXT_COMPLETED,
419
+ EventType.REASONING_DELTA,
420
+ EventType.REASONING_COMPLETED,
421
+ }:
422
+ continue
423
+ text = self._coerce_text(event.payload.get("text"))
424
+ if not text:
425
+ continue
426
+ if event.event_type == EventType.REASONING_COMPLETED:
427
+ if not self.include_reasoning:
428
+ continue
429
+ if not reasoning_text:
430
+ delta = text
431
+ reasoning_text = text
432
+ elif text.startswith(reasoning_text):
433
+ delta = text[len(reasoning_text) :]
434
+ reasoning_text = text
435
+ else:
436
+ delta = text
437
+ reasoning_text += text
438
+ if delta:
439
+ await artifacts.push("thinking", delta)
440
+ continue
441
+ if event.event_type == EventType.REASONING_DELTA:
442
+ if not self.include_reasoning:
443
+ continue
444
+ reasoning_text += text
445
+ await artifacts.push("thinking", text)
446
+ continue
447
+ # TEXT_COMPLETED 是累计全文,去重只发新增 suffix;TEXT_DELTA 是增量直接透传。
448
+ if event.event_type == EventType.TEXT_COMPLETED:
449
+ if not output_text:
450
+ delta = text
451
+ output_text = text
452
+ replace_snapshot = False
453
+ elif text.startswith(output_text):
454
+ delta = text[len(output_text) :]
455
+ output_text = text
456
+ replace_snapshot = False
457
+ else:
458
+ delta = text
459
+ output_text = text
460
+ replace_snapshot = True
461
+ else:
462
+ delta = text
463
+ output_text += text
464
+ replace_snapshot = False
465
+ if not delta:
466
+ continue
467
+ await artifacts.push("text", delta, replace_snapshot=replace_snapshot)
468
+ if self._cancel_was_accepted(context, handle):
469
+ await artifacts.close()
470
+ raise _RunCanceled()
471
+ await artifacts.close()
472
+ if input_required:
473
+ await self.task_adapter.persist_resume_state(
474
+ task_id=str(context.task_id or ""),
475
+ context=context,
476
+ handle=handle,
477
+ checkpoint_id=checkpoint_id,
478
+ call_id=call_id,
479
+ payload_kind=payload_kind,
480
+ )
481
+ await updater.update_status(
482
+ TaskState.TASK_STATE_INPUT_REQUIRED,
483
+ message=updater.new_agent_message(parts=[Part(text=input_prompt)]),
484
+ metadata=dict(ADK_V2_INTEGRATION_METADATA),
485
+ )
486
+ raise _InputRequired()
487
+ return output_text
488
+
489
+ def _cancel_was_accepted(self, context: RequestContext, handle: RunHandle) -> bool:
490
+ was_cancel_accepted = getattr(self.task_adapter, "was_cancel_accepted", None)
491
+ return bool(
492
+ callable(was_cancel_accepted)
493
+ and was_cancel_accepted(str(context.task_id or ""), context, handle)
494
+ )
495
+
496
+ async def _forget_task(self, context: RequestContext, handle: RunHandle | None) -> None:
497
+ forget_task = getattr(self.task_adapter, "forget_task", None)
498
+ if handle is not None and callable(forget_task):
499
+ result = forget_task(str(context.task_id or ""), context, handle)
500
+ if inspect.isawaitable(result):
501
+ await result
502
+
189
503
  @classmethod
190
504
  def _coerce_text(cls, payload: Any) -> str:
191
505
  if payload is None:
@@ -199,3 +513,6 @@ class KsAgentExecutor(AgentExecutor):
199
513
  return cls._coerce_text(value)
200
514
  return json.dumps(payload, ensure_ascii=False, default=str)
201
515
  return str(payload)
516
+
517
+
518
+ __all__ = ["A2ARuntimeExecutor"]