agentengine-sdk-python 0.8.1__py3-none-any.whl → 0.8.2__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 (411) hide show
  1. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/METADATA +22 -2
  2. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/RECORD +356 -222
  3. ksadk/a2a/_space_client_events.py +381 -0
  4. ksadk/a2a/event_adapter.py +156 -64
  5. ksadk/a2a/executor.py +179 -63
  6. ksadk/a2a/space_client.py +6 -333
  7. ksadk/a2ui/core.py +222 -53
  8. ksadk/agui/_agent_helpers.py +243 -0
  9. ksadk/agui/a2ui_projection.py +12 -5
  10. ksadk/agui/agent.py +443 -259
  11. ksadk/api/client.py +525 -27
  12. ksadk/builders/code_builder.py +284 -8
  13. ksadk/builders/container_builder.py +8 -1
  14. ksadk/builders/managed_runtime_builder.py +39 -35
  15. ksadk/cli/__init__.py +17 -0
  16. ksadk/cli/cmd_dashboard.py +15 -1
  17. ksadk/cli/cmd_deploy.py +10 -2
  18. ksadk/cli/cmd_eval.py +66 -9
  19. ksadk/cli/cmd_evalset.py +284 -0
  20. ksadk/cli/cmd_files.py +5 -1
  21. ksadk/cli/cmd_hermes.py +1 -126
  22. ksadk/cli/cmd_invoke.py +28 -38
  23. ksadk/cli/cmd_managed_runtime.py +95 -0
  24. ksadk/cli/cmd_observe.py +85 -0
  25. ksadk/cli/cmd_openclaw.py +1 -435
  26. ksadk/cli/cmd_replay.py +28 -4
  27. ksadk/cli/cmd_studio.py +99 -8
  28. ksadk/cli/cmd_web.py +10 -2
  29. ksadk/cli/hermes_env.py +151 -0
  30. ksadk/cli/invoke_payload.py +49 -0
  31. ksadk/cli/openclaw_env.py +458 -0
  32. ksadk/codex/client.py +180 -30
  33. ksadk/codex/runtime.py +428 -326
  34. ksadk/configs/env_registry.py +66 -12
  35. ksadk/configs/env_registry_pcm.py +181 -0
  36. ksadk/configs/env_var_spec.py +18 -0
  37. ksadk/configs/global_config.py +5 -0
  38. ksadk/context_engine/__init__.py +98 -0
  39. ksadk/context_engine/assembler.py +176 -0
  40. ksadk/context_engine/baseline.py +419 -0
  41. ksadk/context_engine/cache_observability.py +228 -0
  42. ksadk/context_engine/capabilities.py +443 -0
  43. ksadk/context_engine/contributors.py +344 -0
  44. ksadk/context_engine/hosted_pipeline.py +417 -0
  45. ksadk/context_engine/models.py +117 -0
  46. ksadk/context_engine/planner.py +698 -0
  47. ksadk/context_engine/policies.py +319 -0
  48. ksadk/context_engine/projection.py +31 -0
  49. ksadk/context_engine/shadow_plan.py +266 -0
  50. ksadk/context_engine/tokenizer.py +164 -0
  51. ksadk/conversations/context.py +126 -6
  52. ksadk/conversations/message_projection.py +292 -28
  53. ksadk/conversations/model_context.py +49 -3
  54. ksadk/conversations/runtime_compaction.py +207 -5
  55. ksadk/conversations/runtime_input.py +184 -2
  56. ksadk/conversations/runtime_invocation.py +81 -3
  57. ksadk/conversations/runtime_observability.py +141 -0
  58. ksadk/conversations/runtime_payloads.py +42 -0
  59. ksadk/conversations/runtime_preparation.py +272 -2
  60. ksadk/conversations/runtime_resume.py +20 -1
  61. ksadk/conversations/runtime_stream_events.py +94 -9
  62. ksadk/conversations/semantic_summary.py +311 -1
  63. ksadk/conversations/session_lock.py +51 -0
  64. ksadk/deployment/env_forward.py +72 -0
  65. ksadk/deployment/managed_runtime.py +5 -0
  66. ksadk/deployment/providers/serverless.py +143 -69
  67. ksadk/evaluation/__init__.py +32 -0
  68. ksadk/evaluation/a2a_adapter.py +9 -1
  69. ksadk/evaluation/adapters.py +18 -5
  70. ksadk/evaluation/agent_eval_client.py +395 -0
  71. ksadk/evaluation/cloud_binding.py +80 -0
  72. ksadk/evaluation/cloud_converter.py +194 -0
  73. ksadk/evaluation/cloud_service.py +199 -0
  74. ksadk/evaluation/contracts.py +73 -4
  75. ksadk/evaluation/evaluators.py +183 -17
  76. ksadk/evaluation/evidence.py +258 -0
  77. ksadk/evaluation/executor.py +31 -6
  78. ksadk/evaluation/local_adapter.py +550 -0
  79. ksadk/evaluation/service_env.py +27 -0
  80. ksadk/evaluation/studio_build_adapter.py +253 -0
  81. ksadk/evaluation/target.py +15 -6
  82. ksadk/events/__init__.py +13 -13
  83. ksadk/events/_v1_compat/__init__.py +1 -0
  84. ksadk/events/_v1_compat/models.py +299 -0
  85. ksadk/events/_v1_compat/parser.py +247 -0
  86. ksadk/events/_v1_compat/projection.py +753 -0
  87. ksadk/events/adapters/__init__.py +34 -0
  88. ksadk/events/adapters/_a2a_snapshot.py +622 -0
  89. ksadk/events/adapters/_a2a_support.py +275 -0
  90. ksadk/events/adapters/_codex_interactions.py +375 -0
  91. ksadk/events/adapters/_codex_items.py +801 -0
  92. ksadk/events/adapters/_codex_validators.py +311 -0
  93. ksadk/events/adapters/_langgraph_support.py +785 -0
  94. ksadk/events/adapters/a2a.py +841 -0
  95. ksadk/events/adapters/adk.py +503 -0
  96. ksadk/events/adapters/codex.py +717 -0
  97. ksadk/events/adapters/langgraph.py +745 -0
  98. ksadk/events/canonical.py +440 -0
  99. ksadk/events/canonical_replay.py +497 -0
  100. ksadk/events/canonical_store.py +468 -0
  101. ksadk/events/cold_recovery.py +257 -0
  102. ksadk/events/content.py +89 -0
  103. ksadk/events/identity.py +86 -0
  104. ksadk/events/pipeline.py +455 -0
  105. ksadk/events/projections.py +66 -0
  106. ksadk/events/reducer.py +661 -0
  107. ksadk/events/replay.py +3 -36
  108. ksadk/events/runtime_event.py +20 -265
  109. ksadk/events/session_event.py +324 -0
  110. ksadk/events/store.py +3 -316
  111. ksadk/events/v1_compat.py +41 -0
  112. ksadk/harness/runtime.py +160 -57
  113. ksadk/identity/resolver.py +4 -3
  114. ksadk/interaction/__init__.py +28 -0
  115. ksadk/interaction/contracts.py +136 -0
  116. ksadk/interaction/ledger.py +189 -0
  117. ksadk/interaction/provider.py +122 -0
  118. ksadk/interaction/providers/__init__.py +52 -0
  119. ksadk/interaction/providers/adk.py +40 -0
  120. ksadk/interaction/providers/codex.py +94 -0
  121. ksadk/interaction/providers/langgraph.py +58 -0
  122. ksadk/kernel/__init__.py +117 -0
  123. ksadk/kernel/authorization.py +258 -0
  124. ksadk/kernel/bootstrap.py +1074 -0
  125. ksadk/kernel/contract_fingerprints.py +61 -0
  126. ksadk/kernel/contracts.py +362 -0
  127. ksadk/kernel/control.py +219 -0
  128. ksadk/kernel/errors.py +79 -0
  129. ksadk/kernel/ingress.py +1102 -0
  130. ksadk/kernel/mapping.py +111 -0
  131. ksadk/kernel/memory_store.py +1133 -0
  132. ksadk/kernel/postgres_store.py +1756 -0
  133. ksadk/kernel/recovery.py +452 -0
  134. ksadk/kernel/runtime_identity.py +103 -0
  135. ksadk/kernel/sql/001_agent_kernel.sql +120 -0
  136. ksadk/kernel/sqlite_store.py +1410 -0
  137. ksadk/kernel/state.py +145 -0
  138. ksadk/kernel/store.py +232 -0
  139. ksadk/kernel/worker.py +953 -0
  140. ksadk/knowledge_base/client.py +7 -0
  141. ksadk/knowledge_base/service.py +32 -5
  142. ksadk/memory/__init__.py +61 -0
  143. ksadk/memory/adk/backends/base_ltm_backend.py +96 -1
  144. ksadk/memory/adk/backends/http_ltm_backend.py +14 -5
  145. ksadk/memory/adk/backends/inmemory_ltm_backend.py +136 -3
  146. ksadk/memory/adk/backends/sdk_ltm_backend.py +384 -2
  147. ksadk/memory/adk/backends/sqlite_ltm_backend.py +95 -0
  148. ksadk/memory/coordinator.py +461 -0
  149. ksadk/memory/events.py +202 -0
  150. ksadk/memory/extraction.py +228 -0
  151. ksadk/memory/ltm_backend_factory.py +16 -3
  152. ksadk/memory/models.py +316 -0
  153. ksadk/memory/policy.py +215 -0
  154. ksadk/memory/provider.py +47 -0
  155. ksadk/memory/provider_adapter.py +159 -0
  156. ksadk/memory/provider_resolver.py +71 -0
  157. ksadk/memory/providers/__init__.py +5 -0
  158. ksadk/memory/providers/local_sqlite.py +490 -0
  159. ksadk/memory/resolved_policy.py +145 -0
  160. ksadk/memory/service.py +188 -6
  161. ksadk/model_proxy/bootstrap.py +16 -9
  162. ksadk/model_proxy/detect.py +202 -9
  163. ksadk/model_proxy/namespace.py +39 -15
  164. ksadk/model_proxy/server.py +24 -5
  165. ksadk/model_proxy/transform.py +60 -2
  166. ksadk/observability/__init__.py +25 -0
  167. ksadk/observability/session_log.py +420 -0
  168. ksadk/observability/trajectory.py +105 -0
  169. ksadk/prompts/__init__.py +71 -0
  170. ksadk/prompts/compiler.py +215 -0
  171. ksadk/prompts/models.py +82 -0
  172. ksadk/prompts/projection.py +89 -0
  173. ksadk/prompts/resolved.py +137 -0
  174. ksadk/prompts/sources.py +233 -0
  175. ksadk/runners/_langgraph_runner_streams.py +822 -0
  176. ksadk/runners/adk_runner.py +5 -5
  177. ksadk/runners/base_runner.py +16 -0
  178. ksadk/runners/factory.py +1 -2
  179. ksadk/runners/langgraph_runner.py +198 -437
  180. ksadk/runtime/_runner_adapter/__init__.py +1 -0
  181. ksadk/runtime/_runner_adapter/stream_mapping.py +788 -0
  182. ksadk/runtime/adapter.py +118 -2
  183. ksadk/runtime/conversation_execution.py +316 -182
  184. ksadk/runtime/executor.py +144 -3
  185. ksadk/runtime/factory.py +193 -7
  186. ksadk/runtime/hosted_finalizer.py +268 -0
  187. ksadk/runtime/launch.py +14 -1
  188. ksadk/runtime/preprocessing.py +25 -3
  189. ksadk/runtime/runner_adapter.py +288 -508
  190. ksadk/runtime/usage.py +33 -0
  191. ksadk/sandbox/backends/e2b.py +35 -2
  192. ksadk/server/composition.py +8 -0
  193. ksadk/server/factory.py +69 -6
  194. ksadk/server/routes/kernel_ingress.py +258 -0
  195. ksadk/server/routes/openai_compat.py +88 -8
  196. ksadk/server/routes/projection.py +35 -20
  197. ksadk/server/routes/run.py +94 -0
  198. ksadk/server/routes/sessions.py +2 -0
  199. ksadk/server/routes/streaming.py +5 -2
  200. ksadk/server/static/assets/{ArtifactsPanel-CakCqxEN.js → ArtifactsPanel-DSU1sWD_.js} +1 -1
  201. ksadk/server/static/assets/{CodeBlock-CynLknyO.js → CodeBlock-DOH6MVcn.js} +4 -4
  202. ksadk/server/static/assets/{MathMessageMarkdown-3sQdOysS.js → MathMessageMarkdown-BL8my1R2.js} +4 -4
  203. ksadk/server/static/assets/MathMessageMarkdown-BorAY7qD.css +1 -0
  204. ksadk/server/static/assets/MermaidBlock-Dz4IP-Tx.js +312 -0
  205. ksadk/server/static/assets/NativeTerminalPanel-DUrK0JpZ.js +1 -0
  206. ksadk/server/static/assets/abnfDiagram-VCTEODGH-g20pFzNV.js +1 -0
  207. ksadk/server/static/assets/arc-C4FzinUA.js +1 -0
  208. ksadk/server/static/assets/architecture-7GRP2DOG-DjKratdf.js +1 -0
  209. ksadk/server/static/assets/architectureDiagram-5GKGNRK7-DpIqL5h4.js +36 -0
  210. ksadk/server/static/assets/blockDiagram-NRAW4CY4-BjkljTNz.js +129 -0
  211. ksadk/server/static/assets/c4Diagram-UCG6FXSJ-Dk_ieq2X.js +38 -0
  212. ksadk/server/static/assets/channel-4cQHKtx1.js +1 -0
  213. ksadk/server/static/assets/chunk-2Q5K7J3B-DmgWkESh.js +1 -0
  214. ksadk/server/static/assets/{chunk-FMBD7UC4-B_fH2YuH.js → chunk-5VM5RSS4-BVHAchFb.js} +1 -1
  215. ksadk/server/static/assets/{chunk-ND2GUHAM-D6rFLaVV.js → chunk-F27PBJKO-C-ipQzuS.js} +1 -1
  216. ksadk/server/static/assets/chunk-G27WJ6UU-C2EyJbIK.js +231 -0
  217. ksadk/server/static/assets/{chunk-4BX2VUAB-B9drTgOW.js → chunk-JWPE2WC7-vYvVJb_M.js} +1 -1
  218. ksadk/server/static/assets/chunk-LCL6LL3I-BcI6ysPT.js +206 -0
  219. ksadk/server/static/assets/chunk-POPQ4Y6H-C030x_Z1.js +1 -0
  220. ksadk/server/static/assets/chunk-RHFEMEQ7-yyGpsz4n.js +168 -0
  221. ksadk/server/static/assets/chunk-SVP7TREG-BLTlmMU7.js +88 -0
  222. ksadk/server/static/assets/chunk-XXDRQBXY-C_32ArgP.js +1 -0
  223. ksadk/server/static/assets/classDiagram-DTDB5LWJ-DLFd9Xi0.js +1 -0
  224. ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-DLFd9Xi0.js +1 -0
  225. ksadk/server/static/assets/{cose-bilkent-S5V4N54A-ChNs_Mdk.js → cose-bilkent-JH36ORCC-9YQYwdl0.js} +1 -1
  226. ksadk/server/static/assets/cynefin-OW5HDTMX-DjEM48Qp.js +1 -0
  227. ksadk/server/static/assets/cynefinDiagram-5FMLGOSQ-ErLF5N13.js +62 -0
  228. ksadk/server/static/assets/cytoscape.esm-CyCl8rPi.js +321 -0
  229. ksadk/server/static/assets/dagre-3AP2YEHR-DFiQF-6f.js +4 -0
  230. ksadk/server/static/assets/dagre-BuhGnRI1.js +1 -0
  231. ksadk/server/static/assets/diagram-S7CK7UJ4-Bt1v8-GC.js +30 -0
  232. ksadk/server/static/assets/diagram-UQ7AKVKN-DSCxJdBK.js +41 -0
  233. ksadk/server/static/assets/diagram-VSXAHHWV-DHfYwp_9.js +3 -0
  234. ksadk/server/static/assets/diagram-VX7I27RA-DdzD-4Le.js +24 -0
  235. ksadk/server/static/assets/diagram-Z3DM3KII-Du-nAJ9F.js +24 -0
  236. ksadk/server/static/assets/{dist-Dli7qW-B.js → dist-8hqcx0sU.js} +1 -1
  237. ksadk/server/static/assets/dist-B7seoj3d.js +1 -0
  238. ksadk/server/static/assets/{dist-C1sz5Eo3.js → dist-BE7bu-DL.js} +1 -1
  239. ksadk/server/static/assets/{dist-fhOuWds5.js → dist-BVDffZ0U.js} +1 -1
  240. ksadk/server/static/assets/{dist-BjOdlDjj.js → dist-BX8z72IC.js} +1 -1
  241. ksadk/server/static/assets/{dist-DsHPdk3W.js → dist-Bf2M8m3N.js} +1 -1
  242. ksadk/server/static/assets/{dist-o2OVmKvt.js → dist-BjU6y-A2.js} +1 -1
  243. ksadk/server/static/assets/{dist-DWKByxWV.js → dist-BytlxIDT.js} +1 -1
  244. ksadk/server/static/assets/{dist-DJN_6Y39.js → dist-CSh_DE14.js} +1 -1
  245. ksadk/server/static/assets/{dist-DQU9EWw-.js → dist-CXYYBw3_.js} +1 -1
  246. ksadk/server/static/assets/{dist-4N2NMVYg.js → dist-C_wsv-Qd.js} +1 -1
  247. ksadk/server/static/assets/{dist-gLnrGsoU.js → dist-ClxGviKw.js} +1 -1
  248. ksadk/server/static/assets/{dist-j6Dxmf_j.js → dist-CttYUqzS.js} +1 -1
  249. ksadk/server/static/assets/{dist-D6MpCZtz.js → dist-DNp6rLRA.js} +1 -1
  250. ksadk/server/static/assets/{dist-BB3-OwTQ.js → dist-DUX-id_F.js} +1 -1
  251. ksadk/server/static/assets/{dist-_pHPTyZ-.js → dist-DmldrHd_.js} +1 -1
  252. ksadk/server/static/assets/{dist-CWV4lDr1.js → dist-DnfXs8Vn.js} +2 -2
  253. ksadk/server/static/assets/{dist-B5oS1zcj.js → dist-Dq9gLD7L.js} +1 -1
  254. ksadk/server/static/assets/{dist-B-jNR6Vn.js → dist-W-TIVntx.js} +1 -1
  255. ksadk/server/static/assets/{dist-BIlKoW0k.js → dist-dDdADKFA.js} +1 -1
  256. ksadk/server/static/assets/{dist-X4TnqwOK.js → dist-dydu68Fl.js} +1 -1
  257. ksadk/server/static/assets/{dist-CmZHzlwZ.js → dist-ngtN0DJB.js} +1 -1
  258. ksadk/server/static/assets/ebnfDiagram-PWID7BFC-Da9C9NO1.js +1 -0
  259. ksadk/server/static/assets/erDiagram-SSCWMZ5O-EAGqqR79.js +99 -0
  260. ksadk/server/static/assets/eventmodeling-NTZA5JFV-CMgMaJrC.js +1 -0
  261. ksadk/server/static/assets/flowDiagram-A5DVABFB-BBKL0x3P.js +1 -0
  262. ksadk/server/static/assets/ganttDiagram-EL5Y4UJY-DqJsKb59.js +292 -0
  263. ksadk/server/static/assets/gitGraph-4MIJSDKK-Dw63mrhw.js +1 -0
  264. ksadk/server/static/assets/gitGraphDiagram-WWUBYQGX-B6g4dtDi.js +106 -0
  265. ksadk/server/static/assets/index-8ipRcQ-M.js +240 -0
  266. ksadk/server/static/assets/index-ByFCqjlh.css +2 -0
  267. ksadk/server/static/assets/info-A6RAGUB7-B6iAblII.js +1 -0
  268. ksadk/server/static/assets/infoDiagram-RXCK75RN-Djm_nbd5.js +2 -0
  269. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-K0OOfVna.js → ishikawaDiagram-5VMMS53U-DWya9SG2.js} +6 -6
  270. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-8LeOJaZp.js → journeyDiagram-EYS64GPL-DutxhSNI.js} +6 -6
  271. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-C6uCgZrW.js → kanban-definition-3QL26DDD-DJdOF8Fm.js} +10 -10
  272. ksadk/server/static/assets/katex-vFWytM5c.js +257 -0
  273. ksadk/server/static/assets/{linear-hdBcYzM8.js → linear-BHjG8b-J.js} +1 -1
  274. ksadk/server/static/assets/mermaid-parser.core-KGSy4jWT.js +166 -0
  275. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-Bjy_2v4_.js → mindmap-definition-FBJOCRG2-9-HC88D4.js} +29 -29
  276. ksadk/server/static/assets/packet-AYTQ26CC-BOYAipuq.js +1 -0
  277. ksadk/server/static/assets/pegDiagram-XKGWAZYB-DrEZd4fD.js +1 -0
  278. ksadk/server/static/assets/pie-WAS4IAKB-Ctq0Kbku.js +1 -0
  279. ksadk/server/static/assets/pieDiagram-E7YTZNPT-DpjqOFFp.js +39 -0
  280. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-BmkRbwCg.js → quadrantDiagram-AXDQQJYC-BiNhgOXX.js} +6 -6
  281. ksadk/server/static/assets/radar-RG4KPBEZ-DN0-ul_0.js +1 -0
  282. ksadk/server/static/assets/railroad-74A4TZTK-C6cC_to0.js +1 -0
  283. ksadk/server/static/assets/railroad-abnf-HS5TGJTU-BimjIuRc.js +1 -0
  284. ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-CvY65ABq.js +1 -0
  285. ksadk/server/static/assets/railroad-peg-WCYAUIDC-D72Iq_Tv.js +1 -0
  286. ksadk/server/static/assets/railroadDiagram-O6MQD6OU-_Y3Ojg6G.js +1 -0
  287. ksadk/server/static/assets/requirementDiagram-EFPCY7ZU-DeCFdg9K.js +84 -0
  288. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-l6iXmU65.js → sankeyDiagram-P5KCCOFB-CPH75rhw.js} +7 -7
  289. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-Cq6UBeQB.js → sequenceDiagram-WJ2MYXX4-OEpQQdr4.js} +10 -10
  290. ksadk/server/static/assets/sizeCapture-X5ZJPWSS-heUErzhI.js +1 -0
  291. ksadk/server/static/assets/stateDiagram-HBIQ2CUA-DvA3jSMB.js +1 -0
  292. ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BgQY03nz.js +1 -0
  293. ksadk/server/static/assets/swimlanes-XN3QIQJK-CR-dfN_t.js +1 -0
  294. ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-DLVw3q5Q.js +8 -0
  295. ksadk/server/static/assets/{timeline-definition-PNZ67QCA-DEz24vV0.js → timeline-definition-24CTP7MA-DAVJf1TX.js} +7 -7
  296. ksadk/server/static/assets/treeView-Q6P3EWNA-BMpO0wsB.js +1 -0
  297. ksadk/server/static/assets/treemap-WGGIJYW6-Et0aqzU3.js +1 -0
  298. ksadk/server/static/assets/vennDiagram-4TSXK5OY-DwM8eDkh.js +34 -0
  299. ksadk/server/static/assets/wardley-WFR3VGLG-CbY0mhpl.js +1 -0
  300. ksadk/server/static/assets/wardleyDiagram-VM6X3IG4-DZL-Zz2t.js +78 -0
  301. ksadk/server/static/assets/xychartDiagram-S5SC5T6Z-Dy6Yh_hu.js +7 -0
  302. ksadk/server/static/index.html +2 -2
  303. ksadk/sessions/__init__.py +26 -9
  304. ksadk/sessions/_local_service_sync.py +688 -0
  305. ksadk/sessions/_local_tables.py +21 -0
  306. ksadk/sessions/_postgres_schema.py +305 -0
  307. ksadk/sessions/_postgres_tables.py +15 -0
  308. ksadk/sessions/base.py +80 -1
  309. ksadk/sessions/in_memory.py +53 -2
  310. ksadk/sessions/local_service.py +63 -604
  311. ksadk/sessions/postgres_service.py +58 -148
  312. ksadk/sessions/resilient.py +35 -1
  313. ksadk/studio/api.py +577 -25
  314. ksadk/studio/api_contracts.py +114 -5
  315. ksadk/studio/api_memory_routes.py +82 -0
  316. ksadk/studio/authoring.py +252 -27
  317. ksadk/studio/authoring_coordinator.py +799 -67
  318. ksadk/studio/builder.py +88 -11
  319. ksadk/studio/capabilities.py +17 -0
  320. ksadk/studio/cloud.py +1916 -95
  321. ksadk/studio/codex_agent_service.py +125 -14
  322. ksadk/studio/codex_authoring.py +469 -0
  323. ksadk/studio/codex_builder.py +138 -25
  324. ksadk/studio/codex_manifest.py +51 -22
  325. ksadk/studio/codex_run.py +46 -3
  326. ksadk/studio/compiler.py +1 -0
  327. ksadk/studio/contracts.py +114 -3
  328. ksadk/studio/event_store.py +25 -95
  329. ksadk/studio/framework_run.py +231 -3
  330. ksadk/studio/hosted_kernel.py +317 -0
  331. ksadk/studio/manifest_resolver.py +131 -0
  332. ksadk/studio/model_client.py +95 -9
  333. ksadk/studio/model_profile_service.py +1 -1
  334. ksadk/studio/operations.py +37 -5
  335. ksadk/studio/otel_trace.py +348 -42
  336. ksadk/studio/pcm_memory.py +85 -0
  337. ksadk/studio/repository.py +23 -2
  338. ksadk/studio/resource_catalog.py +43 -0
  339. ksadk/studio/run_service.py +563 -137
  340. ksadk/studio/runtime_source.py +42 -16
  341. ksadk/studio/service.py +1180 -72
  342. ksadk/studio/shared_web.py +32 -22
  343. ksadk/studio/static/assets/index-BLpcdrgW.css +1 -0
  344. ksadk/studio/static/assets/index-CYekR2Xv.js +260 -0
  345. ksadk/studio/static/index.html +2 -2
  346. ksadk/studio/templates.py +100 -8
  347. ksadk/studio/validator.py +19 -0
  348. ksadk/studio/workspace.py +30 -32
  349. ksadk/tracing/setup.py +304 -3
  350. ksadk/version.py +1 -1
  351. ksadk_runtime_common/schemas/runtime_event_v1.json +37 -0
  352. ksadk_runtime_common/schemas/runtime_event_v2.json +271 -0
  353. ksadk/events/parser.py +0 -191
  354. ksadk/server/static/assets/MathMessageMarkdown-DeYh8QPr.css +0 -1
  355. ksadk/server/static/assets/MermaidBlock-NERD_ef7.js +0 -303
  356. ksadk/server/static/assets/NativeTerminalPanel-CiHWf82q.js +0 -1
  357. ksadk/server/static/assets/arc-DrgjcKhl.js +0 -1
  358. ksadk/server/static/assets/architecture-7EHR7CIX-CpZLRCcY.js +0 -1
  359. ksadk/server/static/assets/architectureDiagram-3BPJPVTR-BXoVRbQ6.js +0 -36
  360. ksadk/server/static/assets/blockDiagram-GPEHLZMM-BnaB3Ywv.js +0 -132
  361. ksadk/server/static/assets/c4Diagram-AAUBKEIU-Dw4weZcp.js +0 -10
  362. ksadk/server/static/assets/channel-Dt3rQTFI.js +0 -1
  363. ksadk/server/static/assets/chunk-2J33WTMH-ChjpLK7j.js +0 -1
  364. ksadk/server/static/assets/chunk-55IACEB6-CK4ROATy.js +0 -1
  365. ksadk/server/static/assets/chunk-727SXJPM-B9KtTTgw.js +0 -206
  366. ksadk/server/static/assets/chunk-AQP2D5EJ-1O1mki3m.js +0 -231
  367. ksadk/server/static/assets/chunk-QZHKN3VN-iXEZtKQc.js +0 -1
  368. ksadk/server/static/assets/classDiagram-4FO5ZUOK-DIveV17Q.js +0 -1
  369. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-DIveV17Q.js +0 -1
  370. ksadk/server/static/assets/cytoscape.esm-DelgaX4f.js +0 -321
  371. ksadk/server/static/assets/dagre-BM42HDAG-D0QXg_MQ.js +0 -4
  372. ksadk/server/static/assets/dagre-Bx709z4p.js +0 -1
  373. ksadk/server/static/assets/diagram-2AECGRRQ-DBDDKwBQ.js +0 -43
  374. ksadk/server/static/assets/diagram-5GNKFQAL-BkwDOxmO.js +0 -10
  375. ksadk/server/static/assets/diagram-KO2AKTUF-Bv8waYvY.js +0 -3
  376. ksadk/server/static/assets/diagram-LMA3HP47-e1VECDdu.js +0 -24
  377. ksadk/server/static/assets/diagram-OG6HWLK6-Ds9adfRl.js +0 -24
  378. ksadk/server/static/assets/dist-B5J2uhNk.js +0 -1
  379. ksadk/server/static/assets/erDiagram-TEJ5UH35-BPItFKH6.js +0 -85
  380. ksadk/server/static/assets/eventmodeling-FCH6USID-DYFSxbqr.js +0 -1
  381. ksadk/server/static/assets/flowDiagram-I6XJVG4X-BbYB9w5Y.js +0 -162
  382. ksadk/server/static/assets/ganttDiagram-6RSMTGT7-TQXa0KXv.js +0 -292
  383. ksadk/server/static/assets/gitGraph-WXDBUCRP-DyNYp6BN.js +0 -1
  384. ksadk/server/static/assets/gitGraphDiagram-PVQCEYII-bKblCnpa.js +0 -106
  385. ksadk/server/static/assets/graphlib-B8gBHxth.js +0 -1
  386. ksadk/server/static/assets/index-R52pJJvg.js +0 -177
  387. ksadk/server/static/assets/index-jJr3_955.css +0 -2
  388. ksadk/server/static/assets/info-J43DQDTF-WUx7qTde.js +0 -1
  389. ksadk/server/static/assets/infoDiagram-5YYISTIA-BGVT7J_i.js +0 -2
  390. ksadk/server/static/assets/katex-CYWseY5E.js +0 -265
  391. ksadk/server/static/assets/mermaid-parser.core-BWrDjSZM.js +0 -161
  392. ksadk/server/static/assets/packet-YPE3B663-BN13y_xl.js +0 -1
  393. ksadk/server/static/assets/pie-LRSECV5Y-ftSLXhhc.js +0 -1
  394. ksadk/server/static/assets/pieDiagram-4H26LBE5-GmmqNiVH.js +0 -30
  395. ksadk/server/static/assets/radar-GUYGQ44K-jaICkIeO.js +0 -1
  396. ksadk/server/static/assets/requirementDiagram-4Y6WPE33-DSWFqL7c.js +0 -84
  397. ksadk/server/static/assets/stateDiagram-AJRCARHV-qzTQpg3Y.js +0 -1
  398. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-BYCoYGAN.js +0 -1
  399. ksadk/server/static/assets/treeView-BLDUP644-Cpib_vlz.js +0 -1
  400. ksadk/server/static/assets/treemap-LRROVOQU-DQxfmVHK.js +0 -1
  401. ksadk/server/static/assets/vennDiagram-CIIHVFJN-CMCixki2.js +0 -34
  402. ksadk/server/static/assets/wardley-L42UT6IY-BQYHnnkU.js +0 -1
  403. ksadk/server/static/assets/wardleyDiagram-YWT4CUSO-CVzB8F9K.js +0 -78
  404. ksadk/server/static/assets/xychartDiagram-2RQKCTM6-BFBqBLc1.js +0 -7
  405. ksadk/studio/evaluation.py +0 -192
  406. ksadk/studio/static/assets/index-CaEN0eTR.css +0 -1
  407. ksadk/studio/static/assets/index-DI2eY_NH.js +0 -252
  408. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/WHEEL +0 -0
  409. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/entry_points.txt +0 -0
  410. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/licenses/LICENSE +0 -0
  411. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/top_level.txt +0 -0
