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
ksadk/runtime/usage.py ADDED
@@ -0,0 +1,33 @@
1
+ """Canonical runtime usage projection helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Mapping
6
+
7
+
8
+ def canonical_usage_payload(
9
+ usage: Mapping[str, Any], *, runtime_type: str
10
+ ) -> dict[str, Any]:
11
+ """Normalize provider usage, including nested cache/reasoning details."""
12
+ input_details = usage.get("input_token_details")
13
+ output_details = usage.get("output_token_details")
14
+ cached = (
15
+ input_details.get("cached")
16
+ if isinstance(input_details, Mapping)
17
+ else usage.get("cached_tokens")
18
+ )
19
+ reasoning = (
20
+ output_details.get("reasoning")
21
+ if isinstance(output_details, Mapping)
22
+ else usage.get("reasoning_tokens")
23
+ )
24
+ input_tokens = int(usage.get("input_tokens") or 0)
25
+ output_tokens = int(usage.get("output_tokens") or 0)
26
+ return {
27
+ "input_tokens": input_tokens,
28
+ "output_tokens": output_tokens,
29
+ "total_tokens": int(usage.get("total_tokens") or input_tokens + output_tokens),
30
+ "cached_tokens": int(cached or 0),
31
+ "reasoning_tokens": int(reasoning or 0),
32
+ "source": str(usage.get("source") or runtime_type),
33
+ }
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import logging
3
4
  import os
4
5
  import time
5
6
  from typing import Any
@@ -12,10 +13,15 @@ from ksadk.sandbox.base import (
12
13
  SandboxSpec,
13
14
  )
14
15
 
16
+ logger = logging.getLogger(__name__)
17
+
15
18
  _TRANSIENT_STARTUP_ERROR_NAMES = {
16
19
  "NotFoundException",
17
20
  "FileNotFoundException",
18
21
  "SandboxNotFoundException",
22
+ # create 快速返回后 envd RPC 通道可能尚未就绪 (connection refused / unavailable),
23
+ # SDK 将其包装为 TimeoutException 抛出, 需按瞬时启动错误重试。
24
+ "TimeoutException",
19
25
  }
20
26
 
21
27
 
@@ -148,7 +154,7 @@ class E2BSandboxBackend:
148
154
  allow_internet_access=self.spec.allow_internet_access,
149
155
  )
150
156
  session = self._wrap_sandbox(sandbox)
151
- self._wait_until_ready(session)
157
+ self._wait_until_ready(session, runtime_env)
152
158
  session.write_files(
153
159
  [(item.target_path, item.source.read_bytes()) for item in input_files or []]
154
160
  )
@@ -157,6 +163,33 @@ class E2BSandboxBackend:
157
163
  def _wrap_sandbox(self, sandbox: Any) -> E2BSandboxSession:
158
164
  return E2BSandboxSession(sandbox)
159
165
 
160
- def _wait_until_ready(self, session: E2BSandboxSession) -> None:
166
+ def _wait_until_ready(
167
+ self, session: E2BSandboxSession, env: dict[str, str] | None = None
168
+ ) -> None:
161
169
  _with_startup_retry(lambda: session.run_command("true"))
162
170
  _with_startup_retry(lambda: session.write_file("/tmp/.ksadk-sandbox-ready", ""))
171
+ self._wait_env_applied(session, env or {})
172
+
173
+ def _wait_env_applied(self, session: E2BSandboxSession, env: dict[str, str]) -> None:
174
+ # create 返回后 envd 异步应用 envVars, 实测存在秒级延迟;
175
+ # 轮询一个非空变量直到值就位, 避免紧随其后的 agent 进程读到缺失的环境。
176
+ probe = next(((k, v) for k, v in env.items() if v), None)
177
+ if probe is None:
178
+ return
179
+ key, expected = probe
180
+ attempts = _startup_retry_attempts()
181
+ delay = _startup_retry_delay()
182
+ for attempt in range(attempts):
183
+ result = session.run_command(f"printenv {key} || true")
184
+ if result.stdout.strip() == expected:
185
+ return
186
+ time.sleep(min(delay * (2**attempt), 1.0))
187
+ # 不 raise: 模板可能预置同名空值覆盖 create 注入 (envd 合并优先级问题),
188
+ # 此时调用方仍可通过 run_command(env=...) 的 per-command 注入拿到正确值。
189
+ logger.warning(
190
+ "E2B sandbox env %s not applied after %d attempts (expected %r); "
191
+ "template may predefine an empty override",
192
+ key,
193
+ attempts,
194
+ expected,
195
+ )
@@ -13,6 +13,14 @@ def configure_runtime_app(app: FastAPI, state: RuntimeAppState, groups: set[str]
13
13
  _configure_route_dependencies()
14
14
  _register_integrated_routers(app, state)
15
15
 
16
+ # Agent Kernel canonical ingress(/agent-kernel/v1/*)必须先于 route group
17
+ # 注册:group 内的静态 UI catch-all(GET /{path}) 会吞掉所有未匹配 GET,
18
+ # 后注册的 kernel GET 路由(health / SubscribeSessionEvents)将被遮蔽 404。
19
+ # 灰度开关关闭时 bootstrap 返回 None、路由统一回 503,不影响旧路径。
20
+ from ksadk.kernel import ingress as _kernel_ingress
21
+
22
+ app.include_router(_kernel_ingress.agent_kernel_router())
23
+
16
24
  ordered = sorted(group for group in groups if group != "health_meta")
17
25
  if "health_meta" in groups:
18
26
  ordered.append("health_meta")
ksadk/server/factory.py CHANGED
@@ -194,6 +194,7 @@ class RuntimeAppConfig:
194
194
  agui: Optional[Any] = None,
195
195
  runtime_executor: RuntimeExecutor | None = None,
196
196
  launch_context: RuntimeLaunchContext | None = None,
197
+ kernel_adapter_provider: Callable[[], RuntimeAdapter] | None = None,
197
198
  session_service_provider: Callable[[], Any] | None = None,
198
199
  session_backend_provider: Callable[[], dict[str, Any]] | None = None,
199
200
  ) -> None:
@@ -208,6 +209,9 @@ class RuntimeAppConfig:
208
209
  self.agui = agui
209
210
  self.runtime_executor = runtime_executor
210
211
  self.launch_context = launch_context
212
+ # 特殊 runtime 可显式提供 worker/recovery 的 adapter;常规部署从
213
+ # runtime_executor 的同一 registry 派生,见 _kernel_adapter_provider。
214
+ self.kernel_adapter_provider = kernel_adapter_provider
211
215
  self.session_service_provider = session_service_provider
212
216
  self.session_backend_provider = session_backend_provider
213
217
 
@@ -353,6 +357,40 @@ def create_runtime_app(
353
357
 
354
358
  @asynccontextmanager
355
359
  async def _lifespan(app: FastAPI) -> AsyncIterator[None]:
360
+ # 生产 kernel 的 composition root 必须启动 worker/lease/recovery,
361
+ # 不能只注册一个可接收命令、却永远不会消费的 ingress kernel。
362
+ from ksadk.kernel import ingress as _kernel_ingress
363
+ from ksadk.kernel.bootstrap import (
364
+ bootstrap_agent_kernel_runtime_from_env,
365
+ clear_agent_kernel_runtime,
366
+ )
367
+ from ksadk.runtime.factory import kernel_start_request_defaults
368
+
369
+ adapter_provider = _kernel_adapter_provider(config)
370
+ request_defaults = (
371
+ kernel_start_request_defaults(config.launch_context)
372
+ if config.launch_context is not None
373
+ else {}
374
+ )
375
+ kernel_runtime = await bootstrap_agent_kernel_runtime_from_env(
376
+ adapter_provider=adapter_provider,
377
+ runtime_executor=config.runtime_executor,
378
+ launch_context=config.launch_context,
379
+ start_request_defaults=request_defaults,
380
+ session_service=state.resolve_session_service(),
381
+ )
382
+ app.state.agent_kernel_runtime = kernel_runtime
383
+ if kernel_runtime is not None:
384
+ # Kernel canonical events are the replay authority for the normal
385
+ # Session APIs as well. In memory/ephemeral mode bootstrap reuses
386
+ # the app-owned service above; in PostgreSQL mode it owns the
387
+ # durable service and the HTTP routes must adopt that exact one.
388
+ # Keeping two services made foreground SSE look correct while a
389
+ # later ListSessionEvents call returned an empty history forever.
390
+ kernel_session_service = kernel_runtime.config.session_service
391
+ if kernel_session_service is None: # pragma: no cover - defensive
392
+ raise RuntimeError("agent kernel runtime has no Session service")
393
+ state.session_service = kernel_session_service
356
394
  try:
357
395
  if state.a2a_bootstrap is not None:
358
396
  await state.a2a_bootstrap.start()
@@ -360,6 +398,10 @@ def create_runtime_app(
360
398
  finally:
361
399
  if state.a2a_bootstrap is not None:
362
400
  await state.a2a_bootstrap.stop()
401
+ if kernel_runtime is not None:
402
+ await kernel_runtime.close()
403
+ clear_agent_kernel_runtime()
404
+ _kernel_ingress.clear_agent_kernel()
363
405
  await shutdown_runtime_resources(state)
364
406
 
365
407
  app = FastAPI(
@@ -488,6 +530,25 @@ def create_runtime_app(
488
530
  return app
489
531
 
490
532
 
533
+ def _kernel_adapter_provider(
534
+ config: RuntimeAppConfig,
535
+ ) -> Callable[[], RuntimeAdapter] | None:
536
+ """Return the adapter source used by the durable kernel runtime.
537
+
538
+ Hosted startup deliberately fails when neither a provider nor a Runtime
539
+ launch context is available. It must never construct an unrelated local
540
+ adapter merely to make the ingress look healthy.
541
+ """
542
+
543
+ if config.kernel_adapter_provider is not None:
544
+ return config.kernel_adapter_provider
545
+ if config.a2a_runtime_adapter is not None:
546
+ return lambda: config.a2a_runtime_adapter
547
+ if config.runtime_executor is not None and config.launch_context is not None:
548
+ return lambda: config.runtime_executor.create_adapter(config.launch_context)
549
+ return None
550
+
551
+
491
552
  def _wire_a2a_if_enabled(app: FastAPI, state: RuntimeAppState, config: RuntimeAppConfig) -> None:
492
553
  """Mount A2A with an explicitly injected RuntimeAdapter.
