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
ksadk/studio/service.py CHANGED
@@ -4,8 +4,11 @@ from __future__ import annotations
4
4
 
5
5
  import asyncio
6
6
  import hashlib
7
+ import json
7
8
  import logging
8
9
  import os
10
+ import zipfile
11
+ from collections.abc import Mapping, Sequence
9
12
  from dataclasses import dataclass
10
13
  from pathlib import Path
11
14
  from typing import Any, Callable, Literal, cast
@@ -13,6 +16,7 @@ from urllib.parse import urlparse
13
16
  from uuid import uuid4
14
17
 
15
18
  from ksadk.api import AgentEngineClient
19
+ from ksadk.conversations.contracts import ConversationCapability, ConversationSurface
16
20
  from ksadk.evaluation import (
17
21
  EvaluationConfig as PublicEvaluationConfig,
18
22
  )
@@ -39,10 +43,22 @@ from ksadk.evaluation.studio_build_adapter import (
39
43
  from ksadk.events.store import RuntimeEventStore
40
44
  from ksadk.observability.session_log import SessionLogError, export_session_log
41
45
  from ksadk.observability.trajectory import encode_sse, project_trajectory_event
46
+ from ksadk.plugins.contracts import PluginManifest
47
+ from ksadk.plugins.providers.legacy import LegacyHarnessSource
48
+ from ksadk.plugins.providers.legacy_catalog import (
49
+ KSADK_HARNESS_AGENT_PROVIDER_PLUGIN_ID,
50
+ )
42
51
  from ksadk.runtime import RuntimeExecutor, build_default_runtime_registry
52
+ from ksadk.scheduler.contracts import (
53
+ ScheduleCommandTemplate,
54
+ ScheduledTask,
55
+ ScheduledTaskTarget,
56
+ ScheduleSpec,
57
+ )
43
58
  from ksadk.sessions.local_service import LocalSessionService
44
59
  from ksadk.studio.agent_avatar_assets import AgentAvatarAssetStore
45
60
  from ksadk.studio.agent_lifecycle import delete_framework_agent
61
+ from ksadk.studio.attachment_store import ConversationAttachmentStore
46
62
  from ksadk.studio.authoring_coordinator import StudioAuthoringCoordinator
47
63
  from ksadk.studio.builder import AgentBundleBuilder
48
64
  from ksadk.studio.capabilities import builtin_tool_contracts
@@ -84,6 +100,10 @@ from ksadk.studio.contracts import (
84
100
  RuntimeRef,
85
101
  ToolContract,
86
102
  )
103
+ from ksadk.studio.dsh_provider_registration import (
104
+ StudioDshProviderRegistrationError,
105
+ StudioDshProviderRegistrationManager,
106
+ )
87
107
  from ksadk.studio.errors import StudioError
88
108
  from ksadk.studio.event_store import RunEventStore
89
109
  from ksadk.studio.framework_run import FrameworkRunSpecResolver
@@ -91,11 +111,19 @@ from ksadk.studio.mcp_runtime import MCPRuntimeAdapter
91
111
  from ksadk.studio.model_client import CredentialResolver, OpenAICompatibleModelClient
92
112
  from ksadk.studio.model_profile_service import test_model_profile_connection
93
113
  from ksadk.studio.operations import OperationManager
114
+ from ksadk.studio.plugin_composition import StudioPluginCompositionCompiler
115
+ from ksadk.studio.plugin_runtime import StudioPluginRuntime
94
116
  from ksadk.studio.repository import AgentDraftRepository, BuildRepository, load_yaml_file
95
117
  from ksadk.studio.resource_catalog import LocalResourceCatalog
96
- from ksadk.studio.run_service import StudioRunService
118
+ from ksadk.studio.run_service import StudioRunService, StudioRunSpec
97
119
  from ksadk.studio.runtime_catalog import inspect_runtime_catalog
98
120
  from ksadk.studio.runtime_source import materialize_generated_runtime_source
121
+ from ksadk.studio.scheduler_runtime import (
122
+ StudioScheduledKernelRegistry,
123
+ StudioSchedulerRuntimeError,
124
+ )
125
+ from ksadk.studio.scheduler_service import StudioSchedulerService
126
+ from ksadk.studio.soul import soul_digest
99
127
  from ksadk.studio.templates import (
100
128
  compose_blank_agent,
101
129
  compose_research_agent,
@@ -129,11 +157,34 @@ class StudioService:
129
157
  cloud_gateway: CloudDeploymentGateway | None = None,
130
158
  codex_runtime_inspector: RuntimeInspector | None = None,
131
159
  runtime_executor: RuntimeExecutor | None = None,
160
+ harness_reasoner: Any | None = None,
161
+ plugin_provider_manifests: Mapping[str, PluginManifest] | None = None,
162
+ plugin_provider_factories: Mapping[str, Any] | None = None,
163
+ legacy_harness_sources: Sequence[LegacyHarnessSource] = (),
164
+ dsh_provider_registration_manager: StudioDshProviderRegistrationManager | None = None,
132
165
  ) -> None:
166
+ provider_manifests = dict(plugin_provider_manifests or {})
167
+ provider_factories = dict(plugin_provider_factories or {})
168
+ if provider_manifests.keys() != provider_factories.keys():
169
+ raise ValueError(
170
+ "plugin provider manifests and factories must use the same exact references"
171
+ )
133
172
  self.workspace = Workspace(root)
134
173
  self.workspace.initialize()
174
+ self._startup_provider_manifests = provider_manifests
175
+ self._startup_provider_factories = provider_factories
176
+ self._active_provider_manifests = dict(provider_manifests)
177
+ self._dsh_provider_registration_manager = (
178
+ dsh_provider_registration_manager
179
+ or StudioDshProviderRegistrationManager.discover_or_create_workspace_default(
180
+ self.workspace.root
181
+ )
182
+ )
183
+ self._start_lock = asyncio.Lock()
184
+ self._started = False
135
185
  self._apply_persisted_settings()
136
186
  self.avatar_assets = AgentAvatarAssetStore(self.workspace)
187
+ self.conversation_attachments = ConversationAttachmentStore(self.workspace)
137
188
  self.drafts = AgentDraftRepository(self.workspace)
138
189
  self.catalog = LocalResourceCatalog(self.workspace)
139
190
  self.builds = BuildRepository(self.workspace)
@@ -147,6 +198,11 @@ class StudioService:
147
198
  ),
