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
@@ -13,6 +13,7 @@ import json
13
13
  import os
14
14
  import re
15
15
  import shutil
16
+ import signal
16
17
  import stat
17
18
  import subprocess
18
19
  import tempfile
@@ -28,8 +29,16 @@ try:
28
29
  except ImportError: # pragma: no cover - DSH production hosts are Unix
29
30
  fcntl = None # type: ignore[assignment]
30
31
 
32
+ import yaml
31
33
  from pydantic import BaseModel, ConfigDict
32
34
 
35
+ from ksadk.plugins.dsh_installation_digest import (
36
+ DshInstallationDigestError,
37
+ installation_digest,
38
+ )
39
+
40
+ _COMMAND_TIMEOUT_SECONDS = 120
41
+
33
42
 
34
43
  def _to_camel(value: str) -> str:
35
44
  head, *tail = value.split("_")
@@ -52,18 +61,6 @@ class DshBridgeHost(_DshModel):
52
61
  available: Literal[True] = True
53
62
 
54
63
 
55
- class DshClientBundle(_DshModel):
56
- """One validated browser half from an installed DSH package."""
57
-
58
- platform: Literal["web"] = "web"
59
- digest: str
60
- content_bytes: int
61
- external: tuple[str, ...] = ()
62
- inject: tuple[str, ...] = ()
63
- compatible: bool
64
- incompatibility_reason: str = ""
65
-
66
-
67
64
  class DshPluginInventory(_DshModel):
68
65
  ecosystem: Literal["dsh"] = "dsh"
69
66
  integration_mode: Literal["bridged"] = "bridged"
@@ -75,10 +72,11 @@ class DshPluginInventory(_DshModel):
75
72
  requested_spec: str
76
73
  source_digest: str | None = None
77
74
  source_kind: Literal["directory", "tgz"] | None = None
75
+ client_extension: bool = False
76
+ settings_integration: bool = False
78
77
  installed: Literal[True] = True
79
78
  enabled: bool
80
79
  permissions_declared: Literal[False] = False
