agentengine-sdk-python 0.8.1__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 (476) hide show
  1. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.3.dist-info}/METADATA +25 -12
  2. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.3.dist-info}/RECORD +421 -232
  3. ksadk/_build_provenance.json +1 -0
  4. ksadk/a2a/_space_client_events.py +381 -0
  5. ksadk/a2a/event_adapter.py +156 -64
  6. ksadk/a2a/executor.py +179 -63
  7. ksadk/a2a/space_client.py +6 -333
  8. ksadk/a2ui/core.py +222 -53
  9. ksadk/agui/_agent_helpers.py +247 -0
  10. ksadk/agui/a2ui_projection.py +12 -5
  11. ksadk/agui/agent.py +443 -259
  12. ksadk/api/client.py +528 -27
  13. ksadk/builders/code_builder.py +284 -8
  14. ksadk/builders/container_builder.py +8 -1
  15. ksadk/builders/managed_runtime_builder.py +42 -35
  16. ksadk/cli/__init__.py +23 -0
  17. ksadk/cli/cmd_dashboard.py +15 -1
  18. ksadk/cli/cmd_deploy.py +10 -2
  19. ksadk/cli/cmd_eval.py +66 -9
  20. ksadk/cli/cmd_evalset.py +284 -0
  21. ksadk/cli/cmd_files.py +5 -1
  22. ksadk/cli/cmd_hermes.py +1 -126
  23. ksadk/cli/cmd_invoke.py +28 -38
  24. ksadk/cli/cmd_managed_runtime.py +95 -0
  25. ksadk/cli/cmd_observe.py +85 -0
  26. ksadk/cli/cmd_openclaw.py +1 -435
  27. ksadk/cli/cmd_plugin.py +657 -0
  28. ksadk/cli/cmd_replay.py +28 -4
  29. ksadk/cli/cmd_studio.py +99 -8
  30. ksadk/cli/cmd_web.py +10 -2
  31. ksadk/cli/hermes_env.py +151 -0
  32. ksadk/cli/invoke_payload.py +49 -0
  33. ksadk/cli/openclaw_env.py +458 -0
  34. ksadk/codex/client.py +180 -30
  35. ksadk/codex/runtime.py +475 -331
  36. ksadk/configs/env_registry.py +94 -12
  37. ksadk/configs/env_registry_pcm.py +181 -0
  38. ksadk/configs/env_var_spec.py +18 -0
  39. ksadk/configs/global_config.py +5 -0
  40. ksadk/configs/settings.py +9 -0
  41. ksadk/context_engine/__init__.py +98 -0
  42. ksadk/context_engine/assembler.py +176 -0
  43. ksadk/context_engine/baseline.py +419 -0
  44. ksadk/context_engine/cache_observability.py +228 -0
  45. ksadk/context_engine/capabilities.py +443 -0
  46. ksadk/context_engine/contributors.py +409 -0
  47. ksadk/context_engine/hosted_pipeline.py +417 -0
  48. ksadk/context_engine/models.py +117 -0
  49. ksadk/context_engine/planner.py +698 -0
  50. ksadk/context_engine/policies.py +319 -0
  51. ksadk/context_engine/projection.py +31 -0
  52. ksadk/context_engine/shadow_plan.py +266 -0
  53. ksadk/context_engine/tokenizer.py +164 -0
  54. ksadk/conversations/__init__.py +19 -0
  55. ksadk/conversations/context.py +230 -30
  56. ksadk/conversations/contracts.py +266 -0
  57. ksadk/conversations/message_projection.py +308 -26
  58. ksadk/conversations/model_context.py +49 -3
  59. ksadk/conversations/projector.py +616 -0
  60. ksadk/conversations/reducer.py +98 -0
  61. ksadk/conversations/runtime_compaction.py +207 -5
  62. ksadk/conversations/runtime_input.py +184 -2
  63. ksadk/conversations/runtime_invocation.py +81 -3
  64. ksadk/conversations/runtime_observability.py +141 -0
  65. ksadk/conversations/runtime_payloads.py +42 -0
  66. ksadk/conversations/runtime_preparation.py +272 -2
  67. ksadk/conversations/runtime_resume.py +20 -1
  68. ksadk/conversations/runtime_stream_events.py +94 -9
  69. ksadk/conversations/semantic_summary.py +311 -1
  70. ksadk/conversations/session_lock.py +51 -0
  71. ksadk/deployment/env_forward.py +72 -0
  72. ksadk/deployment/managed_runtime.py +5 -0
  73. ksadk/deployment/providers/serverless.py +143 -69
  74. ksadk/evaluation/__init__.py +32 -0
  75. ksadk/evaluation/a2a_adapter.py +9 -1
  76. ksadk/evaluation/adapters.py +18 -5
  77. ksadk/evaluation/agent_eval_client.py +395 -0
  78. ksadk/evaluation/cloud_binding.py +80 -0
  79. ksadk/evaluation/cloud_converter.py +194 -0
  80. ksadk/evaluation/cloud_service.py +199 -0
  81. ksadk/evaluation/contracts.py +73 -4
  82. ksadk/evaluation/evaluators.py +183 -17
  83. ksadk/evaluation/evidence.py +258 -0
  84. ksadk/evaluation/executor.py +31 -6
  85. ksadk/evaluation/local_adapter.py +550 -0
  86. ksadk/evaluation/service_env.py +27 -0
  87. ksadk/evaluation/studio_build_adapter.py +253 -0
  88. ksadk/evaluation/target.py +15 -6
  89. ksadk/events/__init__.py +13 -13
  90. ksadk/events/_v1_compat/__init__.py +1 -0
  91. ksadk/events/_v1_compat/models.py +299 -0
  92. ksadk/events/_v1_compat/parser.py +247 -0
  93. ksadk/events/_v1_compat/projection.py +755 -0
  94. ksadk/events/adapters/__init__.py +34 -0
  95. ksadk/events/adapters/_a2a_snapshot.py +622 -0
  96. ksadk/events/adapters/_a2a_support.py +275 -0
  97. ksadk/events/adapters/_codex_interactions.py +375 -0
  98. ksadk/events/adapters/_codex_items.py +801 -0
  99. ksadk/events/adapters/_codex_validators.py +311 -0
  100. ksadk/events/adapters/_langgraph_support.py +785 -0
  101. ksadk/events/adapters/a2a.py +841 -0
  102. ksadk/events/adapters/adk.py +503 -0
  103. ksadk/events/adapters/codex.py +907 -0
  104. ksadk/events/adapters/langgraph.py +745 -0
  105. ksadk/events/canonical.py +440 -0
  106. ksadk/events/canonical_replay.py +497 -0
  107. ksadk/events/canonical_store.py +468 -0
  108. ksadk/events/cold_recovery.py +257 -0
  109. ksadk/events/content.py +89 -0
  110. ksadk/events/identity.py +86 -0
  111. ksadk/events/pipeline.py +455 -0
  112. ksadk/events/projections.py +66 -0
  113. ksadk/events/reducer.py +661 -0
  114. ksadk/events/replay.py +3 -36
  115. ksadk/events/runtime_event.py +20 -265
  116. ksadk/events/session_event.py +324 -0
  117. ksadk/events/store.py +3 -316
  118. ksadk/events/v1_compat.py +41 -0
  119. ksadk/harness/__init__.py +45 -35
  120. ksadk/harness/runtime.py +220 -58
  121. ksadk/identity/resolver.py +4 -3
  122. ksadk/interaction/__init__.py +28 -0
  123. ksadk/interaction/contracts.py +136 -0
  124. ksadk/interaction/ledger.py +189 -0
  125. ksadk/interaction/provider.py +122 -0
  126. ksadk/interaction/providers/__init__.py +52 -0
  127. ksadk/interaction/providers/adk.py +40 -0
  128. ksadk/interaction/providers/codex.py +94 -0
  129. ksadk/interaction/providers/langgraph.py +58 -0
  130. ksadk/kernel/__init__.py +117 -0
  131. ksadk/kernel/authorization.py +258 -0
  132. ksadk/kernel/bootstrap.py +1074 -0
  133. ksadk/kernel/contract_fingerprints.py +62 -0
  134. ksadk/kernel/contracts.py +370 -0
  135. ksadk/kernel/control.py +220 -0
  136. ksadk/kernel/errors.py +79 -0
  137. ksadk/kernel/ingress.py +1123 -0
  138. ksadk/kernel/mapping.py +111 -0
  139. ksadk/kernel/memory_store.py +1134 -0
  140. ksadk/kernel/postgres_store.py +1757 -0
  141. ksadk/kernel/recovery.py +452 -0
  142. ksadk/kernel/runtime_identity.py +103 -0
  143. ksadk/kernel/sql/001_agent_kernel.sql +120 -0
  144. ksadk/kernel/sqlite_store.py +1411 -0
  145. ksadk/kernel/state.py +145 -0
  146. ksadk/kernel/store.py +232 -0
  147. ksadk/kernel/worker.py +964 -0
  148. ksadk/knowledge_base/client.py +7 -0
  149. ksadk/knowledge_base/service.py +32 -5
  150. ksadk/memory/__init__.py +61 -0
  151. ksadk/memory/adk/backends/base_ltm_backend.py +96 -1
  152. ksadk/memory/adk/backends/http_ltm_backend.py +14 -5
  153. ksadk/memory/adk/backends/inmemory_ltm_backend.py +136 -3
  154. ksadk/memory/adk/backends/sdk_ltm_backend.py +384 -2
  155. ksadk/memory/adk/backends/sqlite_ltm_backend.py +95 -0
  156. ksadk/memory/coordinator.py +461 -0
  157. ksadk/memory/events.py +202 -0
  158. ksadk/memory/extraction.py +228 -0
  159. ksadk/memory/ltm_backend_factory.py +16 -3
  160. ksadk/memory/models.py +316 -0
  161. ksadk/memory/policy.py +215 -0
  162. ksadk/memory/provider.py +47 -0
  163. ksadk/memory/provider_adapter.py +159 -0
  164. ksadk/memory/provider_resolver.py +71 -0
  165. ksadk/memory/providers/__init__.py +5 -0
  166. ksadk/memory/providers/local_sqlite.py +497 -0
  167. ksadk/memory/resolved_policy.py +145 -0
  168. ksadk/memory/service.py +188 -6
  169. ksadk/model_proxy/bootstrap.py +16 -9
  170. ksadk/model_proxy/detect.py +202 -9
  171. ksadk/model_proxy/namespace.py +39 -15
  172. ksadk/model_proxy/server.py +24 -5
  173. ksadk/model_proxy/transform.py +60 -2
  174. ksadk/observability/__init__.py +25 -0
  175. ksadk/observability/session_log.py +420 -0
  176. ksadk/observability/trajectory.py +105 -0
  177. ksadk/plugins/__init__.py +167 -0
  178. ksadk/plugins/bridges/__init__.py +45 -0
  179. ksadk/plugins/bridges/codex.py +552 -0
  180. ksadk/plugins/bridges/dsh.py +1116 -0
  181. ksadk/plugins/builtins.py +747 -0
  182. ksadk/plugins/bundle.py +246 -0
  183. ksadk/plugins/bundle_security.py +167 -0
  184. ksadk/plugins/composition.py +529 -0
  185. ksadk/plugins/context_contributor.py +437 -0
  186. ksadk/plugins/contracts.py +451 -0
  187. ksadk/plugins/dsh_toolchain.py +935 -0
  188. ksadk/plugins/ecosystem_bridge.py +1105 -0
  189. ksadk/plugins/ecosystem_probe.py +191 -0
  190. ksadk/plugins/host.py +724 -0
  191. ksadk/plugins/providers/__init__.py +85 -0
  192. ksadk/plugins/providers/bundles/ksadk-codex/cordis.patch.yml +3 -0
  193. ksadk/plugins/providers/bundles/ksadk-codex/index.mjs +18 -0
  194. ksadk/plugins/providers/bundles/ksadk-codex/package.json +19 -0
  195. ksadk/plugins/providers/bundles/ksadk-harness/cordis.patch.yml +3 -0
  196. ksadk/plugins/providers/bundles/ksadk-harness/index.mjs +19 -0
  197. ksadk/plugins/providers/bundles/ksadk-harness/package.json +19 -0
  198. ksadk/plugins/providers/codex.py +722 -0
  199. ksadk/plugins/providers/codex_dsh.py +83 -0
  200. ksadk/plugins/providers/dsh.py +951 -0
  201. ksadk/plugins/providers/dsh_descriptor_host.py +226 -0
  202. ksadk/plugins/providers/harness.py +627 -0
  203. ksadk/plugins/providers/harness_dsh.py +91 -0
  204. ksadk/plugins/providers/legacy.py +215 -0
  205. ksadk/plugins/providers/legacy_catalog.py +100 -0
  206. ksadk/plugins/providers/shipped_dsh.py +370 -0
  207. ksadk/plugins/resolver.py +331 -0
  208. ksadk/plugins/subagent_providers/__init__.py +12 -0
  209. ksadk/plugins/subagent_providers/codex.py +443 -0
  210. ksadk/plugins/subagents.py +333 -0
  211. ksadk/prompts/__init__.py +71 -0
  212. ksadk/prompts/compiler.py +215 -0
  213. ksadk/prompts/models.py +82 -0
  214. ksadk/prompts/projection.py +89 -0
  215. ksadk/prompts/resolved.py +137 -0
  216. ksadk/prompts/sources.py +233 -0
  217. ksadk/runners/_langgraph_runner_streams.py +822 -0
  218. ksadk/runners/adk_runner.py +5 -5
  219. ksadk/runners/base_runner.py +16 -0
  220. ksadk/runners/factory.py +1 -2
  221. ksadk/runners/langgraph_runner.py +198 -437
  222. ksadk/runtime/_runner_adapter/__init__.py +1 -0
  223. ksadk/runtime/_runner_adapter/stream_mapping.py +797 -0
  224. ksadk/runtime/adapter.py +119 -2
  225. ksadk/runtime/conversation_execution.py +316 -182
  226. ksadk/runtime/executor.py +144 -3
  227. ksadk/runtime/factory.py +268 -8
  228. ksadk/runtime/framework_adapters.py +18 -0
  229. ksadk/runtime/hosted_finalizer.py +268 -0
  230. ksadk/runtime/launch.py +14 -1
  231. ksadk/runtime/preprocessing.py +25 -3
  232. ksadk/runtime/runner_adapter.py +293 -508
  233. ksadk/runtime/usage.py +33 -0
  234. ksadk/sandbox/__init__.py +2 -1
  235. ksadk/sandbox/backends/e2b.py +35 -2
  236. ksadk/sandbox/factory.py +72 -0
  237. ksadk/scheduler/__init__.py +18 -0
  238. ksadk/scheduler/calendar.py +102 -0
  239. ksadk/scheduler/contracts.py +254 -0
  240. ksadk/scheduler/dispatcher.py +129 -0
  241. ksadk/scheduler/engine.py +416 -0
  242. ksadk/scheduler/sqlite_store.py +563 -0
  243. ksadk/server/composition.py +8 -0
  244. ksadk/server/factory.py +69 -6
  245. ksadk/server/routes/kernel_ingress.py +258 -0
  246. ksadk/server/routes/openai_compat.py +88 -8
  247. ksadk/server/routes/projection.py +35 -20
  248. ksadk/server/routes/run.py +94 -0
  249. ksadk/server/routes/sessions.py +2 -0
  250. ksadk/server/routes/streaming.py +5 -2
  251. ksadk/server/static/assets/{ArtifactsPanel-CakCqxEN.js → ArtifactsPanel-10GPXIJ6.js} +1 -1
  252. ksadk/server/static/assets/{CodeBlock-CynLknyO.js → CodeBlock-BXk1l-G1.js} +4 -4
  253. ksadk/server/static/assets/MathMessageMarkdown-BorAY7qD.css +1 -0
  254. ksadk/server/static/assets/{MathMessageMarkdown-3sQdOysS.js → MathMessageMarkdown-D9uB55Oh.js} +4 -4
  255. ksadk/server/static/assets/MermaidBlock--OEYoXIJ.js +312 -0
  256. ksadk/server/static/assets/NativeTerminalPanel-DrEe1syq.js +1 -0
  257. ksadk/server/static/assets/abnfDiagram-VCTEODGH-eeTFJLvo.js +1 -0
  258. ksadk/server/static/assets/arc-S3ZNbIAd.js +1 -0
  259. ksadk/server/static/assets/architecture-7GRP2DOG-Dk9nRQ6-.js +1 -0
  260. ksadk/server/static/assets/architectureDiagram-5GKGNRK7-CRjgEJDX.js +36 -0
  261. ksadk/server/static/assets/blockDiagram-NRAW4CY4-C7bsBoNH.js +129 -0
  262. ksadk/server/static/assets/c4Diagram-UCG6FXSJ-BX3c33Ob.js +38 -0
  263. ksadk/server/static/assets/channel-s354Yo6o.js +1 -0
  264. ksadk/server/static/assets/chunk-2Q5K7J3B-CDpPpKR5.js +1 -0
  265. ksadk/server/static/assets/{chunk-FMBD7UC4-B_fH2YuH.js → chunk-5VM5RSS4-bZ7ws-8m.js} +1 -1
  266. ksadk/server/static/assets/{chunk-ND2GUHAM-D6rFLaVV.js → chunk-F27PBJKO-D2r0pvhY.js} +1 -1
  267. ksadk/server/static/assets/chunk-G27WJ6UU-CZwF3oUP.js +231 -0
  268. ksadk/server/static/assets/{chunk-4BX2VUAB-B9drTgOW.js → chunk-JWPE2WC7-DigFYCML.js} +1 -1
  269. ksadk/server/static/assets/chunk-LCL6LL3I-CnorGV5q.js +206 -0
  270. ksadk/server/static/assets/chunk-POPQ4Y6H-CexntQA-.js +1 -0
  271. ksadk/server/static/assets/chunk-RHFEMEQ7-CHTC7UxA.js +168 -0
  272. ksadk/server/static/assets/chunk-SVP7TREG-BC1EWt_-.js +88 -0
  273. ksadk/server/static/assets/chunk-XXDRQBXY-BTG24xN0.js +1 -0
  274. ksadk/server/static/assets/classDiagram-DTDB5LWJ-Dd0nucl9.js +1 -0
  275. ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-Dd0nucl9.js +1 -0
  276. ksadk/server/static/assets/{cose-bilkent-S5V4N54A-ChNs_Mdk.js → cose-bilkent-JH36ORCC-CkonRdAS.js} +1 -1
  277. ksadk/server/static/assets/cynefin-OW5HDTMX-BBlT4uMf.js +1 -0
  278. ksadk/server/static/assets/cynefinDiagram-5FMLGOSQ-DEOCBe15.js +62 -0
  279. ksadk/server/static/assets/cytoscape.esm-CyCl8rPi.js +321 -0
  280. ksadk/server/static/assets/dagre-3AP2YEHR-DBThPuLa.js +4 -0
  281. ksadk/server/static/assets/dagre-ClTPrTht.js +1 -0
  282. ksadk/server/static/assets/diagram-S7CK7UJ4-DsZHrnpJ.js +30 -0
  283. ksadk/server/static/assets/diagram-UQ7AKVKN-BJR4nhVr.js +41 -0
  284. ksadk/server/static/assets/diagram-VSXAHHWV-xzcERX0a.js +3 -0
  285. ksadk/server/static/assets/diagram-VX7I27RA-tn2xM1xu.js +24 -0
  286. ksadk/server/static/assets/diagram-Z3DM3KII-BZPGBkw5.js +24 -0
  287. ksadk/server/static/assets/{dist-gLnrGsoU.js → dist-4zN1_tq5.js} +1 -1
  288. ksadk/server/static/assets/{dist-C1sz5Eo3.js → dist-8VWI-2bZ.js} +1 -1
  289. ksadk/server/static/assets/{dist-BjOdlDjj.js → dist-B-46q3Hk.js} +1 -1
  290. ksadk/server/static/assets/{dist-o2OVmKvt.js → dist-B0gdk4rl.js} +1 -1
  291. ksadk/server/static/assets/{dist-4N2NMVYg.js → dist-B1oWRmrH.js} +1 -1
  292. ksadk/server/static/assets/{dist-CmZHzlwZ.js → dist-BI1qQH4n.js} +1 -1
  293. ksadk/server/static/assets/{dist-X4TnqwOK.js → dist-BJLUwH6W.js} +1 -1
  294. ksadk/server/static/assets/{dist-B5oS1zcj.js → dist-Bbwqa8TG.js} +1 -1
  295. ksadk/server/static/assets/{dist-j6Dxmf_j.js → dist-Btyo4vX4.js} +1 -1
  296. ksadk/server/static/assets/{dist-DQU9EWw-.js → dist-C71CsujO.js} +1 -1
  297. ksadk/server/static/assets/{dist-BIlKoW0k.js → dist-CXpH_Rdd.js} +1 -1
  298. ksadk/server/static/assets/{dist-DJN_6Y39.js → dist-Cq4OL6nO.js} +1 -1
  299. ksadk/server/static/assets/{dist-Dli7qW-B.js → dist-D1V3f1B4.js} +1 -1
  300. ksadk/server/static/assets/{dist-fhOuWds5.js → dist-DJmCPRVB.js} +1 -1
  301. ksadk/server/static/assets/{dist-CWV4lDr1.js → dist-DQAB0qn_.js} +2 -2
  302. ksadk/server/static/assets/{dist-D6MpCZtz.js → dist-DUwKa_l6.js} +1 -1
  303. ksadk/server/static/assets/dist-DdRBIceA.js +1 -0
  304. ksadk/server/static/assets/{dist-DWKByxWV.js → dist-DfGFrb0v.js} +1 -1
  305. ksadk/server/static/assets/{dist-DsHPdk3W.js → dist-DjhwGUgZ.js} +1 -1
  306. ksadk/server/static/assets/{dist-B-jNR6Vn.js → dist-DyqV4fL4.js} +1 -1
  307. ksadk/server/static/assets/{dist-BB3-OwTQ.js → dist-M67Hwu0n.js} +1 -1
  308. ksadk/server/static/assets/{dist-_pHPTyZ-.js → dist-u8tVH5Ak.js} +1 -1
  309. ksadk/server/static/assets/ebnfDiagram-PWID7BFC-DVfMsLge.js +1 -0
  310. ksadk/server/static/assets/erDiagram-SSCWMZ5O-CWEZSYaw.js +99 -0
  311. ksadk/server/static/assets/eventmodeling-NTZA5JFV-Dr5okd8V.js +1 -0
  312. ksadk/server/static/assets/flowDiagram-A5DVABFB-VEO-cVcO.js +1 -0
  313. ksadk/server/static/assets/ganttDiagram-EL5Y4UJY-TWIxoyYL.js +292 -0
  314. ksadk/server/static/assets/gitGraph-4MIJSDKK-CKiKY9Cf.js +1 -0
  315. ksadk/server/static/assets/gitGraphDiagram-WWUBYQGX-DDh2hd2S.js +106 -0
  316. ksadk/server/static/assets/index-B2k_urY8.js +241 -0
  317. ksadk/server/static/assets/index-ILPt6IWF.css +2 -0
  318. ksadk/server/static/assets/info-A6RAGUB7-ubA4Nm_p.js +1 -0
  319. ksadk/server/static/assets/infoDiagram-RXCK75RN-CgoyWiDR.js +2 -0
  320. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-K0OOfVna.js → ishikawaDiagram-5VMMS53U-CyRIu_om.js} +6 -6
  321. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-8LeOJaZp.js → journeyDiagram-EYS64GPL-DN5JXHzN.js} +6 -6
  322. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-C6uCgZrW.js → kanban-definition-3QL26DDD-CNP3ui66.js} +10 -10
  323. ksadk/server/static/assets/katex-DWEzQy1d.js +257 -0
  324. ksadk/server/static/assets/{linear-hdBcYzM8.js → linear-C-qHp7d2.js} +1 -1
  325. ksadk/server/static/assets/mermaid-parser.core-Cl-K943T.js +166 -0
  326. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-Bjy_2v4_.js → mindmap-definition-FBJOCRG2-D5DB5mYm.js} +29 -29
  327. ksadk/server/static/assets/packet-AYTQ26CC-C7R3NUlK.js +1 -0
  328. ksadk/server/static/assets/pegDiagram-XKGWAZYB-BQVc9ia_.js +1 -0
  329. ksadk/server/static/assets/pie-WAS4IAKB-BIT6qjr3.js +1 -0
  330. ksadk/server/static/assets/pieDiagram-E7YTZNPT-B6UCAvtY.js +39 -0
  331. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-BmkRbwCg.js → quadrantDiagram-AXDQQJYC-mIr2d3eb.js} +6 -6
  332. ksadk/server/static/assets/radar-RG4KPBEZ-l5noef9X.js +1 -0
  333. ksadk/server/static/assets/railroad-74A4TZTK-Baea6ZvM.js +1 -0
  334. ksadk/server/static/assets/railroad-abnf-HS5TGJTU-OKn8zZnU.js +1 -0
  335. ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-DdKAKC4l.js +1 -0
  336. ksadk/server/static/assets/railroad-peg-WCYAUIDC-Bds8udlG.js +1 -0
  337. ksadk/server/static/assets/railroadDiagram-O6MQD6OU-Dt3FFDmK.js +1 -0
  338. ksadk/server/static/assets/requirementDiagram-EFPCY7ZU-Cz_vQtgF.js +84 -0
  339. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-l6iXmU65.js → sankeyDiagram-P5KCCOFB-wlO1TsH5.js} +7 -7
  340. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-Cq6UBeQB.js → sequenceDiagram-WJ2MYXX4-CHRbElYq.js} +10 -10
  341. ksadk/server/static/assets/sizeCapture-X5ZJPWSS-DXX54iyO.js +1 -0
  342. ksadk/server/static/assets/stateDiagram-HBIQ2CUA-Dp_lw9II.js +1 -0
  343. ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-5b-8o2iS.js +1 -0
  344. ksadk/server/static/assets/swimlanes-XN3QIQJK-D8O5UlLG.js +1 -0
  345. ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-uf6WPRA4.js +8 -0
  346. ksadk/server/static/assets/{timeline-definition-PNZ67QCA-DEz24vV0.js → timeline-definition-24CTP7MA-8mfKcxvG.js} +7 -7
  347. ksadk/server/static/assets/treeView-Q6P3EWNA-CK5CSrSO.js +1 -0
  348. ksadk/server/static/assets/treemap-WGGIJYW6-Ch2BKgAM.js +1 -0
  349. ksadk/server/static/assets/vennDiagram-4TSXK5OY-EnNl0DE7.js +34 -0
  350. ksadk/server/static/assets/wardley-WFR3VGLG-YW3Q0ml_.js +1 -0
  351. ksadk/server/static/assets/wardleyDiagram-VM6X3IG4-DBu5fbYr.js +78 -0
  352. ksadk/server/static/assets/xychartDiagram-S5SC5T6Z-BSVXs4IY.js +7 -0
  353. ksadk/server/static/index.html +2 -2
  354. ksadk/sessions/__init__.py +26 -9
  355. ksadk/sessions/_local_service_sync.py +688 -0
  356. ksadk/sessions/_local_tables.py +21 -0
  357. ksadk/sessions/_postgres_schema.py +305 -0
  358. ksadk/sessions/_postgres_tables.py +15 -0
  359. ksadk/sessions/base.py +80 -1
  360. ksadk/sessions/in_memory.py +53 -2
  361. ksadk/sessions/local_service.py +63 -604
  362. ksadk/sessions/postgres_service.py +58 -148
  363. ksadk/sessions/resilient.py +35 -1
  364. ksadk/skills/service_client.py +48 -0
  365. ksadk/studio/agent_lifecycle.py +26 -20
  366. ksadk/studio/api.py +1063 -69
  367. ksadk/studio/api_catalog_routes.py +5 -0
  368. ksadk/studio/api_contracts.py +148 -6
  369. ksadk/studio/api_memory_routes.py +82 -0
  370. ksadk/studio/api_plugin_routes.py +480 -0
  371. ksadk/studio/attachment_store.py +187 -0
  372. ksadk/studio/authoring.py +259 -28
  373. ksadk/studio/authoring_coordinator.py +809 -67
  374. ksadk/studio/builder.py +197 -40
  375. ksadk/studio/capabilities.py +17 -0
  376. ksadk/studio/cloud.py +1920 -95
  377. ksadk/studio/codex_agent_service.py +137 -15
  378. ksadk/studio/codex_authoring.py +469 -0
  379. ksadk/studio/codex_builder.py +177 -25
  380. ksadk/studio/codex_manifest.py +71 -22
  381. ksadk/studio/codex_run.py +59 -4
  382. ksadk/studio/compatibility_report.py +445 -0
  383. ksadk/studio/compiler.py +7 -1
  384. ksadk/studio/contracts.py +234 -4
  385. ksadk/studio/dsh_provider_registration.py +768 -0
  386. ksadk/studio/event_store.py +79 -101
  387. ksadk/studio/framework_run.py +264 -3
  388. ksadk/studio/hosted_kernel.py +317 -0
  389. ksadk/studio/manifest_resolver.py +131 -0
  390. ksadk/studio/model_client.py +95 -9
  391. ksadk/studio/model_profile_service.py +1 -1
  392. ksadk/studio/operations.py +47 -5
  393. ksadk/studio/otel_trace.py +348 -42
  394. ksadk/studio/pcm_memory.py +85 -0
  395. ksadk/studio/plugin_composition.py +301 -0
  396. ksadk/studio/plugin_kernel_adapter.py +128 -0
  397. ksadk/studio/plugin_runtime.py +652 -0
  398. ksadk/studio/repository.py +23 -2
  399. ksadk/studio/resource_catalog.py +49 -1
  400. ksadk/studio/run_service.py +1266 -202
  401. ksadk/studio/runtime_source.py +51 -17
  402. ksadk/studio/scheduler_runtime.py +374 -0
  403. ksadk/studio/scheduler_service.py +211 -0
  404. ksadk/studio/service.py +1815 -117
  405. ksadk/studio/shared_web.py +199 -50
  406. ksadk/studio/soul.py +52 -0
  407. ksadk/studio/static/assets/index-DXs1Eq4M.css +1 -0
  408. ksadk/studio/static/assets/index-DeDqwTsD.js +283 -0
  409. ksadk/studio/static/index.html +2 -2
  410. ksadk/studio/templates.py +100 -8
  411. ksadk/studio/validator.py +20 -1
  412. ksadk/studio/workspace.py +31 -32
  413. ksadk/tracing/setup.py +304 -3
  414. ksadk/version.py +1 -1
  415. ksadk_runtime_common/schemas/runtime_event_v1.json +37 -0
  416. ksadk_runtime_common/schemas/runtime_event_v2.json +271 -0
  417. ksadk_runtime_common/workspace_files/path_utils.py +41 -0
  418. ksadk/events/parser.py +0 -191
  419. ksadk/server/static/assets/MathMessageMarkdown-DeYh8QPr.css +0 -1
  420. ksadk/server/static/assets/MermaidBlock-NERD_ef7.js +0 -303
  421. ksadk/server/static/assets/NativeTerminalPanel-CiHWf82q.js +0 -1
  422. ksadk/server/static/assets/arc-DrgjcKhl.js +0 -1
  423. ksadk/server/static/assets/architecture-7EHR7CIX-CpZLRCcY.js +0 -1
  424. ksadk/server/static/assets/architectureDiagram-3BPJPVTR-BXoVRbQ6.js +0 -36
  425. ksadk/server/static/assets/blockDiagram-GPEHLZMM-BnaB3Ywv.js +0 -132
  426. ksadk/server/static/assets/c4Diagram-AAUBKEIU-Dw4weZcp.js +0 -10
  427. ksadk/server/static/assets/channel-Dt3rQTFI.js +0 -1
  428. ksadk/server/static/assets/chunk-2J33WTMH-ChjpLK7j.js +0 -1
  429. ksadk/server/static/assets/chunk-55IACEB6-CK4ROATy.js +0 -1
  430. ksadk/server/static/assets/chunk-727SXJPM-B9KtTTgw.js +0 -206
  431. ksadk/server/static/assets/chunk-AQP2D5EJ-1O1mki3m.js +0 -231
  432. ksadk/server/static/assets/chunk-QZHKN3VN-iXEZtKQc.js +0 -1
  433. ksadk/server/static/assets/classDiagram-4FO5ZUOK-DIveV17Q.js +0 -1
  434. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-DIveV17Q.js +0 -1
  435. ksadk/server/static/assets/cytoscape.esm-DelgaX4f.js +0 -321
  436. ksadk/server/static/assets/dagre-BM42HDAG-D0QXg_MQ.js +0 -4
  437. ksadk/server/static/assets/dagre-Bx709z4p.js +0 -1
  438. ksadk/server/static/assets/diagram-2AECGRRQ-DBDDKwBQ.js +0 -43
  439. ksadk/server/static/assets/diagram-5GNKFQAL-BkwDOxmO.js +0 -10
  440. ksadk/server/static/assets/diagram-KO2AKTUF-Bv8waYvY.js +0 -3
  441. ksadk/server/static/assets/diagram-LMA3HP47-e1VECDdu.js +0 -24
  442. ksadk/server/static/assets/diagram-OG6HWLK6-Ds9adfRl.js +0 -24
  443. ksadk/server/static/assets/dist-B5J2uhNk.js +0 -1
  444. ksadk/server/static/assets/erDiagram-TEJ5UH35-BPItFKH6.js +0 -85
  445. ksadk/server/static/assets/eventmodeling-FCH6USID-DYFSxbqr.js +0 -1
  446. ksadk/server/static/assets/flowDiagram-I6XJVG4X-BbYB9w5Y.js +0 -162
  447. ksadk/server/static/assets/ganttDiagram-6RSMTGT7-TQXa0KXv.js +0 -292
  448. ksadk/server/static/assets/gitGraph-WXDBUCRP-DyNYp6BN.js +0 -1
  449. ksadk/server/static/assets/gitGraphDiagram-PVQCEYII-bKblCnpa.js +0 -106
  450. ksadk/server/static/assets/graphlib-B8gBHxth.js +0 -1
  451. ksadk/server/static/assets/index-R52pJJvg.js +0 -177
  452. ksadk/server/static/assets/index-jJr3_955.css +0 -2
  453. ksadk/server/static/assets/info-J43DQDTF-WUx7qTde.js +0 -1
  454. ksadk/server/static/assets/infoDiagram-5YYISTIA-BGVT7J_i.js +0 -2
  455. ksadk/server/static/assets/katex-CYWseY5E.js +0 -265
  456. ksadk/server/static/assets/mermaid-parser.core-BWrDjSZM.js +0 -161
  457. ksadk/server/static/assets/packet-YPE3B663-BN13y_xl.js +0 -1
  458. ksadk/server/static/assets/pie-LRSECV5Y-ftSLXhhc.js +0 -1
  459. ksadk/server/static/assets/pieDiagram-4H26LBE5-GmmqNiVH.js +0 -30
  460. ksadk/server/static/assets/radar-GUYGQ44K-jaICkIeO.js +0 -1
  461. ksadk/server/static/assets/requirementDiagram-4Y6WPE33-DSWFqL7c.js +0 -84
  462. ksadk/server/static/assets/stateDiagram-AJRCARHV-qzTQpg3Y.js +0 -1
  463. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-BYCoYGAN.js +0 -1
  464. ksadk/server/static/assets/treeView-BLDUP644-Cpib_vlz.js +0 -1
  465. ksadk/server/static/assets/treemap-LRROVOQU-DQxfmVHK.js +0 -1
  466. ksadk/server/static/assets/vennDiagram-CIIHVFJN-CMCixki2.js +0 -34
  467. ksadk/server/static/assets/wardley-L42UT6IY-BQYHnnkU.js +0 -1
  468. ksadk/server/static/assets/wardleyDiagram-YWT4CUSO-CVzB8F9K.js +0 -78
  469. ksadk/server/static/assets/xychartDiagram-2RQKCTM6-BFBqBLc1.js +0 -7
  470. ksadk/studio/evaluation.py +0 -192
  471. ksadk/studio/static/assets/index-CaEN0eTR.css +0 -1
  472. ksadk/studio/static/assets/index-DI2eY_NH.js +0 -252
  473. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.3.dist-info}/WHEEL +0 -0
  474. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.3.dist-info}/entry_points.txt +0 -0
  475. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.3.dist-info}/licenses/LICENSE +0 -0
  476. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,480 @@
