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
@@ -4,8 +4,10 @@ LangGraphRunner - LangGraph 框架运行时
4
4
  直接透传 LangGraph 原生能力,最小化封装
5
5
  """
6
6
 
7
+ from __future__ import annotations
8
+
9
+ import asyncio
7
10
  import base64
8
- import inspect
9
11
  import os
10
12
  import re
11
13
  import uuid
@@ -14,19 +16,30 @@ from typing import Any, AsyncIterator, Dict, Mapping
14
16
  from langgraph.types import Command
15
17
 
16
18
  from ksadk.conversations.attachments import classify_attachment_kind, read_attachment_uri_bytes
17
- from ksadk.conversations.reasoning_markup import ReasoningMarkupParser, strip_reasoning_markup
19
+ from ksadk.runners._langgraph_runner_streams import _LangGraphStreamMixin
18
20
  from ksadk.runners.base_runner import BaseRunner
19
- from ksadk.runners.usage_accumulator import accumulate_usage
20
- from ksadk.runners.utils import get_langfuse_callbacks, get_langfuse_metadata, load_agent_module
21
+ from ksadk.runners.utils import load_agent_module
21
22
  from ksadk.sessions.continuity import LangGraphSessionAdapter
22
23
 
23
24
 
24
- class LangGraphRunner(BaseRunner):
25
+ class LangGraphRunner(_LangGraphStreamMixin, BaseRunner):
25
26
  """LangGraph 框架运行时
26
27
 
27
28
  透传原生 LangGraph 功能,支持任意 State 格式
28
29
  """
29
30
 
31
+ # ToolGateway approvals can arise after an otherwise terminal tool call;
32
+ # this runner opts into the runtime's semantic follow-up continuation.
33
+ supports_gateway_approval_semantic_resume = True
34
+
35
+ def __init__(self, detection_result: Any, project_dir: str):
36
+ super().__init__(detection_result, project_dir)
37
+ self._managed_checkpoint_lock = asyncio.Lock()
38
+ self._managed_checkpoint_prepared = False
39
+ self._managed_checkpoint_error: tuple[str, str] | None = None
40
+ self._managed_checkpoint_pool: Any = None
41
+ self._managed_checkpoint_namespace = ""
42
+
30
43
  def load_agent(self) -> None:
31
44
  self._load_agent(force_reload=False)
32
45
 
@@ -49,7 +62,13 @@ class LangGraphRunner(BaseRunner):
49
62
  normalized = self.sync_process_model_env(model)
50
63
  if normalized is None or self._agent is None:
51
64
  return
52
- if normalized == getattr(self, "_loaded_model_name", None):
65
+ # Studio's generated graph reads the model environment while building
66
+ # each model turn. Reloading it here would discard the managed
67
+ # PostgreSQL checkpointer that was installed asynchronously below.
68
+ if (
69
+ normalized == getattr(self, "_loaded_model_name", None)
70
+ or self._managed_checkpoint_pool is not None
71
+ ):
53
72
  return
54
73
  self._load_agent(force_reload=True)
55
74
 
@@ -62,24 +81,23 @@ class LangGraphRunner(BaseRunner):
62
81
  if checkpointer is None:
63
82
  checkpointer = getattr(agent, "_checkpointer", None)
64
83
  if checkpointer is None:
84
+ error_code, error_reason = self._managed_checkpoint_error or ("", "")
65
85
  return {
66
86
  "Supported": False,
67
87
  "Backend": "none",
68
88
  "Scope": "unknown",
69
89
  "Durable": False,
70
90
  "SharedAcrossPods": False,
71
- "Reason": "LangGraph graph has no configured checkpointer",
91
+ "ResumeMode": "none",
92
+ **({"ReasonCode": error_code} if error_code else {}),
93
+ "Reason": error_reason or "LangGraph graph has no configured checkpointer",
72
94
  }
73
95
 
74
- checkpointer_type = type(checkpointer)
75
- type_name = f"{checkpointer_type.__module__}.{checkpointer_type.__name__}".lower()
76
- if "memory" in type_name or "inmemory" in type_name:
77
- backend = "memory"
78
- elif "sqlite" in type_name:
79
- backend = "sqlite"
80
- elif "postgres" in type_name:
81
- backend = "postgres"
82
- else:
96
+ backend = self._checkpoint_backend_from_saver(checkpointer)
97
+ if backend == "unknown":
98
+ # Some third-party savers hide their concrete type. Preserve the
99
+ # explicit legacy declaration for those cases, but never let it
100
+ # override a detectable in-memory saver.
83
101
  backend = str(os.getenv("KSADK_CHECKPOINT_BACKEND") or "").strip().lower()
84
102
  if backend == "local":
85
103
  backend = "sqlite"
@@ -108,17 +126,28 @@ class LangGraphRunner(BaseRunner):
108
126
  reason = "In-memory checkpoint cannot be recovered after process restart or across pods"
109
127
 
110
128
  return {
111
- "Supported": True,
129
+ # A local saver may be useful for interactive development, but it
130
+ # is not a native durable-resume capability in a hosted runtime.
131
+ "Supported": backend not in {"memory", "inmemory", "unknown", ""},
112
132
  "Backend": backend,
113
133
  "Scope": scope,
114
134
  "Durable": durable,
115
135
  "SharedAcrossPods": shared,
116
- "ResumeMode": "time_travel",
136
+ "ResumeMode": "time_travel" if durable else "none",
137
+ **(
138
+ {"ReasonCode": "CHECKPOINTER_NOT_DURABLE"}
139
+ if backend in {"memory", "inmemory", "unknown", ""}
140
+ else {}
141
+ ),
117
142
  "Reason": reason,
118
143
  }
119
144
 
120
145
  def get_runtime_capabilities(self) -> dict[str, Any]:
121
146
  capabilities = super().get_runtime_capabilities()
147
+ capabilities["model_call_boundaries"] = True
148
+ reason_code = str(capabilities["Checkpoint"].get("ReasonCode") or "")
149
+ if reason_code:
150
+ capabilities["ResumeRun"]["ReasonCode"] = reason_code
122
151
  capabilities["SessionContinuity"] = {
123
152
  "Supported": True,
124
153
  "Type": (
@@ -129,15 +158,149 @@ class LangGraphRunner(BaseRunner):
129
158
  }
130
159
  return capabilities
131
160
 
132
- def _get_config(self, session_id: str) -> dict:
133
- """获取运行配置"""
134
- config: dict[str, Any] = {"configurable": {"thread_id": session_id}}
161
+ @staticmethod
162
+ def _checkpoint_backend_from_saver(checkpointer: Any) -> str:
163
+ if checkpointer is None:
164
+ return "unknown"
165
+ for saver_type in type(checkpointer).__mro__:
166
+ qualified_name = f"{saver_type.__module__}.{saver_type.__name__}".lower()
167
+ if "checkpoint.postgres" in qualified_name or "postgressaver" in qualified_name:
168
+ return "postgres"
169
+ if "checkpoint.sqlite" in qualified_name or "sqlitesaver" in qualified_name:
170
+ return "sqlite"
171
+ if "checkpoint.memory" in qualified_name or saver_type.__name__.lower() in {
172
+ "memorysaver",
173
+ "inmemorysaver",
174
+ }:
175
+ return "memory"
176
+ return "unknown"
135
177
 
136
- langfuse_callbacks = get_langfuse_callbacks()
137
- if langfuse_callbacks:
138
- config["callbacks"] = langfuse_callbacks
139
- config["metadata"] = get_langfuse_metadata(session_id)
178
+ @staticmethod
179
+ def _env_flag(name: str) -> bool:
180
+ return str(os.getenv(name) or "").strip().lower() in {"1", "true", "yes", "on"}
181
+
182
+ @staticmethod
183
+ def _resolve_checkpoint_namespace() -> str:
184
+ session_namespace = str(os.getenv("KSADK_SESSION_NAMESPACE") or "").strip()
185
+ if session_namespace:
186
+ return session_namespace
187
+ agent_id = str(
188
+ os.getenv("AGENTENGINE_AGENT_ID") or os.getenv("KSADK_AGENT_ID") or "default"
189
+ ).strip()
190
+ return f"agent:{agent_id}"
191
+
192
+ async def _create_managed_postgres_saver(self, dsn: str) -> tuple[Any, Any]:
193
+ from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
194
+ from psycopg.rows import dict_row
195
+ from psycopg_pool import AsyncConnectionPool
196
+
197
+ timeout = max(0.1, float(os.getenv("KSADK_SESSION_CONNECT_TIMEOUT") or "5"))
198
+ pool = AsyncConnectionPool(
199
+ conninfo=dsn,
200
+ min_size=1,
201
+ max_size=10,
202
+ open=False,
203
+ timeout=timeout,
204
+ kwargs={
205
+ "autocommit": True,
206
+ "prepare_threshold": 0,
207
+ "row_factory": dict_row,
208
+ },
209
+ )
210
+ try:
211
+ await pool.open(wait=True, timeout=timeout)
212
+ saver = AsyncPostgresSaver(pool)
213
+ await saver.setup()
214
+ return saver, pool
215
+ except Exception:
216
+ await pool.close()
217
+ raise
140
218
 
219
+ async def prepare_runtime_capabilities(self) -> None:
220
+ """Install the managed saver before a graph can begin an interaction.
221
+
222
+ The graph module must opt into this seam by exporting
223
+ ``ksadk_graph_factory(*, checkpointer)``. We never mutate a compiled
224
+ graph's private attributes: failed configuration remains fail-closed
225
+ and capability discovery honestly reports why native resume is absent.
226
+ """
227
+ if self._managed_checkpoint_prepared:
228
+ return
229
+ async with self._managed_checkpoint_lock:
230
+ if self._managed_checkpoint_prepared:
231
+ return
232
+
233
+ checkpointer = getattr(self._agent, "checkpointer", None)
234
+ if checkpointer is None:
235
+ checkpointer = getattr(self._agent, "_checkpointer", None)
236
+ if self._checkpoint_backend_from_saver(checkpointer) == "postgres":
237
+ self._managed_checkpoint_namespace = self._resolve_checkpoint_namespace()
238
+ self._managed_checkpoint_prepared = True
239
+ return
240
+
241
+ dsn = str(
242
+ os.getenv("KSADK_LANGGRAPH_CHECKPOINT_DSN")
243
+ or os.getenv("KSADK_SESSION_DSN")
244
+ or ""
245
+ ).strip()
246
+ if not self._env_flag("KSADK_LANGGRAPH_AUTO_CHECKPOINT") or not dsn:
247
+ self._managed_checkpoint_prepared = True
248
+ return
249
+
250
+ factory = getattr(self._module, "ksadk_graph_factory", None)
251
+ if not callable(factory):
252
+ self._managed_checkpoint_error = (
253
+ "LANGGRAPH_FACTORY_REQUIRED",
254
+ "LangGraph graph has no durable checkpointer; export "
255
+ "ksadk_graph_factory(*, checkpointer) for managed PostgreSQL checkpoints",
256
+ )
257
+ self._managed_checkpoint_prepared = True
258
+ return
259
+
260
+ pool = None
261
+ try:
262
+ saver, pool = await self._create_managed_postgres_saver(dsn)
263
+ managed_graph = factory(checkpointer=saver)
264
+ if not callable(getattr(managed_graph, "invoke", None)):
265
+ raise TypeError("ksadk_graph_factory must return a compiled LangGraph graph")
266
+ self._agent = managed_graph
267
+ self._managed_checkpoint_pool = pool
268
+ self._managed_checkpoint_namespace = self._resolve_checkpoint_namespace()
269
+ self._managed_checkpoint_error = None
270
+ except (ModuleNotFoundError, ImportError):
271
+ self._managed_checkpoint_error = (
272
+ "DEPENDENCY_MISSING",
273
+ "langgraph-checkpoint-postgres and psycopg are required "
274
+ "for managed checkpoints",
275
+ )
276
+ except Exception as exc: # noqa: BLE001
277
+ error_name = type(exc).__name__.lower()
278
+ self._managed_checkpoint_error = (
279
+ "SCHEMA_PERMISSION_DENIED"
280
+ if "privilege" in error_name or "permission" in error_name
281
+ else "DB_UNREACHABLE",
282
+ "Managed LangGraph PostgreSQL checkpointer initialization failed",
283
+ )
284
+ finally:
285
+ if pool is not None and self._managed_checkpoint_pool is None:
286
+ try:
287
+ await pool.close()
288
+ except Exception: # noqa: BLE001
289
+ pass
290
+ self._managed_checkpoint_prepared = True
291
+
292
+ async def close(self) -> None:
293
+ pool = self._managed_checkpoint_pool
294
+ self._managed_checkpoint_pool = None
295
+ if pool is not None:
296
+ await pool.close()
297
+ await super().close()
298
+
299
+ def _get_config(self, session_id: str) -> dict:
300
+ """获取运行配置"""
301
+ config = {"configurable": {"thread_id": session_id}}
302
+ if self._managed_checkpoint_namespace:
303
+ config["configurable"]["checkpoint_ns"] = self._managed_checkpoint_namespace
141
304
  return config
142
305
 
143
306
  @staticmethod
@@ -188,6 +351,38 @@ class LangGraphRunner(BaseRunner):
188
351
  resume_value = {interrupt_id: value} if interrupt_id else value
189
352
  return Command(resume=resume_value)
190
353
 
354
+ @staticmethod
355
+ def _is_gateway_approval_semantic_resume(value: Any) -> bool:
356
+ """Whether ``value`` came from a completed ToolGateway approval.
357
+
358
+ Native LangGraph ``interrupt()`` values must keep using
359
+ ``Command(resume=...)``. In contrast, a ToolGateway can return an
360
+ ``approval_required`` result from an otherwise normal tool call; the
361
+ graph then reaches its terminal node before KsADK shows the approval
362
+ card. Its approved tool result needs a new semantic graph turn.
363
+ """
364
+
365
+ return bool(
366
+ isinstance(value, Mapping)
367
+ and value.get("_ksadk_gateway_approval_resume") is True
368
+ and str(value.get("type") or "") == "function_call_output"
369
+ )
370
+
371
+ @staticmethod
372
+ def _gateway_approval_follow_up_input() -> str:
373
+ """A neutral prompt for the post-approval semantic continuation.
374
+
375
+ The actual approval response and durable tool result are already in
376
+ the session history. Do not repeat their contents here: project hooks
377
+ commonly route by input keywords, and copying a workspace path back
378
+ into a synthetic user message can accidentally trigger another tool.
379
+ """
380
+
381
+ return (
382
+ "系统已完成此前获批的操作。请基于会话记录中的真实结果,直接向用户说明完成情况;"
383
+ "不要重试,也不要再次要求确认。"
384
+ )
385
+
191
386
  @staticmethod
192
387
  def _checkpoint_ref_from_state(state: Any) -> dict[str, Any]:
193
388
  state_config = None
@@ -378,6 +573,12 @@ class LangGraphRunner(BaseRunner):
378
573
  for msg in history:
379
574
  role = msg.get("role")
380
575
  content = msg.get("content", "")
576
+ # Runtime-owned tool/approval records are preserved in the durable
577
+ # transcript, but must not be taught back to LangGraph as plain text.
578
+ if isinstance(content, str) and content.startswith(
579
+ ("[tool_call]", "[tool_result]", "[approval_request]", "[approval_response]")
580
+ ):
581
+ continue
381
582
  if role == "user":
382
583
  messages.append(HumanMessage(content=content))
383
584
  elif role in ("assistant", "model"):
@@ -575,6 +776,7 @@ class LangGraphRunner(BaseRunner):
575
776
  1. 简化格式: {"input": "hello"} - 自动转换为 messages
576
777
  2. 原生格式: {"messages": [...]} 或自定义 State - 直接透传
577
778
  """