@@ -13,22 +13,32 @@ from __future__ import annotations
13
13
  import asyncio
14
14
  import copy
15
15
  import inspect
16
- import json
17
16
  import logging
17
+ import time
18
18
  from collections.abc import Mapping
19
- from contextlib import nullcontext
20
19
  from dataclasses import dataclass, field
21
20
  from typing import Any, AsyncIterator, Dict, Optional, cast
22
21
 
23
- from ksadk.conversations.runtime_input import _runner_name
24
- from ksadk.conversations.runtime_observability import (
25
- _conversation_span_scope,
26
- _set_conversation_input_attributes,
27
- _set_conversation_output_attributes,
28
- _set_conversation_span_attributes,
29
- _set_conversation_usage_attributes,
22
+ from pydantic import JsonValue
23
+
24
+ from ksadk.events.canonical import (
25
+ ApprovalRequest,
26
+ InteractionRequested,
27
+ OutputRef,
28
+ RunCanceled,
29
+ RunCompleted,
30
+ RunInterrupted,
31
+ RunStarted,
32
+ RuntimeEvent,
33
+ SourceRef,
34
+ )
35
+ from ksadk.events.identity import (
36
+ stable_event_id,
37
+ stable_item_id,
38
+ stable_scope_id,
30
39
  )
31
- from ksadk.events.runtime_event import EventType, RuntimeEvent
40
+ from ksadk.kernel.contracts import RuntimeCapability, RuntimeCapabilityMatrix
41
+ from ksadk.kernel.errors import UnsupportedControlError
32
42
  from ksadk.runners.base_runner import BaseRunner
33
43
  from ksadk.runtime.adapter import (
34
44
  RESUME_START_REQUEST_NATIVE_KEY,
@@ -44,79 +54,21 @@ from ksadk.runtime.adapter import (
44
54
  )
45
55
  from ksadk.runtime.preprocessing import PreparedRuntimeStart, prepare_runtime_start
46
56
  from ksadk.runtime.runner_loading import ensure_runner_loaded
47
- from ksadk.runtime_context import platform_invocation_scope
48
- from ksadk.tools.gateway import approval_interrupt_info_from_result
49
57
 
50
58
  logger = logging.getLogger(__name__)
51
59
 
52
- _STREAM_STOP = object()
53
- _ResumeKey = tuple[str, str, str, str, str]
54
-
55
-
56
- async def _anext_or_stop(gen: AsyncIterator[Any]) -> Any:
57
- """取下一个 chunk;流结束返回 _STREAM_STOP sentinel(便于竞速)。"""
58
- try:
59
- return await gen.__anext__()
60
- except StopAsyncIteration:
61
- return _STREAM_STOP
62
-
63
-
64
- def _a2ui_surface_event(chunk: Any) -> tuple[str, dict[str, Any]] | None:
65
- """Recognize a validated A2UI tool envelope and make it a first-class event.
60
+ # 保留既有 monkeypatch patch 点:stream_mapping 在调用时经本模块属性解析。
61
+ from ksadk.conversations.runtime_observability import ( # noqa: E402,F401
62
+ _conversation_span_scope,
63
+ )
66
64
 
67
- The dynamic ``generate_a2ui`` tool returns official v0.9 operations as a
68
- JSON tool result. Tool results are otherwise opaque to the runtime, which
69
- would leave AG-UI with nothing to project until a page reload reconstructs
70
- history. Convert exactly that envelope at the runtime boundary so it is
71
- streamed, persisted, and replayed like every other A2UI surface.
72
- """
65
+ # dict-chunk 退化路径的流竞速/事件映射实现拆至 _runner_adapter 子包(纯移动,行为不变)。
66
+ from ksadk.runtime._runner_adapter.stream_mapping import ( # noqa: E402
67
+ _STREAM_STOP,
68
+ _RunnerStreamMappingMixin,
69
+ )
73
70
 
74
- if not isinstance(chunk, dict):
75
- return None
76
- value = chunk.get("tool_output", chunk.get("output"))
77
- if value is not None and hasattr(value, "content"):
78
- value = value.content
79
- if isinstance(value, str):
80
- try:
81
- value = json.loads(value)
82
- except (TypeError, ValueError):
83
- return None
84
- if not isinstance(value, Mapping):
85
- return None
86
- operations_raw = value.get("a2ui_operations")
87
- if not isinstance(operations_raw, list) or not operations_raw:
88
- return None
89
- operations = [dict(operation) for operation in operations_raw if isinstance(operation, Mapping)]
90
- if not operations:
91
- return None
92
-
93
- known: list[tuple[str, str]] = []
94
- for operation in operations:
95
- for key, event_type in (
96
- ("createSurface", EventType.A2UI_SURFACE_BEGIN),
97
- ("updateComponents", EventType.A2UI_SURFACE_UPDATE),
98
- ("updateDataModel", EventType.A2UI_SURFACE_UPDATE),
99
- ("deleteSurface", EventType.A2UI_SURFACE_END),
100
- ):
101
- detail = operation.get(key)
102
- if isinstance(detail, Mapping) and isinstance(detail.get("surfaceId"), str):
103
- surface_id = detail["surfaceId"].strip()
104
- if surface_id:
105
- known.append((surface_id, event_type))
106
- break
107
- if not known:
108
- return None
109
- surface_ids = {surface_id for surface_id, _event_type in known}
110
- if len(surface_ids) != 1:
111
- logger.warning("ignoring A2UI tool result with multiple surfaces")
112
- return None
113
- surface_id = known[0][0]
114
- event_type = (
115
- EventType.A2UI_SURFACE_BEGIN
116
- if any(kind == EventType.A2UI_SURFACE_BEGIN for _surface_id, kind in known)
117
- else known[0][1]
118
- )
119
- return event_type, {"surface_id": surface_id, "operations": operations}
71
+ _ResumeKey = tuple[str, str, str, str, str]
120
72
 
121
73
 
122
74
  def _coerce_literal(value: Any, allowed: tuple[str, ...], default: str) -> Any:
@@ -161,9 +113,13 @@ class _ActiveRun:
161
113
  skip_runner: bool = False
162
114
  done: bool = False
163
115
  completion_metrics: dict[str, Any] = field(default_factory=dict)
116
+ # dict-chunk 退化路径:追踪已 ItemStarted 的 item key,避免重复发 Started。
117
+ started_items: set[tuple[str, str]] = field(default_factory=set)
118
+ # dict-chunk 退化路径:final_answer message 的 item_id,供 RunCompleted.output_refs 引用。
119
+ final_answer_item_id: Optional[str] = None
164
120
 
165
121
 
166
- class RunnerRuntimeAdapter(RuntimeAdapter):
122
+ class RunnerRuntimeAdapter(_RunnerStreamMappingMixin, RuntimeAdapter):
167
123
  """把 ``BaseRunner`` 映射为平台六动词的通用 adapter。"""
168
124
 
169
125
  def __init__(self, runner: BaseRunner, *, runtime_type: str) -> None:
@@ -182,6 +138,63 @@ class RunnerRuntimeAdapter(RuntimeAdapter):
182
138
 
183
139
  # ---- 框架钩子(子类按需 override) ----
184
140
 
141
+ def capabilities(self) -> RuntimeCapabilityMatrix:
142
+ """诚实矩阵:cancel 经 asyncio 任务打断(emulated,过 conformance);
143
+ resume/checkpoint 依赖 runner 声明的原生 checkpoint;attach/durable_restore
144
+ 依赖 ``attach_runtime_handle`` seam 与跨进程持久化,内存表不算数。
145
+ """
146
+
147
+ def _unavailable(reason: str) -> RuntimeCapability:
148
+ return RuntimeCapability(supported=False, mode="unavailable", reason=reason)
149
+
150
+ checkpoint_capability = self._checkpoint_capability()
151
+ attach_seam = callable(getattr(self._runner, "attach_runtime_handle", None))
152
+ checkpoint_supported = bool(checkpoint_capability.supported)
153
+ durable_supported = bool(
154
+ checkpoint_capability.durable
155
+ and checkpoint_capability.shared_across_pods
156
+ and attach_seam
157
+ )
158
+ return RuntimeCapabilityMatrix(
159
+ cancel=RuntimeCapability(
160
+ supported=True,
161
+ mode="emulated",
162
+ reason="runner_stream_task_interrupt",
163
+ ),
164
+ pause=_unavailable("runtime_no_native_pause"),
165
+ resume=(
166
+ RuntimeCapability(supported=True, mode="native")
167
+ if checkpoint_supported
168
+ else _unavailable("runtime_no_native_checkpoint")
169
+ ),
170
+ submit_interaction=_unavailable("runtime_no_live_interaction_channel"),
171
+ attach=(
172
+ RuntimeCapability(supported=True, mode="native")
173
+ if attach_seam
174
+ else _unavailable("runner_no_durable_attach_seam")
175
+ ),
176
+ steer=_unavailable("runtime_no_native_steer"),
177
+ inject=_unavailable("runtime_no_native_inject"),
178
+ checkpoint=(
179
+ RuntimeCapability(supported=True, mode="native")
180
+ if checkpoint_supported
181
+ else _unavailable("runtime_no_native_checkpoint")
182
+ ),
183
+ durable_restore=(
184
+ RuntimeCapability(supported=True, mode="native")
185
+ if durable_supported
186
+ else _unavailable("durable_restore_requires_cross_process_checkpoint")
187
+ ),
188
+ )
189
+
190
+ async def durable_restore(self, handle: RunHandle) -> RunHandle:
191
+ if not self.capabilities().durable_restore.supported:
192
+ raise UnsupportedControlError(
193
+ f"{self._runtime_type} has no cross-process checkpoint backend for run "
194
+ f"{handle.run_id!r}"
195
+ )
196
+ return await self.attach(handle)
197
+
185
198
  def _checkpoint_capability(self) -> CheckpointCapability:
186
199
  """诚实暴露 checkpoint 粒度。默认读 runner.describe_checkpoint_capability。"""
187
200
  raw = {}
@@ -285,7 +298,7 @@ class RunnerRuntimeAdapter(RuntimeAdapter):
285
298
  )
286
299
  attach = getattr(self._runner, "attach_runtime_handle", None)
287
300
  if not callable(attach):
288
- raise RuntimeError(
301
+ raise UnsupportedControlError(
289
302
  f"runner for {self._runtime_type!r} has no durable "
290
303
  "attach_runtime_handle capability"
291
304
  )
@@ -408,7 +421,7 @@ class RunnerRuntimeAdapter(RuntimeAdapter):
408
421
  capability = self._require_native_checkpoint_capability()
409
422
  checkpoint_id = str(handle.native_ref.get("checkpoint_id") or "").strip()
410
423
  if not checkpoint_id:
411
- raise RuntimeError(
424
+ raise UnsupportedControlError(
412
425
  f"{self._runtime_type} runner has no native checkpoint for run "
413
426
  f"{handle.run_id!r}"
414
427
  )
@@ -425,7 +438,7 @@ class RunnerRuntimeAdapter(RuntimeAdapter):
425
438
  if capability.supported:
426
439
  return capability
427
440
  detail = capability.reason or "runner does not expose framework checkpoints"
428
- raise RuntimeError(
441
+ raise UnsupportedControlError(
429
442
  f"{self._runtime_type} native checkpoint capability is unavailable: {detail}"
430
443
  )
431
444
 
@@ -495,30 +508,19 @@ class RunnerRuntimeAdapter(RuntimeAdapter):
495
508
  # 消费 pending cancel:start 时若已记 pending,立即中断该 turn。
496
509
  if handle.run_id in self._pending_cancels:
497
510
  self._pending_cancels.discard(handle.run_id)
498
- yield self._event(
499
- handle,
500
- EventType.RUN_CANCELED,
501
- {
502
- "status": "cancelled",
503
- "cancel_result": CancelResult.PENDING_CANCEL_RECORDED.value,
504
- },
505
- )
511
+ yield self._make_run_canceled(handle, reason=CancelResult.PENDING_CANCEL_RECORDED.value)
506
512
  return
507
513
 
508
514
  if run.skip_runner:
509
515
  run.done = True
510
516
  self._active_runs.pop(handle.run_id, None)
511
- yield self._event(
512
- handle,
513
- EventType.RUN_COMPLETED,
514
- self._completion_payload(handle, status="already_resumed"),
515
- )
517
+ yield self._make_run_completed(handle)
516
518
  return
517
519
 
518
520
  if run.resume_key is not None:
519
521
  self._consumed_resumes.add(run.resume_key)
520
522
 
521
- yield self._event(handle, EventType.RUN_STARTED, {"status": "in_progress"})
523
+ yield self._make_run_started(handle)
522
524
 
523
525
  request = run.__dict__.get("_start_request")
524
526
  runner_input = self._build_runner_input(handle, request)
@@ -530,43 +532,35 @@ class RunnerRuntimeAdapter(RuntimeAdapter):
530
532
  run.stream = gen
531
533
  async for event in gen:
532
534
  yield event
533
- if event.event_type == EventType.APPROVAL_REQUESTED:
535
+ if event.event_type == "interaction.requested":
534
536
  approval_interrupted = True
537
+ # Track pending approval for canonical streams that bypass
538
+ # _chunk_to_event (e.g. stream_canonical_events).
539
+ if isinstance(event, InteractionRequested):
540
+ call_id = str(event.interaction_id or "")
541
+ if call_id:
542
+ run.pending_approvals.add(call_id)
543
+ pending_ids = handle.native_ref.setdefault("pending_approval_ids", [])
544
+ if call_id not in pending_ids:
545
+ pending_ids.append(call_id)
535
546
  if event.event_type in {
536
- EventType.RUN_COMPLETED,
537
- EventType.RUN_FAILED,
538
- EventType.RUN_CANCELED,
539
- EventType.RUN_INTERRUPTED,
547
+ "run.completed",
548
+ "run.failed",
549
+ "run.canceled",
550
+ "run.interrupted",
540
551
  }:
541
552
  terminal_event_seen = True
542
- if event.event_type in {EventType.RUN_FAILED, EventType.RUN_CANCELED}:
553
+ if event.event_type in {"run.failed", "run.canceled"}:
543
554
  return
544
555
 
545
556
  if run.interrupt_event.is_set() and not terminal_event_seen:
546
- yield self._event(
547
- handle,
548
- EventType.RUN_CANCELED,
549
- {
550
- "status": "cancelled",
551
- "cancel_result": CancelResult.INTERRUPTED_ACTIVE_TURN.value,
552
- },
557
+ yield self._make_run_canceled(
558
+ handle, reason=CancelResult.INTERRUPTED_ACTIVE_TURN.value
553
559
  )
554
560
  elif approval_interrupted and not terminal_event_seen:
555
- yield self._event(
556
- handle,
557
- EventType.RUN_INTERRUPTED,
558
- {"status": "input_required"},
559
- )
561
+ yield self._make_run_interrupted(handle, reason="input_required")
560
562
  elif not terminal_event_seen:
561
- yield self._event(
562
- handle,
563
- EventType.RUN_COMPLETED,
564
- self._completion_payload(
565
- handle,
566
- status="completed",
567
- metrics=run.completion_metrics,
568
- ),
569
- )
563
+ yield self._make_run_completed(handle, run=run, metrics=run.completion_metrics)
570
564
  finally:
571
565
  run.stream = None
572
566
  run.done = True
@@ -585,15 +579,17 @@ class RunnerRuntimeAdapter(RuntimeAdapter):
585
579
  else {}
586
580
  )
587
581
  base_metadata = merged.get("metadata")
588
- merged.update({
589
- "input": override.get("input"),
590
- "session_id": handle.session_id,
591
- "invocation_id": handle.run_id,
592
- "metadata": {
593
- **(dict(base_metadata) if isinstance(base_metadata, Mapping) else {}),
594
- **dict(override.get("metadata") or {}),
595
- },
596
- })
582
+ merged.update(
583
+ {
584
+ "input": override.get("input"),
585
+ "session_id": handle.session_id,
586
+ "invocation_id": handle.run_id,
587
+ "metadata": {
588
+ **(dict(base_metadata) if isinstance(base_metadata, Mapping) else {}),
589
+ **dict(override.get("metadata") or {}),
590
+ },
591
+ }
592
+ )
597
593
  for key, value in override.items():
598
594
  if key not in ("input", "metadata"):
599
595
  merged[key] = value
@@ -617,355 +613,19 @@ class RunnerRuntimeAdapter(RuntimeAdapter):
617
613
  "metadata": metadata,
618
614
  }
619
615
 
620
- async def _map_runner_stream(
621
- self, handle: RunHandle, runner_input: dict
622
- ) -> AsyncIterator[RuntimeEvent]:
623
- run = self._active_runs.get(handle.run_id)
624
- interrupt = run.interrupt_event if run is not None else None
625
- prepared_start = run.__dict__.get("_prepared_start") if run is not None else None
626
- invocation_context = (
627
- prepared_start.context if isinstance(prepared_start, PreparedRuntimeStart) else None
628
- )
629
- scope = (
630
- platform_invocation_scope(invocation_context)
631
- if invocation_context is not None
632
- else nullcontext()
633
- )
634
- runner_name = _runner_name(self._runner)
635
- accumulated_output = ""
636
- usage: dict[str, Any] = {}
637
- runner_gen: Optional[AsyncIterator[Any]] = None
638
- async with _conversation_span_scope(runner_name) as span:
639
- if isinstance(prepared_start, PreparedRuntimeStart):
640
- _set_conversation_span_attributes(
641
- span,
642
- agent_id=str(handle.native_ref.get("agent_id") or "agent"),
643
- user_id=str(handle.native_ref.get("user_id") or "user"),
644
- session_id=handle.session_id,
645
- invocation_id=handle.run_id,
646
- runner_name=runner_name,
647
- model=prepared_start.context.model,
648
- response_id=prepared_start.response_id,
649
- )
650
- _set_conversation_input_attributes(span, prepared_start.input_text)
651
- try:
652
- with scope:
653
- canonical_stream = getattr(self._runner, "stream_runtime_events", None)
654
- stream_result = (
655
- canonical_stream(runner_input)
656
- if callable(canonical_stream)
657
- else self._runner.stream(runner_input)
658
- )
659
- if inspect.iscoroutine(stream_result):
660
- # runner.stream 若声明为 async def -> AsyncIterator(非 async generator),
661
- # 调用返回 coroutine,需 await 得到迭代器。
662
- stream_result = await stream_result
663
- runner_gen = cast(AsyncIterator[Any], stream_result)
664
- while True:
665
- # 竞速:下一个 runner chunk vs cancel 中断事件。
666
- chunk_task = asyncio.ensure_future(_anext_or_stop(runner_gen))
667
- if run is not None:
668
- run.chunk_task = chunk_task
669
- wait_set = {chunk_task}
670
- interrupt_task = (
671
- asyncio.ensure_future(interrupt.wait())
672
- if interrupt is not None
673
- else None
674
- )
675
- if interrupt_task is not None:
676
- wait_set.add(interrupt_task)
677
- done, pending = await asyncio.wait(
678
- wait_set, return_when=asyncio.FIRST_COMPLETED
679
- )
680
- for task in pending:
681
- task.cancel()
682
- if pending:
683
- await asyncio.gather(*pending, return_exceptions=True)
684
- if interrupt_task is not None and interrupt_task in done:
685
- # cancel 中断:安全关闭 runner 流(同一 task)并停止。
686
- chunk_task.cancel()
687
- await asyncio.gather(chunk_task, return_exceptions=True)
688
- return
689
- try:
690
- chunk = chunk_task.result()
691
- except asyncio.CancelledError:
692
- if interrupt is not None and interrupt.is_set():
693
- return
694
- raise
695
- finally:
696
- if run is not None:
697
- run.chunk_task = None
698
- if chunk is _STREAM_STOP:
699
- return
700
- if isinstance(chunk, RuntimeEvent):
701
- if chunk.event_type in {
702
- EventType.TEXT_DELTA,
703
- EventType.TEXT_COMPLETED,
704
- } and chunk.phase == "final_answer":
705
- text = self._coerce(chunk.payload.get("text"))
706
- if chunk.event_type == EventType.TEXT_COMPLETED:
707
- accumulated_output = text
708
- else:
709
- accumulated_output += text
710
- elif chunk.event_type == EventType.USAGE_REPORTED:
711
- usage.update(chunk.payload)
712
- if isinstance(chunk, dict):
713
- chunk_type = str(chunk.get("type") or "")
714
- if chunk_type == "final" and run is not None:
715
- for source_key, target_key in (
716
- ("duration_ms", "duration_ms"),
717
- ("started_at", "started_at"),
718
- ("completed_at", "completed_at"),
719
- ("metrics_source", "source"),
720
- ):
721
- if chunk.get(source_key) is not None:
722
- run.completion_metrics[target_key] = chunk[source_key]
723
- if chunk_type in {"final", "text", "text_delta"}:
724
- text = self._coerce(
725
- chunk.get("delta") or chunk.get("output") or chunk.get("data")
726
- )
727
- if text:
728
- if chunk_type == "final" or chunk.get("replace"):
729
- accumulated_output = text
730
- else:
731
- accumulated_output += text
732
- raw_usage = chunk.get("usage")
733
- if isinstance(raw_usage, dict):
734
- usage.update(raw_usage)
735
- event = self._chunk_to_event(handle, run, chunk)
736
- if event is not None:
737
- yield event
738
- a2ui_surface = _a2ui_surface_event(chunk)
739
- if a2ui_surface is not None:
740
- event_type, payload = a2ui_surface
741
- yield self._event(handle, event_type, payload)
742
- finally:
743
- if accumulated_output:
744
- _set_conversation_output_attributes(span, accumulated_output)
745
- _set_conversation_usage_attributes(span, usage)
746
- if runner_gen is not None:
747
- aclose = getattr(runner_gen, "aclose", None)
748
- if callable(aclose):
749
- try:
750
- await aclose()
751
- except Exception: # noqa: BLE001
752
- pass
753
- if run is not None:
754
- run.cancellation_ack.set()
755
-
756
- def _chunk_to_event(
757
- self, handle: RunHandle, run: Optional[_ActiveRun], chunk: Any
758
- ) -> Optional[RuntimeEvent]:
759
- if isinstance(chunk, RuntimeEvent):
760
- # Outer adapter owns the public lifecycle envelope. A native
761
- # Runtime may emit its own RUN_STARTED with private identifiers;
762
- # suppress that duplicate and rebind all other canonical events
763
- # to the public handle without flattening their payloads.
764
- if chunk.event_type == EventType.RUN_STARTED:
765
- return None
766
- return RuntimeEvent.create(
767
- chunk.event_type,
768
- agent_id=str(handle.native_ref.get("agent_id") or "agent"),
769
- user_id=str(handle.native_ref.get("user_id") or "user"),
770
- session_id=handle.session_id,
771
- invocation_id=handle.run_id,
772
- seq_id=self._next_seq(),
773
- phase=chunk.phase,
774
- payload=dict(chunk.payload),
775
- event_id=chunk.event_id,
776
- timestamp=chunk.timestamp,
777
- )
778
- if not isinstance(chunk, dict):
779
- return self._event(
780
- handle, EventType.TEXT_DELTA, {"text": str(chunk)}, phase="commentary"
781
- )
782
- chunk_type = chunk.get("type")
783
- if chunk_type in ("reasoning", "reasoning_delta", "thinking"):
784
- text = self._coerce(
785
- chunk.get("delta")
786
- or chunk.get("content")
787
- or chunk.get("output")
788
- or chunk.get("data")
789
- )
790
- if not text:
791
- return None
792
- event_type = (
793
- EventType.REASONING_COMPLETED
794
- if chunk.get("status") in ("completed", "done")
795
- else EventType.REASONING_DELTA
796
- )
797
- return self._event(
798
- handle,
799
- event_type,
800
- {"text": text},
801
- phase="commentary",
802
- )
803
- if chunk_type in ("tool_call", "tool_start"):
804
- call_id = str(
805
- chunk.get("tool_call_id")
806
- or chunk.get("call_id")
807
- or chunk.get("run_id")
808
- or chunk.get("id")
809
- or ""
810
- )
811
- name = str(chunk.get("tool_name") or chunk.get("name") or "tool")
812
- return self._event(
813
- handle,
814
- EventType.TOOL_CALL_BEGIN,
815
- {
816
- "call_id": call_id or name,
817
- "name": name,
818
- "args": chunk.get("tool_args", chunk.get("args")),
819
- },
820
- )
821
- if chunk_type in ("tool_result", "tool_end"):
822
- call_id = str(
823
- chunk.get("tool_call_id")
824
- or chunk.get("call_id")
825
- or chunk.get("run_id")
826
- or chunk.get("id")
827
- or ""
828
- )
829
- name = str(chunk.get("tool_name") or chunk.get("name") or "tool")
830
- tool_args = chunk.get("tool_args", chunk.get("args"))
831
- result = chunk.get("tool_output", chunk.get("output"))
832
- approval_detail = approval_interrupt_info_from_result(
833
- result,
834
- fallback_tool_name=name,
835
- tool_args=tool_args,
836
- run_id=call_id or None,
837
- )
838
- if approval_detail is not None:
839
- return self._approval_requested_event(
840
- handle,
841
- run,
842
- detail=approval_detail,
843
- call_id=call_id,
844
- )
845
- return self._event(
846
- handle,
847
- EventType.TOOL_CALL_END,
848
- {
849
- "call_id": call_id or name,
850
- "name": name,
851
- "result": result,
852
- "error": chunk.get("error"),
853
- },
854
- )
855
- if chunk_type in ("interrupt", "approval", "approval_required"):
856
- raw_detail = chunk.get("interrupt_info") or chunk.get("detail") or {}
857
- detail = dict(raw_detail) if isinstance(raw_detail, Mapping) else {}
858
- call_id = str(
859
- chunk.get("call_id")
860
- or chunk.get("approval_id")
861
- or chunk.get("id")
862
- or ""
863
- )
864
- return self._approval_requested_event(
865
- handle,
866
- run,
867
- detail=detail,
868
- call_id=call_id,
869
- )
870
- if chunk_type == "checkpoint":
871
- raw_metadata = chunk.get("metadata")
872
- metadata: dict[str, Any] = raw_metadata if isinstance(raw_metadata, dict) else {}
873
- raw_agentengine = metadata.get("agentengine")
874
- agentengine: dict[str, Any] = (
875
- raw_agentengine if isinstance(raw_agentengine, dict) else {}
876
- )
877
- framework = str(agentengine.get("framework") or self._runtime_type)
878
- framework_ref = agentengine.get("framework_ref") or {}
879
- runtime_ref = (
880
- framework_ref.get(framework) if isinstance(framework_ref, dict) else {}
881
- ) or {}
882
- checkpoint_id = str(
883
- runtime_ref.get("checkpoint_id") if isinstance(runtime_ref, dict) else ""
884
- )
885
- if not checkpoint_id:
886
- return None
887
- handle.native_ref["checkpoint_id"] = checkpoint_id
888
- known_checkpoint_ids = handle.native_ref.setdefault("known_checkpoint_ids", [])
889
- if checkpoint_id not in known_checkpoint_ids:
890
- known_checkpoint_ids.append(checkpoint_id)
891
- handle.native_ref["framework_ref"] = framework_ref
892
- if isinstance(runtime_ref, dict):
893
- handle.native_ref.update(runtime_ref)
894
- return self._event(
895
- handle,
896
- EventType.CHECKPOINT_CREATED,
897
- {
898
- "checkpoint_id": checkpoint_id,
899
- "granularity": self._checkpoint_capability().granularity,
900
- "run_id": str(agentengine.get("run_id") or handle.run_id),
901
- "framework": framework,
902
- "framework_ref": framework_ref,
903
- "resume_target": framework_ref,
904
- },
905
- )
906
- if chunk_type == "graph_update":
907
- return self._event(
908
- handle,
909
- EventType.RUN_PROGRESS,
910
- {
911
- "status": "in_progress",
912
- "node": str(chunk.get("node") or ""),
913
- "state_update": self._coerce(chunk.get("output")),
914
- },
915
- )
916
- if chunk_type == "usage":
917
- raw_usage = chunk.get("usage")
918
- usage: dict[str, Any] = raw_usage if isinstance(raw_usage, dict) else {}
919
- return self._event(
920
- handle,
921
- EventType.USAGE_REPORTED,
922
- {
923
- "input_tokens": int(usage.get("input_tokens") or 0),
924
- "output_tokens": int(usage.get("output_tokens") or 0),
925
- "total_tokens": int(usage.get("total_tokens") or 0),
926
- "cached_tokens": int(usage.get("cached_tokens") or 0),
927
- "reasoning_tokens": int(usage.get("reasoning_tokens") or 0),
928
- "source": str(usage.get("source") or self._runtime_type),
929
- },
930
- )
931
- if chunk_type == "error":
932
- error = self._coerce(chunk.get("message") or chunk.get("error"))
933
- return self._event(
934
- handle,
935
- EventType.RUN_FAILED,
936
- {
937
- "status": "failed",
938
- "error": error or "runner failed",
939
- },
940
- )
941
- if chunk_type == "final":
942
- return self._event(
943
- handle,
944
- EventType.TEXT_COMPLETED,
945
- {"text": self._coerce(chunk.get("output"))},
946
- phase="final_answer",
947
- )
948
- text = self._coerce(chunk.get("delta") or chunk.get("output") or chunk.get("data"))
949
- if not text:
950
- return None
951
- payload: dict[str, Any] = {"text": text}
952
- if chunk.get("replace"):
953
- payload["replace"] = True
954
- return self._event(handle, EventType.TEXT_DELTA, payload, phase="commentary")
955
-
956
- def _approval_requested_event(
616
+ # ---- canonical event construction helpers ----
617
+
618
+ def _interaction_requested_from_approval(
957
619
  self,
958
620
  handle: RunHandle,
959
621
  run: Optional[_ActiveRun],
960
622
  *,
961
623
  detail: Mapping[str, Any],
962
624
  call_id: str,
963
- ) -> RuntimeEvent:
964
- """Convert one framework/tool approval to the canonical runtime event."""
625
+ ) -> list[RuntimeEvent]:
626
+ """把 ToolGateway 结果中携带的审批请求转为 canonical InteractionRequested。"""
965
627
 
966
- approval_id = str(
967
- detail.get("approval_request_id") or detail.get("id") or call_id or ""
968
- )
628
+ approval_id = str(detail.get("approval_request_id") or detail.get("id") or call_id or "")
969
629
  resolved_call_id = str(call_id or approval_id)
970
630
  if run is not None and approval_id:
971
631
  run.pending_approvals.add(approval_id)
@@ -973,52 +633,172 @@ class RunnerRuntimeAdapter(RuntimeAdapter):
973
633
  pending_approval_ids = handle.native_ref.setdefault("pending_approval_ids", [])
974
634
  if approval_id not in pending_approval_ids:
975
635
  pending_approval_ids.append(approval_id)
976
- return self._event(
977
- handle,
978
- EventType.APPROVAL_REQUESTED,
979
- {
980
- "approval_id": approval_id,
981
- "call_id": resolved_call_id,
982
- "kind": "tool",
983
- "detail": dict(detail),
636
+ framework = self._runtime_type
637
+ run_id = handle.run_id
638
+ scope_id = stable_scope_id(framework, run_id)
639
+ interaction_id = resolved_call_id or stable_item_id(framework, run_id, "interaction")
640
+ item_id = stable_item_id(framework, run_id, "interaction")
641
+ detail_value: JsonValue = (
642
+ cast(JsonValue, dict(detail)) if isinstance(detail, Mapping) else None
643
+ )
644
+ return [
645
+ InteractionRequested(
646
+ **self._canonical_kwargs(
647
+ handle,
648
+ scope_id=scope_id,
649
+ item_id=item_id,
650
+ event_type="interaction.requested",
651
+ part_id="interaction",
652
+ ),
653
+ interaction_id=interaction_id,
654
+ interaction_kind="approval",
655
+ request=ApprovalRequest(
656
+ call_id=resolved_call_id or None,
657
+ kind="tool",
658
+ detail=detail_value,
659
+ ),
660
+ )
661
+ ]
662
+
663
+ def _make_source(self, handle: RunHandle, *, protocol: str | None = None) -> SourceRef:
664
+ # SourceRef.framework 是封闭枚举;测试 fixture 或自定义 runtime_type 落到
665
+ # 通用 "ksadk",原生框架名原样保留。
666
+ framework = self._runtime_type
667
+ if framework not in {"adk", "langgraph", "codex", "a2a", "ksadk"}:
668
+ framework = "ksadk"
669
+ return SourceRef(
670
+ framework=framework,
671
+ protocol=protocol,
672
+ native_run_id=handle.run_id,
673
+ metadata={
674
+ "agent_id": str(handle.native_ref.get("agent_id") or "agent"),
675
+ "user_id": str(handle.native_ref.get("user_id") or "user"),
676
+ "session_id": handle.session_id,
677
+ "invocation_id": handle.run_id,
984
678
  },
985
679
  )
986
680
 
987
- def _event(
681
+ def _canonical_kwargs(
988
682
  self,
989
683
  handle: RunHandle,
990
- event_type: str,
991
- payload: dict,
992
684
  *,
993
- phase: Optional[str] = None,
994
- ) -> RuntimeEvent:
995
- return RuntimeEvent.create(
996
- event_type,
997
- agent_id=str(handle.native_ref.get("agent_id") or "agent"),
998
- user_id=str(handle.native_ref.get("user_id") or "user"),
999
- session_id=handle.session_id,
1000
- invocation_id=handle.run_id,
1001
- seq_id=self._next_seq(),
1002
- phase=phase,
1003
- payload=payload,
685
+ scope_id: str,
686
+ item_id: str,
687
+ event_type: str,
688
+ part_id: str,
689
+ ) -> dict[str, Any]:
690
+ """Build common EventEnvelope kwargs for the dict-chunk degraded path."""
691
+ framework = self._runtime_type
692
+ run_id = handle.run_id
693
+ # TODO(runtime-event-v2): dict chunk 退化路径,chunk_ordinal 用 seq counter;
694
+ # LangGraph/Codex 切 stream_canonical_events 后清理
695
+ n = self._next_seq()
696
+ return {
697
+ "schema_version": 2,
698
+ "event_id": stable_event_id(
699
+ framework, scope_id, item_id, event_type, part_id, run_id, n
700
+ ),
701
+ "seq": n,
702
+ "timestamp": time.time(),
703
+ "run_id": run_id,
704
+ "scope_id": scope_id,
705
+ "source": self._make_source(handle),
706
+ }
707
+
708
+ def _make_run_started(self, handle: RunHandle) -> RunStarted:
709
+ framework = self._runtime_type
710
+ run_id = handle.run_id
711
+ scope_id = stable_scope_id(framework, run_id)
712
+ item_id = stable_item_id(framework, run_id, "$run")
713
+ return RunStarted(
714
+ schema_version=2,
715
+ event_id=stable_event_id(framework, scope_id, item_id, "run.started", "run", run_id, 0),
716
+ seq=self._next_seq(),
717
+ timestamp=time.time(),
718
+ run_id=run_id,
719
+ scope_id=scope_id,
720
+ source=self._make_source(handle),
721
+ status="running",
1004
722
  )
1005
723
 
1006
- def _completion_payload(
724
+ def _make_run_completed(
1007
725
  self,
1008
726
  handle: RunHandle,
1009
727
  *,
1010
- status: str,
728
+ run: Optional[_ActiveRun] = None,
1011
729
  metrics: Mapping[str, Any] | None = None,
1012
- ) -> dict[str, Any]:
1013
- payload: dict[str, Any] = {"status": status, **dict(metrics or {})}
1014
- framework_ref = handle.native_ref.get("framework_ref")
1015
- if isinstance(framework_ref, Mapping) and framework_ref:
1016
- payload["agentengine"] = {
1017
- "run_id": handle.run_id,
1018
- "framework": self._runtime_type,
1019
- "framework_ref": dict(framework_ref),
1020
- }
1021
- return payload
730
+ ) -> RunCompleted:
731
+ framework = self._runtime_type
732
+ run_id = handle.run_id
733
+ scope_id = stable_scope_id(framework, run_id)
734
+ item_id = stable_item_id(framework, run_id, "$run")
735
+ output_refs: tuple[OutputRef, ...] = ()
736
+ if run is not None and run.final_answer_item_id:
737
+ output_refs = (
738
+ OutputRef(
739
+ scope_id=scope_id,
740
+ item_id=run.final_answer_item_id,
741
+ part_id="text-0",
742
+ ),
743
+ )
744
+ source = self._make_source(handle)
745
+ if metrics:
746
+ source = source.model_copy(
747
+ update={"metadata": {**source.metadata, "metrics": dict(metrics)}}
748
+ )
749
+ return RunCompleted(
750
+ schema_version=2,
751
+ event_id=stable_event_id(
752
+ framework, scope_id, item_id, "run.completed", "run", run_id, 0
753
+ ),
754
+ seq=self._next_seq(),
755
+ timestamp=time.time(),
756
+ run_id=run_id,
757
+ scope_id=scope_id,
758
+ source=source,
759
+ status="completed",
760
+ output_refs=output_refs,
761
+ )
762
+
763
+ def _make_run_canceled(self, handle: RunHandle, *, reason: str | None = None) -> RunCanceled:
764
+ framework = self._runtime_type
765
+ run_id = handle.run_id
766
+ scope_id = stable_scope_id(framework, run_id)
767
+ item_id = stable_item_id(framework, run_id, "$run")
768
+ return RunCanceled(
769
+ schema_version=2,
770
+ event_id=stable_event_id(
771
+ framework, scope_id, item_id, "run.canceled", "run", run_id, 0
772
+ ),
773
+ seq=self._next_seq(),
774
+ timestamp=time.time(),
775
+ run_id=run_id,
776
+ scope_id=scope_id,
777
+ source=self._make_source(handle),
778
+ status="canceled",
779
+ reason=reason,
780
+ )
781
+
782
+ def _make_run_interrupted(
783
+ self, handle: RunHandle, *, reason: str | None = None
784
+ ) -> RunInterrupted:
785
+ framework = self._runtime_type
786
+ run_id = handle.run_id
787
+ scope_id = stable_scope_id(framework, run_id)
788
+ item_id = stable_item_id(framework, run_id, "$run")
789
+ return RunInterrupted(
790
+ schema_version=2,
791
+ event_id=stable_event_id(
792
+ framework, scope_id, item_id, "run.interrupted", "run", run_id, 0
793
+ ),
794
+ seq=self._next_seq(),
795
+ timestamp=time.time(),
796
+ run_id=run_id,
797
+ scope_id=scope_id,
798
+ source=self._make_source(handle),
799
+ status="interrupted",
800
+ reason=reason,
801
+ )
1022
802
 
1023
803
  @staticmethod
1024
804
  def _coerce(value: Any) -> str: