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,747 @@
1
+ """Built-in capability runtimes for immutable AgentBundle execution.
2
+
3
+ These plugins deliberately consume only the already validated
4
+ ``ResolvedPluginBundle`` plus explicit process services supplied at host
5
+ construction. They never reach back into Studio's mutable catalogs. The
6
+ module also keeps rendering as a projection of the canonical ConversationItem
7
+ contract; it does not introduce another event or transcript pipeline.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import hashlib
13
+ import json
14
+ from collections.abc import Callable, Mapping, Sequence
15
+ from pathlib import Path, PurePosixPath
16
+ from typing import Any, cast
17
+
18
+ from ksadk.conversations.contracts import ConversationItem
19
+ from ksadk.events.session_event import SessionServiceEventStore
20
+ from ksadk.harness.config import McpToolSpec
21
+ from ksadk.plugins.bundle import ResolvedPluginBundle
22
+ from ksadk.plugins.contracts import (
23
+ CompositionProfile,
24
+ PluginManifest,
25
+ plugin_lock_digest,
26
+ )
27
+ from ksadk.plugins.host import PluginHostError
28
+ from ksadk.plugins.providers.harness import HarnessSkillContribution, HarnessTurnRequest
29
+ from ksadk.plugins.resolver import composition_profile_digest
30
+ from ksadk.sessions.base import BaseSessionService
31
+ from ksadk.sessions.local_service import LocalSessionService
32
+
33
+ BUILTIN_PLUGIN_VERSION = "1.0.0"
34
+
35
+ SQLITE_SESSION_STORE_PLUGIN_ID = "io.ksadk.session-store.sqlite"
36
+ WORKSPACE_MCP_PLUGIN_ID = "io.ksadk.mcp.workspace"
37
+ WORKSPACE_SKILL_PLUGIN_ID = "io.ksadk.skill.workspace"
38
+ READ_ONLY_CONTEXT_PLUGIN_ID = "io.ksadk.context.bundle-readonly"
39
+ CORE_RENDERER_PLUGIN_ID = "io.ksadk.renderer.conversation-core"
40
+
41
+ SecretResolver = Callable[[str], str | None]
42
+
43
+
44
+ def _manifest_digest(plugin_id: str) -> str:
45
+ payload = f"{plugin_id}@{BUILTIN_PLUGIN_VERSION}:builtin-runtime-v1".encode()
46
+ return f"sha256:{hashlib.sha256(payload).hexdigest()}"
47
+
48
+
49
+ def _manifest(
50
+ plugin_id: str,
51
+ *,
52
+ definition: str,
53
+ slot: str,
54
+ mode: str,
55
+ permissions: Sequence[str] = (),
56
+ secret_fields: Sequence[str] = (),
57
+ ) -> PluginManifest:
58
+ return cast(
59
+ PluginManifest,
60
+ PluginManifest.model_validate(
61
+ {
62
+ "metadata": {"id": plugin_id, "version": BUILTIN_PLUGIN_VERSION},
63
+ "spec": {
64
+ "domain": "ksadk-platform",
65
+ "runtime": "python",
66
+ "entrypoint": "ksadk.plugins.builtins:builtin_capability_factories",
67
+ "provides": [
68
+ {"definition": definition, "slot": slot, "mode": mode}
69
+ ],
70
+ "secretFields": list(secret_fields),
71
+ "permissions": list(permissions),
72
+ "isolation": "in-process",
73
+ "compatibility": {
74
+ "kernelApi": ">=1,<2",
75
+ "runtimeProtocols": ["agentkit.runtime/v1"],
76
+ "python": ">=3.10,<3.15",
77
+ },
78
+ "healthContract": "plugin.health/v1",
79
+ "provenance": {
80
+ "source": "builtin",
81
+ "digest": _manifest_digest(plugin_id),
82
+ "license": "Apache-2.0",
83
+ },
84
+ },
85
+ },
86
+ ),
87
+ )
88
+
89
+
90
+ def builtin_capability_manifests() -> tuple[PluginManifest, ...]:
91
+ """Return the exact, deterministic Phase 2 built-in capability catalog."""
92
+
93
+ return (
94
+ _manifest(
95
+ SQLITE_SESSION_STORE_PLUGIN_ID,
96
+ definition="session.event-store/v1",
97
+ slot="session.events",
98
+ mode="unique",
99
+ permissions=("filesystem:session-store",),
100
+ ),
101
+ _manifest(
102
+ WORKSPACE_MCP_PLUGIN_ID,
103
+ definition="mcp.connector/v1",
104
+ slot="mcp.workspace",
105
+ mode="multiple",
106
+ permissions=("network:mcp",),
107
+ secret_fields=("apiKeyRef",),
108
+ ),
109
+ _manifest(
110
+ WORKSPACE_SKILL_PLUGIN_ID,
111
+ definition="skill.source/v1",
112
+ slot="skill.workspace",
113
+ mode="multiple",
114
+ permissions=("filesystem:bundle-read",),
115
+ ),
116
+ _manifest(
117
+ READ_ONLY_CONTEXT_PLUGIN_ID,
118
+ definition="context.contributor/v1",
119
+ slot="context.bundle",
120
+ mode="multiple",
121
+ permissions=("filesystem:bundle-read",),
122
+ ),
123
+ _manifest(
124
+ CORE_RENDERER_PLUGIN_ID,
125
+ definition="session.item.renderer/v1",
126
+ slot="renderer.core",
127
+ mode="multiple",
128
+ ),
129
+ )
130
+
131
+
132
+ class _BuiltinRuntime:
133
+ def __init__(self, plugin_id: str, version: str) -> None:
134
+ self.plugin_id = plugin_id
135
+ self.version = version
136
+ self._ready = False
137
+ self._disposed = False
138
+
139
+ async def start(self) -> None:
140
+ if self._disposed:
141
+ raise PluginHostError(
142
+ "builtin_capability_disposed",
143
+ f"built-in capability {self.plugin_id}@{self.version} is disposed",
144
+ )
145
+ self._ready = True
146
+
147
+ async def health(self) -> bool:
148
+ return self._ready and not self._disposed
149
+
150
+ async def drain(self) -> None:
151
+ self._ready = False
152
+
153
+ async def dispose(self) -> None:
154
+ self._ready = False
155
+ self._disposed = True
156
+
157
+ def _require_ready(self) -> None:
158
+ if not self._ready or self._disposed:
159
+ raise PluginHostError(
160
+ "builtin_capability_unavailable",
161
+ f"built-in capability {self.plugin_id}@{self.version} is not ready",
162
+ )
163
+
164
+ def _config(self, bundle: ResolvedPluginBundle) -> Mapping[str, Any]:
165
+ self._require_ready()
166
+ return _bound_capability_config(bundle, self.plugin_id, self.version)
167
+
168
+
169
+ class SQLiteSessionStoreRuntime(_BuiltinRuntime):
170
+ """Own one durable SQLite session service for an active profile."""
171
+
172
+ def __init__(self, plugin_id: str, version: str, *, db_path: Path) -> None:
173
+ super().__init__(plugin_id, version)
174
+ self.db_path = db_path.resolve()
175
+ self._service: LocalSessionService | None = None
176
+ self._event_store: SessionServiceEventStore | None = None
177
+
178
+ async def start(self) -> None:
179
+ if self._disposed:
180
+ await super().start()
181
+ try:
182
+ self._service = LocalSessionService(self.db_path)
183
+ self._event_store = SessionServiceEventStore(self._service)
184
+ except Exception as error: # noqa: BLE001 - durable backend boundary
185
+ self._service = None
186
+ self._event_store = None
187
+ raise PluginHostError(
188
+ "builtin_session_store_start_failed",
189
+ "SQLite session store could not be initialized",
190
+ ) from error
191
+ self._ready = True
192
+
193
+ def session_service_for(self, bundle: ResolvedPluginBundle) -> BaseSessionService:
194
+ self._config(bundle)
195
+ if self._service is None: # pragma: no cover - protected by lifecycle
196
+ raise PluginHostError(
197
+ "builtin_capability_unavailable", "SQLite session service is unavailable"
198
+ )
199
+ return self._service
200
+
201
+ def event_store_for(self, bundle: ResolvedPluginBundle) -> SessionServiceEventStore:
202
+ self._config(bundle)
203
+ if self._event_store is None: # pragma: no cover - protected by lifecycle
204
+ raise PluginHostError(
205
+ "builtin_capability_unavailable", "SQLite event store is unavailable"
206
+ )
207
+ return self._event_store
208
+
209
+ async def dispose(self) -> None:
210
+ service = self._service
211
+ self._ready = False
212
+ self._disposed = True
213
+ if service is not None:
214
+ await service.aclose()
215
+
216
+
217
+ class WorkspaceMCPRuntime(_BuiltinRuntime):
218
+ """Materialize locked workspace MCP resources for the Harness provider."""
219
+
220
+ def __init__(
221
+ self,
222
+ plugin_id: str,
223
+ version: str,
224
+ *,
225
+ secret_resolver: SecretResolver | None,
226
+ ) -> None:
227
+ super().__init__(plugin_id, version)
228
+ self._secret_resolver = secret_resolver
229
+
230
+ def inventory(self, bundle: ResolvedPluginBundle) -> tuple[dict[str, Any], ...]:
231
+ resources = self._resources(bundle)
232
+ inventory: list[dict[str, Any]] = []
233
+ for materializer, resolved in resources:
234
+ inventory.append(
235
+ {
236
+ "name": resolved["name"],
237
+ "version": resolved["version"],
238
+ "transport": resolved["transport"],
239
+ "endpointUrl": resolved.get("endpointUrl"),
240
+ "toolFilter": tuple(_string_list(materializer.get("toolFilter"))),
241
+ }
242
+ )
243
+ return tuple(inventory)
244
+
245
+ def harness_mcp_specs(
246
+ self, bundle: ResolvedPluginBundle
247
+ ) -> tuple[McpToolSpec, ...]:
248
+ specs: list[McpToolSpec] = []
249
+ for materializer, resolved in self._resources(bundle):
250
+ transport = _required_string(resolved, "transport", code="builtin_mcp_invalid")
251
+ if transport not in {"http", "sse"}:
252
+ raise PluginHostError(
253
+ "builtin_mcp_transport_unsupported",
254
+ f"workspace MCP {resolved['name']!r} uses unsupported transport",
255
+ )
256
+ endpoint = _required_string(
257
+ resolved, "endpointUrl", code="builtin_mcp_endpoint_missing"
258
+ )
259
+ secret_ref = _optional_string(materializer.get("apiKeyRef"))
260
+ api_key: str | None = None
261
+ if secret_ref is not None:
262
+ if not _is_secret_reference(secret_ref):
263
+ raise PluginHostError(
264
+ "builtin_mcp_secret_ref_invalid",
265
+ "workspace MCP apiKeyRef must be an external secret reference",
266
+ )
267
+ if self._secret_resolver is None:
268
+ raise PluginHostError(
269
+ "builtin_mcp_secret_unavailable",
270
+ "workspace MCP credential resolver is unavailable",
271
+ )
272
+ try:
273
+ api_key = self._secret_resolver(secret_ref)
274
+ except Exception as error: # noqa: BLE001 - secret-provider boundary
275
+ raise PluginHostError(
276
+ "builtin_mcp_secret_unavailable",
277
+ "workspace MCP credential could not be resolved",
278
+ ) from error
279
+ if not api_key:
280
+ raise PluginHostError(
281
+ "builtin_mcp_secret_unavailable",
282
+ "workspace MCP credential could not be resolved",
283
+ )
284
+ specs.append(
285
+ McpToolSpec(
286
+ name=_required_string(resolved, "name", code="builtin_mcp_invalid"),
287
+ url=endpoint,
288
+ api_key=api_key,
289
+ tool_filter=tuple(_string_list(materializer.get("toolFilter"))),
290
+ tool_name_prefix=_optional_string(
291
+ materializer.get("toolNamePrefix")
292
+ ),
293
+ )
294
+ )
295
+ return tuple(specs)
296
+
297
+ def _resources(
298
+ self, bundle: ResolvedPluginBundle
299
+ ) -> tuple[tuple[Mapping[str, Any], Mapping[str, Any]], ...]:
300
+ config = self._config(bundle)
301
+ declared = _resource_entries(config, expected_kind="mcp")
302
+ resolved = _resolved_resources(bundle, "mcpServers")
303
+ return tuple(
304
+ (
305
+ _mapping(entry.get("materializer"), code="builtin_mcp_invalid"),
306
+ _match_resource(entry, resolved, code="builtin_mcp_resource_mismatch"),
307
+ )
308
+ for entry in declared
309
+ )
310
+
311
+
312
+ class WorkspaceSkillRuntime(_BuiltinRuntime):
313
+ """Expose integrity-checked Skill instructions copied into the Bundle."""
314
+
315
+ def harness_skill(self, bundle: ResolvedPluginBundle) -> HarnessSkillContribution:
316
+ config = self._config(bundle)
317
+ declared = _resource_entries(config, expected_kind="skill")
318
+ resolved = _resolved_resources(bundle, "skills")
319
+ contributions: list[HarnessSkillContribution] = []
320
+ for entry in declared:
321
+ item = _match_resource(
322
+ entry, resolved, code="builtin_skill_resource_mismatch"
323
+ )
324
+ name = _required_string(item, "name", code="builtin_skill_invalid")
325
+ bundle_path = _required_string(
326
+ item, "bundlePath", code="builtin_skill_path_invalid"
327
+ )
328
+ directory = _safe_bundle_path(
329
+ bundle,
330
+ bundle_path,
331
+ code="builtin_skill_path_invalid",
332
+ require_declared=False,
333
+ )
334
+ if not directory.is_dir() or directory.is_symlink():
335
+ raise PluginHostError(
336
+ "builtin_skill_path_invalid", f"Skill {name!r} is not a Bundle directory"
337
+ )
338
+ expected_digest = _required_string(
339
+ item, "digest", code="builtin_skill_invalid"
340
+ )
341
+ if _directory_digest(directory) != expected_digest:
342
+ raise PluginHostError(
343
+ "builtin_skill_digest_mismatch",
344
+ f"Skill {name!r} does not match its locked Bundle digest",
345
+ )
346
+ instructions = _required_string(
347
+ item, "instructions", code="builtin_skill_instructions_missing"
348
+ )
349
+ contributions.append(
350
+ HarnessSkillContribution(name=name, instructions=instructions)
351
+ )
352
+ if not contributions:
353
+ raise PluginHostError(
354
+ "builtin_skill_resource_missing", "workspace Skill has no locked resource"
355
+ )
356
+ if len(contributions) == 1:
357
+ return contributions[0]
358
+ return HarnessSkillContribution(
359
+ name="workspace-skills",
360
+ instructions="\n\n".join(
361
+ f"Skill {item.name}:\n{item.instructions}" for item in contributions
362
+ ),
363
+ )
364
+
365
+
366
+ class ReadOnlyBundleContextRuntime(_BuiltinRuntime):
367
+ """Read declared UTF-8 context files from the immutable Bundle only."""
368
+
369
+ async def harness_context(
370
+ self, bundle: ResolvedPluginBundle, request: HarnessTurnRequest
371
+ ) -> str:
372
+ del request
373
+ config = self._config(bundle)
374
+ paths = _string_list(config.get("paths"))
375
+ max_chars = config.get("maxChars", 32_000)
376
+ if not isinstance(max_chars, int) or isinstance(max_chars, bool) or max_chars < 1:
377
+ raise PluginHostError(
378
+ "builtin_context_config_invalid", "maxChars must be a positive integer"
379
+ )
380
+ chunks: list[str] = []
381
+ remaining = max_chars
382
+ for relative in paths:
383
+ path = _safe_bundle_path(
384
+ bundle,
385
+ relative,
386
+ code="builtin_context_path_invalid",
387
+ require_declared=True,
388
+ )
389
+ if not path.is_file() or path.is_symlink():
390
+ raise PluginHostError(
391
+ "builtin_context_path_invalid",
392
+ f"Bundle context path is not a regular file: {relative}",
393
+ )
394
+ try:
395
+ text = path.read_text(encoding="utf-8").strip()
396
+ except (OSError, UnicodeError) as error:
397
+ raise PluginHostError(
398
+ "builtin_context_read_failed",
399
+ f"Bundle context file could not be read: {relative}",
400
+ ) from error
401
+ if not text or remaining == 0:
402
+ continue
403
+ chunk = text[:remaining]
404
+ chunks.append(chunk)
405
+ remaining -= len(chunk)
406
+ return "\n\n".join(chunks)
407
+
408
+
409
+ class CoreConversationRendererRuntime(_BuiltinRuntime):
410
+ """Project canonical items into a safe core renderer view model."""
411
+
412
+ _COMPONENTS = {
413
+ "user_message": "markdown",
414
+ "assistant_text": "markdown",
415
+ "reasoning": "reasoning",
416
+ "tool_call": "tool",
417
+ "approval": "approval",
418
+ "progress": "progress",
419
+ "plan": "plan",
420
+ "goal": "goal",
421
+ "artifact": "artifact",
422
+ "a2ui": "a2ui",
423
+ "error": "error",
424
+ }
425
+
426
+ def render(self, item: ConversationItem) -> dict[str, Any]:
427
+ base: dict[str, Any] = {
428
+ "component": self._COMPONENTS.get(item.kind, "unknown"),
429
+ "itemId": item.item_id,
430
+ "lifecycle": item.lifecycle,
431
+ }
432
+ if item.kind in {"user_message", "assistant_text"}:
433
+ text = item.payload.get("text", "")
434
+ base["text"] = text if isinstance(text, str) else str(text)
435
+ return base
436
+ if item.kind == "unknown":
437
+ base["schemaRef"] = item.payload_schema_ref
438
+ base["summary"] = json.dumps(
439
+ item.payload,
440
+ ensure_ascii=False,
441
+ sort_keys=True,
442
+ separators=(",", ":"),
443
+ default=str,
444
+ )[:4096]
445
+ return base
446
+ # Structured payload stays data. Consumers choose trusted local UI
447
+ # components; no Bundle-provided HTML or JavaScript is executed here.
448
+ base["payload"] = dict(item.payload)
449
+ base["schemaRef"] = item.payload_schema_ref
450
+ return base
451
+
452
+
453
+ class SQLiteSessionStoreFactory:
454
+ def __init__(self, state_root: Path) -> None:
455
+ self._state_root = state_root.resolve()
456
+ self.runtime: SQLiteSessionStoreRuntime | None = None
457
+
458
+ async def stage(
459
+ self,
460
+ manifest: PluginManifest,
461
+ *,
462
+ profile: CompositionProfile,
463
+ services: Mapping[str, Any],
464
+ ) -> SQLiteSessionStoreRuntime:
465
+ del services
466
+ _require_manifest(manifest, SQLITE_SESSION_STORE_PLUGIN_ID)
467
+ profile_key = composition_profile_digest(profile).removeprefix("sha256:")
468
+ runtime = SQLiteSessionStoreRuntime(
469
+ manifest.metadata.id,
470
+ manifest.metadata.version,
471
+ db_path=self._state_root / profile_key / "sessions.sqlite",
472
+ )
473
+ self.runtime = runtime
474
+ return runtime
475
+
476
+
477
+ class _WorkspaceMCPFactory:
478
+ def __init__(self, secret_resolver: SecretResolver | None) -> None:
479
+ self._secret_resolver = secret_resolver
480
+
481
+ async def stage(
482
+ self,
483
+ manifest: PluginManifest,
484
+ *,
485
+ profile: CompositionProfile,
486
+ services: Mapping[str, Any],
487
+ ) -> WorkspaceMCPRuntime:
488
+ del profile
489
+ _require_manifest(manifest, WORKSPACE_MCP_PLUGIN_ID)
490
+ resolver = self._secret_resolver
491
+ service_resolver = services.get("secret_resolver")
492
+ if resolver is None and callable(service_resolver):
493
+ resolver = service_resolver
494
+ return WorkspaceMCPRuntime(
495
+ manifest.metadata.id,
496
+ manifest.metadata.version,
497
+ secret_resolver=resolver,
498
+ )
499
+
500
+
501
+ class _SimpleFactory:
502
+ def __init__(self, plugin_id: str, runtime_type: type[_BuiltinRuntime]) -> None:
503
+ self._plugin_id = plugin_id
504
+ self._runtime_type = runtime_type
505
+
506
+ async def stage(
507
+ self,
508
+ manifest: PluginManifest,
509
+ *,
510
+ profile: CompositionProfile,
511
+ services: Mapping[str, Any],
512
+ ) -> _BuiltinRuntime:
513
+ del profile, services
514
+ _require_manifest(manifest, self._plugin_id)
515
+ return self._runtime_type(manifest.metadata.id, manifest.metadata.version)
516
+
517
+
518
+ def builtin_capability_factories(
519
+ *,
520
+ state_root: Path,
521
+ secret_resolver: SecretResolver | None = None,
522
+ ) -> dict[str, Any]:
523
+ """Create factories with explicit state and secret-provider boundaries."""
524
+
525
+ return {
526
+ SQLITE_SESSION_STORE_PLUGIN_ID: SQLiteSessionStoreFactory(state_root),
527
+ WORKSPACE_MCP_PLUGIN_ID: _WorkspaceMCPFactory(secret_resolver),
528
+ WORKSPACE_SKILL_PLUGIN_ID: _SimpleFactory(
529
+ WORKSPACE_SKILL_PLUGIN_ID, WorkspaceSkillRuntime
530
+ ),
531
+ READ_ONLY_CONTEXT_PLUGIN_ID: _SimpleFactory(
532
+ READ_ONLY_CONTEXT_PLUGIN_ID, ReadOnlyBundleContextRuntime
533
+ ),
534
+ CORE_RENDERER_PLUGIN_ID: _SimpleFactory(
535
+ CORE_RENDERER_PLUGIN_ID, CoreConversationRendererRuntime
536
+ ),
537
+ }
538
+
539
+
540
+ def _require_manifest(manifest: PluginManifest, plugin_id: str) -> None:
541
+ if (
542
+ manifest.metadata.id != plugin_id
543
+ or manifest.metadata.version != BUILTIN_PLUGIN_VERSION
544
+ ):
545
+ raise PluginHostError(
546
+ "builtin_manifest_mismatch",
547
+ f"factory cannot stage {manifest.metadata.id}@{manifest.metadata.version}",
548
+ )
549
+
550
+
551
+ def _bound_capability_config(
552
+ bundle: ResolvedPluginBundle, plugin_id: str, version: str
553
+ ) -> Mapping[str, Any]:
554
+ composition = bundle.composition
555
+ if composition_profile_digest(composition.profile) != composition.profile_digest:
556
+ raise PluginHostError(
557
+ "plugin_bundle_profile_mutated",
558
+ "resolved Bundle composition profile changed after validation",
559
+ )
560
+ if plugin_lock_digest(composition.plugin_lock) != composition.plugin_lock_digest:
561
+ raise PluginHostError(
562
+ "plugin_bundle_lock_mutated",
563
+ "resolved Bundle plugin lock changed after validation",
564
+ )
565
+ ref = f"plugin://{plugin_id}@{version}"
566
+ matches = [item for item in composition.profile.capabilities if item.ref == ref]
567
+ if len(matches) != 1:
568
+ raise PluginHostError(
569
+ "builtin_capability_binding_missing",
570
+ f"resolved Bundle does not bind built-in capability {plugin_id}@{version}",
571
+ )
572
+ if not any(
573
+ item.id == plugin_id and item.version == version
574
+ for item in composition.plugin_lock.plugins
575
+ ):
576
+ raise PluginHostError(
577
+ "builtin_capability_lock_missing",
578
+ f"resolved Bundle does not lock built-in capability {plugin_id}@{version}",
579
+ )
580
+ return cast(Mapping[str, Any], matches[0].config)
581
+
582
+
583
+ def _resource_entries(
584
+ config: Mapping[str, Any], *, expected_kind: str
585
+ ) -> tuple[Mapping[str, Any], ...]:
586
+ raw = config.get("resources")
587
+ if not isinstance(raw, Sequence) or isinstance(raw, (str, bytes)):
588
+ raise PluginHostError(
589
+ f"builtin_{expected_kind}_config_invalid", "resources must be an array"
590
+ )
591
+ entries: list[Mapping[str, Any]] = []
592
+ for item in raw:
593
+ entry = _mapping(item, code=f"builtin_{expected_kind}_config_invalid")
594
+ if entry.get("kind") != expected_kind:
595
+ raise PluginHostError(
596
+ f"builtin_{expected_kind}_resource_mismatch",
597
+ f"resource kind must be {expected_kind!r}",
598
+ )
599
+ for field in ("resourceId", "name", "version", "digest"):
600
+ _required_string(entry, field, code=f"builtin_{expected_kind}_invalid")
601
+ entries.append(entry)
602
+ return tuple(entries)
603
+
604
+
605
+ def _resolved_resources(
606
+ bundle: ResolvedPluginBundle, field: str
607
+ ) -> tuple[Mapping[str, Any], ...]:
608
+ capabilities = _mapping(
609
+ bundle.resolved_agent_spec.get("capabilities"),
610
+ code="builtin_bundle_capabilities_missing",
611
+ )
612
+ raw = capabilities.get(field)
613
+ if not isinstance(raw, Sequence) or isinstance(raw, (str, bytes)):
614
+ raise PluginHostError(
615
+ "builtin_bundle_capabilities_missing", f"Bundle capabilities.{field} is missing"
616
+ )
617
+ return tuple(
618
+ _mapping(item, code="builtin_bundle_capabilities_invalid") for item in raw
619
+ )
620
+
621
+
622
+ def _match_resource(
623
+ declared: Mapping[str, Any],
624
+ resolved: Sequence[Mapping[str, Any]],
625
+ *,
626
+ code: str,
627
+ ) -> Mapping[str, Any]:
628
+ name = _required_string(declared, "name", code=code)
629
+ version = _required_string(declared, "version", code=code)
630
+ digest = _required_string(declared, "digest", code=code)
631
+ matches = [
632
+ item
633
+ for item in resolved
634
+ if item.get("name") == name
635
+ and item.get("version") == version
636
+ and item.get("digest") == digest
637
+ ]
638
+ if len(matches) != 1:
639
+ raise PluginHostError(
640
+ code,
641
+ f"locked resource {name!r}@{version} is missing from resolved Agent spec",
642
+ )
643
+ return matches[0]
644
+
645
+
646
+ def _safe_bundle_path(
647
+ bundle: ResolvedPluginBundle,
648
+ relative_text: str,
649
+ *,
650
+ code: str,
651
+ require_declared: bool,
652
+ ) -> Path:
653
+ relative = PurePosixPath(relative_text)
654
+ if relative.is_absolute() or not relative.parts or ".." in relative.parts:
655
+ raise PluginHostError(code, f"unsafe Bundle path: {relative_text}")
656
+ path = bundle.root.joinpath(*relative.parts)
657
+ cursor = bundle.root
658
+ for part in relative.parts:
659
+ cursor = cursor / part
660
+ if cursor.is_symlink():
661
+ raise PluginHostError(code, f"Bundle path cannot use symlinks: {relative_text}")
662
+ try:
663
+ resolved = path.resolve(strict=True)
664
+ resolved.relative_to(bundle.root)
665
+ except (OSError, ValueError) as error:
666
+ raise PluginHostError(code, f"Bundle path is unavailable: {relative_text}") from error
667
+ if require_declared and relative.as_posix() not in {
668
+ item.path for item in bundle.manifest.files
669
+ }:
670
+ raise PluginHostError(code, f"Bundle path is not declared: {relative_text}")
671
+ return cast(Path, resolved)
672
+
673
+
674
+ def _directory_digest(directory: Path) -> str:
675
+ digest = hashlib.sha256()
676
+ for path in sorted(item for item in directory.rglob("*") if item.is_file()):
677
+ if path.is_symlink():
678
+ raise PluginHostError(
679
+ "builtin_skill_path_invalid", "Skill Bundle cannot contain symlinks"
680
+ )
681
+ relative = path.relative_to(directory).as_posix().encode("utf-8")
682
+ content = path.read_bytes()
683
+ digest.update(len(relative).to_bytes(4, "big"))
684
+ digest.update(relative)
685
+ digest.update(len(content).to_bytes(8, "big"))
686
+ digest.update(content)
687
+ return f"sha256:{digest.hexdigest()}"
688
+
689
+
690
+ def _mapping(value: Any, *, code: str) -> Mapping[str, Any]:
691
+ if not isinstance(value, Mapping):
692
+ raise PluginHostError(code, "expected an object")
693
+ return value
694
+
695
+
696
+ def _required_string(value: Mapping[str, Any], field: str, *, code: str) -> str:
697
+ text = value.get(field)
698
+ if not isinstance(text, str) or not text.strip():
699
+ raise PluginHostError(code, f"{field} must be a non-empty string")
700
+ return text.strip()
701
+
702
+
703
+ def _optional_string(value: Any) -> str | None:
704
+ if value is None:
705
+ return None
706
+ if not isinstance(value, str):
707
+ raise PluginHostError(
708
+ "builtin_config_invalid", "optional configuration value must be a string"
709
+ )
710
+ return value.strip() or None
711
+
712
+
713
+ def _string_list(value: Any) -> tuple[str, ...]:
714
+ if value is None:
715
+ return ()
716
+ if not isinstance(value, Sequence) or isinstance(value, (str, bytes)):
717
+ raise PluginHostError("builtin_config_invalid", "configuration value must be an array")
718
+ result: list[str] = []
719
+ for item in value:
720
+ if not isinstance(item, str) or not item.strip():
721
+ raise PluginHostError(
722
+ "builtin_config_invalid", "array entries must be non-empty strings"
723
+ )
724
+ result.append(item.strip())
725
+ return tuple(result)
726
+
727
+
728
+ def _is_secret_reference(value: str) -> bool:
729
+ return value.startswith(("secret://", "env://", "credential://", "vault://"))
730
+
731
+
732
+ __all__ = [
733
+ "BUILTIN_PLUGIN_VERSION",
734
+ "CORE_RENDERER_PLUGIN_ID",
735
+ "READ_ONLY_CONTEXT_PLUGIN_ID",
736
+ "SQLITE_SESSION_STORE_PLUGIN_ID",
737
+ "WORKSPACE_MCP_PLUGIN_ID",
738
+ "WORKSPACE_SKILL_PLUGIN_ID",
739
+ "CoreConversationRendererRuntime",
740
+ "ReadOnlyBundleContextRuntime",
741
+ "SQLiteSessionStoreFactory",
742
+ "SQLiteSessionStoreRuntime",
743
+ "WorkspaceMCPRuntime",
744
+ "WorkspaceSkillRuntime",
745
+ "builtin_capability_factories",
746
+ "builtin_capability_manifests",
747
+ ]