agentengine-sdk-python 0.7.0__py3-none-any.whl → 0.8.1__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 (450) hide show
  1. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/METADATA +52 -20
  2. agentengine_sdk_python-0.8.1.dist-info/RECORD +644 -0
  3. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/WHEEL +1 -1
  4. ksadk/a2a/__init__.py +180 -11
  5. ksadk/a2a/bootstrap.py +332 -0
  6. ksadk/a2a/card.py +15 -0
  7. ksadk/a2a/context_store.py +203 -0
  8. ksadk/a2a/control_plane.py +1056 -0
  9. ksadk/a2a/credential.py +99 -0
  10. ksadk/a2a/event_adapter.py +149 -0
  11. ksadk/a2a/executor.py +361 -137
  12. ksadk/a2a/external_transport.py +516 -0
  13. ksadk/a2a/identity.py +304 -0
  14. ksadk/a2a/ids.py +22 -0
  15. ksadk/a2a/langgraph.py +285 -0
  16. ksadk/a2a/resume_store.py +201 -0
  17. ksadk/a2a/routes.py +112 -0
  18. ksadk/a2a/server.py +102 -62
  19. ksadk/a2a/service_env.py +33 -0
  20. ksadk/a2a/space_client.py +1122 -0
  21. ksadk/a2a/task_adapter.py +423 -0
  22. ksadk/a2a/task_event_dispatcher.py +167 -0
  23. ksadk/a2a/task_event_outbox.py +326 -0
  24. ksadk/a2a/task_store.py +206 -0
  25. ksadk/a2a_card.py +101 -0
  26. ksadk/a2ui/__init__.py +27 -0
  27. ksadk/a2ui/core.py +186 -0
  28. ksadk/a2ui/models.py +166 -0
  29. ksadk/a2ui/renderer.py +198 -0
  30. ksadk/agui/__init__.py +9 -0
  31. ksadk/agui/a2ui_projection.py +92 -0
  32. ksadk/agui/agent.py +872 -0
  33. ksadk/agui/config.py +92 -0
  34. ksadk/agui/routes.py +47 -0
  35. ksadk/api/client.py +238 -204
  36. ksadk/api/environment.py +27 -0
  37. ksadk/builders/__init__.py +2 -0
  38. ksadk/builders/base.py +16 -14
  39. ksadk/builders/code_builder.py +375 -270
  40. ksadk/builders/container_builder.py +240 -200
  41. ksadk/builders/framework_requirements.py +31 -12
  42. ksadk/builders/ks3_uploader.py +60 -29
  43. ksadk/builders/managed_runtime_builder.py +173 -0
  44. ksadk/builders/mcp_builder.py +91 -92
  45. ksadk/builders/requirements_utils.py +1 -5
  46. ksadk/cli/__init__.py +97 -171
  47. ksadk/cli/cmd_a2a.py +255 -84
  48. ksadk/cli/cmd_agent.py +19 -5
  49. ksadk/cli/cmd_build.py +158 -29
  50. ksadk/cli/cmd_completion.py +47 -35
  51. ksadk/cli/cmd_config.py +267 -198
  52. ksadk/cli/cmd_create.py +507 -740
  53. ksadk/cli/cmd_dashboard.py +120 -45
  54. ksadk/cli/cmd_deploy.py +135 -59
  55. ksadk/cli/cmd_destroy.py +44 -14
  56. ksadk/cli/cmd_eval.py +386 -0
  57. ksadk/cli/cmd_files.py +63 -24
  58. ksadk/cli/cmd_hermes.py +313 -135
  59. ksadk/cli/cmd_invoke.py +184 -122
  60. ksadk/cli/cmd_launch.py +76 -28
  61. ksadk/cli/cmd_mcp.py +98 -64
  62. ksadk/cli/cmd_model.py +14 -5
  63. ksadk/cli/cmd_openclaw.py +564 -268
  64. ksadk/cli/cmd_replay.py +71 -0
  65. ksadk/cli/cmd_run.py +58 -73
  66. ksadk/cli/cmd_status.py +80 -31
  67. ksadk/cli/cmd_studio.py +110 -0
  68. ksadk/cli/cmd_version.py +20 -13
  69. ksadk/cli/cmd_web.py +95 -18
  70. ksadk/cli/deploy_utils.py +20 -20
  71. ksadk/cli/dry_run.py +7 -4
  72. ksadk/cli/env_options.py +83 -0
  73. ksadk/cli/error_utils.py +37 -12
  74. ksadk/cli/global_options.py +4 -3
  75. ksadk/cli/local_runtime.py +1 -2
  76. ksadk/cli/model_catalog.py +6 -2
  77. ksadk/cli/network_options.py +41 -19
  78. ksadk/cli/resource_common.py +6 -2
  79. ksadk/cli/runtime_bootstrap.py +51 -0
  80. ksadk/cli/storage.py +0 -1
  81. ksadk/cli/ui.py +11 -3
  82. ksadk/cli/workflow_common.py +52 -18
  83. ksadk/codex/__init__.py +12 -0
  84. ksadk/codex/client.py +1042 -0
  85. ksadk/codex/phase.py +143 -0
  86. ksadk/codex/runtime.py +940 -0
  87. ksadk/common/aicp_env.py +0 -1
  88. ksadk/common/auth.py +2 -2
  89. ksadk/common/constants.py +1 -3
  90. ksadk/common/kop_client.py +232 -0
  91. ksadk/common/llm_utils.py +17 -13
  92. ksadk/compat/__init__.py +5 -0
  93. ksadk/compat/adk_compat.py +200 -0
  94. ksadk/compat/copilotkit_a2ui.py +330 -0
  95. ksadk/configs/__init__.py +12 -14
  96. ksadk/configs/env_registry.py +661 -116
  97. ksadk/configs/global_config.py +3 -2
  98. ksadk/configs/settings.py +133 -209
  99. ksadk/conversations/__init__.py +85 -92
  100. ksadk/conversations/attachment_storage.py +16 -11
  101. ksadk/conversations/attachments.py +44 -36
  102. ksadk/conversations/compaction_pipeline.py +12 -12
  103. ksadk/conversations/compaction_prompt.py +9 -5
  104. ksadk/conversations/context.py +221 -6
  105. ksadk/conversations/message_projection.py +438 -51
  106. ksadk/conversations/model_context.py +12 -6
  107. ksadk/conversations/model_options.py +0 -1
  108. ksadk/conversations/normalize.py +20 -16
  109. ksadk/conversations/reasoning_markup.py +1 -1
  110. ksadk/conversations/runtime.py +339 -5255
  111. ksadk/conversations/runtime_compaction.py +333 -0
  112. ksadk/conversations/runtime_constants.py +27 -0
  113. ksadk/conversations/runtime_governance.py +185 -0
  114. ksadk/conversations/runtime_input.py +660 -0
  115. ksadk/conversations/runtime_invocation.py +395 -0
  116. ksadk/conversations/runtime_metadata.py +475 -0
  117. ksadk/conversations/runtime_observability.py +367 -0
  118. ksadk/conversations/runtime_payloads.py +272 -0
  119. ksadk/conversations/runtime_persistence.py +440 -0
  120. ksadk/conversations/runtime_preparation.py +426 -0
  121. ksadk/conversations/runtime_resume.py +836 -0
  122. ksadk/conversations/runtime_stream_events.py +902 -0
  123. ksadk/conversations/runtime_streaming.py +700 -0
  124. ksadk/conversations/semantic_summary.py +17 -5
  125. ksadk/conversations/session_title.py +6 -3
  126. ksadk/deployment/__init__.py +15 -16
  127. ksadk/deployment/agent_access.py +12 -23
  128. ksadk/deployment/base.py +51 -38
  129. ksadk/deployment/managed_runtime.py +33 -0
  130. ksadk/deployment/manager.py +85 -71
  131. ksadk/deployment/providers/serverless.py +379 -210
  132. ksadk/deployment/registry.py +38 -32
  133. ksadk/deployment/state.py +11 -11
  134. ksadk/deployment/ui_config.py +1 -1
  135. ksadk/detection/__init__.py +1 -1
  136. ksadk/detection/detector.py +146 -83
  137. ksadk/detection/mcp_detector.py +9 -6
  138. ksadk/evaluation/__init__.py +84 -0
  139. ksadk/evaluation/a2a_adapter.py +444 -0
  140. ksadk/evaluation/adapters.py +54 -0
  141. ksadk/evaluation/contracts.py +409 -0
  142. ksadk/evaluation/evalset.py +351 -0
  143. ksadk/evaluation/evaluators.py +461 -0
  144. ksadk/evaluation/executor.py +118 -0
  145. ksadk/evaluation/storage.py +68 -0
  146. ksadk/evaluation/target.py +73 -0
  147. ksadk/events/__init__.py +25 -0
  148. ksadk/events/parser.py +191 -0
  149. ksadk/events/replay.py +37 -0
  150. ksadk/events/runtime_event.py +273 -0
  151. ksadk/events/store.py +317 -0
  152. ksadk/harness/__init__.py +35 -0
  153. ksadk/harness/app.py +269 -0
  154. ksadk/harness/config.py +181 -0
  155. ksadk/harness/reasoner.py +105 -0
  156. ksadk/harness/runtime.py +364 -0
  157. ksadk/harness/sandbox.py +141 -0
  158. ksadk/harness/tools.py +222 -0
  159. ksadk/hermes_terminal.py +28 -12
  160. ksadk/identity/resolver.py +15 -9
  161. ksadk/ids.py +32 -0
  162. ksadk/knowledge_base/adk_tool.py +1 -1
  163. ksadk/knowledge_base/client.py +38 -33
  164. ksadk/managed_a2a_card.py +160 -0
  165. ksadk/managed_runtime.py +195 -0
  166. ksadk/markdown.py +5 -2
  167. ksadk/mcp_runtime/__init__.py +9 -6
  168. ksadk/memory/__init__.py +1 -1
  169. ksadk/memory/adk/__init__.py +2 -0
  170. ksadk/memory/adk/backends/base_ltm_backend.py +2 -6
  171. ksadk/memory/adk/backends/http_ltm_backend.py +13 -21
  172. ksadk/memory/adk/backends/inmemory_ltm_backend.py +7 -13
  173. ksadk/memory/adk/backends/sdk_ltm_backend.py +36 -36
  174. ksadk/memory/adk/long_term_memory.py +34 -37
  175. ksadk/memory/adk/resilient_session_service.py +4 -5
  176. ksadk/memory/adk/short_term_memory.py +12 -21
  177. ksadk/memory/adk_tool.py +3 -2
  178. ksadk/memory/backends/base.py +19 -19
  179. ksadk/memory/backends/memory.py +24 -25
  180. ksadk/memory/backends/redis.py +31 -30
  181. ksadk/memory/langchain_tool.py +2 -1
  182. ksadk/memory/manager.py +52 -47
  183. ksadk/memory/service.py +16 -6
  184. ksadk/memory/tool.py +4 -0
  185. ksadk/model_policy.py +15 -7
  186. ksadk/model_proxy/__init__.py +51 -0
  187. ksadk/model_proxy/bootstrap.py +80 -0
  188. ksadk/model_proxy/cache.py +165 -0
  189. ksadk/model_proxy/config.py +66 -0
  190. ksadk/model_proxy/demo_webui.py +115 -0
  191. ksadk/model_proxy/detect.py +145 -0
  192. ksadk/model_proxy/gate.py +68 -0
  193. ksadk/model_proxy/namespace.py +201 -0
  194. ksadk/model_proxy/protocol_proxy.py +22 -0
  195. ksadk/model_proxy/server.py +443 -0
  196. ksadk/model_proxy/transform.py +735 -0
  197. ksadk/openclaw_gateway.py +29 -14
  198. ksadk/runners/adk_runner.py +354 -146
  199. ksadk/runners/base_runner.py +71 -29
  200. ksadk/runners/deepagents_runner.py +0 -1
  201. ksadk/runners/factory.py +18 -5
  202. ksadk/runners/langchain_runner.py +29 -742
  203. ksadk/runners/langgraph_runner.py +417 -97
  204. ksadk/runners/patch_langchain.py +16 -16
  205. ksadk/runners/remote_runner.py +134 -33
  206. ksadk/runners/usage_accumulator.py +1 -0
  207. ksadk/runners/utils/__init__.py +0 -10
  208. ksadk/runners/utils/loader.py +23 -7
  209. ksadk/runtime/__init__.py +67 -0
  210. ksadk/runtime/adapter.py +412 -0
  211. ksadk/runtime/conversation_execution.py +553 -0
  212. ksadk/runtime/executor.py +251 -0
  213. ksadk/runtime/factory.py +156 -0
  214. ksadk/runtime/framework_adapters.py +118 -0
  215. ksadk/runtime/launch.py +48 -0
  216. ksadk/runtime/preprocessing.py +131 -0
  217. ksadk/runtime/runner_adapter.py +1032 -0
  218. ksadk/runtime/runner_loading.py +36 -0
  219. ksadk/runtime_context.py +18 -3
  220. ksadk/runtime_state.py +0 -1
  221. ksadk/sandbox/__init__.py +4 -1
  222. ksadk/sandbox/backends/__init__.py +4 -1
  223. ksadk/sandbox/backends/e2b.py +13 -5
  224. ksadk/sandbox/backends/local_process.py +11 -3
  225. ksadk/sandbox/base.py +10 -15
  226. ksadk/sandbox/factory.py +14 -4
  227. ksadk/sandbox/registry.py +57 -6
  228. ksadk/server/__init__.py +39 -4
  229. ksadk/server/api_models.py +15 -5
  230. ksadk/server/app.py +24 -3928
  231. ksadk/server/composition.py +47 -0
  232. ksadk/server/factory.py +622 -0
  233. ksadk/server/routes/__init__.py +1 -0
  234. ksadk/server/routes/checkpoint_resolution.py +116 -0
  235. ksadk/server/routes/common.py +682 -0
  236. ksadk/server/routes/control.py +331 -0
  237. ksadk/server/routes/dependencies.py +98 -0
  238. ksadk/server/routes/misc.py +425 -0
  239. ksadk/server/routes/models.py +451 -0
  240. ksadk/server/routes/openai_compat.py +229 -0
  241. ksadk/server/routes/projection.py +848 -0
  242. ksadk/server/routes/routers.py +52 -0
  243. ksadk/server/routes/run.py +519 -0
  244. ksadk/server/routes/sessions.py +644 -0
  245. ksadk/server/routes/streaming.py +265 -0
  246. ksadk/server/routes/workspace.py +414 -0
  247. ksadk/server/static/assets/{ArtifactsPanel-D-SSn2ee.js → ArtifactsPanel-CakCqxEN.js} +1 -1
  248. ksadk/server/static/assets/CodeBlock-CynLknyO.js +9 -0
  249. ksadk/server/static/assets/MathMessageMarkdown-3sQdOysS.js +8 -0
  250. ksadk/server/static/assets/MermaidBlock-NERD_ef7.js +303 -0
  251. ksadk/server/static/assets/NativeTerminalPanel-CiHWf82q.js +1 -0
  252. ksadk/server/static/assets/{arc-BA13sHc9.js → arc-DrgjcKhl.js} +1 -1
  253. ksadk/server/static/assets/architecture-7EHR7CIX-CpZLRCcY.js +1 -0
  254. ksadk/server/static/assets/{architectureDiagram-3BPJPVTR-DzJzgVoL.js → architectureDiagram-3BPJPVTR-BXoVRbQ6.js} +1 -1
  255. ksadk/server/static/assets/{blockDiagram-GPEHLZMM-UTdVcZhn.js → blockDiagram-GPEHLZMM-BnaB3Ywv.js} +1 -1
  256. ksadk/server/static/assets/{c4Diagram-AAUBKEIU-Dix0GD4m.js → c4Diagram-AAUBKEIU-Dw4weZcp.js} +1 -1
  257. ksadk/server/static/assets/channel-Dt3rQTFI.js +1 -0
  258. ksadk/server/static/assets/{chunk-2J33WTMH-C2JxuZ42.js → chunk-2J33WTMH-ChjpLK7j.js} +1 -1
  259. ksadk/server/static/assets/{chunk-4BX2VUAB-Be57QPgD.js → chunk-4BX2VUAB-B9drTgOW.js} +1 -1
  260. ksadk/server/static/assets/{chunk-55IACEB6-WjSuy4nv.js → chunk-55IACEB6-CK4ROATy.js} +1 -1
  261. ksadk/server/static/assets/{chunk-727SXJPM-BOovP-b5.js → chunk-727SXJPM-B9KtTTgw.js} +1 -1
  262. ksadk/server/static/assets/{chunk-AQP2D5EJ-DOmZNmWj.js → chunk-AQP2D5EJ-1O1mki3m.js} +1 -1
  263. ksadk/server/static/assets/{chunk-FMBD7UC4-DzwlodkU.js → chunk-FMBD7UC4-B_fH2YuH.js} +1 -1
  264. ksadk/server/static/assets/{chunk-ND2GUHAM-DMqAikEU.js → chunk-ND2GUHAM-D6rFLaVV.js} +1 -1
  265. ksadk/server/static/assets/chunk-QZHKN3VN-iXEZtKQc.js +1 -0
  266. ksadk/server/static/assets/classDiagram-4FO5ZUOK-DIveV17Q.js +1 -0
  267. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-DIveV17Q.js +1 -0
  268. ksadk/server/static/assets/{cose-bilkent-S5V4N54A-Zhy2GBnP.js → cose-bilkent-S5V4N54A-ChNs_Mdk.js} +1 -1
  269. ksadk/server/static/assets/{dagre-BM42HDAG-CbKrtIUz.js → dagre-BM42HDAG-D0QXg_MQ.js} +1 -1
  270. ksadk/server/static/assets/{diagram-2AECGRRQ-BRApgQRK.js → diagram-2AECGRRQ-DBDDKwBQ.js} +1 -1
  271. ksadk/server/static/assets/{diagram-5GNKFQAL-D0xbnDiK.js → diagram-5GNKFQAL-BkwDOxmO.js} +1 -1
  272. ksadk/server/static/assets/{diagram-KO2AKTUF-CVugVI9L.js → diagram-KO2AKTUF-Bv8waYvY.js} +1 -1
  273. ksadk/server/static/assets/{diagram-LMA3HP47-DozUGgee.js → diagram-LMA3HP47-e1VECDdu.js} +1 -1
  274. ksadk/server/static/assets/{diagram-OG6HWLK6-ZOYsKlru.js → diagram-OG6HWLK6-Ds9adfRl.js} +1 -1
  275. ksadk/server/static/assets/{dist-DpKtt4Bo.js → dist-4N2NMVYg.js} +1 -1
  276. ksadk/server/static/assets/{dist-CkEKJFtk.js → dist-B-jNR6Vn.js} +1 -1
  277. ksadk/server/static/assets/{dist-D57wnGWd.js → dist-B5J2uhNk.js} +1 -1
  278. ksadk/server/static/assets/{dist-8_SfK1IP.js → dist-B5oS1zcj.js} +1 -1
  279. ksadk/server/static/assets/{dist-DMezvBGJ.js → dist-BB3-OwTQ.js} +1 -1
  280. ksadk/server/static/assets/{dist-2hInLS_J.js → dist-BIlKoW0k.js} +1 -1
  281. ksadk/server/static/assets/{dist-BrCcQ47T.js → dist-BjOdlDjj.js} +1 -1
  282. ksadk/server/static/assets/{dist-BmlMPsCc.js → dist-C1sz5Eo3.js} +1 -1
  283. ksadk/server/static/assets/{dist-B0NP_Z0z.js → dist-CWV4lDr1.js} +1 -1
  284. ksadk/server/static/assets/{dist-D-xOD048.js → dist-CmZHzlwZ.js} +1 -1
  285. ksadk/server/static/assets/{dist-DL_NKc_z.js → dist-D6MpCZtz.js} +1 -1
  286. ksadk/server/static/assets/{dist-DyOkZmze.js → dist-DJN_6Y39.js} +1 -1
  287. ksadk/server/static/assets/{dist-DmFaq_Iy.js → dist-DQU9EWw-.js} +1 -1
  288. ksadk/server/static/assets/{dist-gX6GOLKq.js → dist-DWKByxWV.js} +1 -1
  289. ksadk/server/static/assets/{dist-DyWsb78Q.js → dist-Dli7qW-B.js} +1 -1
  290. ksadk/server/static/assets/{dist-BIJVDMuB.js → dist-DsHPdk3W.js} +1 -1
  291. ksadk/server/static/assets/{dist-Dp0k7bWI.js → dist-X4TnqwOK.js} +1 -1
  292. ksadk/server/static/assets/{dist-8EEyGgDx.js → dist-_pHPTyZ-.js} +1 -1
  293. ksadk/server/static/assets/{dist-CPBtqRTW.js → dist-fhOuWds5.js} +1 -1
  294. ksadk/server/static/assets/{dist-D-JMdWPQ.js → dist-gLnrGsoU.js} +1 -1
  295. ksadk/server/static/assets/{dist-BssVmjIo.js → dist-j6Dxmf_j.js} +1 -1
  296. ksadk/server/static/assets/{dist-C3HIIaN5.js → dist-o2OVmKvt.js} +1 -1
  297. ksadk/server/static/assets/{erDiagram-TEJ5UH35-Bk-9bgE5.js → erDiagram-TEJ5UH35-BPItFKH6.js} +1 -1
  298. ksadk/server/static/assets/eventmodeling-FCH6USID-DYFSxbqr.js +1 -0
  299. ksadk/server/static/assets/{flowDiagram-I6XJVG4X-Dn9kZVuZ.js → flowDiagram-I6XJVG4X-BbYB9w5Y.js} +1 -1
  300. ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BGxXlJqm.js → ganttDiagram-6RSMTGT7-TQXa0KXv.js} +1 -1
  301. ksadk/server/static/assets/gitGraph-WXDBUCRP-DyNYp6BN.js +1 -0
  302. ksadk/server/static/assets/{gitGraphDiagram-PVQCEYII-C7Q0X-wi.js → gitGraphDiagram-PVQCEYII-bKblCnpa.js} +1 -1
  303. ksadk/server/static/assets/index-R52pJJvg.js +177 -0
  304. ksadk/server/static/assets/index-jJr3_955.css +2 -0
  305. ksadk/server/static/assets/info-J43DQDTF-WUx7qTde.js +1 -0
  306. ksadk/server/static/assets/{infoDiagram-5YYISTIA-BO_CSqq5.js → infoDiagram-5YYISTIA-BGVT7J_i.js} +1 -1
  307. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-eYLPLd46.js → ishikawaDiagram-YF4QCWOH-K0OOfVna.js} +1 -1
  308. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-De9geSb0.js → journeyDiagram-JHISSGLW-8LeOJaZp.js} +1 -1
  309. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-Cs80kQto.js → kanban-definition-UN3LZRKU-C6uCgZrW.js} +1 -1
  310. ksadk/server/static/assets/{katex-DSrCFD8n.js → katex-CYWseY5E.js} +1 -1
  311. ksadk/server/static/assets/{linear-CN9sbKTF.js → linear-hdBcYzM8.js} +1 -1
  312. ksadk/server/static/assets/{mermaid-parser.core-DlgSBU8l.js → mermaid-parser.core-BWrDjSZM.js} +2 -2
  313. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-9afz7Oug.js → mindmap-definition-RKZ34NQL-Bjy_2v4_.js} +1 -1
  314. ksadk/server/static/assets/packet-YPE3B663-BN13y_xl.js +1 -0
  315. ksadk/server/static/assets/pie-LRSECV5Y-ftSLXhhc.js +1 -0
  316. ksadk/server/static/assets/{pieDiagram-4H26LBE5-DPv_VstN.js → pieDiagram-4H26LBE5-GmmqNiVH.js} +1 -1
  317. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-o77wZ1XK.js → quadrantDiagram-W4KKPZXB-BmkRbwCg.js} +1 -1
  318. ksadk/server/static/assets/radar-GUYGQ44K-jaICkIeO.js +1 -0
  319. ksadk/server/static/assets/{requirementDiagram-4Y6WPE33-BYVw81VC.js → requirementDiagram-4Y6WPE33-DSWFqL7c.js} +1 -1
  320. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-B7nZFMKT.js → sankeyDiagram-5OEKKPKP-l6iXmU65.js} +1 -1
  321. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-BGRRrFw5.js → sequenceDiagram-3UESZ5HK-Cq6UBeQB.js} +1 -1
  322. ksadk/server/static/assets/{stateDiagram-AJRCARHV-BUfHCoPs.js → stateDiagram-AJRCARHV-qzTQpg3Y.js} +1 -1
  323. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-BYCoYGAN.js +1 -0
  324. ksadk/server/static/assets/{timeline-definition-PNZ67QCA-FbCTGrf9.js → timeline-definition-PNZ67QCA-DEz24vV0.js} +1 -1
  325. ksadk/server/static/assets/treeView-BLDUP644-Cpib_vlz.js +1 -0
  326. ksadk/server/static/assets/treemap-LRROVOQU-DQxfmVHK.js +1 -0
  327. ksadk/server/static/assets/{vennDiagram-CIIHVFJN-DWgwtPsJ.js → vennDiagram-CIIHVFJN-CMCixki2.js} +1 -1
  328. ksadk/server/static/assets/wardley-L42UT6IY-BQYHnnkU.js +1 -0
  329. ksadk/server/static/assets/{wardleyDiagram-YWT4CUSO-2x8G-tTM.js → wardleyDiagram-YWT4CUSO-CVzB8F9K.js} +1 -1
  330. ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-Drma3AYl.js → xychartDiagram-2RQKCTM6-BFBqBLc1.js} +1 -1
  331. ksadk/server/static/index.html +2 -2
  332. ksadk/server/terminal_sessions.py +111 -58
  333. ksadk/sessions/__init__.py +69 -19
  334. ksadk/sessions/base.py +31 -11
  335. ksadk/sessions/continuity.py +2 -3
  336. ksadk/sessions/in_memory.py +46 -2
  337. ksadk/sessions/local_service.py +148 -17
  338. ksadk/sessions/postgres_service.py +143 -25
  339. ksadk/sessions/resilient.py +36 -8
  340. ksadk/skills/models.py +17 -5
  341. ksadk/skills/package_store.py +5 -2
  342. ksadk/skills/runtime/agent.py +20 -5
  343. ksadk/skills/runtime/artifacts.py +1 -5
  344. ksadk/skills/runtime/backends/e2b.py +10 -4
  345. ksadk/skills/runtime/backends/local.py +15 -3
  346. ksadk/skills/runtime/base.py +1 -2
  347. ksadk/skills/runtime/executor.py +10 -3
  348. ksadk/skills/runtime/factory.py +4 -2
  349. ksadk/skills/runtime/loader.py +1 -2
  350. ksadk/skills/runtime/registry.py +15 -10
  351. ksadk/skills/runtime/request.py +3 -1
  352. ksadk/skills/service_client.py +47 -16
  353. ksadk/skills/tool_defs.py +15 -10
  354. ksadk/studio/__init__.py +5 -0
  355. ksadk/studio/agent_avatar_assets.py +120 -0
  356. ksadk/studio/agent_lifecycle.py +51 -0
  357. ksadk/studio/api.py +1046 -0
  358. ksadk/studio/api_catalog_routes.py +324 -0
  359. ksadk/studio/api_contracts.py +177 -0
  360. ksadk/studio/api_helpers.py +132 -0
  361. ksadk/studio/authoring.py +524 -0
  362. ksadk/studio/authoring_coordinator.py +285 -0
  363. ksadk/studio/builder.py +320 -0
  364. ksadk/studio/capabilities.py +351 -0
  365. ksadk/studio/cloud.py +278 -0
  366. ksadk/studio/codex_agent_service.py +583 -0
  367. ksadk/studio/codex_builder.py +324 -0
  368. ksadk/studio/codex_manifest.py +278 -0
  369. ksadk/studio/codex_run.py +352 -0
  370. ksadk/studio/compiler.py +189 -0
  371. ksadk/studio/contracts.py +644 -0
  372. ksadk/studio/errors.py +47 -0
  373. ksadk/studio/evaluation.py +192 -0
  374. ksadk/studio/event_store.py +284 -0
  375. ksadk/studio/framework_run.py +114 -0
  376. ksadk/studio/identifiers.py +33 -0
  377. ksadk/studio/mcp_runtime.py +229 -0
  378. ksadk/studio/model_client.py +543 -0
  379. ksadk/studio/model_profile_service.py +214 -0
  380. ksadk/studio/operations.py +243 -0
  381. ksadk/studio/otel_trace.py +754 -0
  382. ksadk/studio/pagination.py +182 -0
  383. ksadk/studio/python_tool_inspection.py +183 -0
  384. ksadk/studio/repository.py +306 -0
  385. ksadk/studio/resource_catalog.py +1217 -0
  386. ksadk/studio/run_service.py +707 -0
  387. ksadk/studio/runtime_catalog.py +74 -0
  388. ksadk/studio/runtime_source.py +339 -0
  389. ksadk/studio/service.py +1141 -0
  390. ksadk/studio/shared_web.py +994 -0
  391. ksadk/studio/skill_discovery.py +628 -0
  392. ksadk/studio/static/assets/PrismRenderer-IYN-ffww.js +3 -0
  393. ksadk/studio/static/assets/index-CaEN0eTR.css +1 -0
  394. ksadk/studio/static/assets/index-DI2eY_NH.js +252 -0
  395. ksadk/studio/static/assets/react-vendor-BNmTiJ-I.js +9 -0
  396. ksadk/studio/static/assets/rolldown-runtime-hePW80VL.js +1 -0
  397. ksadk/studio/static/index.html +15 -0
  398. ksadk/studio/templates.py +630 -0
  399. ksadk/studio/validator.py +246 -0
  400. ksadk/studio/workspace.py +137 -0
  401. ksadk/terminal_exec_policy.py +3 -4
  402. ksadk/tools/gateway.py +101 -15
  403. ksadk/tools/result_budget.py +12 -4
  404. ksadk/toolsets/__init__.py +161 -37
  405. ksadk/toolsets/a2a.py +426 -0
  406. ksadk/toolsets/platform.py +21 -5
  407. ksadk/toolsets/sandbox.py +82 -16
  408. ksadk/toolsets/skills.py +200 -39
  409. ksadk/toolsets/web.py +123 -26
  410. ksadk/toolsets/workspace.py +212 -101
  411. ksadk/tracing/__init__.py +19 -21
  412. ksadk/tracing/exporters/inmemory_exporter.py +62 -51
  413. ksadk/tracing/setup.py +135 -506
  414. ksadk/tracing/span_utils.py +0 -41
  415. ksadk/tui/loop.py +78 -30
  416. ksadk/tui/stream_render.py +1 -0
  417. ksadk/ui_config.py +5 -3
  418. ksadk/version.py +1 -1
  419. ksadk_runtime_common/memory_backend/registry.py +56 -56
  420. ksadk_runtime_common/schemas/runtime_event_v1.json +95 -0
  421. ksadk_runtime_common/workspace_files/router.py +8 -2
  422. agentengine_sdk_python-0.7.0.dist-info/RECORD +0 -482
  423. ksadk/a2a/card_builder.py +0 -64
  424. ksadk/a2a/client.py +0 -309
  425. ksadk/runners/utils/langfuse.py +0 -230
  426. ksadk/server/static/assets/CodeBlock-P16VOLYi.js +0 -9
  427. ksadk/server/static/assets/MathMessageMarkdown-CdGiQWHR.js +0 -8
  428. ksadk/server/static/assets/MermaidBlock-IOcal8cm.js +0 -303
  429. ksadk/server/static/assets/NativeTerminalPanel-DGSDWsei.js +0 -1
  430. ksadk/server/static/assets/architecture-7EHR7CIX-BvUlsA6l.js +0 -1
  431. ksadk/server/static/assets/channel-CTuydARX.js +0 -1
  432. ksadk/server/static/assets/chunk-QZHKN3VN-BnCydivS.js +0 -1
  433. ksadk/server/static/assets/classDiagram-4FO5ZUOK-BNHg45Be.js +0 -1
  434. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-BNHg45Be.js +0 -1
  435. ksadk/server/static/assets/eventmodeling-FCH6USID-DsAgWnlf.js +0 -1
  436. ksadk/server/static/assets/gitGraph-WXDBUCRP-BIao9Yjg.js +0 -1
  437. ksadk/server/static/assets/index-C00Lwg3L.js +0 -165
  438. ksadk/server/static/assets/index-DAURbCsP.css +0 -1
  439. ksadk/server/static/assets/info-J43DQDTF-CyipqGxU.js +0 -1
  440. ksadk/server/static/assets/packet-YPE3B663-CGkyRxlg.js +0 -1
  441. ksadk/server/static/assets/pie-LRSECV5Y-M0uWAf4n.js +0 -1
  442. ksadk/server/static/assets/radar-GUYGQ44K-Bdbm24-x.js +0 -1
  443. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-8xBN_gLM.js +0 -1
  444. ksadk/server/static/assets/treeView-BLDUP644-npMo0ak1.js +0 -1
  445. ksadk/server/static/assets/treemap-LRROVOQU-CHdzrrdo.js +0 -1
  446. ksadk/server/static/assets/wardley-L42UT6IY-BEQz7wyy.js +0 -1
  447. ksadk/tracing/exporters/langfuse_exporter.py +0 -445
  448. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/entry_points.txt +0 -0
  449. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/licenses/LICENSE +0 -0
  450. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,524 @@
