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,722 @@
1
+ """Runtime-native Codex AgentProvider for the controlled PluginHost.
2
+
3
+ The provider only projects an immutable AgentBundle into the existing Codex
4
+ RuntimeAdapter/RuntimeExecutor conversation path. Canonical SessionEvents and
5
+ native thread continuation remain owned by that path; no provider-local event
6
+ stream or transcript is created here.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ from collections.abc import Callable, Mapping, Sequence
13
+ from dataclasses import dataclass
14
+ from pathlib import Path, PurePosixPath
15
+ from typing import Any
16
+
17
+ from ksadk.plugins.bundle import ResolvedPluginBundle
18
+ from ksadk.plugins.contracts import CompositionProfile, PluginManifest
19
+ from ksadk.plugins.host import PluginExecutionContext, PluginHostError
20
+ from ksadk.runtime import (
21
+ RuntimeExecutor,
22
+ RuntimeLaunchContext,
23
+ RuntimeServices,
24
+ build_default_runtime_registry,
25
+ )
26
+ from ksadk.runtime.conversation_execution import invoke_runtime_conversation_once
27
+ from ksadk.sessions import create_session_service
28
+ from ksadk.sessions.base import BaseSessionService
29
+
30
+
31
+ @dataclass(frozen=True)
32
+ class CodexProviderInventory:
33
+ provider: str
34
+ model: str
35
+ mcp_servers: tuple[str, ...]
36
+ skills: tuple[str, ...]
37
+
38
+
39
+ @dataclass(frozen=True)
40
+ class CodexTurnRequest:
41
+ user_id: str
42
+ session_id: str | None
43
+ messages: tuple[Mapping[str, Any], ...]
44
+ request_metadata: Mapping[str, Any] | None = None
45
+ invocation_id: str | None = None
46
+ model: str | None = None
47
+ collaboration_mode: str | None = None
48
+ goal_objective: str | None = None
49
+
50
+ @classmethod
51
+ def parse(cls, value: Any) -> "CodexTurnRequest":
52
+ if isinstance(value, cls):
53
+ return value
54
+ if not isinstance(value, Mapping):
55
+ raise PluginHostError("codex_input_invalid", "Codex input must be an object")
56
+ allowed_fields = {
57
+ "user_id",
58
+ "userId",
59
+ "session_id",
60
+ "sessionId",
61
+ "messages",
62
+ "input",
63
+ "request_metadata",
64
+ "requestMetadata",
65
+ "invocation_id",
66
+ "invocationId",
67
+ "model",
68
+ "collaboration_mode",
69
+ "collaborationMode",
70
+ "goal_objective",
71
+ "goalObjective",
72
+ }
73
+ unsupported = sorted(str(field) for field in value if field not in allowed_fields)
74
+ if unsupported:
75
+ raise PluginHostError(
76
+ "codex_input_unsupported",
77
+ "Codex input contains undeclared fields: " + ", ".join(unsupported),
78
+ )
79
+ _reject_aliased_duplicates(
80
+ value,
81
+ ("user_id", "userId"),
82
+ ("session_id", "sessionId"),
83
+ ("request_metadata", "requestMetadata"),
84
+ ("invocation_id", "invocationId"),
85
+ ("collaboration_mode", "collaborationMode"),
86
+ ("goal_objective", "goalObjective"),
87
+ )
88
+ if "messages" in value and "input" in value:
89
+ raise PluginHostError(
90
+ "codex_input_invalid",
91
+ "Codex input must use exactly one of messages or input",
92
+ )
93
+ user_id = str(value.get("user_id") or value.get("userId") or "").strip()
94
+ if not user_id:
95
+ raise PluginHostError("codex_input_invalid", "Codex input requires user_id")
96
+ raw_messages = value.get("messages")
97
+ if raw_messages is None and value.get("input") is not None:
98
+ raw_messages = ({"role": "user", "content": value.get("input")},)
99
+ if not isinstance(raw_messages, Sequence) or isinstance(
100
+ raw_messages, (str, bytes)
101
+ ):
102
+ raise PluginHostError("codex_input_invalid", "Codex input requires messages")
103
+ messages: list[Mapping[str, Any]] = []
104
+ for index, message in enumerate(raw_messages):
105
+ if not isinstance(message, Mapping):
106
+ raise PluginHostError(
107
+ "codex_input_invalid", f"Codex messages[{index}] must be an object"
108
+ )
109
+ role = str(message.get("role") or "").strip()
110
+ if role not in {"system", "user", "assistant", "tool"}:
111
+ raise PluginHostError(
112
+ "codex_input_invalid",
113
+ f"Codex messages[{index}] has unsupported role {role!r}",
114
+ )
115
+ messages.append(dict(message))
116
+ if not messages or messages[-1].get("role") != "user":
117
+ raise PluginHostError(
118
+ "codex_input_invalid", "Codex turn must end with a user message"
119
+ )
120
+ metadata = value.get("request_metadata") or value.get("requestMetadata")
121
+ if metadata is not None and not isinstance(metadata, Mapping):
122
+ raise PluginHostError(
123
+ "codex_input_invalid", "request_metadata must be an object"
124
+ )
125
+ session_id = str(
126
+ value.get("session_id") or value.get("sessionId") or ""
127
+ ).strip()
128
+ invocation_id = str(
129
+ value.get("invocation_id") or value.get("invocationId") or ""
130
+ ).strip()
131
+ if len(invocation_id) > 256:
132
+ raise PluginHostError(
133
+ "codex_input_invalid", "Codex invocation_id exceeds 256 characters"
134
+ )
135
+ model = str(value.get("model") or "").strip()
136
+ if len(model) > 256:
137
+ raise PluginHostError(
138
+ "codex_input_invalid", "Codex model exceeds 256 characters"
139
+ )
140
+ collaboration_mode = (
141
+ str(value.get("collaboration_mode") or value.get("collaborationMode") or "")
142
+ .strip()
143
+ .lower()
144
+ )
145
+ if collaboration_mode and collaboration_mode not in {"default", "plan"}:
146
+ raise PluginHostError(
147
+ "codex_input_unsupported",
148
+ "Codex collaboration_mode must be default or plan",
149
+ )
150
+ goal_objective = str(
151
+ value.get("goal_objective") or value.get("goalObjective") or ""
152
+ ).strip()
153
+ if len(goal_objective) > 4096:
154
+ raise PluginHostError(
155
+ "codex_input_invalid", "Codex goal_objective exceeds 4096 characters"
156
+ )
157
+ return cls(
158
+ user_id=user_id,
159
+ session_id=session_id or None,
160
+ messages=tuple(messages),
161
+ request_metadata=dict(metadata) if metadata is not None else None,
162
+ invocation_id=invocation_id or None,
163
+ model=model or None,
164
+ collaboration_mode=collaboration_mode or None,
165
+ goal_objective=goal_objective or None,
166
+ )
167
+
168
+
169
+ @dataclass(frozen=True)
170
+ class CodexTurnResult:
171
+ session_id: str
172
+ output_text: str
173
+ usage: Mapping[str, Any]
174
+ metadata: Mapping[str, Any]
175
+ inventory: CodexProviderInventory
176
+
177
+
178
+ @dataclass(frozen=True)
179
+ class _CodexBundleConfig:
180
+ model: str
181
+ allowed_models: tuple[str, ...]
182
+ prompt: str
183
+ project_dir: Path
184
+ launch_config: Mapping[str, Any]
185
+ inventory: CodexProviderInventory
186
+
187
+
188
+ class CodexAgentProviderRuntime:
189
+ """ExecutableAgentProvider that preserves Codex native loop ownership."""
190
+
191
+ def __init__(
192
+ self,
193
+ *,
194
+ plugin_id: str,
195
+ session_service: BaseSessionService,
196
+ codex_client_factory: Callable[..., Any] | None,
197
+ credential_resolver: Any = None,
198
+ ) -> None:
199
+ self._plugin_id = plugin_id
200
+ self._session_service = session_service
201
+ self._client_factory = codex_client_factory
202
+ self._credentials = credential_resolver
203
+ self._ready = False
204
+ self._disposed = False
205
+ self._last_activation: CodexAgentActivation | None = None
206
+
207
+ @property
208
+ def disposed(self) -> bool:
209
+ return self._disposed
210
+
211
+ @property
212
+ def last_activation(self) -> "CodexAgentActivation | None":
213
+ return self._last_activation
214
+
215
+ async def start(self) -> None:
216
+ if self._disposed:
217
+ raise RuntimeError("Codex provider is disposed")
218
+ self._ready = True
219
+
220
+ async def health(self) -> bool:
221
+ return self._ready and not self._disposed
222
+
223
+ async def drain(self) -> None:
224
+ self._ready = False
225
+
226
+ async def dispose(self) -> None:
227
+ self._ready = False
228
+ self._disposed = True
229
+
230
+ async def prepare(
231
+ self,
232
+ bundle: ResolvedPluginBundle,
233
+ *,
234
+ capabilities: PluginExecutionContext,
235
+ ) -> "CodexAgentActivation":
236
+ if not self._ready or self._disposed:
237
+ raise PluginHostError(
238
+ "codex_provider_unavailable", "Codex provider is not ready"
239
+ )
240
+ _reject_external_execution(bundle, capabilities)
241
+ config = _resolve_bundle_config(
242
+ bundle,
243
+ plugin_id=self._plugin_id,
244
+ credential_resolver=self._credentials,
245
+ )
246
+ activation = CodexAgentActivation(
247
+ bundle=bundle,
248
+ config=config,
249
+ session_service=self._session_service,
250
+ codex_client_factory=self._client_factory,
251
+ )
252
+ self._last_activation = activation
253
+ return activation
254
+
255
+
256
+ class CodexAgentProviderFactory:
257
+ def __init__(
258
+ self,
259
+ *,
260
+ session_service: BaseSessionService | None = None,
261
+ codex_client_factory: Callable[..., Any] | None = None,
262
+ credential_resolver: Any = None,
263
+ ) -> None:
264
+ self._session_service = session_service
265
+ self._client_factory = codex_client_factory
266
+ self._credentials = credential_resolver
267
+ self.runtime: CodexAgentProviderRuntime | None = None
268
+
269
+ async def stage(
270
+ self,
271
+ manifest: PluginManifest,
272
+ *,
273
+ profile: CompositionProfile,
274
+ services: Mapping[str, Any],
275
+ ) -> CodexAgentProviderRuntime:
276
+ del profile
277
+ service = self._session_service or services.get("session_service")
278
+ if service is None:
279
+ service = create_session_service(backend="memory")
280
+ if not isinstance(service, BaseSessionService):
281
+ raise PluginHostError(
282
+ "codex_session_service_invalid",
283
+ "Codex provider requires a BaseSessionService",
284
+ )
285
+ client_factory = self._client_factory or services.get("codex_client_factory")
286
+ credentials = self._credentials or services.get("credential_resolver")
287
+ self.runtime = CodexAgentProviderRuntime(
288
+ plugin_id=manifest.metadata.id,
289
+ session_service=service,
290
+ codex_client_factory=client_factory,
291
+ credential_resolver=credentials,
292
+ )
293
+ return self.runtime
294
+
295
+
296
+ class CodexAgentActivation:
297
+ def __init__(
298
+ self,
299
+ *,
300
+ bundle: ResolvedPluginBundle,
301
+ config: _CodexBundleConfig,
302
+ session_service: BaseSessionService,
303
+ codex_client_factory: Callable[..., Any] | None,
304
+ ) -> None:
305
+ self._bundle = bundle
306
+ self._config = config
307
+ self._session_service = session_service
308
+ self._executor = RuntimeExecutor(build_default_runtime_registry())
309
+ self._launch_context = RuntimeLaunchContext(
310
+ runtime_type="codex",
311
+ project_dir=config.project_dir,
312
+ config=config.launch_config,
313
+ services=RuntimeServices(codex_client_factory=codex_client_factory),
314
+ )
315
+ self._ready = False
316
+ self._disposed = False
317
+ self._kernel_adapters: list[Any] = []
318
+
319
+ @property
320
+ def disposed(self) -> bool:
321
+ return self._disposed
322
+
323
+ async def start(self) -> None:
324
+ if self._disposed:
325
+ raise RuntimeError("Codex activation is disposed")
326
+ self._ready = True
327
+
328
+ async def health(self) -> bool:
329
+ return self._ready and not self._disposed
330
+
331
+ async def execute(self, request: Any) -> CodexTurnResult:
332
+ if not self._ready or self._disposed:
333
+ raise PluginHostError(
334
+ "codex_activation_unavailable", "Codex activation is not ready"
335
+ )
336
+ turn = CodexTurnRequest.parse(request)
337
+ selected_model = turn.model or self._config.model
338
+ if selected_model not in self._config.allowed_models:
339
+ raise PluginHostError(
340
+ "codex_model_unsupported",
341
+ f"Codex model {selected_model!r} is not declared by the immutable Bundle",
342
+ )
343
+ launch_context = self._turn_launch_context(turn)
344
+ preparation = await self._executor.prepare_start(launch_context)
345
+ session_id, result = await invoke_runtime_conversation_once(
346
+ executor=self._executor,
347
+ launch_context=launch_context,
348
+ agent_id=self._bundle.manifest.agent_id,
349
+ user_id=turn.user_id,
350
+ messages=[dict(item) for item in turn.messages],
351
+ session_id=turn.session_id,
352
+ model=selected_model,
353
+ instructions=self._config.prompt,
354
+ request_metadata=turn.request_metadata,
355
+ invocation_id=turn.invocation_id,
356
+ session_service_provider=lambda: self._session_service,
357
+ runtime_preparation=preparation,
358
+ )
359
+ return CodexTurnResult(
360
+ session_id=session_id,
361
+ output_text=str(result.get("output_text") or ""),
362
+ usage=dict(result.get("usage") or {}),
363
+ metadata=dict(result.get("metadata") or {}),
364
+ inventory=CodexProviderInventory(
365
+ provider=self._config.inventory.provider,
366
+ model=selected_model,
367
+ mcp_servers=self._config.inventory.mcp_servers,
368
+ skills=self._config.inventory.skills,
369
+ ),
370
+ )
371
+
372
+ def runtime_adapter(self) -> Any:
373
+ """Create one activation-owned native adapter for AgentKernel.
374
+
375
+ Each Kernel command receives a fresh App Server transport. Durable
376
+ SessionEvent metadata reconnects the next command to the same Codex
377
+ Thread, while retaining adapters here lets activation disposal close a
378
+ command that is still active during Profile drain.
379
+ """
380
+
381
+ if not self._ready or self._disposed:
382
+ raise PluginHostError(
383
+ "codex_activation_unavailable", "Codex activation is not ready"
384
+ )
385
+ adapter = self._executor.create_adapter(self._launch_context)
386
+ self._kernel_adapters.append(adapter)
387
+ return adapter
388
+
389
+ def _turn_launch_context(self, turn: CodexTurnRequest) -> RuntimeLaunchContext:
390
+ config = dict(self._config.launch_config)
391
+ if turn.model:
392
+ config["model"] = turn.model
393
+ if turn.collaboration_mode:
394
+ config["collaboration_mode"] = turn.collaboration_mode
395
+ if turn.goal_objective:
396
+ config["goal_objective"] = turn.goal_objective
397
+ return RuntimeLaunchContext(
398
+ runtime_type=self._launch_context.runtime_type,
399
+ project_dir=self._launch_context.project_dir,
400
+ config=config,
401
+ services=self._launch_context.services,
402
+ deployment_mode=self._launch_context.deployment_mode,
403
+ )
404
+
405
+ async def drain(self) -> None:
406
+ self._ready = False
407
+
408
+ async def dispose(self) -> None:
409
+ self._ready = False
410
+ first_error: BaseException | None = None
411
+ for adapter in reversed(self._kernel_adapters):
412
+ close_all = getattr(adapter, "close_all", None)
413
+ if not callable(close_all):
414
+ continue
415
+ try:
416
+ await close_all()
417
+ except BaseException as error: # cleanup must continue
418
+ if first_error is None:
419
+ first_error = error
420
+ self._kernel_adapters.clear()
421
+ try:
422
+ await self._executor.close_all()
423
+ except BaseException as error: # cleanup must continue
424
+ if first_error is None:
425
+ first_error = error
426
+ self._disposed = True
427
+ if first_error is not None:
428
+ raise first_error
429
+
430
+
431
+ def _reject_aliased_duplicates(
432
+ value: Mapping[str, Any],
433
+ *aliases: tuple[str, str],
434
+ ) -> None:
435
+ for snake_case, camel_case in aliases:
436
+ if snake_case in value and camel_case in value:
437
+ raise PluginHostError(
438
+ "codex_input_invalid",
439
+ f"Codex input cannot contain both {snake_case} and {camel_case}",
440
+ )
441
+
442
+
443
+ def _reject_external_execution(
444
+ bundle: ResolvedPluginBundle,
445
+ capabilities: PluginExecutionContext,
446
+ ) -> None:
447
+ del capabilities
448
+ profile_config = bundle.composition.profile.agent_provider.config
449
+ allowed_config = {"runtimeType", "runtimeVersion"}
450
+ unsupported_config = sorted(set(profile_config) - allowed_config)
451
+ execution = bundle.resolved_agent_spec.get("execution")
452
+ strategy = (
453
+ str(execution.get("strategy") or "direct").strip()
454
+ if isinstance(execution, Mapping)
455
+ else "direct"
456
+ )
457
+ if unsupported_config:
458
+ raise PluginHostError(
459
+ "codex_provider_config_unsupported",
460
+ "Codex AgentProvider received unsupported configuration: "
461
+ + ", ".join(unsupported_config),
462
+ )
463
+ if strategy != "direct":
464
+ raise PluginHostError(
465
+ "codex_external_execution_unsupported",
466
+ f"Codex AgentProvider owns its execution semantics and does not support {strategy!r}",
467
+ )
468
+
469
+
470
+ def _resolve_bundle_config(
471
+ bundle: ResolvedPluginBundle,
472
+ *,
473
+ plugin_id: str,
474
+ credential_resolver: Any,
475
+ ) -> _CodexBundleConfig:
476
+ spec = bundle.resolved_agent_spec
477
+ raw_model = spec.get("model")
478
+ if not isinstance(raw_model, Mapping):
479
+ raise PluginHostError(
480
+ "codex_bundle_model_invalid", "Bundle resolved model must be an object"
481
+ )
482
+ model = str(raw_model.get("model") or "").strip()
483
+ if not model:
484
+ raise PluginHostError(
485
+ "codex_bundle_model_missing", "Bundle resolved Agent spec has no model"
486
+ )
487
+ allowed_models = _resolve_allowed_models(bundle, default_model=model)
488
+ instructions = spec.get("instructions")
489
+ if not isinstance(instructions, Mapping):
490
+ raise PluginHostError(
491
+ "codex_bundle_prompt_invalid", "Bundle instructions must be an object"
492
+ )
493
+ prompt = "\n\n".join(
494
+ part
495
+ for part in (
496
+ str(instructions.get("system") or "").strip(),
497
+ str(instructions.get("task") or "").strip(),
498
+ )
499
+ if part
500
+ )
501
+ if not prompt:
502
+ raise PluginHostError(
503
+ "codex_bundle_prompt_missing", "Bundle resolved Agent spec has no instructions"
504
+ )
505
+ capabilities = spec.get("capabilities")
506
+ if not isinstance(capabilities, Mapping):
507
+ raise PluginHostError(
508
+ "codex_bundle_capabilities_invalid", "Bundle capabilities must be an object"
509
+ )
510
+ if capabilities.get("tools"):
511
+ raise PluginHostError(
512
+ "codex_tools_unsupported", "Codex only accepts its native tools, MCP, and Skills"
513
+ )
514
+ skills = _resolve_skills(bundle.root, capabilities.get("skills"))
515
+ mcp_servers, env = _resolve_mcp(
516
+ capabilities.get("mcpServers") or capabilities.get("mcp_servers"),
517
+ credential_resolver=credential_resolver,
518
+ )
519
+ execution = spec.get("execution")
520
+ execution = execution if isinstance(execution, Mapping) else {}
521
+ project_dir = bundle.root / "runtime"
522
+ if not project_dir.is_dir():
523
+ project_dir = bundle.root
524
+ launch_config: dict[str, Any] = {
525
+ "model": model,
526
+ "models": list(allowed_models),
527
+ "prompt": str(instructions.get("system") or "").strip(),
528
+ "task_prompt": str(instructions.get("task") or "").strip(),
529
+ "sandbox": str(execution.get("sandbox") or "read_only"),
530
+ "approval_mode": str(execution.get("approvalMode") or execution.get("approval_mode") or ""),
531
+ "turn_timeout_seconds": int(
532
+ execution.get("timeoutSeconds") or execution.get("timeout_seconds") or 120
533
+ ),
534
+ "mcp_servers": mcp_servers,
535
+ "skills": skills,
536
+ "env": env,
537
+ }
538
+ return _CodexBundleConfig(
539
+ model=model,
540
+ allowed_models=allowed_models,
541
+ prompt=prompt,
542
+ project_dir=project_dir,
543
+ launch_config=launch_config,
544
+ inventory=CodexProviderInventory(
545
+ provider=plugin_id,
546
+ model=model,
547
+ mcp_servers=tuple(item["name"] for item in mcp_servers),
548
+ skills=tuple(item["name"] for item in skills),
549
+ ),
550
+ )
551
+
552
+
553
+ def _resolve_allowed_models(
554
+ bundle: ResolvedPluginBundle,
555
+ *,
556
+ default_model: str,
557
+ ) -> tuple[str, ...]:
558
+ """Read the immutable model allowlist produced by the Bundle compiler.
559
+
560
+ Older Bundle fixtures without ``runtime-lock.json`` remain pinned to their
561
+ resolved default. A declared lock is already digest-verified by
562
+ ``PluginBundleResolver``; malformed model inventory must fail activation
563
+ instead of silently widening run-level model selection.
564
+ """
565
+
566
+ if not any(
567
+ entry.path == "runtime-lock.json" for entry in bundle.manifest.files
568
+ ):
569
+ return (default_model,)
570
+ try:
571
+ payload = json.loads(
572
+ (bundle.root / "runtime-lock.json").read_text(encoding="utf-8")
573
+ )
574
+ except (OSError, UnicodeError, json.JSONDecodeError) as error:
575
+ raise PluginHostError(
576
+ "codex_bundle_model_inventory_invalid",
577
+ "Bundle runtime-lock.json is not a valid model inventory",
578
+ ) from error
579
+ if not isinstance(payload, Mapping):
580
+ raise PluginHostError(
581
+ "codex_bundle_model_inventory_invalid",
582
+ "Bundle runtime-lock.json must be an object",
583
+ )
584
+ raw_models = payload.get("models")
585
+ if not isinstance(raw_models, Sequence) or isinstance(raw_models, (str, bytes)):
586
+ raise PluginHostError(
587
+ "codex_bundle_model_inventory_invalid",
588
+ "Bundle runtime-lock.json must declare models as a list",
589
+ )
590
+ models = tuple(
591
+ dict.fromkeys(str(item).strip() for item in raw_models if str(item).strip())
592
+ )
593
+ if not models or default_model not in models:
594
+ raise PluginHostError(
595
+ "codex_bundle_model_inventory_invalid",
596
+ "Bundle model inventory must include its resolved default model",
597
+ )
598
+ return models
599
+
600
+
601
+ def _resolve_skills(root: Path, value: Any) -> list[dict[str, str]]:
602
+ if value is None:
603
+ return []
604
+ if not isinstance(value, Sequence) or isinstance(value, (str, bytes)):
605
+ raise PluginHostError("codex_skill_invalid", "Bundle Skills must be a list")
606
+ resolved: list[dict[str, str]] = []
607
+ seen: set[str] = set()
608
+ for index, item in enumerate(value):
609
+ if not isinstance(item, Mapping):
610
+ raise PluginHostError(
611
+ "codex_skill_invalid", f"Bundle Skills[{index}] must be an object"
612
+ )
613
+ name = str(item.get("name") or "").strip()
614
+ relative = PurePosixPath(str(item.get("bundlePath") or item.get("bundle_path") or ""))
615
+ if not name or not relative.parts or relative.is_absolute() or ".." in relative.parts:
616
+ raise PluginHostError(
617
+ "codex_skill_invalid", f"Bundle Skills[{index}] has an invalid native path"
618
+ )
619
+ path = (root / Path(*relative.parts)).resolve()
620
+ try:
621
+ path.relative_to(root)
622
+ except ValueError as error:
623
+ raise PluginHostError(
624
+ "codex_skill_invalid", f"Bundle Skill {name!r} escapes the Bundle root"
625
+ ) from error
626
+ if not (path / "SKILL.md").is_file():
627
+ raise PluginHostError(
628
+ "codex_skill_missing", f"Bundle Skill {name!r} has no SKILL.md"
629
+ )
630
+ if name in seen:
631
+ raise PluginHostError("codex_skill_invalid", f"duplicate Bundle Skill {name!r}")
632
+ seen.add(name)
633
+ # Codex's native SkillInput expects the concrete SKILL.md source, not
634
+ # its containing directory. A directory is accepted by the Python
635
+ # model but silently omitted by the real App Server turn projection.
636
+ resolved.append({"name": name, "path": str(path / "SKILL.md")})
637
+ return resolved
638
+
639
+
640
+ def _resolve_mcp(
641
+ value: Any,
642
+ *,
643
+ credential_resolver: Any,
644
+ ) -> tuple[list[dict[str, str]], dict[str, str]]:
645
+ if value is None:
646
+ return [], {}
647
+ if not isinstance(value, Sequence) or isinstance(value, (str, bytes)):
648
+ raise PluginHostError("codex_mcp_invalid", "Bundle MCP servers must be a list")
649
+ servers: list[dict[str, str]] = []
650
+ env: dict[str, str] = {}
651
+ seen: set[str] = set()
652
+ for index, item in enumerate(value):
653
+ if not isinstance(item, Mapping):
654
+ raise PluginHostError(
655
+ "codex_mcp_invalid", f"Bundle MCP servers[{index}] must be an object"
656
+ )
657
+ name = str(item.get("name") or "").strip()
658
+ transport = str(item.get("transport") or "").strip().lower()
659
+ url = str(item.get("endpointUrl") or item.get("endpoint_url") or "").strip()
660
+ if not name or name in seen:
661
+ raise PluginHostError("codex_mcp_invalid", "Bundle MCP names must be unique")
662
+ if transport not in {"http", "sse"} or not url:
663
+ raise PluginHostError(
664
+ "codex_mcp_transport_unsupported",
665
+ f"Codex Bundle MCP {name!r} requires an http/sse endpoint",
666
+ )
667
+ env_refs = item.get("envRefs") or item.get("env_refs") or {}
668
+ if not isinstance(env_refs, Mapping):
669
+ raise PluginHostError(
670
+ "codex_mcp_invalid", f"Bundle MCP {name!r} envRefs must be an object"
671
+ )
672
+ if len(env_refs) > 1:
673
+ raise PluginHostError(
674
+ "codex_mcp_credentials_unsupported",
675
+ f"Codex HTTP MCP {name!r} accepts at most one bearer credential",
676
+ )
677
+ server = {"name": name, "url": url}
678
+ for env_name, reference in env_refs.items():
679
+ env_key = str(env_name).strip()
680
+ ref = str(reference).strip()
681
+ if not env_key or not ref.startswith(
682
+ ("env://", "secret://", "credential://", "vault://")
683
+ ):
684
+ raise PluginHostError(
685
+ "codex_mcp_credential_invalid",
686
+ f"Bundle MCP {name!r} contains an invalid credential reference",
687
+ )
688
+ if credential_resolver is None:
689
+ raise PluginHostError(
690
+ "codex_mcp_credential_unavailable",
691
+ f"Bundle MCP {name!r} requires a credential resolver",
692
+ )
693
+ try:
694
+ value = (
695
+ credential_resolver.resolve(ref)
696
+ if hasattr(credential_resolver, "resolve")
697
+ else credential_resolver(ref)
698
+ )
699
+ except Exception as error:
700
+ raise PluginHostError(
701
+ "codex_mcp_credential_unavailable",
702
+ f"Bundle MCP {name!r} credential could not be resolved",
703
+ ) from error
704
+ if not str(value):
705
+ raise PluginHostError(
706
+ "codex_mcp_credential_unavailable",
707
+ f"Bundle MCP {name!r} credential is empty",
708
+ )
709
+ env[env_key] = str(value)
710
+ server["env_key"] = env_key
711
+ servers.append(server)
712
+ seen.add(name)
713
+ return servers, env
714
+
715
+
716
+ __all__ = [
717
+ "CodexAgentProviderFactory",
718
+ "CodexAgentProviderRuntime",
719
+ "CodexProviderInventory",
720
+ "CodexTurnRequest",
721
+ "CodexTurnResult",
722
+ ]