148
199
  repository=self.builds,
149
200
  )
201
+ self.plugin_compositions = StudioPluginCompositionCompiler(
202
+ self.workspace,
203
+ self.catalog,
204
+ provider_manifests=provider_manifests,
205
+ )
150
206
  self.event_store = RunEventStore(self.workspace)
151
207
  self.session_service = LocalSessionService(project_dir=str(self.workspace.root))
152
208
  self.runtime_events = RuntimeEventStore(self.session_service)
@@ -194,6 +250,28 @@ class StudioService:
194
250
  credential_resolver=self.credentials
195
251
  )
196
252
  self.model_client = runtime_model_client
253
+ self.plugin_runs = StudioPluginRuntime(
254
+ self.workspace,
255
+ build_repository=self.builds,
256
+ session_service=self.session_service,
257
+ model_client=self.model_client,
258
+ secret_resolver=self.credentials,
259
+ harness_reasoner=harness_reasoner,
260
+ provider_manifests=provider_manifests,
261
+ provider_factories=provider_factories,
262
+ legacy_harness_sources=legacy_harness_sources,
263
+ )
264
+ self.run_service.plugin_runtime = self.plugin_runs
265
+ self.scheduler_runtimes = StudioScheduledKernelRegistry(
266
+ resolve_build=self.resolve_run_spec,
267
+ resolve_adapter_provider=self._scheduler_adapter_provider,
268
+ session_service=self.session_service,
269
+ runtime_executor=self.runtime_executor,
270
+ )
271
+ self.scheduler = StudioSchedulerService(
272
+ self.workspace,
273
+ runtime_registry=self.scheduler_runtimes,
274
+ )
197
275
  self.mcp_runtime = MCPRuntimeAdapter(self.workspace, credentials=self.credentials)
198
276
  self._cloud_gateway_override = cloud_gateway
199
277
  self.cloud = CloudDeploymentService(
@@ -202,15 +280,535 @@ class StudioService:
202
280
  build_repository=self.builds,
203
281
  )
204
282
  self.operations = OperationManager(self.workspace)
205
- self.evaluation_storage = EvaluationStorage(
206
- self.workspace.resolve(".agentkit/evaluations")
207
- )
283
+ self.evaluation_storage = EvaluationStorage(self.workspace.resolve(".agentkit/evaluations"))
208
284
  self.authoring = StudioAuthoringCoordinator(self)
209
285
  self.codex_agents = CodexAgentService(self)
210
286
 