779
+ await self.prepare_runtime_capabilities()
578
780
  payload = dict(input_data)
579
781
  force_graph_invoke = bool(payload.pop("_ksadk_force_graph_invoke", False))
580
782
  if not force_graph_invoke and hasattr(self._agent, "astream_events"):
@@ -586,6 +788,16 @@ class LangGraphRunner(BaseRunner):
586
788
  resume_payload_provided = bool(payload.pop("resume_payload_provided", False))
587
789
  resume_interrupt_id = str(payload.pop("resume_interrupt_id", "") or "")
588
790
  resume_value = payload.get("input")
791
+ is_gateway_approval_resume = bool(
792
+ is_resume and self._is_gateway_approval_semantic_resume(resume_value)
793
+ )
794
+ if is_gateway_approval_resume:
795
+ # ``Command(resume=...)`` only works for a graph that actually
796
+ # yielded LangGraph's native interrupt. ToolGateway approvals are
797
+ # intercepted after a normal tool result, when the graph already
798
+ # ended, so restart a normal turn from the persisted transcript.
799
+ payload["input"] = self._gateway_approval_follow_up_input()
800
+ resume_value = payload["input"]
589
801
  checkpoint_ref = self._extract_langgraph_checkpoint_ref(payload)
590
802
  history = payload.pop("history", [])