493
554
 
@@ -569,15 +630,17 @@ def _wire_agui_if_enabled(app: FastAPI, state: RuntimeAppState, config: RuntimeA
569
630
  self._service = service
570
631
  self._store = RuntimeEventStore(service)
571
632
 
572
- async def append_one(self, event: Any) -> Any:
573
- existing = await self._service.get_session(event.session_id)
633
+ async def append_one(self, session_id: str, event: Any) -> Any:
634
+ existing = await self._service.get_session(session_id)
574
635
  if existing is None:
636
+ metadata = getattr(event, "source", None)
637
+ meta = metadata.metadata if metadata is not None else {}
575
638
  await self._service.create_session(
576
- event.agent_id,
577
- event.user_id,
578
- event.session_id,
639
+ str(meta.get("agent_id") or "agent"),
640
+ str(meta.get("user_id") or "user"),
641
+ session_id,
579
642
  )
580
- return await self._store.append_one(event)
643
+ return await self._store.append_one(session_id, event)
581
644
 
582
645
  async def reserve_once(self, event: Any) -> Any:
583
646
  existing = await self._service.get_session(event.session_id)
@@ -0,0 +1,258 @@
1
+ # -*- coding: utf-8 -*-
2
+ """RunAgent / Responses 入口的 kernel 路径(Phase 1 Task 8 Step 3-5)。
3
+
4
+ 只在 ``kernel_route_active()`` 时启用(灰度 opt-in);旧 HTTP 行为保持兼容:
5
+ - accepted -> 202、duplicate -> 200、rejected -> 400、unsupported -> 409、
6
+ queue_full -> 429、persistence_uncertain -> 503(RECEIPT_HTTP_STATUS)。
7
+ - 旧响应 shape 不变;非流式在 receipt accepted 后才开始消费 stream。
8
+ - SSE 的 reconnect cursor 源自同一 Session seq(SessionEventSubscription)。
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import json
14
+ import logging
15
+ from typing import Any
16
+
17
+ from fastapi.responses import JSONResponse, StreamingResponse
18
+
19
+ from ksadk.kernel import ingress
20
+ from ksadk.kernel.contracts import AgentControlReceipt
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ def _envelope_text(payload: dict[str, Any]) -> str:
26
+ return str(payload.get("delta") or payload.get("text") or "")
27
+
28
+
29
+ async def _kernel_submit(
30
+ *,
31
+ mapper: str,
32
+ session_id: str,
33
+ idempotency_key: str,
34
+ content: Any,
35
+ correlation_ref: str | None,
36
+ source_kind: str,
37
+ runtime_options: dict[str, Any] | None = None,
38
+ ) -> tuple[AgentControlReceipt, ingress.TrustedRuntimeContext]:
39
+ trusted = ingress.trusted_context(
40
+ source_kind=source_kind,
41
+ source_ref=idempotency_key,
42
+ session_id=session_id,
43
+ # A foreground compatibility request admits a mutation and then reads
44
+ # the same session's canonical stream. It remains session-bound, but
45
+ # needs explicit authority for both operations.
46
+ operations=("enqueue", "subscribe_events"),
47
+ )
48
+ correlation_kwarg = {
49
+ "map_run_request": "invocation_id",
50
+ "map_responses_request": "response_id",
51
+ "map_agui_request": "run_id",
52
+ "map_a2a_task": "task_id",
53
+ "map_studio_request": "run_id",
54
+ }[mapper]
55
+ command = getattr(ingress, mapper)(
56
+ trusted=trusted,
57
+ session_id=session_id,
58
+ idempotency_key=idempotency_key,
59
+ content=content,
60
+ **({correlation_kwarg: correlation_ref} if correlation_ref else {}),
61
+ **({"runtime_options": runtime_options} if runtime_options else {}),
62
+ )
63
+ receipt = await ingress.submit_command(command, permit=trusted.permit)
64
+ return receipt, trusted
65
+
66
+
67
+ def _kernel_error_response(receipt: AgentControlReceipt) -> JSONResponse:
68
+ return JSONResponse(
69
+ status_code=ingress.receipt_http_status(receipt),
70
+ content={
71
+ "error": ingress.receipt_error_payload(receipt),
72
+ },
73
+ headers=ingress.receipt_response_headers(receipt),
74
+ )
75
+
76
+
77
+ def _sse_chunk(payload: dict[str, Any], *, event: str | None, seq: int) -> str:
78
+ prefix = f"event: {event}\n" if event else ""
79
+ return f"id: {seq}\n{prefix}data: {json.dumps(payload, ensure_ascii=False)}\n\n"
80
+
81
+
82
+ def kernel_stream_response(
83
+ *,
84
+ receipt: AgentControlReceipt,
85
+ trusted: ingress.TrustedRuntimeContext,
86
+ session_id: str,
87
+ ) -> StreamingResponse:
88
+ """统一 cursor:从 receipt.accepted_seq 之后读 Session 事件。"""
89
+
90
+ after_seq = int(receipt.accepted_seq or 0)
91
+
92
+ async def generator():
93
+ async for seq, projected in ingress.subscribe_projected(
94
+ session_id,
95
+ trusted=trusted,
96
+ after_seq=after_seq,
97
+ projector=_new_responses_projector(),
98
+ ):
99
+ if projected is None:
100
+ continue
101
+ kind, payload = projected
102
+ yield _sse_chunk(payload, event=kind, seq=seq)
103
+ if kind == "response.output_item.done" and (
104
+ (payload.get("item") or {}).get("type") == "mcp_approval_request"
105
+ ):
106
+ yield _sse_chunk(
107
+ {"type": "response.incomplete"},
108
+ event="response.incomplete",
109
+ seq=seq,
110
+ )
111
+ return
112
+ # A foreground response stream is scoped to one admitted run.
113
+ # SessionEventStore subscriptions are deliberately long-lived for
114
+ # replay/SSE clients, so do not leave this HTTP response open after
115
+ # the terminal fact has been projected.
116
+ if kind in {"response.completed", "response.failed", "response.canceled"}:
117
+ return
118
+
119
+ return StreamingResponse(generator(), media_type="text/event-stream")
120
+
121
+
122
+ def _responses_projector(envelope: Any) -> tuple[str, dict[str, Any]] | None:
123
+ """Session envelope -> 旧 Responses SSE shape(cursor 仍用 envelope.seq)。"""
124
+
125
+ payload = envelope.payload or {}
126
+ event_type = envelope.event_type
127
+ if event_type == "interaction.requested":
128
+ request = payload.get("request") or {}
129
+ presentation = request.get("presentation") or {}
130
+ description = presentation.get("description") or ""
131
+ try:
132
+ visible = json.loads(description) if description else {}
133
+ except (TypeError, json.JSONDecodeError):
134
+ visible = {}
135
+ arguments = visible.get("arguments") if isinstance(visible, dict) else {}
136
+ return "response.output_item.done", {
137
+ "type": "response.output_item.done",
138
+ "item": {
139
+ "id": str(payload.get("interaction_id") or ""),
140
+ "type": "mcp_approval_request",
141
+ "name": str(presentation.get("title") or payload.get("kind") or "approval"),
142
+ "arguments": json.dumps(arguments or {}, ensure_ascii=False),
143
+ },
144
+ }
145
+ if event_type == "run.completed":
146
+ text = str(payload.get("output_text") or "")
147
+ return "response.completed", {
148
+ "type": "response.completed",
149
+ "output_text": text,
150
+ "delta": text,
151
+ }
152
+ if event_type == "run.failed":
153
+ return "response.failed", {
154
+ "type": "response.failed",
155
+ "error": payload.get("error") or {"code": "runtime_failed"},
156
+ }
157
+ if event_type in {"run.canceled", "run.interrupted"}:
158
+ return "response.canceled", {
159
+ "type": "response.canceled",
160
+ "reason": payload.get("reason") or event_type,
161
+ }
162
+ text = _envelope_text(payload)
163
+ if text:
164
+ return "response.output_text.delta", {
165
+ "type": "response.output_text.delta",
166
+ "delta": text,
167
+ }
168
+ return None
169
+
170
+
171
+ def _new_responses_projector():
172
+ """Create a session-scoped canonical RuntimeEvent -> Responses projector.
173
+
174
+ ``run.completed.output_refs`` deliberately point at canonical items instead
175
+ of duplicating answer text. A projector therefore keeps only the small
176
+ item snapshot needed for this one HTTP/SSE response and resolves those
177
+ refs when the terminal fact arrives.
178
+ """
179
+
180
+ item_text: dict[str, str] = {}
181
+
182
+ def project(envelope: Any) -> tuple[str, dict[str, Any]] | None:
183
+ payload = envelope.payload or {}
184
+ event_type = envelope.event_type
185
+ if event_type == "item.updated":
186
+ item_id = str(payload.get("item_id") or "")
187
+ update = payload.get("update") or {}
188
+ text = str(update.get("text") or "")
189
+ if item_id and text:
190
+ item_text[item_id] = (
191
+ text if payload.get("op") == "replace" else item_text.get(item_id, "") + text
192
+ )
193
+ return None
194
+ if event_type == "item.completed":
195
+ item_id = str(payload.get("item_id") or "")
196
+ parts = (payload.get("snapshot") or {}).get("parts") or []
197
+ if item_id and isinstance(parts, list):
198
+ item_text[item_id] = "".join(
199
+ str(part.get("text") or "") for part in parts if isinstance(part, dict)
200
+ )
201
+ return None
202
+ if event_type == "run.completed":
203
+ refs = payload.get("output_refs") or []
204
+ output = "".join(
205
+ item_text.get(str(ref.get("item_id") or ""), "")
206
+ for ref in refs
207
+ if isinstance(ref, dict)
208
+ )
209
+ projected_payload = dict(payload)
210
+ projected_payload["output_text"] = output or str(payload.get("output_text") or "")
211
+ return _responses_projector(
212
+ type("Envelope", (), {"event_type": event_type, "payload": projected_payload})()
213
+ )
214
+ return _responses_projector(envelope)
215
+
216
+ return project
217
+
218
+
219
+ async def kernel_conversation_turn(
220
+ *,
221
+ receipt: AgentControlReceipt,
222
+ trusted: ingress.TrustedRuntimeContext,
223
+ session_id: str,
224
+ build_payload,
225
+ ):
226
+ """非流式 kernel 路径:receipt accepted 后订阅聚合 output_text。"""
227
+
228
+ if receipt.status not in ("accepted", "duplicate"):
229
+ return _kernel_error_response(receipt)
230
+ output_text = ""
231
+ async for _seq, projected in ingress.subscribe_projected(
232
+ session_id,
233
+ trusted=trusted,
234
+ after_seq=int(receipt.accepted_seq or 0),
235
+ projector=_new_responses_projector(),
236
+ ):
237
+ if projected and projected[0] == "response.completed":
238
+ output_text = str(projected[1].get("output_text") or output_text)
239
+ break
240
+ if projected and projected[0] in {"response.failed", "response.canceled"}:
241
+ return JSONResponse(
242
+ status_code=502,
243
+ content={"error": projected[1]},
244
+ )
245
+ elif projected:
246
+ output_text += str(projected[1].get("delta") or "")
247
+ payload = build_payload(output_text)
248
+ return JSONResponse(
249
+ status_code=ingress.receipt_http_status(receipt),
250
+ content=payload,
251
+ headers=ingress.receipt_response_headers(receipt),
252
+ )
253
+
254
+
255
+ __all__ = [
256
+ "kernel_conversation_turn",
257
+ "kernel_stream_response",
258
+ ]
@@ -6,6 +6,7 @@ import uuid
6
6
  from collections.abc import Mapping
7
7
  from typing import Any, Dict, List, Optional
8
8
 
9
+ from fastapi import HTTPException
9
10
  from fastapi.responses import StreamingResponse
10
11
  from pydantic import BaseModel
11
12
 
@@ -20,11 +21,18 @@ from ksadk.conversations.runtime_streaming import (
20
21
  stream_runtime_conversation_turn,
21
22
  stream_runtime_responses_conversation_turn,
22
23
  )
24
+ from ksadk.kernel.ingress import kernel_route_active
23
25
  from ksadk.runtime.conversation_execution import invoke_runtime_conversation_once
24
26
  from ksadk.server.factory import get_runtime_execution
25
27
 
26
28
  from . import dependencies as deps
27
29
  from .checkpoint_resolution import _resolve_checkpoint_resume_input_from_session
30
+ from .kernel_ingress import (
31
+ _kernel_error_response,
32
+ _kernel_submit,
33
+ kernel_conversation_turn,
34
+ kernel_stream_response,
35
+ )
28
36
  from .models import (
29
37
  ResponsesRequest,
30
38
  _clean_optional_string,
@@ -60,6 +68,26 @@ async def list_openai_models():
60
68
  """Expose the current model catalog through the OpenAI-compatible path."""
61
69
 
62
70
  payload = await _build_models_payload()
71
+ try:
72
+ _executor, launch_context = get_runtime_execution()
73
+ except HTTPException:
74
+ launch_context = None
75
+ if launch_context is not None:
76
+ config = dict(launch_context.config)
77
+ raw_allowed = (
78
+ config.get("models") or config.get("allowed_models") or config.get("allowedModels")
79
+ )
80
+ if isinstance(raw_allowed, (list, tuple, set)):
81
+ allowed = {str(item).strip() for item in raw_allowed if str(item).strip()}
82
+ default_model = str(config.get("model") or "").strip()
83
+ if default_model:
84
+ allowed.add(default_model)
85
+ payload = dict(payload)
86
+ payload["data"] = [
87
+ item
88
+ for item in payload.get("data", [])
89
+ if str(item.get("id") or "").strip() in allowed
90
+ ]
63
91
  return {
64
92
  "object": "list",
65
93
  "data": payload.get("data", []),
@@ -72,6 +100,8 @@ async def list_openai_models():
72
100
  async def responses(request: ResponsesRequest):
73
101
  """OpenAI Responses 兼容接口。"""
74
102
  executor, launch_context = get_runtime_execution()
103
+ if kernel_route_active():
104
+ return await _kernel_responses(request, launch_context)
75
105
  resolved_session_id, resolved_user_id = _resolve_responses_session_and_user(request)
76
106
  agent_id = _runtime_agent_id(launch_context)
77
107
 
@@ -82,11 +112,7 @@ async def responses(request: ResponsesRequest):
82
112
  session_id=resolved_session_id,
83
113
  resume_input=resume_input,
84
114
  )
85
- messages = (
86
- []
87
- if resume_input is not None
88
- else normalize_responses_input(request.input)
89
- )
115
+ messages = [] if resume_input is not None else normalize_responses_input(request.input)
90
116
  custom_metadata, request_metadata = _split_custom_metadata(request.metadata)
91
117
  if request.previous_response_id:
92
118
  request_metadata["previous_response_id"] = request.previous_response_id
@@ -105,9 +131,7 @@ async def responses(request: ResponsesRequest):
105
131
 
106
132
  if request.stream:
107
133
  runtime_preparation = (
108
- None
109
- if resume_input is not None
110
- else await executor.prepare_start(launch_context)
134
+ None if resume_input is not None else await executor.prepare_start(launch_context)
111
135
  )
112
136
  resume_key = _detached_resume_key_from_input(resolved_session_id, resume_input)
113
137
  _reject_if_detached_resume_active(resume_key)
@@ -169,6 +193,62 @@ async def responses(request: ResponsesRequest):
169
193
  )
170
194
 
171
195
 
196
+ async def _kernel_responses(request: ResponsesRequest, launch_context):
197
+ """kernel 路径(灰度 opt-in):Responses -> AgentControlCommand -> receipt。"""
198
+
199
+ from ksadk.conversations.runtime_persistence import ensure_conversation_session
200
+
201
+ resolved_session_id, resolved_user_id = _resolve_responses_session_and_user(request)
202
+ session = await ensure_conversation_session(
203
+ agent_id=_runtime_agent_id(launch_context),
204
+ user_id=resolved_user_id,
205
+ session_id=resolved_session_id,
206
+ session_service_provider=deps.resolve_session_service,
207
+ )
208
+ session_id = session.id
209
+ metadata = request.metadata if isinstance(request.metadata, dict) else {}
210
+ idempotency_key = (
211
+ _clean_optional_string(metadata.get("idempotency_key"))
212
+ or _metadata_invocation_id(metadata)
213
+ or f"resp_{uuid.uuid4().hex}"
214
+ )
215
+ messages = normalize_responses_input(request.input)
216
+ response_id = f"resp_{uuid.uuid4().hex}"
217
+ receipt, trusted = await _kernel_submit(
218
+ mapper="map_responses_request",
219
+ session_id=session_id,
220
+ idempotency_key=idempotency_key,
221
+ content=messages,
222
+ correlation_ref=response_id,
223
+ source_kind="responses",
224
+ )
225
+ if receipt.status not in ("accepted", "duplicate"):
226
+ return _kernel_error_response(receipt)
227
+
228
+ def build_payload(output_text: str):
229
+ return build_responses_payload(
230
+ output_text=output_text,
231
+ model=request.model,
232
+ session_id=session_id,
233
+ response_id=response_id,
234
+ metadata=None,
235
+ usage=None,
236
+ )
237
+
238
+ if request.stream:
239
+ return kernel_stream_response(
240
+ receipt=receipt,
241
+ trusted=trusted,
242
+ session_id=session_id,
243
+ )
244
+ return await kernel_conversation_turn(
245
+ receipt=receipt,
246
+ trusted=trusted,
247
+ session_id=session_id,
248
+ build_payload=build_payload,
249
+ )
250
+
251
+
172
252
  @openai_compat_router.post("/v1/chat/completions")
173
253
  async def chat_completions(request: ChatCompletionRequest):
174
254
  """OpenAI 兼容的聊天补全接口 (支持流式和非流式)"""