287
+ async def start(self) -> None:
288
+ """Bind ready managed DSH registrations before build or execution."""
289
+
290
+ async with self._start_lock:
291
+ if self._started:
292
+ return
293
+ await self._bootstrap_official_dsh_defaults()
294
+ manifests, factories, manager_refs = await self._provider_snapshot(refresh=False)
295
+ self.plugin_compositions.replace_provider_registrations(manifests)
296
+ self.plugin_runs.replace_provider_registrations(manifests, factories)
297
+ self._active_provider_manifests = manifests
298
+ manager = self._dsh_provider_registration_manager
299
+ if manager is not None and manager_refs is not None:
300
+ manager.mark_bound(manager_refs)
301
+ self._started = True
302
+
303
+ async def refresh_dsh_provider_registrations(self) -> None:
304
+ """Rebind the exact current DSH Profile and release stale activations."""
305
+
306
+ async with self._start_lock:
307
+ if self._dsh_provider_registration_manager is None:
308
+ self._dsh_provider_registration_manager = (
309
+ StudioDshProviderRegistrationManager.discover_or_create_workspace_default(
310
+ self.workspace.root
311
+ )
312
+ )
313
+ await self._bootstrap_official_dsh_defaults()
314
+ if self._started:
315
+ await self.plugin_runs.aclose()
316
+ manifests, factories, manager_refs = await self._provider_snapshot(refresh=True)
317
+ self.plugin_compositions.replace_provider_registrations(manifests)
318
+ self.plugin_runs.replace_provider_registrations(manifests, factories)
319
+ self._active_provider_manifests = manifests
320
+ manager = self._dsh_provider_registration_manager
321
+ if manager is not None and manager_refs is not None:
322
+ manager.mark_bound(manager_refs)
323
+ self._started = True
324
+
325
+ async def _bootstrap_official_dsh_defaults(self) -> None:
326
+ manager = self._dsh_provider_registration_manager
327
+ if manager is None:
328
+ return
329
+ try:
330
+ result = await manager.bootstrap_official_codex_provider()
331
+ except Exception as error: # optional DSH must fail closed to legacy paths
332
+ logging.getLogger(__name__).warning(
333
+ "official DSH provider bootstrap skipped: %s", error
334
+ )
335
+ return
336
+ if result in {"installed", "already_enabled"}:
337
+ logging.getLogger(__name__).info("official Codex DSH provider bootstrap: %s", result)
338
+
339
+ async def _provider_snapshot(
340
+ self, *, refresh: bool
341
+ ) -> tuple[dict[str, PluginManifest], dict[str, Any], tuple[str, ...] | None]:
342
+ manifests = dict(self._startup_provider_manifests)
343
+ factories = dict(self._startup_provider_factories)
344
+ manager = self._dsh_provider_registration_manager
345
+ if manager is None:
346
+ return manifests, factories, None
347
+ try:
348
+ registrations = await (manager.refresh() if refresh else manager.start())
349
+ except StudioDshProviderRegistrationError as error:
350
+ logging.getLogger(__name__).warning(
351
+ "DSH AgentProvider discovery failed closed: %s", error.code
352
+ )
353
+ return manifests, factories, None
354
+ if registrations.manifests.keys() != registrations.factories.keys():
355
+ logging.getLogger(__name__).warning(
356
+ "DSH AgentProvider discovery returned a partial registration set"
357
+ )
358
+ return manifests, factories, None
359
+ for provider_ref, manifest in registrations.manifests.items():
360
+ if provider_ref in manifests and (
361
+ manifests[provider_ref] != manifest
362
+ or factories[provider_ref] is not registrations.factories[provider_ref]
363
+ ):
364
+ logging.getLogger(__name__).warning(
365
+ "DSH AgentProvider registration conflicts with startup provider: %s",
366
+ provider_ref,
367
+ )
368
+ return (
369
+ dict(self._startup_provider_manifests),
370
+ dict(self._startup_provider_factories),
371
+ None,
372
+ )
373
+ manifests[provider_ref] = manifest
374
+ factories[provider_ref] = registrations.factories[provider_ref]
375
+ return manifests, factories, tuple(registrations.manifests)
376
+
377
+ def agent_provider_catalog(self) -> list[dict[str, Any]]:
378
+ """Return only external providers that reached Studio's bound selector."""
379
+
380
+ reserved = {
381
+ KSADK_HARNESS_AGENT_PROVIDER_PLUGIN_ID,
382
+ }
383
+ package_by_ref: dict[str, str] = {}
384
+ display_name_by_ref: dict[str, str] = {}
385
+ manager = self._dsh_provider_registration_manager
386
+ if manager is not None:
387
+ package_by_ref = {
388
+ item.provider_ref: item.package_name
389
+ for item in manager.inventory.packages
390
+ if item.state == "bound" and item.provider_ref is not None
391
+ }
392
+ display_name_by_ref = {
393
+ item.provider_ref: item.display_name
394
+ for item in manager.inventory.packages
395
+ if item.state == "bound" and item.provider_ref is not None and item.display_name
396
+ }
397
+ items = []
398
+ for provider_ref, manifest in sorted(self._active_provider_manifests.items()):
399
+ if manifest.metadata.id in reserved:
400
+ continue
401
+ items.append(
402
+ {
403
+ "providerRef": provider_ref,
404
+ "pluginId": package_by_ref.get(provider_ref, manifest.metadata.id),
405
+ "resolvedVersion": manifest.metadata.version,
406
+ "displayName": display_name_by_ref.get(provider_ref, manifest.metadata.id),
407
+ "state": "enabled",
408
+ "compatible": True,
409
+ "selectable": True,
410
+ "reason": None,
411
+ "permissions": list(manifest.spec.permissions),
412
+ "isolation": manifest.spec.isolation,
413
+ "configSchemaDeclared": manifest.spec.config_schema is not None,
414
+ "secretFields": list(manifest.spec.secret_fields),
415
+ }
416
+ )
417
+ return items
418
+
419
+ def dsh_provider_runtime_state(self, package_name: str) -> dict[str, Any] | None:
420
+ """Project one manager-owned package lifecycle state for plugin APIs."""
421
+
422
+ manager = self._dsh_provider_registration_manager
423
+ if manager is None:
424
+ return None
425
+ status = next(
426
+ (item for item in manager.inventory.packages if item.package_name == package_name),
427
+ None,
428
+ )
429
+ if status is None:
430
+ return None
431
+ return {
432
+ "state": status.state,
433
+ "providerRef": status.provider_ref,
434
+ "errorCode": status.error_code,
435
+ }
436
+
211
437
  def runtime_catalog(self) -> list[dict]:
212
438
  return inspect_runtime_catalog(self.runtime_executor)
213
439
 
