agentengine-sdk-python 0.8.3__py3-none-any.whl → 0.8.4__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 (509) hide show
  1. {agentengine_sdk_python-0.8.3.dist-info → agentengine_sdk_python-0.8.4.dist-info}/METADATA +9 -8
  2. {agentengine_sdk_python-0.8.3.dist-info → agentengine_sdk_python-0.8.4.dist-info}/RECORD +482 -245
  3. ksadk/_build_provenance.json +1 -1
  4. ksadk/api/client.py +105 -91
  5. ksadk/builders/code_builder.py +36 -9
  6. ksadk/builders/framework_requirements.py +2 -1
  7. ksadk/builders/ks3_uploader.py +36 -7
  8. ksadk/builders/managed_runtime_builder.py +1 -0
  9. ksadk/cli/__init__.py +32 -3
  10. ksadk/cli/cmd_managed_runtime.py +13 -1
  11. ksadk/cli/cmd_plugin.py +6 -1
  12. ksadk/cli/cmd_web.py +2 -5
  13. ksadk/cli/runtime_bootstrap.py +23 -1
  14. ksadk/codex/client.py +424 -96
  15. ksadk/codex/projection.py +24 -0
  16. ksadk/codex/runtime.py +56 -298
  17. ksadk/common/auth.py +16 -10
  18. ksadk/configs/env_registry.py +29 -1
  19. ksadk/conversations/message_projection.py +135 -14
  20. ksadk/conversations/projector.py +4 -6
  21. ksadk/conversations/runtime_invocation.py +5 -0
  22. ksadk/conversations/runtime_payloads.py +1 -0
  23. ksadk/conversations/runtime_preparation.py +30 -27
  24. ksadk/conversations/runtime_resume.py +50 -2
  25. ksadk/conversations/runtime_stream_events.py +5 -0
  26. ksadk/conversations/runtime_streaming.py +25 -7
  27. ksadk/deployment/env_forward.py +2 -2
  28. ksadk/deployment/providers/serverless.py +1 -1
  29. ksadk/events/adapters/_codex_validators.py +18 -7
  30. ksadk/events/adapters/langgraph.py +15 -1
  31. ksadk/events/canonical_store.py +3 -1
  32. ksadk/events/projections.py +1 -1
  33. ksadk/harness/config.py +2 -2
  34. ksadk/harness/runtime.py +27 -27
  35. ksadk/harness/tools.py +38 -13
  36. ksadk/interaction/provider.py +1 -0
  37. ksadk/interaction/providers/langgraph.py +1 -0
  38. ksadk/kernel/bootstrap.py +28 -4
  39. ksadk/kernel/ingress.py +15 -2
  40. ksadk/kernel/sqlite_store.py +27 -0
  41. ksadk/kernel/store.py +3 -0
  42. ksadk/kernel/worker.py +65 -10
  43. ksadk/knowledge_base/client.py +57 -27
  44. ksadk/mcp_runtime/__init__.py +12 -4
  45. ksadk/memory/adk/backends/sdk_ltm_backend.py +161 -41
  46. ksadk/memory/adk/resilient_session_service.py +25 -17
  47. ksadk/memory/adk/short_term_memory.py +26 -9
  48. ksadk/memory/models.py +7 -4
  49. ksadk/memory/provider_resolver.py +3 -0
  50. ksadk/model_proxy/server.py +10 -5
  51. ksadk/plugins/__init__.py +4 -0
  52. ksadk/plugins/artifacts.py +275 -0
  53. ksadk/plugins/bridges/__init__.py +4 -2
  54. ksadk/plugins/bridges/codex.py +120 -13
  55. ksadk/plugins/bridges/dsh.py +340 -156
  56. ksadk/plugins/builtins.py +35 -36
  57. ksadk/plugins/bundle_security.py +28 -4
  58. ksadk/plugins/codex_manifest.py +983 -0
  59. ksadk/plugins/composition.py +8 -0
  60. ksadk/plugins/contracts.py +118 -0
  61. ksadk/plugins/delivery.py +153 -0
  62. ksadk/plugins/dsh_installation_digest.py +71 -0
  63. ksadk/plugins/dsh_toolchain.py +110 -21
  64. ksadk/plugins/ecosystem_probe.py +6 -6
  65. ksadk/plugins/host.py +102 -39
  66. ksadk/plugins/kernel_adapter.py +205 -0
  67. ksadk/plugins/providers/__init__.py +23 -0
  68. ksadk/plugins/providers/bundles/dsh-knowledge/cordis.patch.yml +3 -0
  69. ksadk/plugins/providers/bundles/dsh-knowledge/index.mjs +53 -0
  70. ksadk/plugins/providers/bundles/dsh-knowledge/package.json +16 -0
  71. ksadk/plugins/providers/bundles/dsh-memory/cordis.patch.yml +3 -0
  72. ksadk/plugins/providers/bundles/dsh-memory/index.mjs +175 -0
  73. ksadk/plugins/providers/bundles/dsh-memory/package.json +16 -0
  74. ksadk/plugins/providers/bundles/dsh-platform-resources/client.mjs +166 -0
  75. ksadk/plugins/providers/bundles/dsh-platform-resources/cordis.patch.yml +3 -0
  76. ksadk/plugins/providers/bundles/dsh-platform-resources/index.mjs +9 -0
  77. ksadk/plugins/providers/bundles/dsh-platform-resources/package.json +12 -0
  78. ksadk/plugins/providers/bundles/dsh-skill-center/cordis.patch.yml +3 -0
  79. ksadk/plugins/providers/bundles/dsh-skill-center/index.mjs +125 -0
  80. ksadk/plugins/providers/bundles/dsh-skill-center/package.json +16 -0
  81. ksadk/plugins/providers/bundles/ksadk-dsh-capability-host/cordis.patch.yml +3 -0
  82. ksadk/plugins/providers/bundles/ksadk-dsh-capability-host/index.mjs +870 -0
  83. ksadk/plugins/providers/bundles/ksadk-dsh-capability-host/package.json +27 -0
  84. ksadk/plugins/providers/bundles/ksadk-dsh-studio/client.js +92 -0
  85. ksadk/plugins/providers/bundles/ksadk-dsh-studio/index.mjs +15 -0
  86. ksadk/plugins/providers/bundles/ksadk-dsh-studio/package.json +7 -0
  87. ksadk/plugins/providers/codex.py +335 -69
  88. ksadk/plugins/providers/codex_native.py +368 -0
  89. ksadk/plugins/providers/codex_turn.py +301 -0
  90. ksadk/plugins/providers/dsh_capabilities.py +1389 -0
  91. ksadk/plugins/providers/dsh_mcp.py +467 -0
  92. ksadk/plugins/providers/harness.py +118 -113
  93. ksadk/plugins/providers/mcp_projection.py +150 -0
  94. ksadk/plugins/providers/platform_resources.py +657 -0
  95. ksadk/plugins/resolver.py +8 -1
  96. ksadk/resource_runtime/__init__.py +4 -0
  97. ksadk/resource_runtime/broker.py +616 -0
  98. ksadk/resource_runtime/build_artifacts.py +165 -0
  99. ksadk/resource_runtime/contracts.py +138 -0
  100. ksadk/resource_runtime/discovery_receipts.py +100 -0
  101. ksadk/resource_runtime/discovery_skills.py +172 -0
  102. ksadk/resource_runtime/errors.py +6 -0
  103. ksadk/resource_runtime/ipc.py +142 -0
  104. ksadk/resource_runtime/kernel_approval.py +165 -0
  105. ksadk/resource_runtime/knowledge.py +114 -0
  106. ksadk/resource_runtime/langgraph.py +94 -0
  107. ksadk/resource_runtime/leases.py +191 -0
  108. ksadk/resource_runtime/managed_projection.py +186 -0
  109. ksadk/resource_runtime/mcp_server.py +138 -0
  110. ksadk/resource_runtime/memory_provider.py +318 -0
  111. ksadk/resource_runtime/memory_receipts.py +34 -0
  112. ksadk/resource_runtime/operation_ledger.py +211 -0
  113. ksadk/resource_runtime/plugin_config.py +48 -0
  114. ksadk/resource_runtime/policy_authorization.py +54 -0
  115. ksadk/resource_runtime/process.py +110 -0
  116. ksadk/resource_runtime/skill_artifacts.py +153 -0
  117. ksadk/resource_runtime/skill_runtime.py +42 -0
  118. ksadk/resource_runtime/skill_workspace.py +118 -0
  119. ksadk/resource_runtime/skills.py +215 -0
  120. ksadk/resource_runtime/snapshots.py +180 -0
  121. ksadk/resource_runtime/socket_server.py +103 -0
  122. ksadk/resource_runtime/supervisor.py +380 -0
  123. ksadk/resource_runtime/worker.py +504 -0
  124. ksadk/runners/_langgraph_runner_streams.py +74 -6
  125. ksadk/runners/adk_runner.py +149 -11
  126. ksadk/runners/base_runner.py +29 -1
  127. ksadk/runners/langgraph_runner.py +475 -145
  128. ksadk/runtime/_runner_adapter/stream_mapping.py +142 -18
  129. ksadk/runtime/adapter.py +2 -0
  130. ksadk/runtime/conversation_execution.py +92 -16
  131. ksadk/runtime/executor.py +78 -2
  132. ksadk/runtime/factory.py +29 -273
  133. ksadk/runtime/framework_adapters.py +23 -0
  134. ksadk/runtime/preprocessing.py +10 -2
  135. ksadk/runtime/runner_adapter.py +73 -14
  136. ksadk/runtime_context.py +40 -2
  137. ksadk/sandbox/backends/e2b.py +52 -11
  138. ksadk/sandbox/backends/local_process.py +9 -0
  139. ksadk/sandbox/base.py +2 -0
  140. ksadk/sandbox/e2b_connection.py +66 -0
  141. ksadk/scheduler/sqlite_store.py +18 -0
  142. ksadk/server/composition.py +2 -0
  143. ksadk/server/factory.py +5 -0
  144. ksadk/server/persistence_capability.py +516 -0
  145. ksadk/server/routes/control.py +210 -46
  146. ksadk/server/routes/dependencies.py +15 -0
  147. ksadk/server/routes/models.py +47 -4
  148. ksadk/server/routes/projection.py +252 -12
  149. ksadk/server/routes/sessions.py +284 -107
  150. ksadk/server/routes/streaming.py +43 -9
  151. ksadk/server/static/assets/{ArtifactsPanel-10GPXIJ6.js → ArtifactsPanel-CGbWlMNh.js} +1 -1
  152. ksadk/server/static/assets/{CodeBlock-BXk1l-G1.js → CodeBlock-b4C7upmO.js} +3 -3
  153. ksadk/server/static/assets/{MathMessageMarkdown-D9uB55Oh.js → MathMessageMarkdown-DbdW5A4_.js} +4 -4
  154. ksadk/server/static/assets/{MermaidBlock--OEYoXIJ.js → MermaidBlock-BEBN4Ksz.js} +6 -6
  155. ksadk/server/static/assets/{NativeTerminalPanel-DrEe1syq.js → NativeTerminalPanel-zvL-WpAn.js} +1 -1
  156. ksadk/server/static/assets/{abnfDiagram-VCTEODGH-eeTFJLvo.js → abnfDiagram-VCTEODGH-BYafW7Za.js} +1 -1
  157. ksadk/server/static/assets/{arc-S3ZNbIAd.js → arc-bTohInsS.js} +1 -1
  158. ksadk/server/static/assets/architecture-7GRP2DOG-BW5UtSnx.js +1 -0
  159. ksadk/server/static/assets/{architectureDiagram-5GKGNRK7-CRjgEJDX.js → architectureDiagram-5GKGNRK7-Ch5Lbzsi.js} +1 -1
  160. ksadk/server/static/assets/{blockDiagram-NRAW4CY4-C7bsBoNH.js → blockDiagram-NRAW4CY4-kqF4MDOG.js} +1 -1
  161. ksadk/server/static/assets/{c4Diagram-UCG6FXSJ-BX3c33Ob.js → c4Diagram-UCG6FXSJ-8uSRIODv.js} +1 -1
  162. ksadk/server/static/assets/channel-CF5Up3hQ.js +1 -0
  163. ksadk/server/static/assets/chunk-2Q5K7J3B-KkXX47WI.js +1 -0
  164. ksadk/server/static/assets/{chunk-5VM5RSS4-bZ7ws-8m.js → chunk-5VM5RSS4-DkhehZDr.js} +1 -1
  165. ksadk/server/static/assets/{chunk-F27PBJKO-D2r0pvhY.js → chunk-F27PBJKO-BdXhtPe9.js} +1 -1
  166. ksadk/server/static/assets/{chunk-G27WJ6UU-CZwF3oUP.js → chunk-G27WJ6UU-DPTpvpwi.js} +1 -1
  167. ksadk/server/static/assets/{chunk-JWPE2WC7-DigFYCML.js → chunk-JWPE2WC7-Pqoh38el.js} +1 -1
  168. ksadk/server/static/assets/{chunk-LCL6LL3I-CnorGV5q.js → chunk-LCL6LL3I-DeU00qC-.js} +1 -1
  169. ksadk/server/static/assets/{chunk-POPQ4Y6H-CexntQA-.js → chunk-POPQ4Y6H-CzxScFkr.js} +1 -1
  170. ksadk/server/static/assets/{chunk-RHFEMEQ7-CHTC7UxA.js → chunk-RHFEMEQ7-DpqMx-ux.js} +1 -1
  171. ksadk/server/static/assets/{chunk-SVP7TREG-BC1EWt_-.js → chunk-SVP7TREG-C6t0SypQ.js} +1 -1
  172. ksadk/server/static/assets/{chunk-XXDRQBXY-BTG24xN0.js → chunk-XXDRQBXY-DVaeafZk.js} +1 -1
  173. ksadk/server/static/assets/classDiagram-DTDB5LWJ-Cz81acRQ.js +1 -0
  174. ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-Cz81acRQ.js +1 -0
  175. ksadk/server/static/assets/{cose-bilkent-JH36ORCC-CkonRdAS.js → cose-bilkent-JH36ORCC-Bf9D3tzJ.js} +1 -1
  176. ksadk/server/static/assets/cynefin-OW5HDTMX-DVWs8a2t.js +1 -0
  177. ksadk/server/static/assets/{cynefinDiagram-5FMLGOSQ-DEOCBe15.js → cynefinDiagram-5FMLGOSQ-B9cY3iTz.js} +1 -1
  178. ksadk/server/static/assets/{dagre-3AP2YEHR-DBThPuLa.js → dagre-3AP2YEHR-CSwlx3CB.js} +1 -1
  179. ksadk/server/static/assets/{dagre-ClTPrTht.js → dagre-UBpgon5q.js} +1 -1
  180. ksadk/server/static/assets/{diagram-S7CK7UJ4-DsZHrnpJ.js → diagram-S7CK7UJ4-C9akhN9a.js} +1 -1
  181. ksadk/server/static/assets/{diagram-UQ7AKVKN-BJR4nhVr.js → diagram-UQ7AKVKN-CgedhTPI.js} +1 -1
  182. ksadk/server/static/assets/{diagram-VSXAHHWV-xzcERX0a.js → diagram-VSXAHHWV-Cjucz5BY.js} +1 -1
  183. ksadk/server/static/assets/{diagram-VX7I27RA-tn2xM1xu.js → diagram-VX7I27RA-Dd4Vleyn.js} +1 -1
  184. ksadk/server/static/assets/{diagram-Z3DM3KII-BZPGBkw5.js → diagram-Z3DM3KII-CZ2jGtYW.js} +1 -1
  185. ksadk/server/static/assets/{dist-DdRBIceA.js → dist-B2_CVgKv.js} +1 -1
  186. ksadk/server/static/assets/{dist-Cq4OL6nO.js → dist-B3FR1SKK.js} +1 -1
  187. ksadk/server/static/assets/{dist-M67Hwu0n.js → dist-BIr4DSRq.js} +1 -1
  188. ksadk/server/static/assets/{dist-B1oWRmrH.js → dist-BgV_owcB.js} +1 -1
  189. ksadk/server/static/assets/{dist-DyqV4fL4.js → dist-Bj730Ib4.js} +1 -1
  190. ksadk/server/static/assets/{dist-DfGFrb0v.js → dist-BnaTiyzN.js} +1 -1
  191. ksadk/server/static/assets/{dist-C71CsujO.js → dist-C1NMAY5a.js} +1 -1
  192. ksadk/server/static/assets/{dist-CXpH_Rdd.js → dist-C5_ZAZsG.js} +1 -1
  193. ksadk/server/static/assets/{dist-DJmCPRVB.js → dist-CB9iJrvg.js} +1 -1
  194. ksadk/server/static/assets/{dist-DjhwGUgZ.js → dist-CFzsn6BG.js} +1 -1
  195. ksadk/server/static/assets/{dist-BI1qQH4n.js → dist-Cf125P_m.js} +1 -1
  196. ksadk/server/static/assets/{dist-8VWI-2bZ.js → dist-ClYUxfeK.js} +1 -1
  197. ksadk/server/static/assets/{dist-DUwKa_l6.js → dist-DFD6Qa6T.js} +1 -1
  198. ksadk/server/static/assets/{dist-B-46q3Hk.js → dist-DVCOWtha.js} +1 -1
  199. ksadk/server/static/assets/{dist-BJLUwH6W.js → dist-D_ctSS2i.js} +1 -1
  200. ksadk/server/static/assets/{dist-4zN1_tq5.js → dist-DiixqL0J.js} +1 -1
  201. ksadk/server/static/assets/{dist-B0gdk4rl.js → dist-DoVSwoDb.js} +1 -1
  202. ksadk/server/static/assets/{dist-Bbwqa8TG.js → dist-DvFK7p5S.js} +1 -1
  203. ksadk/server/static/assets/{dist-D1V3f1B4.js → dist-QfkEHOlb.js} +1 -1
  204. ksadk/server/static/assets/{dist-Btyo4vX4.js → dist-btbmmX5O.js} +1 -1
  205. ksadk/server/static/assets/{dist-u8tVH5Ak.js → dist-fBO2v5Kt.js} +1 -1
  206. ksadk/server/static/assets/{dist-DQAB0qn_.js → dist-ufY3F5p6.js} +1 -1
  207. ksadk/server/static/assets/{ebnfDiagram-PWID7BFC-DVfMsLge.js → ebnfDiagram-PWID7BFC-Ci8NqqZu.js} +1 -1
  208. ksadk/server/static/assets/{erDiagram-SSCWMZ5O-CWEZSYaw.js → erDiagram-SSCWMZ5O-CCIOhLZG.js} +1 -1
  209. ksadk/server/static/assets/eventmodeling-NTZA5JFV-CQqR2hg3.js +1 -0
  210. ksadk/server/static/assets/flowDiagram-A5DVABFB-DDRRbGPm.js +1 -0
  211. ksadk/server/static/assets/{ganttDiagram-EL5Y4UJY-TWIxoyYL.js → ganttDiagram-EL5Y4UJY-Cp_r9Hb6.js} +1 -1
  212. ksadk/server/static/assets/gitGraph-4MIJSDKK-fgWEf7UD.js +1 -0
  213. ksadk/server/static/assets/{gitGraphDiagram-WWUBYQGX-DDh2hd2S.js → gitGraphDiagram-WWUBYQGX-Dlhc8Wr8.js} +1 -1
  214. ksadk/server/static/assets/index-DcTML1Up.css +2 -0
  215. ksadk/server/static/assets/index-Dh2XHqPK.js +244 -0
  216. ksadk/server/static/assets/info-A6RAGUB7-BUecLTdt.js +1 -0
  217. ksadk/server/static/assets/{infoDiagram-RXCK75RN-CgoyWiDR.js → infoDiagram-RXCK75RN-CO0rRTRA.js} +1 -1
  218. ksadk/server/static/assets/{ishikawaDiagram-5VMMS53U-CyRIu_om.js → ishikawaDiagram-5VMMS53U-CHiaedSS.js} +1 -1
  219. ksadk/server/static/assets/{journeyDiagram-EYS64GPL-DN5JXHzN.js → journeyDiagram-EYS64GPL-CHcCIB09.js} +1 -1
  220. ksadk/server/static/assets/{kanban-definition-3QL26DDD-CNP3ui66.js → kanban-definition-3QL26DDD-CJ_ebBZf.js} +1 -1
  221. ksadk/server/static/assets/{katex-DWEzQy1d.js → katex-B4weu383.js} +1 -1
  222. ksadk/server/static/assets/{linear-C-qHp7d2.js → linear-V-MgYHW1.js} +1 -1
  223. ksadk/server/static/assets/{mermaid-parser.core-Cl-K943T.js → mermaid-parser.core-CBijEX_c.js} +2 -2
  224. ksadk/server/static/assets/{mindmap-definition-FBJOCRG2-D5DB5mYm.js → mindmap-definition-FBJOCRG2-Bc-pQvpp.js} +1 -1
  225. ksadk/server/static/assets/packet-AYTQ26CC-BeW-btFv.js +1 -0
  226. ksadk/server/static/assets/{pegDiagram-XKGWAZYB-BQVc9ia_.js → pegDiagram-XKGWAZYB-D7genGLl.js} +1 -1
  227. ksadk/server/static/assets/pie-WAS4IAKB-X0xh4SaT.js +1 -0
  228. ksadk/server/static/assets/{pieDiagram-E7YTZNPT-B6UCAvtY.js → pieDiagram-E7YTZNPT-Bxj32zwD.js} +1 -1
  229. ksadk/server/static/assets/{quadrantDiagram-AXDQQJYC-mIr2d3eb.js → quadrantDiagram-AXDQQJYC-BURv2mGh.js} +1 -1
  230. ksadk/server/static/assets/radar-RG4KPBEZ-DzRMSqjd.js +1 -0
  231. ksadk/server/static/assets/railroad-74A4TZTK-Cc44icX-.js +1 -0
  232. ksadk/server/static/assets/railroad-abnf-HS5TGJTU-D6S4BdV-.js +1 -0
  233. ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-vD3aVE2o.js +1 -0
  234. ksadk/server/static/assets/railroad-peg-WCYAUIDC-CYbW_b4T.js +1 -0
  235. ksadk/server/static/assets/{railroadDiagram-O6MQD6OU-Dt3FFDmK.js → railroadDiagram-O6MQD6OU-CCsh3avW.js} +1 -1
  236. ksadk/server/static/assets/{requirementDiagram-EFPCY7ZU-Cz_vQtgF.js → requirementDiagram-EFPCY7ZU-_FRRBKEd.js} +1 -1
  237. ksadk/server/static/assets/{sankeyDiagram-P5KCCOFB-wlO1TsH5.js → sankeyDiagram-P5KCCOFB-C-gr2o9M.js} +1 -1
  238. ksadk/server/static/assets/{sequenceDiagram-WJ2MYXX4-CHRbElYq.js → sequenceDiagram-WJ2MYXX4-D97NmfKg.js} +1 -1
  239. ksadk/server/static/assets/{sizeCapture-X5ZJPWSS-DXX54iyO.js → sizeCapture-X5ZJPWSS-DjrPC5FX.js} +1 -1
  240. ksadk/server/static/assets/{stateDiagram-HBIQ2CUA-Dp_lw9II.js → stateDiagram-HBIQ2CUA-DzMNg1hh.js} +1 -1
  241. ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BIcjKFhF.js +1 -0
  242. ksadk/server/static/assets/{swimlanes-XN3QIQJK-D8O5UlLG.js → swimlanes-XN3QIQJK-Cssf_CsU.js} +1 -1
  243. ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-tq3K3Ybl.js +8 -0
  244. ksadk/server/static/assets/{timeline-definition-24CTP7MA-8mfKcxvG.js → timeline-definition-24CTP7MA-BWTHzyS1.js} +1 -1
  245. ksadk/server/static/assets/treeView-Q6P3EWNA-DBXt3GdU.js +1 -0
  246. ksadk/server/static/assets/treemap-WGGIJYW6-DrCy4E_l.js +1 -0
  247. ksadk/server/static/assets/{vennDiagram-4TSXK5OY-EnNl0DE7.js → vennDiagram-4TSXK5OY-BSKgJsmR.js} +1 -1
  248. ksadk/server/static/assets/wardley-WFR3VGLG-CefhOliM.js +1 -0
  249. ksadk/server/static/assets/{wardleyDiagram-VM6X3IG4-DBu5fbYr.js → wardleyDiagram-VM6X3IG4-eflHNoaZ.js} +1 -1
  250. ksadk/server/static/assets/{xychartDiagram-S5SC5T6Z-BSVXs4IY.js → xychartDiagram-S5SC5T6Z-eYqrQ-v4.js} +1 -1
  251. ksadk/server/static/index.html +2 -2
  252. ksadk/session_context.py +66 -0
  253. ksadk/sessions/__init__.py +11 -21
  254. ksadk/sessions/_local_service_sync.py +263 -1
  255. ksadk/sessions/base.py +176 -0
  256. ksadk/sessions/errors.py +4 -0
  257. ksadk/sessions/in_memory.py +309 -19
  258. ksadk/sessions/local_service.py +83 -1
  259. ksadk/sessions/persistence.py +275 -0
  260. ksadk/sessions/postgres_service.py +358 -0
  261. ksadk/sessions/resilience.py +8 -0
  262. ksadk/sessions/resilient.py +362 -4
  263. ksadk/sessions/topology.py +135 -0
  264. ksadk/skills/models.py +26 -4
  265. ksadk/skills/package_store.py +186 -38
  266. ksadk/skills/runtime/agent.py +31 -6
  267. ksadk/skills/runtime/artifact_delivery.py +139 -0
  268. ksadk/skills/runtime/backends/e2b.py +98 -6
  269. ksadk/skills/runtime/backends/local.py +73 -8
  270. ksadk/skills/runtime/base.py +3 -0
  271. ksadk/skills/runtime/executor.py +2 -0
  272. ksadk/skills/runtime/loader.py +10 -2
  273. ksadk/skills/runtime/pinned.py +114 -0
  274. ksadk/skills/runtime/request.py +31 -0
  275. ksadk/skills/service_client.py +167 -28
  276. ksadk/studio/api.py +224 -42
  277. ksadk/studio/api_catalog_routes.py +18 -0
  278. ksadk/studio/api_plugin_routes.py +526 -95
  279. ksadk/studio/api_resource_connections.py +183 -0
  280. ksadk/studio/authoring_coordinator.py +19 -14
  281. ksadk/studio/builder.py +76 -0
  282. ksadk/studio/cloud.py +342 -104
  283. ksadk/studio/cloud_shared_web.py +565 -0
  284. ksadk/studio/codex_agent_service.py +128 -26
  285. ksadk/studio/codex_builder.py +241 -2
  286. ksadk/studio/codex_credentials.py +90 -0
  287. ksadk/studio/codex_manifest.py +31 -4
  288. ksadk/studio/codex_plugin_store.py +746 -0
  289. ksadk/studio/codex_provider_build.py +231 -0
  290. ksadk/studio/codex_run.py +184 -25
  291. ksadk/studio/compatibility_report.py +12 -1
  292. ksadk/studio/compiler.py +78 -14
  293. ksadk/studio/contracts.py +132 -6
  294. ksadk/studio/dsh_application.py +137 -0
  295. ksadk/studio/dsh_capability_service.py +1107 -0
  296. ksadk/studio/dsh_models.py +59 -0
  297. ksadk/studio/dsh_provider_registration.py +320 -13
  298. ksadk/studio/hosted_kernel.py +88 -13
  299. ksadk/studio/mcp_runtime.py +11 -3
  300. ksadk/studio/model_client.py +2 -2
  301. ksadk/studio/plugin_composition.py +190 -23
  302. ksadk/studio/plugin_kernel_adapter.py +12 -112
  303. ksadk/studio/plugin_runtime.py +224 -45
  304. ksadk/studio/resource_authority.py +637 -0
  305. ksadk/studio/resource_binding_validation.py +77 -0
  306. ksadk/studio/resource_build_admission.py +196 -0
  307. ksadk/studio/resource_build_materializer.py +149 -0
  308. ksadk/studio/resource_catalog.py +143 -2
  309. ksadk/studio/resource_connections.py +220 -0
  310. ksadk/studio/run_service.py +462 -52
  311. ksadk/studio/runtime_source.py +15 -7
  312. ksadk/studio/scheduler_assistant.py +317 -0
  313. ksadk/studio/scheduler_runtime.py +9 -0
  314. ksadk/studio/scheduler_service.py +3 -0
  315. ksadk/studio/service.py +490 -44
  316. ksadk/studio/shared_web.py +446 -62
  317. ksadk/studio/static/assets/CodeBlock-DCUUY0PL-BLjA7kbS.js +41 -0
  318. ksadk/studio/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  319. ksadk/studio/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  320. ksadk/studio/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  321. ksadk/studio/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  322. ksadk/studio/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  323. ksadk/studio/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  324. ksadk/studio/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  325. ksadk/studio/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  326. ksadk/studio/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  327. ksadk/studio/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  328. ksadk/studio/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  329. ksadk/studio/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  330. ksadk/studio/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  331. ksadk/studio/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  332. ksadk/studio/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  333. ksadk/studio/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  334. ksadk/studio/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  335. ksadk/studio/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  336. ksadk/studio/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  337. ksadk/studio/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  338. ksadk/studio/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  339. ksadk/studio/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  340. ksadk/studio/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  341. ksadk/studio/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  342. ksadk/studio/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  343. ksadk/studio/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  344. ksadk/studio/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  345. ksadk/studio/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  346. ksadk/studio/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  347. ksadk/studio/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  348. ksadk/studio/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  349. ksadk/studio/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  350. ksadk/studio/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  351. ksadk/studio/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  352. ksadk/studio/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  353. ksadk/studio/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  354. ksadk/studio/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  355. ksadk/studio/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  356. ksadk/studio/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  357. ksadk/studio/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  358. ksadk/studio/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  359. ksadk/studio/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  360. ksadk/studio/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  361. ksadk/studio/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  362. ksadk/studio/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  363. ksadk/studio/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  364. ksadk/studio/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  365. ksadk/studio/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  366. ksadk/studio/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  367. ksadk/studio/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  368. ksadk/studio/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  369. ksadk/studio/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  370. ksadk/studio/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  371. ksadk/studio/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  372. ksadk/studio/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  373. ksadk/studio/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  374. ksadk/studio/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  375. ksadk/studio/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  376. ksadk/studio/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  377. ksadk/studio/static/assets/MathMessageMarkdown-CBcDrAw--BEOyPC3w.css +1 -0
  378. ksadk/studio/static/assets/MathMessageMarkdown-CBcDrAw--CUYIXwgI.js +8 -0
  379. ksadk/studio/static/assets/MermaidBlock-DBFTztfQ-BTYKWEbD.js +44 -0
  380. ksadk/studio/static/assets/PrismRenderer-wiXRHURw.js +3 -0
  381. ksadk/studio/static/assets/abnfDiagram-VCTEODGH-CLETGjKq.js +1 -0
  382. ksadk/studio/static/assets/arc-Pry-7G1_.js +1 -0
  383. ksadk/studio/static/assets/architecture-7GRP2DOG-hXMiO_AU.js +1 -0
  384. ksadk/studio/static/assets/architectureDiagram-5GKGNRK7-B5EQysk6.js +36 -0
  385. ksadk/studio/static/assets/array-BifhSqXX.js +1 -0
  386. ksadk/studio/static/assets/blockDiagram-I7D4REHJ-CFmPl1de.js +129 -0
  387. ksadk/studio/static/assets/c4Diagram-7LVT6UL2-BRrGu8qt.js +38 -0
  388. ksadk/studio/static/assets/channel-BqO-gtEw.js +1 -0
  389. ksadk/studio/static/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
  390. ksadk/studio/static/assets/chunk-4HAMMTFA-D220vUtq.js +62 -0
  391. ksadk/studio/static/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
  392. ksadk/studio/static/assets/chunk-75Z2AOVW-C8lLv_qc.js +2 -0
  393. ksadk/studio/static/assets/chunk-DU6HZSFF-C4DtVpCu.js +128 -0
  394. ksadk/studio/static/assets/chunk-F27PBJKO-Cpw14SNG.js +1 -0
  395. ksadk/studio/static/assets/chunk-FOHPRMQF-DHwB1DNv.js +161 -0
  396. ksadk/studio/static/assets/chunk-GMAD6QVW-DiQA-u2N.js +72 -0
  397. ksadk/studio/static/assets/chunk-GVQU2GXP-DUdNBUCi.js +1 -0
  398. ksadk/studio/static/assets/chunk-IMKFNOWR-BSlbjT_b.js +231 -0
  399. ksadk/studio/static/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
  400. ksadk/studio/static/assets/chunk-L3NEJ4N5-Db8Ab22W.js +1 -0
  401. ksadk/studio/static/assets/chunk-OSK3NFVY-M2m6G3j4.js +10 -0
  402. ksadk/studio/static/assets/chunk-P2QGCYS3-DezaQxiK.js +1 -0
  403. ksadk/studio/static/assets/chunk-POPQ4Y6H-DOpufJ3t.js +1 -0
  404. ksadk/studio/static/assets/chunk-PWAF6VOD-CfLb1gSc.js +1 -0
  405. ksadk/studio/static/assets/chunk-SHT3W25Y-DAyIVp5H.js +168 -0
  406. ksadk/studio/static/assets/chunk-SVP7TREG-DulomOrS.js +88 -0
  407. ksadk/studio/static/assets/chunk-TICWLB2K-bHJYoflj.js +206 -0
  408. ksadk/studio/static/assets/chunk-XXDRQBXY-7PcdR2B1.js +1 -0
  409. ksadk/studio/static/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
  410. ksadk/studio/static/assets/classDiagram-ZZMXUADV-D_bW8JjA.js +1 -0
  411. ksadk/studio/static/assets/classDiagram-v2-VYDZK3BY-D_bW8JjA.js +1 -0
  412. ksadk/studio/static/assets/cose-bilkent-JH36ORCC-snL88wkX.js +1 -0
  413. ksadk/studio/static/assets/cynefin-OW5HDTMX-Bd1YhRo1.js +1 -0
  414. ksadk/studio/static/assets/cynefinDiagram-5FMLGOSQ-BBrIG_mI.js +62 -0
  415. ksadk/studio/static/assets/cytoscape.esm-CGd-uY3x.js +321 -0
  416. ksadk/studio/static/assets/dagre-GXQ25YYZ-DtF1nGdY.js +4 -0
  417. ksadk/studio/static/assets/dagre-PrKaheQc.js +1 -0
  418. ksadk/studio/static/assets/defaultLocale-BFoDCU3G.js +1 -0
  419. ksadk/studio/static/assets/diagram-S7CK7UJ4-0BZ98mya.js +30 -0
  420. ksadk/studio/static/assets/diagram-UQ7AKVKN-6mLKEo6L.js +41 -0
  421. ksadk/studio/static/assets/diagram-VSXAHHWV-CJBIaiZ2.js +3 -0
  422. ksadk/studio/static/assets/diagram-VX7I27RA-BohLEE1C.js +24 -0
  423. ksadk/studio/static/assets/diagram-Z3DM3KII-D8KIGonG.js +24 -0
  424. ksadk/studio/static/assets/dist-v5Q1xZ2K.js +1 -0
  425. ksadk/studio/static/assets/ebnfDiagram-PWID7BFC-C99HU61t.js +1 -0
  426. ksadk/studio/static/assets/erDiagram-RLTQ6QDP-DLGry2Ap.js +99 -0
  427. ksadk/studio/static/assets/eventmodeling-NTZA5JFV-B1_5Ac8j.js +1 -0
  428. ksadk/studio/static/assets/flowDiagram-HODETNUW-DD5hmicn.js +1 -0
  429. ksadk/studio/static/assets/ganttDiagram-EL5Y4UJY-BPJrVLSG.js +292 -0
  430. ksadk/studio/static/assets/gitGraph-4MIJSDKK-Cy_ZQBGi.js +1 -0
  431. ksadk/studio/static/assets/gitGraphDiagram-WWUBYQGX-CZCUz4MT.js +106 -0
  432. ksadk/studio/static/assets/graphlib-DS17s2tU.js +1 -0
  433. ksadk/studio/static/assets/index-CeHL9ARV.css +2 -0
  434. ksadk/studio/static/assets/index-D9QEcVpL.js +320 -0
  435. ksadk/studio/static/assets/info-A6RAGUB7-DBzDQB_I.js +1 -0
  436. ksadk/studio/static/assets/infoDiagram-27XIBGKW-B-sq07Hi.js +2 -0
  437. ksadk/studio/static/assets/init-C-OQMol4.js +1 -0
  438. ksadk/studio/static/assets/ishikawaDiagram-5VMMS53U-CljI2wyf.js +70 -0
  439. ksadk/studio/static/assets/journeyDiagram-3NMN7TZE-D50Fo2sL.js +139 -0
  440. ksadk/studio/static/assets/kanban-definition-UXKFOSKX-DimfOTam.js +89 -0
  441. ksadk/studio/static/assets/katex-DhON-x74.js +257 -0
  442. ksadk/studio/static/assets/line-CX5BOOUP.js +1 -0
  443. ksadk/studio/static/assets/linear-BCxN9JLv.js +1 -0
  444. ksadk/studio/static/assets/mermaid-parser.core-BdCz8fkS.js +7 -0
  445. ksadk/studio/static/assets/mindmap-definition-YA3MSWOX-Bizzqy5R.js +96 -0
  446. ksadk/studio/static/assets/ordinal-BDEzSJ7C.js +1 -0
  447. ksadk/studio/static/assets/packet-AYTQ26CC-gPln-_kG.js +1 -0
  448. ksadk/studio/static/assets/path-fybaL0A-.js +1 -0
  449. ksadk/studio/static/assets/pegDiagram-XKGWAZYB-Dy3fB-gW.js +1 -0
  450. ksadk/studio/static/assets/pie-WAS4IAKB-qZdHMr7B.js +1 -0
  451. ksadk/studio/static/assets/pieDiagram-E7YTZNPT-B9mrFSDF.js +39 -0
  452. ksadk/studio/static/assets/quadrantDiagram-AXDQQJYC-NzUfCbMR.js +7 -0
  453. ksadk/studio/static/assets/radar-RG4KPBEZ-BZlnaea9.js +1 -0
  454. ksadk/studio/static/assets/railroad-74A4TZTK-Ce3__H9c.js +1 -0
  455. ksadk/studio/static/assets/railroad-abnf-HS5TGJTU-DqQn7EzD.js +1 -0
  456. ksadk/studio/static/assets/railroad-ebnf-LZEXJU2U-B55kCA8F.js +1 -0
  457. ksadk/studio/static/assets/railroad-peg-WCYAUIDC-DfQnvkHd.js +1 -0
  458. ksadk/studio/static/assets/railroadDiagram-O6MQD6OU-DnhMjZCk.js +1 -0
  459. ksadk/studio/static/assets/requirementDiagram-BXWQKSXE-BF3d103U.js +84 -0
  460. ksadk/studio/static/assets/rolldown-runtime-Dy4uBu1J-DK3Fl9T5.js +1 -0
  461. ksadk/studio/static/assets/rough.esm-Dy-Kn_BL.js +1 -0
  462. ksadk/studio/static/assets/sankeyDiagram-P5KCCOFB-Bo-JTwt0.js +40 -0
  463. ksadk/studio/static/assets/sequenceDiagram-WJ2MYXX4-Dh4wZtUm.js +162 -0
  464. ksadk/studio/static/assets/sizeCapture-INFHLROL-B0uUizjq.js +1 -0
  465. ksadk/studio/static/assets/src-SFn9St9H.js +1 -0
  466. ksadk/studio/static/assets/stateDiagram-D77RDMKH-DH774G6d.js +1 -0
  467. ksadk/studio/static/assets/stateDiagram-v2-MP3YSRHH-DZvVWPBo.js +1 -0
  468. ksadk/studio/static/assets/swimlanes-42K2YHIH-Bl13OvTX.js +1 -0
  469. ksadk/studio/static/assets/swimlanesDiagram-VR7AAH4N-BnFOTALJ.js +8 -0
  470. ksadk/studio/static/assets/timeline-definition-24CTP7MA-DxmSSjRB.js +120 -0
  471. ksadk/studio/static/assets/treeView-Q6P3EWNA-D5Nn9Swz.js +1 -0
  472. ksadk/studio/static/assets/treemap-WGGIJYW6-CgHgnQhE.js +1 -0
  473. ksadk/studio/static/assets/vennDiagram-4TSXK5OY-B9QEXcJq.js +34 -0
  474. ksadk/studio/static/assets/wardley-WFR3VGLG-DXrK2RRY.js +1 -0
  475. ksadk/studio/static/assets/wardleyDiagram-VM6X3IG4-Ba1AOWoe.js +78 -0
  476. ksadk/studio/static/assets/xychartDiagram-S5SC5T6Z-Cr5pt5Y-.js +7 -0
  477. ksadk/studio/static/index.html +2 -2
  478. ksadk/version.py +1 -1
  479. ksadk/server/static/assets/architecture-7GRP2DOG-Dk9nRQ6-.js +0 -1
  480. ksadk/server/static/assets/channel-s354Yo6o.js +0 -1
  481. ksadk/server/static/assets/chunk-2Q5K7J3B-CDpPpKR5.js +0 -1
  482. ksadk/server/static/assets/classDiagram-DTDB5LWJ-Dd0nucl9.js +0 -1
  483. ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-Dd0nucl9.js +0 -1
  484. ksadk/server/static/assets/cynefin-OW5HDTMX-BBlT4uMf.js +0 -1
  485. ksadk/server/static/assets/eventmodeling-NTZA5JFV-Dr5okd8V.js +0 -1
  486. ksadk/server/static/assets/flowDiagram-A5DVABFB-VEO-cVcO.js +0 -1
  487. ksadk/server/static/assets/gitGraph-4MIJSDKK-CKiKY9Cf.js +0 -1
  488. ksadk/server/static/assets/index-B2k_urY8.js +0 -241
  489. ksadk/server/static/assets/index-ILPt6IWF.css +0 -2
  490. ksadk/server/static/assets/info-A6RAGUB7-ubA4Nm_p.js +0 -1
  491. ksadk/server/static/assets/packet-AYTQ26CC-C7R3NUlK.js +0 -1
  492. ksadk/server/static/assets/pie-WAS4IAKB-BIT6qjr3.js +0 -1
  493. ksadk/server/static/assets/radar-RG4KPBEZ-l5noef9X.js +0 -1
  494. ksadk/server/static/assets/railroad-74A4TZTK-Baea6ZvM.js +0 -1
  495. ksadk/server/static/assets/railroad-abnf-HS5TGJTU-OKn8zZnU.js +0 -1
  496. ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-DdKAKC4l.js +0 -1
  497. ksadk/server/static/assets/railroad-peg-WCYAUIDC-Bds8udlG.js +0 -1
  498. ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-5b-8o2iS.js +0 -1
  499. ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-uf6WPRA4.js +0 -8
  500. ksadk/server/static/assets/treeView-Q6P3EWNA-CK5CSrSO.js +0 -1
  501. ksadk/server/static/assets/treemap-WGGIJYW6-Ch2BKgAM.js +0 -1
  502. ksadk/server/static/assets/wardley-WFR3VGLG-YW3Q0ml_.js +0 -1
  503. ksadk/studio/static/assets/PrismRenderer-IYN-ffww.js +0 -3
  504. ksadk/studio/static/assets/index-DXs1Eq4M.css +0 -1
  505. ksadk/studio/static/assets/index-DeDqwTsD.js +0 -283
  506. {agentengine_sdk_python-0.8.3.dist-info → agentengine_sdk_python-0.8.4.dist-info}/WHEEL +0 -0
  507. {agentengine_sdk_python-0.8.3.dist-info → agentengine_sdk_python-0.8.4.dist-info}/entry_points.txt +0 -0
  508. {agentengine_sdk_python-0.8.3.dist-info → agentengine_sdk_python-0.8.4.dist-info}/licenses/LICENSE +0 -0
  509. {agentengine_sdk_python-0.8.3.dist-info → agentengine_sdk_python-0.8.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1107 @@
1
+ """Studio-owned lifecycle for one profile-level DSH MCP capability host.
2
+
3
+ The service is intentionally ephemeral. It caches one host for the current
4
+ immutable Profile projection, keeps its loopback endpoint and bearer lease in
5
+ memory only, and exposes only descriptor/tool/result projections to routes.
6
+ Profile refresh and shutdown cancel all tracked calls before disposing the
7
+ sidecar.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import asyncio
13
+ import json
14
+ import os
15
+ import re
16
+ import secrets
17
+ from collections.abc import Callable, Coroutine, Mapping, Sequence
18
+ from dataclasses import dataclass
19
+ from dataclasses import field as dataclass_field
20
+ from pathlib import Path
21
+ from typing import Any, Literal, cast
22
+
23
+ import httpx
24
+ from pydantic import ValidationError
25
+
26
+ from ksadk.plugins.bridges.dsh import (
27
+ DshProfileBuildSnapshot,
28
+ DshProfilePluginBridge,
29
+ DshProfileProjection,
30
+ )
31
+ from ksadk.plugins.dsh_toolchain import DshToolchainManager
32
+ from ksadk.plugins.host import PluginHostError
33
+ from ksadk.plugins.providers.dsh_capabilities import (
34
+ DSH_CAPABILITY_BUNDLE_PACKAGE,
35
+ DSH_CAPABILITY_HOST_VERSION,
36
+ DSH_CAPABILITY_MCP_PROTOCOL,
37
+ DshCapabilityTool,
38
+ DshMcpConnectorLease,
39
+ DshProfileCapabilityDescriptor,
40
+ DshProfileCapabilityHost,
41
+ DshProfileCapabilityInventory,
42
+ )
43
+ from ksadk.resource_runtime.broker import ResourceWriteAuthorizer
44
+ from ksadk.resource_runtime.operation_ledger import OperationLedger
45
+ from ksadk.resource_runtime.supervisor import (
46
+ ActiveResources,
47
+ ResourceRevalidator,
48
+ ResourceSupervisor,
49
+ )
50
+ from ksadk.resource_runtime.worker import WorkerInitialization
51
+ from ksadk.studio.errors import StudioError
52
+
53
+ _CALL_ID = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$")
54
+ _TOOL_NAME = re.compile(r"^[A-Za-z0-9_.:-]{1,128}$")
55
+ _DIGEST = re.compile(r"^sha256:[0-9a-f]{64}$")
56
+ _GENERATION_ID = re.compile(r"^dshgen_[A-Za-z0-9_-]{24,96}$")
57
+ _MAX_ARGUMENT_BYTES = 256 * 1024
58
+ _MAX_RESPONSE_BYTES = 1024 * 1024
59
+ _JSON_RPC_ENVELOPE_BYTES = 16 * 1024
60
+
61
+ BridgeFactory = Callable[..., DshProfilePluginBridge]
62
+ HostFactory = Callable[..., DshProfileCapabilityHost]
63
+
64
+
65
+ @dataclass(frozen=True)
66
+ class _ActiveCall:
67
+ lease: DshMcpConnectorLease
68
+ tool_name: str
69
+
70
+
71
+ @dataclass(frozen=True)
72
+ class DshCapabilityRuntimeSnapshot:
73
+ """One atomically fenced descriptor/lease pair for PluginHost."""
74
+
75
+ descriptor: DshProfileCapabilityDescriptor
76
+ lease: DshMcpConnectorLease = dataclass_field(repr=False)
77
+
78
+
79
+ @dataclass(frozen=True)
80
+ class DshCapabilitySnapshot:
81
+ """One generation-consistent public capability inventory."""
82
+
83
+ descriptor: DshProfileCapabilityDescriptor
84
+ tools: tuple[DshCapabilityTool, ...]
85
+ inventory: DshProfileCapabilityInventory
86
+ generation_id: str
87
+
88
+
89
+ def _strict_json_size(value: Any) -> int:
90
+ try:
91
+ encoded = json.dumps(
92
+ value,
93
+ allow_nan=False,
94
+ ensure_ascii=False,
95
+ separators=(",", ":"),
96
+ ).encode("utf-8")
97
+ except (TypeError, ValueError, RecursionError) as error:
98
+ raise StudioError(
99
+ "DSH_CAPABILITY_ARGUMENTS_INVALID",
100
+ "DSH 工具参数必须是严格 JSON 对象",
101
+ status_code=422,
102
+ ) from error
103
+ return len(encoded)
104
+
105
+
106
+ def _duplicate_rejecting_object(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
107
+ value: dict[str, Any] = {}
108
+ for key, item in pairs:
109
+ if key in value:
110
+ raise ValueError("duplicate JSON member")
111
+ value[key] = item
112
+ return value
113
+
114
+
115
+ def _reject_non_finite(value: str) -> None:
116
+ raise ValueError(f"non-finite JSON number: {value}")
117
+
118
+
119
+ class StudioDshCapabilityService:
120
+ """Lazy, generation-fenced access to a DSH Profile's real MCP lease."""
121
+
122
+ def __init__(
123
+ self,
124
+ workspace: Path,
125
+ *,
126
+ dsh_home: Path,
127
+ profile: str = "web",
128
+ dsh_command: Sequence[str] | None = None,
129
+ bridge_factory: BridgeFactory = DshProfilePluginBridge,
130
+ host_factory: HostFactory = DshProfileCapabilityHost,
131
+ mount_studio_app: bool = True,
132
+ max_argument_bytes: int = _MAX_ARGUMENT_BYTES,
133
+ max_response_bytes: int = _MAX_RESPONSE_BYTES,
134
+ ) -> None:
135
+ self._workspace = workspace.resolve()
136
+ self._dsh_home = dsh_home.expanduser().resolve()
137
+ self._profile = profile
138
+ if isinstance(dsh_command, (str, bytes)):
139
+ raise ValueError("dsh_command must be a one-item argv sequence")
140
+ normalized_command = tuple(dsh_command) if dsh_command is not None else ()
141
+ if len(normalized_command) > 1:
142
+ raise ValueError("dsh_command must contain only the DSH executable")
143
+ self._explicit_dsh_executable = normalized_command[0] if normalized_command else None
144
+ self._bridge_factory = bridge_factory
145
+ self._host_factory = host_factory
146
+ self._mount_studio_app = mount_studio_app
147
+ if (
148
+ isinstance(max_argument_bytes, bool)
149
+ or max_argument_bytes < 1
150
+ or max_argument_bytes > 8 * 1024 * 1024
151
+ or isinstance(max_response_bytes, bool)
152
+ or max_response_bytes < 1
153
+ or max_response_bytes > 8 * 1024 * 1024
154
+ ):
155
+ raise ValueError("DSH capability size limits are invalid")
156
+ self._max_argument_bytes = max_argument_bytes
157
+ self._max_result_bytes = max_response_bytes
158
+ self._max_wire_response_bytes = max_response_bytes + _JSON_RPC_ENVELOPE_BYTES
159
+ self._lock = asyncio.Lock()
160
+ self._host: DshProfileCapabilityHost | None = None
161
+ self._lease: DshMcpConnectorLease | None = None
162
+ self._projection: DshProfileProjection | None = None
163
+ self._generation_id: str | None = None
164
+ self._resource_supervisor: ResourceSupervisor | None = None
165
+ self._resource_generation_snapshot: DshProfileBuildSnapshot | None = None
166
+ self._active_calls: dict[str, _ActiveCall] = {}
167
+ self._closed = False
168
+ self.model_projection = None
169
+
170
+ @classmethod
171
+ def discover_or_create_workspace_default(cls, workspace: Path) -> "StudioDshCapabilityService":
172
+ root = workspace.resolve()
173
+ configured_home = os.environ.get("KSADK_DSH_HOME", "").strip()
174
+ dsh_home = (
175
+ Path(configured_home).expanduser()
176
+ if configured_home
177
+ else root / ".agentkit" / "dsh-home"
178
+ )
179
+ profile = os.environ.get("KSADK_DSH_PROFILE", "").strip() or "web"
180
+ configured_bin = os.environ.get("KSADK_DSH_BIN", "").strip()
181
+ command = (str(Path(configured_bin).expanduser()),) if configured_bin else None
182
+ return cls(
183
+ root,
184
+ dsh_home=dsh_home,
185
+ profile=profile,
186
+ dsh_command=command,
187
+ )
188
+
189
+ @classmethod
190
+ def create_workspace_resource_default(cls, workspace: Path) -> "StudioDshCapabilityService":
191
+ """Create the closed execution Profile used by platform resources."""
192
+
193
+ root = workspace.resolve()
194
+ configured_bin = os.environ.get("KSADK_DSH_BIN", "").strip()
195
+ command = (str(Path(configured_bin).expanduser()),) if configured_bin else None
196
+ return cls(
197
+ root,
198
+ dsh_home=root / ".agentkit" / "dsh-home",
199
+ profile="agentkit-resources",
200
+ dsh_command=command,
201
+ mount_studio_app=False,
202
+ )
203
+
204
+ @property
205
+ def profile(self) -> str:
206
+ return self._profile
207
+
208
+ async def application_lease(self) -> DshMcpConnectorLease:
209
+ """Reuse the live generation for browser assets and long-lived streams.
210
+
211
+ Management mutations dispose the generation explicitly. Avoid running
212
+ Profile projection and health checks for every CSS/JS/RPC request.
213
+ """
214
+ if self._lease is not None and self._host is not None and self._host.pid is not None:
215
+ return self._lease
216
+ return await self.connector_lease()
217
+
218
+ async def has_enabled_profile_plugins(self) -> bool:
219
+ """Inspect Profile metadata without starting the capability sidecar."""
220
+
221
+ async with self._lock:
222
+ if self._closed:
223
+ raise StudioError(
224
+ "DSH_CAPABILITY_SERVICE_CLOSED",
225
+ "DSH capability service 已关闭",
226
+ status_code=503,
227
+ )
228
+ command = await asyncio.to_thread(self._resolve_command)
229
+ return await asyncio.to_thread(self._profile_has_enabled_plugins, command)
230
+
231
+ def capture_resource_build_snapshot(self) -> DshProfileBuildSnapshot:
232
+ """Capture immutable Profile inputs for a caller-owned Build staging area.
233
+
234
+ The bridge uses the cross-process Profile transaction lock, so this
235
+ synchronous method is safe to call from Studio's existing Build worker
236
+ thread without starting or mutating the live Core generation.
237
+ """
238
+
239
+ if self._closed:
240
+ raise StudioError(
241
+ "DSH_CAPABILITY_SERVICE_CLOSED",
242
+ "DSH capability service 已关闭",
243
+ status_code=503,
244
+ )
245
+ command = self._resolve_command()
246
+ try:
247
+ with self._bridge_factory(
248
+ dsh_home=self._dsh_home,
249
+ profile=self._profile,
250
+ dsh_command=command,
251
+ cwd=self._workspace,
252
+ ) as bridge:
253
+ return bridge.snapshot_for_build()
254
+ except StudioError:
255
+ raise
256
+ except Exception as error:
257
+ raise self._unavailable(error) from error
258
+
259
+ async def describe(self) -> DshProfileCapabilityDescriptor:
260
+ host, _lease = await self._ready_generation()
261
+ return host.descriptor
262
+
263
+ async def runtime_snapshot(self) -> DshCapabilityRuntimeSnapshot:
264
+ """Fence descriptor and credential to the same supervised generation."""
265
+
266
+ async with self._lock:
267
+ host, lease = await self._ensure_ready_locked()
268
+ descriptor = host.descriptor
269
+ self._validate_lease(descriptor, lease)
270
+ return DshCapabilityRuntimeSnapshot(descriptor=descriptor, lease=lease)
271
+
272
+ async def capability_snapshot(self) -> DshCapabilitySnapshot:
273
+ """Read descriptor, tools and health without crossing a refresh."""
274
+
275
+ async with self._lock:
276
+ host, lease = await self._ensure_ready_locked()
277
+ descriptor = host.descriptor
278
+ self._validate_lease(descriptor, lease)
279
+ tools = await self._list_tools_for_generation(host, lease)
280
+ inventory = await host.inventory()
281
+ if (
282
+ inventory.state != "ready"
283
+ or inventory.profile != descriptor.profile
284
+ or inventory.profile_digest != descriptor.profile_digest
285
+ or inventory.descriptor_digest != descriptor.descriptor_digest
286
+ or inventory.inventory_digest != descriptor.inventory_digest
287
+ or inventory.tool_count != len(descriptor.tools)
288
+ ):
289
+ raise self._protocol_error()
290
+ return DshCapabilitySnapshot(
291
+ descriptor=descriptor,
292
+ tools=tools,
293
+ inventory=inventory,
294
+ generation_id=self._require_generation_id(),
295
+ )
296
+
297
+ async def descriptor_generation(self) -> tuple[DshProfileCapabilityDescriptor, str]:
298
+ """Return a descriptor and non-secret lease epoch under one lock."""
299
+
300
+ async with self._lock:
301
+ host, _lease = await self._ensure_ready_locked()
302
+ return host.descriptor, self._require_generation_id()
303
+
304
+ async def connector_lease(self) -> DshMcpConnectorLease:
305
+ """Return the current in-memory lease to a trusted PluginHost adapter."""
306
+
307
+ host, lease = await self._ready_generation()
308
+ descriptor = host.descriptor
309
+ self._validate_lease(descriptor, lease)
310
+ return lease
311
+
312
+ async def inventory(self) -> DshProfileCapabilityInventory:
313
+ host, _lease = await self._ready_generation()
314
+ return await host.inventory()
315
+
316
+ async def list_tools(
317
+ self,
318
+ *,
319
+ expected_descriptor_digest: str | None = None,
320
+ expected_generation_id: str | None = None,
321
+ ) -> tuple[DshCapabilityTool, ...]:
322
+ self._validate_expected_generation(expected_descriptor_digest, expected_generation_id)
323
+ async with self._lock:
324
+ host, lease = await self._ensure_ready_locked()
325
+ self._require_expected_generation(
326
+ host.descriptor,
327
+ expected_descriptor_digest,
328
+ expected_generation_id,
329
+ )
330
+ return await self._list_tools_for_generation(host, lease)
331
+
332
+ async def _list_tools_for_generation(
333
+ self,
334
+ host: DshProfileCapabilityHost,
335
+ lease: DshMcpConnectorLease,
336
+ ) -> tuple[DshCapabilityTool, ...]:
337
+ request_id = f"list-{os.urandom(12).hex()}"
338
+ result = await self._mcp_request(
339
+ lease,
340
+ request_id=request_id,
341
+ method="tools/list",
342
+ params={},
343
+ timeout_seconds=5.0,
344
+ )
345
+ raw_tools = result.get("tools")
346
+ if not isinstance(raw_tools, list):
347
+ raise self._protocol_error()
348
+ try:
349
+ tools = tuple(DshCapabilityTool.model_validate(item) for item in raw_tools)
350
+ except (TypeError, ValidationError, ValueError) as error:
351
+ raise self._protocol_error() from error
352
+ if tools != host.descriptor.tools:
353
+ raise StudioError(
354
+ "DSH_CAPABILITY_INVENTORY_CHANGED",
355
+ "DSH 工具清单已变化,请刷新插件 Profile 后重试",
356
+ status_code=409,
357
+ )
358
+ return tools
359
+
360
+ @staticmethod
361
+ def _validate_lease(
362
+ descriptor: DshProfileCapabilityDescriptor,
363
+ lease: DshMcpConnectorLease,
364
+ ) -> None:
365
+ if (
366
+ lease.profile != descriptor.profile
367
+ or lease.profile_digest != descriptor.profile_digest
368
+ or lease.descriptor_digest != descriptor.descriptor_digest
369
+ ):
370
+ raise StudioDshCapabilityService._protocol_error()
371
+
372
+ async def call_tool(
373
+ self,
374
+ *,
375
+ call_id: str,
376
+ tool_name: str,
377
+ arguments: Mapping[str, Any],
378
+ deadline_ms: int,
379
+ expected_descriptor_digest: str | None = None,
380
+ expected_generation_id: str | None = None,
381
+ ) -> dict[str, Any]:
382
+ self._validate_expected_generation(expected_descriptor_digest, expected_generation_id)
383
+ if not _CALL_ID.fullmatch(call_id):
384
+ raise StudioError(
385
+ "DSH_CAPABILITY_CALL_ID_INVALID",
386
+ "DSH 工具调用标识无效",
387
+ status_code=422,
388
+ )
389
+ if not isinstance(tool_name, str) or not _TOOL_NAME.fullmatch(tool_name):
390
+ raise StudioError(
391
+ "DSH_CAPABILITY_TOOL_INVALID",
392
+ "DSH 工具名称无效",
393
+ status_code=422,
394
+ )
395
+ if not isinstance(arguments, Mapping):
396
+ raise StudioError(
397
+ "DSH_CAPABILITY_ARGUMENTS_INVALID",
398
+ "DSH 工具参数必须是 JSON 对象",
399
+ status_code=422,
400
+ )
401
+ detached_arguments = dict(arguments)
402
+ if _strict_json_size(detached_arguments) > self._max_argument_bytes:
403
+ raise StudioError(
404
+ "DSH_CAPABILITY_ARGUMENTS_TOO_LARGE",
405
+ "DSH 工具参数超过大小限制",
406
+ status_code=413,
407
+ )
408
+ if (
409
+ isinstance(deadline_ms, bool)
410
+ or not isinstance(deadline_ms, int)
411
+ or deadline_ms < 1
412
+ or deadline_ms > 120_000
413
+ ):
414
+ raise StudioError(
415
+ "DSH_CAPABILITY_DEADLINE_INVALID",
416
+ "DSH 工具调用超时值无效",
417
+ status_code=422,
418
+ )
419
+
420
+ async with self._lock:
421
+ host, lease = await self._ensure_ready_locked()
422
+ self._require_expected_generation(
423
+ host.descriptor,
424
+ expected_descriptor_digest,
425
+ expected_generation_id,
426
+ )
427
+ if tool_name not in {tool.name for tool in host.descriptor.tools}:
428
+ raise StudioError(
429
+ "DSH_CAPABILITY_TOOL_FORBIDDEN",
430
+ "请求的 DSH 工具不在当前能力清单中",
431
+ status_code=403,
432
+ )
433
+ if call_id in self._active_calls:
434
+ raise StudioError(
435
+ "DSH_CAPABILITY_CALL_DUPLICATE",
436
+ "DSH 工具调用标识已在使用",
437
+ status_code=409,
438
+ )
439
+ self._active_calls[call_id] = _ActiveCall(lease=lease, tool_name=tool_name)
440
+
441
+ try:
442
+ try:
443
+ return await asyncio.wait_for(
444
+ self._mcp_request(
445
+ lease,
446
+ request_id=call_id,
447
+ method="tools/call",
448
+ params={"name": tool_name, "arguments": detached_arguments},
449
+ timeout_seconds=(deadline_ms / 1000.0) + 2.0,
450
+ ),
451
+ timeout=(deadline_ms / 1000.0) + 2.0,
452
+ )
453
+ except asyncio.TimeoutError as error:
454
+ await self._send_cancel(lease, call_id)
455
+ raise StudioError(
456
+ "DSH_CAPABILITY_CALL_TIMEOUT",
457
+ "DSH 工具调用超时",
458
+ status_code=504,
459
+ ) from error
460
+ except asyncio.CancelledError:
461
+ await self._send_cancel(lease, call_id)
462
+ raise
463
+ finally:
464
+ async with self._lock:
465
+ current = self._active_calls.get(call_id)
466
+ if current is not None and current.lease is lease:
467
+ self._active_calls.pop(call_id, None)
468
+
469
+ async def cancel(self, call_id: str) -> bool:
470
+ async with self._lock:
471
+ active = self._active_calls.get(call_id)
472
+ if active is None:
473
+ return False
474
+ await self._send_cancel(active.lease, call_id)
475
+ return True
476
+
477
+ async def revoke_runtime_token(
478
+ self,
479
+ lease: DshMcpConnectorLease,
480
+ token: str,
481
+ ) -> None:
482
+ """Revoke one activation-scoped token or kill its generation on failure."""
483
+
484
+ if not isinstance(token, str) or not token.startswith("ks1.") or len(token) > 12 * 1024:
485
+ raise StudioError(
486
+ "DSH_CAPABILITY_SCOPE_INVALID",
487
+ "DSH runtime credential 无效",
488
+ status_code=422,
489
+ )
490
+ async with self._lock:
491
+ if self._closed or self._lease is not lease:
492
+ # A replaced/disposed process has already invalidated its HMAC
493
+ # root, so no live credential remains to revoke.
494
+ return
495
+ try:
496
+ result = await self._mcp_request(
497
+ lease,
498
+ request_id=f"revoke-{os.urandom(12).hex()}",
499
+ method="io.ksadk/scopes/revoke",
500
+ params={"token": token},
501
+ timeout_seconds=2.0,
502
+ )
503
+ if result.get("revoked") is not True:
504
+ raise self._protocol_error()
505
+ except BaseException:
506
+ # Revocation is a security boundary. If it cannot be proven,
507
+ # terminating the generation invalidates every derived token.
508
+ await self._dispose_generation_locked()
509
+ raise
510
+
511
+ async def refresh(self) -> None:
512
+ """Cancel the cached generation and allow the next call to start afresh."""
513
+
514
+ async with self._lock:
515
+ if self._closed:
516
+ return
517
+ await self._dispose_generation_locked()
518
+
519
+ async def aclose(self) -> None:
520
+ async with self._lock:
521
+ if self._closed:
522
+ return
523
+ self._closed = True
524
+ await self._dispose_generation_locked()
525
+
526
+ async def _ready_generation(
527
+ self,
528
+ ) -> tuple[DshProfileCapabilityHost, DshMcpConnectorLease]:
529
+ async with self._lock:
530
+ return await self._ensure_ready_locked()
531
+
532
+ async def _ensure_ready_locked(
533
+ self,
534
+ ) -> tuple[DshProfileCapabilityHost, DshMcpConnectorLease]:
535
+ if self._closed:
536
+ raise StudioError(
537
+ "DSH_CAPABILITY_SERVICE_CLOSED",
538
+ "DSH capability service 已关闭",
539
+ status_code=503,
540
+ )
541
+ host = self._host
542
+ projection = self._projection
543
+ if host is not None:
544
+ if projection is None: # pragma: no cover - internal invariant guard
545
+ await self._dispose_generation_locked()
546
+ raise self._protocol_error()
547
+ if self._lease is not None:
548
+ try:
549
+ if await host.health():
550
+ return host, self._lease
551
+ except (PluginHostError, OSError):
552
+ pass
553
+ self._lease = None
554
+ return await self._start_host_locked(host, projection)
555
+
556
+ try:
557
+ command = await asyncio.to_thread(self._resolve_command)
558
+ projection = await asyncio.to_thread(self._project_profile, command)
559
+ host = self._host_factory(
560
+ command,
561
+ projection=projection,
562
+ dsh_home=self._dsh_home,
563
+ cwd=self._workspace,
564
+ studio_index=(
565
+ Path(__file__).with_name("static") / "index.html"
566
+ if self._mount_studio_app
567
+ else None
568
+ ),
569
+ studio_models=self.model_projection() if self.model_projection else None,
570
+ max_argument_bytes=self._max_argument_bytes,
571
+ max_result_bytes=self._max_result_bytes,
572
+ max_request_bytes=self._max_argument_bytes + 16 * 1024,
573
+ )
574
+ except StudioError:
575
+ raise
576
+ except (PluginHostError, OSError, ValueError) as error:
577
+ raise self._unavailable(error) from error
578
+
579
+ # Retain one Host across transient start failures. Its circuit breaker
580
+ # is generation state; replacing it here would make repeated crashes
581
+ # look like unrelated first failures and prevent the breaker opening.
582
+ self._projection = projection
583
+ self._host = host
584
+ return await self._start_host_locked(host, projection)
585
+
586
+ async def _start_host_locked(
587
+ self,
588
+ host: DshProfileCapabilityHost,
589
+ projection: DshProfileProjection,
590
+ ) -> tuple[DshProfileCapabilityHost, DshMcpConnectorLease]:
591
+ # A restarted Core must never inherit workers or resource handles from
592
+ # the previous generation, including when the restart subsequently fails.
593
+ self._resource_generation_snapshot = None
594
+ await self._close_resource_supervisor_locked()
595
+ generation_id = f"dshgen_{secrets.token_urlsafe(24)}"
596
+ try:
597
+ if "@kingsoftcloud/dsh-platform-resources" in projection.bundles:
598
+ ledger = await asyncio.to_thread(
599
+ OperationLedger, self._workspace / ".agentkit" / "resource-operations"
600
+ )
601
+ self._resource_supervisor = ResourceSupervisor(
602
+ generation_id, operation_ledger=ledger,
603
+ )
604
+ socket_path = await self._resource_supervisor.start_broker()
605
+ await host.configure_resource_socket(socket_path)
606
+ lease = await host.lease()
607
+ descriptor = host.descriptor
608
+ if (
609
+ lease.profile != projection.profile
610
+ or lease.profile_digest != projection.config_digest
611
+ or lease.descriptor_digest != descriptor.descriptor_digest
612
+ ):
613
+ raise self._protocol_error()
614
+ await self._initialize_lease(lease)
615
+ except StudioError as error:
616
+ self._lease = None
617
+ await self._close_resource_supervisor_locked()
618
+ if error.code == "DSH_CAPABILITY_PROTOCOL_INVALID":
619
+ await self._dispose_generation_locked()
620
+ raise
621
+ except (PluginHostError, OSError, ValueError) as error:
622
+ self._lease = None
623
+ await self._close_resource_supervisor_locked()
624
+ raise self._unavailable(error) from error
625
+ except BaseException:
626
+ self._lease = None
627
+ await self._close_resource_supervisor_locked()
628
+ raise
629
+ self._lease = lease
630
+ self._generation_id = generation_id
631
+ return host, lease
632
+
633
+ async def prepare_resource_generation(
634
+ self, expected: DshProfileBuildSnapshot
635
+ ) -> tuple[DshProfileCapabilityDescriptor, str]:
636
+ """Admit a cold Core or share an already attested, identical installation.
637
+
638
+ A same-Profile Core started for Studio UI contributions has no resource
639
+ Build owner yet. Restart it under the frozen snapshot so loading a client
640
+ plugin cannot make platform-resource Agents impossible to run. A generation
641
+ already owned by another resource Build is never replaced implicitly.
642
+ """
643
+ expected = DshProfileBuildSnapshot.model_validate_json(expected.model_dump_json())
644
+ async with self._lock:
645
+ if (
646
+ self._host is not None and self._resource_generation_snapshot != expected
647
+ ):
648
+ if (
649
+ self._resource_generation_snapshot is not None
650
+ or self._projection != expected.projection
651
+ ):
652
+ raise StudioError(
653
+ "RESOURCE_PROFILE_IN_USE",
654
+ "当前 DSH generation 已由其他配置占用;请停止对应运行或选择独立 profile",
655
+ status_code=409,
656
+ )
657
+ if self._lease is not None:
658
+ # The live Core only serves the exact same Profile projection
659
+ # and has not activated resources. Replace it so the process
660
+ # itself starts between the two immutable-installation checks
661
+ # below. A Host retained after a failed start has no live
662
+ # generation and remains the circuit-breaker owner for retry.
663
+ await self._dispose_generation_locked()
664
+ command = await asyncio.to_thread(self._resolve_command)
665
+ try:
666
+ await asyncio.to_thread(self._verify_resource_build_snapshot, command, expected)
667
+ except BaseException:
668
+ await self._dispose_generation_locked()
669
+ raise
670
+ host, _ = await self._ensure_ready_locked()
671
+ try:
672
+ if self._projection != expected.projection:
673
+ raise StudioError(
674
+ "RESOURCE_BUILD_PROFILE_MISMATCH",
675
+ "DSH Core 与 Build 配置不匹配",
676
+ status_code=409,
677
+ )
678
+ await asyncio.to_thread(self._verify_resource_build_snapshot, command, expected)
679
+ except BaseException:
680
+ await self._dispose_generation_locked()
681
+ raise
682
+ self._resource_generation_snapshot = expected
683
+ return host.descriptor, self._require_generation_id()
684
+
685
+ async def activate_resources(
686
+ self, initialization: WorkerInitialization, *, expected: DshProfileBuildSnapshot,
687
+ write_authorizer: ResourceWriteAuthorizer | None = None,
688
+ ) -> ActiveResources:
689
+ """Trusted execution adapter entry after Build and upstream admission.
690
+
691
+ This does not install bundles or expose a resource MCP token. It owns
692
+ worker lifetime alongside the matching existing Core generation.
693
+ """
694
+ initialization = WorkerInitialization.model_validate(initialization.pipe_payload())
695
+ async with self._lock:
696
+ if self._resource_generation_snapshot != expected:
697
+ raise StudioError(
698
+ "RESOURCE_BUILD_NOT_PREPARED",
699
+ "资源 Build 尚未准备对应的 DSH generation",
700
+ status_code=409,
701
+ )
702
+ if initialization.resource_snapshot.dsh_profile != expected:
703
+ raise StudioError(
704
+ "RESOURCE_BUILD_PROFILE_MISMATCH",
705
+ "资源 Build 未锁定当前 DSH 安装快照,请重新构建",
706
+ status_code=409,
707
+ )
708
+ command = await asyncio.to_thread(self._resolve_command)
709
+ try:
710
+ await asyncio.to_thread(self._verify_resource_build_snapshot, command, expected)
711
+ except BaseException:
712
+ await self._dispose_generation_locked()
713
+ raise
714
+ _, lease = await self._ensure_ready_locked()
715
+ if self._resource_generation_snapshot != expected:
716
+ raise StudioError(
717
+ "RESOURCE_BUILD_NOT_PREPARED",
718
+ "DSH 重启后需要重新准备资源 Build",
719
+ status_code=409,
720
+ )
721
+ first = initialization.scopes[0]
722
+ if (
723
+ first.generation_id != self._generation_id
724
+ or first.profile_digest != lease.profile_digest
725
+ ):
726
+ raise StudioError(
727
+ "RESOURCE_BUILD_PROFILE_MISMATCH",
728
+ "资源运行实例与当前 DSH profile/generation 不匹配",
729
+ status_code=409,
730
+ )
731
+ if self._resource_supervisor is None:
732
+ ledger = await asyncio.to_thread(
733
+ OperationLedger, self._workspace / ".agentkit" / "resource-operations"
734
+ )
735
+ self._resource_supervisor = ResourceSupervisor(
736
+ first.generation_id, operation_ledger=ledger,
737
+ )
738
+ return await self._resource_supervisor.activate(
739
+ initialization, write_authorizer=write_authorizer,
740
+ )
741
+
742
+ async def deactivate_resources(self, activation_id: str) -> None:
743
+ async with self._lock:
744
+ if self._resource_supervisor is not None:
745
+ await self._resource_supervisor.deactivate(activation_id)
746
+
747
+ async def resource_connector_lease(
748
+ self,
749
+ current: ActiveResources,
750
+ ) -> DshMcpConnectorLease:
751
+ """Return the matching Core lease only while the worker is still live."""
752
+
753
+ async with self._lock:
754
+ _host, lease = await self._ensure_ready_locked()
755
+ supervisor = self._resource_supervisor
756
+ if (
757
+ supervisor is None
758
+ or not current.leases
759
+ or current.leases[0].scope.generation_id != self._generation_id
760
+ or current.leases[0].scope.profile_digest != lease.profile_digest
761
+ or not await supervisor.owns(current)
762
+ ):
763
+ raise StudioError(
764
+ "RESOURCE_ACTIVATION_STALE",
765
+ "资源 worker 已失效,需要重新激活",
766
+ status_code=409,
767
+ )
768
+ return lease
769
+
770
+ async def renew_resources(
771
+ self, current: ActiveResources, *, expected: DshProfileBuildSnapshot,
772
+ revalidate: ResourceRevalidator,
773
+ ) -> ActiveResources:
774
+ async with self._lock:
775
+ if self._resource_supervisor is None or self._resource_generation_snapshot != expected:
776
+ raise StudioError(
777
+ "RESOURCE_BUILD_NOT_PREPARED", "资源 Build 尚未准备", status_code=409,
778
+ )
779
+ supervisor = self._resource_supervisor
780
+ try:
781
+ command = await asyncio.to_thread(self._resolve_command)
782
+ await asyncio.to_thread(self._verify_resource_build_snapshot, command, expected)
783
+ except BaseException:
784
+ async with self._lock:
785
+ if self._resource_supervisor is supervisor:
786
+ await self._dispose_generation_locked()
787
+ raise
788
+ # Platform checks cannot hold the Studio lock and delay stop/revocation.
789
+ return await supervisor.renew(current, revalidate=revalidate)
790
+
791
+ async def resource_runtime_status(self, *, agent_id: str, activation_id: str) -> dict | None:
792
+ # Observations never start a Core or replace a generation.
793
+ async with self._lock:
794
+ if self._resource_supervisor is None:
795
+ return None
796
+ return await self._resource_supervisor.runtime_status(
797
+ agent_id=agent_id, activation_id=activation_id,
798
+ )
799
+
800
+ async def _close_resource_supervisor_locked(self) -> None:
801
+ supervisor = self._resource_supervisor
802
+ self._resource_supervisor = None
803
+ if supervisor is not None:
804
+ await self._finish_cleanup(supervisor.aclose())
805
+
806
+ def _resolve_command(self) -> tuple[str, ...]:
807
+ try:
808
+ return tuple(DshToolchainManager().require_command(self._explicit_dsh_executable))
809
+ except Exception as error:
810
+ raise StudioError(
811
+ "DSH_CAPABILITY_HOST_UNAVAILABLE",
812
+ "DSH capability host 未安装、版本不匹配或不可用",
813
+ status_code=503,
814
+ ) from error
815
+
816
+ def _project_profile(self, command: Sequence[str]) -> DshProfileProjection:
817
+ try:
818
+ with self._bridge_factory(
819
+ dsh_home=self._dsh_home,
820
+ profile=self._profile,
821
+ dsh_command=command,
822
+ cwd=self._workspace,
823
+ ) as bridge:
824
+ return bridge.project_profile()
825
+ except Exception as error:
826
+ raise self._unavailable(error) from error
827
+
828
+ def _profile_has_enabled_plugins(self, command: Sequence[str]) -> bool:
829
+ try:
830
+ with self._bridge_factory(
831
+ dsh_home=self._dsh_home,
832
+ profile=self._profile,
833
+ dsh_command=command,
834
+ cwd=self._workspace,
835
+ ) as bridge:
836
+ return any(item.enabled for item in bridge.list_plugins())
837
+ except Exception as error:
838
+ raise self._unavailable(error) from error
839
+
840
+ def _verify_resource_build_snapshot(
841
+ self, command: Sequence[str], expected: DshProfileBuildSnapshot
842
+ ) -> None:
843
+ try:
844
+ with self._bridge_factory(
845
+ dsh_home=self._dsh_home,
846
+ profile=self._profile,
847
+ dsh_command=command,
848
+ cwd=self._workspace,
849
+ ) as bridge:
850
+ bridge.verify_build_snapshot(expected)
851
+ except Exception as error:
852
+ raise StudioError(
853
+ "RESOURCE_BUILD_INSTALLATION_MISMATCH",
854
+ "DSH 已安装内容与资源 Build 不一致",
855
+ status_code=409,
856
+ ) from error
857
+
858
+ @staticmethod
859
+ def _validate_expected_generation(
860
+ descriptor_digest: str | None,
861
+ generation_id: str | None,
862
+ ) -> None:
863
+ if descriptor_digest is not None and (
864
+ not isinstance(descriptor_digest, str) or not _DIGEST.fullmatch(descriptor_digest)
865
+ ):
866
+ raise StudioError(
867
+ "DSH_CAPABILITY_DESCRIPTOR_INVALID",
868
+ "DSH capability descriptor 摘要无效",
869
+ status_code=422,
870
+ )
871
+ if generation_id is not None and (
872
+ not isinstance(generation_id, str) or not _GENERATION_ID.fullmatch(generation_id)
873
+ ):
874
+ raise StudioError(
875
+ "DSH_CAPABILITY_GENERATION_INVALID",
876
+ "DSH capability generation 标识无效",
877
+ status_code=422,
878
+ )
879
+
880
+ def _require_expected_generation(
881
+ self,
882
+ descriptor: DshProfileCapabilityDescriptor,
883
+ expected_descriptor_digest: str | None,
884
+ expected_generation_id: str | None,
885
+ ) -> None:
886
+ if (
887
+ expected_descriptor_digest is not None
888
+ and descriptor.descriptor_digest != expected_descriptor_digest
889
+ ) or (expected_generation_id is not None and expected_generation_id != self._generation_id):
890
+ raise StudioError(
891
+ "DSH_CAPABILITY_GENERATION_CHANGED",
892
+ "DSH capability generation 已变化,请刷新后重试",
893
+ status_code=409,
894
+ )
895
+
896
+ def _require_generation_id(self) -> str:
897
+ generation_id = self._generation_id
898
+ if generation_id is None: # pragma: no cover - internal invariant guard
899
+ raise self._protocol_error()
900
+ return generation_id
901
+
902
+ async def _dispose_generation_locked(self) -> None:
903
+ await self._finish_cleanup(self._dispose_generation_owned_locked())
904
+
905
+ async def _dispose_generation_owned_locked(self) -> None:
906
+ """Finish generation teardown before propagating caller cancellation."""
907
+
908
+ host = self._host
909
+ active = tuple(self._active_calls.items())
910
+ self._host = None
911
+ self._lease = None
912
+ self._projection = None
913
+ self._resource_generation_snapshot = None
914
+ self._generation_id = None
915
+ self._active_calls.clear()
916
+ try:
917
+ await self._close_resource_supervisor_locked()
918
+ finally:
919
+ if active:
920
+ await asyncio.gather(
921
+ *(self._send_cancel(call.lease, call_id) for call_id, call in active),
922
+ return_exceptions=True,
923
+ )
924
+ if host is not None:
925
+ await host.dispose()
926
+
927
+ @staticmethod
928
+ async def _finish_cleanup(cleanup: Coroutine[Any, Any, None]) -> None:
929
+ """Defer caller cancellation until an owned-effect cleanup finishes."""
930
+
931
+ cleanup_task = asyncio.create_task(cleanup)
932
+ interrupted = False
933
+ while not cleanup_task.done():
934
+ try:
935
+ await asyncio.shield(cleanup_task)
936
+ except asyncio.CancelledError:
937
+ interrupted = True
938
+ cleanup_task.result()
939
+ if interrupted:
940
+ raise asyncio.CancelledError
941
+
942
+ async def _mcp_request(
943
+ self,
944
+ lease: DshMcpConnectorLease,
945
+ *,
946
+ request_id: str,
947
+ method: Literal[
948
+ "initialize",
949
+ "io.ksadk/scopes/revoke",
950
+ "tools/list",
951
+ "tools/call",
952
+ ],
953
+ params: Mapping[str, Any],
954
+ timeout_seconds: float,
955
+ ) -> dict[str, Any]:
956
+ payload = {
957
+ "jsonrpc": "2.0",
958
+ "id": request_id,
959
+ "method": method,
960
+ "params": dict(params),
961
+ }
962
+ headers = {**lease.headers(), "Content-Type": "application/json"}
963
+ try:
964
+ async with httpx.AsyncClient(
965
+ headers=headers,
966
+ timeout=timeout_seconds,
967
+ trust_env=False,
968
+ ) as client:
969
+ response = await client.post(lease.endpoint, json=payload)
970
+ except httpx.TimeoutException as error:
971
+ raise asyncio.TimeoutError from error
972
+ except httpx.HTTPError as error:
973
+ raise StudioError(
974
+ "DSH_CAPABILITY_HOST_UNAVAILABLE",
975
+ "DSH capability MCP 连接失败",
976
+ status_code=503,
977
+ details={"errorType": type(error).__name__},
978
+ ) from error
979
+ if response.status_code != 200 or len(response.content) > self._max_wire_response_bytes:
980
+ raise self._protocol_error()
981
+ try:
982
+ envelope = json.loads(
983
+ response.content,
984
+ object_pairs_hook=_duplicate_rejecting_object,
985
+ parse_constant=_reject_non_finite,
986
+ )
987
+ except (UnicodeError, ValueError, TypeError, RecursionError) as error:
988
+ raise self._protocol_error() from error
989
+ if (
990
+ not isinstance(envelope, dict)
991
+ or envelope.get("jsonrpc") != "2.0"
992
+ or envelope.get("id") != request_id
993
+ ):
994
+ raise self._protocol_error()
995
+ if "error" in envelope:
996
+ rpc_error = envelope.get("error")
997
+ code = rpc_error.get("code") if isinstance(rpc_error, dict) else None
998
+ raise StudioError(
999
+ "DSH_CAPABILITY_REQUEST_FAILED",
1000
+ "DSH capability MCP 拒绝了请求",
1001
+ status_code=502,
1002
+ details={"rpcCode": code if isinstance(code, int) else None},
1003
+ )
1004
+ result = envelope.get("result")
1005
+ if not isinstance(result, dict):
1006
+ raise self._protocol_error()
1007
+ try:
1008
+ result_bytes = len(
1009
+ json.dumps(
1010
+ result,
1011
+ allow_nan=False,
1012
+ ensure_ascii=False,
1013
+ separators=(",", ":"),
1014
+ ).encode("utf-8")
1015
+ )
1016
+ except (TypeError, ValueError, RecursionError) as error:
1017
+ raise self._protocol_error() from error
1018
+ if result_bytes > self._max_result_bytes:
1019
+ raise self._protocol_error()
1020
+ return cast(dict[str, Any], result)
1021
+
1022
+ async def _initialize_lease(self, lease: DshMcpConnectorLease) -> None:
1023
+ request_id = f"initialize-{os.urandom(12).hex()}"
1024
+ result = await self._mcp_request(
1025
+ lease,
1026
+ request_id=request_id,
1027
+ method="initialize",
1028
+ params={
1029
+ "protocolVersion": DSH_CAPABILITY_MCP_PROTOCOL,
1030
+ "capabilities": {},
1031
+ "clientInfo": {"name": "ksadk-studio", "version": "1.0.0"},
1032
+ },
1033
+ timeout_seconds=5.0,
1034
+ )
1035
+ server_info = result.get("serverInfo")
1036
+ capabilities = result.get("capabilities")
1037
+ if (
1038
+ result.get("protocolVersion") != DSH_CAPABILITY_MCP_PROTOCOL
1039
+ or not isinstance(server_info, dict)
1040
+ or server_info.get("name") != DSH_CAPABILITY_BUNDLE_PACKAGE
1041
+ or server_info.get("version") != DSH_CAPABILITY_HOST_VERSION
1042
+ or not isinstance(capabilities, dict)
1043
+ or not isinstance(capabilities.get("tools"), dict)
1044
+ ):
1045
+ raise self._protocol_error()
1046
+ await self._send_notification(
1047
+ lease,
1048
+ method="notifications/initialized",
1049
+ params={},
1050
+ tolerate_failure=False,
1051
+ )
1052
+
1053
+ async def _send_cancel(self, lease: DshMcpConnectorLease, call_id: str) -> None:
1054
+ await self._send_notification(
1055
+ lease,
1056
+ method="notifications/cancelled",
1057
+ params={"requestId": call_id, "reason": "Studio UI cancelled the call"},
1058
+ tolerate_failure=True,
1059
+ )
1060
+
1061
+ async def _send_notification(
1062
+ self,
1063
+ lease: DshMcpConnectorLease,
1064
+ *,
1065
+ method: Literal["notifications/initialized", "notifications/cancelled"],
1066
+ params: Mapping[str, Any],
1067
+ tolerate_failure: bool,
1068
+ ) -> None:
1069
+ headers = {**lease.headers(), "Content-Type": "application/json"}
1070
+ payload = {"jsonrpc": "2.0", "method": method, "params": dict(params)}
1071
+ try:
1072
+ async with httpx.AsyncClient(headers=headers, timeout=2.0, trust_env=False) as client:
1073
+ response = await client.post(lease.endpoint, json=payload)
1074
+ if response.status_code not in {200, 202, 204}:
1075
+ raise self._protocol_error()
1076
+ except (httpx.HTTPError, StudioError):
1077
+ # Disposal still kills the process and aborts all work. Cancellation
1078
+ # is best effort here so refresh/close cannot be held hostage by a
1079
+ # broken loopback listener.
1080
+ if tolerate_failure:
1081
+ return
1082
+ raise self._protocol_error() from None
1083
+
1084
+ @staticmethod
1085
+ def _protocol_error() -> StudioError:
1086
+ return StudioError(
1087
+ "DSH_CAPABILITY_PROTOCOL_INVALID",
1088
+ "DSH capability MCP 返回了无效响应",
1089
+ status_code=502,
1090
+ )
1091
+
1092
+ @staticmethod
1093
+ def _unavailable(error: BaseException) -> StudioError:
1094
+ reason = getattr(error, "code", None)
1095
+ return StudioError(
1096
+ "DSH_CAPABILITY_HOST_UNAVAILABLE",
1097
+ "DSH capability host 当前不可用",
1098
+ status_code=503,
1099
+ details={"reason": reason} if isinstance(reason, str) else {},
1100
+ )
1101
+
1102
+
1103
+ __all__ = [
1104
+ "DshCapabilityRuntimeSnapshot",
1105
+ "DshCapabilitySnapshot",
1106
+ "StudioDshCapabilityService",
1107
+ ]