591
803
  native_context = self.build_native_context(payload.get("platform_context"))
@@ -600,14 +812,19 @@ class LangGraphRunner(BaseRunner):
600
812
  # 判断输入格式 / resume
601
813
  if is_checkpoint_resume:
602
814
  state = resume_value
603
- elif is_resume:
815
+ elif is_resume and not is_gateway_approval_resume:
604
816
  # ``Command(resume=...)`` is delivered to the graph's suspended
605
817
  # interrupt. A custom prepare-state hook is for new user input;
606
818
  # applying it here can rewrite an approval decision into ordinary
607
819
  # graph state and turn an approved HITL action into a rejection.
608
820
  state = resume_value
609
821
  elif self._has_prepare_state_hook():
610
- state = self._prepare_state_with_hook(payload, session_id, history)
822
+ state = self._prepare_state_with_hook(
823
+ payload,
824
+ session_id,
825
+ history,
826
+ is_resume=is_gateway_approval_resume,
827
+ )
611
828
  else:
612
829
  state = self._to_state(payload, history)
613
830
 
@@ -622,7 +839,7 @@ class LangGraphRunner(BaseRunner):
622
839
  config=config,
623
840
  context=native_context,
624
841
  )
625
- elif is_resume:
842
+ elif is_resume and not is_gateway_approval_resume:
626
843
  result = await self._invoke_graph(
627
844
  Command(resume=state),
628
845
  config=config,
@@ -834,414 +1051,16 @@ class LangGraphRunner(BaseRunner):
834
1051
  )