440
+ def _scheduler_adapter_provider(self, spec: StudioRunSpec): # type: ignore[no-untyped-def]
441
+ if spec.plugin_bundle_root is not None:
442
+ return self.plugin_runs.kernel_adapter_provider(spec)
443
+ return lambda: self.runtime_executor.create_adapter(spec.launch_context)
444
+
445
+ def resolve_run_spec(
446
+ self,
447
+ build_id: str,
448
+ *,
449
+ model: str | None = None,
450
+ sandbox: str | None = None,
451
+ approval_mode: str | None = None,
452
+ ) -> StudioRunSpec:
453
+ """Resolve one immutable Build through its only compatible resolver."""
454
+
455
+ try:
456
+ return self.codex_runs.resolve(
457
+ build_id,
458
+ model=model,
459
+ sandbox=sandbox,
460
+ approval_mode=approval_mode,
461
+ )
462
+ except Exception as exc:
463
+ if getattr(exc, "status_code", None) != 404:
464
+ raise
465
+ framework_build = self.builds.get(build_id)
466
+ runtime_type = framework_build.runtime_type.strip().lower()
467
+ if runtime_type in {"harness", "plugin"}:
468
+ return self.plugin_runs.resolve(build_id, model=model)
469
+ return self.framework_runs.resolve(
470
+ build_id,
471
+ model=model,
472
+ approval_mode=approval_mode,
473
+ )
474
+
475
+ def validate_schedule_build(
476
+ self,
477
+ build_ref: str | None,
478
+ *,
479
+ agent_id: str | None = None,
480
+ ) -> None:
481
+ """Reject a ScheduledTask target build that is not a real immutable Build.
482
+
483
+ The agent-scoped authoring path resolves the Build server-side via
484
+ :meth:`ensure_current_build`, so this guard is primarily for the raw
485
+ ``/api/v1/schedules`` surface that accepts a caller-supplied
486
+ ``agent_version_ref``. A version name or tag (for example "v1") is
487
+ not an immutable Build id and must fail at admission with an actionable
488
+ error instead of collapsing into a later opaque DISPATCH_FAILED.
489
+ """
490
+ ref = (build_ref or "").strip()
491
+ if not ref:
492
+ raise StudioError(
493
+ "SCHEDULE_BUILD_REQUIRED",
494
+ "定时任务必须绑定一个已成功构建的 Build id(不能为空或使用版本名)",
495
+ status_code=422,
496
+ field="target.agentVersionRef",
497
+ )
498
+ try:
499
+ spec = self.resolve_run_spec(ref)
500
+ except StudioError as error:
501
+ if error.status_code == 404:
502
+ raise StudioError(
503
+ "SCHEDULE_BUILD_UNAVAILABLE",
504
+ f"定时任务绑定的 Build {ref!r} 不可用:请使用已成功构建的 Build id,而非版本名",
505
+ status_code=422,
506
+ field="target.agentVersionRef",
507
+ details={"buildId": ref},
508
+ ) from error
509
+ raise
510
+ except Exception as error:
511
+ raise StudioError(
512
+ "SCHEDULE_BUILD_UNAVAILABLE",
513
+ f"定时任务绑定的 Build {ref!r} 不可用:{error}",
514
+ status_code=422,
515
+ field="target.agentVersionRef",
516
+ details={"buildId": ref},
517
+ ) from error
518
+ if agent_id and spec.agent_id != agent_id:
519
+ raise StudioError(
520
+ "SCHEDULE_AGENT_MISMATCH",
521
+ "定时任务绑定的 Build 与所选 Agent 不一致",
522
+ status_code=422,
523
+ field="target.agentVersionRef",
524
+ details={"buildId": ref, "agentId": agent_id},
525
+ )
526
+
527
+ def conversation_surface(
528
+ self,
529
+ build_id: str,
530
+ *,
531
+ session_id: str,
532
+ ) -> ConversationSurface:
533
+ """Describe the actual local composer contract for one Build/session.
534
+
535
+ This is intentionally conservative. It derives the declaration from
536
+ the resolved Build, rather than guessing support from a model name or
537
+ sending provider-specific fields to every Runtime.
538
+ """
539
+
540
+ spec = self.resolve_run_spec(build_id)
541
+ runtime_type = spec.launch_context.runtime_type.strip().lower()
542
+ runtime_mode: Literal["native", "translated"] = (
543
+ "native" if runtime_type == "codex" else "translated"
544
+ )
545
+ inputs = [ConversationCapability(name="text", mode="native")]
546
+ if spec.model:
547
+ inputs.append(ConversationCapability(name="model.select", mode=runtime_mode))
548
+ inputs.append(ConversationCapability(name="approval", mode=runtime_mode))
549
+ if runtime_type == "codex":
550
+ inputs.extend(
551
+ (
552
+ # Studio projects image data URLs to native Codex ImageInput.
553
+ ConversationCapability(name="attachment.image", mode="native"),
554
+ # Bounded UTF-8/code attachments are made model-visible as
555
+ # deterministic text parts by the Responses compatibility
556
+ # adapter. Binary files remain unavailable in Phase 2.
557
+ ConversationCapability(name="attachment.file", mode="translated"),
558
+ ConversationCapability(name="reasoning.effort", mode="native"),
559
+ ConversationCapability(name="goal", mode="native"),
560
+ ConversationCapability(name="plan", mode="native"),
561
+ )
562
+ )
563
+
564
+ outputs = [
565
+ ConversationCapability(name="text", mode="native"),
566
+ ConversationCapability(name="streaming", mode=runtime_mode),
567
+ ]
568
+ if runtime_type == "codex":
569
+ outputs.extend(
570
+ (
571
+ ConversationCapability(name="reasoning", mode="native"),
572
+ ConversationCapability(name="tool.inspect", mode="native"),
573
+ ConversationCapability(name="approval", mode="native"),
574
+ ConversationCapability(name="goal", mode="native"),
575
+ ConversationCapability(name="plan", mode="native"),
576
+ ConversationCapability(name="cancel", mode="native"),
577
+ )
578
+ )
579
+ return ConversationSurface(
580
+ surface_id=f"studio.build.{spec.build_id}",
581
+ session_id=session_id,
582
+ provider_ref=f"studio.runtime.{runtime_type}",
583
+ inputs=tuple(inputs),
584
+ outputs=tuple(outputs),
585
+ )
586
+
587
+ async def ensure_current_build(self, agent_id: str):
588
+ """Return the Build that is eligible for a local Studio turn.
589
+
590
+ This is deliberately one shared resolver for Web chat and Scheduler
591
+ authoring. A Scheduler must never select a mutable Draft or a stale
592
+ Codex manifest merely because a UI happened to show it as an Agent.
593
+ """
594
+
595
+ await self.start()
596
+ if self.is_codex_agent(agent_id):
597
+ self.codex_agent_detail(agent_id)
598
+ builds = [
599
+ record
600
+ for record in self.codex_builds.list()
601
+ if record.agent_name == agent_id and self.codex_builder.is_current(record)
602
+ ]
603
+ if builds:
604
+ return builds[0]
605
+ return await asyncio.to_thread(self.codex_builder.build, agent_id)
606
+ draft = self.drafts.get(agent_id)
607
+ composition_required = self.plugin_compositions.required_for(draft)
608
+ for record in self.builds.list_for_agent(agent_id):
609
+ if record.status == BuildStatus.SUCCEEDED:
610
+ if composition_required and (
611
+ record.source_revision != draft.metadata.revision
612
+ or not self._build_has_composition(record)
613
+ ):
614
+ continue
615
+ if composition_required:
616
+ composition = self.plugin_compositions.compile_if_required(draft)
617
+ if composition is None: # pragma: no cover - guarded by required_for
618
+ raise StudioError(
619
+ "PLUGIN_COMPOSITION_REQUIRED",
620
+ "当前 Agent 需要插件组合,但无法生成 Composition",
621
+ status_code=409,
622
+ )
623
+ self.plugin_compositions.bind_build(
624
+ composition,
625
+ agent_id=draft.metadata.id,
626
+ build_id=record.id,
627
+ )
628
+ return record
629
+ if draft.spec.model is None and not draft.spec.bindings.model_profile_id:
630
+ raise StudioError(
631
+ "AGENT_MODEL_REQUIRED",
632
+ "当前 Agent 未绑定 Model Profile,请先在 Agent 配置中选择模型;"
633
+ "API Key 只提供访问凭证,不会自动绑定模型。",
634
+ status_code=422,
635
+ field="spec.bindings.modelProfileId",
636
+ )
637
+ return await asyncio.to_thread(self._build_agent_bundle, draft)
638
+
639
+ def _build_agent_bundle(self, draft: AgentDraft):
640
+ composition = self.plugin_compositions.compile_if_required(draft)
641
+ record = self.builder.build(draft, composition=composition)
642
+ if composition is not None:
643
+ self.plugin_compositions.bind_build(
644
+ composition,
645
+ agent_id=draft.metadata.id,
646
+ build_id=record.id,
647
+ )
648
+ return record
649
+
650
+ def _build_has_composition(self, record: Any) -> bool:
651
+ """Reject stale pre-Phase-2 builds for a composed runtime.
652
+
653
+ This check is intentionally scoped to Harness/plugin runtimes. Legacy
654
+ ADK/LangGraph build selection retains its existing behavior.
655
+ """
656
+
657
+ if not record.artifact_path:
658
+ return False
659
+ try:
660
+ archive = self.workspace.resolve(record.artifact_path, must_exist=True)
661
+ with zipfile.ZipFile(archive) as bundle:
662
+ manifest = json.loads(bundle.read("manifest.json"))
663
+ names = frozenset(bundle.namelist())
664
+ return bool(
665
+ isinstance(manifest, dict)
666
+ and manifest.get("compositionProfileDigest")
667
+ and "composition-profile.json" in names
668
+ and "plugin-lock.json" in names
669
+ )
670
+ except (KeyError, OSError, UnicodeError, ValueError, zipfile.BadZipFile):
671
+ return False
672
+
673
+ async def create_agent_schedule(
674
+ self,
675
+ agent_id: str,
676
+ *,
677
+ display_name: str,
678
+ prompt: str,
679
+ schedule: ScheduleSpec,
680
+ enabled: bool = True,
681
+ continuity: Literal["new_session", "continue_session"] = "new_session",
682
+ session_id: str | None = None,
683
+ ) -> ScheduledTask:
684
+ """Create a local-only schedule for the Kernel Runtime that owns it.
685
+
686
+ The browser provides only human intent (prompt/calendar/continuity).
687
+ The trusted Studio process resolves the immutable Build and concrete
688
+ Kernel instance. Thus a task cannot be pointed at another Agent
689
+ Instance, smuggle a permit reference, or claim cloud scheduling.
690
+ """
691
+
692
+ await self.start()
693
+ build = await self.ensure_current_build(agent_id)
694
+ spec = self.resolve_run_spec(build.id)
695
+ await self.scheduler_runtimes.start()
696
+ try:
697
+ target = await self.scheduler_runtimes.ensure_build(
698
+ build.id,
699
+ expected_agent_id=agent_id,
700
+ )
701
+ runtime = self.scheduler_runtimes.runtime_for_build(build.id)
702
+ except StudioSchedulerRuntimeError as error:
703
+ raise StudioError(
704
+ error.code,
705
+ str(error),
706
+ status_code=409,
707
+ details={"agentId": agent_id, "buildId": build.id},
708
+ ) from error
709
+ if continuity == "continue_session" and not session_id:
710
+ raise StudioError(
711
+ "SCHEDULE_SESSION_REQUIRED",
712
+ "继续会话的定时任务必须选择一个已有会话。",
713
+ status_code=422,
714
+ field="sessionId",
715
+ )
716
+ if continuity == "continue_session":
717
+ self._require_schedule_continuation(runtime, spec.launch_context.runtime_type)
718
+ task = ScheduledTask(
719
+ task_id=f"sched-{uuid4().hex[:20]}",
720
+ display_name=display_name,
721
+ target=ScheduledTaskTarget(
722
+ agent_id=agent_id,
723
+ tenant_id=target.tenant_id,
724
+ agent_instance_id=target.agent_instance_id,
725
+ agent_version_ref=str(build.id),
726
+ session_id=session_id,
727
+ # This is an opaque local authority marker, never a browser
728
+ # supplied credential. The dispatcher obtains a short-lived
729
+ # in-process permit at the AgentControl ingress boundary.
730
+ authorization_ref="runtime://local-kernel-ingress",
731
+ ),
732
+ schedule=schedule,
733
+ command=ScheduleCommandTemplate(payload={"content": prompt}),
734
+ enabled=enabled,
735
+ continuity=continuity,
736
+ )
737
+ return self.scheduler.create_task(task)
738
+
739
+ @staticmethod
740
+ def _require_schedule_continuation(runtime: Any, runtime_type: str) -> None:
741
+ """Fail closed unless the bound Provider can preserve one conversation.
742
+
743
+ Harness owns process-local Session history without exposing the generic
744
+ checkpoint ``resume`` verb. Codex and external adapters must declare
745
+ the typed resume capability. An older Runtime that lacks either proof
746
+ remains usable for normal conversations and ``new_session`` schedules;
747
+ only the optional follow-up mode is rejected.
748
+ """
749
+
750
+ if runtime_type.strip().lower() == "harness":
751
+ return
752
+ kernel = getattr(runtime, "kernel", None)
753
+ describe = getattr(kernel, "capabilities", None)
754
+ matrix = describe() if callable(describe) else None
755
+ resume = getattr(matrix, "resume", None)
756
+ if not bool(getattr(resume, "supported", False)):
757
+ raise StudioError(
758
+ "SCHEDULE_CONTINUATION_UNAVAILABLE",
759
+ "当前 Runtime 未声明可恢复的会话续接能力;可改用新会话定时任务。",
760
+ status_code=409,
761
+ field="continuity",
762
+ details={"runtimeType": runtime_type},
763
+ )
764
+
765
+ def list_agent_schedules(self, agent_id: str) -> list[ScheduledTask]:
766
+ return [task for task in self.scheduler.list_tasks() if task.target.agent_id == agent_id]
767
+
768
+ def get_agent_schedule(self, agent_id: str, task_id: str) -> ScheduledTask:
769
+ task = self.scheduler.get_task(task_id)
770
+ if task.target.agent_id != agent_id:
771
+ from ksadk.studio.errors import not_found
772
+
773
+ raise not_found("schedule", task_id)
774
+ return task
775
+
776
+ def update_agent_schedule(
777
+ self,
778
+ agent_id: str,
779
+ task_id: str,
780
+ *,
781
+ display_name: str,
782
+ prompt: str,
783
+ schedule: ScheduleSpec,
784
+ enabled: bool,
785
+ continuity: Literal["new_session", "continue_session"],
786
+ session_id: str | None,
787
+ ) -> ScheduledTask:
788
+ existing = self.get_agent_schedule(agent_id, task_id)
789
+ if continuity == "continue_session" and not session_id:
790
+ raise StudioError(
791
+ "SCHEDULE_SESSION_REQUIRED",
792
+ "继续会话的定时任务必须选择一个已有会话。",
793
+ status_code=422,
794
+ field="sessionId",
795
+ )
796
+ task = existing.model_copy(
797
+ update={
798
+ "display_name": display_name,
799
+ "schedule": schedule,
800
+ "command": ScheduleCommandTemplate(payload={"content": prompt}),
801
+ "enabled": enabled,
802
+ "continuity": continuity,
803
+ "target": existing.target.model_copy(update={"session_id": session_id}),
804
+ }
805
+ )
806
+ return self.scheduler.update_task(task_id, task)
807
+
808
+ async def run_agent_schedule_now(self, agent_id: str, task_id: str):
809
+ self.get_agent_schedule(agent_id, task_id)
810
+ return await self.scheduler.run_now(task_id)
811
+
214
812
  def codex_manifest_state(self, agent_id: str | None = None) -> dict:
