agentengine-sdk-python 0.6.9__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 (384) hide show
  1. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/METADATA +41 -23
  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 +252 -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 +533 -726
  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 +173 -90
  54. ksadk/cli/cmd_invoke.py +388 -159
  55. ksadk/cli/cmd_launch.py +64 -28
  56. ksadk/cli/cmd_mcp.py +103 -64
  57. ksadk/cli/cmd_model.py +14 -5
  58. ksadk/cli/cmd_openclaw.py +488 -228
  59. ksadk/cli/cmd_replay.py +71 -0
  60. ksadk/cli/cmd_run.py +44 -14
  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 +38 -16
  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 +501 -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 +656 -0
  97. ksadk/conversations/model_context.py +22 -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 -5187
  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 +52 -39
  120. ksadk/deployment/manager.py +63 -65
  121. ksadk/deployment/providers/serverless.py +326 -209
  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 +267 -0
  156. ksadk/memory/adk/short_term_memory.py +33 -27
  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 +1248 -164
  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 -530
  183. ksadk/runners/langgraph_runner.py +490 -95
  184. ksadk/runners/patch_langchain.py +16 -16
  185. ksadk/runners/remote_runner.py +264 -38
  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 -3743
  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-C0meuOdi.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-bj2rZnP5.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-2u1ltDIj.js → architectureDiagram-3BPJPVTR-Dej8jX8c.js} +1 -1
  229. ksadk/server/static/assets/{blockDiagram-GPEHLZMM-D6fvdK20.js → blockDiagram-GPEHLZMM-1F64jqvy.js} +1 -1
  230. ksadk/server/static/assets/{c4Diagram-AAUBKEIU-BK82L5n5.js → c4Diagram-AAUBKEIU-9q7AUAKb.js} +1 -1
  231. ksadk/server/static/assets/channel-iUGv2N24.js +1 -0
  232. ksadk/server/static/assets/{chunk-2J33WTMH-DLVdkeyQ.js → chunk-2J33WTMH-6qgwJm7Q.js} +1 -1
  233. ksadk/server/static/assets/{chunk-4BX2VUAB-BbwsYcbW.js → chunk-4BX2VUAB-CsWIQRhc.js} +1 -1
  234. ksadk/server/static/assets/{chunk-55IACEB6-DOairxzw.js → chunk-55IACEB6-BK8paA20.js} +1 -1
  235. ksadk/server/static/assets/{chunk-727SXJPM-hCO487gs.js → chunk-727SXJPM-B-JEhy7h.js} +1 -1
  236. ksadk/server/static/assets/{chunk-AQP2D5EJ-XU8vDOuG.js → chunk-AQP2D5EJ-DYBvMHAa.js} +1 -1
  237. ksadk/server/static/assets/{chunk-FMBD7UC4-PrapyfMu.js → chunk-FMBD7UC4-BGhvzDYt.js} +1 -1
  238. ksadk/server/static/assets/{chunk-ND2GUHAM-CboKhTZ3.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-_N8bJ-EU.js → cose-bilkent-S5V4N54A-CX3yotHq.js} +1 -1
  243. ksadk/server/static/assets/{dagre-BM42HDAG-FoDlo5J2.js → dagre-BM42HDAG-C7TguAWo.js} +1 -1
  244. ksadk/server/static/assets/{diagram-2AECGRRQ-BBp6ebBw.js → diagram-2AECGRRQ-Dd17_kws.js} +1 -1
  245. ksadk/server/static/assets/{diagram-5GNKFQAL-CqOvG1PR.js → diagram-5GNKFQAL-DVIClIil.js} +1 -1
  246. ksadk/server/static/assets/{diagram-KO2AKTUF-O9T3t6ba.js → diagram-KO2AKTUF-Df_3gYQa.js} +1 -1
  247. ksadk/server/static/assets/{diagram-LMA3HP47-CGG8ekBO.js → diagram-LMA3HP47-BhLk6ZtA.js} +1 -1
  248. ksadk/server/static/assets/{diagram-OG6HWLK6-4BmH1EEg.js → diagram-OG6HWLK6-D4GNGvuu.js} +1 -1
  249. ksadk/server/static/assets/{dist-CiZHB6ZW.js → dist-6CVKuTAe.js} +1 -1
  250. ksadk/server/static/assets/{dist-BMKM66B4.js → dist-BUHRxvDG.js} +1 -1
  251. ksadk/server/static/assets/{dist-CZ6C3x8B.js → dist-BZ9n9AjO.js} +1 -1
  252. ksadk/server/static/assets/{dist-DbaH3p5h.js → dist-BibcG1V9.js} +1 -1
  253. ksadk/server/static/assets/{dist-BVzcWa6D.js → dist-Bs-jQcRX.js} +1 -1
  254. ksadk/server/static/assets/{dist-D99wf6_X.js → dist-C2xjE7cO.js} +1 -1
  255. ksadk/server/static/assets/{dist-DLMjL01k.js → dist-C7-3jInC.js} +1 -1
  256. ksadk/server/static/assets/{dist-DI-oVAXw.js → dist-C99THtIA.js} +1 -1
  257. ksadk/server/static/assets/{dist-BkJjNih3.js → dist-CGcp2ex8.js} +1 -1
  258. ksadk/server/static/assets/{dist-D5JGcxBx.js → dist-CVQRZjPu.js} +1 -1
  259. ksadk/server/static/assets/{dist-CWiTDN_V.js → dist-Cf23NY4L.js} +1 -1
  260. ksadk/server/static/assets/{dist-DLeM1OCK.js → dist-CgF3fj15.js} +1 -1
  261. ksadk/server/static/assets/{dist-BmuTWcm7.js → dist-Cwt7koer.js} +1 -1
  262. ksadk/server/static/assets/{dist-C2DKRFVK.js → dist-D82mtD2s.js} +1 -1
  263. ksadk/server/static/assets/{dist-B67uF2uK.js → dist-DF06ORQo.js} +1 -1
  264. ksadk/server/static/assets/{dist-Cwhk4lwI.js → dist-DHn6nBzr.js} +1 -1
  265. ksadk/server/static/assets/{dist-S4klkK5W.js → dist-Djgl2wVZ.js} +1 -1
  266. ksadk/server/static/assets/{dist-DCpZrjaK.js → dist-DlzCRy0S.js} +1 -1
  267. ksadk/server/static/assets/{dist-Cp1_jKwf.js → dist-DqNnNFrw.js} +1 -1
  268. ksadk/server/static/assets/{dist-6kSZvCZh.js → dist-DyvYLMW9.js} +1 -1
  269. ksadk/server/static/assets/{dist-DM0rEQPk.js → dist-eoW6tZXA.js} +1 -1
  270. ksadk/server/static/assets/{dist-Bk1l05Ni.js → dist-ivOya1LD.js} +1 -1
  271. ksadk/server/static/assets/{erDiagram-TEJ5UH35-oJhaci7S.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-DtTmUan-.js → flowDiagram-I6XJVG4X-DGaJjGNJ.js} +1 -1
  274. ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BSsb_eij.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-BR5s6Pws.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-DYPc5cvh.js → infoDiagram-5YYISTIA-CYIIsb8y.js} +1 -1
  281. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-MoBdALDg.js → ishikawaDiagram-YF4QCWOH-DLOHGvWw.js} +1 -1
  282. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-DgLiQT8l.js → journeyDiagram-JHISSGLW-BIzKeejT.js} +1 -1
  283. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-CsFx112F.js → kanban-definition-UN3LZRKU-BWHoFSw2.js} +1 -1
  284. ksadk/server/static/assets/{katex-DRsiwpM2.js → katex-DATssjp1.js} +1 -1
  285. ksadk/server/static/assets/{linear-TNRQ5Orv.js → linear-CvMBw9cA.js} +1 -1
  286. ksadk/server/static/assets/{mermaid-parser.core-D2eMVOgo.js → mermaid-parser.core-mGAcEwcq.js} +2 -2
  287. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-BtByYFhL.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-DPnuCB8c.js → pieDiagram-4H26LBE5-By-ra07M.js} +1 -1
  291. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-miFpCPv4.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-mjB_yYkW.js → requirementDiagram-4Y6WPE33-DpW90iu3.js} +1 -1
  294. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-D7hSlZZZ.js → sankeyDiagram-5OEKKPKP-DpGa6EAV.js} +1 -1
  295. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-D9Cx6hC3.js → sequenceDiagram-3UESZ5HK-NTKNGGe5.js} +1 -1
  296. ksadk/server/static/assets/{stateDiagram-AJRCARHV-D_zrg47o.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-CwSec0Wa.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-agCVobiF.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-TmrlhWNt.js → wardleyDiagram-YWT4CUSO-BR54GPmr.js} +1 -1
  304. ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-DV71V-ah.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 +86 -40
  308. ksadk/sessions/base.py +31 -11
  309. ksadk/sessions/continuity.py +28 -5
  310. ksadk/sessions/in_memory.py +46 -2
  311. ksadk/sessions/local_service.py +148 -17
  312. ksadk/sessions/postgres_service.py +216 -35
  313. ksadk/sessions/resilience.py +52 -0
  314. ksadk/sessions/resilient.py +388 -0
  315. ksadk/skills/models.py +17 -5
  316. ksadk/skills/package_store.py +5 -2
  317. ksadk/skills/runtime/agent.py +20 -5
  318. ksadk/skills/runtime/artifacts.py +1 -5
  319. ksadk/skills/runtime/backends/e2b.py +10 -4
  320. ksadk/skills/runtime/backends/local.py +15 -3
  321. ksadk/skills/runtime/base.py +1 -2
  322. ksadk/skills/runtime/executor.py +10 -3
  323. ksadk/skills/runtime/factory.py +4 -2
  324. ksadk/skills/runtime/loader.py +1 -2
  325. ksadk/skills/runtime/registry.py +15 -10
  326. ksadk/skills/runtime/request.py +3 -1
  327. ksadk/skills/service_client.py +38 -17
  328. ksadk/skills/tool_defs.py +15 -10
  329. ksadk/terminal_exec_policy.py +3 -4
  330. ksadk/tools/gateway.py +92 -15
  331. ksadk/tools/result_budget.py +12 -4
  332. ksadk/toolsets/__init__.py +136 -36
  333. ksadk/toolsets/platform.py +21 -5
  334. ksadk/toolsets/sandbox.py +82 -16
  335. ksadk/toolsets/skills.py +200 -39
  336. ksadk/toolsets/web.py +123 -26
  337. ksadk/toolsets/workspace.py +264 -114
  338. ksadk/tracing/exporters/inmemory_exporter.py +62 -51
  339. ksadk/tracing/exporters/langfuse_exporter.py +91 -76
  340. ksadk/tracing/setup.py +132 -53
  341. ksadk/tracing/span_utils.py +5 -2
  342. ksadk/tui/__init__.py +22 -7
  343. ksadk/tui/loop.py +1925 -0
  344. ksadk/tui/stream_render.py +69 -0
  345. ksadk/ui_config.py +5 -3
  346. ksadk/version.py +1 -1
  347. ksadk_runtime_common/schemas/runtime_event_v1.json +95 -0
  348. agentengine_sdk_python-0.6.9.dist-info/RECORD +0 -487
  349. ksadk/server/static/assets/CodeBlock-BbI4HPtJ.js +0 -9
  350. ksadk/server/static/assets/MathMessageMarkdown-DMLQYoLp.js +0 -8
  351. ksadk/server/static/assets/MermaidBlock-DCR8WZ17.js +0 -303
  352. ksadk/server/static/assets/NativeTerminalPanel-CKUvWhUz.js +0 -1
  353. ksadk/server/static/assets/architecture-7EHR7CIX-Cr6Xz1_c.js +0 -1
  354. ksadk/server/static/assets/channel-BSSOwFIb.js +0 -1
  355. ksadk/server/static/assets/chunk-QZHKN3VN-XXpdiwUL.js +0 -1
  356. ksadk/server/static/assets/classDiagram-4FO5ZUOK-C5HvGiyK.js +0 -1
  357. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-C5HvGiyK.js +0 -1
  358. ksadk/server/static/assets/eventmodeling-FCH6USID-CIgwHJLL.js +0 -1
  359. ksadk/server/static/assets/gitGraph-WXDBUCRP-BVAJ7uo0.js +0 -1
  360. ksadk/server/static/assets/index-BH7-pC0B.js +0 -165
  361. ksadk/server/static/assets/index-DAURbCsP.css +0 -1
  362. ksadk/server/static/assets/info-J43DQDTF-MT80V4ge.js +0 -1
  363. ksadk/server/static/assets/packet-YPE3B663-BLKhbXZB.js +0 -1
  364. ksadk/server/static/assets/pie-LRSECV5Y-DFOikjSY.js +0 -1
  365. ksadk/server/static/assets/radar-GUYGQ44K-095b1Ikw.js +0 -1
  366. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-J9kCb0nC.js +0 -1
  367. ksadk/server/static/assets/treeView-BLDUP644-yinfJEY6.js +0 -1
  368. ksadk/server/static/assets/treemap-LRROVOQU-DosUqN1Q.js +0 -1
  369. ksadk/server/static/assets/wardley-L42UT6IY-Bvp7-5uH.js +0 -1
  370. ksadk/tui/app.py +0 -400
  371. ksadk/tui/clipboard.py +0 -100
  372. ksadk/tui/widgets/__init__.py +0 -22
  373. ksadk/tui/widgets/assistant.py +0 -89
  374. ksadk/tui/widgets/base.py +0 -54
  375. ksadk/tui/widgets/chat_input.py +0 -246
  376. ksadk/tui/widgets/history.py +0 -156
  377. ksadk/tui/widgets/system.py +0 -87
  378. ksadk/tui/widgets/thinking.py +0 -110
  379. ksadk/tui/widgets/tool_call.py +0 -161
  380. ksadk/tui/widgets/user.py +0 -37
  381. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/WHEEL +0 -0
  382. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/entry_points.txt +0 -0
  383. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/licenses/LICENSE +0 -0
  384. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,656 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Mapping, Sequence
