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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (371) hide show
  1. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/METADATA +36 -20
  2. agentengine_sdk_python-0.8.0.dist-info/RECORD +578 -0
  3. ksadk/a2a/__init__.py +178 -11
  4. ksadk/a2a/bootstrap.py +334 -0
  5. ksadk/a2a/card.py +120 -0
  6. ksadk/a2a/context_store.py +203 -0
  7. ksadk/a2a/control_plane.py +818 -0
  8. ksadk/a2a/credential.py +99 -0
  9. ksadk/a2a/event_adapter.py +149 -0
  10. ksadk/a2a/executor.py +406 -89
  11. ksadk/a2a/external_transport.py +516 -0
  12. ksadk/a2a/identity.py +304 -0
  13. ksadk/a2a/ids.py +22 -0
  14. ksadk/a2a/langgraph.py +285 -0
  15. ksadk/a2a/resume_store.py +201 -0
  16. ksadk/a2a/routes.py +105 -0
  17. ksadk/a2a/server.py +107 -60
  18. ksadk/a2a/space_client.py +1101 -0
  19. ksadk/a2a/task_adapter.py +423 -0
  20. ksadk/a2a/task_event_dispatcher.py +167 -0
  21. ksadk/a2a/task_event_outbox.py +326 -0
  22. ksadk/a2a/task_store.py +206 -0
  23. ksadk/a2ui/__init__.py +27 -0
  24. ksadk/a2ui/core.py +186 -0
  25. ksadk/a2ui/models.py +166 -0
  26. ksadk/a2ui/renderer.py +198 -0
  27. ksadk/agui/__init__.py +9 -0
  28. ksadk/agui/a2ui_projection.py +92 -0
  29. ksadk/agui/agent.py +857 -0
  30. ksadk/agui/config.py +86 -0
  31. ksadk/agui/routes.py +55 -0
  32. ksadk/api/client.py +233 -191
  33. ksadk/builders/__init__.py +2 -0
  34. ksadk/builders/base.py +16 -14
  35. ksadk/builders/code_builder.py +318 -236
  36. ksadk/builders/container_builder.py +187 -167
  37. ksadk/builders/framework_requirements.py +29 -11
  38. ksadk/builders/ks3_uploader.py +58 -27
  39. ksadk/builders/managed_runtime_builder.py +145 -0
  40. ksadk/builders/mcp_builder.py +91 -92
  41. ksadk/builders/requirements_utils.py +1 -5
  42. ksadk/cli/__init__.py +89 -171
  43. ksadk/cli/cmd_a2a.py +254 -70
  44. ksadk/cli/cmd_agent.py +19 -5
  45. ksadk/cli/cmd_build.py +158 -29
  46. ksadk/cli/cmd_completion.py +47 -35
  47. ksadk/cli/cmd_config.py +267 -198
  48. ksadk/cli/cmd_create.py +500 -720
  49. ksadk/cli/cmd_dashboard.py +120 -45
  50. ksadk/cli/cmd_deploy.py +117 -58
  51. ksadk/cli/cmd_destroy.py +44 -14
  52. ksadk/cli/cmd_files.py +63 -24
  53. ksadk/cli/cmd_hermes.py +168 -89
  54. ksadk/cli/cmd_invoke.py +184 -122
  55. ksadk/cli/cmd_launch.py +64 -28
  56. ksadk/cli/cmd_mcp.py +98 -64
  57. ksadk/cli/cmd_model.py +14 -5
  58. ksadk/cli/cmd_openclaw.py +483 -227
  59. ksadk/cli/cmd_replay.py +71 -0
  60. ksadk/cli/cmd_run.py +33 -10
  61. ksadk/cli/cmd_status.py +80 -31
  62. ksadk/cli/cmd_version.py +20 -13
  63. ksadk/cli/cmd_web.py +89 -13
  64. ksadk/cli/deploy_utils.py +20 -20
  65. ksadk/cli/dry_run.py +5 -3
  66. ksadk/cli/error_utils.py +37 -12
  67. ksadk/cli/global_options.py +1 -2
  68. ksadk/cli/local_runtime.py +1 -2
  69. ksadk/cli/model_catalog.py +6 -2
  70. ksadk/cli/network_options.py +34 -14
  71. ksadk/cli/resource_common.py +6 -2
  72. ksadk/cli/storage.py +0 -1
  73. ksadk/cli/ui.py +5 -3
  74. ksadk/cli/workflow_common.py +51 -17
  75. ksadk/codex/__init__.py +12 -0
  76. ksadk/codex/client.py +408 -0
  77. ksadk/codex/phase.py +143 -0
  78. ksadk/codex/runtime.py +449 -0
  79. ksadk/common/aicp_env.py +0 -1
  80. ksadk/common/auth.py +2 -2
  81. ksadk/common/constants.py +1 -3
  82. ksadk/common/llm_utils.py +17 -13
  83. ksadk/compat/__init__.py +5 -0
  84. ksadk/compat/adk_compat.py +200 -0
  85. ksadk/compat/copilotkit_a2ui.py +330 -0
  86. ksadk/configs/__init__.py +12 -12
  87. ksadk/configs/env_registry.py +500 -100
  88. ksadk/configs/global_config.py +2 -1
  89. ksadk/configs/settings.py +151 -124
  90. ksadk/conversations/__init__.py +7 -5
  91. ksadk/conversations/attachment_storage.py +16 -11
  92. ksadk/conversations/attachments.py +44 -36
  93. ksadk/conversations/compaction_pipeline.py +12 -12
  94. ksadk/conversations/compaction_prompt.py +9 -5
  95. ksadk/conversations/context.py +221 -6
  96. ksadk/conversations/message_projection.py +438 -51
  97. ksadk/conversations/model_context.py +12 -6
  98. ksadk/conversations/model_options.py +0 -1
  99. ksadk/conversations/normalize.py +20 -16
  100. ksadk/conversations/reasoning_markup.py +1 -1
  101. ksadk/conversations/runtime.py +339 -5255
  102. ksadk/conversations/runtime_compaction.py +333 -0
  103. ksadk/conversations/runtime_constants.py +27 -0
  104. ksadk/conversations/runtime_governance.py +185 -0
  105. ksadk/conversations/runtime_input.py +652 -0
  106. ksadk/conversations/runtime_invocation.py +395 -0
  107. ksadk/conversations/runtime_metadata.py +475 -0
  108. ksadk/conversations/runtime_observability.py +355 -0
  109. ksadk/conversations/runtime_payloads.py +272 -0
  110. ksadk/conversations/runtime_persistence.py +440 -0
  111. ksadk/conversations/runtime_preparation.py +418 -0
  112. ksadk/conversations/runtime_resume.py +816 -0
  113. ksadk/conversations/runtime_stream_events.py +902 -0
  114. ksadk/conversations/runtime_streaming.py +547 -0
  115. ksadk/conversations/semantic_summary.py +17 -5
  116. ksadk/conversations/session_title.py +6 -3
  117. ksadk/deployment/__init__.py +15 -16
  118. ksadk/deployment/agent_access.py +12 -23
  119. ksadk/deployment/base.py +51 -38
  120. ksadk/deployment/manager.py +63 -65
  121. ksadk/deployment/providers/serverless.py +309 -202
  122. ksadk/deployment/registry.py +38 -32
  123. ksadk/deployment/state.py +11 -11
  124. ksadk/deployment/ui_config.py +1 -1
  125. ksadk/detection/__init__.py +1 -1
  126. ksadk/detection/detector.py +146 -83
  127. ksadk/detection/mcp_detector.py +9 -6
  128. ksadk/events/__init__.py +25 -0
  129. ksadk/events/parser.py +191 -0
  130. ksadk/events/replay.py +37 -0
  131. ksadk/events/runtime_event.py +264 -0
  132. ksadk/events/store.py +317 -0
  133. ksadk/harness/__init__.py +34 -0
  134. ksadk/harness/app.py +278 -0
  135. ksadk/harness/config.py +181 -0
  136. ksadk/harness/reasoner.py +105 -0
  137. ksadk/harness/runner.py +211 -0
  138. ksadk/harness/sandbox.py +141 -0
  139. ksadk/harness/tools.py +222 -0
  140. ksadk/hermes_terminal.py +23 -12
  141. ksadk/identity/resolver.py +15 -9
  142. ksadk/ids.py +32 -0
  143. ksadk/knowledge_base/adk_tool.py +1 -1
  144. ksadk/knowledge_base/client.py +23 -29
  145. ksadk/managed_runtime.py +188 -0
  146. ksadk/markdown.py +5 -2
  147. ksadk/mcp_runtime/__init__.py +9 -6
  148. ksadk/memory/__init__.py +1 -1
  149. ksadk/memory/adk/__init__.py +2 -0
  150. ksadk/memory/adk/backends/base_ltm_backend.py +2 -6
  151. ksadk/memory/adk/backends/http_ltm_backend.py +13 -21
  152. ksadk/memory/adk/backends/inmemory_ltm_backend.py +7 -13
  153. ksadk/memory/adk/backends/sdk_ltm_backend.py +26 -33
  154. ksadk/memory/adk/long_term_memory.py +31 -35
  155. ksadk/memory/adk/resilient_session_service.py +4 -5
  156. ksadk/memory/adk/short_term_memory.py +12 -21
  157. ksadk/memory/adk_tool.py +3 -2
  158. ksadk/memory/backends/base.py +19 -19
  159. ksadk/memory/backends/memory.py +24 -25
  160. ksadk/memory/backends/redis.py +31 -30
  161. ksadk/memory/langchain_tool.py +2 -1
  162. ksadk/memory/manager.py +52 -47
  163. ksadk/memory/service.py +10 -8
  164. ksadk/model_policy.py +15 -7
  165. ksadk/model_proxy/__init__.py +51 -0
  166. ksadk/model_proxy/bootstrap.py +80 -0
  167. ksadk/model_proxy/cache.py +165 -0
  168. ksadk/model_proxy/config.py +45 -0
  169. ksadk/model_proxy/demo_webui.py +115 -0
  170. ksadk/model_proxy/detect.py +145 -0
  171. ksadk/model_proxy/gate.py +68 -0
  172. ksadk/model_proxy/namespace.py +173 -0
  173. ksadk/model_proxy/protocol_proxy.py +22 -0
  174. ksadk/model_proxy/server.py +300 -0
  175. ksadk/model_proxy/transform.py +691 -0
  176. ksadk/openclaw_gateway.py +29 -14
  177. ksadk/runners/adk_runner.py +345 -120
  178. ksadk/runners/base_runner.py +46 -29
  179. ksadk/runners/codex_runner.py +132 -0
  180. ksadk/runners/deepagents_runner.py +0 -1
  181. ksadk/runners/factory.py +17 -5
  182. ksadk/runners/langchain_runner.py +29 -742
  183. ksadk/runners/langgraph_runner.py +352 -90
  184. ksadk/runners/patch_langchain.py +16 -16
  185. ksadk/runners/remote_runner.py +134 -33
  186. ksadk/runners/usage_accumulator.py +1 -0
  187. ksadk/runners/utils/langfuse.py +3 -3
  188. ksadk/runners/utils/loader.py +7 -7
  189. ksadk/runtime/__init__.py +41 -0
  190. ksadk/runtime/adapter.py +345 -0
  191. ksadk/runtime/framework_adapters.py +124 -0
  192. ksadk/runtime/preprocessing.py +118 -0
  193. ksadk/runtime/runner_adapter.py +873 -0
  194. ksadk/runtime_context.py +18 -3
  195. ksadk/runtime_state.py +0 -1
  196. ksadk/sandbox/__init__.py +4 -1
  197. ksadk/sandbox/backends/__init__.py +4 -1
  198. ksadk/sandbox/backends/e2b.py +13 -5
  199. ksadk/sandbox/backends/local_process.py +11 -3
  200. ksadk/sandbox/base.py +10 -15
  201. ksadk/sandbox/factory.py +14 -4
  202. ksadk/sandbox/registry.py +57 -6
  203. ksadk/server/api_models.py +15 -5
  204. ksadk/server/app.py +455 -3885
  205. ksadk/server/composition.py +21 -0
  206. ksadk/server/factory.py +624 -0
  207. ksadk/server/routes/__init__.py +1 -0
  208. ksadk/server/routes/checkpoint_resolution.py +116 -0
  209. ksadk/server/routes/common.py +713 -0
  210. ksadk/server/routes/control.py +327 -0
  211. ksadk/server/routes/dependencies.py +98 -0
  212. ksadk/server/routes/misc.py +448 -0
  213. ksadk/server/routes/models.py +443 -0
  214. ksadk/server/routes/openai_compat.py +207 -0
  215. ksadk/server/routes/projection.py +795 -0
  216. ksadk/server/routes/routers.py +52 -0
  217. ksadk/server/routes/run.py +726 -0
  218. ksadk/server/routes/sessions.py +649 -0
  219. ksadk/server/routes/streaming.py +223 -0
  220. ksadk/server/routes/workspace.py +381 -0
  221. ksadk/server/static/assets/{ArtifactsPanel-D-SSn2ee.js → ArtifactsPanel-Cjnqt-8W.js} +1 -1
  222. ksadk/server/static/assets/CodeBlock-BqyqZfuB.js +9 -0
  223. ksadk/server/static/assets/MathMessageMarkdown-BpH7jiFM.js +8 -0
  224. ksadk/server/static/assets/MermaidBlock-DWuxETaJ.js +303 -0
  225. ksadk/server/static/assets/NativeTerminalPanel-MDTopWMO.js +1 -0
  226. ksadk/server/static/assets/{arc-BA13sHc9.js → arc-CFKwSk_B.js} +1 -1
  227. ksadk/server/static/assets/architecture-7EHR7CIX-CynqOWjZ.js +1 -0
  228. ksadk/server/static/assets/{architectureDiagram-3BPJPVTR-DzJzgVoL.js → architectureDiagram-3BPJPVTR-Dej8jX8c.js} +1 -1
  229. ksadk/server/static/assets/{blockDiagram-GPEHLZMM-UTdVcZhn.js → blockDiagram-GPEHLZMM-1F64jqvy.js} +1 -1
  230. ksadk/server/static/assets/{c4Diagram-AAUBKEIU-Dix0GD4m.js → c4Diagram-AAUBKEIU-9q7AUAKb.js} +1 -1
  231. ksadk/server/static/assets/channel-iUGv2N24.js +1 -0
  232. ksadk/server/static/assets/{chunk-2J33WTMH-C2JxuZ42.js → chunk-2J33WTMH-6qgwJm7Q.js} +1 -1
  233. ksadk/server/static/assets/{chunk-4BX2VUAB-Be57QPgD.js → chunk-4BX2VUAB-CsWIQRhc.js} +1 -1
  234. ksadk/server/static/assets/{chunk-55IACEB6-WjSuy4nv.js → chunk-55IACEB6-BK8paA20.js} +1 -1
  235. ksadk/server/static/assets/{chunk-727SXJPM-BOovP-b5.js → chunk-727SXJPM-B-JEhy7h.js} +1 -1
  236. ksadk/server/static/assets/{chunk-AQP2D5EJ-DOmZNmWj.js → chunk-AQP2D5EJ-DYBvMHAa.js} +1 -1
  237. ksadk/server/static/assets/{chunk-FMBD7UC4-DzwlodkU.js → chunk-FMBD7UC4-BGhvzDYt.js} +1 -1
  238. ksadk/server/static/assets/{chunk-ND2GUHAM-DMqAikEU.js → chunk-ND2GUHAM-D98vPd-E.js} +1 -1
  239. ksadk/server/static/assets/chunk-QZHKN3VN-YQIEXeF6.js +1 -0
  240. ksadk/server/static/assets/classDiagram-4FO5ZUOK-CDAUuoXK.js +1 -0
  241. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-CDAUuoXK.js +1 -0
  242. ksadk/server/static/assets/{cose-bilkent-S5V4N54A-Zhy2GBnP.js → cose-bilkent-S5V4N54A-CX3yotHq.js} +1 -1
  243. ksadk/server/static/assets/{dagre-BM42HDAG-CbKrtIUz.js → dagre-BM42HDAG-C7TguAWo.js} +1 -1
  244. ksadk/server/static/assets/{diagram-2AECGRRQ-BRApgQRK.js → diagram-2AECGRRQ-Dd17_kws.js} +1 -1
  245. ksadk/server/static/assets/{diagram-5GNKFQAL-D0xbnDiK.js → diagram-5GNKFQAL-DVIClIil.js} +1 -1
  246. ksadk/server/static/assets/{diagram-KO2AKTUF-CVugVI9L.js → diagram-KO2AKTUF-Df_3gYQa.js} +1 -1
  247. ksadk/server/static/assets/{diagram-LMA3HP47-DozUGgee.js → diagram-LMA3HP47-BhLk6ZtA.js} +1 -1
  248. ksadk/server/static/assets/{diagram-OG6HWLK6-ZOYsKlru.js → diagram-OG6HWLK6-D4GNGvuu.js} +1 -1
  249. ksadk/server/static/assets/{dist-DmFaq_Iy.js → dist-6CVKuTAe.js} +1 -1
  250. ksadk/server/static/assets/{dist-8EEyGgDx.js → dist-BUHRxvDG.js} +1 -1
  251. ksadk/server/static/assets/{dist-B0NP_Z0z.js → dist-BZ9n9AjO.js} +1 -1
  252. ksadk/server/static/assets/{dist-DMezvBGJ.js → dist-BibcG1V9.js} +1 -1
  253. ksadk/server/static/assets/{dist-C3HIIaN5.js → dist-Bs-jQcRX.js} +1 -1
  254. ksadk/server/static/assets/{dist-BrCcQ47T.js → dist-C2xjE7cO.js} +1 -1
  255. ksadk/server/static/assets/{dist-Dp0k7bWI.js → dist-C7-3jInC.js} +1 -1
  256. ksadk/server/static/assets/{dist-D-xOD048.js → dist-C99THtIA.js} +1 -1
  257. ksadk/server/static/assets/{dist-CkEKJFtk.js → dist-CGcp2ex8.js} +1 -1
  258. ksadk/server/static/assets/{dist-DyOkZmze.js → dist-CVQRZjPu.js} +1 -1
  259. ksadk/server/static/assets/{dist-BssVmjIo.js → dist-Cf23NY4L.js} +1 -1
  260. ksadk/server/static/assets/{dist-BIJVDMuB.js → dist-CgF3fj15.js} +1 -1
  261. ksadk/server/static/assets/{dist-2hInLS_J.js → dist-Cwt7koer.js} +1 -1
  262. ksadk/server/static/assets/{dist-DyWsb78Q.js → dist-D82mtD2s.js} +1 -1
  263. ksadk/server/static/assets/{dist-D-JMdWPQ.js → dist-DF06ORQo.js} +1 -1
  264. ksadk/server/static/assets/{dist-BmlMPsCc.js → dist-DHn6nBzr.js} +1 -1
  265. ksadk/server/static/assets/{dist-gX6GOLKq.js → dist-Djgl2wVZ.js} +1 -1
  266. ksadk/server/static/assets/{dist-DpKtt4Bo.js → dist-DlzCRy0S.js} +1 -1
  267. ksadk/server/static/assets/{dist-8_SfK1IP.js → dist-DqNnNFrw.js} +1 -1
  268. ksadk/server/static/assets/{dist-D57wnGWd.js → dist-DyvYLMW9.js} +1 -1
  269. ksadk/server/static/assets/{dist-CPBtqRTW.js → dist-eoW6tZXA.js} +1 -1
  270. ksadk/server/static/assets/{dist-DL_NKc_z.js → dist-ivOya1LD.js} +1 -1
  271. ksadk/server/static/assets/{erDiagram-TEJ5UH35-Bk-9bgE5.js → erDiagram-TEJ5UH35-CnfRcY2M.js} +1 -1
  272. ksadk/server/static/assets/eventmodeling-FCH6USID-ChYO-ijM.js +1 -0
  273. ksadk/server/static/assets/{flowDiagram-I6XJVG4X-Dn9kZVuZ.js → flowDiagram-I6XJVG4X-DGaJjGNJ.js} +1 -1
  274. ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BGxXlJqm.js → ganttDiagram-6RSMTGT7-DpCoB2g9.js} +1 -1
  275. ksadk/server/static/assets/gitGraph-WXDBUCRP-BN_0Fk-j.js +1 -0
  276. ksadk/server/static/assets/{gitGraphDiagram-PVQCEYII-C7Q0X-wi.js → gitGraphDiagram-PVQCEYII-9IKVFBVT.js} +1 -1
  277. ksadk/server/static/assets/index-QSwCHuB8.js +177 -0
  278. ksadk/server/static/assets/index-jOPmTOZH.css +2 -0
  279. ksadk/server/static/assets/info-J43DQDTF-Cp6o3vId.js +1 -0
  280. ksadk/server/static/assets/{infoDiagram-5YYISTIA-BO_CSqq5.js → infoDiagram-5YYISTIA-CYIIsb8y.js} +1 -1
  281. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-eYLPLd46.js → ishikawaDiagram-YF4QCWOH-DLOHGvWw.js} +1 -1
  282. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-De9geSb0.js → journeyDiagram-JHISSGLW-BIzKeejT.js} +1 -1
  283. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-Cs80kQto.js → kanban-definition-UN3LZRKU-BWHoFSw2.js} +1 -1
  284. ksadk/server/static/assets/{katex-DSrCFD8n.js → katex-DATssjp1.js} +1 -1
  285. ksadk/server/static/assets/{linear-CN9sbKTF.js → linear-CvMBw9cA.js} +1 -1
  286. ksadk/server/static/assets/{mermaid-parser.core-DlgSBU8l.js → mermaid-parser.core-mGAcEwcq.js} +2 -2
  287. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-9afz7Oug.js → mindmap-definition-RKZ34NQL-CSAQxEAe.js} +1 -1
  288. ksadk/server/static/assets/packet-YPE3B663-wQzct3cT.js +1 -0
  289. ksadk/server/static/assets/pie-LRSECV5Y-BgTQiv-9.js +1 -0
  290. ksadk/server/static/assets/{pieDiagram-4H26LBE5-DPv_VstN.js → pieDiagram-4H26LBE5-By-ra07M.js} +1 -1
  291. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-o77wZ1XK.js → quadrantDiagram-W4KKPZXB-CxIR4JXI.js} +1 -1
  292. ksadk/server/static/assets/radar-GUYGQ44K-BIq5fXsn.js +1 -0
  293. ksadk/server/static/assets/{requirementDiagram-4Y6WPE33-BYVw81VC.js → requirementDiagram-4Y6WPE33-DpW90iu3.js} +1 -1
  294. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-B7nZFMKT.js → sankeyDiagram-5OEKKPKP-DpGa6EAV.js} +1 -1
  295. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-BGRRrFw5.js → sequenceDiagram-3UESZ5HK-NTKNGGe5.js} +1 -1
  296. ksadk/server/static/assets/{stateDiagram-AJRCARHV-BUfHCoPs.js → stateDiagram-AJRCARHV-DGpd9yFG.js} +1 -1
  297. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-B3o579IQ.js +1 -0
  298. ksadk/server/static/assets/{timeline-definition-PNZ67QCA-FbCTGrf9.js → timeline-definition-PNZ67QCA-jqoYs3jQ.js} +1 -1
  299. ksadk/server/static/assets/treeView-BLDUP644-Dqi-E0EM.js +1 -0
  300. ksadk/server/static/assets/treemap-LRROVOQU-ClQP9CJ9.js +1 -0
  301. ksadk/server/static/assets/{vennDiagram-CIIHVFJN-DWgwtPsJ.js → vennDiagram-CIIHVFJN-l3Z7-wbP.js} +1 -1
  302. ksadk/server/static/assets/wardley-L42UT6IY-8XLvnNIs.js +1 -0
  303. ksadk/server/static/assets/{wardleyDiagram-YWT4CUSO-2x8G-tTM.js → wardleyDiagram-YWT4CUSO-BR54GPmr.js} +1 -1
  304. ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-Drma3AYl.js → xychartDiagram-2RQKCTM6-SkiH8GsK.js} +1 -1
  305. ksadk/server/static/index.html +2 -2
  306. ksadk/server/terminal_sessions.py +111 -58
  307. ksadk/sessions/__init__.py +69 -19
  308. ksadk/sessions/base.py +31 -11
  309. ksadk/sessions/continuity.py +2 -3
  310. ksadk/sessions/in_memory.py +46 -2
  311. ksadk/sessions/local_service.py +148 -17
  312. ksadk/sessions/postgres_service.py +143 -25
  313. ksadk/sessions/resilient.py +36 -8
  314. ksadk/skills/models.py +17 -5
  315. ksadk/skills/package_store.py +5 -2
  316. ksadk/skills/runtime/agent.py +20 -5
  317. ksadk/skills/runtime/artifacts.py +1 -5
  318. ksadk/skills/runtime/backends/e2b.py +10 -4
  319. ksadk/skills/runtime/backends/local.py +15 -3
  320. ksadk/skills/runtime/base.py +1 -2
  321. ksadk/skills/runtime/executor.py +10 -3
  322. ksadk/skills/runtime/factory.py +4 -2
  323. ksadk/skills/runtime/loader.py +1 -2
  324. ksadk/skills/runtime/registry.py +15 -10
  325. ksadk/skills/runtime/request.py +3 -1
  326. ksadk/skills/service_client.py +38 -17
  327. ksadk/skills/tool_defs.py +15 -10
  328. ksadk/terminal_exec_policy.py +3 -4
  329. ksadk/tools/gateway.py +92 -15
  330. ksadk/tools/result_budget.py +12 -4
  331. ksadk/toolsets/__init__.py +134 -36
  332. ksadk/toolsets/platform.py +21 -5
  333. ksadk/toolsets/sandbox.py +82 -16
  334. ksadk/toolsets/skills.py +200 -39
  335. ksadk/toolsets/web.py +123 -26
  336. ksadk/toolsets/workspace.py +212 -101
  337. ksadk/tracing/exporters/inmemory_exporter.py +62 -51
  338. ksadk/tracing/exporters/langfuse_exporter.py +91 -76
  339. ksadk/tracing/setup.py +69 -53
  340. ksadk/tracing/span_utils.py +5 -2
  341. ksadk/tui/loop.py +78 -30
  342. ksadk/tui/stream_render.py +1 -0
  343. ksadk/ui_config.py +5 -3
  344. ksadk/version.py +1 -1
  345. ksadk_runtime_common/schemas/runtime_event_v1.json +95 -0
  346. agentengine_sdk_python-0.7.0.dist-info/RECORD +0 -482
  347. ksadk/server/static/assets/CodeBlock-P16VOLYi.js +0 -9
  348. ksadk/server/static/assets/MathMessageMarkdown-CdGiQWHR.js +0 -8
  349. ksadk/server/static/assets/MermaidBlock-IOcal8cm.js +0 -303
  350. ksadk/server/static/assets/NativeTerminalPanel-DGSDWsei.js +0 -1
  351. ksadk/server/static/assets/architecture-7EHR7CIX-BvUlsA6l.js +0 -1
  352. ksadk/server/static/assets/channel-CTuydARX.js +0 -1
  353. ksadk/server/static/assets/chunk-QZHKN3VN-BnCydivS.js +0 -1
  354. ksadk/server/static/assets/classDiagram-4FO5ZUOK-BNHg45Be.js +0 -1
  355. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-BNHg45Be.js +0 -1
  356. ksadk/server/static/assets/eventmodeling-FCH6USID-DsAgWnlf.js +0 -1
  357. ksadk/server/static/assets/gitGraph-WXDBUCRP-BIao9Yjg.js +0 -1
  358. ksadk/server/static/assets/index-C00Lwg3L.js +0 -165
  359. ksadk/server/static/assets/index-DAURbCsP.css +0 -1
  360. ksadk/server/static/assets/info-J43DQDTF-CyipqGxU.js +0 -1
  361. ksadk/server/static/assets/packet-YPE3B663-CGkyRxlg.js +0 -1
  362. ksadk/server/static/assets/pie-LRSECV5Y-M0uWAf4n.js +0 -1
  363. ksadk/server/static/assets/radar-GUYGQ44K-Bdbm24-x.js +0 -1
  364. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-8xBN_gLM.js +0 -1
  365. ksadk/server/static/assets/treeView-BLDUP644-npMo0ak1.js +0 -1
  366. ksadk/server/static/assets/treemap-LRROVOQU-CHdzrrdo.js +0 -1
  367. ksadk/server/static/assets/wardley-L42UT6IY-BEQz7wyy.js +0 -1
  368. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/WHEEL +0 -0
  369. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/entry_points.txt +0 -0
  370. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/licenses/LICENSE +0 -0
  371. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/top_level.txt +0 -0
