agentengine-sdk-python 0.8.2__py3-none-any.whl → 0.8.3__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 (250) hide show
  1. {agentengine_sdk_python-0.8.2.dist-info → agentengine_sdk_python-0.8.3.dist-info}/METADATA +18 -25
  2. {agentengine_sdk_python-0.8.2.dist-info → agentengine_sdk_python-0.8.3.dist-info}/RECORD +224 -169
  3. ksadk/_build_provenance.json +1 -0
  4. ksadk/agui/_agent_helpers.py +4 -0
  5. ksadk/api/client.py +3 -0
  6. ksadk/builders/managed_runtime_builder.py +3 -0
  7. ksadk/cli/__init__.py +6 -0
  8. ksadk/cli/cmd_plugin.py +657 -0
  9. ksadk/codex/runtime.py +50 -8
  10. ksadk/configs/env_registry.py +31 -3
  11. ksadk/configs/settings.py +9 -0
  12. ksadk/context_engine/contributors.py +73 -8
  13. ksadk/conversations/__init__.py +19 -0
  14. ksadk/conversations/context.py +104 -24
  15. ksadk/conversations/contracts.py +266 -0
  16. ksadk/conversations/message_projection.py +19 -1
  17. ksadk/conversations/projector.py +616 -0
  18. ksadk/conversations/reducer.py +98 -0
  19. ksadk/events/_v1_compat/projection.py +2 -0
  20. ksadk/events/adapters/codex.py +190 -0
  21. ksadk/harness/__init__.py +45 -35
  22. ksadk/harness/runtime.py +60 -1
  23. ksadk/kernel/contract_fingerprints.py +5 -4
  24. ksadk/kernel/contracts.py +8 -0
  25. ksadk/kernel/control.py +1 -0
  26. ksadk/kernel/ingress.py +21 -0
  27. ksadk/kernel/memory_store.py +1 -0
  28. ksadk/kernel/postgres_store.py +1 -0
  29. ksadk/kernel/sqlite_store.py +1 -0
  30. ksadk/kernel/worker.py +11 -0
  31. ksadk/memory/providers/local_sqlite.py +7 -0
  32. ksadk/plugins/__init__.py +167 -0
  33. ksadk/plugins/bridges/__init__.py +45 -0
  34. ksadk/plugins/bridges/codex.py +552 -0
  35. ksadk/plugins/bridges/dsh.py +1116 -0
  36. ksadk/plugins/builtins.py +747 -0
  37. ksadk/plugins/bundle.py +246 -0
  38. ksadk/plugins/bundle_security.py +167 -0
  39. ksadk/plugins/composition.py +529 -0
  40. ksadk/plugins/context_contributor.py +437 -0
  41. ksadk/plugins/contracts.py +451 -0
  42. ksadk/plugins/dsh_toolchain.py +935 -0
  43. ksadk/plugins/ecosystem_bridge.py +1105 -0
  44. ksadk/plugins/ecosystem_probe.py +191 -0
  45. ksadk/plugins/host.py +724 -0
  46. ksadk/plugins/providers/__init__.py +85 -0
  47. ksadk/plugins/providers/bundles/ksadk-codex/cordis.patch.yml +3 -0
  48. ksadk/plugins/providers/bundles/ksadk-codex/index.mjs +18 -0
  49. ksadk/plugins/providers/bundles/ksadk-codex/package.json +19 -0
  50. ksadk/plugins/providers/bundles/ksadk-harness/cordis.patch.yml +3 -0
  51. ksadk/plugins/providers/bundles/ksadk-harness/index.mjs +19 -0
  52. ksadk/plugins/providers/bundles/ksadk-harness/package.json +19 -0
  53. ksadk/plugins/providers/codex.py +722 -0
  54. ksadk/plugins/providers/codex_dsh.py +83 -0
  55. ksadk/plugins/providers/dsh.py +951 -0
  56. ksadk/plugins/providers/dsh_descriptor_host.py +226 -0
  57. ksadk/plugins/providers/harness.py +627 -0
  58. ksadk/plugins/providers/harness_dsh.py +91 -0
  59. ksadk/plugins/providers/legacy.py +215 -0
  60. ksadk/plugins/providers/legacy_catalog.py +100 -0
  61. ksadk/plugins/providers/shipped_dsh.py +370 -0
  62. ksadk/plugins/resolver.py +331 -0
  63. ksadk/plugins/subagent_providers/__init__.py +12 -0
  64. ksadk/plugins/subagent_providers/codex.py +443 -0
  65. ksadk/plugins/subagents.py +333 -0
  66. ksadk/runtime/_runner_adapter/stream_mapping.py +51 -42
  67. ksadk/runtime/adapter.py +1 -0
  68. ksadk/runtime/factory.py +76 -2
  69. ksadk/runtime/framework_adapters.py +18 -0
  70. ksadk/runtime/runner_adapter.py +5 -0
  71. ksadk/sandbox/__init__.py +2 -1
  72. ksadk/sandbox/factory.py +72 -0
  73. ksadk/scheduler/__init__.py +18 -0
  74. ksadk/scheduler/calendar.py +102 -0
  75. ksadk/scheduler/contracts.py +254 -0
  76. ksadk/scheduler/dispatcher.py +129 -0
  77. ksadk/scheduler/engine.py +416 -0
  78. ksadk/scheduler/sqlite_store.py +563 -0
  79. ksadk/server/static/assets/{ArtifactsPanel-DSU1sWD_.js → ArtifactsPanel-10GPXIJ6.js} +1 -1
  80. ksadk/server/static/assets/{CodeBlock-DOH6MVcn.js → CodeBlock-BXk1l-G1.js} +1 -1
  81. ksadk/server/static/assets/{MathMessageMarkdown-BL8my1R2.js → MathMessageMarkdown-D9uB55Oh.js} +1 -1
  82. ksadk/server/static/assets/{MermaidBlock-Dz4IP-Tx.js → MermaidBlock--OEYoXIJ.js} +6 -6
  83. ksadk/server/static/assets/{NativeTerminalPanel-DUrK0JpZ.js → NativeTerminalPanel-DrEe1syq.js} +1 -1
  84. ksadk/server/static/assets/{abnfDiagram-VCTEODGH-g20pFzNV.js → abnfDiagram-VCTEODGH-eeTFJLvo.js} +1 -1
  85. ksadk/server/static/assets/{arc-C4FzinUA.js → arc-S3ZNbIAd.js} +1 -1
  86. ksadk/server/static/assets/architecture-7GRP2DOG-Dk9nRQ6-.js +1 -0
  87. ksadk/server/static/assets/{architectureDiagram-5GKGNRK7-DpIqL5h4.js → architectureDiagram-5GKGNRK7-CRjgEJDX.js} +1 -1
  88. ksadk/server/static/assets/{blockDiagram-NRAW4CY4-BjkljTNz.js → blockDiagram-NRAW4CY4-C7bsBoNH.js} +1 -1
  89. ksadk/server/static/assets/{c4Diagram-UCG6FXSJ-Dk_ieq2X.js → c4Diagram-UCG6FXSJ-BX3c33Ob.js} +1 -1
  90. ksadk/server/static/assets/channel-s354Yo6o.js +1 -0
  91. ksadk/server/static/assets/chunk-2Q5K7J3B-CDpPpKR5.js +1 -0
  92. ksadk/server/static/assets/{chunk-5VM5RSS4-BVHAchFb.js → chunk-5VM5RSS4-bZ7ws-8m.js} +1 -1
  93. ksadk/server/static/assets/{chunk-F27PBJKO-C-ipQzuS.js → chunk-F27PBJKO-D2r0pvhY.js} +1 -1
  94. ksadk/server/static/assets/{chunk-G27WJ6UU-C2EyJbIK.js → chunk-G27WJ6UU-CZwF3oUP.js} +1 -1
  95. ksadk/server/static/assets/{chunk-JWPE2WC7-vYvVJb_M.js → chunk-JWPE2WC7-DigFYCML.js} +1 -1
  96. ksadk/server/static/assets/{chunk-LCL6LL3I-BcI6ysPT.js → chunk-LCL6LL3I-CnorGV5q.js} +1 -1
  97. ksadk/server/static/assets/{chunk-POPQ4Y6H-C030x_Z1.js → chunk-POPQ4Y6H-CexntQA-.js} +1 -1
  98. ksadk/server/static/assets/{chunk-RHFEMEQ7-yyGpsz4n.js → chunk-RHFEMEQ7-CHTC7UxA.js} +1 -1
  99. ksadk/server/static/assets/{chunk-SVP7TREG-BLTlmMU7.js → chunk-SVP7TREG-BC1EWt_-.js} +1 -1
  100. ksadk/server/static/assets/{chunk-XXDRQBXY-C_32ArgP.js → chunk-XXDRQBXY-BTG24xN0.js} +1 -1
  101. ksadk/server/static/assets/classDiagram-DTDB5LWJ-Dd0nucl9.js +1 -0
  102. ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-Dd0nucl9.js +1 -0
  103. ksadk/server/static/assets/{cose-bilkent-JH36ORCC-9YQYwdl0.js → cose-bilkent-JH36ORCC-CkonRdAS.js} +1 -1
  104. ksadk/server/static/assets/cynefin-OW5HDTMX-BBlT4uMf.js +1 -0
  105. ksadk/server/static/assets/{cynefinDiagram-5FMLGOSQ-ErLF5N13.js → cynefinDiagram-5FMLGOSQ-DEOCBe15.js} +1 -1
  106. ksadk/server/static/assets/{dagre-3AP2YEHR-DFiQF-6f.js → dagre-3AP2YEHR-DBThPuLa.js} +1 -1
  107. ksadk/server/static/assets/{dagre-BuhGnRI1.js → dagre-ClTPrTht.js} +1 -1
  108. ksadk/server/static/assets/{diagram-S7CK7UJ4-Bt1v8-GC.js → diagram-S7CK7UJ4-DsZHrnpJ.js} +1 -1
  109. ksadk/server/static/assets/{diagram-UQ7AKVKN-DSCxJdBK.js → diagram-UQ7AKVKN-BJR4nhVr.js} +1 -1
  110. ksadk/server/static/assets/{diagram-VSXAHHWV-DHfYwp_9.js → diagram-VSXAHHWV-xzcERX0a.js} +1 -1
  111. ksadk/server/static/assets/{diagram-VX7I27RA-DdzD-4Le.js → diagram-VX7I27RA-tn2xM1xu.js} +1 -1
  112. ksadk/server/static/assets/{diagram-Z3DM3KII-Du-nAJ9F.js → diagram-Z3DM3KII-BZPGBkw5.js} +1 -1
  113. ksadk/server/static/assets/{dist-ClxGviKw.js → dist-4zN1_tq5.js} +1 -1
  114. ksadk/server/static/assets/{dist-BE7bu-DL.js → dist-8VWI-2bZ.js} +1 -1
  115. ksadk/server/static/assets/{dist-BX8z72IC.js → dist-B-46q3Hk.js} +1 -1
  116. ksadk/server/static/assets/{dist-BjU6y-A2.js → dist-B0gdk4rl.js} +1 -1
  117. ksadk/server/static/assets/{dist-C_wsv-Qd.js → dist-B1oWRmrH.js} +1 -1
  118. ksadk/server/static/assets/{dist-ngtN0DJB.js → dist-BI1qQH4n.js} +1 -1
  119. ksadk/server/static/assets/{dist-dydu68Fl.js → dist-BJLUwH6W.js} +1 -1
  120. ksadk/server/static/assets/{dist-Dq9gLD7L.js → dist-Bbwqa8TG.js} +1 -1
  121. ksadk/server/static/assets/{dist-CttYUqzS.js → dist-Btyo4vX4.js} +1 -1
  122. ksadk/server/static/assets/{dist-CXYYBw3_.js → dist-C71CsujO.js} +1 -1
  123. ksadk/server/static/assets/{dist-dDdADKFA.js → dist-CXpH_Rdd.js} +1 -1
  124. ksadk/server/static/assets/{dist-CSh_DE14.js → dist-Cq4OL6nO.js} +1 -1
  125. ksadk/server/static/assets/{dist-8hqcx0sU.js → dist-D1V3f1B4.js} +1 -1
  126. ksadk/server/static/assets/{dist-BVDffZ0U.js → dist-DJmCPRVB.js} +1 -1
  127. ksadk/server/static/assets/{dist-DnfXs8Vn.js → dist-DQAB0qn_.js} +1 -1
  128. ksadk/server/static/assets/{dist-DNp6rLRA.js → dist-DUwKa_l6.js} +1 -1
  129. ksadk/server/static/assets/{dist-B7seoj3d.js → dist-DdRBIceA.js} +1 -1
  130. ksadk/server/static/assets/{dist-BytlxIDT.js → dist-DfGFrb0v.js} +1 -1
  131. ksadk/server/static/assets/{dist-Bf2M8m3N.js → dist-DjhwGUgZ.js} +1 -1
  132. ksadk/server/static/assets/{dist-W-TIVntx.js → dist-DyqV4fL4.js} +1 -1
  133. ksadk/server/static/assets/{dist-DUX-id_F.js → dist-M67Hwu0n.js} +1 -1
  134. ksadk/server/static/assets/{dist-DmldrHd_.js → dist-u8tVH5Ak.js} +1 -1
  135. ksadk/server/static/assets/{ebnfDiagram-PWID7BFC-Da9C9NO1.js → ebnfDiagram-PWID7BFC-DVfMsLge.js} +1 -1
  136. ksadk/server/static/assets/{erDiagram-SSCWMZ5O-EAGqqR79.js → erDiagram-SSCWMZ5O-CWEZSYaw.js} +1 -1
  137. ksadk/server/static/assets/eventmodeling-NTZA5JFV-Dr5okd8V.js +1 -0
  138. ksadk/server/static/assets/flowDiagram-A5DVABFB-VEO-cVcO.js +1 -0
  139. ksadk/server/static/assets/{ganttDiagram-EL5Y4UJY-DqJsKb59.js → ganttDiagram-EL5Y4UJY-TWIxoyYL.js} +1 -1
  140. ksadk/server/static/assets/gitGraph-4MIJSDKK-CKiKY9Cf.js +1 -0
  141. ksadk/server/static/assets/{gitGraphDiagram-WWUBYQGX-B6g4dtDi.js → gitGraphDiagram-WWUBYQGX-DDh2hd2S.js} +1 -1
  142. ksadk/server/static/assets/index-B2k_urY8.js +241 -0
  143. ksadk/server/static/assets/index-ILPt6IWF.css +2 -0
  144. ksadk/server/static/assets/info-A6RAGUB7-ubA4Nm_p.js +1 -0
  145. ksadk/server/static/assets/{infoDiagram-RXCK75RN-Djm_nbd5.js → infoDiagram-RXCK75RN-CgoyWiDR.js} +1 -1
  146. ksadk/server/static/assets/{ishikawaDiagram-5VMMS53U-DWya9SG2.js → ishikawaDiagram-5VMMS53U-CyRIu_om.js} +1 -1
  147. ksadk/server/static/assets/{journeyDiagram-EYS64GPL-DutxhSNI.js → journeyDiagram-EYS64GPL-DN5JXHzN.js} +1 -1
  148. ksadk/server/static/assets/{kanban-definition-3QL26DDD-DJdOF8Fm.js → kanban-definition-3QL26DDD-CNP3ui66.js} +1 -1
  149. ksadk/server/static/assets/{katex-vFWytM5c.js → katex-DWEzQy1d.js} +1 -1
  150. ksadk/server/static/assets/{linear-BHjG8b-J.js → linear-C-qHp7d2.js} +1 -1
  151. ksadk/server/static/assets/{mermaid-parser.core-KGSy4jWT.js → mermaid-parser.core-Cl-K943T.js} +2 -2
  152. ksadk/server/static/assets/{mindmap-definition-FBJOCRG2-9-HC88D4.js → mindmap-definition-FBJOCRG2-D5DB5mYm.js} +1 -1
  153. ksadk/server/static/assets/packet-AYTQ26CC-C7R3NUlK.js +1 -0
  154. ksadk/server/static/assets/{pegDiagram-XKGWAZYB-DrEZd4fD.js → pegDiagram-XKGWAZYB-BQVc9ia_.js} +1 -1
  155. ksadk/server/static/assets/pie-WAS4IAKB-BIT6qjr3.js +1 -0
  156. ksadk/server/static/assets/{pieDiagram-E7YTZNPT-DpjqOFFp.js → pieDiagram-E7YTZNPT-B6UCAvtY.js} +1 -1
  157. ksadk/server/static/assets/{quadrantDiagram-AXDQQJYC-BiNhgOXX.js → quadrantDiagram-AXDQQJYC-mIr2d3eb.js} +1 -1
  158. ksadk/server/static/assets/radar-RG4KPBEZ-l5noef9X.js +1 -0
  159. ksadk/server/static/assets/railroad-74A4TZTK-Baea6ZvM.js +1 -0
  160. ksadk/server/static/assets/railroad-abnf-HS5TGJTU-OKn8zZnU.js +1 -0
  161. ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-DdKAKC4l.js +1 -0
  162. ksadk/server/static/assets/railroad-peg-WCYAUIDC-Bds8udlG.js +1 -0
  163. ksadk/server/static/assets/{railroadDiagram-O6MQD6OU-_Y3Ojg6G.js → railroadDiagram-O6MQD6OU-Dt3FFDmK.js} +1 -1
  164. ksadk/server/static/assets/{requirementDiagram-EFPCY7ZU-DeCFdg9K.js → requirementDiagram-EFPCY7ZU-Cz_vQtgF.js} +1 -1
  165. ksadk/server/static/assets/{sankeyDiagram-P5KCCOFB-CPH75rhw.js → sankeyDiagram-P5KCCOFB-wlO1TsH5.js} +1 -1
  166. ksadk/server/static/assets/{sequenceDiagram-WJ2MYXX4-OEpQQdr4.js → sequenceDiagram-WJ2MYXX4-CHRbElYq.js} +1 -1
  167. ksadk/server/static/assets/{sizeCapture-X5ZJPWSS-heUErzhI.js → sizeCapture-X5ZJPWSS-DXX54iyO.js} +1 -1
  168. ksadk/server/static/assets/{stateDiagram-HBIQ2CUA-DvA3jSMB.js → stateDiagram-HBIQ2CUA-Dp_lw9II.js} +1 -1
  169. ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-5b-8o2iS.js +1 -0
  170. ksadk/server/static/assets/{swimlanes-XN3QIQJK-CR-dfN_t.js → swimlanes-XN3QIQJK-D8O5UlLG.js} +1 -1
  171. ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-uf6WPRA4.js +8 -0
  172. ksadk/server/static/assets/{timeline-definition-24CTP7MA-DAVJf1TX.js → timeline-definition-24CTP7MA-8mfKcxvG.js} +1 -1
  173. ksadk/server/static/assets/treeView-Q6P3EWNA-CK5CSrSO.js +1 -0
  174. ksadk/server/static/assets/treemap-WGGIJYW6-Ch2BKgAM.js +1 -0
  175. ksadk/server/static/assets/{vennDiagram-4TSXK5OY-DwM8eDkh.js → vennDiagram-4TSXK5OY-EnNl0DE7.js} +1 -1
  176. ksadk/server/static/assets/wardley-WFR3VGLG-YW3Q0ml_.js +1 -0
  177. ksadk/server/static/assets/{wardleyDiagram-VM6X3IG4-DZL-Zz2t.js → wardleyDiagram-VM6X3IG4-DBu5fbYr.js} +1 -1
  178. ksadk/server/static/assets/{xychartDiagram-S5SC5T6Z-Dy6Yh_hu.js → xychartDiagram-S5SC5T6Z-BSVXs4IY.js} +1 -1
  179. ksadk/server/static/index.html +2 -2
  180. ksadk/skills/service_client.py +48 -0
  181. ksadk/studio/agent_lifecycle.py +26 -20
  182. ksadk/studio/api.py +513 -71
  183. ksadk/studio/api_catalog_routes.py +5 -0
  184. ksadk/studio/api_contracts.py +34 -1
  185. ksadk/studio/api_plugin_routes.py +480 -0
  186. ksadk/studio/attachment_store.py +187 -0
  187. ksadk/studio/authoring.py +10 -4
  188. ksadk/studio/authoring_coordinator.py +12 -2
  189. ksadk/studio/builder.py +123 -43
  190. ksadk/studio/cloud.py +4 -0
  191. ksadk/studio/codex_agent_service.py +12 -1
  192. ksadk/studio/codex_builder.py +54 -15
  193. ksadk/studio/codex_manifest.py +21 -1
  194. ksadk/studio/codex_run.py +16 -4
  195. ksadk/studio/compatibility_report.py +445 -0
  196. ksadk/studio/compiler.py +6 -1
  197. ksadk/studio/contracts.py +123 -4
  198. ksadk/studio/dsh_provider_registration.py +768 -0
  199. ksadk/studio/event_store.py +54 -6
  200. ksadk/studio/framework_run.py +37 -4
  201. ksadk/studio/hosted_kernel.py +1 -1
  202. ksadk/studio/operations.py +12 -2
  203. ksadk/studio/plugin_composition.py +301 -0
  204. ksadk/studio/plugin_kernel_adapter.py +128 -0
  205. ksadk/studio/plugin_runtime.py +652 -0
  206. ksadk/studio/resource_catalog.py +6 -1
  207. ksadk/studio/run_service.py +713 -75
  208. ksadk/studio/runtime_source.py +9 -1
  209. ksadk/studio/scheduler_runtime.py +374 -0
  210. ksadk/studio/scheduler_service.py +211 -0
  211. ksadk/studio/service.py +672 -82
  212. ksadk/studio/shared_web.py +167 -28
  213. ksadk/studio/soul.py +52 -0
  214. ksadk/studio/static/assets/index-DXs1Eq4M.css +1 -0
  215. ksadk/studio/static/assets/index-DeDqwTsD.js +283 -0
  216. ksadk/studio/static/index.html +2 -2
  217. ksadk/studio/validator.py +1 -1
  218. ksadk/studio/workspace.py +1 -0
  219. ksadk/version.py +1 -1
  220. ksadk_runtime_common/workspace_files/path_utils.py +41 -0
  221. ksadk/server/static/assets/architecture-7GRP2DOG-DjKratdf.js +0 -1
  222. ksadk/server/static/assets/channel-4cQHKtx1.js +0 -1
  223. ksadk/server/static/assets/chunk-2Q5K7J3B-DmgWkESh.js +0 -1
  224. ksadk/server/static/assets/classDiagram-DTDB5LWJ-DLFd9Xi0.js +0 -1
  225. ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-DLFd9Xi0.js +0 -1
  226. ksadk/server/static/assets/cynefin-OW5HDTMX-DjEM48Qp.js +0 -1
  227. ksadk/server/static/assets/eventmodeling-NTZA5JFV-CMgMaJrC.js +0 -1
  228. ksadk/server/static/assets/flowDiagram-A5DVABFB-BBKL0x3P.js +0 -1
  229. ksadk/server/static/assets/gitGraph-4MIJSDKK-Dw63mrhw.js +0 -1
  230. ksadk/server/static/assets/index-8ipRcQ-M.js +0 -240
  231. ksadk/server/static/assets/index-ByFCqjlh.css +0 -2
  232. ksadk/server/static/assets/info-A6RAGUB7-B6iAblII.js +0 -1
  233. ksadk/server/static/assets/packet-AYTQ26CC-BOYAipuq.js +0 -1
  234. ksadk/server/static/assets/pie-WAS4IAKB-Ctq0Kbku.js +0 -1
  235. ksadk/server/static/assets/radar-RG4KPBEZ-DN0-ul_0.js +0 -1
  236. ksadk/server/static/assets/railroad-74A4TZTK-C6cC_to0.js +0 -1
  237. ksadk/server/static/assets/railroad-abnf-HS5TGJTU-BimjIuRc.js +0 -1
  238. ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-CvY65ABq.js +0 -1
  239. ksadk/server/static/assets/railroad-peg-WCYAUIDC-D72Iq_Tv.js +0 -1
  240. ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BgQY03nz.js +0 -1
  241. ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-DLVw3q5Q.js +0 -8
  242. ksadk/server/static/assets/treeView-Q6P3EWNA-BMpO0wsB.js +0 -1
  243. ksadk/server/static/assets/treemap-WGGIJYW6-Et0aqzU3.js +0 -1
  244. ksadk/server/static/assets/wardley-WFR3VGLG-CbY0mhpl.js +0 -1
  245. ksadk/studio/static/assets/index-BLpcdrgW.css +0 -1
  246. ksadk/studio/static/assets/index-CYekR2Xv.js +0 -260
  247. {agentengine_sdk_python-0.8.2.dist-info → agentengine_sdk_python-0.8.3.dist-info}/WHEEL +0 -0
  248. {agentengine_sdk_python-0.8.2.dist-info → agentengine_sdk_python-0.8.3.dist-info}/entry_points.txt +0 -0
  249. {agentengine_sdk_python-0.8.2.dist-info → agentengine_sdk_python-0.8.3.dist-info}/licenses/LICENSE +0 -0
  250. {agentengine_sdk_python-0.8.2.dist-info → agentengine_sdk_python-0.8.3.dist-info}/top_level.txt +0 -0
