agentengine-sdk-python 0.8.0__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 (494) hide show
  1. {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/METADATA +43 -7
  2. agentengine_sdk_python-0.8.2.dist-info/RECORD +778 -0
  3. {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/WHEEL +1 -1
  4. ksadk/a2a/__init__.py +2 -0
  5. ksadk/a2a/_space_client_events.py +381 -0
  6. ksadk/a2a/bootstrap.py +0 -2
  7. ksadk/a2a/card.py +6 -111
  8. ksadk/a2a/control_plane.py +239 -1
  9. ksadk/a2a/event_adapter.py +156 -64
  10. ksadk/a2a/executor.py +195 -172
  11. ksadk/a2a/routes.py +12 -5
  12. ksadk/a2a/server.py +1 -8
  13. ksadk/a2a/service_env.py +33 -0
  14. ksadk/a2a/space_client.py +43 -349
  15. ksadk/a2a_card.py +101 -0
  16. ksadk/a2ui/core.py +222 -53
  17. ksadk/agui/_agent_helpers.py +243 -0
  18. ksadk/agui/a2ui_projection.py +12 -5
  19. ksadk/agui/agent.py +474 -275
  20. ksadk/agui/config.py +11 -5
  21. ksadk/agui/routes.py +7 -15
  22. ksadk/api/client.py +530 -40
  23. ksadk/api/environment.py +27 -0
  24. ksadk/builders/code_builder.py +352 -53
  25. ksadk/builders/container_builder.py +71 -44
  26. ksadk/builders/framework_requirements.py +2 -1
  27. ksadk/builders/ks3_uploader.py +2 -2
  28. ksadk/builders/managed_runtime_builder.py +60 -28
  29. ksadk/cli/__init__.py +25 -0
  30. ksadk/cli/cmd_a2a.py +18 -31
  31. ksadk/cli/cmd_create.py +13 -26
  32. ksadk/cli/cmd_dashboard.py +15 -1
  33. ksadk/cli/cmd_deploy.py +28 -3
  34. ksadk/cli/cmd_eval.py +443 -0
  35. ksadk/cli/cmd_evalset.py +284 -0
  36. ksadk/cli/cmd_files.py +5 -1
  37. ksadk/cli/cmd_hermes.py +137 -163
  38. ksadk/cli/cmd_invoke.py +28 -38
  39. ksadk/cli/cmd_launch.py +12 -0
  40. ksadk/cli/cmd_managed_runtime.py +95 -0
  41. ksadk/cli/cmd_observe.py +85 -0
  42. ksadk/cli/cmd_openclaw.py +85 -479
  43. ksadk/cli/cmd_replay.py +28 -4
  44. ksadk/cli/cmd_run.py +29 -67
  45. ksadk/cli/cmd_studio.py +201 -0
  46. ksadk/cli/cmd_web.py +15 -6
  47. ksadk/cli/dry_run.py +2 -1
  48. ksadk/cli/env_options.py +83 -0
  49. ksadk/cli/global_options.py +3 -1
  50. ksadk/cli/hermes_env.py +151 -0
  51. ksadk/cli/invoke_payload.py +49 -0
  52. ksadk/cli/network_options.py +7 -5
  53. ksadk/cli/openclaw_env.py +458 -0
  54. ksadk/cli/runtime_bootstrap.py +51 -0
  55. ksadk/cli/ui.py +6 -0
  56. ksadk/cli/workflow_common.py +5 -5
  57. ksadk/codex/__init__.py +3 -3
  58. ksadk/codex/client.py +833 -49
  59. ksadk/codex/phase.py +1 -1
  60. ksadk/codex/runtime.py +720 -127
  61. ksadk/common/kop_client.py +232 -0
  62. ksadk/configs/__init__.py +1 -3
  63. ksadk/configs/env_registry.py +244 -45
  64. ksadk/configs/env_registry_pcm.py +181 -0
  65. ksadk/configs/env_var_spec.py +18 -0
  66. ksadk/configs/global_config.py +6 -1
  67. ksadk/configs/settings.py +5 -108
  68. ksadk/context_engine/__init__.py +98 -0
  69. ksadk/context_engine/assembler.py +176 -0
  70. ksadk/context_engine/baseline.py +419 -0
  71. ksadk/context_engine/cache_observability.py +228 -0
  72. ksadk/context_engine/capabilities.py +443 -0
  73. ksadk/context_engine/contributors.py +344 -0
  74. ksadk/context_engine/hosted_pipeline.py +417 -0
  75. ksadk/context_engine/models.py +117 -0
  76. ksadk/context_engine/planner.py +698 -0
  77. ksadk/context_engine/policies.py +319 -0
  78. ksadk/context_engine/projection.py +31 -0
  79. ksadk/context_engine/shadow_plan.py +266 -0
  80. ksadk/context_engine/tokenizer.py +164 -0
  81. ksadk/conversations/__init__.py +85 -94
  82. ksadk/conversations/context.py +126 -6
  83. ksadk/conversations/message_projection.py +292 -28
  84. ksadk/conversations/model_context.py +49 -3
  85. ksadk/conversations/runtime_compaction.py +207 -5
  86. ksadk/conversations/runtime_input.py +193 -3
  87. ksadk/conversations/runtime_invocation.py +81 -3
  88. ksadk/conversations/runtime_observability.py +157 -4
  89. ksadk/conversations/runtime_payloads.py +42 -0
  90. ksadk/conversations/runtime_preparation.py +280 -2
  91. ksadk/conversations/runtime_resume.py +71 -32
  92. ksadk/conversations/runtime_stream_events.py +94 -9
  93. ksadk/conversations/runtime_streaming.py +175 -22
  94. ksadk/conversations/semantic_summary.py +311 -1
  95. ksadk/conversations/session_lock.py +51 -0
  96. ksadk/deployment/env_forward.py +72 -0
  97. ksadk/deployment/managed_runtime.py +38 -0
  98. ksadk/deployment/manager.py +24 -8
  99. ksadk/deployment/providers/serverless.py +228 -92
  100. ksadk/evaluation/__init__.py +116 -0
  101. ksadk/evaluation/a2a_adapter.py +452 -0
  102. ksadk/evaluation/adapters.py +67 -0
  103. ksadk/evaluation/agent_eval_client.py +395 -0
  104. ksadk/evaluation/cloud_binding.py +80 -0
  105. ksadk/evaluation/cloud_converter.py +194 -0
  106. ksadk/evaluation/cloud_service.py +199 -0
  107. ksadk/evaluation/contracts.py +478 -0
  108. ksadk/evaluation/evalset.py +351 -0
  109. ksadk/evaluation/evaluators.py +627 -0
  110. ksadk/evaluation/evidence.py +258 -0
  111. ksadk/evaluation/executor.py +143 -0
  112. ksadk/evaluation/local_adapter.py +550 -0
  113. ksadk/evaluation/service_env.py +27 -0
  114. ksadk/evaluation/storage.py +68 -0
  115. ksadk/evaluation/studio_build_adapter.py +253 -0
  116. ksadk/evaluation/target.py +82 -0
  117. ksadk/events/__init__.py +13 -13
  118. ksadk/events/_v1_compat/__init__.py +1 -0
  119. ksadk/events/_v1_compat/models.py +299 -0
  120. ksadk/events/_v1_compat/parser.py +247 -0
  121. ksadk/events/_v1_compat/projection.py +753 -0
  122. ksadk/events/adapters/__init__.py +34 -0
  123. ksadk/events/adapters/_a2a_snapshot.py +622 -0
  124. ksadk/events/adapters/_a2a_support.py +275 -0
  125. ksadk/events/adapters/_codex_interactions.py +375 -0
  126. ksadk/events/adapters/_codex_items.py +801 -0
  127. ksadk/events/adapters/_codex_validators.py +311 -0
  128. ksadk/events/adapters/_langgraph_support.py +785 -0
  129. ksadk/events/adapters/a2a.py +841 -0
  130. ksadk/events/adapters/adk.py +503 -0
  131. ksadk/events/adapters/codex.py +717 -0
  132. ksadk/events/adapters/langgraph.py +745 -0
  133. ksadk/events/canonical.py +440 -0
  134. ksadk/events/canonical_replay.py +497 -0
  135. ksadk/events/canonical_store.py +468 -0
  136. ksadk/events/cold_recovery.py +257 -0
  137. ksadk/events/content.py +89 -0
  138. ksadk/events/identity.py +86 -0
  139. ksadk/events/pipeline.py +455 -0
  140. ksadk/events/projections.py +66 -0
  141. ksadk/events/reducer.py +661 -0
  142. ksadk/events/replay.py +3 -36
  143. ksadk/events/runtime_event.py +20 -256
  144. ksadk/events/session_event.py +324 -0
  145. ksadk/events/store.py +3 -316
  146. ksadk/events/v1_compat.py +41 -0
  147. ksadk/harness/__init__.py +3 -2
  148. ksadk/harness/app.py +114 -123
  149. ksadk/harness/config.py +1 -1
  150. ksadk/harness/runtime.py +467 -0
  151. ksadk/hermes_terminal.py +5 -0
  152. ksadk/identity/resolver.py +4 -3
  153. ksadk/interaction/__init__.py +28 -0
  154. ksadk/interaction/contracts.py +136 -0
  155. ksadk/interaction/ledger.py +189 -0
  156. ksadk/interaction/provider.py +122 -0
  157. ksadk/interaction/providers/__init__.py +52 -0
  158. ksadk/interaction/providers/adk.py +40 -0
  159. ksadk/interaction/providers/codex.py +94 -0
  160. ksadk/interaction/providers/langgraph.py +58 -0
  161. ksadk/kernel/__init__.py +117 -0
  162. ksadk/kernel/authorization.py +258 -0
  163. ksadk/kernel/bootstrap.py +1074 -0
  164. ksadk/kernel/contract_fingerprints.py +61 -0
  165. ksadk/kernel/contracts.py +362 -0
  166. ksadk/kernel/control.py +219 -0
  167. ksadk/kernel/errors.py +79 -0
  168. ksadk/kernel/ingress.py +1102 -0
  169. ksadk/kernel/mapping.py +111 -0
  170. ksadk/kernel/memory_store.py +1133 -0
  171. ksadk/kernel/postgres_store.py +1756 -0
  172. ksadk/kernel/recovery.py +452 -0
  173. ksadk/kernel/runtime_identity.py +103 -0
  174. ksadk/kernel/sql/001_agent_kernel.sql +120 -0
  175. ksadk/kernel/sqlite_store.py +1410 -0
  176. ksadk/kernel/state.py +145 -0
  177. ksadk/kernel/store.py +232 -0
  178. ksadk/kernel/worker.py +953 -0
  179. ksadk/knowledge_base/client.py +22 -4
  180. ksadk/knowledge_base/service.py +32 -5
  181. ksadk/managed_a2a_card.py +160 -0
  182. ksadk/managed_runtime.py +16 -9
  183. ksadk/memory/__init__.py +61 -0
  184. ksadk/memory/adk/backends/base_ltm_backend.py +96 -1
  185. ksadk/memory/adk/backends/http_ltm_backend.py +14 -5
  186. ksadk/memory/adk/backends/inmemory_ltm_backend.py +136 -3
  187. ksadk/memory/adk/backends/sdk_ltm_backend.py +394 -5
  188. ksadk/memory/adk/backends/sqlite_ltm_backend.py +95 -0
  189. ksadk/memory/adk/long_term_memory.py +3 -2
  190. ksadk/memory/coordinator.py +461 -0
  191. ksadk/memory/events.py +202 -0
  192. ksadk/memory/extraction.py +228 -0
  193. ksadk/memory/ltm_backend_factory.py +16 -3
  194. ksadk/memory/models.py +316 -0
  195. ksadk/memory/policy.py +215 -0
  196. ksadk/memory/provider.py +47 -0
  197. ksadk/memory/provider_adapter.py +159 -0
  198. ksadk/memory/provider_resolver.py +71 -0
  199. ksadk/memory/providers/__init__.py +5 -0
  200. ksadk/memory/providers/local_sqlite.py +490 -0
  201. ksadk/memory/resolved_policy.py +145 -0
  202. ksadk/memory/service.py +198 -8
  203. ksadk/memory/tool.py +4 -0
  204. ksadk/model_proxy/bootstrap.py +16 -9
  205. ksadk/model_proxy/config.py +22 -1
  206. ksadk/model_proxy/detect.py +202 -9
  207. ksadk/model_proxy/namespace.py +67 -15
  208. ksadk/model_proxy/server.py +168 -6
  209. ksadk/model_proxy/transform.py +105 -3
  210. ksadk/observability/__init__.py +25 -0
  211. ksadk/observability/session_log.py +420 -0
  212. ksadk/observability/trajectory.py +105 -0
  213. ksadk/prompts/__init__.py +71 -0
  214. ksadk/prompts/compiler.py +215 -0
  215. ksadk/prompts/models.py +82 -0
  216. ksadk/prompts/projection.py +89 -0
  217. ksadk/prompts/resolved.py +137 -0
  218. ksadk/prompts/sources.py +233 -0
  219. ksadk/runners/_langgraph_runner_streams.py +822 -0
  220. ksadk/runners/adk_runner.py +14 -31
  221. ksadk/runners/base_runner.py +44 -3
  222. ksadk/runners/factory.py +3 -3
  223. ksadk/runners/langgraph_runner.py +252 -433
  224. ksadk/runners/utils/__init__.py +0 -10
  225. ksadk/runners/utils/loader.py +16 -0
  226. ksadk/runtime/__init__.py +67 -41
  227. ksadk/runtime/_runner_adapter/__init__.py +1 -0
  228. ksadk/runtime/_runner_adapter/stream_mapping.py +788 -0
  229. ksadk/runtime/adapter.py +199 -16
  230. ksadk/runtime/conversation_execution.py +687 -0
  231. ksadk/runtime/executor.py +392 -0
  232. ksadk/runtime/factory.py +342 -0
  233. ksadk/runtime/framework_adapters.py +6 -12
  234. ksadk/runtime/hosted_finalizer.py +268 -0
  235. ksadk/runtime/launch.py +61 -0
  236. ksadk/runtime/preprocessing.py +49 -14
  237. ksadk/runtime/runner_adapter.py +330 -391
  238. ksadk/runtime/runner_loading.py +36 -0
  239. ksadk/runtime/usage.py +33 -0
  240. ksadk/sandbox/backends/e2b.py +36 -3
  241. ksadk/server/__init__.py +39 -4
  242. ksadk/server/app.py +18 -492
  243. ksadk/server/composition.py +36 -2
  244. ksadk/server/factory.py +210 -149
  245. ksadk/server/routes/common.py +15 -46
  246. ksadk/server/routes/control.py +13 -9
  247. ksadk/server/routes/kernel_ingress.py +258 -0
  248. ksadk/server/routes/misc.py +10 -33
  249. ksadk/server/routes/models.py +14 -6
  250. ksadk/server/routes/openai_compat.py +129 -27
  251. ksadk/server/routes/projection.py +84 -16
  252. ksadk/server/routes/run.py +347 -460
  253. ksadk/server/routes/sessions.py +37 -40
  254. ksadk/server/routes/streaming.py +52 -7
  255. ksadk/server/routes/workspace.py +54 -21
  256. ksadk/server/static/assets/{ArtifactsPanel-Cjnqt-8W.js → ArtifactsPanel-DSU1sWD_.js} +1 -1
  257. ksadk/server/static/assets/{CodeBlock-BqyqZfuB.js → CodeBlock-DOH6MVcn.js} +4 -4
  258. ksadk/server/static/assets/{MathMessageMarkdown-BpH7jiFM.js → MathMessageMarkdown-BL8my1R2.js} +4 -4
  259. ksadk/server/static/assets/MathMessageMarkdown-BorAY7qD.css +1 -0
  260. ksadk/server/static/assets/MermaidBlock-Dz4IP-Tx.js +312 -0
  261. ksadk/server/static/assets/NativeTerminalPanel-DUrK0JpZ.js +1 -0
  262. ksadk/server/static/assets/abnfDiagram-VCTEODGH-g20pFzNV.js +1 -0
  263. ksadk/server/static/assets/arc-C4FzinUA.js +1 -0
  264. ksadk/server/static/assets/architecture-7GRP2DOG-DjKratdf.js +1 -0
  265. ksadk/server/static/assets/architectureDiagram-5GKGNRK7-DpIqL5h4.js +36 -0
  266. ksadk/server/static/assets/blockDiagram-NRAW4CY4-BjkljTNz.js +129 -0
  267. ksadk/server/static/assets/c4Diagram-UCG6FXSJ-Dk_ieq2X.js +38 -0
  268. ksadk/server/static/assets/channel-4cQHKtx1.js +1 -0
  269. ksadk/server/static/assets/chunk-2Q5K7J3B-DmgWkESh.js +1 -0
  270. ksadk/server/static/assets/{chunk-FMBD7UC4-BGhvzDYt.js → chunk-5VM5RSS4-BVHAchFb.js} +1 -1
  271. ksadk/server/static/assets/{chunk-ND2GUHAM-D98vPd-E.js → chunk-F27PBJKO-C-ipQzuS.js} +1 -1
  272. ksadk/server/static/assets/chunk-G27WJ6UU-C2EyJbIK.js +231 -0
  273. ksadk/server/static/assets/{chunk-4BX2VUAB-CsWIQRhc.js → chunk-JWPE2WC7-vYvVJb_M.js} +1 -1
  274. ksadk/server/static/assets/chunk-LCL6LL3I-BcI6ysPT.js +206 -0
  275. ksadk/server/static/assets/chunk-POPQ4Y6H-C030x_Z1.js +1 -0
  276. ksadk/server/static/assets/chunk-RHFEMEQ7-yyGpsz4n.js +168 -0
  277. ksadk/server/static/assets/chunk-SVP7TREG-BLTlmMU7.js +88 -0
  278. ksadk/server/static/assets/chunk-XXDRQBXY-C_32ArgP.js +1 -0
  279. ksadk/server/static/assets/classDiagram-DTDB5LWJ-DLFd9Xi0.js +1 -0
  280. ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-DLFd9Xi0.js +1 -0
  281. ksadk/server/static/assets/{cose-bilkent-S5V4N54A-CX3yotHq.js → cose-bilkent-JH36ORCC-9YQYwdl0.js} +1 -1
  282. ksadk/server/static/assets/cynefin-OW5HDTMX-DjEM48Qp.js +1 -0
  283. ksadk/server/static/assets/cynefinDiagram-5FMLGOSQ-ErLF5N13.js +62 -0
  284. ksadk/server/static/assets/cytoscape.esm-CyCl8rPi.js +321 -0
  285. ksadk/server/static/assets/dagre-3AP2YEHR-DFiQF-6f.js +4 -0
  286. ksadk/server/static/assets/dagre-BuhGnRI1.js +1 -0
  287. ksadk/server/static/assets/diagram-S7CK7UJ4-Bt1v8-GC.js +30 -0
  288. ksadk/server/static/assets/diagram-UQ7AKVKN-DSCxJdBK.js +41 -0
  289. ksadk/server/static/assets/diagram-VSXAHHWV-DHfYwp_9.js +3 -0
  290. ksadk/server/static/assets/diagram-VX7I27RA-DdzD-4Le.js +24 -0
  291. ksadk/server/static/assets/diagram-Z3DM3KII-Du-nAJ9F.js +24 -0
  292. ksadk/server/static/assets/{dist-D82mtD2s.js → dist-8hqcx0sU.js} +1 -1
  293. ksadk/server/static/assets/dist-B7seoj3d.js +1 -0
  294. ksadk/server/static/assets/{dist-DHn6nBzr.js → dist-BE7bu-DL.js} +1 -1
  295. ksadk/server/static/assets/{dist-eoW6tZXA.js → dist-BVDffZ0U.js} +1 -1
  296. ksadk/server/static/assets/{dist-C2xjE7cO.js → dist-BX8z72IC.js} +1 -1
  297. ksadk/server/static/assets/{dist-CgF3fj15.js → dist-Bf2M8m3N.js} +1 -1
  298. ksadk/server/static/assets/{dist-Bs-jQcRX.js → dist-BjU6y-A2.js} +1 -1
  299. ksadk/server/static/assets/{dist-Djgl2wVZ.js → dist-BytlxIDT.js} +1 -1
  300. ksadk/server/static/assets/{dist-CVQRZjPu.js → dist-CSh_DE14.js} +1 -1
  301. ksadk/server/static/assets/{dist-6CVKuTAe.js → dist-CXYYBw3_.js} +1 -1
  302. ksadk/server/static/assets/{dist-DlzCRy0S.js → dist-C_wsv-Qd.js} +1 -1
  303. ksadk/server/static/assets/{dist-DF06ORQo.js → dist-ClxGviKw.js} +1 -1
  304. ksadk/server/static/assets/{dist-Cf23NY4L.js → dist-CttYUqzS.js} +1 -1
  305. ksadk/server/static/assets/{dist-ivOya1LD.js → dist-DNp6rLRA.js} +1 -1
  306. ksadk/server/static/assets/{dist-BibcG1V9.js → dist-DUX-id_F.js} +1 -1
  307. ksadk/server/static/assets/{dist-BUHRxvDG.js → dist-DmldrHd_.js} +1 -1
  308. ksadk/server/static/assets/{dist-BZ9n9AjO.js → dist-DnfXs8Vn.js} +2 -2
  309. ksadk/server/static/assets/{dist-DqNnNFrw.js → dist-Dq9gLD7L.js} +1 -1
  310. ksadk/server/static/assets/{dist-CGcp2ex8.js → dist-W-TIVntx.js} +1 -1
  311. ksadk/server/static/assets/{dist-Cwt7koer.js → dist-dDdADKFA.js} +1 -1
  312. ksadk/server/static/assets/{dist-C7-3jInC.js → dist-dydu68Fl.js} +1 -1
  313. ksadk/server/static/assets/{dist-C99THtIA.js → dist-ngtN0DJB.js} +1 -1
  314. ksadk/server/static/assets/ebnfDiagram-PWID7BFC-Da9C9NO1.js +1 -0
  315. ksadk/server/static/assets/erDiagram-SSCWMZ5O-EAGqqR79.js +99 -0
  316. ksadk/server/static/assets/eventmodeling-NTZA5JFV-CMgMaJrC.js +1 -0
  317. ksadk/server/static/assets/flowDiagram-A5DVABFB-BBKL0x3P.js +1 -0
  318. ksadk/server/static/assets/ganttDiagram-EL5Y4UJY-DqJsKb59.js +292 -0
  319. ksadk/server/static/assets/gitGraph-4MIJSDKK-Dw63mrhw.js +1 -0
  320. ksadk/server/static/assets/gitGraphDiagram-WWUBYQGX-B6g4dtDi.js +106 -0
  321. ksadk/server/static/assets/index-8ipRcQ-M.js +240 -0
  322. ksadk/server/static/assets/index-ByFCqjlh.css +2 -0
  323. ksadk/server/static/assets/info-A6RAGUB7-B6iAblII.js +1 -0
  324. ksadk/server/static/assets/infoDiagram-RXCK75RN-Djm_nbd5.js +2 -0
  325. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-DLOHGvWw.js → ishikawaDiagram-5VMMS53U-DWya9SG2.js} +6 -6
  326. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-BIzKeejT.js → journeyDiagram-EYS64GPL-DutxhSNI.js} +6 -6
  327. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-BWHoFSw2.js → kanban-definition-3QL26DDD-DJdOF8Fm.js} +10 -10
  328. ksadk/server/static/assets/katex-vFWytM5c.js +257 -0
  329. ksadk/server/static/assets/{linear-CvMBw9cA.js → linear-BHjG8b-J.js} +1 -1
  330. ksadk/server/static/assets/mermaid-parser.core-KGSy4jWT.js +166 -0
  331. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-CSAQxEAe.js → mindmap-definition-FBJOCRG2-9-HC88D4.js} +29 -29
  332. ksadk/server/static/assets/packet-AYTQ26CC-BOYAipuq.js +1 -0
  333. ksadk/server/static/assets/pegDiagram-XKGWAZYB-DrEZd4fD.js +1 -0
  334. ksadk/server/static/assets/pie-WAS4IAKB-Ctq0Kbku.js +1 -0
  335. ksadk/server/static/assets/pieDiagram-E7YTZNPT-DpjqOFFp.js +39 -0
  336. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-CxIR4JXI.js → quadrantDiagram-AXDQQJYC-BiNhgOXX.js} +6 -6
  337. ksadk/server/static/assets/radar-RG4KPBEZ-DN0-ul_0.js +1 -0
  338. ksadk/server/static/assets/railroad-74A4TZTK-C6cC_to0.js +1 -0
  339. ksadk/server/static/assets/railroad-abnf-HS5TGJTU-BimjIuRc.js +1 -0
  340. ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-CvY65ABq.js +1 -0
  341. ksadk/server/static/assets/railroad-peg-WCYAUIDC-D72Iq_Tv.js +1 -0
  342. ksadk/server/static/assets/railroadDiagram-O6MQD6OU-_Y3Ojg6G.js +1 -0
  343. ksadk/server/static/assets/requirementDiagram-EFPCY7ZU-DeCFdg9K.js +84 -0
  344. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-DpGa6EAV.js → sankeyDiagram-P5KCCOFB-CPH75rhw.js} +7 -7
  345. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-NTKNGGe5.js → sequenceDiagram-WJ2MYXX4-OEpQQdr4.js} +10 -10
  346. ksadk/server/static/assets/sizeCapture-X5ZJPWSS-heUErzhI.js +1 -0
  347. ksadk/server/static/assets/stateDiagram-HBIQ2CUA-DvA3jSMB.js +1 -0
  348. ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BgQY03nz.js +1 -0
  349. ksadk/server/static/assets/swimlanes-XN3QIQJK-CR-dfN_t.js +1 -0
  350. ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-DLVw3q5Q.js +8 -0
  351. ksadk/server/static/assets/{timeline-definition-PNZ67QCA-jqoYs3jQ.js → timeline-definition-24CTP7MA-DAVJf1TX.js} +7 -7
  352. ksadk/server/static/assets/treeView-Q6P3EWNA-BMpO0wsB.js +1 -0
  353. ksadk/server/static/assets/treemap-WGGIJYW6-Et0aqzU3.js +1 -0
  354. ksadk/server/static/assets/vennDiagram-4TSXK5OY-DwM8eDkh.js +34 -0
  355. ksadk/server/static/assets/wardley-WFR3VGLG-CbY0mhpl.js +1 -0
  356. ksadk/server/static/assets/wardleyDiagram-VM6X3IG4-DZL-Zz2t.js +78 -0
  357. ksadk/server/static/assets/xychartDiagram-S5SC5T6Z-Dy6Yh_hu.js +7 -0
  358. ksadk/server/static/index.html +2 -2
  359. ksadk/sessions/__init__.py +26 -9
  360. ksadk/sessions/_local_service_sync.py +688 -0
  361. ksadk/sessions/_local_tables.py +21 -0
  362. ksadk/sessions/_postgres_schema.py +305 -0
  363. ksadk/sessions/_postgres_tables.py +15 -0
  364. ksadk/sessions/base.py +80 -1
  365. ksadk/sessions/in_memory.py +53 -2
  366. ksadk/sessions/local_service.py +63 -604
  367. ksadk/sessions/postgres_service.py +58 -148
  368. ksadk/sessions/resilient.py +35 -1
  369. ksadk/skills/runtime/backends/e2b.py +1 -1
  370. ksadk/skills/service_client.py +13 -3
  371. ksadk/studio/__init__.py +5 -0
  372. ksadk/studio/agent_avatar_assets.py +120 -0
  373. ksadk/studio/agent_lifecycle.py +51 -0
  374. ksadk/studio/api.py +1598 -0
  375. ksadk/studio/api_catalog_routes.py +324 -0
  376. ksadk/studio/api_contracts.py +286 -0
  377. ksadk/studio/api_helpers.py +132 -0
  378. ksadk/studio/api_memory_routes.py +82 -0
  379. ksadk/studio/authoring.py +749 -0
  380. ksadk/studio/authoring_coordinator.py +1017 -0
  381. ksadk/studio/builder.py +397 -0
  382. ksadk/studio/capabilities.py +368 -0
  383. ksadk/studio/cloud.py +2099 -0
  384. ksadk/studio/codex_agent_service.py +694 -0
  385. ksadk/studio/codex_authoring.py +469 -0
  386. ksadk/studio/codex_builder.py +437 -0
  387. ksadk/studio/codex_manifest.py +307 -0
  388. ksadk/studio/codex_run.py +395 -0
  389. ksadk/studio/compiler.py +190 -0
  390. ksadk/studio/contracts.py +755 -0
  391. ksadk/studio/errors.py +47 -0
  392. ksadk/studio/event_store.py +214 -0
  393. ksadk/studio/framework_run.py +342 -0
  394. ksadk/studio/hosted_kernel.py +317 -0
  395. ksadk/studio/identifiers.py +33 -0
  396. ksadk/studio/manifest_resolver.py +131 -0
  397. ksadk/studio/mcp_runtime.py +229 -0
  398. ksadk/studio/model_client.py +629 -0
  399. ksadk/studio/model_profile_service.py +214 -0
  400. ksadk/studio/operations.py +275 -0
  401. ksadk/studio/otel_trace.py +1060 -0
  402. ksadk/studio/pagination.py +182 -0
  403. ksadk/studio/pcm_memory.py +85 -0
  404. ksadk/studio/python_tool_inspection.py +183 -0
  405. ksadk/studio/repository.py +327 -0
  406. ksadk/studio/resource_catalog.py +1260 -0
  407. ksadk/studio/run_service.py +1133 -0
  408. ksadk/studio/runtime_catalog.py +74 -0
  409. ksadk/studio/runtime_source.py +365 -0
  410. ksadk/studio/service.py +2249 -0
  411. ksadk/studio/shared_web.py +1004 -0
  412. ksadk/studio/skill_discovery.py +628 -0
  413. ksadk/studio/static/assets/PrismRenderer-IYN-ffww.js +3 -0
  414. ksadk/studio/static/assets/index-BLpcdrgW.css +1 -0
  415. ksadk/studio/static/assets/index-CYekR2Xv.js +260 -0
  416. ksadk/studio/static/assets/react-vendor-BNmTiJ-I.js +9 -0
  417. ksadk/studio/static/assets/rolldown-runtime-hePW80VL.js +1 -0
  418. ksadk/studio/static/index.html +15 -0
  419. ksadk/studio/templates.py +722 -0
  420. ksadk/studio/validator.py +265 -0
  421. ksadk/studio/workspace.py +135 -0
  422. ksadk/tools/gateway.py +9 -0
  423. ksadk/toolsets/__init__.py +27 -1
  424. ksadk/toolsets/a2a.py +426 -0
  425. ksadk/tracing/__init__.py +19 -21
  426. ksadk/tracing/setup.py +317 -403
  427. ksadk/tracing/span_utils.py +0 -44
  428. ksadk/version.py +1 -1
  429. ksadk_runtime_common/memory_backend/registry.py +56 -56
  430. ksadk_runtime_common/schemas/runtime_event_v1.json +37 -0
  431. ksadk_runtime_common/schemas/runtime_event_v2.json +271 -0
  432. ksadk_runtime_common/workspace_files/router.py +8 -2
  433. agentengine_sdk_python-0.8.0.dist-info/RECORD +0 -578
  434. ksadk/a2a/card_builder.py +0 -64
  435. ksadk/a2a/client.py +0 -309
  436. ksadk/events/parser.py +0 -191
  437. ksadk/harness/runner.py +0 -211
  438. ksadk/runners/codex_runner.py +0 -132
  439. ksadk/runners/utils/langfuse.py +0 -230
  440. ksadk/server/static/assets/MathMessageMarkdown-DeYh8QPr.css +0 -1
  441. ksadk/server/static/assets/MermaidBlock-DWuxETaJ.js +0 -303
  442. ksadk/server/static/assets/NativeTerminalPanel-MDTopWMO.js +0 -1
  443. ksadk/server/static/assets/arc-CFKwSk_B.js +0 -1
  444. ksadk/server/static/assets/architecture-7EHR7CIX-CynqOWjZ.js +0 -1
  445. ksadk/server/static/assets/architectureDiagram-3BPJPVTR-Dej8jX8c.js +0 -36
  446. ksadk/server/static/assets/blockDiagram-GPEHLZMM-1F64jqvy.js +0 -132
  447. ksadk/server/static/assets/c4Diagram-AAUBKEIU-9q7AUAKb.js +0 -10
  448. ksadk/server/static/assets/channel-iUGv2N24.js +0 -1
  449. ksadk/server/static/assets/chunk-2J33WTMH-6qgwJm7Q.js +0 -1
  450. ksadk/server/static/assets/chunk-55IACEB6-BK8paA20.js +0 -1
  451. ksadk/server/static/assets/chunk-727SXJPM-B-JEhy7h.js +0 -206
  452. ksadk/server/static/assets/chunk-AQP2D5EJ-DYBvMHAa.js +0 -231
  453. ksadk/server/static/assets/chunk-QZHKN3VN-YQIEXeF6.js +0 -1
  454. ksadk/server/static/assets/classDiagram-4FO5ZUOK-CDAUuoXK.js +0 -1
  455. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-CDAUuoXK.js +0 -1
  456. ksadk/server/static/assets/cytoscape.esm-DelgaX4f.js +0 -321
  457. ksadk/server/static/assets/dagre-BM42HDAG-C7TguAWo.js +0 -4
  458. ksadk/server/static/assets/dagre-Bx709z4p.js +0 -1
  459. ksadk/server/static/assets/diagram-2AECGRRQ-Dd17_kws.js +0 -43
  460. ksadk/server/static/assets/diagram-5GNKFQAL-DVIClIil.js +0 -10
  461. ksadk/server/static/assets/diagram-KO2AKTUF-Df_3gYQa.js +0 -3
  462. ksadk/server/static/assets/diagram-LMA3HP47-BhLk6ZtA.js +0 -24
  463. ksadk/server/static/assets/diagram-OG6HWLK6-D4GNGvuu.js +0 -24
  464. ksadk/server/static/assets/dist-DyvYLMW9.js +0 -1
  465. ksadk/server/static/assets/erDiagram-TEJ5UH35-CnfRcY2M.js +0 -85
  466. ksadk/server/static/assets/eventmodeling-FCH6USID-ChYO-ijM.js +0 -1
  467. ksadk/server/static/assets/flowDiagram-I6XJVG4X-DGaJjGNJ.js +0 -162
  468. ksadk/server/static/assets/ganttDiagram-6RSMTGT7-DpCoB2g9.js +0 -292
  469. ksadk/server/static/assets/gitGraph-WXDBUCRP-BN_0Fk-j.js +0 -1
  470. ksadk/server/static/assets/gitGraphDiagram-PVQCEYII-9IKVFBVT.js +0 -106
  471. ksadk/server/static/assets/graphlib-B8gBHxth.js +0 -1
  472. ksadk/server/static/assets/index-QSwCHuB8.js +0 -177
  473. ksadk/server/static/assets/index-jOPmTOZH.css +0 -2
  474. ksadk/server/static/assets/info-J43DQDTF-Cp6o3vId.js +0 -1
  475. ksadk/server/static/assets/infoDiagram-5YYISTIA-CYIIsb8y.js +0 -2
  476. ksadk/server/static/assets/katex-DATssjp1.js +0 -265
  477. ksadk/server/static/assets/mermaid-parser.core-mGAcEwcq.js +0 -161
  478. ksadk/server/static/assets/packet-YPE3B663-wQzct3cT.js +0 -1
  479. ksadk/server/static/assets/pie-LRSECV5Y-BgTQiv-9.js +0 -1
  480. ksadk/server/static/assets/pieDiagram-4H26LBE5-By-ra07M.js +0 -30
  481. ksadk/server/static/assets/radar-GUYGQ44K-BIq5fXsn.js +0 -1
  482. ksadk/server/static/assets/requirementDiagram-4Y6WPE33-DpW90iu3.js +0 -84
  483. ksadk/server/static/assets/stateDiagram-AJRCARHV-DGpd9yFG.js +0 -1
  484. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-B3o579IQ.js +0 -1
  485. ksadk/server/static/assets/treeView-BLDUP644-Dqi-E0EM.js +0 -1
  486. ksadk/server/static/assets/treemap-LRROVOQU-ClQP9CJ9.js +0 -1
  487. ksadk/server/static/assets/vennDiagram-CIIHVFJN-l3Z7-wbP.js +0 -34
  488. ksadk/server/static/assets/wardley-L42UT6IY-8XLvnNIs.js +0 -1
  489. ksadk/server/static/assets/wardleyDiagram-YWT4CUSO-BR54GPmr.js +0 -78
  490. ksadk/server/static/assets/xychartDiagram-2RQKCTM6-SkiH8GsK.js +0 -7
  491. ksadk/tracing/exporters/langfuse_exporter.py +0 -460
  492. {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/entry_points.txt +0 -0
  493. {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/licenses/LICENSE +0 -0
  494. {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/top_level.txt +0 -0
@@ -13,24 +13,35 @@ 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 (
44
+ RESUME_START_REQUEST_NATIVE_KEY,
34
45
  BaseRuntime,
35
46
  CancelResult,
36
47
  CheckpointCapability,
@@ -42,78 +53,22 @@ from ksadk.runtime.adapter import (
42
53
  StartRequest,
43
54
  )
44
55
  from ksadk.runtime.preprocessing import PreparedRuntimeStart, prepare_runtime_start
45
- from ksadk.runtime_context import platform_invocation_scope
56
+ from ksadk.runtime.runner_loading import ensure_runner_loaded
46
57
 
47
58
  logger = logging.getLogger(__name__)
48
59
 
49
- _STREAM_STOP = object()
50
- _ResumeKey = tuple[str, str, str, str, str]
51
-
52
-
53
- async def _anext_or_stop(gen: AsyncIterator[Any]) -> Any:
54
- """取下一个 chunk;流结束返回 _STREAM_STOP sentinel(便于竞速)。"""
55
- try:
56
- return await gen.__anext__()
57
- except StopAsyncIteration:
58
- return _STREAM_STOP
59
-
60
-
61
- def _a2ui_surface_event(chunk: Any) -> tuple[str, dict[str, Any]] | None:
62
- """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
+ )
63
64
 
64
- The dynamic ``generate_a2ui`` tool returns official v0.9 operations as a
65
- JSON tool result. Tool results are otherwise opaque to the runtime, which
66
- would leave AG-UI with nothing to project until a page reload reconstructs
67
- history. Convert exactly that envelope at the runtime boundary so it is
68
- streamed, persisted, and replayed like every other A2UI surface.
69
- """
65
+ # dict-chunk 退化路径的流竞速/事件映射实现拆至 _runner_adapter 子包(纯移动,行为不变)。
66
+ from ksadk.runtime._runner_adapter.stream_mapping import ( # noqa: E402
67
+ _STREAM_STOP,
68
+ _RunnerStreamMappingMixin,
69
+ )
70
70
 
71
- if not isinstance(chunk, dict):
72
- return None
73
- value = chunk.get("tool_output", chunk.get("output"))
74
- if value is not None and hasattr(value, "content"):
75
- value = value.content
76
- if isinstance(value, str):
77
- try:
78
- value = json.loads(value)
79
- except (TypeError, ValueError):
80
- return None
81
- if not isinstance(value, Mapping):
82
- return None
83
- operations_raw = value.get("a2ui_operations")
84
- if not isinstance(operations_raw, list) or not operations_raw:
85
- return None
86
- operations = [dict(operation) for operation in operations_raw if isinstance(operation, Mapping)]
87
- if not operations:
88
- return None
89
-
90
- known: list[tuple[str, str]] = []
91
- for operation in operations:
92
- for key, event_type in (
93
- ("createSurface", EventType.A2UI_SURFACE_BEGIN),
94
- ("updateComponents", EventType.A2UI_SURFACE_UPDATE),
95
- ("updateDataModel", EventType.A2UI_SURFACE_UPDATE),
96
- ("deleteSurface", EventType.A2UI_SURFACE_END),
97
- ):
98
- detail = operation.get(key)
99
- if isinstance(detail, Mapping) and isinstance(detail.get("surfaceId"), str):
100
- surface_id = detail["surfaceId"].strip()
101
- if surface_id:
102
- known.append((surface_id, event_type))
103
- break
104
- if not known:
105
- return None
106
- surface_ids = {surface_id for surface_id, _event_type in known}
107
- if len(surface_ids) != 1:
108
- logger.warning("ignoring A2UI tool result with multiple surfaces")
109
- return None
110
- surface_id = known[0][0]
111
- event_type = (
112
- EventType.A2UI_SURFACE_BEGIN
113
- if any(kind == EventType.A2UI_SURFACE_BEGIN for _surface_id, kind in known)
114
- else known[0][1]
115
- )
116
- return event_type, {"surface_id": surface_id, "operations": operations}
71
+ _ResumeKey = tuple[str, str, str, str, str]
117
72
 
118
73
 
119
74
  def _coerce_literal(value: Any, allowed: tuple[str, ...], default: str) -> Any:
@@ -157,9 +112,14 @@ class _ActiveRun:
157
112
  resume_fingerprint: Any = None
158
113
  skip_runner: bool = False
159
114
  done: bool = False
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
160
120
 
161
121
 
162
- class RunnerRuntimeAdapter(RuntimeAdapter):
122
+ class RunnerRuntimeAdapter(_RunnerStreamMappingMixin, RuntimeAdapter):
163
123
  """把 ``BaseRunner`` 映射为平台六动词的通用 adapter。"""
164
124
 
165
125
  def __init__(self, runner: BaseRunner, *, runtime_type: str) -> None:
@@ -178,6 +138,63 @@ class RunnerRuntimeAdapter(RuntimeAdapter):
178
138
 
179
139
  # ---- 框架钩子(子类按需 override) ----
180
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
+
181
198
  def _checkpoint_capability(self) -> CheckpointCapability:
182
199
  """诚实暴露 checkpoint 粒度。默认读 runner.describe_checkpoint_capability。"""
183
200
  raw = {}
@@ -230,7 +247,13 @@ class RunnerRuntimeAdapter(RuntimeAdapter):
230
247
 
231
248
  # ---- 六动词 ----
232
249
 
250
+ async def preflight(self) -> None:
251
+ """Load the framework agent before a streaming HTTP response commits."""
252
+
253
+ ensure_runner_loaded(self._runner, runtime_type=self._runtime_type)
254
+
233
255
  async def start(self, request: StartRequest) -> RunHandle:
256
+ ensure_runner_loaded(self._runner, runtime_type=self._runtime_type)
234
257
  run_id = str(request.metadata.get("invocation_id") or self._next_invocation_id())
235
258
  prepared_start = await prepare_runtime_start(request, self._runner)
236
259
  handle = RunHandle(
@@ -275,7 +298,7 @@ class RunnerRuntimeAdapter(RuntimeAdapter):
275
298
  )
276
299
  attach = getattr(self._runner, "attach_runtime_handle", None)
277
300
  if not callable(attach):
278
- raise RuntimeError(
301
+ raise UnsupportedControlError(
279
302
  f"runner for {self._runtime_type!r} has no durable "
280
303
  "attach_runtime_handle capability"
281
304
  )
@@ -336,6 +359,12 @@ class RunnerRuntimeAdapter(RuntimeAdapter):
336
359
  return handle
337
360
  self._require_native_checkpoint_capability()
338
361
  self._known_runs.add(handle.run_id)
362
+ prepared_start: PreparedRuntimeStart | None = None
363
+ raw_start_request = handle.native_ref.pop(RESUME_START_REQUEST_NATIVE_KEY, None)
364
+ if isinstance(raw_start_request, Mapping):
365
+ prepared_start = await prepare_runtime_start(
366
+ StartRequest.model_validate(raw_start_request), self._runner
367
+ )
339
368
  # _resume_native 返回的 runner_input 覆盖存到 run 上,下一次 stream() 经
340
369
  # _build_runner_input 消费,以框架原生方式真驱动恢复。
341
370
  override = await self._resume_native(handle, target, payload)
@@ -380,6 +409,8 @@ class RunnerRuntimeAdapter(RuntimeAdapter):
380
409
  resume_key=resume_key,
381
410
  resume_fingerprint=resume_fingerprint,
382
411
  )
412
+ if prepared_start is not None:
413
+ run.__dict__["_prepared_start"] = prepared_start
383
414
  if override:
384
415
  run.__dict__["_resume_input_override"] = override
385
416
  self._resume_decisions[resume_key] = resume_fingerprint
@@ -390,7 +421,7 @@ class RunnerRuntimeAdapter(RuntimeAdapter):
390
421
  capability = self._require_native_checkpoint_capability()
391
422
  checkpoint_id = str(handle.native_ref.get("checkpoint_id") or "").strip()
392
423
  if not checkpoint_id:
393
- raise RuntimeError(
424
+ raise UnsupportedControlError(
394
425
  f"{self._runtime_type} runner has no native checkpoint for run "
395
426
  f"{handle.run_id!r}"
396
427
  )
@@ -407,7 +438,7 @@ class RunnerRuntimeAdapter(RuntimeAdapter):
407
438
  if capability.supported:
408
439
  return capability
409
440
  detail = capability.reason or "runner does not expose framework checkpoints"
410
- raise RuntimeError(
441
+ raise UnsupportedControlError(
411
442
  f"{self._runtime_type} native checkpoint capability is unavailable: {detail}"
412
443
  )
413
444
 
@@ -477,30 +508,19 @@ class RunnerRuntimeAdapter(RuntimeAdapter):
477
508
  # 消费 pending cancel:start 时若已记 pending,立即中断该 turn。
478
509
  if handle.run_id in self._pending_cancels:
479
510
  self._pending_cancels.discard(handle.run_id)
480
- yield self._event(
481
- handle,
482
- EventType.RUN_CANCELED,
483
- {
484
- "status": "cancelled",
485
- "cancel_result": CancelResult.PENDING_CANCEL_RECORDED.value,
486
- },
487
- )
511
+ yield self._make_run_canceled(handle, reason=CancelResult.PENDING_CANCEL_RECORDED.value)
488
512
  return
489
513
 
490
514
  if run.skip_runner:
491
515
  run.done = True
492
516
  self._active_runs.pop(handle.run_id, None)
493
- yield self._event(
494
- handle,
495
- EventType.RUN_COMPLETED,
496
- {"status": "already_resumed"},
497
- )
517
+ yield self._make_run_completed(handle)
498
518
  return
499
519
 
500
520
  if run.resume_key is not None:
501
521
  self._consumed_resumes.add(run.resume_key)
502
522
 
503
- yield self._event(handle, EventType.RUN_STARTED, {"status": "in_progress"})
523
+ yield self._make_run_started(handle)
504
524
 
505
525
  request = run.__dict__.get("_start_request")
506
526
  runner_input = self._build_runner_input(handle, request)
@@ -512,35 +532,35 @@ class RunnerRuntimeAdapter(RuntimeAdapter):
512
532
  run.stream = gen
513
533
  async for event in gen:
514
534
  yield event
515
- if event.event_type == EventType.APPROVAL_REQUESTED:
535
+ if event.event_type == "interaction.requested":
516
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)
517
546
  if event.event_type in {
518
- EventType.RUN_COMPLETED,
519
- EventType.RUN_FAILED,
520
- EventType.RUN_CANCELED,
521
- EventType.RUN_INTERRUPTED,
547
+ "run.completed",
548
+ "run.failed",
549
+ "run.canceled",
550
+ "run.interrupted",
522
551
  }:
523
552
  terminal_event_seen = True
524
- if event.event_type in {EventType.RUN_FAILED, EventType.RUN_CANCELED}:
553
+ if event.event_type in {"run.failed", "run.canceled"}:
525
554
  return
526
555
 
527
556
  if run.interrupt_event.is_set() and not terminal_event_seen:
528
- yield self._event(
529
- handle,
530
- EventType.RUN_CANCELED,
531
- {
532
- "status": "cancelled",
533
- "cancel_result": CancelResult.INTERRUPTED_ACTIVE_TURN.value,
534
- },
557
+ yield self._make_run_canceled(
558
+ handle, reason=CancelResult.INTERRUPTED_ACTIVE_TURN.value
535
559
  )
536
560
  elif approval_interrupted and not terminal_event_seen:
537
- yield self._event(
538
- handle,
539
- EventType.RUN_INTERRUPTED,
540
- {"status": "input_required"},
541
- )
561
+ yield self._make_run_interrupted(handle, reason="input_required")
542
562
  elif not terminal_event_seen:
543
- yield self._event(handle, EventType.RUN_COMPLETED, {"status": "completed"})
563
+ yield self._make_run_completed(handle, run=run, metrics=run.completion_metrics)
544
564
  finally:
545
565
  run.stream = None
546
566
  run.done = True
@@ -551,19 +571,30 @@ class RunnerRuntimeAdapter(RuntimeAdapter):
551
571
  # 直接作为 runner 输入,驱动框架原生恢复。
552
572
  run = self._active_runs.get(handle.run_id)
553
573
  override = run.__dict__.get("_resume_input_override") if run is not None else None
574
+ prepared_start = run.__dict__.get("_prepared_start") if run is not None else None
554
575
  if override is not None:
555
- merged = {
556
- "input": override.get("input"),
557
- "session_id": handle.session_id,
558
- "invocation_id": handle.run_id,
559
- "metadata": dict(override.get("metadata") or {}),
560
- }
576
+ merged = (
577
+ dict(prepared_start.runner_input)
578
+ if isinstance(prepared_start, PreparedRuntimeStart)
579
+ else {}
580
+ )
581
+ base_metadata = merged.get("metadata")
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
+ )
561
593
  for key, value in override.items():
562
594
  if key not in ("input", "metadata"):
563
595
  merged[key] = value
564
596
  return merged
565
597
 
566
- prepared_start = run.__dict__.get("_prepared_start") if run is not None else None
567
598
  if isinstance(prepared_start, PreparedRuntimeStart):
568
599
  return dict(prepared_start.runner_input)
569
600
 
@@ -582,283 +613,191 @@ class RunnerRuntimeAdapter(RuntimeAdapter):
582
613
  "metadata": metadata,
583
614
  }
584
615
 
585
- async def _map_runner_stream(
586
- self, handle: RunHandle, runner_input: dict
587
- ) -> AsyncIterator[RuntimeEvent]:
588
- run = self._active_runs.get(handle.run_id)
589
- interrupt = run.interrupt_event if run is not None else None
590
- prepared_start = run.__dict__.get("_prepared_start") if run is not None else None
591
- invocation_context = (
592
- prepared_start.context if isinstance(prepared_start, PreparedRuntimeStart) else None
593
- )
594
- scope = (
595
- platform_invocation_scope(invocation_context)
596
- if invocation_context is not None
597
- else nullcontext()
616
+ # ---- canonical event construction helpers ----
617
+
618
+ def _interaction_requested_from_approval(
619
+ self,
620
+ handle: RunHandle,
621
+ run: Optional[_ActiveRun],
622
+ *,
623
+ detail: Mapping[str, Any],
624
+ call_id: str,
625
+ ) -> list[RuntimeEvent]:
626
+ """把 ToolGateway 结果中携带的审批请求转为 canonical InteractionRequested。"""
627
+
628
+ approval_id = str(detail.get("approval_request_id") or detail.get("id") or call_id or "")
629
+ resolved_call_id = str(call_id or approval_id)
630
+ if run is not None and approval_id:
631
+ run.pending_approvals.add(approval_id)
632
+ if approval_id:
633
+ pending_approval_ids = handle.native_ref.setdefault("pending_approval_ids", [])
634
+ if approval_id not in pending_approval_ids:
635
+ pending_approval_ids.append(approval_id)
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
598
643
  )
599
- runner_name = _runner_name(self._runner)
600
- accumulated_output: list[str] = []
601
- usage: dict[str, Any] = {}
602
- runner_gen: Optional[AsyncIterator[Any]] = None
603
- async with _conversation_span_scope(runner_name) as span:
604
- if isinstance(prepared_start, PreparedRuntimeStart):
605
- _set_conversation_span_attributes(
606
- span,
607
- agent_id=str(handle.native_ref.get("agent_id") or "agent"),
608
- user_id=str(handle.native_ref.get("user_id") or "user"),
609
- session_id=handle.session_id,
610
- invocation_id=handle.run_id,
611
- runner_name=runner_name,
612
- model=prepared_start.context.model,
613
- response_id=prepared_start.response_id,
614
- )
615
- _set_conversation_input_attributes(span, prepared_start.input_text)
616
- try:
617
- with scope:
618
- stream_result = self._runner.stream(runner_input)
619
- if inspect.iscoroutine(stream_result):
620
- # runner.stream 若声明为 async def -> AsyncIterator(非 async generator),
621
- # 调用返回 coroutine,需 await 得到迭代器。
622
- stream_result = await stream_result
623
- runner_gen = cast(AsyncIterator[Any], stream_result)
624
- while True:
625
- # 竞速:下一个 runner chunk vs cancel 中断事件。
626
- chunk_task = asyncio.ensure_future(_anext_or_stop(runner_gen))
627
- if run is not None:
628
- run.chunk_task = chunk_task
629
- wait_set = {chunk_task}
630
- interrupt_task = (
631
- asyncio.ensure_future(interrupt.wait())
632
- if interrupt is not None
633
- else None
634
- )
635
- if interrupt_task is not None:
636
- wait_set.add(interrupt_task)
637
- done, pending = await asyncio.wait(
638
- wait_set, return_when=asyncio.FIRST_COMPLETED
639
- )
640
- for task in pending:
641
- task.cancel()
642
- if pending:
643
- await asyncio.gather(*pending, return_exceptions=True)
644
- if interrupt_task is not None and interrupt_task in done:
645
- # cancel 中断:安全关闭 runner 流(同一 task)并停止。
646
- chunk_task.cancel()
647
- await asyncio.gather(chunk_task, return_exceptions=True)
648
- return
649
- try:
650
- chunk = chunk_task.result()
651
- except asyncio.CancelledError:
652
- if interrupt is not None and interrupt.is_set():
653
- return
654
- raise
655
- finally:
656
- if run is not None:
657
- run.chunk_task = None
658
- if chunk is _STREAM_STOP:
659
- return
660
- if isinstance(chunk, dict):
661
- chunk_type = str(chunk.get("type") or "")
662
- if chunk_type in {"final", "text", "text_delta"}:
663
- text = self._coerce(
664
- chunk.get("delta") or chunk.get("output") or chunk.get("data")
665
- )
666
- if text:
667
- accumulated_output.append(text)
668
- raw_usage = chunk.get("usage")
669
- if isinstance(raw_usage, dict):
670
- usage.update(raw_usage)
671
- event = self._chunk_to_event(handle, run, chunk)
672
- if event is not None:
673
- yield event
674
- a2ui_surface = _a2ui_surface_event(chunk)
675
- if a2ui_surface is not None:
676
- event_type, payload = a2ui_surface
677
- yield self._event(handle, event_type, payload)
678
- finally:
679
- if accumulated_output:
680
- _set_conversation_output_attributes(span, "".join(accumulated_output))
681
- _set_conversation_usage_attributes(span, usage)
682
- if runner_gen is not None:
683
- aclose = getattr(runner_gen, "aclose", None)
684
- if callable(aclose):
685
- try:
686
- await aclose()
687
- except Exception: # noqa: BLE001
688
- pass
689
- if run is not None:
690
- run.cancellation_ack.set()
691
-
692
- def _chunk_to_event(
693
- self, handle: RunHandle, run: Optional[_ActiveRun], chunk: Any
694
- ) -> Optional[RuntimeEvent]:
695
- if not isinstance(chunk, dict):
696
- return self._event(
697
- handle, EventType.TEXT_DELTA, {"text": str(chunk)}, phase="commentary"
698
- )
699
- chunk_type = chunk.get("type")
700
- if chunk_type in ("reasoning", "reasoning_delta", "thinking"):
701
- text = self._coerce(
702
- chunk.get("delta")
703
- or chunk.get("content")
704
- or chunk.get("output")
705
- or chunk.get("data")
706
- )
707
- if not text:
708
- return None
709
- event_type = (
710
- EventType.REASONING_COMPLETED
711
- if chunk.get("status") in ("completed", "done")
712
- else EventType.REASONING_DELTA
713
- )
714
- return self._event(
715
- handle,
716
- event_type,
717
- {"text": text},
718
- phase="commentary",
719
- )
720
- if chunk_type in ("tool_call", "tool_start"):
721
- call_id = str(
722
- chunk.get("tool_call_id")
723
- or chunk.get("call_id")
724
- or chunk.get("run_id")
725
- or chunk.get("id")
726
- or ""
727
- )
728
- name = str(chunk.get("tool_name") or chunk.get("name") or "tool")
729
- return self._event(
730
- handle,
731
- EventType.TOOL_CALL_BEGIN,
732
- {
733
- "call_id": call_id or name,
734
- "name": name,
735
- "args": chunk.get("tool_args", chunk.get("args")),
736
- },
737
- )
738
- if chunk_type in ("tool_result", "tool_end"):
739
- call_id = str(
740
- chunk.get("tool_call_id")
741
- or chunk.get("call_id")
742
- or chunk.get("run_id")
743
- or chunk.get("id")
744
- or ""
745
- )
746
- name = str(chunk.get("tool_name") or chunk.get("name") or "tool")
747
- return self._event(
748
- handle,
749
- EventType.TOOL_CALL_END,
750
- {
751
- "call_id": call_id or name,
752
- "name": name,
753
- "result": chunk.get("tool_output", chunk.get("output")),
754
- "error": chunk.get("error"),
755
- },
756
- )
757
- if chunk_type in ("interrupt", "approval", "approval_required"):
758
- detail = chunk.get("interrupt_info") or chunk.get("detail") or {}
759
- detail_id = detail.get("approval_request_id") if isinstance(detail, dict) else None
760
- call_id = str(
761
- chunk.get("call_id")
762
- or chunk.get("approval_id")
763
- or chunk.get("id")
764
- or detail_id
765
- or ""
766
- )
767
- if run is not None and call_id:
768
- run.pending_approvals.add(call_id)
769
- if call_id:
770
- pending_approval_ids = handle.native_ref.setdefault("pending_approval_ids", [])
771
- if call_id not in pending_approval_ids:
772
- pending_approval_ids.append(call_id)
773
- return self._event(
774
- handle,
775
- EventType.APPROVAL_REQUESTED,
776
- {
777
- "approval_id": call_id,
778
- "call_id": call_id,
779
- "kind": "tool",
780
- "detail": detail,
781
- },
782
- )
783
- if chunk_type == "checkpoint":
784
- metadata = chunk.get("metadata") or {}
785
- agentengine = metadata.get("agentengine") if isinstance(metadata, dict) else {}
786
- framework_ref = (
787
- agentengine.get("framework_ref") if isinstance(agentengine, dict) else {}
788
- ) or {}
789
- runtime_ref = (
790
- framework_ref.get(self._runtime_type) if isinstance(framework_ref, dict) else {}
791
- ) or {}
792
- checkpoint_id = str(
793
- runtime_ref.get("checkpoint_id") if isinstance(runtime_ref, dict) else ""
794
- )
795
- if not checkpoint_id:
796
- return None
797
- handle.native_ref["checkpoint_id"] = checkpoint_id
798
- known_checkpoint_ids = handle.native_ref.setdefault("known_checkpoint_ids", [])
799
- if checkpoint_id not in known_checkpoint_ids:
800
- known_checkpoint_ids.append(checkpoint_id)
801
- handle.native_ref["framework_ref"] = framework_ref
802
- if isinstance(runtime_ref, dict):
803
- handle.native_ref.update(runtime_ref)
804
- return self._event(
805
- handle,
806
- EventType.CHECKPOINT_CREATED,
807
- {
808
- "checkpoint_id": checkpoint_id,
809
- "granularity": self._checkpoint_capability().granularity,
810
- "resume_target": framework_ref,
811
- },
812
- )
813
- if chunk_type == "graph_update":
814
- return self._event(
815
- handle,
816
- EventType.RUN_PROGRESS,
817
- {
818
- "status": "in_progress",
819
- "node": str(chunk.get("node") or ""),
820
- "state_update": self._coerce(chunk.get("output")),
821
- },
822
- )
823
- if chunk_type == "error":
824
- error = self._coerce(chunk.get("message") or chunk.get("error"))
825
- return self._event(
826
- handle,
827
- EventType.RUN_FAILED,
828
- {
829
- "status": "failed",
830
- "error": error or "runner failed",
831
- },
832
- )
833
- if chunk_type == "final":
834
- return self._event(
835
- handle,
836
- EventType.TEXT_COMPLETED,
837
- {"text": self._coerce(chunk.get("output"))},
838
- phase="final_answer",
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
+ ),
839
660
  )
840
- text = self._coerce(chunk.get("delta") or chunk.get("output") or chunk.get("data"))
841
- if not text:
842
- return None
843
- return self._event(handle, EventType.TEXT_DELTA, {"text": text}, phase="commentary")
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,
678
+ },
679
+ )
844
680
 
845
- def _event(
681
+ def _canonical_kwargs(
846
682
  self,
847
683
  handle: RunHandle,
684
+ *,
685
+ scope_id: str,
686
+ item_id: str,
848
687
  event_type: str,
849
- payload: dict,
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",
722
+ )
723
+
724
+ def _make_run_completed(
725
+ self,
726
+ handle: RunHandle,
850
727
  *,
851
- phase: Optional[str] = None,
852
- ) -> RuntimeEvent:
853
- return RuntimeEvent.create(
854
- event_type,
855
- agent_id=str(handle.native_ref.get("agent_id") or "agent"),
856
- user_id=str(handle.native_ref.get("user_id") or "user"),
857
- session_id=handle.session_id,
858
- invocation_id=handle.run_id,
859
- seq_id=self._next_seq(),
860
- phase=phase,
861
- payload=payload,
728
+ run: Optional[_ActiveRun] = None,
729
+ metrics: Mapping[str, Any] | None = None,
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,
862
801
  )
863
802
 
864
803
  @staticmethod