@@ -4,6 +4,14 @@ from collections.abc import Mapping, Sequence
4
4
  from typing import Any
5
5
  from urllib.parse import quote
6
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
+
7
15
 
8
16
  def project_session_messages(
9
17
  events: Sequence[Mapping[str, Any]],
@@ -14,8 +22,13 @@ def project_session_messages(
14
22
  ) -> list[dict[str, Any]]:
15
23
  """Project persisted runtime events into the chat history contract."""
16
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)
17
30
  groups: dict[str, list[Mapping[str, Any]]] = {}
18
- for event in sorted(events, key=lambda item: int(item.get("SeqId") or 0)):
31
+ for event in sorted(normalized, key=lambda item: int(item.get("SeqId") or 0)):
19
32
  invocation_id = str(event.get("InvocationId") or f"seq:{event.get('SeqId')}")
20
33
  groups.setdefault(invocation_id, []).append(event)
21
34
 
@@ -27,6 +40,7 @@ def project_session_messages(
27
40
  include_reasoning=include_reasoning,
28
41
  include_tool_events=include_tool_events,
29
42
  include_attachments=include_attachments,
43
+ approval_responses=approval_responses,
30
44
  )
31
45
  )
32
46
  return sorted(messages, key=lambda item: int(item.get("SeqId") or 0))
@@ -38,15 +52,24 @@ def _project_event_group(
38
52
  include_reasoning: bool,
39
53
  include_tool_events: bool,
40
54
  include_attachments: bool,
55
+ approval_responses: Mapping[str, Mapping[str, Any]],
41
56
  ) -> list[dict[str, Any]]:
42
57
  reasoning = [
43
58
  {"text": _event_text(event), "SeqId": event.get("SeqId")}
44
59
  for event in events
45
60
  if event.get("EventType") == "reasoning" and _event_text(event)
46
61
  ]
47
- tool_events = _project_tool_events(events) if include_tool_events else []
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)
48
68
  projected: list[dict[str, Any]] = []
49
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)
50
73
 
51
74
  for event in events:
52
75
  event_type = str(event.get("EventType") or "")
@@ -63,36 +86,176 @@ def _project_event_group(
63
86
  message["Reasoning"] = reasoning
64
87
  if tool_events:
65
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
66
93
  projected.append(message)
67
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)
68
99
 
69
- if not assistant_seen and (reasoning or tool_events):
100
+ if not assistant_seen and (
101
+ latest_snapshot is not None or streamed_text or reasoning or tool_events or activities
102
+ ):
70
103
  anchor = next(
71
104
  (
72
105
  event
73
106
  for event in reversed(events)
74
107
  if event.get("EventType")
75
- in {"assistant_stream_snapshot", "approval_request", "tool_call", "reasoning"}
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
+ }
76
118
  ),