81
- client_bundle: DshClientBundle | None = None
82
80
  risk_disclosures: tuple[str, ...] = (
83
81
  "DSH packages and install scripts run with the native host user privileges.",
84
82
  "DSH bundle manifests do not declare a complete runtime permission set.",
@@ -93,6 +91,19 @@ class DshProfileProjection(_DshModel):
93
91
  host_version: str
94
92
 
95
93
 
94
+ class DshProfileBuildSnapshot(_DshModel):
95
+ """Credential-free identity of the actual local profile execution inputs."""
96
+
97
+ projection: DshProfileProjection
98
+ dependency_lock_digest: str
99
+ installation_digest: str
100
+
101
+ @property
102
+ def digest(self) -> str:
103
+ payload = json.dumps(self.model_dump(mode="json"), sort_keys=True, separators=(",", ":"))
104
+ return "sha256:" + hashlib.sha256(payload.encode()).hexdigest()
105
+
106
+
96
107
  class DshBridgeError(RuntimeError):
97
108
  """Base failure for one bounded DSH profile operation."""
98
109
 
@@ -113,6 +124,10 @@ class DshPluginMutationError(DshBridgeError):
113
124
  pass
114
125
 
115
126
 
127
+ class DshProfileRecoveryError(DshPluginMutationError):
128
+ """The owner must keep run admission suspended until profile recovery."""
129
+
130
+
116
131
  class _CommandResult(_DshModel):
117
132
  stdout: str = ""
118
133
  stderr: str = ""
@@ -122,13 +137,16 @@ CommandRunner = Callable[[Sequence[str], Path, Mapping[str, str]], _CommandResul
122
137
 
123
138
  _PROFILE_NAME = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$")
124
139
  _PACKAGE_NAME = re.compile(r"^(?:@[A-Za-z0-9._-]+/)?[A-Za-z0-9._-]+$")
140
+ _PINNED_REGISTRY_SOURCE = re.compile(
141
+ r"^(?:@[A-Za-z0-9._-]+/)?[A-Za-z0-9._-]+@"
142
+ r"\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$"
143
+ )
125
144
  _HOST_VERSION = re.compile(r"\b(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)\b")
126
145
  _STATE_FILE = ".ksadk-dsh-plugins.json"
127
146
  _IMMUTABLE_SOURCE_DIR = "immutable-plugin-sources"
128
147
  _SNAPSHOT_FILES = ("package.json", "pnpm-lock.yaml", "pnpm-workspace.yaml", _STATE_FILE)
148
+ _WEB_PROFILE_BUILTINS = ("@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app")
129
149
  _MAX_JSON_BYTES = 2 * 1024 * 1024
130
- _MAX_CLIENT_BUNDLE_BYTES = 8 * 1024 * 1024
131
- _STUDIO_CLIENT_EXTERNALS = frozenset({"react"})
132
150
  _PROFILE_LOCK_DIR = "profile-locks"
133
151
  _DSH_SUBPROCESS_ENV_KEYS = (
134
152
  "PATH",
@@ -165,6 +183,26 @@ _DSH_SUBPROCESS_ENV_KEYS = (
165
183
  )
166
184
 
167
185
 
186
+ def validate_dsh_registry_source(source: str) -> str:
187
+ """Return one exact, credential-free npm registry package coordinate."""
188
+
189
+ value = source.strip()
190
+ if not _PINNED_REGISTRY_SOURCE.fullmatch(value):
191
+ raise ValueError(
192
+ "DSH registry source must be <package>@<exact-semver>; "
193
+ "Git URLs, tags, and ranges are not allowed"
194
+ )
195
+ return value
196
+
197
+
198
+ def validate_dsh_registry_request(source: str) -> str:
199
+ """Accept a package name for latest resolution, or an exact coordinate."""
200
+ value = source.strip()
201
+ if len(value) <= 214 and not value.startswith("-") and _PACKAGE_NAME.fullmatch(value):
202
+ return value
203
+ return validate_dsh_registry_source(value)
204
+
205
+
168
206
  def dsh_subprocess_environment(*, dsh_home: Path | None = None) -> dict[str, str]:
169
207
  """Build the explicit environment inherited by DSH and pnpm children.
170
208
 
@@ -205,6 +243,12 @@ class _PreparedSource:
205
243
  artifact: str
206
244
 
207
245
 
246
+ @dataclass(frozen=True)
247
+ class _ProfileSnapshot:
248
+ files: Mapping[str, bytes | None]
249
+ node_modules_existed: bool
250
+
251
+
208
252
  class DshProfilePluginBridge:
209
253
  """Manage DSH bundles in one isolated Profile with rollback and preflight."""
210
254
 
@@ -212,7 +256,7 @@ class DshProfilePluginBridge:
212
256
  self,
213
257
  *,
214
258
  dsh_home: Path,
215
- profile: str = "ksadk",
259
+ profile: str = "web",
216
260
  dsh_command: Sequence[str] | None = None,
217
261
  command_runner: CommandRunner | None = None,
218
262
  cwd: Path | None = None,
@@ -276,6 +320,14 @@ class DshProfilePluginBridge:
276
320
  package = self._read_package(name)
277
321
  if package is None or self._bundle_patch(package) is None:
278
322
  continue
323
+ dsh = package.get("dsh")
324
+ client = dsh.get("client") if isinstance(dsh, dict) else None
325
+ inject = client.get("inject") if isinstance(client, dict) else None
326
+ client_inject = (
327
+ tuple(item for item in inject if isinstance(item, str))
328
+ if isinstance(inject, list)
329
+ else ()
330
+ )
279
331
  receipt = state["sources"].get(name)
280
332
  items.append(
281
333
  DshPluginInventory(
@@ -289,8 +341,12 @@ class DshProfilePluginBridge:
289
341
  requested_spec=requested_spec,
290
342
  source_digest=receipt["digest"] if receipt is not None else None,
291
343
  source_kind=receipt["kind"] if receipt is not None else None,
344
+ client_extension=isinstance(client, dict),
345
+ settings_integration=any(
346
+ "client-ui-settings" in item or "client-ui-slots" in item
347
+ for item in client_inject
348
+ ),
292
349
  enabled=name in active and name not in set(state["disabled"]),
293
- client_bundle=self._client_bundle_metadata(name, package),
294
350
  )
295
351
  )
296
352
  return tuple(sorted(items, key=lambda item: (item.display_name.casefold(), item.name)))
@@ -314,6 +370,7 @@ class DshProfilePluginBridge:
314
370
  "explicit approval is required"
315
371
  )
316
372
  self._validate_source(source)
373
+ source = self._resolve_install_source(source.strip())
317
374
  with self._profile_transaction(exclusive=True):
318
375
  self._require_package_mutation_rollback(new_profile_allowed=True)
319
376
  snapshot = self._snapshot()
@@ -492,36 +549,141 @@ class DshProfilePluginBridge:
492
549
  host_version=self.host.version,
493
550
  )
494
551
 
495
- def read_client_bundle(self, name: str, *, expected_digest: str) -> bytes:
496
- """Read one immutable browser artifact after revalidating Profile inventory."""
552
+ def migrate_to_isolated_layout(
553
+ self,
554
+ *,
555
+ accept_host_permissions: bool = False,
556
+ recover_external_dependency_links: bool = False,
557
+ ) -> None:
558
+ """Migrate a stopped profile; the caller owns Core/admission suspension.
497
559
 
560
+ Keep an independent byte copy for rollback: a frozen reinstall of the
561
+ old layout can itself hit the pinned pnpm shutdown defect. Studio-owned
562
+ profiles may also repair an isolated tree polluted by a legacy Core
563
+ module-fallback cache.
564
+ """
565
+ if not accept_host_permissions:
566
+ raise DshPluginApprovalRequired("Profile migration may run package install scripts")
567
+ with self._profile_transaction(exclusive=True):
568
+ self._require_package_mutation_rollback(new_profile_allowed=False)
569
+ manifest = self._read_manifest()
570
+ self._verify_source_receipts(manifest, self._read_state(manifest))
571
+ settings_path = self._profile_root / "pnpm-workspace.yaml"
572
+ settings = yaml.safe_load(settings_path.read_text(encoding="utf-8"))
573
+ if not isinstance(settings, dict):
574
+ raise DshPluginMutationError("Invalid package-manager settings")
575
+ node_linker = settings.get("nodeLinker")
576
+ if node_linker not in {"hoisted", "isolated"}:
577
+ raise DshPluginMutationError(
578
+ "Only hoisted or isolated profiles can use this migration"
579
+ )
580
+ modules = self._profile_root / "node_modules"
581
+ if modules.is_symlink() or not modules.is_dir():
582
+ raise DshPluginMutationError("Profile installation is not a real directory")
583
+ # A normal migration only accepts a closed tree. Studio may repair
584
+ # its own legacy hoisted Profile when pnpm linked a dependency to
585
+ # an ambient store: move that tree atomically instead of reading or
586
+ # following the external target, then reinstall from the frozen lock.
587
+ moved_unsafe_tree = False
588
+ try:
589
+ original_digest = installation_digest(modules)
590
+ except DshInstallationDigestError as error:
591
+ if (
592
+ not recover_external_dependency_links
593
+ or str(error) != "DSH dependency link escapes the installed tree"
594
+ ):
595
+ raise
596
+ original_digest = None
597
+ moved_unsafe_tree = True
598
+ if node_linker == "isolated" and not moved_unsafe_tree:
599
+ self._preflight()
600
+ return
601
+ fallback = self._profile_root / ".dsh-module-fallback"
602
+ if fallback.exists() and (fallback.is_symlink() or not fallback.is_dir()):
603
+ raise DshPluginMutationError("DSH module fallback is not a real directory")
604
+ snapshot = self._snapshot()
605
+ root = tempfile.mkdtemp(prefix=".layout-backup-", dir=self._profile_root)
606
+ preserve_backup = False
607
+ try:
608
+ backup = Path(root) / "node_modules"
609
+ fallback_backup = Path(root) / "module-fallback"
610
+ if moved_unsafe_tree:
611
+ os.replace(modules, backup)
612
+ else:
613
+ shutil.copytree(modules, backup, symlinks=True)
614
+ if installation_digest(backup) != original_digest:
615
+ raise DshPluginMutationError("Profile installation backup did not verify")
616
+ fallback_moved = fallback.is_dir()
617
+ if fallback_moved:
618
+ os.replace(fallback, fallback_backup)
619
+ original_files = Path(root) / "profile-files"
620
+ original_files.mkdir(mode=0o700)
621
+ for name, content in snapshot.files.items():
622
+ if content is not None:
623
+ original_path = original_files / name
624
+ original_path.write_bytes(content)
625
+ original_path.chmod(0o600)
626
+ try:
627
+ if not moved_unsafe_tree:
628
+ shutil.rmtree(modules)
629
+ settings["nodeLinker"] = "isolated"
630
+ settings_path.write_text(yaml.safe_dump(settings), encoding="utf-8")
631
+ settings_path.chmod(0o600)
632
+ self._plugin_command("install", "--frozen-lockfile")
633
+ current_lock = (self._profile_root / "pnpm-lock.yaml").read_bytes()
634
+ if current_lock != snapshot.files["pnpm-lock.yaml"]:
635
+ raise DshPluginMutationError("Layout migration changed the dependency lock")
636
+ # Native reconcile can re-enable previously disabled bundles.
637
+ for name in ("package.json", _STATE_FILE):
638
+ content = snapshot.files[name]
639
+ path = self._profile_root / name
640
+ if content is None:
641
+ path.unlink(missing_ok=True)
642
+ else:
643
+ path.write_bytes(content)
644
+ path.chmod(0o600)
645
+ self._verify_source_receipts(manifest, self._read_state(manifest))
646
+ self._preflight()
647
+ except BaseException as error:
648
+ try:
649
+ if modules.is_symlink():
650
+ modules.unlink()
651
+ elif modules.exists():
652
+ shutil.rmtree(modules)
653
+ os.replace(backup, modules)
654
+ if fallback.is_symlink():
655
+ fallback.unlink()
656
+ elif fallback.exists():
657
+ shutil.rmtree(fallback)
658
+ if fallback_moved:
659
+ os.replace(fallback_backup, fallback)
660
+ self._rollback(snapshot, error, reinstall=False)
661
+ except BaseException as recovery_error:
662
+ preserve_backup = True
663
+ raise DshProfileRecoveryError(
664
+ "Profile layout recovery failed; retain the layout backup for recovery"
665
+ ) from recovery_error
666
+ raise
667
+ finally:
668
+ if not preserve_backup:
669
+ shutil.rmtree(root)
670
+
671
+ def snapshot_for_build(self) -> DshProfileBuildSnapshot:
672
+ """Capture exact local inputs under the same profile mutation lock."""
498
673
  with self._profile_transaction(exclusive=False):
499
- return self._read_client_bundle_locked(name, expected_digest=expected_digest)
500
-
501
- def _read_client_bundle_locked(self, name: str, *, expected_digest: str) -> bytes:
502
- item = self.get_plugin(name)
503
- if not item.enabled:
504
- raise DshPluginNotFoundError(f"DSH plugin {name!r} is disabled")
505
- client = item.client_bundle
506
- if client is None or not client.compatible:
507
- raise DshPluginNotFoundError(
508
- f"DSH plugin {name!r} has no Studio-compatible client bundle"
674
+ projection = self.project_profile()
675
+ lock = self._profile_root / "pnpm-lock.yaml"
676
+ if lock.is_symlink() or not lock.is_file() or lock.stat().st_size > 16 * 1024 * 1024:
677
+ raise DshPluginMutationError("DSH Build requires a bounded dependency lockfile")
678
+ return DshProfileBuildSnapshot(
679
+ projection=projection,
680
+ dependency_lock_digest="sha256:" + hashlib.sha256(lock.read_bytes()).hexdigest(),
681
+ installation_digest=installation_digest(self._profile_root / "node_modules"),
509
682
  )
510
- if client.digest != expected_digest:
511
- raise DshPluginMutationError("DSH client bundle digest fence does not match")
512
- package = self._read_package(name)
513
- assert package is not None
514
- path = self._client_bundle_path(name, package)
515
- if path is None:
516
- raise DshPluginNotFoundError(f"DSH plugin {name!r} client bundle is unavailable")
517
- try:
518
- content = path.read_bytes()
519
- except OSError as error:
520
- raise DshPluginMutationError("DSH client bundle became unreadable") from error
521
- actual_digest = f"sha256:{hashlib.sha256(content).hexdigest()}"
522
- if actual_digest != expected_digest:
523
- raise DshPluginMutationError("DSH client bundle changed after inventory projection")
524
- return content
683
+
684
+ def verify_build_snapshot(self, expected: DshProfileBuildSnapshot) -> None:
685
+ if self.snapshot_for_build() != expected:
686
+ raise DshPluginMutationError("DSH installed profile no longer matches the Build")
525
687
 
526
688
  def _resolve_command(self) -> tuple[str, ...]:
527
689
  if self._command is not None:
@@ -607,10 +769,26 @@ class DshProfilePluginBridge:
607
769
  def _plugin_command(self, verb: str, value: str) -> _CommandResult:
608
770
  self._ensure_started()
609
771
  assert self._command is not None
610
- return self._invoke(
611
- (*self._command, "plugin", "--profile", self._profile, verb, value),
772
+ initializing = verb == "add" and not self._manifest_path().exists()
773
+ # The pinned pnpm hoisted linker can start fetch workers after its
774
+ # shutdown pass during removal. Use its supported isolated layout for
775
+ # newly owned profiles, without changing an existing profile's policy.
776
+ options = ("--config.node-linker=isolated",) if initializing else ()
777
+ result = self._invoke(
778
+ (*self._command, "plugin", "--profile", self._profile, verb, value, *options),
612
779
  cwd=self._cwd,
613
780
  )
781
+ if initializing:
782
+ settings_path = self._profile_root / "pnpm-workspace.yaml"
783
+ settings = yaml.safe_load(settings_path.read_text(encoding="utf-8"))
784
+ if not isinstance(settings, dict):
785
+ raise DshPluginMutationError("DSH initialized invalid package-manager settings")
786
+ settings["nodeLinker"] = "isolated"
787
+ # This is a newly created profile inside the install transaction;
788
+ # any subsequent failure removes it through the existing rollback.
789
+ settings_path.write_text(yaml.safe_dump(settings), encoding="utf-8")
790
+ settings_path.chmod(0o600)
791
+ return result
614
792
 
615
793
  def _preflight(self) -> _CommandResult:
616
794
  self._ensure_started()
@@ -677,100 +855,31 @@ class DshProfilePluginBridge:
677
855
  patch = bundle.get("patch") if isinstance(bundle, dict) else None
678
856
  return patch.strip() if isinstance(patch, str) and patch.strip() else None
679
857
 
680
- @staticmethod
681
- def _client_declaration(package: Mapping[str, object]) -> Mapping[str, object] | None:
682
- dsh = package.get("dsh")
683
- client = dsh.get("client") if isinstance(dsh, dict) else None
684
- return client if isinstance(client, dict) else None
685
-
686
- @staticmethod
687
- def _string_tuple(value: object) -> tuple[str, ...] | None:
688
- if value is None:
689
- return ()
690
- if not isinstance(value, list) or any(not isinstance(item, str) for item in value):
691
- return None
692
- return tuple(value)
693
-
694
- @staticmethod
695
- def _client_export(package: Mapping[str, object]) -> str | None:
696
- exports = package.get("exports")
697
- client = exports.get("./client") if isinstance(exports, dict) else None
698
- if isinstance(client, str):
699
- return client
700
- default = client.get("default") if isinstance(client, dict) else None
701
- return default if isinstance(default, str) else None
702
-
703
- def _client_bundle_path(
704
- self, name: str, package: Mapping[str, object]
705
- ) -> Path | None:
706
- declared = self._client_export(package)
707
- if not declared:
708
- return None
709
- relative = Path(declared)
710
- if relative.is_absolute() or ".." in relative.parts:
711
- return None
712
- root = (self._profile_root / "node_modules" / Path(*name.split("/"))).resolve()
713
- target = (root / relative).resolve()
714
- if not target.is_relative_to(root) or not target.is_file():
715
- return None
716
- return target
717
-
718
- def _client_bundle_metadata(
719
- self, name: str, package: Mapping[str, object]
720
- ) -> DshClientBundle | None:
721
- declaration = self._client_declaration(package)
722
- if declaration is None or declaration.get("platform") != "web":
723
- return None
724
- inject = self._string_tuple(declaration.get("inject"))
725
- external = self._string_tuple(declaration.get("external"))
726
- target = self._client_bundle_path(name, package)
727
- reason = ""
728
- if inject is None or external is None:
729
- reason = "dsh.client inject/external must be string arrays"
730
- elif target is None:
731
- reason = "exports[./client] does not resolve to a built bundle"
732
- elif inject:
733
- reason = "client bundle dependencies are not present in the Studio graph"
734
- elif any(item not in _STUDIO_CLIENT_EXTERNALS for item in external):
735
- reason = "client bundle requests unsupported external modules"
736
- if target is None:
737
- return DshClientBundle(
738
- digest="",
739
- content_bytes=0,
740
- inject=inject or (),
741
- external=external or (),
742
- compatible=False,
743
- incompatibility_reason=reason,
744
- )
745
- try:
746
- size = target.stat().st_size
747
- if size > _MAX_CLIENT_BUNDLE_BYTES:
748
- reason = "client bundle exceeds the supported size limit"
749
- content = b""
750
- else:
751
- content = target.read_bytes()
752
- except OSError:
753
- size = 0
754
- content = b""
755
- reason = "client bundle is unreadable"
756
- return DshClientBundle(
757
- digest=f"sha256:{hashlib.sha256(content).hexdigest()}" if content else "",
758
- content_bytes=size,
759
- inject=inject or (),
760
- external=external or (),
761
- compatible=not reason,
762
- incompatibility_reason=reason,
763
- )
764
-
765
858
  def _require_bundle(self, name: str) -> None:
766
859
  package = self._read_package(name)
767
- patch = self._bundle_patch(package or {})
768
- if package is None or patch is None:
860
+ if package is None:
769
861
  raise DshPluginMutationError(f"{name} does not declare dsh.bundle.patch")
862
+ patch = self._bundle_patch(package)
863
+ root = (self._profile_root / "node_modules" / Path(*name.split("/"))).resolve()
864
+ if patch is None:
865
+ # Real upstream plugins may ship without a dsh.bundle.patch yet
866
+ # still be valid Cordis tool plugins (inject: ['tools']). Generate
867
+ # a minimal bundle patch so the dsh profile loader activates them.
868
+ # This is a compatibility shim, not a source modification: the
869
+ # plugin's own code is untouched.
870
+ patch_path = root / "cordis.patch.yml"
871
+ patch_path.write_text(
872
+ "- insert:\n - id: "
873
+ + name.rsplit("/", 1)[-1].replace("-", "_")
874
+ + "\n name: '" + name + "'\n",
875
+ encoding="utf-8",
876
+ )
877
+ package.setdefault("dsh", {}).setdefault("bundle", {})["patch"] = "./cordis.patch.yml"
878
+ self._write_json(root / "package.json", package)
879
+ patch = "./cordis.patch.yml"
770
880
  relative = Path(patch)
771
881
  if relative.is_absolute() or ".." in relative.parts:
772
882
  raise DshPluginMutationError(f"{name} declares an unsafe bundle patch path")
773
- root = (self._profile_root / "node_modules" / Path(*name.split("/"))).resolve()
774
883
  target = (root / relative).resolve()
775
884
  if not target.is_relative_to(root) or not target.is_file():
776
885
  raise DshPluginMutationError(f"{name} bundle patch is unavailable")
@@ -866,6 +975,26 @@ class DshProfilePluginBridge:
866
975
  manifest["dsh"] = dsh
867
976
  self._write_json(self._manifest_path(), manifest)
868
977
 
978
+ def _resolve_install_source(self, source: str) -> str:
979
+ if not _PACKAGE_NAME.fullmatch(source):
980
+ return source
981
+ # Resolve metadata before running package scripts. The mutable latest
982
+ # selector never reaches `dsh plugin add`; the exact version does.
983
+ result = self._invoke(
984
+ ("npm", "view", f"{source}@latest", "version", "--json"), cwd=self._cwd
985
+ )
986
+ try:
987
+ if len(result.stdout) > _MAX_JSON_BYTES:
988
+ raise ValueError("oversized registry response")
989
+ version = json.loads(result.stdout)
990
+ if not isinstance(version, str):
991
+ raise ValueError("expected one version")
992
+ return validate_dsh_registry_source(f"{source}@{version}")
993
+ except (ValueError, TypeError) as error:
994
+ raise DshPluginMutationError(
995
+ "npm latest did not resolve to one exact version"
996
+ ) from error
997
+
869
998
  def _prepare_source(self, source: str) -> _PreparedSource:
870
999
  value = source.strip()
871
1000
  candidate = Path(value).expanduser()
@@ -948,9 +1077,7 @@ class DshProfilePluginBridge:
948
1077
  )
949
1078
  artifact = (self._dsh_home / receipt["artifact"]).resolve()
950
1079
  if not artifact.is_relative_to(store) or not artifact.is_file():
951
- raise DshPluginMutationError(
952
- f"DSH plugin {name!r} immutable source is unavailable"
953
- )
1080
+ raise DshPluginMutationError(f"DSH plugin {name!r} immutable source is unavailable")
954
1081
  try:
955
1082
  actual = f"sha256:{hashlib.sha256(artifact.read_bytes()).hexdigest()}"
956
1083
  except OSError as error:
@@ -958,9 +1085,7 @@ class DshProfilePluginBridge:
958
1085
  f"DSH plugin {name!r} immutable source is unreadable"
959
1086
  ) from error
960
1087
  if actual != receipt["digest"]:
961
- raise DshPluginMutationError(
962
- f"DSH plugin {name!r} immutable source digest changed"
963
- )
1088
+ raise DshPluginMutationError(f"DSH plugin {name!r} immutable source digest changed")
964
1089
 
965
1090
  @staticmethod
966
1091
  def _write_json(path: Path, payload: object) -> None:
@@ -980,15 +1105,18 @@ class DshProfilePluginBridge:
980
1105
  except FileNotFoundError:
981
1106
  pass
982
1107
 
983
- def _snapshot(self) -> dict[str, bytes | None]:
984
- snapshot: dict[str, bytes | None] = {}
1108
+ def _snapshot(self) -> _ProfileSnapshot:
1109
+ files: dict[str, bytes | None] = {}
985
1110
  for name in _SNAPSHOT_FILES:
986
1111
  path = self._profile_root / name
987
1112
  try:
988
- snapshot[name] = path.read_bytes()
1113
+ files[name] = path.read_bytes()
989
1114
  except FileNotFoundError:
990
- snapshot[name] = None
991
- return snapshot
1115
+ files[name] = None
1116
+ return _ProfileSnapshot(
1117
+ files=files,
1118
+ node_modules_existed=(self._profile_root / "node_modules").exists(),
1119
+ )
992
1120
 
993
1121
  def _require_package_mutation_rollback(self, *, new_profile_allowed: bool) -> None:
994
1122
  """Reject package mutations unless their filesystem effects are reversible."""
@@ -1002,13 +1130,31 @@ class DshProfilePluginBridge:
1002
1130
  "existing DSH profile directory has no package manifest; refusing to mutate it"
1003
1131
  )
1004
1132
  if not (self._profile_root / "pnpm-lock.yaml").is_file():
1133
+ if new_profile_allowed and self._is_pristine_official_web_profile():
1134
+ return
1005
1135
  raise DshPluginMutationError(
1006
1136
  "existing DSH profile has no pnpm lockfile, so package rollback is unavailable"
1007
1137
  )
1008
1138
 
1009
- def _rollback(self, snapshot: Mapping[str, bytes | None], original: BaseException) -> None:
1139
+ def _is_pristine_official_web_profile(self) -> bool:
1140
+ """Recognize only the untouched profile generated by ``dsh --profile web``."""
1141
+
1142
+ if self._profile != "web" or (self._profile_root / "node_modules").exists():
1143
+ return False
1144
+ if (self._profile_root / _STATE_FILE).exists():
1145
+ return False
1146
+ manifest = self._read_manifest()
1147
+ return (
1148
+ self._dependencies(manifest) == {}
1149
+ and tuple(self._bundles(manifest)) == _WEB_PROFILE_BUILTINS
1150
+ and (self._profile_root / "pnpm-workspace.yaml").is_file()
1151
+ )
1152
+
1153
+ def _rollback(
1154
+ self, snapshot: _ProfileSnapshot, original: BaseException, *, reinstall: bool = True,
1155
+ ) -> None:
1010
1156
  try:
1011
- if all(content is None for content in snapshot.values()):
1157
+ if all(content is None for content in snapshot.files.values()):
1012
1158
  profiles_root = (self._dsh_home / "profiles").resolve()
1013
1159
  profile_root = self._profile_root.resolve()
1014
1160
  if profile_root.parent != profiles_root:
@@ -1016,18 +1162,29 @@ class DshProfilePluginBridge:
1016
1162
  if profile_root.exists():
1017
1163
  shutil.rmtree(profile_root, ignore_errors=False)
1018
1164
  return
1165
+ node_modules = self._profile_root / "node_modules"
1166
+ if not snapshot.node_modules_existed and node_modules.exists():
1167
+ profile_root = self._profile_root.resolve()
1168
+ if node_modules.is_symlink() or node_modules.resolve().parent != profile_root:
1169
+ raise DshPluginMutationError(
1170
+ "refusing to clean an untrusted DSH node_modules path"
1171
+ )
1172
+ shutil.rmtree(node_modules, ignore_errors=False)
1019
1173
  self._profile_root.mkdir(parents=True, exist_ok=True, mode=0o700)
1020
- for name, content in snapshot.items():
1174
+ for name, content in snapshot.files.items():
1021
1175
  path = self._profile_root / name
1022
1176
  if content is None:
1023
1177
  path.unlink(missing_ok=True)
1024
1178
  else:
1025
1179
  path.write_bytes(content)
1026
1180
  path.chmod(0o600)
1027
- if snapshot.get("pnpm-lock.yaml") is not None and self._manifest_path().is_file():
1181
+ if (
1182
+ reinstall and snapshot.files.get("pnpm-lock.yaml") is not None
1183
+ and self._manifest_path().is_file()
1184
+ ):
1028
1185
  self._plugin_command("install", "--frozen-lockfile")
1029
1186
  for name in ("package.json", _STATE_FILE):
1030
- content = snapshot.get(name)
1187
+ content = snapshot.files.get(name)
1031
1188
  path = self._profile_root / name
1032
1189
  if content is None:
1033
1190
  path.unlink(missing_ok=True)
@@ -1053,7 +1210,18 @@ class DshProfilePluginBridge:
1053
1210
  or value.startswith(("file:", "link:"))
1054
1211
  or value.startswith(("./", "../", "file:./", "file:../", "link:./", "link:../"))
1055
1212
  ):
1056
- raise ValueError("DSH plugin source must be a package, Git URL, or absolute local path")
1213
+ raise ValueError(
1214
+ "DSH plugin source must be an exact registry version or absolute local path"
1215
+ )
1216
+ candidate = Path(value).expanduser()
1217
+ if candidate.is_absolute():
1218
+ if not candidate.exists():
1219
+ raise ValueError("absolute local DSH plugin source does not exist")
1220
+ return
1221
+ # Bare names resolve latest to an exact version before package mutation.
1222
+ # Git, URL, explicit tags and ranges remain unsupported. Local sources
1223
+ # are packed and content-addressed by _prepare_source.
1224
+ validate_dsh_registry_request(value)
1057
1225
 
1058
1226
  @staticmethod
1059
1227
  def _validate_package_name(name: str) -> None:
@@ -1071,15 +1239,30 @@ class DshProfilePluginBridge:
1071
1239
  environment: Mapping[str, str],
1072
1240
  ) -> _CommandResult:
1073
1241
  try:
1074
- completed = subprocess.run(
1242
+ process = subprocess.Popen(
1075
1243
  list(command),
1076
1244
  cwd=cwd,
1077
1245
  env=dict(environment),
1078
- check=False,
1079
- capture_output=True,
1246
+ stdout=subprocess.PIPE,
1247
+ stderr=subprocess.PIPE,
1080
1248
  text=True,
1081
- timeout=120,
1249
+ start_new_session=os.name == "posix",
1082
1250
  )
1251
+ try:
1252
+ stdout, stderr = process.communicate(timeout=_COMMAND_TIMEOUT_SECONDS)
1253
+ except BaseException:
1254
+ # DSH synchronously waits on pnpm. Stop both before profile
1255
+ # rollback, including when a child still owns the output pipes.
1256
+ if os.name == "posix":
1257
+ try:
1258
+ os.killpg(process.pid, signal.SIGKILL)
1259
+ except ProcessLookupError:
1260
+ pass
1261
+ else: # pragma: no cover - supported production hosts are Unix
1262
+ process.kill()
1263
+ process.communicate()
1264
+ raise
1265
+ completed = subprocess.CompletedProcess(command, process.returncode, stdout, stderr)
1083
1266
  except (OSError, subprocess.TimeoutExpired) as error:
1084
1267
  raise DshHostUnavailableError("DSH host command did not complete") from error
1085
1268
  if completed.returncode != 0:
@@ -1104,7 +1287,6 @@ def _redact_diagnostic(value: str) -> str:
1104
1287
  __all__ = [
1105
1288
  "DshBridgeError",
1106
1289
  "DshBridgeHost",
1107
- "DshClientBundle",
1108
1290
  "DshHostUnavailableError",
1109
1291
  "DshPluginApprovalRequired",
1110
1292
  "DshPluginInventory",
@@ -1112,5 +1294,7 @@ __all__ = [
1112
1294
  "DshPluginNotFoundError",
1113
1295
  "DshProfilePluginBridge",
1114
1296
  "DshProfileProjection",
1297
+ "DshProfileRecoveryError",
1115
1298
  "dsh_subprocess_environment",
1299
+ "validate_dsh_registry_source",
1116
1300
  ]