4
+ from typing import Any
5
+ from urllib.parse import quote
6
+
7
+ from ksadk.agui.a2ui_projection import project_a2ui_operations
8
+ from ksadk.events.runtime_event import EventType
9
+
10
+
11
+ def _event_metadata(event: Mapping[str, Any]) -> Mapping[str, Any]:
12
+ metadata = event.get("Metadata")
13
+ return metadata if isinstance(metadata, Mapping) else {}
14
+
15
+
16
+ def project_session_messages(
17
+ events: Sequence[Mapping[str, Any]],
18
+ *,
19
+ include_reasoning: bool = False,
20
+ include_tool_events: bool = False,
21
+ include_attachments: bool = True,
22
+ ) -> list[dict[str, Any]]:
23
+ """Project persisted runtime events into the chat history contract."""
24
+
25
+ agui_invocations = _agui_invocation_ids(events)
26
+ normalized = [
27
+ _normalize_runtime_event(event, agui_invocations=agui_invocations) for event in events
28
+ ]
29
+ approval_responses = _approval_response_events(normalized)
30
+ groups: dict[str, list[Mapping[str, Any]]] = {}
31
+ for event in sorted(normalized, key=lambda item: int(item.get("SeqId") or 0)):
32
+ invocation_id = str(event.get("InvocationId") or f"seq:{event.get('SeqId')}")
33
+ groups.setdefault(invocation_id, []).append(event)
34
+
35
+ messages: list[dict[str, Any]] = []
36
+ for group in groups.values():
37
+ messages.extend(
38
+ _project_event_group(
39
+ group,
40
+ include_reasoning=include_reasoning,
41
+ include_tool_events=include_tool_events,
42
+ include_attachments=include_attachments,
43
+ approval_responses=approval_responses,
44
+ )
45
+ )
46
+ return sorted(messages, key=lambda item: int(item.get("SeqId") or 0))
47
+
48
+
49
+ def _project_event_group(
50
+ events: Sequence[Mapping[str, Any]],
51
+ *,
52
+ include_reasoning: bool,
53
+ include_tool_events: bool,
54
+ include_attachments: bool,
55
+ approval_responses: Mapping[str, Mapping[str, Any]],
56
+ ) -> list[dict[str, Any]]:
57
+ reasoning = [
58
+ {"text": _event_text(event), "SeqId": event.get("SeqId")}
59
+ for event in events
60
+ if event.get("EventType") == "reasoning" and _event_text(event)
61
+ ]
62
+ tool_events = (
63
+ _project_tool_events(events, approval_responses=approval_responses)
64
+ if include_tool_events
65
+ else []
66
+ )
67
+ activities = _project_a2ui_activities(events)
68
+ projected: list[dict[str, Any]] = []
69
+ assistant_seen = False
70
+ latest_snapshot: Mapping[str, Any] | None = None
71
+ streamed_text = ""
72
+ start_seq_id = min((int(event.get("SeqId") or 0) for event in events), default=0)
73
+
74
+ for event in events:
75
+ event_type = str(event.get("EventType") or "")
76
+ if event_type == "user_message":
77
+ message = _base_message(event, "user")
78
+ if include_attachments:
79
+ attachments = _event_attachments(event)
80
+ if attachments:
81
+ message["Attachments"] = attachments
82
+ projected.append(message)
83
+ elif event_type == "assistant_message":
84
+ message = _base_message(event, "assistant")
85
+ if include_reasoning and reasoning:
86
+ message["Reasoning"] = reasoning
87
+ if tool_events:
88
+ message["ToolEvents"] = tool_events
89
+ if include_reasoning:
90
+ blocks = _project_interleaved_blocks(events, tool_events=tool_events)
91
+ if blocks:
92
+ message["Blocks"] = blocks
93
+ projected.append(message)
94
+ assistant_seen = True
95
+ elif event_type == "assistant_stream_snapshot":
96
+ latest_snapshot = event
97
+ elif event_type == "assistant_stream_delta":
98
+ streamed_text += _event_text(event)
99
+
100
+ if not assistant_seen and (
101
+ latest_snapshot is not None or streamed_text or reasoning or tool_events or activities
102
+ ):
103
+ anchor = next(
104
+ (
105
+ event
106
+ for event in reversed(events)
107
+ if event.get("EventType")
108
+ in {
109
+ "assistant_stream_snapshot",
110
+ "assistant_stream_delta",
111
+ "approval_request",
112
+ "tool_call",
113
+ "reasoning",
114
+ EventType.A2UI_SURFACE_BEGIN,
115
+ EventType.A2UI_SURFACE_UPDATE,
116
+ EventType.A2UI_SURFACE_END,
117
+ }
118
+ ),
119
+ events[-1],
120
+ )
121
+ message = _base_message(
122
+ anchor,
123
+ "assistant",
124
+ content=(
125
+ _event_text(latest_snapshot) if latest_snapshot is not None else streamed_text
126
+ ),
127
+ )
128
+ if include_reasoning and reasoning:
129
+ message["Reasoning"] = reasoning
130
+ if tool_events:
131
+ message["ToolEvents"] = tool_events
132
+ if include_reasoning:
133
+ blocks = _project_interleaved_blocks(events, tool_events=tool_events)
134
+ if blocks:
135
+ message["Blocks"] = blocks
136
+ projected.append(message)
137
+
138
+ if activities:
139
+ assistant = next(
140
+ (message for message in reversed(projected) if message.get("Role") == "assistant"),
141
+ None,
142
+ )
143
+ if assistant is not None:
144
+ assistant["Activities"] = activities
145
+
146
+ for message in projected:
147
+ message["StartSeqId"] = start_seq_id
148
+ return projected
149
+
150
+
151
+ def _project_interleaved_blocks(
152
+ events: Sequence[Mapping[str, Any]],
153
+ *,
154
+ tool_events: Sequence[Mapping[str, Any]],
155
+ ) -> list[dict[str, Any]]:
156
+ """Reconstruct ordered chat blocks from persisted streaming events.
157
+
158
+ ``assistant_stream_snapshot`` carries a cumulative text value. The delta
159
+ relative to the prior snapshot is the text emitted at that point in the
160
+ event sequence. Reasoning events written before that snapshot can
161
+ therefore be replayed as ``thinking → text → thinking → text`` instead of
162
+ being flattened into the legacy ``Reasoning`` and ``Content`` fields.
163
+
164
+ Older transcripts wrote all reasoning only at terminal time. Their first
165
+ reasoning event appears after streamed text, so their original boundaries
166
+ are unknowable. Return no blocks for those transcripts and let clients
167
+ use the existing best-effort compatibility projection.
168
+ """
169
+
170
+ blocks: list[dict[str, Any]] = []
171
+ latest_snapshot_text = ""
172
+ saw_stream_text = False
173
+ saw_reasoning_after_stream_text = False
174
+ tools_by_seq_id = {
175
+ int(tool.get("SeqId") or 0): tool
176
+ for tool in tool_events
177
+ if int(tool.get("SeqId") or 0) > 0
178
+ }
179
+
180
+ def append_text(text: str, seq_id: Any) -> None:
181
+ if not text:
182
+ return
183
+ if blocks and blocks[-1].get("Type") == "text":
184
+ blocks[-1]["Content"] = f"{blocks[-1].get('Content') or ''}{text}"
185
+ return
186
+ blocks.append({"Type": "text", "Content": text, "SeqId": seq_id})
187
+
188
+ def append_thinking(text: str, seq_id: Any) -> None:
189
+ if not text:
190
+ return
191
+ if blocks and blocks[-1].get("Type") == "thinking":
192
+ blocks[-1]["Content"] = f"{blocks[-1].get('Content') or ''}{text}"
193
+ return
194
+ blocks.append({"Type": "thinking", "Content": text, "SeqId": seq_id})
195
+
196
+ def append_tool(event: Mapping[str, Any]) -> None:
197
+ tool = tools_by_seq_id.get(int(event.get("SeqId") or 0))
198
+ if not tool:
199
+ return
200
+ blocks.append(
201
+ {
202
+ "Type": "tool",
203
+ "SeqId": tool.get("SeqId"),
204
+ "Name": tool.get("Name") or "tool",
205
+ "Args": tool.get("Args"),
206
+ "Result": tool.get("Result"),
207
+ "Status": tool.get("Status") or "completed",
208
+ "ToolCallId": tool.get("ToolCallId"),
209
+ }
210
+ )
211
+
212
+ for event in events:
213
+ event_type = str(event.get("EventType") or "")
214
+ text = _event_text(event)
215
+ seq_id = event.get("SeqId")
216
+
217
+ if event_type == "reasoning":
218
+ metadata = _event_metadata(event)
219
+ if saw_stream_text and metadata.get("stream_boundary") != "before_text":
220
+ saw_reasoning_after_stream_text = True
221
+ append_thinking(text, seq_id)
222
+ continue
223
+ if event_type == "tool_call":
224
+ append_tool(event)
225
+ continue
226
+ if event_type == "assistant_stream_delta":
227
+ append_text(text, seq_id)
228
+ saw_stream_text = saw_stream_text or bool(text)
229
+ continue
230
+ if event_type == "assistant_stream_snapshot":
231
+ if text.startswith(latest_snapshot_text):
232
+ append_text(text[len(latest_snapshot_text) :], seq_id)
233
+ elif text != latest_snapshot_text:
234
+ # A replacement snapshot has no safe incremental boundary.
235
+ # Keep the latest content as a standalone text block instead
236
+ # of duplicating already-replayed output.
237
+ append_text(text, seq_id)
238
+ latest_snapshot_text = text
239
+ saw_stream_text = saw_stream_text or bool(text)
240
+ continue
241
+ if event_type == "assistant_message":
242
+ if latest_snapshot_text and text.startswith(latest_snapshot_text):
243
+ append_text(text[len(latest_snapshot_text) :], seq_id)
244
+ elif not latest_snapshot_text:
245
+ append_text(text, seq_id)
246
+
247
+ if saw_reasoning_after_stream_text:
248
+ return []
249
+ return blocks
250
+
251
+
252
+ def _base_message(
253
+ event: Mapping[str, Any],
254
+ role: str,
255
+ *,
256
+ content: str | None = None,
257
+ ) -> dict[str, Any]:
258
+ metadata = _event_metadata(event)
259
+ message: dict[str, Any] = {
260
+ "MessageId": event.get("EventId"),
261
+ "Role": role,
262
+ "Content": {"text": _event_text(event) if content is None else content},
263
+ "Timestamp": event.get("Timestamp"),
264
+ "SeqId": event.get("SeqId"),
265
+ "InvocationId": event.get("InvocationId"),
266
+ }
267
+ for target, *sources in (
268
+ ("ResponseId", "response_id", "ResponseId"),
269
+ ("TraceId", "trace_id", "TraceId"),
270
+ ("RootSpanId", "root_span_id", "rootSpanId", "RootSpanId"),
271
+ ):
272
+ value = next((metadata.get(source) for source in sources if metadata.get(source)), None)
273
+ if value:
274
+ message[target] = str(value)
275
+ return message
276
+
277
+
278
+ def _event_text(event: Mapping[str, Any]) -> str:
279
+ content = event.get("Content")
280
+ if isinstance(content, str):
281
+ return content
282
+ if not isinstance(content, Mapping):
283
+ return ""
284
+ if content.get("text") is not None:
285
+ return str(content.get("text") or "")
286
+ return "".join(
287
+ str(part.get("text") or "")
288
+ for part in content.get("parts") or []
289
+ if isinstance(part, Mapping)
290
+ )
291
+
292
+
293
+ def _project_tool_events(
294
+ events: Sequence[Mapping[str, Any]],
295
+ *,
296
+ approval_responses: Mapping[str, Mapping[str, Any]],
297
+ ) -> list[dict[str, Any]]:
298
+ projected: list[dict[str, Any]] = []
299
+ pending_calls: list[dict[str, Any]] = []
300
+ pending_by_key: dict[str, dict[str, Any]] = {}
301
+ seen_result_receipts: set[str] = set()
302
+
303
+ for event in events:
304
+ event_type = str(event.get("EventType") or "")
305
+ metadata = _event_metadata(event)
306
+ if event_type == "tool_call":
307
+ entry = _tool_event_from_call(event, metadata)
308
+ projected.append(entry)
309
+ pending_calls.append(entry)
310
+ pair_key = _tool_pair_key(metadata)
311
+ if pair_key:
312
+ pending_by_key[pair_key] = entry
313
+ continue
314
+ if event_type != "tool_result":
315
+ continue
316
+
317
+ receipt = metadata.get("tool_receipt")
318
+ receipt_key = (
319
+ str(receipt.get("idempotency_key") or "") if isinstance(receipt, Mapping) else ""
320
+ )
321
+ if receipt_key and receipt_key in seen_result_receipts:
322
+ continue
323
+ if receipt_key:
324
+ seen_result_receipts.add(receipt_key)
325
+
326
+ name = str(metadata.get("tool_name") or "tool")
327
+ pair_key = _tool_pair_key(metadata)
328
+ match = pending_by_key.get(pair_key) if pair_key else None
329
+ if match is None or match["Status"] != "running":
330
+ match = next(
331
+ (
332
+ item
333
+ for item in reversed(pending_calls)
334
+ if item["Name"] == name and item["Status"] == "running"
335
+ ),
336
+ None,
337
+ )
338
+ if match is None:
339
+ entry = _tool_event_from_call(event, metadata)
340
+ projected.append(entry)
341
+ else:
342
+ entry = match
343
+ output = metadata.get("tool_output", _event_text(event))
344
+ entry["Status"] = (
345
+ "failed" if isinstance(output, Mapping) and output.get("ok") is False else "completed"
346
+ )
347
+ entry["Result"] = output
348
+ entry["ResultSeqId"] = event.get("SeqId")
349
+
350
+ projected.extend(_project_approval_events(events, responses=approval_responses))
351
+ return projected
352
+
353
+
354
+ def _tool_pair_key(metadata: Mapping[str, Any]) -> str:
355
+ call_id = metadata.get("call_id")
356
+ if call_id:
357
+ return f"call:{call_id}"
358
+ tool_receipt = metadata.get("tool_receipt")
359
+ if isinstance(tool_receipt, Mapping) and tool_receipt.get("tool_call_id"):
360
+ return f"call:{tool_receipt['tool_call_id']}"
361
+ run_id = metadata.get("run_id")
362
+ if run_id:
363
+ return f"run:{run_id}:{metadata.get('tool_name') or 'tool'}"
364
+ return ""
365
+
366
+
367
+ def _tool_event_from_call(event: Mapping[str, Any], metadata: Mapping[str, Any]) -> dict[str, Any]:
368
+ tool_receipt = metadata.get("tool_receipt")
369
+ call_id = metadata.get("call_id") or metadata.get("run_id")
370
+ if not call_id and isinstance(tool_receipt, Mapping):
371
+ call_id = tool_receipt.get("tool_call_id")
372
+ return {
373
+ "SeqId": event.get("SeqId"),
374
+ "Type": "tool_call",
375
+ "Name": str(metadata.get("tool_name") or "tool"),
376
+ "Args": metadata.get("tool_args"),
377
+ "Status": "running",
378
+ "ToolCallId": call_id,
379
+ }
380
+
381
+
382
+ def _project_approval_events(
383
+ events: Sequence[Mapping[str, Any]],
384
+ *,
385
+ responses: Mapping[str, Mapping[str, Any]],
386
+ ) -> list[dict[str, Any]]:
387
+ approvals: list[dict[str, Any]] = []
388
+ for request in events:
389
+ if request.get("EventType") != "approval_request":
390
+ continue
391
+ metadata = _event_metadata(request)
392
+ interrupt_info = metadata.get("interrupt_info")
393
+ if not isinstance(interrupt_info, Mapping):
394
+ interrupt_info = {}
395
+ request_id = str(
396
+ interrupt_info.get("approval_request_id") or interrupt_info.get("id") or ""
397
+ )
398
+ response = responses.get(request_id)
399
+ response_metadata = _event_metadata(response) if response is not None else {}
400
+ resume_input = response_metadata.get("resume_input")
401
+ if not isinstance(resume_input, Mapping):
402
+ resume_input = {}
403
+ status = (
404
+ "paused"
405
+ if response is None
406
+ else (
407
+ "approved"
408
+ if resume_input.get("approve") or resume_input.get("approved")
409
+ else "denied"
410
+ )
411
+ )
412
+ entry: dict[str, Any] = {
413
+ "SeqId": request.get("SeqId"),
414
+ "Type": "approval",
415
+ "Name": str(interrupt_info.get("tool_name") or "approval"),
416
+ "Status": status,
417
+ "ApprovalRequestId": request_id or None,
418
+ }
419
+ if metadata.get("protocol") == "ag-ui":
420
+ entry["Protocol"] = "ag-ui"
421
+ arguments = (
422
+ interrupt_info.get("arguments")
423
+ or interrupt_info.get("tool_args")
424
+ or interrupt_info.get("args")
425
+ )
426
+ if arguments is not None:
427
+ entry["Args"] = arguments
428
+ approval_level = interrupt_info.get("approval_level") or interrupt_info.get("risk_level")
429
+ if approval_level:
430
+ entry["ApprovalLevel"] = str(approval_level)
431
+ approval_message = interrupt_info.get("approval_message") or interrupt_info.get("message")
432
+ if approval_message:
433
+ entry["ApprovalMessage"] = str(approval_message)
434
+ approvals.append(entry)
435
+ return approvals
436
+
437
+
438
+ def _approval_response_events(
439
+ events: Sequence[Mapping[str, Any]],
440
+ ) -> dict[str, Mapping[str, Any]]:
441
+ responses: dict[str, Mapping[str, Any]] = {}
442
+ for event in events:
443
+ if event.get("EventType") != "approval_response":
444
+ continue
445
+ metadata = _event_metadata(event)
446
+ resume_input = metadata.get("resume_input")
447
+ if not isinstance(resume_input, Mapping):
448
+ continue
449
+ request_id = str(
450
+ resume_input.get("approval_request_id")
451
+ or resume_input.get("interrupt_id")
452
+ or resume_input.get("id")
453
+ or ""
454
+ )
455
+ if request_id:
456
+ responses[request_id] = event
457
+ return responses
458
+
459
+
460
+ def _project_a2ui_activities(events: Sequence[Mapping[str, Any]]) -> list[dict[str, Any]]:
461
+ activities: list[dict[str, Any]] = []
462
+ for event in events:
463
+ event_type = str(event.get("EventType") or "")
464
+ if event_type not in {
465
+ EventType.A2UI_SURFACE_BEGIN,
466
+ EventType.A2UI_SURFACE_UPDATE,
467
+ EventType.A2UI_SURFACE_END,
468
+ }:
469
+ continue
470
+ content = event.get("Content")
471
+ payload = content if isinstance(content, Mapping) else {}
472
+ operations = project_a2ui_operations(event_type, payload)
473
+ if not operations:
474
+ continue
475
+ surface_id = str(payload.get("surface_id") or payload.get("surfaceId") or "")
476
+ activities.append(
477
+ {
478
+ "SeqId": event.get("SeqId"),
479
+ "Type": "a2ui-surface",
480
+ "MessageId": f"{event.get('InvocationId')}:a2ui:{surface_id}",
481
+ "SurfaceId": surface_id,
482
+ "Content": {
483
+ "surfaceId": surface_id,
484
+ "a2ui_operations": operations,
485
+ },
486
+ }
487
+ )
488
+ return activities
489
+
490
+
491
+ def _agui_invocation_ids(events: Sequence[Mapping[str, Any]]) -> set[str]:
492
+ """Locate AG-UI runs so history written before ``payload.protocol`` remains usable."""
493
+ invocation_ids: set[str] = set()
494
+ for event in events:
495
+ if str(event.get("EventType") or "") != EventType.RUN_STARTED:
496
+ continue
497
+ if not _event_metadata(event).get("ksadk_runtime_event"):
498
+ continue
499
+ content = event.get("Content")
500
+ payload = content.get("payload") if isinstance(content, Mapping) else None
501
+ if isinstance(payload, Mapping) and payload.get("source") == "ag-ui":
502
+ invocation_id = str(event.get("InvocationId") or "")
503
+ if invocation_id:
504
+ invocation_ids.add(invocation_id)
505
+ return invocation_ids
506
+
507
+
508
+ def _normalize_runtime_event(
509
+ event: Mapping[str, Any],
510
+ *,
511
+ agui_invocations: set[str],
512
+ ) -> Mapping[str, Any]:
513
+ metadata = _event_metadata(event)
514
+ if not metadata.get("ksadk_runtime_event"):
515
+ return event
516
+ raw_content = event.get("Content")
517
+ content = raw_content if isinstance(raw_content, Mapping) else {}
518
+ payload = content.get("payload")
519
+ payload = payload if isinstance(payload, Mapping) else {}
520
+ event_type = str(event.get("EventType") or "")
521
+ normalized = dict(event)
522
+ normalized["Content"] = dict(payload)
523
+ normalized_metadata = dict(metadata)
524
+
525
+ if event_type == EventType.RUN_STARTED and payload.get("source") == "ag-ui":
526
+ normalized["EventType"] = "user_message"
527
+ normalized["Content"] = {"text": _input_text(payload.get("input"))}
528
+ normalized["Author"] = "user"
529
+ elif event_type == EventType.TEXT_COMPLETED:
530
+ normalized["EventType"] = "assistant_message"
531
+ elif event_type == EventType.TEXT_DELTA:
532
+ normalized["EventType"] = "assistant_stream_delta"
533
+ elif event_type in {EventType.REASONING_DELTA, EventType.REASONING_COMPLETED}:
534
+ normalized["EventType"] = "reasoning"
535
+ elif event_type == EventType.TOOL_CALL_BEGIN:
536
+ normalized["EventType"] = "tool_call"
537
+ normalized_metadata.update(
538
+ {
539
+ "call_id": payload.get("call_id"),
540
+ "tool_name": payload.get("name"),
541
+ "tool_args": payload.get("args"),
542
+ }
543
+ )
544
+ elif event_type == EventType.TOOL_CALL_END:
545
+ normalized["EventType"] = "tool_result"
546
+ normalized_metadata.update(
547
+ {
548
+ "call_id": payload.get("call_id"),
549
+ "tool_name": payload.get("name"),
550
+ "tool_output": payload.get("result", payload.get("error")),
551
+ }
552
+ )
553
+ elif event_type == EventType.APPROVAL_REQUESTED:
554
+ detail = payload.get("detail")
555
+ detail = detail if isinstance(detail, Mapping) else {}
556
+ approval_request = detail.get("approval_requests")
557
+ approval_request = approval_request if isinstance(approval_request, Mapping) else detail
558
+ action_requests = approval_request.get("action_requests")
559
+ action = (
560
+ next(
561
+ (item for item in action_requests if isinstance(item, Mapping)),
562
+ {},
563
+ )
564
+ if isinstance(action_requests, list)
565
+ else {}
566
+ )
567
+ normalized["EventType"] = "approval_request"
568
+ normalized_metadata["interrupt_info"] = {
569
+ "approval_request_id": payload.get("approval_id") or payload.get("call_id"),
570
+ "id": payload.get("approval_id") or payload.get("call_id"),
571
+ "tool_name": action.get("name")
572
+ or approval_request.get("tool_name")
573
+ or detail.get("tool_name")
574
+ or payload.get("kind")
575
+ or "approval",
576
+ "arguments": action.get("args")
577
+ or action.get("arguments")
578
+ or approval_request.get("arguments")
579
+ or approval_request.get("args")
580
+ or detail.get("arguments")
581
+ or detail.get("args"),
582
+ "approval_level": action.get("approval_level")
583
+ or approval_request.get("approval_level")
584
+ or detail.get("approval_level")
585
+ or payload.get("approval_level"),
586
+ "approval_message": action.get("description")
587
+ or approval_request.get("message")
588
+ or detail.get("message")
589
+ or payload.get("message"),
590
+ }
591
+ is_agui_approval = payload.get("protocol") == "ag-ui" or (
592
+ str(event.get("InvocationId") or "") in agui_invocations
593
+ )
594
+ if is_agui_approval:
595
+ normalized_metadata["protocol"] = "ag-ui"
596
+ elif event_type == EventType.APPROVAL_RESOLVED:
597
+ decision = payload.get("decision")
598
+ normalized["EventType"] = "approval_response"
599
+ normalized_metadata["resume_input"] = {
600
+ "approval_request_id": payload.get("approval_id") or payload.get("call_id"),
601
+ "approve": _approval_decision_is_approved(decision),
602
+ "decision": decision,
603
+ }
604
+ normalized["Metadata"] = normalized_metadata
605
+ return normalized
606
+
607
+
608
+ def _approval_decision_is_approved(decision: Any) -> bool:
609
+ """Accept the historical AG-UI decision envelopes during transcript replay."""
610
+
611
+ if decision is True:
612
+ return True
613
+ if isinstance(decision, Mapping):
614
+ for key in ("approve", "approved"):
615
+ if key in decision:
616
+ return bool(decision[key])
617
+ return _approval_decision_is_approved(decision.get("decision"))
618
+ return isinstance(decision, str) and decision.strip().lower() in {"approve", "approved"}
619
+
620
+
621
+ def _input_text(value: Any) -> str:
622
+ if isinstance(value, str):
623
+ return value
624
+ if isinstance(value, Mapping):
625
+ if value.get("text") is not None:
626
+ return str(value.get("text") or "")
627
+ if value.get("content") is not None:
628
+ return _input_text(value.get("content"))
629
+ if isinstance(value, list):
630
+ return "".join(_input_text(item) for item in value)
631
+ return "" if value is None else str(value)
632
+
633
+
634
+ def _event_attachments(event: Mapping[str, Any]) -> list[dict[str, Any]]:
635
+ metadata = _event_metadata(event)
636
+ attachments: list[dict[str, Any]] = []
637
+ seen: set[str] = set()
638
+ for item in metadata.get("attachments") or []:
639
+ if not isinstance(item, Mapping):
640
+ continue
641
+ file_uri = str(item.get("file_uri") or item.get("fileUri") or "")
642
+ if not file_uri or file_uri in seen:
643
+ continue
644
+ mime = str(item.get("mime_type") or item.get("mimeType") or "")
645
+ attachments.append(
646
+ {
647
+ "file_uri": file_uri,
648
+ "name": str(item.get("display_name") or item.get("displayName") or ""),
649
+ "mime": mime,
650
+ "size": int(item.get("size_bytes") or item.get("sizeBytes") or 0),
651
+ "is_image": mime.startswith("image/"),
652
+ "url": "/agentengine/api/v1/AttachmentContent?FileUri=" + quote(file_uri, safe=""),
653
+ }
654
+ )
655
+ seen.add(file_uri)
656
+ return attachments