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,333 @@
1
+ """Frozen SubagentProvider/v1 seam with explicit lineage and policy bounds.
2
+
3
+ The Kernel does not know whether a child is implemented by Codex, DSH,
4
+ Claude, another KsADK AgentProvider, or a remote service. It only routes a
5
+ bounded request to a named provider and retains the returned child identity.
6
+ Provider-private state and event logs remain with that provider; projected
7
+ child events keep their own ids and sequence numbers.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from collections.abc import AsyncIterator, Mapping, Sequence
13
+ from datetime import datetime
14
+ from typing import Any, Literal, Protocol, cast, runtime_checkable
15
+
16
+ from pydantic import Field, field_validator, model_validator
17
+
18
+ from ksadk.plugins.contracts import PluginContractModel, PluginReference
19
+
20
+
21
+ class SubagentPolicy(PluginContractModel):
22
+ """Parent-owned limits that a child provider may only narrow."""
23
+
24
+ max_depth: int = Field(default=1, ge=1, le=16)
25
+ timeout_seconds: int = Field(default=120, ge=1, le=86_400)
26
+ max_steps: int = Field(default=12, ge=1, le=10_000)
27
+ background: bool = False
28
+ allowed_tools: tuple[str, ...] = ()
29
+ allowed_permissions: tuple[str, ...] = ()
30
+
31
+ @field_validator("allowed_tools", "allowed_permissions")
32
+ @classmethod
33
+ def validate_unique_values(cls, value: tuple[str, ...]) -> tuple[str, ...]:
34
+ if any(not item.strip() for item in value):
35
+ raise ValueError("subagent policy entries must be non-empty")
36
+ if len(value) != len(set(value)):
37
+ raise ValueError("subagent policy entries must be unique")
38
+ return tuple(sorted(value))
39
+
40
+
41
+ class SpawnSubagentRequest(PluginContractModel):
42
+ """One bounded child task requested by an authenticated parent Run."""
43
+
44
+ request_format: Literal["ksadk.subagent-spawn/v1"] = "ksadk.subagent-spawn/v1"
45
+ provider_ref: str
46
+ parent_session_id: str = Field(min_length=1, max_length=256)
47
+ parent_run_id: str = Field(min_length=1, max_length=256)
48
+ task: str = Field(min_length=1, max_length=131_072)
49
+ depth: int = Field(default=1, ge=1, le=16)
50
+ policy: SubagentPolicy = Field(default_factory=SubagentPolicy)
51
+ metadata: dict[str, Any] = Field(default_factory=dict)
52
+
53
+ @field_validator("provider_ref")
54
+ @classmethod
55
+ def validate_provider_ref(cls, value: str) -> str:
56
+ return cast(str, PluginReference(ref=value).ref)
57
+
58
+ @field_validator("metadata")
59
+ @classmethod
60
+ def validate_metadata(cls, value: dict[str, Any]) -> dict[str, Any]:
61
+ _reject_secret_values(value)
62
+ return value
63
+
64
+ @model_validator(mode="after")
65
+ def validate_depth(self) -> "SpawnSubagentRequest":
66
+ if self.depth > self.policy.max_depth:
67
+ raise ValueError("subagent depth exceeds the parent policy maxDepth")
68
+ return self
69
+
70
+
71
+ class ChildHandle(PluginContractModel):
72
+ """Stable lineage and recovery descriptor for one provider-owned child."""
73
+
74
+ handle_format: Literal["ksadk.child-handle/v1"] = "ksadk.child-handle/v1"
75
+ handle_id: str = Field(min_length=1, max_length=256)
76
+ provider_ref: str
77
+ parent_session_id: str = Field(min_length=1, max_length=256)
78
+ parent_run_id: str = Field(min_length=1, max_length=256)
79
+ child_session_id: str | None = Field(default=None, max_length=256)
80
+ child_run_id: str | None = Field(default=None, max_length=256)
81
+ depth: int = Field(ge=1, le=16)
82
+ capabilities: tuple[str, ...] = ()
83
+ capability_digest: str = Field(pattern=r"^sha256:[0-9a-f]{64}$")
84
+ created_at: datetime
85
+ resumable: bool = False
86
+ resume_descriptor: dict[str, Any] | None = None
87
+
88
+ @field_validator("provider_ref")
89
+ @classmethod
90
+ def validate_provider_ref(cls, value: str) -> str:
91
+ return cast(str, PluginReference(ref=value).ref)
92
+
93
+ @field_validator("capabilities")
94
+ @classmethod
95
+ def validate_capabilities(cls, value: tuple[str, ...]) -> tuple[str, ...]:
96
+ if any(not item.strip() for item in value):
97
+ raise ValueError("child capabilities must be non-empty")
98
+ if len(value) != len(set(value)):
99
+ raise ValueError("child capabilities must be unique")
100
+ return tuple(sorted(value))
101
+
102
+ @field_validator("created_at")
103
+ @classmethod
104
+ def validate_created_at(cls, value: datetime) -> datetime:
105
+ if value.tzinfo is None or value.utcoffset() is None:
106
+ raise ValueError("child handle createdAt must include a timezone")
107
+ return value
108
+
109
+ @field_validator("resume_descriptor")
110
+ @classmethod
111
+ def validate_resume_descriptor(
112
+ cls, value: dict[str, Any] | None
113
+ ) -> dict[str, Any] | None:
114
+ if value is not None:
115
+ _reject_secret_values(value)
116
+ return value
117
+
118
+ @model_validator(mode="after")
119
+ def validate_recovery_shape(self) -> "ChildHandle":
120
+ if self.resumable and not self.resume_descriptor:
121
+ raise ValueError("resumable child handle requires a resumeDescriptor")
122
+ if not self.resumable and self.resume_descriptor is not None:
123
+ raise ValueError("non-resumable child handle cannot carry a resumeDescriptor")
124
+ return self
125
+
126
+
127
+ class SubagentStatus(PluginContractModel):
128
+ status_format: Literal["ksadk.subagent-status/v1"] = "ksadk.subagent-status/v1"
129
+ handle_id: str = Field(min_length=1, max_length=256)
130
+ state: Literal[
131
+ "accepted",
132
+ "running",
133
+ "waiting_input",
134
+ "succeeded",
135
+ "failed",
136
+ "cancelled",
137
+ "interrupted",
138
+ "disposed",
139
+ ]
140
+ last_seq: int = Field(default=0, ge=0)
141
+ updated_at: datetime
142
+ reason: str | None = Field(default=None, max_length=2048)
143
+
144
+ @field_validator("updated_at")
145
+ @classmethod
146
+ def validate_updated_at(cls, value: datetime) -> datetime:
147
+ if value.tzinfo is None or value.utcoffset() is None:
148
+ raise ValueError("subagent status updatedAt must include a timezone")
149
+ return value
150
+
151
+
152
+ class SubagentEvent(PluginContractModel):
153
+ """Identity-preserving child event; parent projection must not merge by text."""
154
+
155
+ event_format: Literal["ksadk.subagent-event/v1"] = "ksadk.subagent-event/v1"
156
+ handle_id: str = Field(min_length=1, max_length=256)
157
+ event_id: str = Field(min_length=1, max_length=256)
158
+ seq: int = Field(ge=1)
159
+ kind: Literal["progress", "item", "interaction", "terminal"]
160
+ payload: dict[str, Any] = Field(default_factory=dict)
161
+ native_ref: dict[str, str] = Field(default_factory=dict)
162
+
163
+ @field_validator("payload")
164
+ @classmethod
165
+ def validate_payload(cls, value: dict[str, Any]) -> dict[str, Any]:
166
+ _reject_secret_values(value)
167
+ return value
168
+
169
+
170
+ class SubagentResult(PluginContractModel):
171
+ result_format: Literal["ksadk.subagent-result/v1"] = "ksadk.subagent-result/v1"
172
+ handle_id: str = Field(min_length=1, max_length=256)
173
+ state: Literal["succeeded", "failed", "cancelled", "interrupted"]
174
+ output: Any = None
175
+ output_refs: tuple[str, ...] = ()
176
+ error_code: str | None = Field(default=None, max_length=256)
177
+ error_message: str | None = Field(default=None, max_length=2048)
178
+
179
+ @field_validator("output_refs")
180
+ @classmethod
181
+ def validate_output_refs(cls, value: tuple[str, ...]) -> tuple[str, ...]:
182
+ if any(not item.strip() for item in value):
183
+ raise ValueError("subagent output references must be non-empty")
184
+ if len(value) != len(set(value)):
185
+ raise ValueError("subagent output references must be unique")
186
+ return tuple(sorted(value))
187
+
188
+ @model_validator(mode="after")
189
+ def validate_terminal_shape(self) -> "SubagentResult":
190
+ if self.state == "succeeded" and (self.error_code or self.error_message):
191
+ raise ValueError("successful subagent result cannot carry an error")
192
+ if self.state == "failed" and not self.error_code:
193
+ raise ValueError("failed subagent result requires errorCode")
194
+ return self
195
+
196
+
197
+ @runtime_checkable
198
+ class SubagentProvider(Protocol):
199
+ """Named child execution provider; every method is provider-owned."""
200
+
201
+ async def describe(self) -> Mapping[str, Any]: ...
202
+
203
+ async def available(self) -> bool: ...
204
+
205
+ async def spawn(self, request: SpawnSubagentRequest) -> ChildHandle: ...
206
+
207
+ async def followup(self, handle: ChildHandle, input: Any) -> None: ...
208
+
209
+ async def status(self, handle: ChildHandle) -> SubagentStatus: ...
210
+
211
+ async def interrupt(self, handle: ChildHandle) -> None: ...
212
+
213
+ async def cancel(self, handle: ChildHandle) -> None: ...
214
+
215
+ def subscribe(
216
+ self, handle: ChildHandle, *, after_seq: int = 0
217
+ ) -> AsyncIterator[SubagentEvent]: ...
218
+
219
+ async def result(self, handle: ChildHandle) -> SubagentResult: ...
220
+
221
+ async def dispose(self, handle: ChildHandle) -> None: ...
222
+
223
+
224
+ class SubagentProviderError(RuntimeError):
225
+ """Stable typed failure at the provider routing boundary."""
226
+
227
+ def __init__(self, code: str, message: str) -> None:
228
+ super().__init__(message)
229
+ self.code = code
230
+
231
+
232
+ class SubagentProviderRouter:
233
+ """Route child handles to exact providers without interpreting their state."""
234
+
235
+ def __init__(self, providers: Mapping[str, SubagentProvider]) -> None:
236
+ self._providers = dict(providers)
237
+
238
+ async def spawn(self, request: SpawnSubagentRequest) -> ChildHandle:
239
+ provider = self._provider(request.provider_ref)
240
+ if not await provider.available():
241
+ raise SubagentProviderError(
242
+ "subagent_provider_unavailable",
243
+ f"SubagentProvider {request.provider_ref} is unavailable",
244
+ )
245
+ handle = await provider.spawn(request)
246
+ self._validate_handle(request, handle)
247
+ return handle
248
+
249
+ async def status(self, handle: ChildHandle) -> SubagentStatus:
250
+ return await self._provider_for_handle(handle).status(handle)
251
+
252
+ async def followup(self, handle: ChildHandle, input: Any) -> None:
253
+ await self._provider_for_handle(handle).followup(handle, input)
254
+
255
+ async def interrupt(self, handle: ChildHandle) -> None:
256
+ await self._provider_for_handle(handle).interrupt(handle)
257
+
258
+ async def cancel(self, handle: ChildHandle) -> None:
259
+ await self._provider_for_handle(handle).cancel(handle)
260
+
261
+ def subscribe(
262
+ self, handle: ChildHandle, *, after_seq: int = 0
263
+ ) -> AsyncIterator[SubagentEvent]:
264
+ if after_seq < 0:
265
+ raise SubagentProviderError(
266
+ "subagent_cursor_invalid", "subagent after_seq cannot be negative"
267
+ )
268
+ return self._provider_for_handle(handle).subscribe(handle, after_seq=after_seq)
269
+
270
+ async def result(self, handle: ChildHandle) -> SubagentResult:
271
+ return await self._provider_for_handle(handle).result(handle)
272
+
273
+ async def dispose(self, handle: ChildHandle) -> None:
274
+ await self._provider_for_handle(handle).dispose(handle)
275
+
276
+ def _provider_for_handle(self, handle: ChildHandle) -> SubagentProvider:
277
+ return self._provider(handle.provider_ref)
278
+
279
+ def _provider(self, provider_ref: str) -> SubagentProvider:
280
+ provider = self._providers.get(provider_ref)
281
+ if provider is None:
282
+ raise SubagentProviderError(
283
+ "subagent_provider_not_found",
284
+ f"SubagentProvider {provider_ref} is not registered",
285
+ )
286
+ return provider
287
+
288
+ @staticmethod
289
+ def _validate_handle(
290
+ request: SpawnSubagentRequest,
291
+ handle: ChildHandle,
292
+ ) -> None:
293
+ if (
294
+ handle.provider_ref != request.provider_ref
295
+ or handle.parent_session_id != request.parent_session_id
296
+ or handle.parent_run_id != request.parent_run_id
297
+ or handle.depth != request.depth
298
+ ):
299
+ raise SubagentProviderError(
300
+ "subagent_handle_mismatch",
301
+ "SubagentProvider returned a child handle outside the requested lineage",
302
+ )
303
+
304
+
305
+ _SECRET_KEY_PARTS = ("secret", "password", "token", "apikey", "api_key")
306
+ _SECRET_REF_PREFIXES = ("secret://", "env://", "credential://", "vault://")
307
+
308
+
309
+ def _reject_secret_values(value: Any, *, path: str = "payload") -> None:
310
+ if isinstance(value, Mapping):
311
+ for key, child in value.items():
312
+ key_text = str(key)
313
+ normalized = key_text.replace("-", "").lower()
314
+ if any(part in normalized for part in _SECRET_KEY_PARTS) and child is not None:
315
+ if not isinstance(child, str) or not child.startswith(_SECRET_REF_PREFIXES):
316
+ raise ValueError(f"{path}.{key_text} must contain a secret reference")
317
+ _reject_secret_values(child, path=f"{path}.{key_text}")
318
+ elif isinstance(value, Sequence) and not isinstance(value, (str, bytes, bytearray)):
319
+ for index, child in enumerate(value):
320
+ _reject_secret_values(child, path=f"{path}[{index}]")
321
+
322
+
323
+ __all__ = [
324
+ "ChildHandle",
325
+ "SpawnSubagentRequest",
326
+ "SubagentEvent",
327
+ "SubagentPolicy",
328
+ "SubagentProvider",
329
+ "SubagentProviderError",
330
+ "SubagentProviderRouter",
331
+ "SubagentResult",
332
+ "SubagentStatus",
333
+ ]
@@ -69,7 +69,7 @@ def _a2ui_surface_event(
69
69
  self: Any,
70
70
  handle: RunHandle,
71
71
  chunk: Any,
72
- ) -> RuntimeEvent | None:
72
+ ) -> tuple[RuntimeEvent, ...]:
73
73
  """Recognize a validated A2UI tool envelope and emit a canonical data-item event.
