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
@@ -0,0 +1,652 @@
1
+ """Local Studio execution for immutable PluginHost AgentBundle v2 builds."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import json
7
+ from collections.abc import Mapping, Sequence
8
+ from dataclasses import dataclass
9
+ from pathlib import Path
10
+ from typing import Any, cast
11
+
12
+ from ksadk.harness.reasoner import (
13
+ HarnessReasoner,
14
+ HarnessReasoningTurn,
15
+ HarnessToolCall,
16
+ )
17
+ from ksadk.plugins.builtins import (
18
+ builtin_capability_factories,
19
+ builtin_capability_manifests,
20
+ )
21
+ from ksadk.plugins.bundle import PluginBundleError, PluginBundleResolver, ResolvedPluginBundle
22
+ from ksadk.plugins.contracts import CompositionProfile, PluginManifest
23
+ from ksadk.plugins.host import PluginHost, PluginHostError
24
+ from ksadk.plugins.providers.harness import (
25
+ HarnessTurnResult,
26
+ KsADKHarnessProviderFactory,
27
+ )
28
+ from ksadk.plugins.providers.legacy import (
29
+ LegacyBundleAdapter,
30
+ LegacyBundleCompatibilityError,
31
+ LegacyHarnessSource,
32
+ )
33
+ from ksadk.plugins.providers.legacy_catalog import (
34
+ KSADK_HARNESS_AGENT_PROVIDER_PLUGIN_ID,
35
+ builtin_agent_provider_manifests,
36
+ legacy_harness_agent_provider_manifest,
37
+ )
38
+ from ksadk.plugins.resolver import PluginRegistry
39
+ from ksadk.runtime import RuntimeLaunchContext
40
+ from ksadk.sessions.base import BaseSessionService
41
+ from ksadk.studio.contracts import (
42
+ BuildRecord,
43
+ NetworkPolicy,
44
+ ResolvedModel,
45
+ )
46
+ from ksadk.studio.errors import StudioError
47
+ from ksadk.studio.model_client import OpenAICompatibleModelClient
48
+ from ksadk.studio.plugin_kernel_adapter import StudioPluginKernelAdapter
49
+ from ksadk.studio.repository import BuildRepository
50
+ from ksadk.studio.run_service import StudioRunSpec
51
+ from ksadk.studio.workspace import Workspace
52
+
53
+ _COMPOSED_RUNTIME_TYPES = frozenset({"harness", "plugin"})
54
+
55
+
56
+ @dataclass(frozen=True)
57
+ class StudioPluginTurnResult:
58
+ """Normalized result retained at the Studio/third-party boundary."""
59
+
60
+ output_text: str
61
+ session_id: str
62
+ usage: Mapping[str, Any]
63
+ metadata: Mapping[str, Any]
64
+ raw: Any
65
+
66
+
67
+ @dataclass
68
+ class _HostEntry:
69
+ agent_id: str
70
+ bundle: ResolvedPluginBundle
71
+ host: PluginHost
72
+
73
+
74
+ class _StudioHarnessReasoner:
75
+ """Bind the Harness loop to the immutable Studio model/network policy."""
76
+
77
+ def __init__(
78
+ self,
79
+ client: OpenAICompatibleModelClient,
80
+ *,
81
+ model: ResolvedModel,
82
+ network_policy: NetworkPolicy,
83
+ timeout_seconds: int,
84
+ max_attempts: int,
85
+ backoff_seconds: float,
86
+ ) -> None:
87
+ self._client = client
88
+ self._model = model
89
+ self._network_policy = network_policy
90
+ self._timeout_seconds = timeout_seconds
91
+ self._max_attempts = max_attempts
92
+ self._backoff_seconds = backoff_seconds
93
+
94
+ async def complete(
95
+ self,
96
+ *,
97
+ model: str,
98
+ prompt: str,
99
+ messages: Sequence[dict[str, Any]],
100
+ tools: Sequence[Any],
101
+ ) -> HarnessReasoningTurn:
102
+ del prompt
103
+ if model != self._model.model:
104
+ raise PluginHostError(
105
+ "harness_model_not_bound",
106
+ f"Harness requested unbound model {model!r}",
107
+ )
108
+ response = await self._client.complete(
109
+ self._model,
110
+ messages=[dict(message) for message in messages],
111
+ network_policy=self._network_policy,
112
+ timeout_seconds=self._timeout_seconds,
113
+ max_attempts=self._max_attempts,
114
+ backoff_seconds=self._backoff_seconds,
115
+ tools=[dict(tool.openai_schema) for tool in tools],
116
+ allow_empty=bool(tools),
117
+ )
118
+ calls: list[HarnessToolCall] = []
119
+ for call in response.tool_calls:
120
+ try:
121
+ arguments = json.loads(call.arguments or "{}")
122
+ except json.JSONDecodeError as error:
123
+ raise PluginHostError(
124
+ "harness_tool_arguments_invalid",
125
+ f"model emitted invalid arguments for tool {call.name!r}",
126
+ ) from error
127
+ if not isinstance(arguments, dict):
128
+ raise PluginHostError(
129
+ "harness_tool_arguments_invalid",
130
+ f"model emitted non-object arguments for tool {call.name!r}",
131
+ )
132
+ calls.append(
133
+ HarnessToolCall(
134
+ call_id=call.id,
135
+ name=call.name,
136
+ arguments=arguments,
137
+ )
138
+ )
139
+ return HarnessReasoningTurn(
140
+ final_text=response.content or None,
141
+ tool_calls=tuple(calls),
142
+ )
143
+
144
+
145
+ class StudioPluginRuntime:
146
+ """Resolve, activate, and retain composed providers for Studio sessions."""
147
+
148
+ def __init__(
149
+ self,
150
+ workspace: Workspace,
151
+ *,
152
+ build_repository: BuildRepository,
153
+ session_service: BaseSessionService,
154
+ model_client: OpenAICompatibleModelClient,
155
+ secret_resolver: Any,
156
+ harness_reasoner: HarnessReasoner | None = None,
157
+ provider_manifests: Mapping[str, PluginManifest] | None = None,
158
+ provider_factories: Mapping[str, Any] | None = None,
159
+ legacy_harness_sources: Sequence[LegacyHarnessSource] = (),
160
+ ) -> None:
161
+ self.workspace = workspace
162
+ self.builds = build_repository
163
+ self._session_service = session_service
164
+ self._model_client = model_client
165
+ self._secret_resolver = secret_resolver
166
+ self._harness_reasoner = harness_reasoner
167
+ self._provider_manifests = dict(provider_manifests or {})
168
+ self._provider_factories = dict(provider_factories or {})
169
+ self._legacy_bundles = LegacyBundleAdapter(legacy_harness_sources)
170
+ self._lock = asyncio.Lock()
171
+ self._hosts: dict[str, _HostEntry] = {}
172
+
173
+ def replace_provider_registrations(
174
+ self,
175
+ provider_manifests: Mapping[str, PluginManifest],
176
+ provider_factories: Mapping[str, Any],
177
+ ) -> None:
178
+ """Bind one startup snapshot before any provider activation exists."""
179
+
180
+ manifests = dict(provider_manifests)
181
+ factories = dict(provider_factories)
182
+ if manifests.keys() != factories.keys():
183
+ raise ValueError(
184
+ "plugin provider manifests and factories must use the same exact references"
185
+ )
186
+ if self._hosts:
187
+ raise RuntimeError("cannot replace provider registrations after activation")
188
+ self._provider_manifests = manifests
189
+ self._provider_factories = factories
190
+
191
+ @property
192
+ def active_activation_count(self) -> int:
193
+ return sum(entry.host.activation_count for entry in self._hosts.values())
194
+
195
+ def resolve(self, build_id: str, *, model: str | None = None) -> StudioRunSpec:
196
+ build = self.builds.get(build_id)
197
+ runtime_type = build.runtime_type.strip().lower()
198
+ if runtime_type not in _COMPOSED_RUNTIME_TYPES:
199
+ raise StudioError(
200
+ "BUILD_RUNTIME_UNSUPPORTED",
201
+ "Build 不是 PluginHost Harness/Provider Runtime",
202
+ status_code=422,
203
+ details={"buildId": build_id, "runtimeType": runtime_type},
204
+ )
205
+ bundle_root = self._bundle_root(build)
206
+ bundle = self._resolve_bundle(bundle_root)
207
+ self._preflight_bundle(bundle)
208
+ selected_model = self._select_model(build, model)
209
+ resolved = bundle.resolved_agent_spec
210
+ instructions = resolved.get("instructions")
211
+ instructions = instructions if isinstance(instructions, Mapping) else {}
212
+ return StudioRunSpec(
213
+ launch_context=RuntimeLaunchContext(
214
+ runtime_type=runtime_type,
215
+ project_dir=bundle_root,
216
+ config={"plugin_bundle_digest": bundle.bundle_digest},
217
+ ),
218
+ build_id=build.id,
219
+ agent_id=build.agent_id,
220
+ model=selected_model,
221
+ request_config={
222
+ "agent_system": str(instructions.get("system") or ""),
223
+ "agent_task": str(instructions.get("task") or ""),
224
+ "plugin_bundle_digest": bundle.bundle_digest,
225
+ },
226
+ manifest_sha256=build.resolved_digest,
227
+ plugin_bundle_root=bundle_root,
228
+ )
229
+
230
+ async def execute(
231
+ self,
232
+ spec: StudioRunSpec,
233
+ request: Mapping[str, Any],
234
+ *,
235
+ session_id: str,
236
+ ) -> StudioPluginTurnResult:
237
+ if spec.plugin_bundle_root is None:
238
+ raise PluginHostError(
239
+ "plugin_bundle_unavailable", "Studio run has no PluginHost Bundle"
240
+ )
241
+ entry = await self._host_for(spec.plugin_bundle_root)
242
+ if entry.agent_id != spec.agent_id:
243
+ raise PluginHostError(
244
+ "plugin_bundle_agent_mismatch",
245
+ "Studio run Agent does not match its immutable PluginHost Bundle",
246
+ )
247
+ activation = await entry.host.open_activation(
248
+ entry.bundle,
249
+ activation_key=session_id,
250
+ )
251
+ raw = await activation.execute(dict(request))
252
+ return _normalize_result(raw, session_id=session_id)
253
+
254
+ def kernel_adapter_provider(self, spec: StudioRunSpec): # type: ignore[no-untyped-def]
255
+ """Return a lazy, Build-pinned adapter factory for Scheduler Kernel."""
256
+
257
+ if spec.plugin_bundle_root is None:
258
+ raise StudioError(
259
+ "PLUGIN_RUNTIME_UNAVAILABLE",
260
+ "Studio Build 没有 PluginHost Bundle",
261
+ status_code=409,
262
+ )
263
+ return lambda: StudioPluginKernelAdapter(self, spec)
264
+
265
+ async def kernel_adapter(
266
+ self,
267
+ spec: StudioRunSpec,
268
+ *,
269
+ session_id: str,
270
+ ) -> Any:
271
+ """Bind a Scheduler run to the provider-owned activation adapter."""
272
+
273
+ if spec.plugin_bundle_root is None:
274
+ raise StudioError(
275
+ "PLUGIN_RUNTIME_UNAVAILABLE",
276
+ "Studio Build 没有 PluginHost Bundle",
277
+ status_code=409,
278
+ )
279
+ entry = await self._host_for(spec.plugin_bundle_root)
280
+ if entry.agent_id != spec.agent_id:
281
+ raise PluginHostError(
282
+ "plugin_bundle_agent_mismatch",
283
+ "Studio run Agent does not match its immutable PluginHost Bundle",
284
+ )
285
+ activation = await entry.host.open_activation(
286
+ entry.bundle,
287
+ activation_key=session_id,
288
+ )
289
+ return await activation.runtime_adapter()
290
+
291
+ async def close_session(self, session_id: str) -> None:
292
+ async with self._lock:
293
+ entries = tuple(self._hosts.values())
294
+ for entry in entries:
295
+ await entry.host.close_activation(session_id)
296
+
297
+ async def aclose(self) -> None:
298
+ async with self._lock:
299
+ entries = tuple(self._hosts.values())
300
+ self._hosts.clear()
301
+ for entry in entries:
302
+ await entry.host.dispose()
303
+
304
+ async def _host_for(self, bundle_root: Path) -> _HostEntry:
305
+ # Re-resolve every turn. This deliberately rechecks enabled receipts and
306
+ # package digests rather than trusting a previously healthy child.
307
+ bundle = self._resolve_bundle(bundle_root)
308
+ key = bundle.bundle_digest
309
+ async with self._lock:
310
+ existing = self._hosts.get(key)
311
+ if existing is not None:
312
+ return existing
313
+
314
+ registry, factories, permissions = self._runtime_components(bundle)
315
+ verified = (
316
+ bundle
317
+ if bundle.manifest.bundle_format == "agentkit.bundle/v1"
318
+ else PluginBundleResolver(registry).resolve(bundle_root)
319
+ )
320
+ services: dict[str, Any] = {
321
+ "session_service": self._session_service,
322
+ # Providers resolve credential *references* at activation time.
323
+ # The DSH discovery host never receives this service.
324
+ "credential_resolver": self._secret_resolver,
325
+ }
326
+ provider_id, _provider_version = _parse_plugin_ref(
327
+ verified.composition.profile.agent_provider.ref
328
+ )
329
+ if provider_id == KSADK_HARNESS_AGENT_PROVIDER_PLUGIN_ID:
330
+ services["harness_reasoner"] = (
331
+ self._harness_reasoner or self._bound_harness_reasoner(verified)
332
+ )
333
+ host = PluginHost(
334
+ registry,
335
+ factories,
336
+ allowed_permissions=permissions,
337
+ services=services,
338
+ )
339
+ try:
340
+ await host.apply(verified.composition.profile)
341
+ except BaseException:
342
+ await host.dispose()
343
+ raise
344
+ candidate = _HostEntry(
345
+ agent_id=verified.manifest.agent_id,
346
+ bundle=verified,
347
+ host=host,
348
+ )
349
+ stale = [
350
+ (digest, entry)
351
+ for digest, entry in self._hosts.items()
352
+ if entry.agent_id == candidate.agent_id and digest != key
353
+ ]
354
+ self._hosts[key] = candidate
355
+ for digest, entry in stale:
356
+ self._hosts.pop(digest, None)
357
+ await entry.host.dispose()
358
+ return candidate
359
+
360
+ def _resolve_bundle(self, bundle_root: Path) -> ResolvedPluginBundle:
361
+ registered_ids = {
362
+ manifest.metadata.id for manifest in self._provider_manifests.values()
363
+ }
364
+ try:
365
+ manifest, selection = self._legacy_bundles.select_from_bundle(
366
+ bundle_root,
367
+ registered_provider_ids=registered_ids,
368
+ )
369
+ except LegacyBundleCompatibilityError as error:
370
+ code = (
371
+ "AGENT_PROVIDER_NOT_REGISTERED"
372
+ if error.code == "agent_provider_not_registered"
373
+ else "PLUGIN_BUNDLE_INVALID"
374
+ )
375
+ raise StudioError(
376
+ code,
377
+ "Harness Bundle 兼容性校验失败",
378
+ status_code=409,
379
+ details={"reason": error.code},
380
+ ) from error
381
+ if selection is not None and selection.route == "legacy":
382
+ assert selection.manifest is not None
383
+ profile = CompositionProfile.model_validate(
384
+ {
385
+ "agentProvider": {
386
+ "ref": (
387
+ f"plugin://{selection.manifest.metadata.id}"
388
+ f"@{selection.manifest.metadata.version}"
389
+ )
390
+ }
391
+ }
392
+ )
393
+ registry = PluginRegistry(
394
+ [selection.manifest, *builtin_capability_manifests()]
395
+ )
396
+ try:
397
+ resolved = json.loads(
398
+ (bundle_root / "resolved-agent-spec.json").read_text(encoding="utf-8")
399
+ )
400
+ except (OSError, UnicodeError, json.JSONDecodeError) as error:
401
+ raise StudioError(
402
+ "PLUGIN_BUNDLE_INVALID",
403
+ "Legacy Harness Bundle 缺少 resolved Agent spec",
404
+ status_code=409,
405
+ details={"reason": "legacy_resolved_spec_invalid"},
406
+ ) from error
407
+ if not isinstance(resolved, dict):
408
+ raise StudioError(
409
+ "PLUGIN_BUNDLE_INVALID",
410
+ "Legacy Harness resolved Agent spec 必须是对象",
411
+ status_code=409,
412
+ details={"reason": "legacy_resolved_spec_invalid"},
413
+ )
414
+ return ResolvedPluginBundle(
415
+ root=bundle_root,
416
+ manifest=manifest,
417
+ resolved_agent_spec=resolved,
418
+ composition=registry.resolve(profile),
419
+ )
420
+ profile = self._read_profile(bundle_root)
421
+ manifests = [
422
+ *builtin_agent_provider_manifests(),
423
+ *builtin_capability_manifests(),
424
+ ]
425
+ external = self._external_manifest(profile)
426
+ if external is not None:
427
+ manifests.append(external)
428
+ try:
429
+ return PluginBundleResolver(PluginRegistry(manifests)).resolve(bundle_root)
430
+ except PluginBundleError as error:
431
+ raise StudioError(
432
+ "PLUGIN_BUNDLE_INVALID",
433
+ "PluginHost Bundle 校验失败",
434
+ status_code=409,
435
+ details={"reason": error.code},
436
+ ) from error
437
+
438
+ def _runtime_components(
439
+ self,
440
+ bundle: ResolvedPluginBundle,
441
+ ) -> tuple[PluginRegistry, dict[str, Any], frozenset[str]]:
442
+ manifests: list[PluginManifest] = [
443
+ *builtin_agent_provider_manifests(),
444
+ *builtin_capability_manifests(),
445
+ ]
446
+ factories = builtin_capability_factories(
447
+ state_root=self.workspace.resolve(".agentkit/plugin-runtime/state"),
448
+ secret_resolver=self._secret_resolver.resolve,
449
+ )
450
+ provider_id, provider_version = _parse_plugin_ref(
451
+ bundle.composition.profile.agent_provider.ref
452
+ )
453
+ provider_ref = f"plugin://{provider_id}@{provider_version}"
454
+ if bundle.manifest.bundle_format == "agentkit.bundle/v1":
455
+ manifest = legacy_harness_agent_provider_manifest()
456
+ factory = KsADKHarnessProviderFactory(session_service=self._session_service)
457
+ else:
458
+ manifest = self._provider_manifests.get(provider_ref)
459
+ factory = self._provider_factories.get(provider_ref)
460
+ if manifest is None or factory is None:
461
+ raise StudioError(
462
+ "AGENT_PROVIDER_NOT_REGISTERED",
463
+ "AgentProvider 尚未由当前 DSH Profile 完成预检与注册",
464
+ status_code=409,
465
+ details={"provider": provider_ref},
466
+ )
467
+ manifests.append(manifest)
468
+ factories[provider_id] = factory
469
+
470
+ builtin_ids = {
471
+ manifest.metadata.id
472
+ for manifest in (
473
+ *builtin_agent_provider_manifests(),
474
+ *builtin_capability_manifests(),
475
+ )
476
+ }
477
+ allowed = {
478
+ permission
479
+ for manifest in manifests
480
+ if manifest.metadata.id in builtin_ids
481
+ for permission in manifest.spec.permissions
482
+ }
483
+ security = bundle.resolved_agent_spec.get("security")
484
+ if isinstance(security, Mapping):
485
+ raw = security.get("allowedPermissions") or security.get("allowed_permissions") or []
486
+ if isinstance(raw, Sequence) and not isinstance(raw, (str, bytes)):
487
+ allowed.update(str(item) for item in raw)
488
+ return PluginRegistry(manifests), factories, frozenset(allowed)
489
+
490
+ def _preflight_bundle(self, bundle: ResolvedPluginBundle) -> None:
491
+ """Reject an unusable composition before a Run or Schedule is admitted.
492
+
493
+ Provider activation is intentionally lazy, but permission and graph
494
+ admission are deterministic from the immutable Bundle. Delaying this
495
+ check until ``RuntimeAdapter.start`` lets an accepted Inbox message
496
+ repeatedly create PENDING Runs that can never become runnable.
497
+ """
498
+
499
+ registry, factories, permissions = self._runtime_components(bundle)
500
+ host = PluginHost(
501
+ registry,
502
+ factories,
503
+ allowed_permissions=permissions,
504
+ )
505
+ try:
506
+ host.preflight(bundle.composition.profile)
507
+ except PluginHostError as error:
508
+ code = (
509
+ "PLUGIN_PERMISSION_DENIED"
510
+ if error.code == "plugin_permission_denied"
511
+ else "PLUGIN_RUNTIME_PREFLIGHT_FAILED"
512
+ )
513
+ raise StudioError(
514
+ code,
515
+ "Agent 插件组合未通过运行前检查",
516
+ status_code=409,
517
+ details={"reason": error.code},
518
+ ) from error
519
+
520
+ def _bound_harness_reasoner(self, bundle: ResolvedPluginBundle) -> HarnessReasoner:
521
+ spec = bundle.resolved_agent_spec
522
+ model = ResolvedModel.model_validate(spec.get("model"))
523
+ security = spec.get("security")
524
+ security = security if isinstance(security, Mapping) else {}
525
+ network = NetworkPolicy.model_validate(security.get("network") or {})
526
+ execution = spec.get("execution")
527
+ execution = execution if isinstance(execution, Mapping) else {}
528
+ retry = execution.get("retry")
529
+ retry = retry if isinstance(retry, Mapping) else {}
530
+ return _StudioHarnessReasoner(
531
+ self._model_client,
532
+ model=model,
533
+ network_policy=network,
534
+ timeout_seconds=int(
535
+ execution.get("timeoutSeconds") or execution.get("timeout_seconds") or 120
536
+ ),
537
+ max_attempts=int(
538
+ retry.get("maxAttempts") or retry.get("max_attempts") or 2
539
+ ),
540
+ backoff_seconds=float(
541
+ retry.get("backoffSeconds") or retry.get("backoff_seconds") or 1
542
+ ),
543
+ )
544
+
545
+ def _external_manifest(self, profile: CompositionProfile) -> PluginManifest | None:
546
+ plugin_id, version = _parse_plugin_ref(profile.agent_provider.ref)
547
+ builtin_ids = {
548
+ manifest.metadata.id for manifest in builtin_agent_provider_manifests()
549
+ }
550
+ if plugin_id in builtin_ids:
551
+ return None
552
+ provider_ref = f"plugin://{plugin_id}@{version}"
553
+ manifest = self._provider_manifests.get(provider_ref)
554
+ if manifest is None:
555
+ raise StudioError(
556
+ "AGENT_PROVIDER_NOT_REGISTERED",
557
+ "AgentProvider 尚未由当前 DSH Profile 完成预检与注册",
558
+ status_code=409,
559
+ details={"provider": provider_ref},
560
+ )
561
+ return manifest
562
+
563
+ def _read_profile(self, bundle_root: Path) -> CompositionProfile:
564
+ try:
565
+ return cast(
566
+ CompositionProfile,
567
+ CompositionProfile.model_validate_json(
568
+ (bundle_root / "composition-profile.json").read_text(
569
+ encoding="utf-8"
570
+ )
571
+ ),
572
+ )
573
+ except (OSError, UnicodeError, ValueError) as error:
574
+ raise StudioError(
575
+ "PLUGIN_BUNDLE_INVALID",
576
+ "PluginHost Bundle 缺少有效的 Composition Profile",
577
+ status_code=409,
578
+ ) from error
579
+
580
+ def _bundle_root(self, build: BuildRecord) -> Path:
581
+ if not build.artifact_path:
582
+ raise StudioError("BUILD_NOT_READY", "Build 尚未生成制品", status_code=409)
583
+ archive = cast(
584
+ Path,
585
+ self.workspace.resolve(build.artifact_path, must_exist=True),
586
+ )
587
+ bundle_root = archive.parent / "agent-bundle"
588
+ if not bundle_root.is_dir():
589
+ raise StudioError(
590
+ "PLUGIN_BUNDLE_UNAVAILABLE",
591
+ "Build 缺少可运行的 PluginHost Bundle",
592
+ status_code=409,
593
+ )
594
+ return bundle_root
595
+
596
+ @staticmethod
597
+ def _select_model(build: BuildRecord, requested: str | None) -> str:
598
+ allowed = [
599
+ str(item) for item in build.runtime_lock.get("models") or [] if str(item)
600
+ ]
601
+ default = str(build.runtime_lock.get("model") or "").strip()
602
+ if default and default not in allowed:
603
+ allowed.insert(0, default)
604
+ selected = str(requested or default).strip()
605
+ if not selected:
606
+ raise StudioError(
607
+ "AGENT_MODEL_REQUIRED", "Build 没有绑定可运行模型", status_code=422
608
+ )
609
+ if allowed and selected not in allowed:
610
+ raise StudioError(
611
+ "MODEL_NOT_BOUND",
612
+ "请求模型未绑定到当前 Agent Build",
613
+ status_code=422,
614
+ details={"model": selected, "allowedModels": allowed},
615
+ )
616
+ return selected
617
+
618
+ def _parse_plugin_ref(value: str) -> tuple[str, str]:
619
+ plugin_id, version = value.removeprefix("plugin://").rsplit("@", 1)
620
+ return plugin_id, version
621
+
622
+
623
+ def _normalize_result(raw: Any, *, session_id: str) -> StudioPluginTurnResult:
624
+ if isinstance(raw, HarnessTurnResult):
625
+ return StudioPluginTurnResult(
626
+ output_text=raw.output_text,
627
+ session_id=raw.session_id,
628
+ usage=dict(raw.usage),
629
+ metadata=dict(raw.metadata),
630
+ raw=raw,
631
+ )
632
+ if not isinstance(raw, Mapping):
633
+ raise PluginHostError(
634
+ "provider_result_invalid", "AgentProvider result must be an object"
635
+ )
636
+ output_text = str(raw.get("outputText") or raw.get("output_text") or raw.get("output") or "")
637
+ if not output_text:
638
+ raise PluginHostError(
639
+ "provider_result_invalid", "AgentProvider result must contain outputText"
640
+ )
641
+ usage = raw.get("usage") if isinstance(raw.get("usage"), Mapping) else {}
642
+ metadata = raw.get("metadata") if isinstance(raw.get("metadata"), Mapping) else {}
643
+ return StudioPluginTurnResult(
644
+ output_text=output_text,
645
+ session_id=str(raw.get("sessionId") or raw.get("session_id") or session_id),
646
+ usage=dict(usage),
647
+ metadata=dict(metadata),
648
+ raw=raw,
649
+ )
650
+
651
+
652
+ __all__ = ["StudioPluginRuntime", "StudioPluginTurnResult"]
@@ -338,7 +338,12 @@ class LocalResourceCatalog:
338
338
  if not catalog:
339
339
  if cached is not None:
340
340
  return cached[1], cached[2]
341
- catalog = [normalize_model_metadata({"id": current_model or "glm-5.1"})]
341
+ # No provider response and no cache: return an empty catalog so the
342
+ # UI can guide the user to configure a provider instead of showing
343
+ # a phantom default model.
344
+ self._provider_models = {}
345
+ self._provider_catalog_cache[cache_key] = (now, [], source)
346
+ return [], source
342
347
 
343
348
  descriptors: list[ResourceDescriptor] = []
344
349
  for item in catalog: