agentengine-sdk-python 0.8.1__py3-none-any.whl → 0.8.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (411) hide show
  1. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/METADATA +22 -2
  2. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/RECORD +356 -222
  3. ksadk/a2a/_space_client_events.py +381 -0
  4. ksadk/a2a/event_adapter.py +156 -64
  5. ksadk/a2a/executor.py +179 -63
  6. ksadk/a2a/space_client.py +6 -333
  7. ksadk/a2ui/core.py +222 -53
  8. ksadk/agui/_agent_helpers.py +243 -0
  9. ksadk/agui/a2ui_projection.py +12 -5
  10. ksadk/agui/agent.py +443 -259
  11. ksadk/api/client.py +525 -27
  12. ksadk/builders/code_builder.py +284 -8
  13. ksadk/builders/container_builder.py +8 -1
  14. ksadk/builders/managed_runtime_builder.py +39 -35
  15. ksadk/cli/__init__.py +17 -0
  16. ksadk/cli/cmd_dashboard.py +15 -1
  17. ksadk/cli/cmd_deploy.py +10 -2
  18. ksadk/cli/cmd_eval.py +66 -9
  19. ksadk/cli/cmd_evalset.py +284 -0
  20. ksadk/cli/cmd_files.py +5 -1
  21. ksadk/cli/cmd_hermes.py +1 -126
  22. ksadk/cli/cmd_invoke.py +28 -38
  23. ksadk/cli/cmd_managed_runtime.py +95 -0
  24. ksadk/cli/cmd_observe.py +85 -0
  25. ksadk/cli/cmd_openclaw.py +1 -435
  26. ksadk/cli/cmd_replay.py +28 -4
  27. ksadk/cli/cmd_studio.py +99 -8
  28. ksadk/cli/cmd_web.py +10 -2
  29. ksadk/cli/hermes_env.py +151 -0
  30. ksadk/cli/invoke_payload.py +49 -0
  31. ksadk/cli/openclaw_env.py +458 -0
  32. ksadk/codex/client.py +180 -30
  33. ksadk/codex/runtime.py +428 -326
  34. ksadk/configs/env_registry.py +66 -12
  35. ksadk/configs/env_registry_pcm.py +181 -0
  36. ksadk/configs/env_var_spec.py +18 -0
  37. ksadk/configs/global_config.py +5 -0
  38. ksadk/context_engine/__init__.py +98 -0
  39. ksadk/context_engine/assembler.py +176 -0
  40. ksadk/context_engine/baseline.py +419 -0
  41. ksadk/context_engine/cache_observability.py +228 -0
  42. ksadk/context_engine/capabilities.py +443 -0
  43. ksadk/context_engine/contributors.py +344 -0
  44. ksadk/context_engine/hosted_pipeline.py +417 -0
  45. ksadk/context_engine/models.py +117 -0
  46. ksadk/context_engine/planner.py +698 -0
  47. ksadk/context_engine/policies.py +319 -0
  48. ksadk/context_engine/projection.py +31 -0
  49. ksadk/context_engine/shadow_plan.py +266 -0
  50. ksadk/context_engine/tokenizer.py +164 -0
  51. ksadk/conversations/context.py +126 -6
  52. ksadk/conversations/message_projection.py +292 -28
  53. ksadk/conversations/model_context.py +49 -3
  54. ksadk/conversations/runtime_compaction.py +207 -5
  55. ksadk/conversations/runtime_input.py +184 -2
  56. ksadk/conversations/runtime_invocation.py +81 -3
  57. ksadk/conversations/runtime_observability.py +141 -0
  58. ksadk/conversations/runtime_payloads.py +42 -0
  59. ksadk/conversations/runtime_preparation.py +272 -2
  60. ksadk/conversations/runtime_resume.py +20 -1
  61. ksadk/conversations/runtime_stream_events.py +94 -9
  62. ksadk/conversations/semantic_summary.py +311 -1
  63. ksadk/conversations/session_lock.py +51 -0
  64. ksadk/deployment/env_forward.py +72 -0
  65. ksadk/deployment/managed_runtime.py +5 -0
  66. ksadk/deployment/providers/serverless.py +143 -69
  67. ksadk/evaluation/__init__.py +32 -0
  68. ksadk/evaluation/a2a_adapter.py +9 -1
  69. ksadk/evaluation/adapters.py +18 -5
  70. ksadk/evaluation/agent_eval_client.py +395 -0
  71. ksadk/evaluation/cloud_binding.py +80 -0
  72. ksadk/evaluation/cloud_converter.py +194 -0
  73. ksadk/evaluation/cloud_service.py +199 -0
  74. ksadk/evaluation/contracts.py +73 -4
  75. ksadk/evaluation/evaluators.py +183 -17
  76. ksadk/evaluation/evidence.py +258 -0
  77. ksadk/evaluation/executor.py +31 -6
  78. ksadk/evaluation/local_adapter.py +550 -0
  79. ksadk/evaluation/service_env.py +27 -0
  80. ksadk/evaluation/studio_build_adapter.py +253 -0
  81. ksadk/evaluation/target.py +15 -6
  82. ksadk/events/__init__.py +13 -13
  83. ksadk/events/_v1_compat/__init__.py +1 -0
  84. ksadk/events/_v1_compat/models.py +299 -0
  85. ksadk/events/_v1_compat/parser.py +247 -0
  86. ksadk/events/_v1_compat/projection.py +753 -0
  87. ksadk/events/adapters/__init__.py +34 -0
  88. ksadk/events/adapters/_a2a_snapshot.py +622 -0
  89. ksadk/events/adapters/_a2a_support.py +275 -0
  90. ksadk/events/adapters/_codex_interactions.py +375 -0
  91. ksadk/events/adapters/_codex_items.py +801 -0
  92. ksadk/events/adapters/_codex_validators.py +311 -0
  93. ksadk/events/adapters/_langgraph_support.py +785 -0
  94. ksadk/events/adapters/a2a.py +841 -0
  95. ksadk/events/adapters/adk.py +503 -0
  96. ksadk/events/adapters/codex.py +717 -0
  97. ksadk/events/adapters/langgraph.py +745 -0
  98. ksadk/events/canonical.py +440 -0
  99. ksadk/events/canonical_replay.py +497 -0
  100. ksadk/events/canonical_store.py +468 -0
  101. ksadk/events/cold_recovery.py +257 -0
  102. ksadk/events/content.py +89 -0
  103. ksadk/events/identity.py +86 -0
  104. ksadk/events/pipeline.py +455 -0
  105. ksadk/events/projections.py +66 -0
  106. ksadk/events/reducer.py +661 -0
  107. ksadk/events/replay.py +3 -36
  108. ksadk/events/runtime_event.py +20 -265
  109. ksadk/events/session_event.py +324 -0
  110. ksadk/events/store.py +3 -316
  111. ksadk/events/v1_compat.py +41 -0
  112. ksadk/harness/runtime.py +160 -57
  113. ksadk/identity/resolver.py +4 -3
  114. ksadk/interaction/__init__.py +28 -0
  115. ksadk/interaction/contracts.py +136 -0
  116. ksadk/interaction/ledger.py +189 -0
  117. ksadk/interaction/provider.py +122 -0
  118. ksadk/interaction/providers/__init__.py +52 -0
  119. ksadk/interaction/providers/adk.py +40 -0
  120. ksadk/interaction/providers/codex.py +94 -0
  121. ksadk/interaction/providers/langgraph.py +58 -0
  122. ksadk/kernel/__init__.py +117 -0
  123. ksadk/kernel/authorization.py +258 -0
  124. ksadk/kernel/bootstrap.py +1074 -0
  125. ksadk/kernel/contract_fingerprints.py +61 -0
  126. ksadk/kernel/contracts.py +362 -0
  127. ksadk/kernel/control.py +219 -0
  128. ksadk/kernel/errors.py +79 -0
  129. ksadk/kernel/ingress.py +1102 -0
  130. ksadk/kernel/mapping.py +111 -0
  131. ksadk/kernel/memory_store.py +1133 -0
  132. ksadk/kernel/postgres_store.py +1756 -0
  133. ksadk/kernel/recovery.py +452 -0
  134. ksadk/kernel/runtime_identity.py +103 -0
  135. ksadk/kernel/sql/001_agent_kernel.sql +120 -0
  136. ksadk/kernel/sqlite_store.py +1410 -0
  137. ksadk/kernel/state.py +145 -0
  138. ksadk/kernel/store.py +232 -0
  139. ksadk/kernel/worker.py +953 -0
  140. ksadk/knowledge_base/client.py +7 -0
  141. ksadk/knowledge_base/service.py +32 -5
  142. ksadk/memory/__init__.py +61 -0
  143. ksadk/memory/adk/backends/base_ltm_backend.py +96 -1
  144. ksadk/memory/adk/backends/http_ltm_backend.py +14 -5
  145. ksadk/memory/adk/backends/inmemory_ltm_backend.py +136 -3
  146. ksadk/memory/adk/backends/sdk_ltm_backend.py +384 -2
  147. ksadk/memory/adk/backends/sqlite_ltm_backend.py +95 -0
  148. ksadk/memory/coordinator.py +461 -0
  149. ksadk/memory/events.py +202 -0
  150. ksadk/memory/extraction.py +228 -0
  151. ksadk/memory/ltm_backend_factory.py +16 -3
  152. ksadk/memory/models.py +316 -0
  153. ksadk/memory/policy.py +215 -0
  154. ksadk/memory/provider.py +47 -0
  155. ksadk/memory/provider_adapter.py +159 -0
  156. ksadk/memory/provider_resolver.py +71 -0
  157. ksadk/memory/providers/__init__.py +5 -0
  158. ksadk/memory/providers/local_sqlite.py +490 -0
  159. ksadk/memory/resolved_policy.py +145 -0
  160. ksadk/memory/service.py +188 -6
  161. ksadk/model_proxy/bootstrap.py +16 -9
  162. ksadk/model_proxy/detect.py +202 -9
  163. ksadk/model_proxy/namespace.py +39 -15
  164. ksadk/model_proxy/server.py +24 -5
  165. ksadk/model_proxy/transform.py +60 -2
  166. ksadk/observability/__init__.py +25 -0
  167. ksadk/observability/session_log.py +420 -0
  168. ksadk/observability/trajectory.py +105 -0
  169. ksadk/prompts/__init__.py +71 -0
  170. ksadk/prompts/compiler.py +215 -0
  171. ksadk/prompts/models.py +82 -0
  172. ksadk/prompts/projection.py +89 -0
  173. ksadk/prompts/resolved.py +137 -0
  174. ksadk/prompts/sources.py +233 -0
  175. ksadk/runners/_langgraph_runner_streams.py +822 -0
  176. ksadk/runners/adk_runner.py +5 -5
  177. ksadk/runners/base_runner.py +16 -0
  178. ksadk/runners/factory.py +1 -2
  179. ksadk/runners/langgraph_runner.py +198 -437
  180. ksadk/runtime/_runner_adapter/__init__.py +1 -0
  181. ksadk/runtime/_runner_adapter/stream_mapping.py +788 -0
  182. ksadk/runtime/adapter.py +118 -2
  183. ksadk/runtime/conversation_execution.py +316 -182
  184. ksadk/runtime/executor.py +144 -3
  185. ksadk/runtime/factory.py +193 -7
  186. ksadk/runtime/hosted_finalizer.py +268 -0
  187. ksadk/runtime/launch.py +14 -1
  188. ksadk/runtime/preprocessing.py +25 -3
  189. ksadk/runtime/runner_adapter.py +288 -508
  190. ksadk/runtime/usage.py +33 -0
  191. ksadk/sandbox/backends/e2b.py +35 -2
  192. ksadk/server/composition.py +8 -0
  193. ksadk/server/factory.py +69 -6
  194. ksadk/server/routes/kernel_ingress.py +258 -0
  195. ksadk/server/routes/openai_compat.py +88 -8
  196. ksadk/server/routes/projection.py +35 -20
  197. ksadk/server/routes/run.py +94 -0
  198. ksadk/server/routes/sessions.py +2 -0
  199. ksadk/server/routes/streaming.py +5 -2
  200. ksadk/server/static/assets/{ArtifactsPanel-CakCqxEN.js → ArtifactsPanel-DSU1sWD_.js} +1 -1
  201. ksadk/server/static/assets/{CodeBlock-CynLknyO.js → CodeBlock-DOH6MVcn.js} +4 -4
  202. ksadk/server/static/assets/{MathMessageMarkdown-3sQdOysS.js → MathMessageMarkdown-BL8my1R2.js} +4 -4
  203. ksadk/server/static/assets/MathMessageMarkdown-BorAY7qD.css +1 -0
  204. ksadk/server/static/assets/MermaidBlock-Dz4IP-Tx.js +312 -0
  205. ksadk/server/static/assets/NativeTerminalPanel-DUrK0JpZ.js +1 -0
  206. ksadk/server/static/assets/abnfDiagram-VCTEODGH-g20pFzNV.js +1 -0
  207. ksadk/server/static/assets/arc-C4FzinUA.js +1 -0
  208. ksadk/server/static/assets/architecture-7GRP2DOG-DjKratdf.js +1 -0
  209. ksadk/server/static/assets/architectureDiagram-5GKGNRK7-DpIqL5h4.js +36 -0
  210. ksadk/server/static/assets/blockDiagram-NRAW4CY4-BjkljTNz.js +129 -0
  211. ksadk/server/static/assets/c4Diagram-UCG6FXSJ-Dk_ieq2X.js +38 -0
  212. ksadk/server/static/assets/channel-4cQHKtx1.js +1 -0
  213. ksadk/server/static/assets/chunk-2Q5K7J3B-DmgWkESh.js +1 -0
  214. ksadk/server/static/assets/{chunk-FMBD7UC4-B_fH2YuH.js → chunk-5VM5RSS4-BVHAchFb.js} +1 -1
  215. ksadk/server/static/assets/{chunk-ND2GUHAM-D6rFLaVV.js → chunk-F27PBJKO-C-ipQzuS.js} +1 -1
  216. ksadk/server/static/assets/chunk-G27WJ6UU-C2EyJbIK.js +231 -0
  217. ksadk/server/static/assets/{chunk-4BX2VUAB-B9drTgOW.js → chunk-JWPE2WC7-vYvVJb_M.js} +1 -1
  218. ksadk/server/static/assets/chunk-LCL6LL3I-BcI6ysPT.js +206 -0
  219. ksadk/server/static/assets/chunk-POPQ4Y6H-C030x_Z1.js +1 -0
  220. ksadk/server/static/assets/chunk-RHFEMEQ7-yyGpsz4n.js +168 -0
  221. ksadk/server/static/assets/chunk-SVP7TREG-BLTlmMU7.js +88 -0
  222. ksadk/server/static/assets/chunk-XXDRQBXY-C_32ArgP.js +1 -0
  223. ksadk/server/static/assets/classDiagram-DTDB5LWJ-DLFd9Xi0.js +1 -0
  224. ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-DLFd9Xi0.js +1 -0
  225. ksadk/server/static/assets/{cose-bilkent-S5V4N54A-ChNs_Mdk.js → cose-bilkent-JH36ORCC-9YQYwdl0.js} +1 -1
  226. ksadk/server/static/assets/cynefin-OW5HDTMX-DjEM48Qp.js +1 -0
  227. ksadk/server/static/assets/cynefinDiagram-5FMLGOSQ-ErLF5N13.js +62 -0
  228. ksadk/server/static/assets/cytoscape.esm-CyCl8rPi.js +321 -0
  229. ksadk/server/static/assets/dagre-3AP2YEHR-DFiQF-6f.js +4 -0
  230. ksadk/server/static/assets/dagre-BuhGnRI1.js +1 -0
  231. ksadk/server/static/assets/diagram-S7CK7UJ4-Bt1v8-GC.js +30 -0
  232. ksadk/server/static/assets/diagram-UQ7AKVKN-DSCxJdBK.js +41 -0
  233. ksadk/server/static/assets/diagram-VSXAHHWV-DHfYwp_9.js +3 -0
  234. ksadk/server/static/assets/diagram-VX7I27RA-DdzD-4Le.js +24 -0
  235. ksadk/server/static/assets/diagram-Z3DM3KII-Du-nAJ9F.js +24 -0
  236. ksadk/server/static/assets/{dist-Dli7qW-B.js → dist-8hqcx0sU.js} +1 -1
  237. ksadk/server/static/assets/dist-B7seoj3d.js +1 -0
  238. ksadk/server/static/assets/{dist-C1sz5Eo3.js → dist-BE7bu-DL.js} +1 -1
  239. ksadk/server/static/assets/{dist-fhOuWds5.js → dist-BVDffZ0U.js} +1 -1
  240. ksadk/server/static/assets/{dist-BjOdlDjj.js → dist-BX8z72IC.js} +1 -1
  241. ksadk/server/static/assets/{dist-DsHPdk3W.js → dist-Bf2M8m3N.js} +1 -1
  242. ksadk/server/static/assets/{dist-o2OVmKvt.js → dist-BjU6y-A2.js} +1 -1
  243. ksadk/server/static/assets/{dist-DWKByxWV.js → dist-BytlxIDT.js} +1 -1
  244. ksadk/server/static/assets/{dist-DJN_6Y39.js → dist-CSh_DE14.js} +1 -1
  245. ksadk/server/static/assets/{dist-DQU9EWw-.js → dist-CXYYBw3_.js} +1 -1
  246. ksadk/server/static/assets/{dist-4N2NMVYg.js → dist-C_wsv-Qd.js} +1 -1
  247. ksadk/server/static/assets/{dist-gLnrGsoU.js → dist-ClxGviKw.js} +1 -1
  248. ksadk/server/static/assets/{dist-j6Dxmf_j.js → dist-CttYUqzS.js} +1 -1
  249. ksadk/server/static/assets/{dist-D6MpCZtz.js → dist-DNp6rLRA.js} +1 -1
  250. ksadk/server/static/assets/{dist-BB3-OwTQ.js → dist-DUX-id_F.js} +1 -1
  251. ksadk/server/static/assets/{dist-_pHPTyZ-.js → dist-DmldrHd_.js} +1 -1
  252. ksadk/server/static/assets/{dist-CWV4lDr1.js → dist-DnfXs8Vn.js} +2 -2
  253. ksadk/server/static/assets/{dist-B5oS1zcj.js → dist-Dq9gLD7L.js} +1 -1
  254. ksadk/server/static/assets/{dist-B-jNR6Vn.js → dist-W-TIVntx.js} +1 -1
  255. ksadk/server/static/assets/{dist-BIlKoW0k.js → dist-dDdADKFA.js} +1 -1
  256. ksadk/server/static/assets/{dist-X4TnqwOK.js → dist-dydu68Fl.js} +1 -1
  257. ksadk/server/static/assets/{dist-CmZHzlwZ.js → dist-ngtN0DJB.js} +1 -1
  258. ksadk/server/static/assets/ebnfDiagram-PWID7BFC-Da9C9NO1.js +1 -0
  259. ksadk/server/static/assets/erDiagram-SSCWMZ5O-EAGqqR79.js +99 -0
  260. ksadk/server/static/assets/eventmodeling-NTZA5JFV-CMgMaJrC.js +1 -0
  261. ksadk/server/static/assets/flowDiagram-A5DVABFB-BBKL0x3P.js +1 -0
  262. ksadk/server/static/assets/ganttDiagram-EL5Y4UJY-DqJsKb59.js +292 -0
  263. ksadk/server/static/assets/gitGraph-4MIJSDKK-Dw63mrhw.js +1 -0
  264. ksadk/server/static/assets/gitGraphDiagram-WWUBYQGX-B6g4dtDi.js +106 -0
  265. ksadk/server/static/assets/index-8ipRcQ-M.js +240 -0
  266. ksadk/server/static/assets/index-ByFCqjlh.css +2 -0
  267. ksadk/server/static/assets/info-A6RAGUB7-B6iAblII.js +1 -0
  268. ksadk/server/static/assets/infoDiagram-RXCK75RN-Djm_nbd5.js +2 -0
  269. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-K0OOfVna.js → ishikawaDiagram-5VMMS53U-DWya9SG2.js} +6 -6
  270. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-8LeOJaZp.js → journeyDiagram-EYS64GPL-DutxhSNI.js} +6 -6
  271. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-C6uCgZrW.js → kanban-definition-3QL26DDD-DJdOF8Fm.js} +10 -10
  272. ksadk/server/static/assets/katex-vFWytM5c.js +257 -0
  273. ksadk/server/static/assets/{linear-hdBcYzM8.js → linear-BHjG8b-J.js} +1 -1
  274. ksadk/server/static/assets/mermaid-parser.core-KGSy4jWT.js +166 -0
  275. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-Bjy_2v4_.js → mindmap-definition-FBJOCRG2-9-HC88D4.js} +29 -29
  276. ksadk/server/static/assets/packet-AYTQ26CC-BOYAipuq.js +1 -0
  277. ksadk/server/static/assets/pegDiagram-XKGWAZYB-DrEZd4fD.js +1 -0
  278. ksadk/server/static/assets/pie-WAS4IAKB-Ctq0Kbku.js +1 -0
  279. ksadk/server/static/assets/pieDiagram-E7YTZNPT-DpjqOFFp.js +39 -0
  280. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-BmkRbwCg.js → quadrantDiagram-AXDQQJYC-BiNhgOXX.js} +6 -6
  281. ksadk/server/static/assets/radar-RG4KPBEZ-DN0-ul_0.js +1 -0
  282. ksadk/server/static/assets/railroad-74A4TZTK-C6cC_to0.js +1 -0
  283. ksadk/server/static/assets/railroad-abnf-HS5TGJTU-BimjIuRc.js +1 -0
  284. ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-CvY65ABq.js +1 -0
  285. ksadk/server/static/assets/railroad-peg-WCYAUIDC-D72Iq_Tv.js +1 -0
  286. ksadk/server/static/assets/railroadDiagram-O6MQD6OU-_Y3Ojg6G.js +1 -0
  287. ksadk/server/static/assets/requirementDiagram-EFPCY7ZU-DeCFdg9K.js +84 -0
  288. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-l6iXmU65.js → sankeyDiagram-P5KCCOFB-CPH75rhw.js} +7 -7
  289. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-Cq6UBeQB.js → sequenceDiagram-WJ2MYXX4-OEpQQdr4.js} +10 -10
  290. ksadk/server/static/assets/sizeCapture-X5ZJPWSS-heUErzhI.js +1 -0
  291. ksadk/server/static/assets/stateDiagram-HBIQ2CUA-DvA3jSMB.js +1 -0
  292. ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BgQY03nz.js +1 -0
  293. ksadk/server/static/assets/swimlanes-XN3QIQJK-CR-dfN_t.js +1 -0
  294. ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-DLVw3q5Q.js +8 -0
  295. ksadk/server/static/assets/{timeline-definition-PNZ67QCA-DEz24vV0.js → timeline-definition-24CTP7MA-DAVJf1TX.js} +7 -7
  296. ksadk/server/static/assets/treeView-Q6P3EWNA-BMpO0wsB.js +1 -0
  297. ksadk/server/static/assets/treemap-WGGIJYW6-Et0aqzU3.js +1 -0
  298. ksadk/server/static/assets/vennDiagram-4TSXK5OY-DwM8eDkh.js +34 -0
  299. ksadk/server/static/assets/wardley-WFR3VGLG-CbY0mhpl.js +1 -0
  300. ksadk/server/static/assets/wardleyDiagram-VM6X3IG4-DZL-Zz2t.js +78 -0
  301. ksadk/server/static/assets/xychartDiagram-S5SC5T6Z-Dy6Yh_hu.js +7 -0
  302. ksadk/server/static/index.html +2 -2
  303. ksadk/sessions/__init__.py +26 -9
  304. ksadk/sessions/_local_service_sync.py +688 -0
  305. ksadk/sessions/_local_tables.py +21 -0
  306. ksadk/sessions/_postgres_schema.py +305 -0
  307. ksadk/sessions/_postgres_tables.py +15 -0
  308. ksadk/sessions/base.py +80 -1
  309. ksadk/sessions/in_memory.py +53 -2
  310. ksadk/sessions/local_service.py +63 -604
  311. ksadk/sessions/postgres_service.py +58 -148
  312. ksadk/sessions/resilient.py +35 -1
  313. ksadk/studio/api.py +577 -25
  314. ksadk/studio/api_contracts.py +114 -5
  315. ksadk/studio/api_memory_routes.py +82 -0
  316. ksadk/studio/authoring.py +252 -27
  317. ksadk/studio/authoring_coordinator.py +799 -67
  318. ksadk/studio/builder.py +88 -11
  319. ksadk/studio/capabilities.py +17 -0
  320. ksadk/studio/cloud.py +1916 -95
  321. ksadk/studio/codex_agent_service.py +125 -14
  322. ksadk/studio/codex_authoring.py +469 -0
  323. ksadk/studio/codex_builder.py +138 -25
  324. ksadk/studio/codex_manifest.py +51 -22
  325. ksadk/studio/codex_run.py +46 -3
  326. ksadk/studio/compiler.py +1 -0
  327. ksadk/studio/contracts.py +114 -3
  328. ksadk/studio/event_store.py +25 -95
  329. ksadk/studio/framework_run.py +231 -3
  330. ksadk/studio/hosted_kernel.py +317 -0
  331. ksadk/studio/manifest_resolver.py +131 -0
  332. ksadk/studio/model_client.py +95 -9
  333. ksadk/studio/model_profile_service.py +1 -1
  334. ksadk/studio/operations.py +37 -5
  335. ksadk/studio/otel_trace.py +348 -42
  336. ksadk/studio/pcm_memory.py +85 -0
  337. ksadk/studio/repository.py +23 -2
  338. ksadk/studio/resource_catalog.py +43 -0
  339. ksadk/studio/run_service.py +563 -137
  340. ksadk/studio/runtime_source.py +42 -16
  341. ksadk/studio/service.py +1180 -72
  342. ksadk/studio/shared_web.py +32 -22
  343. ksadk/studio/static/assets/index-BLpcdrgW.css +1 -0
  344. ksadk/studio/static/assets/index-CYekR2Xv.js +260 -0
  345. ksadk/studio/static/index.html +2 -2
  346. ksadk/studio/templates.py +100 -8
  347. ksadk/studio/validator.py +19 -0
  348. ksadk/studio/workspace.py +30 -32
  349. ksadk/tracing/setup.py +304 -3
  350. ksadk/version.py +1 -1
  351. ksadk_runtime_common/schemas/runtime_event_v1.json +37 -0
  352. ksadk_runtime_common/schemas/runtime_event_v2.json +271 -0
  353. ksadk/events/parser.py +0 -191
  354. ksadk/server/static/assets/MathMessageMarkdown-DeYh8QPr.css +0 -1
  355. ksadk/server/static/assets/MermaidBlock-NERD_ef7.js +0 -303
  356. ksadk/server/static/assets/NativeTerminalPanel-CiHWf82q.js +0 -1
  357. ksadk/server/static/assets/arc-DrgjcKhl.js +0 -1
  358. ksadk/server/static/assets/architecture-7EHR7CIX-CpZLRCcY.js +0 -1
  359. ksadk/server/static/assets/architectureDiagram-3BPJPVTR-BXoVRbQ6.js +0 -36
  360. ksadk/server/static/assets/blockDiagram-GPEHLZMM-BnaB3Ywv.js +0 -132
  361. ksadk/server/static/assets/c4Diagram-AAUBKEIU-Dw4weZcp.js +0 -10
  362. ksadk/server/static/assets/channel-Dt3rQTFI.js +0 -1
  363. ksadk/server/static/assets/chunk-2J33WTMH-ChjpLK7j.js +0 -1
  364. ksadk/server/static/assets/chunk-55IACEB6-CK4ROATy.js +0 -1
  365. ksadk/server/static/assets/chunk-727SXJPM-B9KtTTgw.js +0 -206
  366. ksadk/server/static/assets/chunk-AQP2D5EJ-1O1mki3m.js +0 -231
  367. ksadk/server/static/assets/chunk-QZHKN3VN-iXEZtKQc.js +0 -1
  368. ksadk/server/static/assets/classDiagram-4FO5ZUOK-DIveV17Q.js +0 -1
  369. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-DIveV17Q.js +0 -1
  370. ksadk/server/static/assets/cytoscape.esm-DelgaX4f.js +0 -321
  371. ksadk/server/static/assets/dagre-BM42HDAG-D0QXg_MQ.js +0 -4
  372. ksadk/server/static/assets/dagre-Bx709z4p.js +0 -1
  373. ksadk/server/static/assets/diagram-2AECGRRQ-DBDDKwBQ.js +0 -43
  374. ksadk/server/static/assets/diagram-5GNKFQAL-BkwDOxmO.js +0 -10
  375. ksadk/server/static/assets/diagram-KO2AKTUF-Bv8waYvY.js +0 -3
  376. ksadk/server/static/assets/diagram-LMA3HP47-e1VECDdu.js +0 -24
  377. ksadk/server/static/assets/diagram-OG6HWLK6-Ds9adfRl.js +0 -24
  378. ksadk/server/static/assets/dist-B5J2uhNk.js +0 -1
  379. ksadk/server/static/assets/erDiagram-TEJ5UH35-BPItFKH6.js +0 -85
  380. ksadk/server/static/assets/eventmodeling-FCH6USID-DYFSxbqr.js +0 -1
  381. ksadk/server/static/assets/flowDiagram-I6XJVG4X-BbYB9w5Y.js +0 -162
  382. ksadk/server/static/assets/ganttDiagram-6RSMTGT7-TQXa0KXv.js +0 -292
  383. ksadk/server/static/assets/gitGraph-WXDBUCRP-DyNYp6BN.js +0 -1
  384. ksadk/server/static/assets/gitGraphDiagram-PVQCEYII-bKblCnpa.js +0 -106
  385. ksadk/server/static/assets/graphlib-B8gBHxth.js +0 -1
  386. ksadk/server/static/assets/index-R52pJJvg.js +0 -177
  387. ksadk/server/static/assets/index-jJr3_955.css +0 -2
  388. ksadk/server/static/assets/info-J43DQDTF-WUx7qTde.js +0 -1
  389. ksadk/server/static/assets/infoDiagram-5YYISTIA-BGVT7J_i.js +0 -2
  390. ksadk/server/static/assets/katex-CYWseY5E.js +0 -265
  391. ksadk/server/static/assets/mermaid-parser.core-BWrDjSZM.js +0 -161
  392. ksadk/server/static/assets/packet-YPE3B663-BN13y_xl.js +0 -1
  393. ksadk/server/static/assets/pie-LRSECV5Y-ftSLXhhc.js +0 -1
  394. ksadk/server/static/assets/pieDiagram-4H26LBE5-GmmqNiVH.js +0 -30
  395. ksadk/server/static/assets/radar-GUYGQ44K-jaICkIeO.js +0 -1
  396. ksadk/server/static/assets/requirementDiagram-4Y6WPE33-DSWFqL7c.js +0 -84
  397. ksadk/server/static/assets/stateDiagram-AJRCARHV-qzTQpg3Y.js +0 -1
  398. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-BYCoYGAN.js +0 -1
  399. ksadk/server/static/assets/treeView-BLDUP644-Cpib_vlz.js +0 -1
  400. ksadk/server/static/assets/treemap-LRROVOQU-DQxfmVHK.js +0 -1
  401. ksadk/server/static/assets/vennDiagram-CIIHVFJN-CMCixki2.js +0 -34
  402. ksadk/server/static/assets/wardley-L42UT6IY-BQYHnnkU.js +0 -1
  403. ksadk/server/static/assets/wardleyDiagram-YWT4CUSO-CVzB8F9K.js +0 -78
  404. ksadk/server/static/assets/xychartDiagram-2RQKCTM6-BFBqBLc1.js +0 -7
  405. ksadk/studio/evaluation.py +0 -192
  406. ksadk/studio/static/assets/index-CaEN0eTR.css +0 -1
  407. ksadk/studio/static/assets/index-DI2eY_NH.js +0 -252
  408. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/WHEEL +0 -0
  409. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/entry_points.txt +0 -0
  410. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/licenses/LICENSE +0 -0
  411. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,468 @@