74
74
 
75
75
  The dynamic ``generate_a2ui`` tool returns official v0.9 operations as a
@@ -83,7 +83,7 @@ def _a2ui_surface_event(
83
83
  """
84
84
 
85
85
  if not isinstance(chunk, dict):
86
- return None
86
+ return ()
87
87
  value = chunk.get("tool_output", chunk.get("output"))
88
88
  if value is not None and hasattr(value, "content"):
89
89
  value = value.content
@@ -91,15 +91,15 @@ def _a2ui_surface_event(
91
91
  try:
92
92
  value = json.loads(value)
93
93
  except (TypeError, ValueError):
94
- return None
94
+ return ()
95
95
  if not isinstance(value, Mapping):
96
- return None
96
+ return ()
97
97
  operations_raw = value.get("a2ui_operations")
98
98
  if not isinstance(operations_raw, list) or not operations_raw:
99
- return None
99
+ return ()
100
100
  operations = [dict(operation) for operation in operations_raw if isinstance(operation, Mapping)]
101
101
  if not operations:
102
- return None
102
+ return ()
103
103
 
104
104
  known: list[tuple[str, str]] = [] # (surface_id, lifecycle)
105
105
  for operation in operations:
@@ -116,71 +116,75 @@ def _a2ui_surface_event(
116
116
  known.append((surface_id, lifecycle))
117
117
  break
118
118
  if not known:
119
- return None
119
+ return ()
120
120
  surface_ids = {surface_id for surface_id, _lifecycle in known}
121
121
  if len(surface_ids) != 1:
122
122
  logger.warning("ignoring A2UI tool result with multiple surfaces")
123
- return None
123
+ return ()
124
124
  surface_id = known[0][0]
125
125
  lifecycle = "begin" if any(lc == "begin" for _, lc in known) else known[0][1]
126
126
 
127
127
  framework = self._runtime_type
128
128
  run_id = handle.run_id
129
129
  scope_id = stable_scope_id(framework, run_id)
130
- item_id = stable_item_id(framework, run_id, "a2ui", surface_id)
130
+ # A dynamic A2UI tool result is a complete, immutable operation batch. It
131
+ # must therefore be represented by a closed canonical item. Reusing one
132
+ # long-lived item per surface leaves a create/update-only batch open at the
133
+ # run terminal and makes strict reducers reject the stream.
134
+ start_seq = self._next_seq()
135
+ batch_ref = str(chunk.get("call_id") or chunk.get("run_id") or chunk.get("id") or "")
136
+ if not batch_ref:
137
+ batch_ref = json.dumps(
138
+ operations,
139
+ ensure_ascii=False,
140
+ sort_keys=True,
141
+ separators=(",", ":"),
142
+ )
143
+ item_id = stable_item_id(framework, run_id, "a2ui-batch", surface_id, batch_ref)
131
144
  source = SourceRef(
132
145
  framework=framework,
133
146
  protocol="a2ui",
134
147
  native_run_id=run_id,
135
- metadata={"surface_id": surface_id},
148
+ metadata={
149
+ "surface_id": surface_id,
150
+ "operation_batch": True,
151
+ "surface_lifecycle": lifecycle,
152
+ },
136
153
  )
137
154
  # TODO(runtime-event-v2): dict chunk 退化路径,chunk_ordinal 用 seq counter;
138
155
  # LangGraph/Codex 切 stream_canonical_events 后清理
139
- n = self._next_seq()
140
156
  timestamp = time.time()
141
- if lifecycle == "begin":
142
- return ItemStarted(
157
+ snapshot = ContentSnapshot(parts=(DataContent(part_id="a2ui-ops", data=operations),))
158
+ completed_seq = self._next_seq()
159
+ return (
160
+ ItemStarted(
143
161
  schema_version=2,
144
162
  event_id=stable_event_id(
145
- framework, scope_id, item_id, "item.started", "a2ui", run_id, n
163
+ framework, scope_id, item_id, "item.started", "a2ui", run_id, start_seq
146
164
  ),
147
- seq=n,
165
+ seq=start_seq,
148
166
  timestamp=timestamp,
149
167
  run_id=run_id,
150
168
  scope_id=scope_id,
151
169
  source=source,
152
170
  item_id=item_id,
153
171
  item_kind="data",
154
- initial=ContentSnapshot(parts=(DataContent(part_id="a2ui-ops", data=operations),)),
155
- )
156
- if lifecycle == "update":
157
- return ItemUpdated(
172
+ initial=snapshot,
173
+ ),
174
+ ItemCompleted(
158
175
  schema_version=2,
159
176
  event_id=stable_event_id(
160
- framework, scope_id, item_id, "item.updated", "a2ui", run_id, n
177
+ framework, scope_id, item_id, "item.completed", "a2ui", run_id, completed_seq
161
178
  ),
162
- seq=n,
179
+ seq=completed_seq,
163
180
  timestamp=timestamp,
164
181
  run_id=run_id,
165
182
  scope_id=scope_id,
166
183
  source=source,
167
184
  item_id=item_id,
168
185
  item_kind="data",
169
- op="replace",
170
- update=DataContent(part_id="a2ui-ops", data=operations),
171
- )
172
- # lifecycle == "end"
173
- return ItemCompleted(
174
- schema_version=2,
175
- event_id=stable_event_id(framework, scope_id, item_id, "item.completed", "a2ui", run_id, n),
176
- seq=n,
177
- timestamp=timestamp,
178
- run_id=run_id,
179
- scope_id=scope_id,
180
- source=source,
181
- item_id=item_id,
182
- item_kind="data",
183
- snapshot=ContentSnapshot(parts=()),
186
+ snapshot=snapshot,
187
+ ),
184
188
  )
185
189
 
186
190
 
@@ -323,8 +327,7 @@ class _RunnerStreamMappingMixin:
323
327
  usage.update(raw_usage)
324
328
  for event in self._chunk_to_event(handle, run, chunk): # type: ignore[attr-defined]
325
329
  yield event
326
- a2ui_surface = _a2ui_surface_event(self, handle, chunk)
327
- if a2ui_surface is not None:
330
+ for a2ui_surface in _a2ui_surface_event(self, handle, chunk):
328
331
  yield a2ui_surface
329
332
  finally:
330
333
  if accumulated_output:
@@ -708,8 +711,10 @@ class _RunnerStreamMappingMixin:
708
711
  if run is not None:
709
712
  run.final_answer_item_id = item_id
710
713
  text_content = TextContent(part_id="text-0", text=output)
711
- # Auto-close any open commentary/reasoning item before emitting final_answer.
712
- # Text/thinking deltas create items that are never ItemCompleted;
714
+ # Auto-close any open explicit commentary/reasoning item before completing
715
+ # final_answer. Text deltas already belong to the final-answer item and must
716
+ # keep that identity through completion.
717
+ # Commentary/thinking deltas create items that are never ItemCompleted;
713
718
  # without this, RunCompleted fails _ensure_no_open_items.
714
719
  #
715
720
  # Close them *before* allocating the final-answer item. Event
@@ -764,9 +769,13 @@ class _RunnerStreamMappingMixin:
764
769
  text = self._coerce(chunk.get("delta") or chunk.get("output") or chunk.get("data")) # type: ignore[attr-defined]
765
770
  if not text:
766
771
  return []
767
- item_id = stable_item_id(framework, run_id, "message", "commentary")
772
+ is_commentary = chunk_type in {"commentary", "commentary_delta"}
773
+ phase = "commentary" if is_commentary else "final_answer"
774
+ item_id = stable_item_id(framework, run_id, "message", phase)
775
+ if run is not None and not is_commentary:
776
+ run.final_answer_item_id = item_id
768
777
  op: str = "replace" if chunk.get("replace") else "append"
769
- events = ensure_started(item_id=item_id, item_kind="message", phase="commentary")
778
+ events = ensure_started(item_id=item_id, item_kind="message", phase=phase)
770
779
  events.append(
771
780
  ItemUpdated(
772
781
  **self._canonical_kwargs( # type: ignore[attr-defined]
ksadk/runtime/adapter.py CHANGED
@@ -417,6 +417,7 @@ class RuntimeAdapter(ABC):
417
417
  inject=_unavailable("runtime_no_native_inject"),
418
418
  checkpoint=_unavailable(),
419
419
  durable_restore=_unavailable(),
420
+ interaction_mode="unavailable",
420
421
  )
421
422
 
422
423
  def native_capabilities(self) -> dict[str, object]:
ksadk/runtime/factory.py CHANGED
@@ -3,7 +3,10 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import dataclasses
6
+ import hashlib
6
7
  import os
8
+ import re
9
+ import shutil
7
10
  import tempfile
8
11
  from pathlib import Path
9
12
  from typing import Any
@@ -11,8 +14,7 @@ from typing import Any
11
14
  from ksadk.codex.client import AsyncCodexClient
12
15
  from ksadk.codex.runtime import CodexRuntimeAdapter
13
16
  from ksadk.runners.base_runner import BaseRunner
14
- from ksadk.runtime.adapter import StartRequest
15
- from ksadk.runtime.adapter import RuntimeAdapter, RuntimeRegistry
17
+ from ksadk.runtime.adapter import RuntimeAdapter, RuntimeRegistry, StartRequest
16
18
  from ksadk.runtime.framework_adapters import ADKRuntimeAdapter, LangGraphRuntimeAdapter
17
19
  from ksadk.runtime.launch import RuntimeLaunchContext
18
20
 
@@ -88,6 +90,19 @@ def kernel_start_request_defaults(context: RuntimeLaunchContext) -> dict[str, An
88
90
  }
89
91
  if base_instructions:
90
92
  request_config["base_instructions"] = base_instructions
93
+ collaboration_mode = str(config.get("collaboration_mode") or "").strip().lower()
94
+ if collaboration_mode:
95
+ if collaboration_mode not in {"default", "plan"}:
96
+ raise ValueError("Codex collaboration_mode must be default or plan")
97
+ request_config["collaboration_mode"] = collaboration_mode
98
+ goal_objective = str(config.get("goal_objective") or "").strip()
99
+ if goal_objective:
100
+ request_config["goal_objective"] = goal_objective
101
+ raw_skills = config.get("skills")
102
+ if isinstance(raw_skills, (list, tuple)):
103
+ request_config["skills"] = [
104
+ dict(item) for item in raw_skills if isinstance(item, dict)
105
+ ]
91
106
  defaults["config"] = request_config
92
107
  return defaults
93
108
 
@@ -158,6 +173,7 @@ def _manifest_mcp_overrides(config: dict[str, Any]) -> list[str]:
158
173
  def _create_codex(context: RuntimeLaunchContext) -> RuntimeAdapter:
159
174
  client_factory = context.services.codex_client_factory or AsyncCodexClient
160
175
  config = dict(context.config)
176
+ bound_skill_paths: dict[str, str] = {}
161
177
  overrides = list(config.get("codex_overrides") or [])
162
178
  manifest_mcp_overrides = _manifest_mcp_overrides(config)
163
179
  overrides.extend(item for item in manifest_mcp_overrides if item not in overrides)
@@ -185,6 +201,9 @@ def _create_codex(context: RuntimeLaunchContext) -> RuntimeAdapter:
185
201
  )
186
202
  env = dict(getattr(base_cfg, "env", None) or {})
187
203
  isolated_home = _isolated_codex_home(context.project_dir)
204
+ bound_skill_paths = _materialize_bound_codex_skills(
205
+ isolated_home, config.get("skills")
206
+ )
188
207
  env.setdefault("CODEX_HOME", str(isolated_home))
189
208
  # HOME 级隔离:codex app-server 还会按约定扫 ~/.agents/skills、
190
209
  # ~/.claude/skills 等宿主目录,仅设 CODEX_HOME 挡不住。隔离 HOME 后这些
@@ -219,6 +238,7 @@ def _create_codex(context: RuntimeLaunchContext) -> RuntimeAdapter:
219
238
  client,
220
239
  sandbox_read_only=sandbox_read_only,
221
240
  turn_timeout_seconds=float(timeout) if timeout is not None else None,
241
+ bound_skill_paths=bound_skill_paths,
222
242
  )
223
243
 
224
244
 
@@ -263,6 +283,60 @@ def _isolated_codex_home(project_dir: Any) -> Path:
263
283
  return fallback_home
264
284
 
265
285
 
286
+ def _materialize_bound_codex_skills(
287
+ codex_home: Path, value: Any
288
+ ) -> dict[str, str]:
289
+ """Expose immutable Bundle Skills through Codex's native skill catalog.
290
+
291
+ Passing an arbitrary ``SkillInput`` path is accepted by the Python SDK but
292
+ ignored by the real App Server unless the Skill is discoverable by the
293
+ native host. Copy each content-addressed Bundle Skill into the isolated
294
+ CODEX_HOME so the host owns discovery while the Bundle remains read-only.
295
+ Content-addressed directory names avoid overwriting user-managed Skills
296
+ when an explicit KSADK_CODEX_HOME is used.
297
+ """
298
+
299
+ if not isinstance(value, (list, tuple)):
300
+ return {}
301
+ skills_root = codex_home / "skills"
302
+ skills_root.mkdir(parents=True, exist_ok=True)
303
+ installed: dict[str, str] = {}
304
+ for item in value:
305
+ if not isinstance(item, dict):
306
+ continue
307
+ name = str(item.get("name") or "").strip()
308
+ raw_path = str(item.get("path") or "").strip()
309
+ if not name or not raw_path:
310
+ continue
311
+ skill_file = Path(raw_path).resolve()
312
+ if skill_file.name != "SKILL.md" or not skill_file.is_file():
313
+ continue
314
+ source_dir = skill_file.parent
315
+ digest = hashlib.sha256()
316
+ for source in sorted(path for path in source_dir.rglob("*") if path.is_file()):
317
+ digest.update(source.relative_to(source_dir).as_posix().encode("utf-8"))
318
+ digest.update(b"\0")
319
+ digest.update(source.read_bytes())
320
+ digest.update(b"\0")
321
+ safe_name = re.sub(r"[^A-Za-z0-9._-]+", "-", name).strip(".-") or "skill"
322
+ target = skills_root / f"ksadk-{digest.hexdigest()[:16]}-{safe_name}"
323
+ if target.is_dir():
324
+ installed[name] = str(target / "SKILL.md")
325
+ continue
326
+ staging = Path(tempfile.mkdtemp(prefix=".ksadk-skill-", dir=skills_root))
327
+ try:
328
+ shutil.copytree(source_dir, staging, dirs_exist_ok=True)
329
+ try:
330
+ staging.replace(target)
331
+ except FileExistsError:
332
+ pass
333
+ finally:
334
+ if staging.exists():
335
+ shutil.rmtree(staging)
336
+ installed[name] = str(target / "SKILL.md")
337
+ return installed
338
+
339
+
266
340
  def _apply_codex_overrides(client: Any, overrides: Any) -> None:
267
341
  """Append MCP server --config overrides to the client's CodexConfig."""
268
342
  try:
@@ -63,6 +63,24 @@ class LangGraphRuntimeAdapter(RunnerRuntimeAdapter):
63
63
  def __init__(self, runner: BaseRunner) -> None:
64
64
  super().__init__(runner, runtime_type="langgraph")
65
65
 
66
+ def capabilities(self): # noqa: ANN201
67
+ """Advertise checkpoint interaction only when LangGraph can resume it.
68
+
69
+ ``RunnerRuntimeAdapter`` is intentionally conservative because a
70
+ generic checkpoint is not proof of original-interrupt delivery. The
71
+ LangGraph adapter owns that mapping, so it is the only runner-family
72
+ adapter that may publish ``durable_resume``.
73
+ """
74
+
75
+ matrix = super().capabilities()
76
+ return matrix.model_copy(
77
+ update={
78
+ "interaction_mode": (
79
+ "durable_resume" if matrix.resume.supported else "unavailable"
80
+ )
81
+ }
82
+ )
83
+
66
84
  async def _resume_native(
67
85
  self,
68
86
  handle: RunHandle,