1
+ """Inspect-first Agent authoring workflows used by AgentKit Studio.
2
+
3
+ Every flow returns a proposal or inspection before it mutates the canonical
4
+ Agent registry. Quick creation is the only one-step flow and still delegates
5
+ ID allocation to the server.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import hashlib
11
+ import io
12
+ import json
13
+ import re
14
+ import shutil
15
+ import stat
16
+ import zipfile
17
+ from dataclasses import dataclass
18
+ from pathlib import Path, PurePosixPath
19
+ from typing import Any, Literal, cast
20
+ from urllib.parse import urlparse
21
+ from uuid import uuid4
22
+
23
+ import yaml # type: ignore[import-untyped]
24
+ from pydantic import BaseModel, ConfigDict, Field, ValidationError
25
+
26
+ from ksadk.detection.detector import FrameworkDetector
27
+ from ksadk.studio.capabilities import canonical_json, sha256_digest
28
+ from ksadk.studio.codex_manifest import CodexAgentManifest
29
+ from ksadk.studio.contracts import (
30
+ AgentDraft,
31
+ Instructions,
32
+ NetworkPolicy,
33
+ RuntimeRef,
34
+ )
35
+ from ksadk.studio.errors import StudioError
36
+ from ksadk.studio.workspace import Workspace
37
+
38
+ _SLUG = re.compile(r"[^a-z0-9]+")
39
+ _MAX_IMPORT_BYTES = 100 * 1024 * 1024
40
+ _MAX_IMPORT_FILES = 2000
41
+ _SUPPORTED_RUNTIMES = frozenset({"codex", "adk", "langgraph"})
42
+
43
+
44
+ class ConversationProposal(BaseModel):
45
+ model_config = ConfigDict(extra="forbid")
46
+
47
+ name: str = Field(min_length=1, max_length=128)
48
+ slug: str = Field(min_length=1, max_length=63)
49
+ runtimeType: Literal["codex", "adk", "langgraph"]
50
+ description: str = Field(default="", max_length=1024)
51
+ instructions: Instructions
52
+
53
+
54
+ @dataclass(frozen=True)
55
+ class ImportInspection:
56
+ token: str
57
+ kind: Literal["agent-draft", "codex-manifest"]
58
+ payload: dict[str, Any]
59
+ runtime_type: str
60
+ display_name: str
61
+ source_digest: str
62
+ files: tuple[str, ...]
63
+ staging_directory: Path
64
+ manifest_directory: PurePosixPath
65
+
66
+
67
+ class AgentAuthoringService:
68
+ """Pure inspection and staging support; StudioService owns final commits."""
69
+
70
+ def __init__(self, workspace: Workspace) -> None:
71
+ self.workspace = workspace
72
+ self.root = workspace.resolve(".agentkit/authoring")
73
+ self.import_root = workspace.resolve(".agentkit/authoring/imports")
74
+ self.project_root = workspace.resolve(".agentkit/authoring/projects")
75
+ self.import_root.mkdir(parents=True, exist_ok=True)
76
+ self.project_root.mkdir(parents=True, exist_ok=True)
77
+
78
+ @staticmethod
79
+ def normalize_slug(value: str) -> str:
80
+ normalized = _SLUG.sub("-", str(value or "").strip().lower()).strip("-")
81
+ if not normalized or not normalized[0].isalpha():
82
+ normalized = f"agent-{normalized}" if normalized else "agent"
83
+ return normalized[:48].rstrip("-")
84
+
85
+ def allocate_agent_id(self, _slug: str) -> str:
86
+ """Allocate a server-owned identifier without using a slug as a path segment.
87
+
88
+ Callers retain the normalized slug in Agent metadata for display and search,
89
+ while source directories only use this opaque identifier.
90
+ """
91
+
92
+ for _attempt in range(100):
93
+ candidate = f"agentkit-{uuid4().hex[:8]}"
94
+ if not (self.workspace.resolve("agents") / candidate).exists():
95
+ return candidate
96
+ raise StudioError(
97
+ "AGENT_ID_ALLOCATION_FAILED",
98
+ "无法分配唯一 Agent ID",
99
+ status_code=500,
100
+ )
101
+
102
+ @staticmethod
103
+ def runtime_ref(agent_id: str, runtime_type: str) -> RuntimeRef:
104
+ normalized = str(runtime_type or "").strip().lower()
105
+ if normalized not in _SUPPORTED_RUNTIMES:
106
+ raise StudioError(
107
+ "RUNTIME_NOT_SUPPORTED",
108
+ "创建方式仅支持 Codex、ADK 和 LangGraph",
109
+ status_code=422,
110
+ field="runtimeType",
111
+ details={"runtimeType": normalized},
112
+ )
113
+ if normalized == "codex":
114
+ return RuntimeRef(type="codex", version="0.144.4")
115
+ return RuntimeRef(
116
+ type=cast(Any, normalized),
117
+ project_path=f"agents/{agent_id}/source",
118
+ entry_point="agent.py",
119
+ agent_variable="graph" if normalized == "langgraph" else "root_agent",
120
+ )
121
+
122
+ def inspect_import(self, content: bytes, *, filename: str) -> dict[str, Any]:
123
+ if not content:
124
+ raise StudioError(
125
+ "AGENT_IMPORT_EMPTY",
126
+ "导入文件不能为空",
127
+ status_code=422,
128
+ )
129
+ if len(content) > _MAX_IMPORT_BYTES:
130
+ raise StudioError(
131
+ "AGENT_IMPORT_TOO_LARGE",
132
+ "Agent 导入包不能超过 100 MiB",
133
+ status_code=413,
134
+ )
135
+ token = f"imp_{uuid4().hex}"
136
+ staging = self.workspace.resolve(self.import_root / token)
137
+ staging.mkdir(parents=True, exist_ok=False)
138
+ try:
139
+ files, manifest_path = self._stage_import(staging, content, filename=filename)
140
+ payload = self._load_yaml_object(manifest_path)
141
+ kind, runtime_type, display_name = self._classify_import(payload)
142
+ manifest_relative = manifest_path.relative_to(staging)
143
+ record = {
144
+ "format": "agentkit.authoring-import/v1",
145
+ "token": token,
146
+ "kind": kind,
147
+ "runtimeType": runtime_type,
148
+ "displayName": display_name,
149
+ "sourceDigest": self._tree_digest(staging, exclude={"inspection.json"}),
150
+ "files": files,
151
+ "manifestPath": manifest_relative.as_posix(),
152
+ "manifestDirectory": manifest_relative.parent.as_posix(),
153
+ "payload": payload,
154
+ }
155
+ self.workspace.atomic_write_text(
156
+ staging / "inspection.json",
157
+ json.dumps(record, ensure_ascii=False, sort_keys=True, indent=2) + "\n",
158
+ )
159
+ return self._public_import_record(record)
160
+ except BaseException:
161
+ shutil.rmtree(staging, ignore_errors=True)
162
+ raise
163
+
164
+ def load_import(self, token: str) -> ImportInspection:
165
+ staging = self._token_directory(self.import_root, token, prefix="imp_")
166
+ record_path = staging / "inspection.json"
167
+ try:
168
+ record = json.loads(record_path.read_text(encoding="utf-8"))
169
+ except (OSError, ValueError) as exc:
170
+ raise StudioError(
171
+ "AGENT_IMPORT_INSPECTION_NOT_FOUND",
172
+ "导入检查不存在或已失效",
173
+ status_code=404,
174
+ ) from exc
175
+ expected = str(record.get("sourceDigest") or "")
176
+ actual = self._tree_digest(staging, exclude={"inspection.json"})
177
+ if not expected or expected != actual:
178
+ raise StudioError(
179
+ "AGENT_IMPORT_CHANGED",
180
+ "导入暂存内容在确认前发生变化,请重新检查",
181
+ status_code=409,
182
+ )
183
+ manifest_directory = PurePosixPath(str(record.get("manifestDirectory") or "."))
184
+ return ImportInspection(
185
+ token=token,
186
+ kind=cast(Any, record["kind"]),
187
+ payload=cast(dict[str, Any], record["payload"]),
188
+ runtime_type=str(record["runtimeType"]),
189
+ display_name=str(record["displayName"]),
190
+ source_digest=expected,
191
+ files=tuple(str(item) for item in record.get("files") or []),
192
+ staging_directory=staging,
193
+ manifest_directory=manifest_directory,
194
+ )
195
+
196
+ def consume_import(self, token: str) -> None:
197
+ staging = self._token_directory(self.import_root, token, prefix="imp_")
198
+ shutil.rmtree(staging, ignore_errors=True)
199
+
200
+ def source_directory(self, inspection: ImportInspection) -> Path | None:
201
+ if inspection.kind != "agent-draft":
202
+ return None
203
+ draft = AgentDraft.model_validate(inspection.payload)
204
+ runtime = draft.spec.runtime
205
+ if runtime is None or not runtime.project_path:
206
+ return None
207
+ candidates = [
208
+ inspection.staging_directory / inspection.manifest_directory / runtime.project_path,
209
+ inspection.staging_directory / runtime.project_path,
210
+ inspection.staging_directory / inspection.manifest_directory / "source",
211
+ ]
212
+ for candidate in candidates:
213
+ resolved = candidate.resolve()
214
+ try:
215
+ resolved.relative_to(inspection.staging_directory.resolve())
216
+ except ValueError:
217
+ continue
218
+ if resolved.is_dir():
219
+ return resolved
220
+ return None
221
+
222
+ def inspect_project(self, project_path: str) -> dict[str, Any]:
223
+ project = self.workspace.resolve(project_path, must_exist=True)
224
+ if not project.is_dir():
225
+ raise StudioError(
226
+ "PROJECT_PATH_INVALID",
227
+ "项目识别路径必须是工作区内的目录",
228
+ status_code=422,
229
+ )
230
+ detection = FrameworkDetector(str(project)).detect()
231
+ runtime_type = detection.type.value
232
+ if not detection.is_valid or runtime_type not in _SUPPORTED_RUNTIMES:
233
+ raise StudioError(
234
+ "PROJECT_RUNTIME_UNSUPPORTED",
235
+ "没有识别到可由 Studio 管理的 Codex、ADK 或 LangGraph Runtime",
236
+ status_code=422,
237
+ details={"detected": runtime_type},
238
+ )
239
+ token = f"prj_{uuid4().hex}"
240
+ relative = self.workspace.relative(project)
241
+ record = {
242
+ "format": "agentkit.authoring-project/v1",
243
+ "token": token,
244
+ "projectPath": relative,
245
+ "runtimeType": runtime_type,
246
+ "name": detection.name or project.name,
247
+ "entryPoint": detection.entry_point or None,
248
+ "agentVariable": detection.agent_variable or None,
249
+ "confidence": float(detection.confidence),
250
+ "evidence": {
251
+ "config": dict(detection.raw_config or {}),
252
+ "packagePath": detection.package_path,
253
+ },
254
+ "sourceDigest": self._tree_digest(project),
255
+ "requiresConfirmation": True,
256
+ }
257
+ self.workspace.atomic_write_text(
258
+ self.project_root / f"{token}.json",
259
+ json.dumps(record, ensure_ascii=False, sort_keys=True, indent=2) + "\n",
260
+ )
261
+ return {**record, "inspectionToken": token}
262
+
263
+ def load_project(self, token: str) -> dict[str, Any]:
264
+ if not re.fullmatch(r"prj_[0-9a-f]{32}", token):
265
+ raise StudioError(
266
+ "PROJECT_INSPECTION_NOT_FOUND",
267
+ "项目识别检查不存在或已失效",
268
+ status_code=404,
269
+ )
270
+ path = self.workspace.resolve(self.project_root / f"{token}.json")
271
+ try:
272
+ record = json.loads(path.read_text(encoding="utf-8"))
273
+ except (OSError, ValueError) as exc:
274
+ raise StudioError(
275
+ "PROJECT_INSPECTION_NOT_FOUND",
276
+ "项目识别检查不存在或已失效",
277
+ status_code=404,
278
+ ) from exc
279
+ project = self.workspace.resolve(str(record["projectPath"]), must_exist=True)
280
+ if self._tree_digest(project) != record.get("sourceDigest"):
281
+ raise StudioError(
282
+ "PROJECT_CHANGED_AFTER_INSPECTION",
283
+ "项目在确认前发生变化,请重新识别",
284
+ status_code=409,
285
+ )
286
+ return cast(dict[str, Any], record)
287
+
288
+ def consume_project(self, token: str) -> None:
289
+ path = self.workspace.resolve(self.project_root / f"{token}.json")
290
+ if path.is_file():
291
+ path.unlink()
292
+
293
+ @staticmethod
294
+ def parse_conversation_proposal(content: str) -> ConversationProposal:
295
+ text = str(content or "").strip()
296
+ if text.startswith("```"):
297
+ lines = text.splitlines()
298
+ if lines and lines[0].startswith("```"):
299
+ lines = lines[1:]
300
+ if lines and lines[-1].strip() == "```":
301
+ lines = lines[:-1]
302
+ text = "\n".join(lines).strip()
303
+ try:
304
+ payload = json.loads(text)
305
+ proposal = ConversationProposal.model_validate(payload)
306
+ except (ValueError, ValidationError) as exc:
307
+ raise StudioError(
308
+ "AUTHORING_MODEL_OUTPUT_INVALID",
309
+ "对话构建模型没有返回合法的 Agent Draft Patch",
310
+ status_code=502,
311
+ details={"reason": str(exc)},
312
+ ) from exc
313
+ normalized_slug = AgentAuthoringService.normalize_slug(proposal.slug)
314
+ return proposal.model_copy(update={"slug": normalized_slug})
315
+
316
+ @staticmethod
317
+ def conversation_messages(messages: list[dict[str, str]]) -> list[dict[str, str]]:
318
+ if not messages:
319
+ raise StudioError(
320
+ "AUTHORING_CONVERSATION_EMPTY",
321
+ "对话构建至少需要一条消息",
322
+ status_code=422,
323
+ )
324
+ normalized: list[dict[str, str]] = [
325
+ {
326
+ "role": "system",
327
+ "content": (
328
+ "你是 AgentKit Studio 的 Agent 设计助手。根据对话生成一个 JSON Draft Patch,"
329
+ "不得输出 Markdown。字段必须且只能包含 name、slug、runtimeType、description、"
330
+ "instructions;runtimeType 只能是 codex、adk、langgraph;instructions 必须包含"
331
+ " system 和 task。只提出配置,不写文件、不宣称已经创建。"
332
+ ),
333
+ }
334
+ ]
335
+ for item in messages:
336
+ role = str(item.get("role") or "").strip()
337
+ content = str(item.get("content") or "").strip()
338
+ if role not in {"user", "assistant"} or not content:
339
+ raise StudioError(
340
+ "AUTHORING_CONVERSATION_INVALID",
341
+ "对话消息只允许非空 user/assistant 文本",
342
+ status_code=422,
343
+ )
344
+ normalized.append({"role": role, "content": content})
345
+ return normalized
346
+
347
+ @staticmethod
348
+ def authoring_network_policy(endpoint_url: str) -> NetworkPolicy:
349
+ host = (urlparse(endpoint_url).hostname or "").lower().rstrip(".")
350
+ return NetworkPolicy(
351
+ mode="restricted",
352
+ allowed_hosts=[host] if host else [],
353
+ allow_private_network=False,
354
+ )
355
+
356
+ def _stage_import(
357
+ self,
358
+ staging: Path,
359
+ content: bytes,
360
+ *,
361
+ filename: str,
362
+ ) -> tuple[list[str], Path]:
363
+ if filename.lower().endswith(".zip"):
364
+ return self._extract_zip(staging, content)
365
+ if not filename.lower().endswith((".yaml", ".yml")):
366
+ raise StudioError(
367
+ "AGENT_IMPORT_FORMAT_UNSUPPORTED",
368
+ "仅支持 Agent YAML 或 ZIP",
369
+ status_code=422,
370
+ )
371
+ path = staging / "agent.yaml"
372
+ path.write_bytes(content)
373
+ return ["agent.yaml"], path
374
+
375
+ def _extract_zip(self, staging: Path, content: bytes) -> tuple[list[str], Path]:
376
+ try:
377
+ archive = zipfile.ZipFile(io.BytesIO(content))
378
+ except zipfile.BadZipFile as exc:
379
+ raise StudioError(
380
+ "AGENT_IMPORT_ARCHIVE_INVALID",
381
+ "Agent ZIP 无法解析",
382
+ status_code=422,
383
+ ) from exc
384
+ with archive:
385
+ entries = [item for item in archive.infolist() if not item.is_dir()]
386
+ if not entries or len(entries) > _MAX_IMPORT_FILES:
387
+ raise StudioError(
388
+ "AGENT_IMPORT_ARCHIVE_INVALID",
389
+ "Agent ZIP 文件数量不合法",
390
+ status_code=422,
391
+ )
392
+ expanded = sum(item.file_size for item in entries)
393
+ if expanded > _MAX_IMPORT_BYTES:
394
+ raise StudioError(
395
+ "AGENT_IMPORT_TOO_LARGE",
396
+ "Agent ZIP 解压后不能超过 100 MiB",
397
+ status_code=413,
398
+ )
399
+ files: list[str] = []
400
+ manifests: list[Path] = []
401
+ for item in entries:
402
+ path = PurePosixPath(item.filename)
403
+ mode = (item.external_attr >> 16) & 0o170000
404
+ if (
405
+ path.is_absolute()
406
+ or ".." in path.parts
407
+ or mode in {stat.S_IFLNK, stat.S_IFCHR, stat.S_IFBLK, stat.S_IFIFO}
408
+ or (item.compress_size == 0 and item.file_size > 0)
409
+ or (
410
+ item.compress_size > 0
411
+ and item.file_size / item.compress_size > 200
412
+ )
413
+ ):
414
+ raise StudioError(
415
+ "AGENT_IMPORT_ARCHIVE_UNSAFE",
416
+ "Agent ZIP 包含不安全路径、链接或压缩条目",
417
+ status_code=422,
418
+ details={"entry": item.filename},
419
+ )
420
+ target = staging / Path(*path.parts)
421
+ target.parent.mkdir(parents=True, exist_ok=True)
422
+ target.write_bytes(archive.read(item))
423
+ files.append(path.as_posix())
424
+ if path.name in {"agent.yaml", "agentengine.yaml"}:
425
+ manifests.append(target)
426
+ if len(manifests) != 1:
427
+ raise StudioError(
428
+ "AGENT_IMPORT_MANIFEST_REQUIRED",
429
+ "Agent ZIP 必须且只能包含一个 agent.yaml 或 agentengine.yaml",
430
+ status_code=422,
431
+ )
432
+ return sorted(files), manifests[0]
433
+
434
+ @staticmethod
435
+ def _load_yaml_object(path: Path) -> dict[str, Any]:
436
+ try:
437
+ payload = yaml.safe_load(path.read_text(encoding="utf-8-sig"))
438
+ except (OSError, UnicodeDecodeError, yaml.YAMLError) as exc:
439
+ raise StudioError(
440
+ "AGENT_IMPORT_MANIFEST_INVALID",
441
+ "Agent YAML 无法解析",
442
+ status_code=422,
443
+ ) from exc
444
+ if not isinstance(payload, dict):
445
+ raise StudioError(
446
+ "AGENT_IMPORT_MANIFEST_INVALID",
447
+ "Agent YAML 根节点必须是对象",
448
+ status_code=422,
449
+ )
450
+ return cast(dict[str, Any], payload)
451
+
452
+ @staticmethod
453
+ def _classify_import(payload: dict[str, Any]) -> tuple[str, str, str]:
454
+ try:
455
+ if payload.get("kind") == "Agent":
456
+ draft = AgentDraft.model_validate(payload)
457
+ if draft.spec.runtime is None:
458
+ raise ValueError("Agent Draft 缺少 spec.runtime")
459
+ return "agent-draft", draft.spec.runtime.type, draft.metadata.name
460
+ manifest = CodexAgentManifest.model_validate(payload)
461
+ return "codex-manifest", "codex", manifest.name
462
+ except (ValueError, ValidationError) as exc:
463
+ raise StudioError(
464
+ "AGENT_IMPORT_CONTRACT_INVALID",
465
+ "导入内容不符合 Agent Draft 或 Codex agentengine.yaml 合同",
466
+ status_code=422,
467
+ details={"reason": str(exc)},
468
+ ) from exc
469
+
470
+ @staticmethod
471
+ def _tree_digest(root: Path, *, exclude: set[str] | None = None) -> str:
472
+ ignored = exclude or set()
473
+ entries: list[dict[str, Any]] = []
474
+ for path in sorted(root.rglob("*"), key=lambda item: item.as_posix()):
475
+ if path.is_symlink():
476
+ raise StudioError(
477
+ "AUTHORING_SOURCE_UNSAFE",
478
+ "待检查内容包含软链接",
479
+ status_code=422,
480
+ details={"path": str(path)},
481
+ )
482
+ if not path.is_file() or path.name in ignored:
483
+ continue
484
+ relative = path.relative_to(root).as_posix()
485
+ content = path.read_bytes()
486
+ entries.append(
487
+ {
488
+ "path": relative,
489
+ "sha256": hashlib.sha256(content).hexdigest(),
490
+ "size": len(content),
491
+ }
492
+ )
493
+ return sha256_digest(canonical_json(entries))
494
+
495
+ def _token_directory(self, root: Path, token: str, *, prefix: str) -> Path:
496
+ if not re.fullmatch(rf"{re.escape(prefix)}[0-9a-f]{{32}}", token):
497
+ raise StudioError(
498
+ "AGENT_IMPORT_INSPECTION_NOT_FOUND",
499
+ "导入检查不存在或已失效",
500
+ status_code=404,
501
+ )
502
+ path = self.workspace.resolve(root / token)
503
+ if not path.is_dir():
504
+ raise StudioError(
505
+ "AGENT_IMPORT_INSPECTION_NOT_FOUND",
506
+ "导入检查不存在或已失效",
507
+ status_code=404,
508
+ )
509
+ return path
510
+
511
+ @staticmethod
512
+ def _public_import_record(record: dict[str, Any]) -> dict[str, Any]:
513
+ return {
514
+ "inspectionToken": record["token"],
515
+ "kind": record["kind"],
516
+ "runtimeType": record["runtimeType"],
517
+ "displayName": record["displayName"],
518
+ "sourceDigest": record["sourceDigest"],
519
+ "files": record["files"],
520
+ "requiresConfirmation": True,
521
+ }
522
+
523
+
524
+ __all__ = ["AgentAuthoringService", "ConversationProposal", "ImportInspection"]