1
+ """Studio lifecycle API for DSH plugins and the Codex compatibility bridge."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import hashlib
7
+ import os
8
+ from pathlib import Path
9
+ from typing import Any, Callable
10
+ from urllib.parse import urlencode, urlparse
11
+ from uuid import uuid4
12
+
13
+ from fastapi import FastAPI, Query, Response
14
+ from pydantic import BaseModel, ConfigDict, Field, field_validator
15
+
16
+ from ksadk.plugins.bridges.codex import (
17
+ CodexAppServerPluginBridge,
18
+ CodexBridgeHost,
19
+ CodexPluginApprovalRequired,
20
+ CodexPluginDetail,
21
+ CodexPluginInventory,
22
+ CodexPluginNotFoundError,
23
+ )
24
+ from ksadk.plugins.bridges.dsh import (
25
+ DshBridgeHost,
26
+ DshHostUnavailableError,
27
+ DshPluginApprovalRequired,
28
+ DshPluginInventory,
29
+ DshPluginMutationError,
30
+ DshPluginNotFoundError,
31
+ DshProfilePluginBridge,
32
+ )
33
+ from ksadk.plugins.dsh_toolchain import DshToolchainError, DshToolchainManager
34
+ from ksadk.studio.errors import StudioError
35
+ from ksadk.studio.service import StudioService
36
+
37
+
38
+ class CodexPluginInstallRequest(BaseModel):
39
+ model_config = ConfigDict(extra="forbid", populate_by_name=True)
40
+ marketplace_name: str | None = Field(
41
+ default=None, alias="marketplaceName", min_length=1, max_length=256
42
+ )
43
+ accept_undeclared_permissions: bool = Field(default=False, alias="acceptUndeclaredPermissions")
44
+
45
+
46
+ class DshPluginInstallRequest(BaseModel):
47
+ model_config = ConfigDict(extra="forbid", populate_by_name=True)
48
+ source: str = Field(min_length=1, max_length=2048)
49
+ accept_host_permissions: bool = Field(default=False, alias="acceptHostPermissions")
50
+
51
+ @field_validator("source")
52
+ @classmethod
53
+ def validate_source(cls, value: str) -> str:
54
+ normalized = value.strip()
55
+ if not normalized or any(character in value for character in ("\x00", "\r", "\n")):
56
+ raise ValueError("source must be one package, Git URL, or absolute local path")
57
+ return normalized
58
+
59
+
60
+ class DshPluginUpdateRequest(BaseModel):
61
+ model_config = ConfigDict(extra="forbid", populate_by_name=True)
62
+ accept_host_permissions: bool = Field(default=False, alias="acceptHostPermissions")
63
+
64
+
65
+ def _studio_codex_home(studio: StudioService) -> tuple[Path, str]:
66
+ configured = os.environ.get("KSADK_CODEX_HOME", "").strip()
67
+ if configured:
68
+ return Path(configured).expanduser(), "explicit"
69
+ return studio.workspace.root / ".agentkit" / "codex-home", "workspace-isolated"
70
+
71
+
72
+ def _studio_dsh_options(studio: StudioService) -> tuple[Path, str, tuple[str, ...] | None, str]:
73
+ configured_home = os.environ.get("KSADK_DSH_HOME", "").strip()
74
+ home = (
75
+ Path(configured_home).expanduser()
76
+ if configured_home
77
+ else studio.workspace.root / ".agentkit" / "dsh-home"
78
+ )
79
+ configured_bin = os.environ.get("KSADK_DSH_BIN", "").strip()
80
+ if configured_bin:
81
+ command = (str(Path(configured_bin).expanduser()),)
82
+ else:
83
+ try:
84
+ command = DshToolchainManager().require_command()
85
+ except (DshToolchainError, OSError, ValueError):
86
+ # Keep the bridge's established PATH lookup when the optional
87
+ # pinned toolchain has not been installed or is unusable.
88
+ command = None
89
+ profile = os.environ.get("KSADK_DSH_PROFILE", "").strip() or "studio"
90
+ return home, profile, command, "explicit" if configured_home else "workspace-isolated"
91
+
92
+
93
+ def _public_host(kind: str, host: Any | None, *, home_mode: str) -> dict[str, Any]:
94
+ codex = kind == "codex"
95
+ return {
96
+ "hostId": "codex-app-server" if codex else "deepseek-harness",
97
+ "available": host is not None,
98
+ "status": "available" if host is not None else "unavailable",
99
+ "version": host.version if host is not None else None,
100
+ "protocol": host.protocol
101
+ if host is not None
102
+ else ("codex.app-server/v1" if codex else "dsh.profile/v1"),
103
+ "homeMode": home_mode,
104
+ }
105
+
106
+
107
+ def _public_codex_source(source: BaseModel) -> dict[str, Any]:
108
+ raw = source.model_dump(by_alias=True, mode="json")
109
+ source_type = str(raw.get("type") or "remote")
110
+ payload: dict[str, Any] = {"type": source_type}
111
+ if source_type == "local":
112
+ payload["name"] = Path(str(raw.get("path") or "plugin")).name or "plugin"
113
+ elif source_type == "git":
114
+ payload["name"] = Path(urlparse(str(raw.get("url") or "")).path.rstrip("/")).name
115
+ for key in ("refName", "sha"):
116
+ if raw.get(key):
117
+ payload[key] = raw[key]
118
+ elif source_type == "npm":
119
+ payload.update(package=raw.get("package"), version=raw.get("version"))
120
+ return payload
121
+
122
+
123
+ def _public_codex_inventory(
124
+ inventory: CodexPluginInventory, *, host: CodexBridgeHost, home_mode: str
125
+ ) -> dict[str, Any]:
126
+ return {
127
+ "ecosystem": "codex",
128
+ "integrationMode": "bridged",
129
+ "pluginId": inventory.plugin_id,
130
+ "resolvedVersion": inventory.version,
131
+ "distributionName": inventory.name,
132
+ "displayName": inventory.name,
133
+ "marketplaceName": inventory.marketplace_name,
134
+ "source": _public_codex_source(inventory.source),
135
+ "installed": inventory.installed,
136
+ "state": "enabled" if inventory.enabled else "disabled",
137
+ "enabled": inventory.enabled,
138
+ "availability": inventory.availability,
139
+ "permissions": [],
140
+ "permissionsDeclared": False,
141
+ "riskDisclosures": list(inventory.risk_disclosures),
142
+ "isolation": "host-managed",
143
+ "runtimeState": None,
144
+ "host": _public_host("codex", host, home_mode=home_mode),
145
+ }
146
+
147
+
148
+ def _public_codex_detail(
149
+ detail: CodexPluginDetail, *, host: CodexBridgeHost, home_mode: str
150
+ ) -> dict[str, Any]:
151
+ return {
152
+ "item": _public_codex_inventory(detail.inventory, host=host, home_mode=home_mode),
153
+ "description": detail.description,
154
+ "capabilities": {
155
+ "skills": list(detail.skills),
156
+ "mcpServers": list(detail.mcp_servers),
157
+ "hooks": list(detail.hooks),
158
+ "apps": list(detail.apps),
159
+ "scheduledTasks": list(detail.scheduled_tasks),
160
+ },
161
+ }
162
+
163
+
164
+ def _public_dsh_inventory(
165
+ inventory: DshPluginInventory,
166
+ *,
167
+ host: DshBridgeHost,
168
+ home_mode: str,
169
+ runtime_state: dict[str, Any] | None = None,
170
+ ) -> dict[str, Any]:
171
+ client = inventory.client_bundle
172
+ return {
173
+ "ecosystem": "dsh",
174
+ "integrationMode": "bridged",
175
+ "pluginId": inventory.name,
176
+ "resolvedVersion": inventory.version,
177
+ "distributionName": inventory.name,
178
+ "displayName": inventory.display_name,
179
+ "description": inventory.description,
180
+ "profile": inventory.profile,
181
+ "source": {"type": "host-profile", "name": inventory.name},
182
+ "installed": True,
183
+ "state": "enabled" if inventory.enabled else "disabled",
184
+ "enabled": inventory.enabled,
185
+ "permissions": [],
186
+ "permissionsDeclared": False,
187
+ "riskDisclosures": list(inventory.risk_disclosures),
188
+ "isolation": "host-managed",
189
+ "runtimeState": runtime_state,
190
+ "clientBundle": (
191
+ {
192
+ "platform": client.platform,
193
+ "digest": client.digest,
194
+ "contentBytes": client.content_bytes,
195
+ "external": list(client.external),
196
+ "inject": list(client.inject),
197
+ "compatible": client.compatible,
198
+ "incompatibilityReason": client.incompatibility_reason or None,
199
+ }
200
+ if client is not None
201
+ else None
202
+ ),
203
+ "host": _public_host("dsh", host, home_mode=home_mode),
204
+ }
205
+
206
+
207
+ def _public_dsh_client_bundle(inventory: DshPluginInventory) -> dict[str, Any] | None:
208
+ client = inventory.client_bundle
209
+ if client is None:
210
+ return None
211
+ query = urlencode({"pluginName": inventory.name, "digest": client.digest})
212
+ return {
213
+ "pluginId": inventory.name,
214
+ "enabled": inventory.enabled,
215
+ "compatible": client.compatible,
216
+ "digest": client.digest,
217
+ "contentBytes": client.content_bytes,
218
+ "external": list(client.external),
219
+ "inject": list(client.inject),
220
+ "incompatibilityReason": client.incompatibility_reason or None,
221
+ "url": f"/api/v1/plugin-ecosystems/dsh/client-bundle?{query}",
222
+ }
223
+
224
+
225
+ def _codex_error(error: Exception) -> StudioError:
226
+ if isinstance(error, CodexPluginNotFoundError):
227
+ return StudioError(
228
+ "CODEX_PLUGIN_NOT_FOUND", "Codex 插件不存在或来源不唯一", status_code=404
229
+ )
230
+ if isinstance(error, CodexPluginApprovalRequired):
231
+ return StudioError(
232
+ "CODEX_PLUGIN_RISK_CONFIRMATION_REQUIRED",
233
+ "安装 Codex 插件前必须确认宿主权限风险",
234
+ status_code=422,
235
+ )
236
+ return StudioError("CODEX_PLUGIN_HOST_UNAVAILABLE", "Codex 插件宿主当前不可用", status_code=503)
237
+
238
+
239
+ def _dsh_error(error: Exception) -> StudioError:
240
+ if isinstance(error, DshPluginNotFoundError):
241
+ return StudioError("DSH_PLUGIN_NOT_FOUND", "DSH 插件未安装", status_code=404)
242
+ if isinstance(error, DshPluginApprovalRequired):
243
+ return StudioError(
244
+ "DSH_PLUGIN_RISK_CONFIRMATION_REQUIRED",
245
+ "安装或升级 DSH 插件前必须确认宿主权限风险",
246
+ status_code=422,
247
+ )
248
+ if isinstance(error, DshPluginMutationError):
249
+ return StudioError(
250
+ "DSH_PLUGIN_MUTATION_FAILED", "DSH 插件操作失败,原 Profile 已保留", status_code=409
251
+ )
252
+ if isinstance(error, (DshHostUnavailableError, OSError)):
253
+ return StudioError("DSH_PLUGIN_HOST_UNAVAILABLE", "DSH 插件宿主当前不可用", status_code=503)
254
+ return StudioError("DSH_PLUGIN_OPERATION_FAILED", "DSH 插件操作失败", status_code=422)
255
+
256
+
257
+ def register_plugin_routes(app: FastAPI, studio: StudioService) -> None:
258
+ """Register only DSH Profile and Codex App Server lifecycle routes."""
259
+
260
+ codex_home, codex_mode = _studio_codex_home(studio)
261
+ dsh_home, dsh_profile, dsh_command, dsh_mode = _studio_dsh_options(studio)
262
+
263
+ def public_dsh(item: DshPluginInventory, host: DshBridgeHost) -> dict[str, Any]:
264
+ return _public_dsh_inventory(
265
+ item,
266
+ host=host,
267
+ home_mode=dsh_mode,
268
+ runtime_state=studio.dsh_provider_runtime_state(item.name),
269
+ )
270
+
271
+ def call_dsh(operation: Callable[[DshProfilePluginBridge], Any]) -> Any:
272
+ try:
273
+ with DshProfilePluginBridge(
274
+ dsh_home=dsh_home, profile=dsh_profile, dsh_command=dsh_command
275
+ ) as bridge:
276
+ return bridge.host, operation(bridge)
277
+ except Exception as error:
278
+ raise _dsh_error(error) from None
279
+
280
+ @app.get("/api/v1/plugin-ecosystems/codex/plugins")
281
+ async def list_codex_plugins(
282
+ installed_only: bool = Query(default=False), force_refetch: bool = Query(default=False)
283
+ ):
284
+ try:
285
+ async with CodexAppServerPluginBridge(codex_home=codex_home) as bridge:
286
+ items = await bridge.list_plugins(force_refetch=force_refetch)
287
+ visible = [item for item in items if item.installed or not installed_only]
288
+ return {
289
+ "ecosystem": "codex",
290
+ "integrationMode": "bridged",
291
+ "host": _public_host("codex", bridge.host, home_mode=codex_mode),
292
+ "items": [
293
+ _public_codex_inventory(item, host=bridge.host, home_mode=codex_mode)
294
+ for item in visible
295
+ ],
296
+ }
297
+ except Exception:
298
+ return {
299
+ "ecosystem": "codex",
300
+ "integrationMode": "bridged",
301
+ "host": _public_host("codex", None, home_mode=codex_mode),
302
+ "items": [],
303
+ "error": {
304
+ "code": "CODEX_PLUGIN_HOST_UNAVAILABLE",
305
+ "message": "Codex 插件宿主当前不可用",
306
+ },
307
+ }
308
+
309
+ @app.get("/api/v1/plugin-ecosystems/codex/plugins/{plugin_id}")
310
+ async def get_codex_plugin(
311
+ plugin_id: str, marketplace_name: str | None = Query(default=None, max_length=256)
312
+ ):
313
+ try:
314
+ async with CodexAppServerPluginBridge(codex_home=codex_home) as bridge:
315
+ detail = await bridge.read_plugin(plugin_id, marketplace_name=marketplace_name)
316
+ return _public_codex_detail(detail, host=bridge.host, home_mode=codex_mode)
317
+ except Exception as error:
318
+ raise _codex_error(error) from None
319
+
320
+ @app.post("/api/v1/plugin-ecosystems/codex/plugins/{plugin_id}:install")
321
+ async def install_codex_plugin(plugin_id: str, payload: CodexPluginInstallRequest):
322
+ if not payload.accept_undeclared_permissions:
323
+ raise _codex_error(CodexPluginApprovalRequired("approval required"))
324
+ try:
325
+ async with CodexAppServerPluginBridge(codex_home=codex_home) as bridge:
326
+ result = await bridge.install_plugin(
327
+ plugin_id,
328
+ marketplace_name=payload.marketplace_name,
329
+ accept_undeclared_permissions=True,
330
+ install_attempt_id=f"studio-{uuid4().hex}",
331
+ )
332
+ return {
333
+ "item": _public_codex_inventory(
334
+ result.inventory, host=bridge.host, home_mode=codex_mode
335
+ ),
336
+ "authPolicy": result.auth_policy,
337
+ "appsNeedingAuth": list(result.apps_needing_auth),
338
+ }
339
+ except Exception as error:
340
+ raise _codex_error(error) from None
341
+
342
+ @app.delete("/api/v1/plugin-ecosystems/codex/plugins/{plugin_id}", status_code=204)
343
+ async def uninstall_codex_plugin(plugin_id: str):
344
+ try:
345
+ async with CodexAppServerPluginBridge(codex_home=codex_home) as bridge:
346
+ await bridge.uninstall_plugin(plugin_id)
347
+ return Response(status_code=204)
348
+ except Exception as error:
349
+ raise _codex_error(error) from None
350
+
351
+ @app.get("/api/v1/plugin-ecosystems/dsh/plugins")
352
+ async def list_dsh_plugins():
353
+ try:
354
+ host, items = await asyncio.to_thread(call_dsh, lambda bridge: bridge.list_plugins())
355
+ return {
356
+ "ecosystem": "dsh",
357
+ "integrationMode": "bridged",
358
+ "profile": dsh_profile,
359
+ "host": _public_host("dsh", host, home_mode=dsh_mode),
360
+ "items": [public_dsh(item, host) for item in items],
361
+ }
362
+ except StudioError as error:
363
+ return {
364
+ "ecosystem": "dsh",
365
+ "integrationMode": "bridged",
366
+ "profile": dsh_profile,
367
+ "host": _public_host("dsh", None, home_mode=dsh_mode),
368
+ "items": [],
369
+ "error": {"code": error.code, "message": error.message},
370
+ }
371
+
372
+ @app.get("/api/v1/plugin-ecosystems/dsh/profile")
373
+ async def get_dsh_profile_projection():
374
+ host, result = await asyncio.to_thread(
375
+ call_dsh, lambda bridge: (bridge.project_profile(), bridge.list_plugins())
376
+ )
377
+ projection, items = result
378
+ bundles = [
379
+ projected
380
+ for item in items
381
+ if item.enabled
382
+ for projected in [_public_dsh_client_bundle(item)]
383
+ if projected is not None
384
+ ]
385
+ graph_hash = hashlib.sha256(projection.config_digest.encode("utf-8"))
386
+ for bundle in bundles:
387
+ for value in (str(bundle["pluginId"]), str(bundle["digest"])):
388
+ encoded = value.encode("utf-8")
389
+ graph_hash.update(f"{len(encoded)}:".encode("ascii"))
390
+ graph_hash.update(encoded)
391
+ return {
392
+ "host": _public_host("dsh", host, home_mode=dsh_mode),
393
+ "profile": projection.model_dump(mode="json", by_alias=True),
394
+ "clientGraphDigest": f"sha256:{graph_hash.hexdigest()}",
395
+ "clientBundles": bundles,
396
+ }
397
+
398
+ @app.get("/api/v1/plugin-ecosystems/dsh/client-bundle")
399
+ async def get_dsh_client_bundle(
400
+ plugin_name: str = Query(alias="pluginName", min_length=1, max_length=256),
401
+ digest: str = Query(pattern=r"^sha256:[0-9a-f]{64}$"),
402
+ ):
403
+ _, content = await asyncio.to_thread(
404
+ call_dsh,
405
+ lambda bridge: bridge.read_client_bundle(plugin_name, expected_digest=digest),
406
+ )
407
+ return Response(
408
+ content=content,
409
+ media_type="application/javascript; charset=utf-8",
410
+ headers={
411
+ "Cache-Control": "private, max-age=31536000, immutable",
412
+ "ETag": f'"{digest}"',
413
+ "X-Content-Type-Options": "nosniff",
414
+ },
415
+ )
416
+
417
+ @app.post("/api/v1/plugin-ecosystems/dsh/plugins:install", status_code=201)
418
+ async def install_dsh_plugin(payload: DshPluginInstallRequest):
419
+ if not payload.accept_host_permissions:
420
+ raise _dsh_error(DshPluginApprovalRequired("approval required"))
421
+ host, item = await asyncio.to_thread(
422
+ call_dsh,
423
+ lambda bridge: bridge.install_plugin(payload.source, accept_host_permissions=True),
424
+ )
425
+ await studio.refresh_dsh_provider_registrations()
426
+ return {"item": public_dsh(item, host)}
427
+
428
+ async def mutate_dsh(plugin_name: str, operation: str):
429
+ def action(bridge: DshProfilePluginBridge):
430
+ if operation == "enable":
431
+ return bridge.set_enabled(plugin_name, enabled=True)
432
+ if operation == "disable":
433
+ return bridge.set_enabled(plugin_name, enabled=False)
434
+ if operation == "uninstall":
435
+ return bridge.uninstall_plugin(plugin_name)
436
+ return bridge.get_plugin(plugin_name)
437
+
438
+ return await asyncio.to_thread(call_dsh, action)
439
+
440
+ @app.post("/api/v1/plugin-ecosystems/dsh/plugins/{plugin_name:path}:enable")
441
+ async def enable_dsh_plugin(plugin_name: str):
442
+ host, item = await mutate_dsh(plugin_name, "enable")
443
+ await studio.refresh_dsh_provider_registrations()
444
+ return {"item": public_dsh(item, host)}
445
+
446
+ @app.post("/api/v1/plugin-ecosystems/dsh/plugins/{plugin_name:path}:disable")
447
+ async def disable_dsh_plugin(plugin_name: str):
448
+ host, item = await mutate_dsh(plugin_name, "disable")
449
+ await studio.refresh_dsh_provider_registrations()
450
+ return {"item": public_dsh(item, host)}
451
+
452
+ @app.post("/api/v1/plugin-ecosystems/dsh/plugins/{plugin_name:path}:update")
453
+ async def update_dsh_plugin(plugin_name: str, payload: DshPluginUpdateRequest):
454
+ if not payload.accept_host_permissions:
455
+ raise _dsh_error(DshPluginApprovalRequired("approval required"))
456
+ host, item = await asyncio.to_thread(
457
+ call_dsh,
458
+ lambda bridge: bridge.update_plugin(plugin_name, accept_host_permissions=True),
459
+ )
460
+ await studio.refresh_dsh_provider_registrations()
461
+ return {"item": public_dsh(item, host)}
462
+
463
+ @app.get("/api/v1/plugin-ecosystems/dsh/plugins/{plugin_name:path}")
464
+ async def get_dsh_plugin(plugin_name: str):
465
+ host, item = await mutate_dsh(plugin_name, "get")
466
+ return {"item": public_dsh(item, host)}
467
+
468
+ @app.delete("/api/v1/plugin-ecosystems/dsh/plugins/{plugin_name:path}", status_code=204)
469
+ async def uninstall_dsh_plugin(plugin_name: str):
470
+ await mutate_dsh(plugin_name, "uninstall")
471
+ await studio.refresh_dsh_provider_registrations()
472
+ return Response(status_code=204)
473
+
474
+
475
+ __all__ = [
476
+ "CodexPluginInstallRequest",
477
+ "DshPluginInstallRequest",
478
+ "DshPluginUpdateRequest",
479
+ "register_plugin_routes",
480
+ ]
@@ -0,0 +1,187 @@
1
+ """Bounded, content-addressed local attachments for ConversationInput."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+ import re
8
+ from dataclasses import dataclass
9
+ from pathlib import Path
10
+
11
+ from ksadk.studio.errors import StudioError, not_found
12
+ from ksadk.studio.workspace import Workspace
13
+
14
+ _ATTACHMENT_REF = re.compile(r"^attachment://sha256/(?P<digest>[0-9a-f]{64})$")
15
+ _SAFE_FILENAME = re.compile(r"[^A-Za-z0-9._-]+")
16
+ _TEXT_MEDIA_TYPES = {
17
+ "application/json",
18
+ "application/toml",
19
+ "application/xml",
20
+ "application/x-httpd-php",
21
+ "application/x-sh",
22
+ "application/x-yaml",
23
+ }
24
+ _TEXT_SUFFIXES = {
25
+ ".csv",
26
+ ".css",
27
+ ".go",
28
+ ".html",
29
+ ".ini",
30
+ ".java",
31
+ ".js",
32
+ ".json",
33
+ ".jsx",
34
+ ".log",
35
+ ".md",
36
+ ".py",
37
+ ".rs",
38
+ ".sh",
39
+ ".toml",
40
+ ".ts",
41
+ ".tsx",
42
+ ".txt",
43
+ ".xml",
44
+ ".yaml",
45
+ ".yml",
46
+ }
47
+
48
+
49
+ @dataclass(frozen=True)
50
+ class StoredConversationAttachment:
51
+ attachment_ref: str
52
+ path: Path
53
+ name: str
54
+ media_type: str
55
+ size: int
56
+ digest: str
57
+
58
+
59
+ class ConversationAttachmentStore:
60
+ """Persist only the small image/text inputs supported by local Studio."""
61
+
62
+ MAX_BYTES = 1_500_000
63
+
64
+ def __init__(self, workspace: Workspace) -> None:
65
+ self.workspace = workspace
66
+ self.root = self.workspace.resolve(".agentkit/assets/conversation-attachments")
67
+ self.root.mkdir(parents=True, exist_ok=True)
68
+
69
+ def store(self, content: bytes, *, filename: str, media_type: str) -> dict[str, object]:
70
+ if not content:
71
+ raise StudioError(
72
+ "CONVERSATION_ATTACHMENT_EMPTY",
73
+ "会话附件不能为空",
74
+ status_code=422,
75
+ field="file",
76
+ )
77
+ if len(content) > self.MAX_BYTES:
78
+ raise StudioError(
79
+ "CONVERSATION_ATTACHMENT_TOO_LARGE",
80
+ "会话附件不能超过 1.5 MiB",
81
+ status_code=413,
82
+ field="file",
83
+ details={"maxBytes": self.MAX_BYTES},
84
+ )
85
+ normalized_type = media_type.split(";", 1)[0].strip().lower()
86
+ safe_name = _SAFE_FILENAME.sub("-", Path(filename).name).strip(".-")[:160]
87
+ safe_name = safe_name or "attachment"
88
+ if not self._supported(content, filename=safe_name, media_type=normalized_type):
89
+ raise StudioError(
90
+ "CONVERSATION_ATTACHMENT_TYPE_UNSUPPORTED",
91
+ "会话附件仅支持图片或 UTF-8 文本、代码文件",
92
+ status_code=415,
93
+ field="file",
94
+ )
95
+
96
+ digest = hashlib.sha256(content).hexdigest()
97
+ content_path = self.root / f"{digest}.bin"
98
+ metadata_path = self.root / f"{digest}.json"
99
+ if not content_path.is_file():
100
+ self.workspace.atomic_write_bytes(content_path, content)
101
+ if not metadata_path.is_file():
102
+ self.workspace.atomic_write_text(
103
+ metadata_path,
104
+ json.dumps(
105
+ {
106
+ "digest": digest,
107
+ "name": safe_name,
108
+ "mediaType": normalized_type or "application/octet-stream",
109
+ "size": len(content),
110
+ },
111
+ ensure_ascii=False,
112
+ sort_keys=True,
113
+ separators=(",", ":"),
114
+ )
115
+ + "\n",
116
+ )
117
+ # The reference identifies content, so its persisted metadata is the
118
+ # authority as well. Returning a second upload's conflicting name or
119
+ # media type would create a ref that immediately fails on resolution.
120
+ stored = self.resolve(f"attachment://sha256/{digest}")
121
+ return {
122
+ "attachmentRef": stored.attachment_ref,
123
+ "name": stored.name,
124
+ "mediaType": stored.media_type,
125
+ "size": stored.size,
126
+ "digest": stored.digest,
127
+ }
128
+
129
+ def resolve(self, attachment_ref: str) -> StoredConversationAttachment:
130
+ match = _ATTACHMENT_REF.fullmatch(attachment_ref)
131
+ if match is None:
132
+ raise not_found("conversation-attachment", attachment_ref)
133
+ digest = match.group("digest")
134
+ content_path = self.root / f"{digest}.bin"
135
+ metadata_path = self.root / f"{digest}.json"
136
+ if not content_path.is_file() or not metadata_path.is_file():
137
+ raise not_found("conversation-attachment", attachment_ref)
138
+ try:
139
+ metadata = json.loads(metadata_path.read_text(encoding="utf-8"))
140
+ content = content_path.read_bytes()
141
+ content_size = len(content)
142
+ except (OSError, json.JSONDecodeError) as exc:
143
+ raise StudioError(
144
+ "CONVERSATION_ATTACHMENT_CORRUPT",
145
+ "会话附件元数据损坏",
146
+ status_code=409,
147
+ details={"attachmentRef": attachment_ref},
148
+ ) from exc
149
+ actual_digest = hashlib.sha256(content).hexdigest()
150
+ if (
151
+ metadata.get("digest") != digest
152
+ or actual_digest != digest
153
+ or int(metadata.get("size") or -1) != content_size
154
+ ):
155
+ raise StudioError(
156
+ "CONVERSATION_ATTACHMENT_CORRUPT",
157
+ "会话附件完整性校验失败",
158
+ status_code=409,
159
+ details={"attachmentRef": attachment_ref},
160
+ )
161
+ return StoredConversationAttachment(
162
+ attachment_ref=attachment_ref,
163
+ path=content_path,
164
+ name=str(metadata.get("name") or "attachment"),
165
+ media_type=str(metadata.get("mediaType") or "application/octet-stream"),
166
+ size=content_size,
167
+ digest=digest,
168
+ )
169
+
170
+ @staticmethod
171
+ def _supported(content: bytes, *, filename: str, media_type: str) -> bool:
172
+ if media_type.startswith("image/"):
173
+ return True
174
+ if (
175
+ media_type.startswith("text/")
176
+ or media_type in _TEXT_MEDIA_TYPES
177
+ or Path(filename).suffix.lower() in _TEXT_SUFFIXES
178
+ ):
179
+ try:
180
+ content.decode("utf-8")
181
+ except UnicodeDecodeError:
182
+ return False
183
+ return True
184
+ return False
185
+
186
+
187
+ __all__ = ["ConversationAttachmentStore", "StoredConversationAttachment"]