835
1052
  return events
836
1053
 
837
- async def stream(self, input_data: Dict[str, Any]) -> AsyncIterator[Dict[str, Any]]:
838
- """流式调用 LangGraph 图"""
839
- payload = dict(input_data)
840
- payload.pop("_ksadk_force_graph_invoke", None)
841
- session_id = payload.pop("session_id", None) or str(uuid.uuid4())[:8]
842
- history = payload.pop("history", [])
843
- is_resume = payload.pop("resume", False)
844
- is_checkpoint_resume = bool(payload.pop("checkpoint_resume", False))
845
- resume_payload_provided = bool(payload.pop("resume_payload_provided", False))
846
- resume_interrupt_id = str(payload.pop("resume_interrupt_id", "") or "")
847
- resume_value = payload.get("input")
848
- checkpoint_ref = self._extract_langgraph_checkpoint_ref(payload)
849
- native_context = self.build_native_context(payload.get("platform_context"))
850
- invoke_payload = dict(payload)
851
- invoke_payload["session_id"] = session_id
852
- if history:
853
- invoke_payload["history"] = history
854
- if is_resume:
855
- invoke_payload["resume"] = True
856
- if is_checkpoint_resume:
857
- invoke_payload["checkpoint_resume"] = True
858
- invoke_payload["resume_payload_provided"] = resume_payload_provided
859
- invoke_payload["resume_interrupt_id"] = resume_interrupt_id
860
-
861
- config = self._get_config(session_id)
862
- if is_checkpoint_resume:
863
- config = self._apply_checkpoint_resume_config(
864
- config,
865
- session_id=session_id,
866
- checkpoint_ref=checkpoint_ref,
867
- )
868
-
869
- if is_checkpoint_resume:
870
- state = resume_value
871
- elif is_resume:
872
- # Keep the interrupt value intact for ``Command(resume=...)``;
873
- # prepare-state hooks only shape fresh user turns.
874
- state = resume_value
875
- elif self._has_prepare_state_hook():
876
- state = self._prepare_state_with_hook(payload, session_id, history)
877
- else:
878
- state = self._to_state(payload, history)
879
-
880
- accumulated_text = ""
881
- accumulated_reasoning = ""
882
- inline_reasoning_parser = ReasoningMarkupParser()
883
- emitted_non_text_event = False
884
- final_output_text = ""
885
- final_output_usage: dict[str, Any] = {}
886
- final_output_last_usage: dict[str, Any] = {}
887
- model_run_usages: dict[str, dict[str, Any]] = {}
888
- model_run_order: list[str] = []
889
- stream_usage_run_keys: set[str] = set()
890
- latest_stream_usage: dict[str, Any] = {}
891
-
892
- def model_run_key(
893
- event: Mapping[str, Any],
894
- *,
895
- fallback_key: str | None = None,
896
- ) -> str:
897
- raw_run_id = event.get("run_id")
898
- return (
899
- str(raw_run_id)
900
- if raw_run_id
901
- else fallback_key or f"model-event-{len(model_run_order)}"
902
- )
903
-
904
- def record_model_usage(
905
- event: Mapping[str, Any],
906
- usage: dict[str, Any],
907
- *,
908
- fallback_key: str | None = None,
909
- ) -> None:
910
- if not usage:
911
- return
912
- run_key = model_run_key(event, fallback_key=fallback_key)
913
- if run_key not in model_run_usages:
914
- model_run_order.append(run_key)
915
- model_run_usages[run_key] = dict(usage)
916
-
917
- def accumulated_model_usage() -> dict[str, Any]:
918
- if len(model_run_order) == 1:
919
- return dict(model_run_usages.get(model_run_order[0]) or {})
920
- usage: dict[str, Any] = {}
921
- for run_key in model_run_order:
922
- usage = accumulate_usage(usage, model_run_usages.get(run_key) or {})
923
- return usage
924
-
925
- def latest_model_usage() -> dict[str, Any]:
926
- for run_key in reversed(model_run_order):
927
- usage = model_run_usages.get(run_key)
928
- if usage:
929
- return dict(usage)
930
- return {}
931
-
932
- if is_checkpoint_resume and callable(getattr(self._agent, "astream", None)):
933
- try:
934
- async for chunk in self._stream_checkpoint_resume_updates(
935
- stream_input=self._checkpoint_resume_input(
936
- state,
937
- payload_provided=resume_payload_provided,
938
- interrupt_id=resume_interrupt_id,
939
- ),
940
- config=config,
941
- context=native_context,
942
- ):
943
- yield chunk
944
- return
945
- except Exception as e:
946
- yield {
947
- "type": "error",
948
- "message": str(e) or "LangGraph checkpoint resume failed",
949
- "checkpoint_id": str(checkpoint_ref.get("checkpoint_id") or ""),
950
- "exception_type": type(e).__name__,
951
- }
952
- return
953
-
954
- if not hasattr(self._agent, "astream_events"):
955
- result = await self.invoke(invoke_payload)
956
- final_chunk = {"output": result.get("output", ""), "type": "final"}
957
- usage = self._extract_usage(result)
958
- if usage:
959
- final_chunk["usage"] = usage
960
- last_usage = self._extract_last_usage(result)
961
- if last_usage:
962
- final_chunk.setdefault("metadata", {})["last_usage"] = last_usage
963
- yield final_chunk
964
- return
965
-
966
- try:
967
- stream_input = (
968
- self._checkpoint_resume_input(
969
- state,
970
- payload_provided=resume_payload_provided,
971
- interrupt_id=resume_interrupt_id,
972
- )
973
- if is_checkpoint_resume
974
- else (Command(resume=state) if is_resume else state)
975
- )
976
- # stream_mode 含 "custom" 才会产生 on_custom_stream 事件(custom writer);
977
- # 保留默认 "values" 以兼容既有 on_chain_end/graph_update 消费。
978
- stream_kwargs = {"version": "v2", "config": config}
979
- if self._callable_accepts_keyword(self._agent.astream_events, "stream_mode"):
980
- stream_kwargs["stream_mode"] = ["values", "custom"]
981
- if native_context and self._callable_accepts_keyword(
982
- self._agent.astream_events, "context"
983
- ):
984
- stream_kwargs["context"] = native_context
985
- async for event in self._agent.astream_events(stream_input, **stream_kwargs):
986
- event_kind = event.get("event", "")
987
-
988
- if event_kind == "on_chat_model_stream":
989
- chunk = event.get("data", {}).get("chunk")
990
- if not chunk:
991
- continue
992
- chunk_usage = self._extract_usage(chunk)
993
- if chunk_usage:
994
- # Some LangChain providers attach cumulative usage to
995
- # every stream chunk, and LangChain may then sum those
996
- # cumulative snapshots into an inflated
997
- # on_chat_model_end usage. For a concrete model run,
998
- # keep the latest stream snapshot and ignore the later
999
- # end usage for that same run_id.
1000
- latest_stream_usage = dict(chunk_usage)
1001
- if event.get("run_id"):
1002
- run_key = model_run_key(event)
1003
- stream_usage_run_keys.add(run_key)
1004
- record_model_usage(event, latest_stream_usage)
1005
-
1006
- # 推理内容
1007
- reasoning = getattr(chunk, "reasoning_content", None)
1008
- if not reasoning and hasattr(chunk, "additional_kwargs"):
1009
- reasoning = chunk.additional_kwargs.get("reasoning_content")
1010
-
1011
- if reasoning:
1012
- accumulated_reasoning += reasoning
1013
- yield {"delta": reasoning, "type": "thinking"}
1014
-
1015
- # 常规内容
1016
- if hasattr(chunk, "content") and chunk.content:
1017
- content = self._filter_tool_tags(chunk.content)
1018
- if isinstance(content, str):
1019
- if accumulated_reasoning and content.startswith(accumulated_reasoning):
1020
- content = content[len(accumulated_reasoning) :]
1021
- elif reasoning and content.startswith(reasoning):
1022
- content = content[len(reasoning) :]
1023
- if content:
1024
- for part in inline_reasoning_parser.feed(content):
1025
- if not part.text or not part.text.strip():
1026
- continue
1027
- if part.kind == "thinking":
1028
- accumulated_reasoning += part.text
1029
- yield {"delta": part.text, "type": "thinking"}
1030
- else:
1031
- accumulated_text += part.text
1032
- yield {"delta": part.text, "type": "text"}
1033
-
1034
- elif event_kind == "on_chat_model_end":
1035
- data = event.get("data") or {}
1036
- output = data.get("output") if isinstance(data, Mapping) else None
1037
- usage = self._extract_usage(output) or self._extract_usage(data)
1038
- last_usage = self._extract_last_usage(output) or self._extract_last_usage(data)
1039
- run_key = model_run_key(event)
1040
- if run_key not in stream_usage_run_keys:
1041
- record_model_usage(event, last_usage or usage)
1042
-
1043
- elif event_kind == "on_chain_stream":
1044
- # node 内 get_stream_writer() 写入的自定义数据,经 stream_mode 含
1045
- # "custom" 时,astream_events 包成 on_chain_stream,chunk 为
1046
- # (mode, value) tuple:("custom", value) 是 writer 透传内容,
1047
- # ("values", state) 是 state 快照(忽略,终态走 on_chain_end)。
1048
- # 编排方常用 custom writer 把"调远端 agent/子图"的流式增量透传出来。
1049
- chunk = event.get("data", {}).get("chunk")
1050
- if not (
1051
- isinstance(chunk, tuple) and len(chunk) == 2 and chunk[0] == "custom"
1052
- ):
1053
- continue
1054
- data = chunk[1]
1055
- if isinstance(data, str):
1056
- accumulated_text += data
1057
- yield {"delta": data, "type": "text"}
1058
- continue
1059
- if isinstance(data, Mapping):
1060
- custom_type = str(data.get("type") or "text")
1061
- if custom_type in ("tool_call", "tool_result"):
1062
- # 结构化工具事件:透传完整 payload(tool_name/tool_args/
1063
- # tool_output 等),不计入正文,供 UI 渲染工具卡片。
1064
- out = {"type": custom_type}
1065
- out.update({k: v for k, v in data.items() if k != "type"})
1066
- yield out
1067
- continue
1068
- custom_delta = ""
1069
- for key in ("delta", "text", "content", "output", "data"):
1070
- value = data.get(key)
1071
- if isinstance(value, str) and value:
1072
- custom_delta = value
1073
- break
1074
- if not custom_delta:
1075
- continue
1076
- replace = bool(data.get("replace"))
1077
- if custom_type == "thinking":
1078
- accumulated_reasoning = (
1079
- custom_delta
1080
- if replace
1081
- else accumulated_reasoning + custom_delta
1082
- )
1083
- else:
1084
- accumulated_text = (
1085
- custom_delta if replace else accumulated_text + custom_delta
1086
- )
1087
- out = {"delta": custom_delta, "type": custom_type}
1088
- if replace:
1089
- out["replace"] = True
1090
- yield out
1091
- continue
1092
- if data is not None:
1093
- accumulated_text += str(data)
1094
- yield {"delta": str(data), "type": "text"}
1095
-
1096
- elif event_kind == "on_tool_start":
1097
- emitted_non_text_event = True
1098
- yield {
1099
- "type": "tool_call",
1100
- "tool_name": event.get("name", "unknown"),
1101
- "tool_args": event.get("data", {}).get("input", {}),
1102
- "run_id": event.get("run_id"),
1103
- }
1104
-
1105
- elif event_kind == "on_tool_end":
1106
- emitted_non_text_event = True
1107
- tool_output = event.get("data", {}).get("output", "")
1108
- # LangGraph returns a ToolMessage here for normal tools.
1109
- # Preserve its content instead of serializing the repr,
1110
- # otherwise structured output such as A2UI envelopes becomes
1111
- # unparsable. Keep the callback run_id below: it is paired
1112
- # with the preceding ``on_tool_start`` event on this stream.
1113
- normalized_output = getattr(tool_output, "content", tool_output)
1114
- if isinstance(tool_output, Mapping) and "content" in tool_output:
1115
- normalized_output = tool_output["content"]
1116
- yield {
1117
- "type": "tool_result",
1118
- "tool_name": event.get("name", "unknown"),
1119
- "tool_args": event.get("data", {}).get("input", {}),
1120
- "tool_output": normalized_output,
1121
- "run_id": event.get("run_id"),
1122
- }
1123
-
1124
- elif event_kind == "on_chain_end":
1125
- output = event.get("data", {}).get("output", {})
1126
- if isinstance(output, dict) and "__interrupt__" in output:
1127
- emitted_non_text_event = True
1128
- yield {
1129
- "type": "interrupt",
1130
- "interrupt_info": output["__interrupt__"],
1131
- "session_id": session_id,
1132
- }
1133
- return
1134
- extracted_output = self._extract_output(output)
1135
- if extracted_output:
1136
- final_output_text = strip_reasoning_markup(str(extracted_output))
1137
- final_output_usage = self._extract_usage(output)
1138
- final_output_last_usage = self._extract_last_usage(output)
1139
-
1140
- except Exception as e:
1141
- if "Interrupt" in type(e).__name__:
1142
- yield {
1143
- "type": "interrupt",
1144
- "interrupt_info": self._get_interrupt_info(self._agent.get_state(config)),
1145
- "session_id": session_id,
1146
- }
1147
- return
1148
- raise
1149
-
1150
- # goal-18(ksadk-web 人机交互):图因审批门(HITL)在流式中静默暂停时,
1151
- # 这里把审批详情(action_requests)作为 approval 事件冒出,供 UI 渲染审批卡。
1152
- # 此前流式路径只在 checkpoint 标 resumable,UI 拿不到"该批哪个工具/什么参数/允许哪些决定"。
1153
- # 注:get_state 在部分 agent 上是 async,统一按 awaitable 处理;取不到则跳过,不破坏事件流。
1154
- pending_approval = None
1155
- try:
1156
- _get_state = getattr(self._agent, "aget_state", None) or getattr(
1157
- self._agent, "get_state", None
1158
- )
1159
- if _get_state is not None:
1160
- _maybe_state = _get_state(config)
1161
- if inspect.isawaitable(_maybe_state):
1162
- _maybe_state = await _maybe_state
1163
- pending_approval = self._get_interrupt_info(_maybe_state)
1164
- except Exception:
1165
- pending_approval = None
1166
- if pending_approval:
1167
- yield {
1168
- "type": "approval",
1169
- "interrupt_info": pending_approval,
1170
- "session_id": session_id,
1171
- }
1172
- metadata = await self._latest_checkpoint_metadata(config)
1173
- if metadata:
1174
- yield {"type": "checkpoint", "metadata": metadata}
1175
- return
1176
-
1177
- for part in inline_reasoning_parser.flush():
1178
- if not part.text or not part.text.strip():
1179
- continue
1180
- if part.kind == "thinking":
1181
- accumulated_reasoning += part.text
1182
- yield {"delta": part.text, "type": "thinking"}
1183
- else:
1184
- accumulated_text += part.text
1185
- yield {"delta": part.text, "type": "text"}
1186
-
1187
- if not accumulated_text:
1188
- if final_output_text:
1189
- final_chunk = {"output": final_output_text, "type": "final"}
1190
- usage = accumulated_model_usage() or final_output_usage or latest_stream_usage
1191
- last_usage = (
1192
- latest_model_usage() or final_output_last_usage or latest_stream_usage or usage
1193
- )
1194
- if usage:
1195
- final_chunk["usage"] = usage
1196
- if last_usage:
1197
- final_chunk.setdefault("metadata", {})["last_usage"] = last_usage
1198
- yield final_chunk
1199
- elif not emitted_non_text_event:
1200
- result = await self.invoke({**invoke_payload, "_ksadk_force_graph_invoke": True})
1201
- fallback_chunk: dict[str, Any] = {
1202
- "output": result.get("output", ""),
1203
- "type": "final",
1204
- }
1205
- usage = self._extract_usage(result)
1206
- if usage:
1207
- fallback_chunk["usage"] = usage
1208
- last_usage = self._extract_last_usage(result)
1209
- if last_usage:
1210
- fallback_chunk.setdefault("metadata", {})["last_usage"] = last_usage
1211
- yield fallback_chunk
1212
- checkpoint_metadata = result.get("metadata") if isinstance(result, dict) else None
1213
- if isinstance(checkpoint_metadata, dict) and checkpoint_metadata.get("agentengine"):
1214
- yield {"type": "checkpoint", "metadata": checkpoint_metadata}
1215
- return
1216
- else:
1217
- final_chunk = {"output": accumulated_text, "type": "final"}
1218
- state_usage = await self._latest_state_usage(config)
1219
- usage = (
1220
- accumulated_model_usage()
1221
- or state_usage
1222
- or final_output_usage
1223
- or latest_stream_usage
1224
- )
1225
- if usage:
1226
- final_chunk["usage"] = usage
1227
- last_usage = (
1228
- latest_model_usage()
1229
- or state_usage
1230
- or final_output_last_usage
1231
- or latest_stream_usage
1232
- or usage
1233
- )
1234
- final_chunk.setdefault("metadata", {})["last_usage"] = last_usage
1235
- yield final_chunk
1236
-
1237
- metadata = await self._latest_checkpoint_metadata(config)
1238
- if metadata:
1239
- yield {"type": "checkpoint", "metadata": metadata}
1240
-
1241
1054
  def _filter_tool_tags(self, content: str) -> str:
1242
- """过滤 <tool_call> 标签"""
1055
+ """过滤完整的 XML tool_call 标签。"""
1243
1056
  if not isinstance(content, str):
1244
1057
  return content
1245
1058
  content = re.sub(r"<tool_call>.*?</tool_call>", "", content, flags=re.DOTALL)
1246
1059
  content = re.sub(r"</?(?:tool_call|arg_key|arg_value)>", "", content)
1247
1060
  return content
1061
+
1062
+ async def stream(self, input_data: Dict[str, Any]) -> AsyncIterator[Dict[str, Any]]:
1063
+ """Prepare the managed checkpoint before yielding the first event."""
1064
+ await self.prepare_runtime_capabilities()
1065
+ async for event in super().stream(input_data):
1066
+ yield event