agentengine-sdk-python 0.8.2__py3-none-any.whl → 0.8.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (563) hide show
  1. {agentengine_sdk_python-0.8.2.dist-info → agentengine_sdk_python-0.8.4.dist-info}/METADATA +22 -28
  2. agentengine_sdk_python-0.8.4.dist-info/RECORD +1070 -0
  3. ksadk/_build_provenance.json +1 -0
  4. ksadk/agui/_agent_helpers.py +4 -0
  5. ksadk/api/client.py +108 -91
  6. ksadk/builders/code_builder.py +36 -9
  7. ksadk/builders/framework_requirements.py +2 -1
  8. ksadk/builders/ks3_uploader.py +36 -7
  9. ksadk/builders/managed_runtime_builder.py +4 -0
  10. ksadk/cli/__init__.py +38 -3
  11. ksadk/cli/cmd_managed_runtime.py +13 -1
  12. ksadk/cli/cmd_plugin.py +662 -0
  13. ksadk/cli/cmd_web.py +2 -5
  14. ksadk/cli/runtime_bootstrap.py +23 -1
  15. ksadk/codex/client.py +424 -96
  16. ksadk/codex/projection.py +24 -0
  17. ksadk/codex/runtime.py +83 -283
  18. ksadk/common/auth.py +16 -10
  19. ksadk/configs/env_registry.py +59 -3
  20. ksadk/configs/settings.py +9 -0
  21. ksadk/context_engine/contributors.py +73 -8
  22. ksadk/conversations/__init__.py +19 -0
  23. ksadk/conversations/context.py +104 -24
  24. ksadk/conversations/contracts.py +266 -0
  25. ksadk/conversations/message_projection.py +154 -15
  26. ksadk/conversations/projector.py +614 -0
  27. ksadk/conversations/reducer.py +98 -0
  28. ksadk/conversations/runtime_invocation.py +5 -0
  29. ksadk/conversations/runtime_payloads.py +1 -0
  30. ksadk/conversations/runtime_preparation.py +30 -27
  31. ksadk/conversations/runtime_resume.py +50 -2
  32. ksadk/conversations/runtime_stream_events.py +5 -0
  33. ksadk/conversations/runtime_streaming.py +25 -7
  34. ksadk/deployment/env_forward.py +2 -2
  35. ksadk/deployment/providers/serverless.py +1 -1
  36. ksadk/events/_v1_compat/projection.py +2 -0
  37. ksadk/events/adapters/_codex_validators.py +18 -7
  38. ksadk/events/adapters/codex.py +190 -0
  39. ksadk/events/adapters/langgraph.py +15 -1
  40. ksadk/events/canonical_store.py +3 -1
  41. ksadk/events/projections.py +1 -1
  42. ksadk/harness/__init__.py +45 -35
  43. ksadk/harness/config.py +2 -2
  44. ksadk/harness/runtime.py +75 -16
  45. ksadk/harness/tools.py +38 -13
  46. ksadk/interaction/provider.py +1 -0
  47. ksadk/interaction/providers/langgraph.py +1 -0
  48. ksadk/kernel/bootstrap.py +28 -4
  49. ksadk/kernel/contract_fingerprints.py +5 -4
  50. ksadk/kernel/contracts.py +8 -0
  51. ksadk/kernel/control.py +1 -0
  52. ksadk/kernel/ingress.py +36 -2
  53. ksadk/kernel/memory_store.py +1 -0
  54. ksadk/kernel/postgres_store.py +1 -0
  55. ksadk/kernel/sqlite_store.py +28 -0
  56. ksadk/kernel/store.py +3 -0
  57. ksadk/kernel/worker.py +76 -10
  58. ksadk/knowledge_base/client.py +57 -27
  59. ksadk/mcp_runtime/__init__.py +12 -4
  60. ksadk/memory/adk/backends/sdk_ltm_backend.py +161 -41
  61. ksadk/memory/adk/resilient_session_service.py +25 -17
  62. ksadk/memory/adk/short_term_memory.py +26 -9
  63. ksadk/memory/models.py +7 -4
  64. ksadk/memory/provider_resolver.py +3 -0
  65. ksadk/memory/providers/local_sqlite.py +7 -0
  66. ksadk/model_proxy/server.py +10 -5
  67. ksadk/plugins/__init__.py +171 -0
  68. ksadk/plugins/artifacts.py +275 -0
  69. ksadk/plugins/bridges/__init__.py +47 -0
  70. ksadk/plugins/bridges/codex.py +659 -0
  71. ksadk/plugins/bridges/dsh.py +1300 -0
  72. ksadk/plugins/builtins.py +746 -0
  73. ksadk/plugins/bundle.py +246 -0
  74. ksadk/plugins/bundle_security.py +191 -0
  75. ksadk/plugins/codex_manifest.py +983 -0
  76. ksadk/plugins/composition.py +537 -0
  77. ksadk/plugins/context_contributor.py +437 -0
  78. ksadk/plugins/contracts.py +569 -0
  79. ksadk/plugins/delivery.py +153 -0
  80. ksadk/plugins/dsh_installation_digest.py +71 -0
  81. ksadk/plugins/dsh_toolchain.py +1024 -0
  82. ksadk/plugins/ecosystem_bridge.py +1105 -0
  83. ksadk/plugins/ecosystem_probe.py +191 -0
  84. ksadk/plugins/host.py +787 -0
  85. ksadk/plugins/kernel_adapter.py +205 -0
  86. ksadk/plugins/providers/__init__.py +108 -0
  87. ksadk/plugins/providers/bundles/dsh-knowledge/cordis.patch.yml +3 -0
  88. ksadk/plugins/providers/bundles/dsh-knowledge/index.mjs +53 -0
  89. ksadk/plugins/providers/bundles/dsh-knowledge/package.json +16 -0
  90. ksadk/plugins/providers/bundles/dsh-memory/cordis.patch.yml +3 -0
  91. ksadk/plugins/providers/bundles/dsh-memory/index.mjs +175 -0
  92. ksadk/plugins/providers/bundles/dsh-memory/package.json +16 -0
  93. ksadk/plugins/providers/bundles/dsh-platform-resources/client.mjs +166 -0
  94. ksadk/plugins/providers/bundles/dsh-platform-resources/cordis.patch.yml +3 -0
  95. ksadk/plugins/providers/bundles/dsh-platform-resources/index.mjs +9 -0
  96. ksadk/plugins/providers/bundles/dsh-platform-resources/package.json +12 -0
  97. ksadk/plugins/providers/bundles/dsh-skill-center/cordis.patch.yml +3 -0
  98. ksadk/plugins/providers/bundles/dsh-skill-center/index.mjs +125 -0
  99. ksadk/plugins/providers/bundles/dsh-skill-center/package.json +16 -0
  100. ksadk/plugins/providers/bundles/ksadk-codex/cordis.patch.yml +3 -0
  101. ksadk/plugins/providers/bundles/ksadk-codex/index.mjs +18 -0
  102. ksadk/plugins/providers/bundles/ksadk-codex/package.json +19 -0
  103. ksadk/plugins/providers/bundles/ksadk-dsh-capability-host/cordis.patch.yml +3 -0
  104. ksadk/plugins/providers/bundles/ksadk-dsh-capability-host/index.mjs +870 -0
  105. ksadk/plugins/providers/bundles/ksadk-dsh-capability-host/package.json +27 -0
  106. ksadk/plugins/providers/bundles/ksadk-dsh-studio/client.js +92 -0
  107. ksadk/plugins/providers/bundles/ksadk-dsh-studio/index.mjs +15 -0
  108. ksadk/plugins/providers/bundles/ksadk-dsh-studio/package.json +7 -0
  109. ksadk/plugins/providers/bundles/ksadk-harness/cordis.patch.yml +3 -0
  110. ksadk/plugins/providers/bundles/ksadk-harness/index.mjs +19 -0
  111. ksadk/plugins/providers/bundles/ksadk-harness/package.json +19 -0
  112. ksadk/plugins/providers/codex.py +988 -0
  113. ksadk/plugins/providers/codex_dsh.py +83 -0
  114. ksadk/plugins/providers/codex_native.py +368 -0
  115. ksadk/plugins/providers/codex_turn.py +301 -0
  116. ksadk/plugins/providers/dsh.py +951 -0
  117. ksadk/plugins/providers/dsh_capabilities.py +1389 -0
  118. ksadk/plugins/providers/dsh_descriptor_host.py +226 -0
  119. ksadk/plugins/providers/dsh_mcp.py +467 -0
  120. ksadk/plugins/providers/harness.py +632 -0
  121. ksadk/plugins/providers/harness_dsh.py +91 -0
  122. ksadk/plugins/providers/legacy.py +215 -0
  123. ksadk/plugins/providers/legacy_catalog.py +100 -0
  124. ksadk/plugins/providers/mcp_projection.py +150 -0
  125. ksadk/plugins/providers/platform_resources.py +657 -0
  126. ksadk/plugins/providers/shipped_dsh.py +370 -0
  127. ksadk/plugins/resolver.py +338 -0
  128. ksadk/plugins/subagent_providers/__init__.py +12 -0
  129. ksadk/plugins/subagent_providers/codex.py +443 -0
  130. ksadk/plugins/subagents.py +333 -0
  131. ksadk/resource_runtime/__init__.py +4 -0
  132. ksadk/resource_runtime/broker.py +616 -0
  133. ksadk/resource_runtime/build_artifacts.py +165 -0
  134. ksadk/resource_runtime/contracts.py +138 -0
  135. ksadk/resource_runtime/discovery_receipts.py +100 -0
  136. ksadk/resource_runtime/discovery_skills.py +172 -0
  137. ksadk/resource_runtime/errors.py +6 -0
  138. ksadk/resource_runtime/ipc.py +142 -0
  139. ksadk/resource_runtime/kernel_approval.py +165 -0
  140. ksadk/resource_runtime/knowledge.py +114 -0
  141. ksadk/resource_runtime/langgraph.py +94 -0
  142. ksadk/resource_runtime/leases.py +191 -0
  143. ksadk/resource_runtime/managed_projection.py +186 -0
  144. ksadk/resource_runtime/mcp_server.py +138 -0
  145. ksadk/resource_runtime/memory_provider.py +318 -0
  146. ksadk/resource_runtime/memory_receipts.py +34 -0
  147. ksadk/resource_runtime/operation_ledger.py +211 -0
  148. ksadk/resource_runtime/plugin_config.py +48 -0
  149. ksadk/resource_runtime/policy_authorization.py +54 -0
  150. ksadk/resource_runtime/process.py +110 -0
  151. ksadk/resource_runtime/skill_artifacts.py +153 -0
  152. ksadk/resource_runtime/skill_runtime.py +42 -0
  153. ksadk/resource_runtime/skill_workspace.py +118 -0
  154. ksadk/resource_runtime/skills.py +215 -0
  155. ksadk/resource_runtime/snapshots.py +180 -0
  156. ksadk/resource_runtime/socket_server.py +103 -0
  157. ksadk/resource_runtime/supervisor.py +380 -0
  158. ksadk/resource_runtime/worker.py +504 -0
  159. ksadk/runners/_langgraph_runner_streams.py +74 -6
  160. ksadk/runners/adk_runner.py +149 -11
  161. ksadk/runners/base_runner.py +29 -1
  162. ksadk/runners/langgraph_runner.py +475 -145
  163. ksadk/runtime/_runner_adapter/stream_mapping.py +193 -60
  164. ksadk/runtime/adapter.py +3 -0
  165. ksadk/runtime/conversation_execution.py +92 -16
  166. ksadk/runtime/executor.py +78 -2
  167. ksadk/runtime/factory.py +30 -200
  168. ksadk/runtime/framework_adapters.py +41 -0
  169. ksadk/runtime/preprocessing.py +10 -2
  170. ksadk/runtime/runner_adapter.py +78 -14
  171. ksadk/runtime_context.py +40 -2
  172. ksadk/sandbox/__init__.py +2 -1
  173. ksadk/sandbox/backends/e2b.py +52 -11
  174. ksadk/sandbox/backends/local_process.py +9 -0
  175. ksadk/sandbox/base.py +2 -0
  176. ksadk/sandbox/e2b_connection.py +66 -0
  177. ksadk/sandbox/factory.py +72 -0
  178. ksadk/scheduler/__init__.py +18 -0
  179. ksadk/scheduler/calendar.py +102 -0
  180. ksadk/scheduler/contracts.py +254 -0
  181. ksadk/scheduler/dispatcher.py +129 -0
  182. ksadk/scheduler/engine.py +416 -0
  183. ksadk/scheduler/sqlite_store.py +581 -0
  184. ksadk/server/composition.py +2 -0
  185. ksadk/server/factory.py +5 -0
  186. ksadk/server/persistence_capability.py +516 -0
  187. ksadk/server/routes/control.py +210 -46
  188. ksadk/server/routes/dependencies.py +15 -0
  189. ksadk/server/routes/models.py +47 -4
  190. ksadk/server/routes/projection.py +252 -12
  191. ksadk/server/routes/sessions.py +284 -107
  192. ksadk/server/routes/streaming.py +43 -9
  193. ksadk/server/static/assets/{ArtifactsPanel-DSU1sWD_.js → ArtifactsPanel-CGbWlMNh.js} +1 -1
  194. ksadk/server/static/assets/{CodeBlock-DOH6MVcn.js → CodeBlock-b4C7upmO.js} +3 -3
  195. ksadk/server/static/assets/{MathMessageMarkdown-BL8my1R2.js → MathMessageMarkdown-DbdW5A4_.js} +4 -4
  196. ksadk/server/static/assets/{MermaidBlock-Dz4IP-Tx.js → MermaidBlock-BEBN4Ksz.js} +6 -6
  197. ksadk/server/static/assets/{NativeTerminalPanel-DUrK0JpZ.js → NativeTerminalPanel-zvL-WpAn.js} +1 -1
  198. ksadk/server/static/assets/{abnfDiagram-VCTEODGH-g20pFzNV.js → abnfDiagram-VCTEODGH-BYafW7Za.js} +1 -1
  199. ksadk/server/static/assets/{arc-C4FzinUA.js → arc-bTohInsS.js} +1 -1
  200. ksadk/server/static/assets/architecture-7GRP2DOG-BW5UtSnx.js +1 -0
  201. ksadk/server/static/assets/{architectureDiagram-5GKGNRK7-DpIqL5h4.js → architectureDiagram-5GKGNRK7-Ch5Lbzsi.js} +1 -1
  202. ksadk/server/static/assets/{blockDiagram-NRAW4CY4-BjkljTNz.js → blockDiagram-NRAW4CY4-kqF4MDOG.js} +1 -1
  203. ksadk/server/static/assets/{c4Diagram-UCG6FXSJ-Dk_ieq2X.js → c4Diagram-UCG6FXSJ-8uSRIODv.js} +1 -1
  204. ksadk/server/static/assets/channel-CF5Up3hQ.js +1 -0
  205. ksadk/server/static/assets/chunk-2Q5K7J3B-KkXX47WI.js +1 -0
  206. ksadk/server/static/assets/{chunk-5VM5RSS4-BVHAchFb.js → chunk-5VM5RSS4-DkhehZDr.js} +1 -1
  207. ksadk/server/static/assets/{chunk-F27PBJKO-C-ipQzuS.js → chunk-F27PBJKO-BdXhtPe9.js} +1 -1
  208. ksadk/server/static/assets/{chunk-G27WJ6UU-C2EyJbIK.js → chunk-G27WJ6UU-DPTpvpwi.js} +1 -1
  209. ksadk/server/static/assets/{chunk-JWPE2WC7-vYvVJb_M.js → chunk-JWPE2WC7-Pqoh38el.js} +1 -1
  210. ksadk/server/static/assets/{chunk-LCL6LL3I-BcI6ysPT.js → chunk-LCL6LL3I-DeU00qC-.js} +1 -1
  211. ksadk/server/static/assets/{chunk-POPQ4Y6H-C030x_Z1.js → chunk-POPQ4Y6H-CzxScFkr.js} +1 -1
  212. ksadk/server/static/assets/{chunk-RHFEMEQ7-yyGpsz4n.js → chunk-RHFEMEQ7-DpqMx-ux.js} +1 -1
  213. ksadk/server/static/assets/{chunk-SVP7TREG-BLTlmMU7.js → chunk-SVP7TREG-C6t0SypQ.js} +1 -1
  214. ksadk/server/static/assets/{chunk-XXDRQBXY-C_32ArgP.js → chunk-XXDRQBXY-DVaeafZk.js} +1 -1
  215. ksadk/server/static/assets/classDiagram-DTDB5LWJ-Cz81acRQ.js +1 -0
  216. ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-Cz81acRQ.js +1 -0
  217. ksadk/server/static/assets/{cose-bilkent-JH36ORCC-9YQYwdl0.js → cose-bilkent-JH36ORCC-Bf9D3tzJ.js} +1 -1
  218. ksadk/server/static/assets/cynefin-OW5HDTMX-DVWs8a2t.js +1 -0
  219. ksadk/server/static/assets/{cynefinDiagram-5FMLGOSQ-ErLF5N13.js → cynefinDiagram-5FMLGOSQ-B9cY3iTz.js} +1 -1
  220. ksadk/server/static/assets/{dagre-3AP2YEHR-DFiQF-6f.js → dagre-3AP2YEHR-CSwlx3CB.js} +1 -1
  221. ksadk/server/static/assets/{dagre-BuhGnRI1.js → dagre-UBpgon5q.js} +1 -1
  222. ksadk/server/static/assets/{diagram-S7CK7UJ4-Bt1v8-GC.js → diagram-S7CK7UJ4-C9akhN9a.js} +1 -1
  223. ksadk/server/static/assets/{diagram-UQ7AKVKN-DSCxJdBK.js → diagram-UQ7AKVKN-CgedhTPI.js} +1 -1
  224. ksadk/server/static/assets/{diagram-VSXAHHWV-DHfYwp_9.js → diagram-VSXAHHWV-Cjucz5BY.js} +1 -1
  225. ksadk/server/static/assets/{diagram-VX7I27RA-DdzD-4Le.js → diagram-VX7I27RA-Dd4Vleyn.js} +1 -1
  226. ksadk/server/static/assets/{diagram-Z3DM3KII-Du-nAJ9F.js → diagram-Z3DM3KII-CZ2jGtYW.js} +1 -1
  227. ksadk/server/static/assets/{dist-B7seoj3d.js → dist-B2_CVgKv.js} +1 -1
  228. ksadk/server/static/assets/{dist-CSh_DE14.js → dist-B3FR1SKK.js} +1 -1
  229. ksadk/server/static/assets/{dist-DUX-id_F.js → dist-BIr4DSRq.js} +1 -1
  230. ksadk/server/static/assets/{dist-C_wsv-Qd.js → dist-BgV_owcB.js} +1 -1
  231. ksadk/server/static/assets/{dist-W-TIVntx.js → dist-Bj730Ib4.js} +1 -1
  232. ksadk/server/static/assets/{dist-BytlxIDT.js → dist-BnaTiyzN.js} +1 -1
  233. ksadk/server/static/assets/{dist-CXYYBw3_.js → dist-C1NMAY5a.js} +1 -1
  234. ksadk/server/static/assets/{dist-dDdADKFA.js → dist-C5_ZAZsG.js} +1 -1
  235. ksadk/server/static/assets/{dist-BVDffZ0U.js → dist-CB9iJrvg.js} +1 -1
  236. ksadk/server/static/assets/{dist-Bf2M8m3N.js → dist-CFzsn6BG.js} +1 -1
  237. ksadk/server/static/assets/{dist-ngtN0DJB.js → dist-Cf125P_m.js} +1 -1
  238. ksadk/server/static/assets/{dist-BE7bu-DL.js → dist-ClYUxfeK.js} +1 -1
  239. ksadk/server/static/assets/{dist-DNp6rLRA.js → dist-DFD6Qa6T.js} +1 -1
  240. ksadk/server/static/assets/{dist-BX8z72IC.js → dist-DVCOWtha.js} +1 -1
  241. ksadk/server/static/assets/{dist-dydu68Fl.js → dist-D_ctSS2i.js} +1 -1
  242. ksadk/server/static/assets/{dist-ClxGviKw.js → dist-DiixqL0J.js} +1 -1
  243. ksadk/server/static/assets/{dist-BjU6y-A2.js → dist-DoVSwoDb.js} +1 -1
  244. ksadk/server/static/assets/{dist-Dq9gLD7L.js → dist-DvFK7p5S.js} +1 -1
  245. ksadk/server/static/assets/{dist-8hqcx0sU.js → dist-QfkEHOlb.js} +1 -1
  246. ksadk/server/static/assets/{dist-CttYUqzS.js → dist-btbmmX5O.js} +1 -1
  247. ksadk/server/static/assets/{dist-DmldrHd_.js → dist-fBO2v5Kt.js} +1 -1
  248. ksadk/server/static/assets/{dist-DnfXs8Vn.js → dist-ufY3F5p6.js} +1 -1
  249. ksadk/server/static/assets/{ebnfDiagram-PWID7BFC-Da9C9NO1.js → ebnfDiagram-PWID7BFC-Ci8NqqZu.js} +1 -1
  250. ksadk/server/static/assets/{erDiagram-SSCWMZ5O-EAGqqR79.js → erDiagram-SSCWMZ5O-CCIOhLZG.js} +1 -1
  251. ksadk/server/static/assets/eventmodeling-NTZA5JFV-CQqR2hg3.js +1 -0
  252. ksadk/server/static/assets/flowDiagram-A5DVABFB-DDRRbGPm.js +1 -0
  253. ksadk/server/static/assets/{ganttDiagram-EL5Y4UJY-DqJsKb59.js → ganttDiagram-EL5Y4UJY-Cp_r9Hb6.js} +1 -1
  254. ksadk/server/static/assets/gitGraph-4MIJSDKK-fgWEf7UD.js +1 -0
  255. ksadk/server/static/assets/{gitGraphDiagram-WWUBYQGX-B6g4dtDi.js → gitGraphDiagram-WWUBYQGX-Dlhc8Wr8.js} +1 -1
  256. ksadk/server/static/assets/index-DcTML1Up.css +2 -0
  257. ksadk/server/static/assets/index-Dh2XHqPK.js +244 -0
  258. ksadk/server/static/assets/info-A6RAGUB7-BUecLTdt.js +1 -0
  259. ksadk/server/static/assets/{infoDiagram-RXCK75RN-Djm_nbd5.js → infoDiagram-RXCK75RN-CO0rRTRA.js} +1 -1
  260. ksadk/server/static/assets/{ishikawaDiagram-5VMMS53U-DWya9SG2.js → ishikawaDiagram-5VMMS53U-CHiaedSS.js} +1 -1
  261. ksadk/server/static/assets/{journeyDiagram-EYS64GPL-DutxhSNI.js → journeyDiagram-EYS64GPL-CHcCIB09.js} +1 -1
  262. ksadk/server/static/assets/{kanban-definition-3QL26DDD-DJdOF8Fm.js → kanban-definition-3QL26DDD-CJ_ebBZf.js} +1 -1
  263. ksadk/server/static/assets/{katex-vFWytM5c.js → katex-B4weu383.js} +1 -1
  264. ksadk/server/static/assets/{linear-BHjG8b-J.js → linear-V-MgYHW1.js} +1 -1
  265. ksadk/server/static/assets/{mermaid-parser.core-KGSy4jWT.js → mermaid-parser.core-CBijEX_c.js} +2 -2
  266. ksadk/server/static/assets/{mindmap-definition-FBJOCRG2-9-HC88D4.js → mindmap-definition-FBJOCRG2-Bc-pQvpp.js} +1 -1
  267. ksadk/server/static/assets/packet-AYTQ26CC-BeW-btFv.js +1 -0
  268. ksadk/server/static/assets/{pegDiagram-XKGWAZYB-DrEZd4fD.js → pegDiagram-XKGWAZYB-D7genGLl.js} +1 -1
  269. ksadk/server/static/assets/pie-WAS4IAKB-X0xh4SaT.js +1 -0
  270. ksadk/server/static/assets/{pieDiagram-E7YTZNPT-DpjqOFFp.js → pieDiagram-E7YTZNPT-Bxj32zwD.js} +1 -1
  271. ksadk/server/static/assets/{quadrantDiagram-AXDQQJYC-BiNhgOXX.js → quadrantDiagram-AXDQQJYC-BURv2mGh.js} +1 -1
  272. ksadk/server/static/assets/radar-RG4KPBEZ-DzRMSqjd.js +1 -0
  273. ksadk/server/static/assets/railroad-74A4TZTK-Cc44icX-.js +1 -0
  274. ksadk/server/static/assets/railroad-abnf-HS5TGJTU-D6S4BdV-.js +1 -0
  275. ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-vD3aVE2o.js +1 -0
  276. ksadk/server/static/assets/railroad-peg-WCYAUIDC-CYbW_b4T.js +1 -0
  277. ksadk/server/static/assets/{railroadDiagram-O6MQD6OU-_Y3Ojg6G.js → railroadDiagram-O6MQD6OU-CCsh3avW.js} +1 -1
  278. ksadk/server/static/assets/{requirementDiagram-EFPCY7ZU-DeCFdg9K.js → requirementDiagram-EFPCY7ZU-_FRRBKEd.js} +1 -1
  279. ksadk/server/static/assets/{sankeyDiagram-P5KCCOFB-CPH75rhw.js → sankeyDiagram-P5KCCOFB-C-gr2o9M.js} +1 -1
  280. ksadk/server/static/assets/{sequenceDiagram-WJ2MYXX4-OEpQQdr4.js → sequenceDiagram-WJ2MYXX4-D97NmfKg.js} +1 -1
  281. ksadk/server/static/assets/{sizeCapture-X5ZJPWSS-heUErzhI.js → sizeCapture-X5ZJPWSS-DjrPC5FX.js} +1 -1
  282. ksadk/server/static/assets/{stateDiagram-HBIQ2CUA-DvA3jSMB.js → stateDiagram-HBIQ2CUA-DzMNg1hh.js} +1 -1
  283. ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BIcjKFhF.js +1 -0
  284. ksadk/server/static/assets/{swimlanes-XN3QIQJK-CR-dfN_t.js → swimlanes-XN3QIQJK-Cssf_CsU.js} +1 -1
  285. ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-tq3K3Ybl.js +8 -0
  286. ksadk/server/static/assets/{timeline-definition-24CTP7MA-DAVJf1TX.js → timeline-definition-24CTP7MA-BWTHzyS1.js} +1 -1
  287. ksadk/server/static/assets/treeView-Q6P3EWNA-DBXt3GdU.js +1 -0
  288. ksadk/server/static/assets/treemap-WGGIJYW6-DrCy4E_l.js +1 -0
  289. ksadk/server/static/assets/{vennDiagram-4TSXK5OY-DwM8eDkh.js → vennDiagram-4TSXK5OY-BSKgJsmR.js} +1 -1
  290. ksadk/server/static/assets/wardley-WFR3VGLG-CefhOliM.js +1 -0
  291. ksadk/server/static/assets/{wardleyDiagram-VM6X3IG4-DZL-Zz2t.js → wardleyDiagram-VM6X3IG4-eflHNoaZ.js} +1 -1
  292. ksadk/server/static/assets/{xychartDiagram-S5SC5T6Z-Dy6Yh_hu.js → xychartDiagram-S5SC5T6Z-eYqrQ-v4.js} +1 -1
  293. ksadk/server/static/index.html +2 -2
  294. ksadk/session_context.py +66 -0
  295. ksadk/sessions/__init__.py +11 -21
  296. ksadk/sessions/_local_service_sync.py +263 -1
  297. ksadk/sessions/base.py +176 -0
  298. ksadk/sessions/errors.py +4 -0
  299. ksadk/sessions/in_memory.py +309 -19
  300. ksadk/sessions/local_service.py +83 -1
  301. ksadk/sessions/persistence.py +275 -0
  302. ksadk/sessions/postgres_service.py +358 -0
  303. ksadk/sessions/resilience.py +8 -0
  304. ksadk/sessions/resilient.py +362 -4
  305. ksadk/sessions/topology.py +135 -0
  306. ksadk/skills/models.py +26 -4
  307. ksadk/skills/package_store.py +186 -38
  308. ksadk/skills/runtime/agent.py +31 -6
  309. ksadk/skills/runtime/artifact_delivery.py +139 -0
  310. ksadk/skills/runtime/backends/e2b.py +98 -6
  311. ksadk/skills/runtime/backends/local.py +73 -8
  312. ksadk/skills/runtime/base.py +3 -0
  313. ksadk/skills/runtime/executor.py +2 -0
  314. ksadk/skills/runtime/loader.py +10 -2
  315. ksadk/skills/runtime/pinned.py +114 -0
  316. ksadk/skills/runtime/request.py +31 -0
  317. ksadk/skills/service_client.py +211 -24
  318. ksadk/studio/agent_lifecycle.py +26 -20
  319. ksadk/studio/api.py +714 -90
  320. ksadk/studio/api_catalog_routes.py +23 -0
  321. ksadk/studio/api_contracts.py +34 -1
  322. ksadk/studio/api_plugin_routes.py +911 -0
  323. ksadk/studio/api_resource_connections.py +183 -0
  324. ksadk/studio/attachment_store.py +187 -0
  325. ksadk/studio/authoring.py +10 -4
  326. ksadk/studio/authoring_coordinator.py +31 -16
  327. ksadk/studio/builder.py +199 -43
  328. ksadk/studio/cloud.py +346 -104
  329. ksadk/studio/cloud_shared_web.py +565 -0
  330. ksadk/studio/codex_agent_service.py +140 -27
  331. ksadk/studio/codex_builder.py +294 -16
  332. ksadk/studio/codex_credentials.py +90 -0
  333. ksadk/studio/codex_manifest.py +51 -4
  334. ksadk/studio/codex_plugin_store.py +746 -0
  335. ksadk/studio/codex_provider_build.py +231 -0
  336. ksadk/studio/codex_run.py +200 -29
  337. ksadk/studio/compatibility_report.py +456 -0
  338. ksadk/studio/compiler.py +84 -15
  339. ksadk/studio/contracts.py +249 -4
  340. ksadk/studio/dsh_application.py +137 -0
  341. ksadk/studio/dsh_capability_service.py +1107 -0
  342. ksadk/studio/dsh_models.py +59 -0
  343. ksadk/studio/dsh_provider_registration.py +1075 -0
  344. ksadk/studio/event_store.py +54 -6
  345. ksadk/studio/framework_run.py +37 -4
  346. ksadk/studio/hosted_kernel.py +89 -14
  347. ksadk/studio/mcp_runtime.py +11 -3
  348. ksadk/studio/model_client.py +2 -2
  349. ksadk/studio/operations.py +12 -2
  350. ksadk/studio/plugin_composition.py +468 -0
  351. ksadk/studio/plugin_kernel_adapter.py +28 -0
  352. ksadk/studio/plugin_runtime.py +831 -0
  353. ksadk/studio/resource_authority.py +637 -0
  354. ksadk/studio/resource_binding_validation.py +77 -0
  355. ksadk/studio/resource_build_admission.py +196 -0
  356. ksadk/studio/resource_build_materializer.py +149 -0
  357. ksadk/studio/resource_catalog.py +149 -3
  358. ksadk/studio/resource_connections.py +220 -0
  359. ksadk/studio/run_service.py +1144 -96
  360. ksadk/studio/runtime_source.py +24 -8
  361. ksadk/studio/scheduler_assistant.py +317 -0
  362. ksadk/studio/scheduler_runtime.py +383 -0
  363. ksadk/studio/scheduler_service.py +214 -0
  364. ksadk/studio/service.py +1131 -95
  365. ksadk/studio/shared_web.py +610 -87
  366. ksadk/studio/soul.py +52 -0
  367. ksadk/studio/static/assets/CodeBlock-DCUUY0PL-BLjA7kbS.js +41 -0
  368. ksadk/studio/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  369. ksadk/studio/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  370. ksadk/studio/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  371. ksadk/studio/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  372. ksadk/studio/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  373. ksadk/studio/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  374. ksadk/studio/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  375. ksadk/studio/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  376. ksadk/studio/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  377. ksadk/studio/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  378. ksadk/studio/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  379. ksadk/studio/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  380. ksadk/studio/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  381. ksadk/studio/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  382. ksadk/studio/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  383. ksadk/studio/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  384. ksadk/studio/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  385. ksadk/studio/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  386. ksadk/studio/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  387. ksadk/studio/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  388. ksadk/studio/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  389. ksadk/studio/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  390. ksadk/studio/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  391. ksadk/studio/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  392. ksadk/studio/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  393. ksadk/studio/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  394. ksadk/studio/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  395. ksadk/studio/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  396. ksadk/studio/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  397. ksadk/studio/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  398. ksadk/studio/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  399. ksadk/studio/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  400. ksadk/studio/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  401. ksadk/studio/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  402. ksadk/studio/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  403. ksadk/studio/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  404. ksadk/studio/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  405. ksadk/studio/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  406. ksadk/studio/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  407. ksadk/studio/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  408. ksadk/studio/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  409. ksadk/studio/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  410. ksadk/studio/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  411. ksadk/studio/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  412. ksadk/studio/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  413. ksadk/studio/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  414. ksadk/studio/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  415. ksadk/studio/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  416. ksadk/studio/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  417. ksadk/studio/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  418. ksadk/studio/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  419. ksadk/studio/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  420. ksadk/studio/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  421. ksadk/studio/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  422. ksadk/studio/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  423. ksadk/studio/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  424. ksadk/studio/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  425. ksadk/studio/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  426. ksadk/studio/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  427. ksadk/studio/static/assets/MathMessageMarkdown-CBcDrAw--BEOyPC3w.css +1 -0
  428. ksadk/studio/static/assets/MathMessageMarkdown-CBcDrAw--CUYIXwgI.js +8 -0
  429. ksadk/studio/static/assets/MermaidBlock-DBFTztfQ-BTYKWEbD.js +44 -0
  430. ksadk/studio/static/assets/PrismRenderer-wiXRHURw.js +3 -0
  431. ksadk/studio/static/assets/abnfDiagram-VCTEODGH-CLETGjKq.js +1 -0
  432. ksadk/studio/static/assets/arc-Pry-7G1_.js +1 -0
  433. ksadk/studio/static/assets/architecture-7GRP2DOG-hXMiO_AU.js +1 -0
  434. ksadk/studio/static/assets/architectureDiagram-5GKGNRK7-B5EQysk6.js +36 -0
  435. ksadk/studio/static/assets/array-BifhSqXX.js +1 -0
  436. ksadk/studio/static/assets/blockDiagram-I7D4REHJ-CFmPl1de.js +129 -0
  437. ksadk/studio/static/assets/c4Diagram-7LVT6UL2-BRrGu8qt.js +38 -0
  438. ksadk/studio/static/assets/channel-BqO-gtEw.js +1 -0
  439. ksadk/studio/static/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
  440. ksadk/studio/static/assets/chunk-4HAMMTFA-D220vUtq.js +62 -0
  441. ksadk/studio/static/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
  442. ksadk/studio/static/assets/chunk-75Z2AOVW-C8lLv_qc.js +2 -0
  443. ksadk/studio/static/assets/chunk-DU6HZSFF-C4DtVpCu.js +128 -0
  444. ksadk/studio/static/assets/chunk-F27PBJKO-Cpw14SNG.js +1 -0
  445. ksadk/studio/static/assets/chunk-FOHPRMQF-DHwB1DNv.js +161 -0
  446. ksadk/studio/static/assets/chunk-GMAD6QVW-DiQA-u2N.js +72 -0
  447. ksadk/studio/static/assets/chunk-GVQU2GXP-DUdNBUCi.js +1 -0
  448. ksadk/studio/static/assets/chunk-IMKFNOWR-BSlbjT_b.js +231 -0
  449. ksadk/studio/static/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
  450. ksadk/studio/static/assets/chunk-L3NEJ4N5-Db8Ab22W.js +1 -0
  451. ksadk/studio/static/assets/chunk-OSK3NFVY-M2m6G3j4.js +10 -0
  452. ksadk/studio/static/assets/chunk-P2QGCYS3-DezaQxiK.js +1 -0
  453. ksadk/studio/static/assets/chunk-POPQ4Y6H-DOpufJ3t.js +1 -0
  454. ksadk/studio/static/assets/chunk-PWAF6VOD-CfLb1gSc.js +1 -0
  455. ksadk/studio/static/assets/chunk-SHT3W25Y-DAyIVp5H.js +168 -0
  456. ksadk/studio/static/assets/chunk-SVP7TREG-DulomOrS.js +88 -0
  457. ksadk/studio/static/assets/chunk-TICWLB2K-bHJYoflj.js +206 -0
  458. ksadk/studio/static/assets/chunk-XXDRQBXY-7PcdR2B1.js +1 -0
  459. ksadk/studio/static/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
  460. ksadk/studio/static/assets/classDiagram-ZZMXUADV-D_bW8JjA.js +1 -0
  461. ksadk/studio/static/assets/classDiagram-v2-VYDZK3BY-D_bW8JjA.js +1 -0
  462. ksadk/studio/static/assets/cose-bilkent-JH36ORCC-snL88wkX.js +1 -0
  463. ksadk/studio/static/assets/cynefin-OW5HDTMX-Bd1YhRo1.js +1 -0
  464. ksadk/studio/static/assets/cynefinDiagram-5FMLGOSQ-BBrIG_mI.js +62 -0
  465. ksadk/studio/static/assets/cytoscape.esm-CGd-uY3x.js +321 -0
  466. ksadk/studio/static/assets/dagre-GXQ25YYZ-DtF1nGdY.js +4 -0
  467. ksadk/studio/static/assets/dagre-PrKaheQc.js +1 -0
  468. ksadk/studio/static/assets/defaultLocale-BFoDCU3G.js +1 -0
  469. ksadk/studio/static/assets/diagram-S7CK7UJ4-0BZ98mya.js +30 -0
  470. ksadk/studio/static/assets/diagram-UQ7AKVKN-6mLKEo6L.js +41 -0
  471. ksadk/studio/static/assets/diagram-VSXAHHWV-CJBIaiZ2.js +3 -0
  472. ksadk/studio/static/assets/diagram-VX7I27RA-BohLEE1C.js +24 -0
  473. ksadk/studio/static/assets/diagram-Z3DM3KII-D8KIGonG.js +24 -0
  474. ksadk/studio/static/assets/dist-v5Q1xZ2K.js +1 -0
  475. ksadk/studio/static/assets/ebnfDiagram-PWID7BFC-C99HU61t.js +1 -0
  476. ksadk/studio/static/assets/erDiagram-RLTQ6QDP-DLGry2Ap.js +99 -0
  477. ksadk/studio/static/assets/eventmodeling-NTZA5JFV-B1_5Ac8j.js +1 -0
  478. ksadk/studio/static/assets/flowDiagram-HODETNUW-DD5hmicn.js +1 -0
  479. ksadk/studio/static/assets/ganttDiagram-EL5Y4UJY-BPJrVLSG.js +292 -0
  480. ksadk/studio/static/assets/gitGraph-4MIJSDKK-Cy_ZQBGi.js +1 -0
  481. ksadk/studio/static/assets/gitGraphDiagram-WWUBYQGX-CZCUz4MT.js +106 -0
  482. ksadk/studio/static/assets/graphlib-DS17s2tU.js +1 -0
  483. ksadk/studio/static/assets/index-CeHL9ARV.css +2 -0
  484. ksadk/studio/static/assets/index-D9QEcVpL.js +320 -0
  485. ksadk/studio/static/assets/info-A6RAGUB7-DBzDQB_I.js +1 -0
  486. ksadk/studio/static/assets/infoDiagram-27XIBGKW-B-sq07Hi.js +2 -0
  487. ksadk/studio/static/assets/init-C-OQMol4.js +1 -0
  488. ksadk/studio/static/assets/ishikawaDiagram-5VMMS53U-CljI2wyf.js +70 -0
  489. ksadk/studio/static/assets/journeyDiagram-3NMN7TZE-D50Fo2sL.js +139 -0
  490. ksadk/studio/static/assets/kanban-definition-UXKFOSKX-DimfOTam.js +89 -0
  491. ksadk/studio/static/assets/katex-DhON-x74.js +257 -0
  492. ksadk/studio/static/assets/line-CX5BOOUP.js +1 -0
  493. ksadk/studio/static/assets/linear-BCxN9JLv.js +1 -0
  494. ksadk/studio/static/assets/mermaid-parser.core-BdCz8fkS.js +7 -0
  495. ksadk/studio/static/assets/mindmap-definition-YA3MSWOX-Bizzqy5R.js +96 -0
  496. ksadk/studio/static/assets/ordinal-BDEzSJ7C.js +1 -0
  497. ksadk/studio/static/assets/packet-AYTQ26CC-gPln-_kG.js +1 -0
  498. ksadk/studio/static/assets/path-fybaL0A-.js +1 -0
  499. ksadk/studio/static/assets/pegDiagram-XKGWAZYB-Dy3fB-gW.js +1 -0
  500. ksadk/studio/static/assets/pie-WAS4IAKB-qZdHMr7B.js +1 -0
  501. ksadk/studio/static/assets/pieDiagram-E7YTZNPT-B9mrFSDF.js +39 -0
  502. ksadk/studio/static/assets/quadrantDiagram-AXDQQJYC-NzUfCbMR.js +7 -0
  503. ksadk/studio/static/assets/radar-RG4KPBEZ-BZlnaea9.js +1 -0
  504. ksadk/studio/static/assets/railroad-74A4TZTK-Ce3__H9c.js +1 -0
  505. ksadk/studio/static/assets/railroad-abnf-HS5TGJTU-DqQn7EzD.js +1 -0
  506. ksadk/studio/static/assets/railroad-ebnf-LZEXJU2U-B55kCA8F.js +1 -0
  507. ksadk/studio/static/assets/railroad-peg-WCYAUIDC-DfQnvkHd.js +1 -0
  508. ksadk/studio/static/assets/railroadDiagram-O6MQD6OU-DnhMjZCk.js +1 -0
  509. ksadk/studio/static/assets/requirementDiagram-BXWQKSXE-BF3d103U.js +84 -0
  510. ksadk/studio/static/assets/rolldown-runtime-Dy4uBu1J-DK3Fl9T5.js +1 -0
  511. ksadk/studio/static/assets/rough.esm-Dy-Kn_BL.js +1 -0
  512. ksadk/studio/static/assets/sankeyDiagram-P5KCCOFB-Bo-JTwt0.js +40 -0
  513. ksadk/studio/static/assets/sequenceDiagram-WJ2MYXX4-Dh4wZtUm.js +162 -0
  514. ksadk/studio/static/assets/sizeCapture-INFHLROL-B0uUizjq.js +1 -0
  515. ksadk/studio/static/assets/src-SFn9St9H.js +1 -0
  516. ksadk/studio/static/assets/stateDiagram-D77RDMKH-DH774G6d.js +1 -0
  517. ksadk/studio/static/assets/stateDiagram-v2-MP3YSRHH-DZvVWPBo.js +1 -0
  518. ksadk/studio/static/assets/swimlanes-42K2YHIH-Bl13OvTX.js +1 -0
  519. ksadk/studio/static/assets/swimlanesDiagram-VR7AAH4N-BnFOTALJ.js +8 -0
  520. ksadk/studio/static/assets/timeline-definition-24CTP7MA-DxmSSjRB.js +120 -0
  521. ksadk/studio/static/assets/treeView-Q6P3EWNA-D5Nn9Swz.js +1 -0
  522. ksadk/studio/static/assets/treemap-WGGIJYW6-CgHgnQhE.js +1 -0
  523. ksadk/studio/static/assets/vennDiagram-4TSXK5OY-B9QEXcJq.js +34 -0
  524. ksadk/studio/static/assets/wardley-WFR3VGLG-DXrK2RRY.js +1 -0
  525. ksadk/studio/static/assets/wardleyDiagram-VM6X3IG4-Ba1AOWoe.js +78 -0
  526. ksadk/studio/static/assets/xychartDiagram-S5SC5T6Z-Cr5pt5Y-.js +7 -0
  527. ksadk/studio/static/index.html +2 -2
  528. ksadk/studio/validator.py +1 -1
  529. ksadk/studio/workspace.py +1 -0
  530. ksadk/version.py +1 -1
  531. ksadk_runtime_common/workspace_files/path_utils.py +41 -0
  532. agentengine_sdk_python-0.8.2.dist-info/RECORD +0 -778
  533. ksadk/server/static/assets/architecture-7GRP2DOG-DjKratdf.js +0 -1
  534. ksadk/server/static/assets/channel-4cQHKtx1.js +0 -1
  535. ksadk/server/static/assets/chunk-2Q5K7J3B-DmgWkESh.js +0 -1
  536. ksadk/server/static/assets/classDiagram-DTDB5LWJ-DLFd9Xi0.js +0 -1
  537. ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-DLFd9Xi0.js +0 -1
  538. ksadk/server/static/assets/cynefin-OW5HDTMX-DjEM48Qp.js +0 -1
  539. ksadk/server/static/assets/eventmodeling-NTZA5JFV-CMgMaJrC.js +0 -1
  540. ksadk/server/static/assets/flowDiagram-A5DVABFB-BBKL0x3P.js +0 -1
  541. ksadk/server/static/assets/gitGraph-4MIJSDKK-Dw63mrhw.js +0 -1
  542. ksadk/server/static/assets/index-8ipRcQ-M.js +0 -240
  543. ksadk/server/static/assets/index-ByFCqjlh.css +0 -2
  544. ksadk/server/static/assets/info-A6RAGUB7-B6iAblII.js +0 -1
  545. ksadk/server/static/assets/packet-AYTQ26CC-BOYAipuq.js +0 -1
  546. ksadk/server/static/assets/pie-WAS4IAKB-Ctq0Kbku.js +0 -1
  547. ksadk/server/static/assets/radar-RG4KPBEZ-DN0-ul_0.js +0 -1
  548. ksadk/server/static/assets/railroad-74A4TZTK-C6cC_to0.js +0 -1
  549. ksadk/server/static/assets/railroad-abnf-HS5TGJTU-BimjIuRc.js +0 -1
  550. ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-CvY65ABq.js +0 -1
  551. ksadk/server/static/assets/railroad-peg-WCYAUIDC-D72Iq_Tv.js +0 -1
  552. ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BgQY03nz.js +0 -1
  553. ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-DLVw3q5Q.js +0 -8
  554. ksadk/server/static/assets/treeView-Q6P3EWNA-BMpO0wsB.js +0 -1
  555. ksadk/server/static/assets/treemap-WGGIJYW6-Et0aqzU3.js +0 -1
  556. ksadk/server/static/assets/wardley-WFR3VGLG-CbY0mhpl.js +0 -1
  557. ksadk/studio/static/assets/PrismRenderer-IYN-ffww.js +0 -3
  558. ksadk/studio/static/assets/index-BLpcdrgW.css +0 -1
  559. ksadk/studio/static/assets/index-CYekR2Xv.js +0 -260
  560. {agentengine_sdk_python-0.8.2.dist-info → agentengine_sdk_python-0.8.4.dist-info}/WHEEL +0 -0
  561. {agentengine_sdk_python-0.8.2.dist-info → agentengine_sdk_python-0.8.4.dist-info}/entry_points.txt +0 -0
  562. {agentengine_sdk_python-0.8.2.dist-info → agentengine_sdk_python-0.8.4.dist-info}/licenses/LICENSE +0 -0
  563. {agentengine_sdk_python-0.8.2.dist-info → agentengine_sdk_python-0.8.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,983 @@
1
+ """Parse and snapshot the file-based data plane of a Codex plugin.
2
+
3
+ Codex App Server remains the lifecycle authority for marketplaces and plugin
4
+ installation. This module starts at the installed plugin root: it validates
5
+ the official ``.codex-plugin/plugin.json`` contract, resolves only contained
6
+ component paths, and records deterministic content digests for admission into
7
+ KsADK's immutable build contracts.
8
+
9
+ The upstream manifest is allowed to grow new fields. Fields understood here
10
+ remain strictly typed, and spelling aliases are normalized only for the
11
+ documented camelCase form and KsADK's legacy snake_case form. Supplying both
12
+ spellings is rejected instead of silently choosing one.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import hashlib
18
+ import json
19
+ import re
20
+ import stat
21
+ from collections.abc import Iterable, Mapping
22
+ from pathlib import Path, PurePosixPath
23
+ from typing import TYPE_CHECKING, Any, Literal
24
+ from urllib.parse import urlsplit
25
+
26
+ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
27
+
28
+ if TYPE_CHECKING:
29
+ from ksadk.plugins.contracts import LockedPluginComponent, PluginSourceSnapshot
30
+
31
+ _MANIFEST_RELATIVE_PATH = PurePosixPath(".codex-plugin/plugin.json")
32
+ _MAX_JSON_BYTES = 4 * 1024 * 1024
33
+ _MAX_ARTIFACT_BYTES = 256 * 1024 * 1024
34
+ _MAX_ARTIFACT_FILES = 20_000
35
+ _SEMVER = re.compile(
36
+ r"^(0|[1-9]\d*)\."
37
+ r"(0|[1-9]\d*)\."
38
+ r"(0|[1-9]\d*)"
39
+ r"(?:-(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\."
40
+ r"(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*)?"
41
+ r"(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$"
42
+ )
43
+ _PLUGIN_NAME = re.compile(r"^[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)*$")
44
+ _DIGEST = re.compile(r"^sha256:[0-9a-f]{64}$")
45
+
46
+
47
+ class CodexPluginManifestError(ValueError):
48
+ """The installed Codex plugin data plane is malformed or unsafe."""
49
+
50
+
51
+ def _to_camel(value: str) -> str:
52
+ head, *tail = value.split("_")
53
+ return head + "".join(part.capitalize() for part in tail)
54
+
55
+
56
+ class _CodexSnapshotModel(BaseModel):
57
+ model_config = ConfigDict(
58
+ alias_generator=_to_camel,
59
+ populate_by_name=True,
60
+ extra="forbid",
61
+ frozen=True,
62
+ )
63
+
64
+
65
+ class _CodexUpstreamModel(_CodexSnapshotModel):
66
+ # Native manifests and companion objects are versioned by Codex, not by
67
+ # KsADK. Preserve additive fields while validating every field we consume.
68
+ model_config = ConfigDict(
69
+ alias_generator=_to_camel,
70
+ populate_by_name=True,
71
+ extra="allow",
72
+ frozen=True,
73
+ )
74
+
75
+
76
+ def _normalize_aliases(
77
+ value: Any,
78
+ aliases: Mapping[str, str],
79
+ *,
80
+ label: str,
81
+ ) -> Any:
82
+ if not isinstance(value, Mapping):
83
+ return value
84
+ normalized = dict(value)
85
+ for snake_name, camel_name in aliases.items():
86
+ if snake_name in normalized and camel_name in normalized:
87
+ raise ValueError(f"{label} cannot contain both {camel_name!r} and {snake_name!r}")
88
+ # Pydantic accepts the declared alias and field name. Leave the
89
+ # caller's spelling intact so validation errors point at the source
90
+ # document rather than at a rewritten key.
91
+ return normalized
92
+
93
+
94
+ def _non_empty(value: str, *, field: str) -> str:
95
+ if not value or value != value.strip() or any(ord(character) < 32 for character in value):
96
+ raise ValueError(f"{field} must be trimmed, non-empty printable text")
97
+ return value
98
+
99
+
100
+ def _relative_reference(
101
+ value: str,
102
+ *,
103
+ field: str,
104
+ require_dot_prefix: bool = False,
105
+ ) -> str:
106
+ _non_empty(value, field=field)
107
+ if "\\" in value or "\x00" in value:
108
+ raise ValueError(f"{field} must use a relative POSIX path")
109
+ if require_dot_prefix and not value.startswith("./"):
110
+ raise ValueError(f"{field} must start with './' relative to the plugin root")
111
+ path = PurePosixPath(value)
112
+ if path.is_absolute() or not path.parts or any(part == ".." for part in path.parts):
113
+ raise ValueError(f"{field} must stay relative to the plugin root")
114
+ normalized = path.as_posix()
115
+ if normalized in {"", "."}:
116
+ raise ValueError(f"{field} must name a component inside the plugin root")
117
+ return value
118
+
119
+
120
+ class CodexPluginAuthor(_CodexUpstreamModel):
121
+ name: str
122
+ email: str | None = None
123
+ url: str | None = None
124
+
125
+ @field_validator("name")
126
+ @classmethod
127
+ def validate_name(cls, value: str) -> str:
128
+ return _non_empty(value, field="author.name")
129
+
130
+ @field_validator("email", "url")
131
+ @classmethod
132
+ def validate_optional_text(cls, value: str | None) -> str | None:
133
+ return None if value is None else _non_empty(value, field="author metadata")
134
+
135
+
136
+ class CodexPluginInterface(_CodexUpstreamModel):
137
+ display_name: str | None = Field(default=None, alias="displayName")
138
+ short_description: str | None = Field(default=None, alias="shortDescription")
139
+ long_description: str | None = Field(default=None, alias="longDescription")
140
+ developer_name: str | None = Field(default=None, alias="developerName")
141
+ category: str | None = None
142
+ capabilities: tuple[str, ...] = ()
143
+ website_url: str | None = Field(default=None, alias="websiteURL")
144
+ privacy_policy_url: str | None = Field(default=None, alias="privacyPolicyURL")
145
+ terms_of_service_url: str | None = Field(default=None, alias="termsOfServiceURL")
146
+ default_prompt: str | tuple[str, ...] | None = Field(default=None, alias="defaultPrompt")
147
+ brand_color: str | None = Field(default=None, alias="brandColor")
148
+ composer_icon: str | None = Field(default=None, alias="composerIcon")
149
+ logo: str | None = None
150
+ logo_dark: str | None = Field(default=None, alias="logoDark")
151
+ screenshots: tuple[str, ...] = ()
152
+
153
+ @model_validator(mode="before")
154
+ @classmethod
155
+ def normalize_spelling(cls, value: Any) -> Any:
156
+ return _normalize_aliases(
157
+ value,
158
+ {
159
+ "display_name": "displayName",
160
+ "short_description": "shortDescription",
161
+ "long_description": "longDescription",
162
+ "developer_name": "developerName",
163
+ "website_url": "websiteURL",
164
+ "privacy_policy_url": "privacyPolicyURL",
165
+ "terms_of_service_url": "termsOfServiceURL",
166
+ "default_prompt": "defaultPrompt",
167
+ "brand_color": "brandColor",
168
+ "composer_icon": "composerIcon",
169
+ "logo_dark": "logoDark",
170
+ },
171
+ label="plugin.json interface",
172
+ )
173
+
174
+ @field_validator(
175
+ "display_name",
176
+ "short_description",
177
+ "long_description",
178
+ "developer_name",
179
+ "category",
180
+ "website_url",
181
+ "privacy_policy_url",
182
+ "terms_of_service_url",
183
+ "brand_color",
184
+ )
185
+ @classmethod
186
+ def validate_optional_text(cls, value: str | None) -> str | None:
187
+ return None if value is None else _non_empty(value, field="interface field")
188
+
189
+ @field_validator("capabilities", "screenshots")
190
+ @classmethod
191
+ def validate_text_tuple(cls, value: tuple[str, ...]) -> tuple[str, ...]:
192
+ for item in value:
193
+ _non_empty(item, field="interface list entry")
194
+ return value
195
+
196
+ @field_validator("default_prompt")
197
+ @classmethod
198
+ def validate_prompt(cls, value: str | tuple[str, ...] | None) -> str | tuple[str, ...] | None:
199
+ if isinstance(value, str):
200
+ return _non_empty(value, field="interface.defaultPrompt")
201
+ if isinstance(value, tuple):
202
+ for item in value:
203
+ _non_empty(item, field="interface.defaultPrompt entry")
204
+ return value
205
+
206
+ @field_validator("composer_icon", "logo", "logo_dark")
207
+ @classmethod
208
+ def validate_optional_asset_path(cls, value: str | None) -> str | None:
209
+ return (
210
+ None
211
+ if value is None
212
+ else _relative_reference(
213
+ value,
214
+ field="interface asset",
215
+ require_dot_prefix=True,
216
+ )
217
+ )
218
+
219
+ @field_validator("screenshots")
220
+ @classmethod
221
+ def validate_screenshot_paths(cls, value: tuple[str, ...]) -> tuple[str, ...]:
222
+ for item in value:
223
+ _relative_reference(
224
+ item,
225
+ field="interface screenshot",
226
+ require_dot_prefix=True,
227
+ )
228
+ return value
229
+
230
+
231
+ class CodexPluginManifest(_CodexUpstreamModel):
232
+ """Normalized official manifest.
233
+
234
+ Only ``name`` is universally required by the native format. Installed
235
+ official plugins exist with deliberately minimal metadata, so optional
236
+ presentation fields cannot be promoted to KsADK-only requirements.
237
+ """
238
+
239
+ name: str
240
+ version: str | None = None
241
+ id: str | None = None
242
+ description: str | None = None
243
+ author: CodexPluginAuthor | None = None
244
+ homepage: str | None = None
245
+ repository: str | None = None
246
+ license: str | None = None
247
+ keywords: tuple[str, ...] = ()
248
+ skills: str | tuple[str, ...] | None = None
249
+ hooks: str | tuple[str, ...] | dict[str, Any] | tuple[dict[str, Any], ...] | None = None
250
+ mcp_servers: str | dict[str, dict[str, Any]] | None = Field(
251
+ default=None,
252
+ alias="mcpServers",
253
+ )
254
+ apps: str | None = None
255
+ interface: CodexPluginInterface | None = None
256
+ bundled_content_variant: str | None = Field(
257
+ default=None,
258
+ alias="bundledContentVariant",
259
+ )
260
+
261
+ @model_validator(mode="before")
262
+ @classmethod
263
+ def normalize_spelling(cls, value: Any) -> Any:
264
+ return _normalize_aliases(
265
+ value,
266
+ {
267
+ "mcp_servers": "mcpServers",
268
+ "bundled_content_variant": "bundledContentVariant",
269
+ },
270
+ label="plugin.json",
271
+ )
272
+
273
+ @field_validator("name")
274
+ @classmethod
275
+ def validate_name(cls, value: str) -> str:
276
+ _non_empty(value, field="plugin.json name")
277
+ if len(value) > 128 or _PLUGIN_NAME.fullmatch(value) is None:
278
+ raise ValueError("plugin.json name is not a valid Codex plugin identifier")
279
+ return value
280
+
281
+ @field_validator("version")
282
+ @classmethod
283
+ def validate_version(cls, value: str | None) -> str | None:
284
+ if value is not None and _SEMVER.fullmatch(value) is None:
285
+ raise ValueError("plugin.json version must be exact semantic version text")
286
+ return value
287
+
288
+ @field_validator(
289
+ "id",
290
+ "description",
291
+ "homepage",
292
+ "repository",
293
+ "license",
294
+ "bundled_content_variant",
295
+ )
296
+ @classmethod
297
+ def validate_optional_text(cls, value: str | None) -> str | None:
298
+ return None if value is None else _non_empty(value, field="plugin.json field")
299
+
300
+ @field_validator("keywords")
301
+ @classmethod
302
+ def validate_keywords(cls, value: tuple[str, ...]) -> tuple[str, ...]:
303
+ for item in value:
304
+ _non_empty(item, field="plugin.json keyword")
305
+ if len(value) != len(set(value)):
306
+ raise ValueError("plugin.json keywords must be unique")
307
+ return value
308
+
309
+ @field_validator("apps")
310
+ @classmethod
311
+ def validate_component_path(cls, value: str | None) -> str | None:
312
+ return (
313
+ None
314
+ if value is None
315
+ else _relative_reference(
316
+ value,
317
+ field="component path",
318
+ require_dot_prefix=True,
319
+ )
320
+ )
321
+
322
+ @field_validator("skills", mode="before")
323
+ @classmethod
324
+ def validate_skill_paths(cls, value: Any) -> Any:
325
+ if value is None:
326
+ return None
327
+ paths = [value] if isinstance(value, str) else value
328
+ if (
329
+ not isinstance(paths, (list, tuple))
330
+ or not paths
331
+ or not all(isinstance(item, str) for item in paths)
332
+ ):
333
+ raise ValueError("skills must be a path or non-empty array of paths")
334
+ normalized = tuple(
335
+ _relative_reference(
336
+ item,
337
+ field="skills",
338
+ require_dot_prefix=True,
339
+ )
340
+ for item in paths
341
+ )
342
+ return normalized[0] if isinstance(value, str) else normalized
343
+
344
+ @field_validator("hooks", mode="before")
345
+ @classmethod
346
+ def validate_hook_declaration(cls, value: Any) -> Any:
347
+ if value is None or isinstance(value, dict):
348
+ return value
349
+ if isinstance(value, str):
350
+ return _relative_reference(
351
+ value,
352
+ field="hooks",
353
+ require_dot_prefix=True,
354
+ )
355
+ if not isinstance(value, (list, tuple)) or not value:
356
+ raise ValueError(
357
+ "hooks must be a path, path array, inline object, or inline object array"
358
+ )
359
+ if all(isinstance(item, str) for item in value):
360
+ return tuple(
361
+ _relative_reference(
362
+ item,
363
+ field="hooks",
364
+ require_dot_prefix=True,
365
+ )
366
+ for item in value
367
+ )
368
+ if all(isinstance(item, dict) for item in value):
369
+ return tuple(dict(item) for item in value)
370
+ raise ValueError("hooks arrays cannot mix paths and inline hook objects")
371
+
372
+ @field_validator("mcp_servers")
373
+ @classmethod
374
+ def validate_mcp_declaration(
375
+ cls, value: str | dict[str, dict[str, Any]] | None
376
+ ) -> str | dict[str, dict[str, Any]] | None:
377
+ if isinstance(value, str):
378
+ return _relative_reference(
379
+ value,
380
+ field="mcpServers",
381
+ require_dot_prefix=True,
382
+ )
383
+ if isinstance(value, dict):
384
+ _validate_named_object_map(value, label="plugin.json mcpServers")
385
+ return value
386
+
387
+
388
+ class CodexPluginSourceCoordinate(_CodexSnapshotModel):
389
+ """Credential-free requested and resolved native source coordinate."""
390
+
391
+ coordinate_format: Literal["codex.plugin-source/v1"] = "codex.plugin-source/v1"
392
+ type: Literal["local", "git", "npm", "remote"]
393
+ requested: str = Field(min_length=1, max_length=2048)
394
+ resolved: str = Field(min_length=1, max_length=2048)
395
+ marketplace_name: str | None = Field(default=None, max_length=256)
396
+ registry: str | None = Field(default=None, max_length=2048)
397
+ integrity: str | None = Field(default=None, max_length=512)
398
+
399
+ @field_validator("requested", "resolved", "marketplace_name", "registry", "integrity")
400
+ @classmethod
401
+ def validate_coordinate_text(cls, value: str | None) -> str | None:
402
+ if value is None:
403
+ return None
404
+ _non_empty(value, field="source coordinate")
405
+ if "://" in value:
406
+ parsed = urlsplit(value)
407
+ if parsed.username or parsed.password or parsed.query or parsed.fragment:
408
+ raise ValueError("source coordinates must not embed credentials or query data")
409
+ return value
410
+
411
+ def to_plugin_source_snapshot(self) -> PluginSourceSnapshot:
412
+ """Project into the generic lock model without coupling the parser to it."""
413
+
414
+ from ksadk.plugins.contracts import PluginSourceSnapshot
415
+
416
+ source_type: Literal["local", "git", "npm", "market"]
417
+ source_type = "market" if self.type == "remote" else self.type
418
+ return PluginSourceSnapshot(
419
+ ecosystem="codex",
420
+ type=source_type,
421
+ requested=self.requested,
422
+ resolved=self.resolved,
423
+ integrity=self.integrity,
424
+ marketplace=self.marketplace_name,
425
+ registry=self.registry,
426
+ )
427
+
428
+
429
+ class CodexPluginComponentSnapshot(_CodexSnapshotModel):
430
+ component_format: Literal["codex.plugin-component/v1"] = "codex.plugin-component/v1"
431
+ kind: Literal["skill", "mcp", "hook", "app"]
432
+ name: str = Field(min_length=1, max_length=256)
433
+ path: str | None = Field(default=None, max_length=1024)
434
+ content_digest: str
435
+
436
+ @field_validator("name")
437
+ @classmethod
438
+ def validate_component_name(cls, value: str) -> str:
439
+ return _non_empty(value, field="component name")
440
+
441
+ @field_validator("path")
442
+ @classmethod
443
+ def validate_component_path(cls, value: str | None) -> str | None:
444
+ return None if value is None else _relative_reference(value, field="component path")
445
+
446
+ @field_validator("content_digest")
447
+ @classmethod
448
+ def validate_digest(cls, value: str) -> str:
449
+ if _DIGEST.fullmatch(value) is None:
450
+ raise ValueError("component contentDigest must be a lowercase sha256 digest")
451
+ return value
452
+
453
+ def to_locked_component(self) -> LockedPluginComponent:
454
+ from ksadk.plugins.contracts import LockedPluginComponent
455
+
456
+ return LockedPluginComponent(
457
+ id=self.name,
458
+ kind=self.kind,
459
+ digest=self.content_digest,
460
+ path=self.path,
461
+ )
462
+
463
+
464
+ class CodexInstalledPluginSnapshot(_CodexSnapshotModel):
465
+ snapshot_format: Literal["codex.installed-plugin-snapshot/v1"] = (
466
+ "codex.installed-plugin-snapshot/v1"
467
+ )
468
+ installed_root: str = Field(min_length=1, max_length=4096)
469
+ source: CodexPluginSourceCoordinate
470
+ manifest: CodexPluginManifest
471
+ manifest_digest: str
472
+ artifact_digest: str
473
+ components: tuple[CodexPluginComponentSnapshot, ...] = ()
474
+
475
+ @field_validator("manifest_digest", "artifact_digest")
476
+ @classmethod
477
+ def validate_digest(cls, value: str) -> str:
478
+ if _DIGEST.fullmatch(value) is None:
479
+ raise ValueError("snapshot digests must be lowercase sha256 digests")
480
+ return value
481
+
482
+ @field_validator("components")
483
+ @classmethod
484
+ def validate_components(
485
+ cls, value: tuple[CodexPluginComponentSnapshot, ...]
486
+ ) -> tuple[CodexPluginComponentSnapshot, ...]:
487
+ identities = [(component.kind, component.name) for component in value]
488
+ if len(identities) != len(set(identities)):
489
+ raise ValueError("component kind/name identities must be unique")
490
+ return tuple(sorted(value, key=lambda item: (item.kind, item.name, item.path or "")))
491
+
492
+
493
+ def _sha256(raw: bytes) -> str:
494
+ return "sha256:" + hashlib.sha256(raw).hexdigest()
495
+
496
+
497
+ def _canonical_json_digest(value: Any) -> str:
498
+ return _sha256(
499
+ json.dumps(
500
+ value,
501
+ sort_keys=True,
502
+ separators=(",", ":"),
503
+ ensure_ascii=False,
504
+ ).encode("utf-8")
505
+ )
506
+
507
+
508
+ def _validate_root(plugin_root: Path) -> Path:
509
+ root = Path(plugin_root).expanduser()
510
+ if root.is_symlink():
511
+ raise CodexPluginManifestError("installed plugin root must not be a symlink")
512
+ if not root.is_dir():
513
+ raise CodexPluginManifestError("installed plugin root must be an existing directory")
514
+ return root.resolve()
515
+
516
+
517
+ def _contained_path(
518
+ root: Path,
519
+ raw_path: str | PurePosixPath,
520
+ *,
521
+ label: str,
522
+ expected: Literal["file", "directory", "either"] = "either",
523
+ ) -> Path:
524
+ relative_text = raw_path.as_posix() if isinstance(raw_path, PurePosixPath) else raw_path
525
+ try:
526
+ _relative_reference(relative_text, field=label)
527
+ except ValueError as exc:
528
+ raise CodexPluginManifestError(str(exc)) from exc
529
+ relative = PurePosixPath(relative_text)
530
+ cursor = root
531
+ for part in relative.parts:
532
+ if part == ".":
533
+ continue
534
+ cursor /= part
535
+ if cursor.is_symlink():
536
+ raise CodexPluginManifestError(f"{label} must not traverse a symlink: {relative}")
537
+ try:
538
+ resolved = cursor.resolve(strict=True)
539
+ resolved.relative_to(root)
540
+ except (OSError, ValueError) as exc:
541
+ raise CodexPluginManifestError(
542
+ f"{label} does not resolve to an existing path inside the plugin root: {relative}"
543
+ ) from exc
544
+ if expected == "file" and not resolved.is_file():
545
+ raise CodexPluginManifestError(f"{label} must resolve to a regular file: {relative}")
546
+ if expected == "directory" and not resolved.is_dir():
547
+ raise CodexPluginManifestError(f"{label} must resolve to a directory: {relative}")
548
+ if expected == "either" and not (resolved.is_file() or resolved.is_dir()):
549
+ raise CodexPluginManifestError(f"{label} must resolve to a regular file or directory")
550
+ return resolved
551
+
552
+
553
+ def _read_json_object(path: Path, *, root: Path, label: str) -> tuple[dict[str, Any], bytes]:
554
+ relative = path.relative_to(root).as_posix()
555
+ path = _contained_path(root, relative, label=label, expected="file")
556
+ try:
557
+ stat = path.stat()
558
+ if stat.st_size > _MAX_JSON_BYTES:
559
+ raise CodexPluginManifestError(f"{label} exceeds {_MAX_JSON_BYTES} bytes")
560
+ raw = path.read_bytes()
561
+ value = json.loads(raw)
562
+ except CodexPluginManifestError:
563
+ raise
564
+ except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
565
+ raise CodexPluginManifestError(f"{label} must contain valid UTF-8 JSON") from exc
566
+ if not isinstance(value, dict):
567
+ raise CodexPluginManifestError(f"{label} must contain one JSON object")
568
+ return value, raw
569
+
570
+
571
+ def _validate_named_object_map(value: Any, *, label: str) -> dict[str, dict[str, Any]]:
572
+ if not isinstance(value, dict):
573
+ raise CodexPluginManifestError(f"{label} must be an object")
574
+ normalized: dict[str, dict[str, Any]] = {}
575
+ for name, config in value.items():
576
+ if not isinstance(name, str):
577
+ raise CodexPluginManifestError(f"{label} names must be strings")
578
+ try:
579
+ _non_empty(name, field=f"{label} name")
580
+ except ValueError as exc:
581
+ raise CodexPluginManifestError(str(exc)) from exc
582
+ if not isinstance(config, dict):
583
+ raise CodexPluginManifestError(f"{label} entry {name!r} must be an object")
584
+ normalized[name] = config
585
+ return normalized
586
+
587
+
588
+ def _component_paths(root: Path, manifest: CodexPluginManifest) -> dict[str, tuple[Path, ...]]:
589
+ paths: dict[str, list[Path]] = {"skills": [], "mcp": [], "hooks": [], "apps": []}
590
+
591
+ def add(
592
+ kind: str,
593
+ declarations: Iterable[str],
594
+ defaults: Iterable[str],
595
+ *,
596
+ expected: Literal["file", "directory", "either"],
597
+ ) -> None:
598
+ default_paths = tuple(defaults)
599
+ raw_paths = (*default_paths, *declarations)
600
+ seen: set[Path] = set()
601
+ for raw_path in raw_paths:
602
+ candidate = root / PurePosixPath(raw_path)
603
+ if raw_path in default_paths and not candidate.exists():
604
+ continue
605
+ resolved = _contained_path(
606
+ root,
607
+ raw_path,
608
+ label=f"{kind} component path",
609
+ expected=expected,
610
+ )
611
+ if resolved not in seen:
612
+ paths[kind].append(resolved)
613
+ seen.add(resolved)
614
+
615
+ skill_paths = (
616
+ (manifest.skills,) if isinstance(manifest.skills, str) else tuple(manifest.skills or ())
617
+ )
618
+ add("skills", skill_paths, (), expected="either")
619
+ mcp_path = manifest.mcp_servers if isinstance(manifest.mcp_servers, str) else None
620
+ add("mcp", (mcp_path,) if mcp_path else (), (), expected="file")
621
+ hook_paths: tuple[str, ...]
622
+ if isinstance(manifest.hooks, str):
623
+ hook_paths = (manifest.hooks,)
624
+ elif isinstance(manifest.hooks, tuple) and all(
625
+ isinstance(item, str) for item in manifest.hooks
626
+ ):
627
+ hook_paths = manifest.hooks
628
+ else:
629
+ hook_paths = ()
630
+ hook_defaults = ("./hooks/hooks.json",) if manifest.hooks is None else ()
631
+ add("hooks", hook_paths, hook_defaults, expected="file")
632
+ add("apps", (manifest.apps,) if manifest.apps else (), (), expected="file")
633
+ return {kind: tuple(value) for kind, value in paths.items()}
634
+
635
+
636
+ def _load_mcp_servers(
637
+ root: Path,
638
+ manifest: CodexPluginManifest,
639
+ paths: tuple[Path, ...],
640
+ ) -> list[tuple[str, dict[str, Any], str | None]]:
641
+ entries: list[tuple[str, dict[str, Any], str | None]] = []
642
+ if isinstance(manifest.mcp_servers, dict):
643
+ for name, config in _validate_named_object_map(
644
+ manifest.mcp_servers, label="plugin.json mcpServers"
645
+ ).items():
646
+ entries.append((name, config, None))
647
+ for path in paths:
648
+ payload, _raw = _read_json_object(path, root=root, label="Codex MCP manifest")
649
+ if "mcpServers" in payload and "mcp_servers" in payload:
650
+ raise CodexPluginManifestError(
651
+ "Codex MCP manifest cannot contain both 'mcpServers' and 'mcp_servers'"
652
+ )
653
+ if "mcpServers" in payload:
654
+ servers = payload["mcpServers"]
655
+ unexpected = set(payload) - {"mcpServers"}
656
+ elif "mcp_servers" in payload:
657
+ servers = payload["mcp_servers"]
658
+ unexpected = set(payload) - {"mcp_servers"}
659
+ else:
660
+ servers = payload
661
+ unexpected = set()
662
+ if unexpected:
663
+ raise CodexPluginManifestError(
664
+ "Codex MCP manifest wrapper cannot contain unrelated fields: "
665
+ + ", ".join(sorted(unexpected))
666
+ )
667
+ relative = path.relative_to(root).as_posix()
668
+ for name, config in _validate_named_object_map(
669
+ servers, label="Codex MCP manifest servers"
670
+ ).items():
671
+ entries.append((name, config, relative))
672
+ _reject_duplicate_names(entries, label="MCP server")
673
+ return entries
674
+
675
+
676
+ def _load_named_companion(
677
+ root: Path,
678
+ paths: tuple[Path, ...],
679
+ *,
680
+ key: str,
681
+ label: str,
682
+ ) -> list[tuple[str, dict[str, Any], str]]:
683
+ entries: list[tuple[str, dict[str, Any], str]] = []
684
+ for path in paths:
685
+ payload, _raw = _read_json_object(path, root=root, label=label)
686
+ if key not in payload:
687
+ raise CodexPluginManifestError(f"{label} must contain a {key!r} object")
688
+ unexpected = set(payload) - {key}
689
+ if unexpected:
690
+ raise CodexPluginManifestError(
691
+ f"{label} cannot contain unrelated fields: " + ", ".join(sorted(unexpected))
692
+ )
693
+ relative = path.relative_to(root).as_posix()
694
+ named_entries = _validate_named_object_map(payload[key], label=f"{label} {key}")
695
+ for name, config in named_entries.items():
696
+ entries.append((name, config, relative))
697
+ _reject_duplicate_names(entries, label=key.rstrip("s"))
698
+ return entries
699
+
700
+
701
+ def _load_hooks(
702
+ root: Path,
703
+ manifest: CodexPluginManifest,
704
+ paths: tuple[Path, ...],
705
+ ) -> list[tuple[str, dict[str, Any], str | None]]:
706
+ grouped: dict[str, list[dict[str, Any]]] = {}
707
+ sources: dict[str, set[str | None]] = {}
708
+
709
+ def append_payload(payload: dict[str, Any], *, relative: str | None) -> None:
710
+ unexpected = set(payload) - {"description", "hooks"}
711
+ if unexpected:
712
+ raise CodexPluginManifestError(
713
+ "Codex hooks manifest cannot contain unrelated fields: "
714
+ + ", ".join(sorted(unexpected))
715
+ )
716
+ description = payload.get("description")
717
+ if description is not None and (
718
+ not isinstance(description, str) or not description.strip()
719
+ ):
720
+ raise CodexPluginManifestError(
721
+ "Codex hooks manifest description must be a non-empty string"
722
+ )
723
+ if "hooks" not in payload or not isinstance(payload["hooks"], dict):
724
+ raise CodexPluginManifestError("Codex hooks manifest must contain a 'hooks' object")
725
+ for event, hooks in payload["hooks"].items():
726
+ if not isinstance(event, str) or not event.strip():
727
+ raise CodexPluginManifestError("Codex hook event names must be non-empty strings")
728
+ if not isinstance(hooks, list) or not all(isinstance(item, dict) for item in hooks):
729
+ raise CodexPluginManifestError(
730
+ f"Codex hooks for event {event!r} must be an array of objects"
731
+ )
732
+ grouped.setdefault(event, []).extend(hooks)
733
+ sources.setdefault(event, set()).add(relative)
734
+
735
+ inline: tuple[dict[str, Any], ...] = ()
736
+ if isinstance(manifest.hooks, dict):
737
+ inline = (manifest.hooks,)
738
+ elif isinstance(manifest.hooks, tuple) and all(
739
+ isinstance(item, dict) for item in manifest.hooks
740
+ ):
741
+ inline = manifest.hooks
742
+ for payload in inline:
743
+ append_payload(payload, relative=None)
744
+ for path in paths:
745
+ payload, _raw = _read_json_object(path, root=root, label="Codex hooks manifest")
746
+ relative = path.relative_to(root).as_posix()
747
+ append_payload(payload, relative=relative)
748
+ return [
749
+ (
750
+ event,
751
+ {"hooks": grouped[event]},
752
+ next(iter(sources[event]))
753
+ if len(sources[event]) == 1 and None not in sources[event]
754
+ else None,
755
+ )
756
+ for event in sorted(grouped)
757
+ ]
758
+
759
+
760
+ def _reject_duplicate_names(
761
+ entries: Iterable[tuple[str, dict[str, Any], str | None]], *, label: str
762
+ ) -> None:
763
+ seen: set[str] = set()
764
+ for name, _config, _path in entries:
765
+ if name in seen:
766
+ raise CodexPluginManifestError(f"duplicate {label} name: {name!r}")
767
+ seen.add(name)
768
+
769
+
770
+ def _iter_skill_roots(root: Path, paths: tuple[Path, ...]) -> list[tuple[str, Path]]:
771
+ skills: list[tuple[str, Path]] = []
772
+ names: set[str] = set()
773
+ for path in paths:
774
+ candidates: list[Path]
775
+ if path.is_file():
776
+ if path.name != "SKILL.md":
777
+ raise CodexPluginManifestError("a file-valued skills path must name SKILL.md")
778
+ candidates = [path.parent]
779
+ else:
780
+ direct_manifest = path / "SKILL.md"
781
+ if direct_manifest.is_file():
782
+ candidates = [path]
783
+ else:
784
+ candidates = [
785
+ child
786
+ for child in sorted(path.iterdir(), key=lambda item: item.name)
787
+ if not child.name.startswith(".") and child.is_dir()
788
+ ]
789
+ for skill_root in candidates:
790
+ relative = skill_root.relative_to(root).as_posix()
791
+ _contained_path(
792
+ root,
793
+ relative,
794
+ label="skill directory",
795
+ expected="directory",
796
+ )
797
+ skill_manifest = skill_root / "SKILL.md"
798
+ if not skill_manifest.is_file() or skill_manifest.is_symlink():
799
+ raise CodexPluginManifestError(f"skill {relative!r} is missing a regular SKILL.md")
800
+ name = skill_root.name
801
+ if name in names:
802
+ raise CodexPluginManifestError(f"duplicate skill directory name: {name!r}")
803
+ names.add(name)
804
+ skills.append((name, skill_root))
805
+ return skills
806
+
807
+
808
+ def _validate_interface_assets(root: Path, interface: CodexPluginInterface | None) -> None:
809
+ if interface is None:
810
+ return
811
+ references = [
812
+ interface.composer_icon,
813
+ interface.logo,
814
+ interface.logo_dark,
815
+ *interface.screenshots,
816
+ ]
817
+ for reference in references:
818
+ if reference is not None:
819
+ _contained_path(root, reference, label="interface asset", expected="file")
820
+
821
+
822
+ def _load_installed_plugin(
823
+ plugin_root: Path,
824
+ ) -> tuple[
825
+ Path,
826
+ CodexPluginManifest,
827
+ bytes,
828
+ list[tuple[str, Path]],
829
+ list[tuple[str, dict[str, Any], str | None]],
830
+ list[tuple[str, dict[str, Any], str | None]],
831
+ list[tuple[str, dict[str, Any], str]],
832
+ ]:
833
+ root = _validate_root(plugin_root)
834
+ manifest_path = root / _MANIFEST_RELATIVE_PATH
835
+ payload, raw = _read_json_object(
836
+ manifest_path,
837
+ root=root,
838
+ label=".codex-plugin/plugin.json",
839
+ )
840
+ try:
841
+ manifest = CodexPluginManifest.model_validate(payload)
842
+ except (TypeError, ValueError) as exc:
843
+ raise CodexPluginManifestError(f"invalid .codex-plugin/plugin.json: {exc}") from exc
844
+
845
+ paths = _component_paths(root, manifest)
846
+ skills = _iter_skill_roots(root, paths["skills"])
847
+ mcp_servers = _load_mcp_servers(root, manifest, paths["mcp"])
848
+ hooks = _load_hooks(root, manifest, paths["hooks"])
849
+ apps = _load_named_companion(
850
+ root,
851
+ paths["apps"],
852
+ key="apps",
853
+ label="Codex app manifest",
854
+ )
855
+ _validate_interface_assets(root, manifest.interface)
856
+ return root, manifest, raw, skills, mcp_servers, hooks, apps
857
+
858
+
859
+ def load_codex_plugin_manifest(plugin_root: Path) -> CodexPluginManifest:
860
+ """Load one installed Codex manifest and validate all referenced components."""
861
+
862
+ _root, manifest, _raw, _skills, _mcp, _hooks, _apps = _load_installed_plugin(plugin_root)
863
+ return manifest
864
+
865
+
866
+ def _tree_digest(path: Path) -> str:
867
+ digest = hashlib.sha256()
868
+ files: list[Path]
869
+ if path.is_file():
870
+ files = [path]
871
+ base = path.parent
872
+ else:
873
+ files = []
874
+ base = path
875
+ for candidate in sorted(
876
+ path.rglob("*"),
877
+ key=lambda item: item.relative_to(path).as_posix(),
878
+ ):
879
+ relative = candidate.relative_to(path).as_posix()
880
+ if candidate.is_symlink():
881
+ raise CodexPluginManifestError(
882
+ f"plugin snapshots must not include symlinks: {relative}"
883
+ )
884
+ if candidate.is_dir():
885
+ continue
886
+ if not candidate.is_file():
887
+ raise CodexPluginManifestError(
888
+ f"plugin snapshots may contain regular files only: {relative}"
889
+ )
890
+ files.append(candidate)
891
+ if len(files) > _MAX_ARTIFACT_FILES:
892
+ raise CodexPluginManifestError(
893
+ f"plugin snapshot exceeds {_MAX_ARTIFACT_FILES} regular files"
894
+ )
895
+ total_bytes = 0
896
+ for candidate in files:
897
+ relative = candidate.relative_to(base).as_posix()
898
+ try:
899
+ raw = candidate.read_bytes()
900
+ except OSError as exc:
901
+ raise CodexPluginManifestError(f"plugin file cannot be read: {relative}") from exc
902
+ total_bytes += len(raw)
903
+ if total_bytes > _MAX_ARTIFACT_BYTES:
904
+ raise CodexPluginManifestError(
905
+ f"plugin snapshot exceeds {_MAX_ARTIFACT_BYTES} content bytes"
906
+ )
907
+ digest.update(relative.encode("utf-8"))
908
+ digest.update(b"\0")
909
+ digest.update(f"{stat.S_IMODE(candidate.stat().st_mode) & 0o111:03o}".encode("ascii"))
910
+ digest.update(b"\0")
911
+ digest.update(str(len(raw)).encode("ascii"))
912
+ digest.update(b"\0")
913
+ digest.update(raw)
914
+ digest.update(b"\0")
915
+ return "sha256:" + digest.hexdigest()
916
+
917
+
918
+ def snapshot_installed_codex_plugin(
919
+ plugin_root: Path,
920
+ *,
921
+ source: CodexPluginSourceCoordinate,
922
+ ) -> CodexInstalledPluginSnapshot:
923
+ """Create a deterministic installed-root and per-component snapshot."""
924
+
925
+ root, manifest, raw, skills, mcp_servers, hooks, apps = _load_installed_plugin(plugin_root)
926
+ components: list[CodexPluginComponentSnapshot] = []
927
+ for name, skill_root in skills:
928
+ components.append(
929
+ CodexPluginComponentSnapshot(
930
+ kind="skill",
931
+ name=name,
932
+ path=skill_root.relative_to(root).as_posix(),
933
+ content_digest=_tree_digest(skill_root),
934
+ )
935
+ )
936
+ for name, config, path in mcp_servers:
937
+ components.append(
938
+ CodexPluginComponentSnapshot(
939
+ kind="mcp",
940
+ name=name,
941
+ path=path,
942
+ content_digest=_canonical_json_digest({"name": name, "config": config}),
943
+ )
944
+ )
945
+ for name, config, path in hooks:
946
+ components.append(
947
+ CodexPluginComponentSnapshot(
948
+ kind="hook",
949
+ name=name,
950
+ path=path,
951
+ content_digest=_canonical_json_digest({"name": name, "config": config}),
952
+ )
953
+ )
954
+ for name, config, path in apps:
955
+ components.append(
956
+ CodexPluginComponentSnapshot(
957
+ kind="app",
958
+ name=name,
959
+ path=path,
960
+ content_digest=_canonical_json_digest({"name": name, "config": config}),
961
+ )
962
+ )
963
+ return CodexInstalledPluginSnapshot(
964
+ installed_root=str(root),
965
+ source=source,
966
+ manifest=manifest,
967
+ manifest_digest=_sha256(raw),
968
+ artifact_digest=_tree_digest(root),
969
+ components=tuple(components),
970
+ )
971
+
972
+
973
+ __all__ = [
974
+ "CodexInstalledPluginSnapshot",
975
+ "CodexPluginAuthor",
976
+ "CodexPluginComponentSnapshot",
977
+ "CodexPluginInterface",
978
+ "CodexPluginManifest",
979
+ "CodexPluginManifestError",
980
+ "CodexPluginSourceCoordinate",
981
+ "load_codex_plugin_manifest",
982
+ "snapshot_installed_codex_plugin",
983
+ ]