215
813
  return self.codex_agents.manifest_state(agent_id)
216
814
 
@@ -315,9 +913,17 @@ class StudioService:
315
913
  status_code=409,
316
914
  details={"sessionId": session_id},
317
915
  )
916
+ await self.plugin_runs.close_session(session_id)
318
917
  await self.session_service.delete_session(session_id)
319
918
  self.event_store.delete_session(session_id)
320
919
 
920
+ async def aclose(self) -> None:
921
+ """Release local provider activations and supervised plugin processes."""
922
+
923
+ await self.plugin_runs.aclose()
924
+ if self._dsh_provider_registration_manager is not None:
925
+ await self._dsh_provider_registration_manager.aclose()
926
+
321
927
  async def _require_runtime_session(self, session_id: str) -> None:
322
928
  if await self.session_service.get_session_metadata(session_id) is None:
323
929
  raise StudioError(
@@ -842,12 +1448,36 @@ class StudioService:
842
1448
 
843
1449
  def agent_detail(self, agent_id: str) -> dict:
844
1450
  if self.is_codex_agent(agent_id):
845
- return self.codex_agent_detail(agent_id)
1451
+ detail = self.codex_agent_detail(agent_id)
1452
+ detail["soulProjection"] = self._soul_projection(detail["draft"])
1453
+ return detail
846
1454
  draft = self.drafts.get(agent_id)
847
1455
  return {
848
1456
  "draft": draft,
849
1457
  "builds": self.builds.list_for_agent(agent_id)[:10],
850
1458
  "validation": self.validator.validate(draft),
1459
+ "soulProjection": self._soul_projection(draft),
1460
+ }
1461
+
1462
+ @staticmethod
1463
+ def _soul_projection(draft: AgentDraft) -> dict[str, object]:
1464
+ """Describe the reviewed Soul source without claiming runtime learning."""
1465
+
1466
+ soul = draft.spec.soul
1467
+ runtime_type = draft.spec.runtime.type if draft.spec.runtime is not None else ""
1468
+ compile_target = {
1469
+ "codex": "managed-runtime.base_instructions",
1470
+ "plugin": "instructions/soul.md",
1471
+ }.get(runtime_type, "resolved-agent-spec.instructions.system")
1472
+ return {
1473
+ "present": soul is not None,
1474
+ "source": "AgentSpec.soul",
1475
+ "sourceRevision": draft.metadata.revision,
1476
+ "schemaVersion": soul.schema_version if soul is not None else "agentkit.soul/v1",
1477
+ "digest": soul_digest(soul) if soul is not None else None,
1478
+ "digestAlgorithm": "sha256-canonical-json",
1479
+ "compileTarget": compile_target,
1480
+ "compileOrder": "before-instructions.system",
851
1481
  }
852
1482
 
853
1483
  def create_studio_agent(
@@ -1000,32 +1630,11 @@ class StudioService:
1000
1630
  revision: int,
1001
1631
  idempotency_key: str,
1002
1632
  ) -> Operation:
1003
- runtime_type = self.agent_runtime_type(agent_id)
1004
- runtime = next(
1005
- (item for item in self.runtime_catalog() if item["runtimeType"] == runtime_type),
1006
- None,
1007
- )
1008
- if runtime is None:
1009
- from ksadk.studio.errors import StudioError
1010
-
1011
- raise StudioError(
1012
- "RUNTIME_NOT_REGISTERED",
1013
- "Agent 引用的 RuntimeAdapter 未注册",
1014
- status_code=422,
1015
- details={"runtimeType": runtime_type},
1016
- )
1017
- if runtime["status"] != "ready":
1018
- from ksadk.studio.errors import StudioError
1019
-
1020
- raise StudioError(
1021
- "RUNTIME_DEPENDENCY_MISSING",
1022
- f"{runtime['displayName']} Runtime 依赖未安装",
1023
- status_code=422,
1024
- details={
1025
- "runtimeType": runtime_type,
1026
- "installCommand": runtime["installCommand"],
1027
- },
1028
- )
1633
+ # Codex build admission already runs the injected runtime inspector and
1634
+ # pins its result into the immutable build. Checking package metadata
1635
+ # first would reject an explicitly supplied RuntimeExecutor/inspector
1636
+ # (including an out-of-process Codex runtime) merely because the local
1637
+ # Studio interpreter does not have the optional wheel installed.
1029
1638
  if self.is_codex_agent(agent_id):
1030
1639
  detail = self.agent_detail(agent_id)
1031
1640
  if revision != detail["draft"].metadata.revision:
@@ -1040,6 +1649,12 @@ class StudioService:
1040
1649
  idempotency_key=idempotency_key,
1041
1650
  agent_id=agent_id,
1042
1651
  )