@@ -89,6 +89,11 @@ def register_catalog_routes(
89
89
  async def catalog_runtimes():
90
90
  return {"items": studio.runtime_catalog(), "nextCursor": None}
91
91
 
92
+ @app.get("/api/v1/agent-providers")
93
+ async def agent_providers():
94
+ await studio.start()
95
+ return {"items": studio.agent_provider_catalog(), "nextCursor": None}
96
+
92
97
  @app.get("/api/v1/catalog/resources/{resource_id}")
93
98
  async def get_catalog_resource(resource_id: str):
94
99
  return studio.catalog.get(resource_id)
@@ -6,8 +6,10 @@ from typing import Any, Literal
6
6
 
7
7
  from pydantic import Field, SecretStr, field_validator
8
8
 
9
+ from ksadk.conversations.contracts import ConversationInput
9
10
  from ksadk.evaluation import EvaluationConfig as PublicEvaluationConfig
10
11
  from ksadk.evaluation import TargetRef
12
+ from ksadk.scheduler.contracts import ScheduleSpec
11
13
  from ksadk.studio.contracts import (
12
14
  AgentBindings,
13
15
  AgentSpec,
@@ -78,7 +80,7 @@ class MessageInput(ContractModel):
78
80
  class QuickAuthoringRequest(ContractModel):
79
81
  name: str = Field(min_length=1, max_length=128)
80
82
  slug: str | None = Field(default=None, min_length=1, max_length=63)
81
- runtime_type: Literal["codex", "adk", "langgraph"]
83
+ runtime_type: Literal["codex", "adk", "langgraph", "plugin"]
82
84
  template: Literal["blank", "research"] = "blank"
83
85
  description: str = Field(default="", max_length=1024)
84
86
  spec: AgentSpec | None = None
@@ -135,9 +137,40 @@ class RunRequest(ContractModel):
135
137
  sandbox: str | None = None
136
138
 
137
139
 
140
+ class ConversationTurnRequest(ContractModel):
141
+ """A capability-gated local Studio turn.
142
+
143
+ Unlike the compatibility ``RunRequest`` this request deliberately contains
144
+ only the provider-neutral ConversationInput. The server resolves the
145
+ active Build's ConversationSurface and rejects undeclared intent before it
146
+ reaches a framework-specific runtime request.
147
+ """
148
+
149
+ input: ConversationInput
150
+ sandbox: str | None = None
151
+
152
+
153
+ class AgentScheduleRequest(ContractModel):
154
+ """Browser-safe authoring input for one local ``ScheduledTask``.
155
+
156
+ Stable Agent/Build/Kernel target fields are intentionally absent. Studio
157
+ resolves those from the active local Runtime binding and refuses creation
158
+ when it cannot prove that binding.
159
+ """
160
+
161
+ display_name: str = Field(min_length=1, max_length=128)
162
+ prompt: str = Field(min_length=1, max_length=32_768)
163
+ schedule: ScheduleSpec
164
+ enabled: bool = True
165
+ continuity: Literal["new_session", "continue_session"] = "new_session"
166
+ session_id: str | None = Field(default=None, min_length=1, max_length=256)
167
+
168
+
138
169
  class InteractionSubmitRequest(ContractModel):
139
170
  name: str = Field(min_length=1, max_length=64)
140
171
  data: dict[str, Any] = Field(default_factory=dict)
172
+ expected_revision: int = Field(alias="expectedRevision", ge=1)
173
+ idempotency_key: str = Field(alias="idempotencyKey", min_length=1, max_length=256)
141
174
 
142
175
 
143
176
  class CloudChatMessageRequest(ContractModel):
@@ -0,0 +1,480 @@
1
+ """Studio lifecycle API for DSH plugins and the Codex compatibility bridge."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import hashlib
7
+ import os
8
+ from pathlib import Path
9
+ from typing import Any, Callable
10
+ from urllib.parse import urlencode, urlparse
11
+ from uuid import uuid4
12
+
13
+ from fastapi import FastAPI, Query, Response
14
+ from pydantic import BaseModel, ConfigDict, Field, field_validator
15
+
16
+ from ksadk.plugins.bridges.codex import (
17
+ CodexAppServerPluginBridge,
18
+ CodexBridgeHost,
19
+ CodexPluginApprovalRequired,
20
+ CodexPluginDetail,
21
+ CodexPluginInventory,
22
+ CodexPluginNotFoundError,
23
+ )
24
+ from ksadk.plugins.bridges.dsh import (
25
+ DshBridgeHost,
26
+ DshHostUnavailableError,
27
+ DshPluginApprovalRequired,
28
+ DshPluginInventory,
29
+ DshPluginMutationError,
30
+ DshPluginNotFoundError,
31
+ DshProfilePluginBridge,
32
+ )
33
+ from ksadk.plugins.dsh_toolchain import DshToolchainError, DshToolchainManager
34
+ from ksadk.studio.errors import StudioError
35
+ from ksadk.studio.service import StudioService
36
+
37
+
38
+ class CodexPluginInstallRequest(BaseModel):
39
+ model_config = ConfigDict(extra="forbid", populate_by_name=True)
40
+ marketplace_name: str | None = Field(
41
+ default=None, alias="marketplaceName", min_length=1, max_length=256
42
+ )
43
+ accept_undeclared_permissions: bool = Field(default=False, alias="acceptUndeclaredPermissions")
44
+
45
+
46
+ class DshPluginInstallRequest(BaseModel):
47
+ model_config = ConfigDict(extra="forbid", populate_by_name=True)
48
+ source: str = Field(min_length=1, max_length=2048)
49
+ accept_host_permissions: bool = Field(default=False, alias="acceptHostPermissions")
50
+
51
+ @field_validator("source")
52
+ @classmethod
53
+ def validate_source(cls, value: str) -> str:
54
+ normalized = value.strip()
55
+ if not normalized or any(character in value for character in ("\x00", "\r", "\n")):
56
+ raise ValueError("source must be one package, Git URL, or absolute local path")
57
+ return normalized
58
+
59
+
60
+ class DshPluginUpdateRequest(BaseModel):
61
+ model_config = ConfigDict(extra="forbid", populate_by_name=True)
62
+ accept_host_permissions: bool = Field(default=False, alias="acceptHostPermissions")
63
+
64
+
65
+ def _studio_codex_home(studio: StudioService) -> tuple[Path, str]:
66
+ configured = os.environ.get("KSADK_CODEX_HOME", "").strip()
67
+ if configured:
68
+ return Path(configured).expanduser(), "explicit"
69
+ return studio.workspace.root / ".agentkit" / "codex-home", "workspace-isolated"
70
+
71
+
72
+ def _studio_dsh_options(studio: StudioService) -> tuple[Path, str, tuple[str, ...] | None, str]:
73
+ configured_home = os.environ.get("KSADK_DSH_HOME", "").strip()
74
+ home = (
75
+ Path(configured_home).expanduser()
76
+ if configured_home
77
+ else studio.workspace.root / ".agentkit" / "dsh-home"
78
+ )
79
+ configured_bin = os.environ.get("KSADK_DSH_BIN", "").strip()
80
+ if configured_bin:
81
+ command = (str(Path(configured_bin).expanduser()),)
82
+ else:
83
+ try:
84
+ command = DshToolchainManager().require_command()
85
+ except (DshToolchainError, OSError, ValueError):
86
+ # Keep the bridge's established PATH lookup when the optional
87
+ # pinned toolchain has not been installed or is unusable.
88
+ command = None
89
+ profile = os.environ.get("KSADK_DSH_PROFILE", "").strip() or "studio"
90
+ return home, profile, command, "explicit" if configured_home else "workspace-isolated"
91
+
92
+
93
+ def _public_host(kind: str, host: Any | None, *, home_mode: str) -> dict[str, Any]:
94
+ codex = kind == "codex"
95
+ return {
96
+ "hostId": "codex-app-server" if codex else "deepseek-harness",
97
+ "available": host is not None,
98
+ "status": "available" if host is not None else "unavailable",
99
+ "version": host.version if host is not None else None,
100
+ "protocol": host.protocol
101
+ if host is not None
102
+ else ("codex.app-server/v1" if codex else "dsh.profile/v1"),
103
+ "homeMode": home_mode,
104
+ }
105
+
106
+
107
+ def _public_codex_source(source: BaseModel) -> dict[str, Any]:
108
+ raw = source.model_dump(by_alias=True, mode="json")
109
+ source_type = str(raw.get("type") or "remote")
110
+ payload: dict[str, Any] = {"type": source_type}
111
+ if source_type == "local":
112
+ payload["name"] = Path(str(raw.get("path") or "plugin")).name or "plugin"
113
+ elif source_type == "git":
114
+ payload["name"] = Path(urlparse(str(raw.get("url") or "")).path.rstrip("/")).name
115
+ for key in ("refName", "sha"):
116
+ if raw.get(key):
117
+ payload[key] = raw[key]
118
+ elif source_type == "npm":
119
+ payload.update(package=raw.get("package"), version=raw.get("version"))
120
+ return payload
121
+
122
+
123
+ def _public_codex_inventory(
124
+ inventory: CodexPluginInventory, *, host: CodexBridgeHost, home_mode: str
125
+ ) -> dict[str, Any]:
126
+ return {
127
+ "ecosystem": "codex",
128
+ "integrationMode": "bridged",
129
+ "pluginId": inventory.plugin_id,
130
+ "resolvedVersion": inventory.version,
131
+ "distributionName": inventory.name,
132
+ "displayName": inventory.name,
133
+ "marketplaceName": inventory.marketplace_name,
134
+ "source": _public_codex_source(inventory.source),
135
+ "installed": inventory.installed,
136
+ "state": "enabled" if inventory.enabled else "disabled",
137
+ "enabled": inventory.enabled,
138
+ "availability": inventory.availability,
139
+ "permissions": [],
140
+ "permissionsDeclared": False,
141
+ "riskDisclosures": list(inventory.risk_disclosures),
142
+ "isolation": "host-managed",
143
+ "runtimeState": None,
144
+ "host": _public_host("codex", host, home_mode=home_mode),
145
+ }
146
+
147
+
148
+ def _public_codex_detail(
149
+ detail: CodexPluginDetail, *, host: CodexBridgeHost, home_mode: str
150
+ ) -> dict[str, Any]:
151
+ return {
152
+ "item": _public_codex_inventory(detail.inventory, host=host, home_mode=home_mode),
153
+ "description": detail.description,
154
+ "capabilities": {
155
+ "skills": list(detail.skills),
156
+ "mcpServers": list(detail.mcp_servers),
157
+ "hooks": list(detail.hooks),
158
+ "apps": list(detail.apps),
159
+ "scheduledTasks": list(detail.scheduled_tasks),
160
+ },
161
+ }
162
+
163
+
164
+ def _public_dsh_inventory(
165
+ inventory: DshPluginInventory,
166
+ *,
167
+ host: DshBridgeHost,
168
+ home_mode: str,
169
+ runtime_state: dict[str, Any] | None = None,
170
+ ) -> dict[str, Any]:
171
+ client = inventory.client_bundle
172
+ return {
173
+ "ecosystem": "dsh",
174
+ "integrationMode": "bridged",
175
+ "pluginId": inventory.name,
176
+ "resolvedVersion": inventory.version,
177
+ "distributionName": inventory.name,
178
+ "displayName": inventory.display_name,
179
+ "description": inventory.description,
180
+ "profile": inventory.profile,
181
+ "source": {"type": "host-profile", "name": inventory.name},
182
+ "installed": True,
183
+ "state": "enabled" if inventory.enabled else "disabled",
184
+ "enabled": inventory.enabled,
185
+ "permissions": [],
186
+ "permissionsDeclared": False,
187
+ "riskDisclosures": list(inventory.risk_disclosures),
188
+ "isolation": "host-managed",
189
+ "runtimeState": runtime_state,
190
+ "clientBundle": (
191
+ {
192
+ "platform": client.platform,
193
+ "digest": client.digest,
194
+ "contentBytes": client.content_bytes,
195
+ "external": list(client.external),
196
+ "inject": list(client.inject),
197
+ "compatible": client.compatible,
198
+ "incompatibilityReason": client.incompatibility_reason or None,
199
+ }
200
+ if client is not None
201
+ else None
202
+ ),
203
+ "host": _public_host("dsh", host, home_mode=home_mode),
204
+ }
205
+
206
+
207
+ def _public_dsh_client_bundle(inventory: DshPluginInventory) -> dict[str, Any] | None:
208
+ client = inventory.client_bundle
209
+ if client is None:
210
+ return None
211
+ query = urlencode({"pluginName": inventory.name, "digest": client.digest})
212
+ return {
213
+ "pluginId": inventory.name,
214
+ "enabled": inventory.enabled,
215
+ "compatible": client.compatible,
216
+ "digest": client.digest,
217
+ "contentBytes": client.content_bytes,
218
+ "external": list(client.external),
219
+ "inject": list(client.inject),
220
+ "incompatibilityReason": client.incompatibility_reason or None,
221
+ "url": f"/api/v1/plugin-ecosystems/dsh/client-bundle?{query}",
222
+ }
223
+
224
+
225
+ def _codex_error(error: Exception) -> StudioError:
226
+ if isinstance(error, CodexPluginNotFoundError):
227
+ return StudioError(
228
+ "CODEX_PLUGIN_NOT_FOUND", "Codex 插件不存在或来源不唯一", status_code=404
229
+ )
230
+ if isinstance(error, CodexPluginApprovalRequired):
231
+ return StudioError(
232
+ "CODEX_PLUGIN_RISK_CONFIRMATION_REQUIRED",
233
+ "安装 Codex 插件前必须确认宿主权限风险",
234
+ status_code=422,
235
+ )
236
+ return StudioError("CODEX_PLUGIN_HOST_UNAVAILABLE", "Codex 插件宿主当前不可用", status_code=503)
237
+
238
+
239
+ def _dsh_error(error: Exception) -> StudioError:
240
+ if isinstance(error, DshPluginNotFoundError):
241
+ return StudioError("DSH_PLUGIN_NOT_FOUND", "DSH 插件未安装", status_code=404)
242
+ if isinstance(error, DshPluginApprovalRequired):
243
+ return StudioError(
244
+ "DSH_PLUGIN_RISK_CONFIRMATION_REQUIRED",
245
+ "安装或升级 DSH 插件前必须确认宿主权限风险",
246
+ status_code=422,
247
+ )
248
+ if isinstance(error, DshPluginMutationError):
249
+ return StudioError(
250
+ "DSH_PLUGIN_MUTATION_FAILED", "DSH 插件操作失败,原 Profile 已保留", status_code=409
251
+ )
252
+ if isinstance(error, (DshHostUnavailableError, OSError)):
253
+ return StudioError("DSH_PLUGIN_HOST_UNAVAILABLE", "DSH 插件宿主当前不可用", status_code=503)
254
+ return StudioError("DSH_PLUGIN_OPERATION_FAILED", "DSH 插件操作失败", status_code=422)
255
+
256
+
257
+ def register_plugin_routes(app: FastAPI, studio: StudioService) -> None:
258
+ """Register only DSH Profile and Codex App Server lifecycle routes."""
259
+
260
+ codex_home, codex_mode = _studio_codex_home(studio)
261
+ dsh_home, dsh_profile, dsh_command, dsh_mode = _studio_dsh_options(studio)
262
+
263
+ def public_dsh(item: DshPluginInventory, host: DshBridgeHost) -> dict[str, Any]:
264
+ return _public_dsh_inventory(
265
+ item,
266
+ host=host,
267
+ home_mode=dsh_mode,
268
+ runtime_state=studio.dsh_provider_runtime_state(item.name),
269
+ )
270
+
271
+ def call_dsh(operation: Callable[[DshProfilePluginBridge], Any]) -> Any:
272
+ try:
273
+ with DshProfilePluginBridge(
274
+ dsh_home=dsh_home, profile=dsh_profile, dsh_command=dsh_command
275
+ ) as bridge:
276
+ return bridge.host, operation(bridge)
277
+ except Exception as error:
278
+ raise _dsh_error(error) from None
279
+
280
+ @app.get("/api/v1/plugin-ecosystems/codex/plugins")
281
+ async def list_codex_plugins(
282
+ installed_only: bool = Query(default=False), force_refetch: bool = Query(default=False)
283
+ ):
284
+ try:
285
+ async with CodexAppServerPluginBridge(codex_home=codex_home) as bridge:
286
+ items = await bridge.list_plugins(force_refetch=force_refetch)
287
+ visible = [item for item in items if item.installed or not installed_only]
288
+ return {
289
+ "ecosystem": "codex",
290
+ "integrationMode": "bridged",
291
+ "host": _public_host("codex", bridge.host, home_mode=codex_mode),
292
+ "items": [
293
+ _public_codex_inventory(item, host=bridge.host, home_mode=codex_mode)
294
+ for item in visible
295
+ ],
296
+ }
297
+ except Exception:
298
+ return {
299
+ "ecosystem": "codex",
300
+ "integrationMode": "bridged",
301
+ "host": _public_host("codex", None, home_mode=codex_mode),
302
+ "items": [],
303
+ "error": {
304
+ "code": "CODEX_PLUGIN_HOST_UNAVAILABLE",
305
+ "message": "Codex 插件宿主当前不可用",
306
+ },
307
+ }
308
+
309
+ @app.get("/api/v1/plugin-ecosystems/codex/plugins/{plugin_id}")
310
+ async def get_codex_plugin(
311
+ plugin_id: str, marketplace_name: str | None = Query(default=None, max_length=256)
312
+ ):
313
+ try:
314
+ async with CodexAppServerPluginBridge(codex_home=codex_home) as bridge:
315
+ detail = await bridge.read_plugin(plugin_id, marketplace_name=marketplace_name)
316
+ return _public_codex_detail(detail, host=bridge.host, home_mode=codex_mode)
317
+ except Exception as error:
318
+ raise _codex_error(error) from None
319
+
320
+ @app.post("/api/v1/plugin-ecosystems/codex/plugins/{plugin_id}:install")
321
+ async def install_codex_plugin(plugin_id: str, payload: CodexPluginInstallRequest):
322
+ if not payload.accept_undeclared_permissions:
323
+ raise _codex_error(CodexPluginApprovalRequired("approval required"))
324
+ try:
325
+ async with CodexAppServerPluginBridge(codex_home=codex_home) as bridge:
326
+ result = await bridge.install_plugin(
327
+ plugin_id,
328
+ marketplace_name=payload.marketplace_name,
329
+ accept_undeclared_permissions=True,
330
+ install_attempt_id=f"studio-{uuid4().hex}",
331
+ )
332
+ return {
333
+ "item": _public_codex_inventory(
334
+ result.inventory, host=bridge.host, home_mode=codex_mode
335
+ ),
336
+ "authPolicy": result.auth_policy,
337
+ "appsNeedingAuth": list(result.apps_needing_auth),
338
+ }
339
+ except Exception as error:
340
+ raise _codex_error(error) from None
341
+
342
+ @app.delete("/api/v1/plugin-ecosystems/codex/plugins/{plugin_id}", status_code=204)
343
+ async def uninstall_codex_plugin(plugin_id: str):
344
+ try:
345
+ async with CodexAppServerPluginBridge(codex_home=codex_home) as bridge:
346
+ await bridge.uninstall_plugin(plugin_id)
347
+ return Response(status_code=204)
348
+ except Exception as error:
349
+ raise _codex_error(error) from None
350
+
351
+ @app.get("/api/v1/plugin-ecosystems/dsh/plugins")
352
+ async def list_dsh_plugins():
353
+ try:
354
+ host, items = await asyncio.to_thread(call_dsh, lambda bridge: bridge.list_plugins())
355
+ return {
356
+ "ecosystem": "dsh",
357
+ "integrationMode": "bridged",
358
+ "profile": dsh_profile,
359
+ "host": _public_host("dsh", host, home_mode=dsh_mode),
360
+ "items": [public_dsh(item, host) for item in items],
361
+ }
362
+ except StudioError as error:
363
+ return {
364
+ "ecosystem": "dsh",
365
+ "integrationMode": "bridged",
366
+ "profile": dsh_profile,
367
+ "host": _public_host("dsh", None, home_mode=dsh_mode),
368
+ "items": [],
369
+ "error": {"code": error.code, "message": error.message},
370
+ }
371
+
372
+ @app.get("/api/v1/plugin-ecosystems/dsh/profile")
373
+ async def get_dsh_profile_projection():
374
+ host, result = await asyncio.to_thread(
375
+ call_dsh, lambda bridge: (bridge.project_profile(), bridge.list_plugins())
376
+ )
377
+ projection, items = result
378
+ bundles = [
379
+ projected
380
+ for item in items
381
+ if item.enabled
382
+ for projected in [_public_dsh_client_bundle(item)]
383
+ if projected is not None
384
+ ]
385
+ graph_hash = hashlib.sha256(projection.config_digest.encode("utf-8"))
386
+ for bundle in bundles:
387
+ for value in (str(bundle["pluginId"]), str(bundle["digest"])):
388
+ encoded = value.encode("utf-8")
389
+ graph_hash.update(f"{len(encoded)}:".encode("ascii"))
390
+ graph_hash.update(encoded)
391
+ return {
392
+ "host": _public_host("dsh", host, home_mode=dsh_mode),
393
+ "profile": projection.model_dump(mode="json", by_alias=True),
394
+ "clientGraphDigest": f"sha256:{graph_hash.hexdigest()}",
395
+ "clientBundles": bundles,
396
+ }
397
+
398
+ @app.get("/api/v1/plugin-ecosystems/dsh/client-bundle")
399
+ async def get_dsh_client_bundle(
400
+ plugin_name: str = Query(alias="pluginName", min_length=1, max_length=256),
401
+ digest: str = Query(pattern=r"^sha256:[0-9a-f]{64}$"),
402
+ ):
403
+ _, content = await asyncio.to_thread(
404
+ call_dsh,
405
+ lambda bridge: bridge.read_client_bundle(plugin_name, expected_digest=digest),
406
+ )
407
+ return Response(
408
+ content=content,
409
+ media_type="application/javascript; charset=utf-8",
410
+ headers={
411
+ "Cache-Control": "private, max-age=31536000, immutable",
412
+ "ETag": f'"{digest}"',
413
+ "X-Content-Type-Options": "nosniff",
414
+ },
415
+ )
416
+
417
+ @app.post("/api/v1/plugin-ecosystems/dsh/plugins:install", status_code=201)
418
+ async def install_dsh_plugin(payload: DshPluginInstallRequest):
419
+ if not payload.accept_host_permissions:
420
+ raise _dsh_error(DshPluginApprovalRequired("approval required"))
421
+ host, item = await asyncio.to_thread(
422
+ call_dsh,
423
+ lambda bridge: bridge.install_plugin(payload.source, accept_host_permissions=True),
424
+ )
425
+ await studio.refresh_dsh_provider_registrations()
426
+ return {"item": public_dsh(item, host)}
427
+
428
+ async def mutate_dsh(plugin_name: str, operation: str):
429
+ def action(bridge: DshProfilePluginBridge):
430
+ if operation == "enable":
431
+ return bridge.set_enabled(plugin_name, enabled=True)
432
+ if operation == "disable":
433
+ return bridge.set_enabled(plugin_name, enabled=False)
434
+ if operation == "uninstall":
435
+ return bridge.uninstall_plugin(plugin_name)
436
+ return bridge.get_plugin(plugin_name)
437
+
438
+ return await asyncio.to_thread(call_dsh, action)
439
+
440
+ @app.post("/api/v1/plugin-ecosystems/dsh/plugins/{plugin_name:path}:enable")
441
+ async def enable_dsh_plugin(plugin_name: str):
442
+ host, item = await mutate_dsh(plugin_name, "enable")
443
+ await studio.refresh_dsh_provider_registrations()
444
+ return {"item": public_dsh(item, host)}
445
+
446
+ @app.post("/api/v1/plugin-ecosystems/dsh/plugins/{plugin_name:path}:disable")
447
+ async def disable_dsh_plugin(plugin_name: str):
448
+ host, item = await mutate_dsh(plugin_name, "disable")
449
+ await studio.refresh_dsh_provider_registrations()
450
+ return {"item": public_dsh(item, host)}
451
+
452
+ @app.post("/api/v1/plugin-ecosystems/dsh/plugins/{plugin_name:path}:update")
453
+ async def update_dsh_plugin(plugin_name: str, payload: DshPluginUpdateRequest):
454
+ if not payload.accept_host_permissions:
455
+ raise _dsh_error(DshPluginApprovalRequired("approval required"))
456
+ host, item = await asyncio.to_thread(
457
+ call_dsh,
458
+ lambda bridge: bridge.update_plugin(plugin_name, accept_host_permissions=True),
459
+ )
460
+ await studio.refresh_dsh_provider_registrations()
461
+ return {"item": public_dsh(item, host)}
462
+
463
+ @app.get("/api/v1/plugin-ecosystems/dsh/plugins/{plugin_name:path}")
464
+ async def get_dsh_plugin(plugin_name: str):
465
+ host, item = await mutate_dsh(plugin_name, "get")
466
+ return {"item": public_dsh(item, host)}
467
+
468
+ @app.delete("/api/v1/plugin-ecosystems/dsh/plugins/{plugin_name:path}", status_code=204)
469
+ async def uninstall_dsh_plugin(plugin_name: str):
470
+ await mutate_dsh(plugin_name, "uninstall")
471
+ await studio.refresh_dsh_provider_registrations()
472
+ return Response(status_code=204)
473
+
474
+
475
+ __all__ = [
476
+ "CodexPluginInstallRequest",
477
+ "DshPluginInstallRequest",
478
+ "DshPluginUpdateRequest",
479
+ "register_plugin_routes",
480
+ ]