77
119
  events[-1],
78
120
  )
79
- message = _base_message(anchor, "assistant", content="")
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
+ )
80
128
  if include_reasoning and reasoning:
81
129
  message["Reasoning"] = reasoning
82
130
  if tool_events:
83
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
84
136
  projected.append(message)
85
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
86
148
  return projected
87
149
 
88
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
+
89
252
  def _base_message(
90
253
  event: Mapping[str, Any],
91
254
  role: str,
92
255
  *,
93
256
  content: str | None = None,
94
257
  ) -> dict[str, Any]:
95
- metadata = event.get("Metadata") if isinstance(event.get("Metadata"), Mapping) else {}
258
+ metadata = _event_metadata(event)
96
259
  message: dict[str, Any] = {
97
260
  "MessageId": event.get("EventId"),
98
261
  "Role": role,
@@ -127,35 +290,56 @@ def _event_text(event: Mapping[str, Any]) -> str:
127
290
  )
128
291
 
129
292
 
130
- def _project_tool_events(events: Sequence[Mapping[str, Any]]) -> list[dict[str, Any]]:
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]]:
131
298
  projected: list[dict[str, Any]] = []
132
- pending_calls: list[tuple[Mapping[str, Any], 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()
133
302
 
134
303
  for event in events:
135
304
  event_type = str(event.get("EventType") or "")
136
- metadata = event.get("Metadata") if isinstance(event.get("Metadata"), Mapping) else {}
305
+ metadata = _event_metadata(event)
137
306
  if event_type == "tool_call":
138
307
  entry = _tool_event_from_call(event, metadata)
139
308
  projected.append(entry)
140
- pending_calls.append((event, entry))
309
+ pending_calls.append(entry)
310
+ pair_key = _tool_pair_key(metadata)
311
+ if pair_key:
312
+ pending_by_key[pair_key] = entry
141
313
  continue
142
314
  if event_type != "tool_result":
143
315
  continue
144
316
 
145
- name = str(metadata.get("tool_name") or "tool")
146
- match = next(
147
- (
148
- item
149
- for item in reversed(pending_calls)
150
- if item[1]["Name"] == name and item[1]["Status"] == "running"
151
- ),
152
- None,
317
+ receipt = metadata.get("tool_receipt")
318
+ receipt_key = (
319
+ str(receipt.get("idempotency_key") or "") if isinstance(receipt, Mapping) else ""
153
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
+ )
154
338
  if match is None:
155
339
  entry = _tool_event_from_call(event, metadata)
156
340
  projected.append(entry)
157
341
  else:
158
- entry = match[1]
342
+ entry = match
159
343
  output = metadata.get("tool_output", _event_text(event))
160
344
  entry["Status"] = (
161
345
  "failed" if isinstance(output, Mapping) and output.get("ok") is False else "completed"
@@ -163,13 +347,24 @@ def _project_tool_events(events: Sequence[Mapping[str, Any]]) -> list[dict[str,
163
347
  entry["Result"] = output
164
348
  entry["ResultSeqId"] = event.get("SeqId")
165
349
 
166
- projected.extend(_project_approval_events(events))
350
+ projected.extend(_project_approval_events(events, responses=approval_responses))
167
351
  return projected
168
352
 
169
353
 
170
- def _tool_event_from_call(
171
- event: Mapping[str, Any], metadata: Mapping[str, Any]
172
- ) -> dict[str, Any]:
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]:
173
368
  tool_receipt = metadata.get("tool_receipt")
174
369
  call_id = metadata.get("call_id") or metadata.get("run_id")
175
370
  if not call_id and isinstance(tool_receipt, Mapping):
@@ -184,28 +379,16 @@ def _tool_event_from_call(
184
379
  }
185
380
 
186
381
 
187
- def _project_approval_events(events: Sequence[Mapping[str, Any]]) -> list[dict[str, Any]]:
188
- responses: dict[str, Mapping[str, Any]] = {}
189
- for event in events:
190
- if event.get("EventType") != "approval_response":
191
- continue
192
- metadata = event.get("Metadata") if isinstance(event.get("Metadata"), Mapping) else {}
193
- resume_input = metadata.get("resume_input")
194
- if not isinstance(resume_input, Mapping):
195
- continue
196
- request_id = str(
197
- resume_input.get("approval_request_id")
198
- or resume_input.get("interrupt_id")
199
- or resume_input.get("id")
200
- or ""
201
- )
202
- responses[request_id] = event
203
-
382
+ def _project_approval_events(
383
+ events: Sequence[Mapping[str, Any]],
384
+ *,
385
+ responses: Mapping[str, Mapping[str, Any]],
386
+ ) -> list[dict[str, Any]]:
204
387
  approvals: list[dict[str, Any]] = []
205
388
  for request in events:
206
389
  if request.get("EventType") != "approval_request":
207
390
  continue
208
- metadata = request.get("Metadata") if isinstance(request.get("Metadata"), Mapping) else {}
391
+ metadata = _event_metadata(request)
209
392
  interrupt_info = metadata.get("interrupt_info")
210
393
  if not isinstance(interrupt_info, Mapping):
211
394
  interrupt_info = {}
@@ -213,18 +396,18 @@ def _project_approval_events(events: Sequence[Mapping[str, Any]]) -> list[dict[s
213
396
  interrupt_info.get("approval_request_id") or interrupt_info.get("id") or ""
214
397
  )
215
398
  response = responses.get(request_id)
216
- response_metadata = (
217
- response.get("Metadata")
218
- if response is not None and isinstance(response.get("Metadata"), Mapping)
219
- else {}
220
- )
399
+ response_metadata = _event_metadata(response) if response is not None else {}
221
400
  resume_input = response_metadata.get("resume_input")
222
401
  if not isinstance(resume_input, Mapping):
223
402
  resume_input = {}
224
- status = "paused" if response is None else (
225
- "approved"
226
- if resume_input.get("approve") or resume_input.get("approved")
227
- else "denied"
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
+ )
228
411
  )
229
412
  entry: dict[str, Any] = {
230
413
  "SeqId": request.get("SeqId"),
@@ -233,6 +416,8 @@ def _project_approval_events(events: Sequence[Mapping[str, Any]]) -> list[dict[s
233
416
  "Status": status,
234
417
  "ApprovalRequestId": request_id or None,
235
418
  }
419
+ if metadata.get("protocol") == "ag-ui":
420
+ entry["Protocol"] = "ag-ui"
236
421
  arguments = (
237
422
  interrupt_info.get("arguments")
238
423
  or interrupt_info.get("tool_args")
@@ -240,12 +425,214 @@ def _project_approval_events(events: Sequence[Mapping[str, Any]]) -> list[dict[s
240
425
  )
241
426
  if arguments is not None:
242
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)
243
434
  approvals.append(entry)
244
435
  return approvals
245
436
 
246
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
+
247
634
  def _event_attachments(event: Mapping[str, Any]) -> list[dict[str, Any]]:
248
- metadata = event.get("Metadata") if isinstance(event.get("Metadata"), Mapping) else {}
635
+ metadata = _event_metadata(event)
249
636
  attachments: list[dict[str, Any]] = []
250
637
  seen: set[str] = set()
251
638
  for item in metadata.get("attachments") or []:
@@ -79,12 +79,12 @@ def _coerce_token_limit(value: Any, *, assume_k_for_plain_values: bool = False)
79
79
  text = text[:-1]
80
80
 
81
81
  try:
82
- parsed = float(text)
82
+ parsed_number = float(text)
83
83
  except (TypeError, ValueError):
84
84
  return None
85
- if parsed <= 0:
85
+ if parsed_number <= 0:
86
86
  return None
87
- resolved = int(parsed * multiplier)
87
+ resolved = int(parsed_number * multiplier)
88
88
  if multiplier == 1 and assume_k_for_plain_values and text.isdigit() and 0 < resolved <= 1000:
89
89
  return resolved * 1000
90
90
  return resolved
@@ -189,7 +189,9 @@ def get_context_window_tokens(model_metadata: Mapping[str, Any] | None = None) -
189
189
  if model_metadata.get("context_window_tokens"):
190
190
  limits["context_window_tokens"] = model_metadata["context_window_tokens"]
191
191
 
192
- return _coerce_positive_int(limits.get("context_window_tokens")) or DEFAULT_CONTEXT_WINDOW_TOKENS
192
+ return (
193
+ _coerce_positive_int(limits.get("context_window_tokens")) or DEFAULT_CONTEXT_WINDOW_TOKENS
194
+ )
193
195
 
194
196
 
195
197
  def get_effective_context_window_tokens(model_metadata: Mapping[str, Any] | None = None) -> int:
@@ -202,7 +204,9 @@ def get_effective_context_window_tokens(model_metadata: Mapping[str, Any] | None
202
204
  limits["max_output_tokens"] = model_metadata["max_output_tokens"]
203
205
 
204
206
  context_window = get_context_window_tokens(model_metadata)
205
- max_output_tokens = _coerce_positive_int(limits.get("max_output_tokens")) or DEFAULT_MAX_OUTPUT_TOKENS
207
+ max_output_tokens = (
208
+ _coerce_positive_int(limits.get("max_output_tokens")) or DEFAULT_MAX_OUTPUT_TOKENS
209
+ )
206
210
  reserved_tokens = min(max_output_tokens, AUTOCOMPACT_SUMMARY_RESERVE_TOKENS)
207
211
  return max(1, context_window - reserved_tokens)
208
212
 
@@ -366,5 +370,7 @@ def normalize_model_metadata(raw_model: Mapping[str, Any] | str | None) -> dict[
366
370
  "pricing": pricing,
367
371
  }
368
372
  normalized["auto_compact_threshold_tokens"] = get_auto_compact_threshold_tokens(normalized)
369
- normalized["auto_compact_threshold_percentage"] = get_auto_compact_threshold_percentage(normalized)
373
+ normalized["auto_compact_threshold_percentage"] = get_auto_compact_threshold_percentage(
374
+ normalized
375
+ )
370
376
  return normalized
@@ -2,7 +2,6 @@ from __future__ import annotations
2
2
 
3
3
  from typing import Any, Mapping
4
4
 
5
-
6
5
  _VALID_REASONING_EFFORTS = {"none", "minimal", "low", "medium", "high", "xhigh"}
7
6
  _CHAT_COMPLETIONS_REASONING_EFFORTS = {"low", "medium", "high"}
8
7
  _PASSTHROUGH_OPTION_KEYS = {"temperature", "top_p", "max_tokens", "max_completion_tokens"}