1
+ """Durable schema-v2 RuntimeEvent storage on the existing session event log.
2
+
3
+ The canonical event envelope intentionally has no ``session_id``. Session
4
+ scope is therefore an explicit store argument and never hidden in source
5
+ metadata. The physical ``SessionEvent.id`` is a deterministic encoding of
6
+ ``(session_id, event_id)`` so the existing durable primary-key constraint can
7
+ enforce the canonical idempotency domain before a session cursor is allocated.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import asyncio
13
+ import hashlib
14
+ import json
15
+ import uuid
16
+ from collections.abc import AsyncIterator, Iterable
17
+ from datetime import datetime, timezone
18
+ from typing import TYPE_CHECKING, Any
19
+
20
+ from ksadk.events.canonical import RuntimeEvent, dump_runtime_event, parse_runtime_event
21
+ from ksadk.kernel.contracts import ActivationWriteGuard, SessionEventEnvelope
22
+ from ksadk.sessions.base import SessionEvent, SessionEventSeqBinding
23
+
24
+ if TYPE_CHECKING:
25
+ from ksadk.events.session_event import SessionEventStore
26
+
27
+ _CANONICAL_RUNTIME_MARKER = "ksadk_canonical_runtime_event"
28
+ _CANONICAL_CONTENT_KEY = "runtime_event"
29
+ _ENVELOPE_MARKER = "ksadk_session_event_envelope"
30
+ _TERMINAL_EVENT_TYPES = frozenset({"run.completed", "run.failed", "run.canceled"})
31
+
32
+ _REQUIRED_SEQ_BINDING: SessionEventSeqBinding = "runtime_event.seq"
33
+
34
+ # Stable namespace for mapping free-form RuntimeEvent event ids onto the
35
+ # UUID-typed ``SessionEventEnvelope.event_id`` contract.
36
+ _RUNTIME_EVENT_UUID_NAMESPACE = uuid.UUID("6e9f0c5a-2f4d-4d8a-9b31-1c2a5f7e9b41")
37
+
38
+
39
+ def runtime_event_envelope_id(session_id: str, event_id: str) -> uuid.UUID:
40
+ """UUID contract id for one runtime fact (deterministic per session)."""
41
+
42
+ try:
43
+ return uuid.UUID(str(event_id))
44
+ except (ValueError, AttributeError, TypeError):
45
+ return uuid.uuid5(_RUNTIME_EVENT_UUID_NAMESPACE, f"{session_id}|{event_id}")
46
+
47
+
48
+ def runtime_event_envelope(session_id: str, event: RuntimeEvent) -> SessionEventEnvelope:
49
+ """Lift one canonical RuntimeEvent fact into a family=runtime/v2 envelope."""
50
+
51
+ if getattr(event, "schema_version", None) != 2:
52
+ raise ValueError("canonical RuntimeEventStore accepts schema_version=2 only")
53
+ timestamp = datetime.fromtimestamp(event.timestamp, tz=timezone.utc).isoformat()
54
+ return SessionEventEnvelope(
55
+ event_id=runtime_event_envelope_id(session_id, event.event_id),
56
+ session_id=session_id,
57
+ seq=0, # overwritten with the store-allocated cursor on persistence
58
+ timestamp=timestamp,
59
+ family="runtime",
60
+ family_version=2,
61
+ event_type=event.event_type,
62
+ payload=dump_runtime_event(event),
63
+ run_id=event.run_id,
64
+ actor_ref=event.source.framework,
65
+ )
66
+
67
+
68
+ def canonical_storage_id(session_id: str, event_id: str) -> str:
69
+ """Return a stable physical id distinct from the producer event id."""
70
+
71
+ if not session_id.strip() or not event_id.strip():
72
+ raise ValueError("session_id and event_id must be nonempty")
73
+ encoded = json.dumps([session_id, event_id], ensure_ascii=False, separators=(",", ":")).encode(
74
+ "utf-8"
75
+ )
76
+ return f"cev_{hashlib.sha256(encoded).hexdigest()[:40]}"
77
+
78
+
79
+ def runtime_event_to_session_event(session_id: str, event: RuntimeEvent) -> SessionEvent:
80
+ """Pack one canonical fact into the existing free-form SessionEvent carrier."""
81
+
82
+ if getattr(event, "schema_version", None) != 2:
83
+ raise ValueError("canonical RuntimeEventStore accepts schema_version=2 only")
84
+ payload = dump_runtime_event(event)
85
+ return SessionEvent(
86
+ id=canonical_storage_id(session_id, event.event_id),
87
+ session_id=session_id,
88
+ author=event.source.framework,
89
+ event_type=event.event_type,
90
+ content={_CANONICAL_CONTENT_KEY: payload},
91
+ timestamp=event.timestamp,
92
+ invocation_id=event.run_id,
93
+ metadata={
94
+ _CANONICAL_RUNTIME_MARKER: True,
95
+ "schema_version": 2,
96
+ "canonical_event_id": event.event_id,
97
+ },
98
+ seq_binding=_REQUIRED_SEQ_BINDING,
99
+ seq_id=int(event.seq),
100
+ )
101
+
102
+
103
+ def session_event_to_runtime_event(event: SessionEvent) -> RuntimeEvent | None:
104
+ """Restore a canonical fact, using the physical session cursor as ``seq``."""
105
+
106
+ metadata = event.metadata or {}
107
+ if metadata.get(_ENVELOPE_MARKER) and metadata.get("family") == "runtime":
108
+ # Task 2 typed view rows: family=runtime/v2 written through the
109
+ # generic SessionEventStore envelope carrier.
110
+ payload = (event.content or {}).get(_CANONICAL_CONTENT_KEY)
111
+ if not isinstance(payload, dict):
112
+ raise ValueError("runtime family SessionEvent is missing runtime_event content")
113
+ if payload.get("seq") != event.seq_id:
114
+ raise ValueError("canonical RuntimeEvent seq does not match physical seq")
115
+ return parse_runtime_event(payload)
116
+ if not metadata.get(_CANONICAL_RUNTIME_MARKER):
117
+ return None
118
+ if metadata.get("schema_version") != 2:
119
+ raise ValueError("canonical SessionEvent marker requires schema_version=2")
120
+ stored_payload = (event.content or {}).get(_CANONICAL_CONTENT_KEY)
121
+ if not isinstance(stored_payload, dict):
122
+ raise ValueError("canonical SessionEvent is missing runtime_event content")
123
+ if stored_payload.get("seq") != event.seq_id:
124
+ raise ValueError("canonical RuntimeEvent seq does not match physical seq")
125
+ payload = dict(stored_payload)
126
+ restored = parse_runtime_event(payload)
127
+ canonical_event_id = str(metadata.get("canonical_event_id") or "")
128
+ if canonical_event_id != restored.event_id:
129
+ raise ValueError("canonical SessionEvent event id metadata does not match content")
130
+ expected_storage_id = canonical_storage_id(event.session_id, restored.event_id)
131
+ if event.id != expected_storage_id:
132
+ raise ValueError("canonical SessionEvent storage id does not match session event identity")
133
+ if event.invocation_id != restored.run_id or event.event_type != restored.event_type:
134
+ raise ValueError("canonical SessionEvent envelope does not match runtime event content")
135
+ return restored
136
+
137
+
138
+ def _is_session_event_store(candidate: Any) -> bool:
139
+ """Duck-type the generic SessionEventStore port without an import cycle."""
140
+
141
+ return all(
142
+ callable(getattr(candidate, name, None)) for name in ("append", "read", "subscribe")
143
+ )
144
+
145
+
146
+ class RuntimeEventStore:
147
+ """Schema-v2-only canonical store with durable session-scoped idempotency.
148
+
149
+ Task 2 起 ``RuntimeEventStore`` 是单一 SessionEvent Store 的 typed view:
150
+ 传入 ``SessionEventStore`` 时走 envelope 写路径(只接受
151
+ ``ActivationWriteGuard``),传 session service 时保持旧 carrier 兼容路径。
152
+ """
153
+
154
+ def __init__(self, store: Any, *, session_id: str | None = None) -> None:
155
+ if _is_session_event_store(store):
156
+ self._event_store: SessionEventStore | None = store
157
+ self._service = getattr(store, "session_service", None)
158
+ self._typed_session_id = session_id
159
+ else:
160
+ self._event_store = None
161
+ self._service = store
162
+ self._typed_session_id = session_id
163
+
164
+ @property
165
+ def session_service(self) -> Any:
166
+ return self._service
167
+
168
+ @property
169
+ def session_id(self) -> str | None:
170
+ """Session bound at construction for the typed (envelope) write path."""
171
+
172
+ return self._typed_session_id
173
+
174
+ @property
175
+ def event_store(self) -> "SessionEventStore | None":
176
+ return self._event_store
177
+
178
+ async def append(
179
+ self,
180
+ session_id_or_event: Any,
181
+ events: Iterable[RuntimeEvent] | None = None,
182
+ *,
183
+ guard: ActivationWriteGuard | None = None,
184
+ ) -> Any:
185
+ """Typed view append: ``append(event, *, guard=ActivationWriteGuard)``.
186
+
187
+ 旧签名 ``append(session_id, events)`` 保持兼容(carrier 路径)。
188
+ """
189
+
190
+ if not isinstance(session_id_or_event, str):
191
+ if events is not None:
192
+ raise TypeError("typed append takes a single RuntimeEvent")
193
+ if not isinstance(guard, ActivationWriteGuard):
194
+ raise TypeError(
195
+ "RuntimeEventStore typed append requires an ActivationWriteGuard"
196
+ )
197
+ return await self.append_typed(session_id_or_event, guard=guard)
198
+ if guard is not None:
199
+ raise TypeError("legacy append(session_id, events) does not take a guard")
200
+ return [await self.append_one(session_id_or_event, event) for event in events or ()]
201
+
202
+ async def append_typed(
203
+ self, event: RuntimeEvent, *, guard: ActivationWriteGuard
204
+ ) -> RuntimeEvent:
205
+ """Persist one fact through the generic SessionEventStore envelope."""
206
+
207
+ if self._event_store is None:
208
+ raise RuntimeError("typed append requires a SessionEventStore-backed runtime view")
209
+ if self._typed_session_id is None or not self._typed_session_id.strip():
210
+ raise ValueError("typed append requires a session_id bound at construction")
211
+ if not isinstance(guard, ActivationWriteGuard):
212
+ raise TypeError("RuntimeEventStore only accepts ActivationWriteGuard")
213
+ envelope = runtime_event_envelope(self._typed_session_id, event)
214
+ persisted = await self._event_store.append(envelope, guard=guard)
215
+ return parse_runtime_event(dict(persisted.payload) | {"seq": persisted.seq})
216
+
217
+ async def append_one(self, session_id: str, event: RuntimeEvent) -> RuntimeEvent:
218
+ persisted, _created = await self.persist_one(session_id, event)
219
+ return persisted
220
+
221
+ async def persist_one(self, session_id: str, event: RuntimeEvent) -> tuple[RuntimeEvent, bool]:
222
+ """Persist before publication and return whether this call created the fact."""
223
+
224
+ if getattr(event, "schema_version", None) != 2:
225
+ raise ValueError("canonical RuntimeEventStore accepts schema_version=2 only")
226
+ if not session_id.strip():
227
+ raise ValueError("session_id must be nonempty")
228
+ self._require_storage_capabilities()
229
+ existing = await self.event_by_id(session_id, event.event_id)
230
+ if existing is not None:
231
+ self._assert_same_fact(existing, event)
232
+ return existing, False
233
+ packed = runtime_event_to_session_event(session_id, event)
234
+ try:
235
+ stored = await self._service.append_event(session_id, packed)
236
+ except Exception:
237
+ # The deterministic physical id turns concurrent appends into an
238
+ # insert-winner/insert-loser race on durable backends. Re-read the
239
+ # winner and only absorb the error when it is the same fact.
240
+ existing = await self.event_by_id(session_id, event.event_id)
241
+ if existing is None:
242
+ raise
243
+ self._assert_same_fact(existing, event)
244
+ return existing, False
245
+ persisted = session_event_to_runtime_event(stored)
246
+ if persisted is None: # pragma: no cover - packed by this module
247
+ raise RuntimeError("canonical RuntimeEvent lost its storage marker")
248
+ self._assert_same_fact(persisted, event)
249
+ return persisted, True
250
+
251
+ async def _read_rows(
252
+ self,
253
+ session_id: str,
254
+ after_seq: int,
255
+ before_seq: int | None,
256
+ *,
257
+ limit: int | None = None,
258
+ ):
259
+ """Typed envelope 路径的读取兜底。
260
+
261
+ hosted PG 的 ``PostgresFencedSessionEventStore`` 只包 kernel store,
262
+ 没有 ``session_service``(``_service is None``)。冷恢复
263
+ (scan_open_runs -> list) 在此之前会 AttributeError,导致 takeover
264
+ recovery 双路径失败 -> runtime degraded。改走 event store 自己的
265
+ ``read``(envelope 语义)再转 SessionEvent 行。
266
+ """
267
+
268
+ if self._service is not None:
269
+ return await self._service.get_events(
270
+ session_id,
271
+ limit=limit,
272
+ after_seq_id=after_seq,
273
+ before_seq_id=before_seq,
274
+ )
275
+ if self._event_store is None:
276
+ raise RuntimeError(
277
+ "RuntimeEventStore has neither a session service nor an event store"
278
+ )
279
+ rows = []
280
+ from ksadk.events.session_event import envelope_to_session_event
281
+
282
+ for envelope in await self._event_store.read(
283
+ session_id, int(after_seq), int(limit or 100_000)
284
+ ):
285
+ if before_seq is not None and int(envelope.seq) >= int(before_seq):
286
+ break
287
+ row = envelope_to_session_event(envelope)
288
+ if int(row.seq_id or 0) != int(envelope.seq):
289
+ row.seq_id = int(envelope.seq)
290
+ rows.append(row)
291
+ return rows
292
+
293
+ async def page(
294
+ self,
295
+ session_id: str,
296
+ *,
297
+ after_seq: int = 0,
298
+ before_seq: int | None = None,
299
+ limit: int = 500,
300
+ ) -> list[RuntimeEvent]:
301
+ """Read the next canonical page in ascending physical cursor order.
302
+
303
+ ``list(..., limit=...)`` is a compatibility tail projection. Durable
304
+ export and replay callers that need bounded forward pagination must use
305
+ this explicit method, otherwise a large session can be read wholesale
306
+ before Python applies its limit.
307
+ """
308
+
309
+ if limit < 1:
310
+ raise ValueError("limit must be positive")
311
+ raw = await self._read_rows(
312
+ session_id,
313
+ int(after_seq),
314
+ before_seq,
315
+ limit=limit,
316
+ )
317
+ events = [
318
+ canonical
319
+ for canonical in (session_event_to_runtime_event(item) for item in raw)
320
+ if canonical is not None
321
+ ]
322
+ events.sort(key=lambda event: event.seq)
323
+ return events[:limit]
324
+
325
+ async def event_by_id(self, session_id: str, event_id: str) -> RuntimeEvent | None:
326
+ if self._service is not None:
327
+ self._require_storage_capabilities()
328
+ storage_id = canonical_storage_id(session_id, event_id)
329
+ stored = await self._service.get_event_by_id(session_id, storage_id)
330
+ return session_event_to_runtime_event(stored) if stored is not None else None
331
+ for event in await self.list(session_id):
332
+ if event.event_id == event_id:
333
+ return event
334
+ return None
335
+
336
+ async def resolve_existing(
337
+ self, session_id: str, candidate: RuntimeEvent
338
+ ) -> RuntimeEvent | None:
339
+ """Return an identical durable fact or raise for an id collision."""
340
+
341
+ existing = await self.event_by_id(session_id, candidate.event_id)
342
+ if existing is not None:
343
+ self._assert_same_fact(existing, candidate)
344
+ return existing
345
+
346
+ async def list(
347
+ self,
348
+ session_id: str,
349
+ *,
350
+ after_seq: int = 0,
351
+ before_seq: int | None = None,
352
+ run_id: str | None = None,
353
+ limit: int | None = None,
354
+ ) -> list[RuntimeEvent]:
355
+ # Run replay uses the backend's invocation index; session replay still
356
+ # reads the shared physical cursor log and filters legacy rows here.
357
+ if run_id is None or self._service is None:
358
+ # run 过滤在 typed envelope 兜底路径上退化为全量读取后按
359
+ # run_id 过滤(fenced store 没有按 invocation 的索引查询)。
360
+ raw = await self._read_rows(session_id, after_seq, before_seq)
361
+ else:
362
+ self._require_storage_capabilities()
363
+ raw = await self._service.get_events_by_invocation_id(
364
+ session_id,
365
+ run_id,
366
+ after_seq_id=after_seq,
367
+ before_seq_id=before_seq,
368
+ )
369
+ events = [
370
+ canonical
371
+ for canonical in (session_event_to_runtime_event(item) for item in raw)
372
+ if canonical is not None and (run_id is None or canonical.run_id == run_id)
373
+ ]
374
+ events.sort(key=lambda event: event.seq)
375
+ if limit is not None:
376
+ if limit < 1:
377
+ raise ValueError("limit must be positive")
378
+ events = events[-limit:]
379
+ return events
380
+
381
+ async def list_run_ids(self, session_id: str) -> list[str]:
382
+ """Distinct run ids in session order of first appearance."""
383
+
384
+ seen: dict[str, None] = {}
385
+ for event in await self.list(session_id):
386
+ seen.setdefault(event.run_id, None)
387
+ return list(seen)
388
+
389
+ async def subscribe_session(
390
+ self,
391
+ session_id: str,
392
+ *,
393
+ after_seq: int = 0,
394
+ poll_interval: float = 0.25,
395
+ timeout: float = 5 * 60,
396
+ ) -> AsyncIterator[RuntimeEvent]:
397
+ cursor = int(after_seq or 0)
398
+ deadline = asyncio.get_running_loop().time() + timeout
399
+ while True:
400
+ rows = await self._service.get_events(session_id, after_seq_id=cursor)
401
+ rows.sort(key=lambda event: event.seq_id)
402
+ for row in rows:
403
+ event = session_event_to_runtime_event(row)
404
+ cursor = row.seq_id
405
+ if event is not None:
406
+ yield event
407
+ if asyncio.get_running_loop().time() >= deadline:
408
+ return
409
+ await asyncio.sleep(poll_interval)
410
+
411
+ async def subscribe_run(
412
+ self,
413
+ session_id: str,
414
+ run_id: str,
415
+ *,
416
+ after_seq: int = 0,
417
+ poll_interval: float = 0.25,
418
+ timeout: float = 5 * 60,
419
+ ) -> AsyncIterator[RuntimeEvent]:
420
+ cursor = int(after_seq or 0)
421
+ deadline = asyncio.get_running_loop().time() + timeout
422
+ while True:
423
+ rows = await self._service.get_events(session_id, after_seq_id=cursor)
424
+ rows.sort(key=lambda event: event.seq_id)
425
+ for row in rows:
426
+ event = session_event_to_runtime_event(row)
427
+ cursor = row.seq_id
428
+ if event is not None and event.run_id == run_id:
429
+ yield event
430
+ if event.event_type in _TERMINAL_EVENT_TYPES:
431
+ return
432
+ if asyncio.get_running_loop().time() >= deadline:
433
+ return
434
+ await asyncio.sleep(poll_interval)
435
+
436
+ @staticmethod
437
+ def _assert_same_fact(existing: RuntimeEvent, candidate: RuntimeEvent) -> None:
438
+ existing_payload = dump_runtime_event(existing)
439
+ candidate_payload = dump_runtime_event(candidate)
440
+ # ``seq`` is the store-assigned delivery cursor, not producer fact
441
+ # identity. Every other canonical field participates in collision
442
+ # validation, including timestamp, source, run_seq and typed content.
443
+ existing_payload.pop("seq", None)
444
+ candidate_payload.pop("seq", None)
445
+ if existing_payload != candidate_payload:
446
+ raise ValueError(f"RuntimeEvent id collision for {candidate.event_id!r}")
447
+
448
+ def _require_storage_capabilities(self) -> None:
449
+ capabilities = self._service.storage_capabilities
450
+ if (
451
+ _REQUIRED_SEQ_BINDING not in capabilities.atomic_seq_bindings
452
+ or not capabilities.indexed_event_lookup
453
+ or not capabilities.indexed_invocation_lookup
454
+ ):
455
+ raise RuntimeError(
456
+ "session backend must support atomic runtime_event.seq binding "
457
+ "and indexed physical event lookup and indexed invocation lookup"
458
+ )
459
+
460
+
461
+ __all__ = [
462
+ "RuntimeEventStore",
463
+ "canonical_storage_id",
464
+ "runtime_event_to_session_event",
465
+ "session_event_to_runtime_event",
466
+ "runtime_event_envelope",
467
+ "runtime_event_envelope_id",
468
+ ]