1652
+
1653
+ # A framework Build compiles and seals source/configuration only. Its
1654
+ # optional Runtime dependency belongs to run preflight, and may live in
1655
+ # a different process or deployment image. Rejecting the Build from
1656
+ # this Studio interpreter's package metadata would make portable
1657
+ # bundles impossible and incorrectly couple authoring to execution.
1043
1658
  return self.submit_build(
1044
1659
  agent_id,
1045
1660
  revision=revision,
@@ -1271,8 +1886,7 @@ class StudioService:
1271
1886
  resolved.model_profile_ids = [
1272
1887
  resource_id
1273
1888
  for resource_id in resolved.model_profile_ids
1274
- if resource_id in known
1275
- or resource_id not in current.model_profile_ids
1889
+ if resource_id in known or resource_id not in current.model_profile_ids
1276
1890
  ]
1277
1891
  resolved.skills = selected_for_materialization(
1278
1892
  candidate.skills,
@@ -1356,7 +1970,7 @@ class StudioService:
1356
1970
  snapshot = draft.model_copy(deep=True)
1357
1971
 
1358
1972
  async def runner(_operation_id: str):
1359
- return await asyncio.to_thread(self.builder.build, snapshot)
1973
+ return await asyncio.to_thread(self._build_agent_bundle, snapshot)
1360
1974
 
1361
1975
  return self.operations.submit(
1362
1976
  kind=OperationKind.BUILD,
@@ -1420,21 +2034,13 @@ class StudioService:
1420
2034
  ):
1421
2035
  """Execute any immutable Studio Build through the canonical executor."""
1422
2036
 
1423
- try:
1424
- spec = self.codex_runs.resolve(
1425
- build_id,
1426
- model=model,
1427
- sandbox=sandbox,
1428
- approval_mode=approval_mode,
1429
- )
1430
- except Exception as exc:
1431
- if getattr(exc, "status_code", None) != 404:
1432
- raise
1433
- spec = self.framework_runs.resolve(
1434
- build_id,
1435
- model=model,
1436
- approval_mode=approval_mode,
1437
- )
2037
+ await self.start()
2038
+ spec = self.resolve_run_spec(
2039
+ build_id,
2040
+ model=model,
2041
+ sandbox=sandbox,
2042
+ approval_mode=approval_mode,
2043
+ )
1438
2044
  if collaboration_mode or goal_objective or reasoning_effort:
1439
2045
  from dataclasses import replace
1440
2046
 
@@ -1660,12 +2266,14 @@ class StudioService:
1660
2266
  evalset = load_evalset(path)
1661
2267
  except (EvalSetParseError, OSError):
1662
2268
  continue
1663
- evalsets.append({
1664
- "path": path.relative_to(self.workspace.root).as_posix(),
1665
- "name": evalset.name,
1666
- "caseCount": len(evalset.cases),
1667
- "contentDigest": evalset.content_digest,
1668
- })
2269
+ evalsets.append(
2270
+ {
2271
+ "path": path.relative_to(self.workspace.root).as_posix(),
2272
+ "name": evalset.name,
2273
+ "caseCount": len(evalset.cases),
2274
+ "contentDigest": evalset.content_digest,
2275
+ }
2276
+ )
1669
2277
  evalsets.sort(key=lambda item: item["path"])
1670
2278
  return {"builds": builds, "evalsets": evalsets}
1671
2279
 
@@ -1840,9 +2448,7 @@ class StudioService:
1840
2448
  # this outbound control-plane request only, never in the Build or
1841
2449
  # deployment receipt.
1842
2450
  launch = self.codex_runs.resolve(build_id)
1843
- runtime_environment = dict(
1844
- (launch.launch_context.config or {}).get("env") or {}
1845
- )
2451
+ runtime_environment = dict((launch.launch_context.config or {}).get("env") or {})
1846
2452
  # Retrying a YAML deployment must update the Agent that the prior
1847
2453
  # receipt already created. CreateAgent can have succeeded before
1848
2454
  # a downstream Runtime-Service start failed; creating again would
@@ -2026,14 +2632,10 @@ class StudioService:
2026
2632
 
2027
2633
  workspace_identity = str(self.workspace.root.resolve())
2028
2634
  region = (
2029
- os.environ.get("AGENTENGINE_REGION")
2030
- or os.environ.get("KSYUN_REGION")
2031
- or "cn-beijing-6"
2635
+ os.environ.get("AGENTENGINE_REGION") or os.environ.get("KSYUN_REGION") or "cn-beijing-6"
2032
2636
  ).strip()
2033
2637
  access_key = (
2034
- os.environ.get("KSYUN_ACCESS_KEY")
2035
- or os.environ.get("KS3_ACCESS_KEY")
2036
- or "unsigned"
2638
+ os.environ.get("KSYUN_ACCESS_KEY") or os.environ.get("KS3_ACCESS_KEY") or "unsigned"
2037
2639
  ).strip()
2038
2640
 
2039
2641
  def opaque(value: str) -> str:
@@ -2060,29 +2662,19 @@ class StudioService:
2060
2662
  "AGENTENGINE_REGION", os.environ.get("KSYUN_REGION", "cn-beijing-6")
2061
2663
  ),
2062
2664
  "cloudBucket": os.environ.get("KS3_BUCKET", ""),
2063
- "cloudAccountId": (
2064
- os.environ.get("KSYUN_ACCOUNT_ID", "").strip()
2065
- ),
2665
+ "cloudAccountId": (os.environ.get("KSYUN_ACCOUNT_ID", "").strip()),
2066
2666
  # AK/SK 不回显(避免本地 UI 回传 secret);只暴露配置状态。
2067
2667
  # 已配置 = 启动环境或已持久化 settings 里 AK+SK 齐全。
2068
2668
  "cloudAccountConfigured": bool(
2069
- (
2070
- os.environ.get("KSYUN_ACCESS_KEY")
2071
- or os.environ.get("KS3_ACCESS_KEY", "")
2072
- ).strip()
2669
+ (os.environ.get("KSYUN_ACCESS_KEY") or os.environ.get("KS3_ACCESS_KEY", "")).strip()
2073
2670
  and (
2074
- os.environ.get("KSYUN_SECRET_KEY")
2075
- or os.environ.get("KS3_SECRET_KEY", "")
2671
+ os.environ.get("KSYUN_SECRET_KEY") or os.environ.get("KS3_SECRET_KEY", "")
2076
2672
  ).strip()
2077
2673
  ),
2078
2674
  "cloudSignedAccountConfigured": bool(
2079
- (
2080
- os.environ.get("KSYUN_ACCESS_KEY")
2081
- or os.environ.get("KS3_ACCESS_KEY", "")
2082
- ).strip()
2675
+ (os.environ.get("KSYUN_ACCESS_KEY") or os.environ.get("KS3_ACCESS_KEY", "")).strip()
2083
2676
  and (
2084
- os.environ.get("KSYUN_SECRET_KEY")
2085
- or os.environ.get("KS3_SECRET_KEY", "")
2677
+ os.environ.get("KSYUN_SECRET_KEY") or os.environ.get("KS3_SECRET_KEY", "")
2086
2678
  ).strip()
2087
2679
  ),
2088
2680
  "traceContent": os.environ.get("KSADK_STUDIO_TRACE_CONTENT", "1") != "0",
@@ -2191,9 +2783,7 @@ class StudioService:
2191
2783
  secret_key = (
2192
2784
  os.environ.get("KSYUN_SECRET_KEY") or os.environ.get("KS3_SECRET_KEY", "")
2193
2785
  ).strip()
2194
- region = os.environ.get(
2195
- "AGENTENGINE_REGION", os.environ.get("KSYUN_REGION", "")
2196
- ).strip()
2786
+ region = os.environ.get("AGENTENGINE_REGION", os.environ.get("KSYUN_REGION", "")).strip()
2197
2787
  if not all((access_key, secret_key, region)):
2198
2788
  return UnavailableCloudGateway()
2199
2789
  control_client = AgentEngineClient(