agentengine-sdk-python 0.6.9__py3-none-any.whl → 0.8.0__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 (384) hide show
  1. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/METADATA +41 -23
  2. agentengine_sdk_python-0.8.0.dist-info/RECORD +578 -0
  3. ksadk/a2a/__init__.py +178 -11
  4. ksadk/a2a/bootstrap.py +334 -0
  5. ksadk/a2a/card.py +120 -0
  6. ksadk/a2a/context_store.py +203 -0
  7. ksadk/a2a/control_plane.py +818 -0
  8. ksadk/a2a/credential.py +99 -0
  9. ksadk/a2a/event_adapter.py +149 -0
  10. ksadk/a2a/executor.py +406 -89
  11. ksadk/a2a/external_transport.py +516 -0
  12. ksadk/a2a/identity.py +304 -0
  13. ksadk/a2a/ids.py +22 -0
  14. ksadk/a2a/langgraph.py +285 -0
  15. ksadk/a2a/resume_store.py +201 -0
  16. ksadk/a2a/routes.py +105 -0
  17. ksadk/a2a/server.py +107 -60
  18. ksadk/a2a/space_client.py +1101 -0
  19. ksadk/a2a/task_adapter.py +423 -0
  20. ksadk/a2a/task_event_dispatcher.py +167 -0
  21. ksadk/a2a/task_event_outbox.py +326 -0
  22. ksadk/a2a/task_store.py +206 -0
  23. ksadk/a2ui/__init__.py +27 -0
  24. ksadk/a2ui/core.py +186 -0
  25. ksadk/a2ui/models.py +166 -0
  26. ksadk/a2ui/renderer.py +198 -0
  27. ksadk/agui/__init__.py +9 -0
  28. ksadk/agui/a2ui_projection.py +92 -0
  29. ksadk/agui/agent.py +857 -0
  30. ksadk/agui/config.py +86 -0
  31. ksadk/agui/routes.py +55 -0
  32. ksadk/api/client.py +252 -191
  33. ksadk/builders/__init__.py +2 -0
  34. ksadk/builders/base.py +16 -14
  35. ksadk/builders/code_builder.py +318 -236
  36. ksadk/builders/container_builder.py +187 -167
  37. ksadk/builders/framework_requirements.py +29 -11
  38. ksadk/builders/ks3_uploader.py +58 -27
  39. ksadk/builders/managed_runtime_builder.py +145 -0
  40. ksadk/builders/mcp_builder.py +91 -92
  41. ksadk/builders/requirements_utils.py +1 -5
  42. ksadk/cli/__init__.py +89 -171
  43. ksadk/cli/cmd_a2a.py +254 -70
  44. ksadk/cli/cmd_agent.py +19 -5
  45. ksadk/cli/cmd_build.py +158 -29
  46. ksadk/cli/cmd_completion.py +47 -35
  47. ksadk/cli/cmd_config.py +267 -198
  48. ksadk/cli/cmd_create.py +533 -726
  49. ksadk/cli/cmd_dashboard.py +120 -45
  50. ksadk/cli/cmd_deploy.py +117 -58
  51. ksadk/cli/cmd_destroy.py +44 -14
  52. ksadk/cli/cmd_files.py +63 -24
  53. ksadk/cli/cmd_hermes.py +173 -90
  54. ksadk/cli/cmd_invoke.py +388 -159
  55. ksadk/cli/cmd_launch.py +64 -28
  56. ksadk/cli/cmd_mcp.py +103 -64
  57. ksadk/cli/cmd_model.py +14 -5
  58. ksadk/cli/cmd_openclaw.py +488 -228
  59. ksadk/cli/cmd_replay.py +71 -0
  60. ksadk/cli/cmd_run.py +44 -14
  61. ksadk/cli/cmd_status.py +80 -31
  62. ksadk/cli/cmd_version.py +20 -13
  63. ksadk/cli/cmd_web.py +89 -13
  64. ksadk/cli/deploy_utils.py +20 -20
  65. ksadk/cli/dry_run.py +5 -3
  66. ksadk/cli/error_utils.py +37 -12
  67. ksadk/cli/global_options.py +1 -2
  68. ksadk/cli/local_runtime.py +1 -2
  69. ksadk/cli/model_catalog.py +6 -2
  70. ksadk/cli/network_options.py +38 -16
  71. ksadk/cli/resource_common.py +6 -2
  72. ksadk/cli/storage.py +0 -1
  73. ksadk/cli/ui.py +5 -3
  74. ksadk/cli/workflow_common.py +51 -17
  75. ksadk/codex/__init__.py +12 -0
  76. ksadk/codex/client.py +408 -0
  77. ksadk/codex/phase.py +143 -0
  78. ksadk/codex/runtime.py +449 -0
  79. ksadk/common/aicp_env.py +0 -1
  80. ksadk/common/auth.py +2 -2
  81. ksadk/common/constants.py +1 -3
  82. ksadk/common/llm_utils.py +17 -13
  83. ksadk/compat/__init__.py +5 -0
  84. ksadk/compat/adk_compat.py +200 -0
  85. ksadk/compat/copilotkit_a2ui.py +330 -0
  86. ksadk/configs/__init__.py +12 -12
  87. ksadk/configs/env_registry.py +501 -100
  88. ksadk/configs/global_config.py +2 -1
  89. ksadk/configs/settings.py +151 -124
  90. ksadk/conversations/__init__.py +7 -5
  91. ksadk/conversations/attachment_storage.py +16 -11
  92. ksadk/conversations/attachments.py +44 -36
  93. ksadk/conversations/compaction_pipeline.py +12 -12
  94. ksadk/conversations/compaction_prompt.py +9 -5
  95. ksadk/conversations/context.py +221 -6
  96. ksadk/conversations/message_projection.py +656 -0
  97. ksadk/conversations/model_context.py +22 -6
  98. ksadk/conversations/model_options.py +0 -1
  99. ksadk/conversations/normalize.py +20 -16
  100. ksadk/conversations/reasoning_markup.py +1 -1
  101. ksadk/conversations/runtime.py +339 -5187
  102. ksadk/conversations/runtime_compaction.py +333 -0
  103. ksadk/conversations/runtime_constants.py +27 -0
  104. ksadk/conversations/runtime_governance.py +185 -0
  105. ksadk/conversations/runtime_input.py +652 -0
  106. ksadk/conversations/runtime_invocation.py +395 -0
  107. ksadk/conversations/runtime_metadata.py +475 -0
  108. ksadk/conversations/runtime_observability.py +355 -0
  109. ksadk/conversations/runtime_payloads.py +272 -0
  110. ksadk/conversations/runtime_persistence.py +440 -0
  111. ksadk/conversations/runtime_preparation.py +418 -0
  112. ksadk/conversations/runtime_resume.py +816 -0
  113. ksadk/conversations/runtime_stream_events.py +902 -0
  114. ksadk/conversations/runtime_streaming.py +547 -0
  115. ksadk/conversations/semantic_summary.py +17 -5
  116. ksadk/conversations/session_title.py +6 -3
  117. ksadk/deployment/__init__.py +15 -16
  118. ksadk/deployment/agent_access.py +12 -23
  119. ksadk/deployment/base.py +52 -39
  120. ksadk/deployment/manager.py +63 -65
  121. ksadk/deployment/providers/serverless.py +326 -209
  122. ksadk/deployment/registry.py +38 -32
  123. ksadk/deployment/state.py +11 -11
  124. ksadk/deployment/ui_config.py +1 -1
  125. ksadk/detection/__init__.py +1 -1
  126. ksadk/detection/detector.py +146 -83
  127. ksadk/detection/mcp_detector.py +9 -6
  128. ksadk/events/__init__.py +25 -0
  129. ksadk/events/parser.py +191 -0
  130. ksadk/events/replay.py +37 -0
  131. ksadk/events/runtime_event.py +264 -0
  132. ksadk/events/store.py +317 -0
  133. ksadk/harness/__init__.py +34 -0
  134. ksadk/harness/app.py +278 -0
  135. ksadk/harness/config.py +181 -0
  136. ksadk/harness/reasoner.py +105 -0
  137. ksadk/harness/runner.py +211 -0
  138. ksadk/harness/sandbox.py +141 -0
  139. ksadk/harness/tools.py +222 -0
  140. ksadk/hermes_terminal.py +23 -12
  141. ksadk/identity/resolver.py +15 -9
  142. ksadk/ids.py +32 -0
  143. ksadk/knowledge_base/adk_tool.py +1 -1
  144. ksadk/knowledge_base/client.py +23 -29
  145. ksadk/managed_runtime.py +188 -0
  146. ksadk/markdown.py +5 -2
  147. ksadk/mcp_runtime/__init__.py +9 -6
  148. ksadk/memory/__init__.py +1 -1
  149. ksadk/memory/adk/__init__.py +2 -0
  150. ksadk/memory/adk/backends/base_ltm_backend.py +2 -6
  151. ksadk/memory/adk/backends/http_ltm_backend.py +13 -21
  152. ksadk/memory/adk/backends/inmemory_ltm_backend.py +7 -13
  153. ksadk/memory/adk/backends/sdk_ltm_backend.py +26 -33
  154. ksadk/memory/adk/long_term_memory.py +31 -35
  155. ksadk/memory/adk/resilient_session_service.py +267 -0
  156. ksadk/memory/adk/short_term_memory.py +33 -27
  157. ksadk/memory/adk_tool.py +3 -2
  158. ksadk/memory/backends/base.py +19 -19
  159. ksadk/memory/backends/memory.py +24 -25
  160. ksadk/memory/backends/redis.py +31 -30
  161. ksadk/memory/langchain_tool.py +2 -1
  162. ksadk/memory/manager.py +52 -47
  163. ksadk/memory/service.py +10 -8
  164. ksadk/model_policy.py +15 -7
  165. ksadk/model_proxy/__init__.py +51 -0
  166. ksadk/model_proxy/bootstrap.py +80 -0
  167. ksadk/model_proxy/cache.py +165 -0
  168. ksadk/model_proxy/config.py +45 -0
  169. ksadk/model_proxy/demo_webui.py +115 -0
  170. ksadk/model_proxy/detect.py +145 -0
  171. ksadk/model_proxy/gate.py +68 -0
  172. ksadk/model_proxy/namespace.py +173 -0
  173. ksadk/model_proxy/protocol_proxy.py +22 -0
  174. ksadk/model_proxy/server.py +300 -0
  175. ksadk/model_proxy/transform.py +691 -0
  176. ksadk/openclaw_gateway.py +29 -14
  177. ksadk/runners/adk_runner.py +1248 -164
  178. ksadk/runners/base_runner.py +46 -29
  179. ksadk/runners/codex_runner.py +132 -0
  180. ksadk/runners/deepagents_runner.py +0 -1
  181. ksadk/runners/factory.py +17 -5
  182. ksadk/runners/langchain_runner.py +29 -530
  183. ksadk/runners/langgraph_runner.py +490 -95
  184. ksadk/runners/patch_langchain.py +16 -16
  185. ksadk/runners/remote_runner.py +264 -38
  186. ksadk/runners/usage_accumulator.py +1 -0
  187. ksadk/runners/utils/langfuse.py +3 -3
  188. ksadk/runners/utils/loader.py +7 -7
  189. ksadk/runtime/__init__.py +41 -0
  190. ksadk/runtime/adapter.py +345 -0
  191. ksadk/runtime/framework_adapters.py +124 -0
  192. ksadk/runtime/preprocessing.py +118 -0
  193. ksadk/runtime/runner_adapter.py +873 -0
  194. ksadk/runtime_context.py +18 -3
  195. ksadk/runtime_state.py +0 -1
  196. ksadk/sandbox/__init__.py +4 -1
  197. ksadk/sandbox/backends/__init__.py +4 -1
  198. ksadk/sandbox/backends/e2b.py +13 -5
  199. ksadk/sandbox/backends/local_process.py +11 -3
  200. ksadk/sandbox/base.py +10 -15
  201. ksadk/sandbox/factory.py +14 -4
  202. ksadk/sandbox/registry.py +57 -6
  203. ksadk/server/api_models.py +15 -5
  204. ksadk/server/app.py +455 -3743
  205. ksadk/server/composition.py +21 -0
  206. ksadk/server/factory.py +624 -0
  207. ksadk/server/routes/__init__.py +1 -0
  208. ksadk/server/routes/checkpoint_resolution.py +116 -0
  209. ksadk/server/routes/common.py +713 -0
  210. ksadk/server/routes/control.py +327 -0
  211. ksadk/server/routes/dependencies.py +98 -0
  212. ksadk/server/routes/misc.py +448 -0
  213. ksadk/server/routes/models.py +443 -0
  214. ksadk/server/routes/openai_compat.py +207 -0
  215. ksadk/server/routes/projection.py +795 -0
  216. ksadk/server/routes/routers.py +52 -0
  217. ksadk/server/routes/run.py +726 -0
  218. ksadk/server/routes/sessions.py +649 -0
  219. ksadk/server/routes/streaming.py +223 -0
  220. ksadk/server/routes/workspace.py +381 -0
  221. ksadk/server/static/assets/{ArtifactsPanel-C0meuOdi.js → ArtifactsPanel-Cjnqt-8W.js} +1 -1
  222. ksadk/server/static/assets/CodeBlock-BqyqZfuB.js +9 -0
  223. ksadk/server/static/assets/MathMessageMarkdown-BpH7jiFM.js +8 -0
  224. ksadk/server/static/assets/MermaidBlock-DWuxETaJ.js +303 -0
  225. ksadk/server/static/assets/NativeTerminalPanel-MDTopWMO.js +1 -0
  226. ksadk/server/static/assets/{arc-bj2rZnP5.js → arc-CFKwSk_B.js} +1 -1
  227. ksadk/server/static/assets/architecture-7EHR7CIX-CynqOWjZ.js +1 -0
  228. ksadk/server/static/assets/{architectureDiagram-3BPJPVTR-2u1ltDIj.js → architectureDiagram-3BPJPVTR-Dej8jX8c.js} +1 -1
  229. ksadk/server/static/assets/{blockDiagram-GPEHLZMM-D6fvdK20.js → blockDiagram-GPEHLZMM-1F64jqvy.js} +1 -1
  230. ksadk/server/static/assets/{c4Diagram-AAUBKEIU-BK82L5n5.js → c4Diagram-AAUBKEIU-9q7AUAKb.js} +1 -1
  231. ksadk/server/static/assets/channel-iUGv2N24.js +1 -0
  232. ksadk/server/static/assets/{chunk-2J33WTMH-DLVdkeyQ.js → chunk-2J33WTMH-6qgwJm7Q.js} +1 -1
  233. ksadk/server/static/assets/{chunk-4BX2VUAB-BbwsYcbW.js → chunk-4BX2VUAB-CsWIQRhc.js} +1 -1
  234. ksadk/server/static/assets/{chunk-55IACEB6-DOairxzw.js → chunk-55IACEB6-BK8paA20.js} +1 -1
  235. ksadk/server/static/assets/{chunk-727SXJPM-hCO487gs.js → chunk-727SXJPM-B-JEhy7h.js} +1 -1
  236. ksadk/server/static/assets/{chunk-AQP2D5EJ-XU8vDOuG.js → chunk-AQP2D5EJ-DYBvMHAa.js} +1 -1
  237. ksadk/server/static/assets/{chunk-FMBD7UC4-PrapyfMu.js → chunk-FMBD7UC4-BGhvzDYt.js} +1 -1
  238. ksadk/server/static/assets/{chunk-ND2GUHAM-CboKhTZ3.js → chunk-ND2GUHAM-D98vPd-E.js} +1 -1
  239. ksadk/server/static/assets/chunk-QZHKN3VN-YQIEXeF6.js +1 -0
  240. ksadk/server/static/assets/classDiagram-4FO5ZUOK-CDAUuoXK.js +1 -0
  241. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-CDAUuoXK.js +1 -0
  242. ksadk/server/static/assets/{cose-bilkent-S5V4N54A-_N8bJ-EU.js → cose-bilkent-S5V4N54A-CX3yotHq.js} +1 -1
  243. ksadk/server/static/assets/{dagre-BM42HDAG-FoDlo5J2.js → dagre-BM42HDAG-C7TguAWo.js} +1 -1
  244. ksadk/server/static/assets/{diagram-2AECGRRQ-BBp6ebBw.js → diagram-2AECGRRQ-Dd17_kws.js} +1 -1
  245. ksadk/server/static/assets/{diagram-5GNKFQAL-CqOvG1PR.js → diagram-5GNKFQAL-DVIClIil.js} +1 -1
  246. ksadk/server/static/assets/{diagram-KO2AKTUF-O9T3t6ba.js → diagram-KO2AKTUF-Df_3gYQa.js} +1 -1
  247. ksadk/server/static/assets/{diagram-LMA3HP47-CGG8ekBO.js → diagram-LMA3HP47-BhLk6ZtA.js} +1 -1
  248. ksadk/server/static/assets/{diagram-OG6HWLK6-4BmH1EEg.js → diagram-OG6HWLK6-D4GNGvuu.js} +1 -1
  249. ksadk/server/static/assets/{dist-CiZHB6ZW.js → dist-6CVKuTAe.js} +1 -1
  250. ksadk/server/static/assets/{dist-BMKM66B4.js → dist-BUHRxvDG.js} +1 -1
  251. ksadk/server/static/assets/{dist-CZ6C3x8B.js → dist-BZ9n9AjO.js} +1 -1
  252. ksadk/server/static/assets/{dist-DbaH3p5h.js → dist-BibcG1V9.js} +1 -1
  253. ksadk/server/static/assets/{dist-BVzcWa6D.js → dist-Bs-jQcRX.js} +1 -1
  254. ksadk/server/static/assets/{dist-D99wf6_X.js → dist-C2xjE7cO.js} +1 -1
  255. ksadk/server/static/assets/{dist-DLMjL01k.js → dist-C7-3jInC.js} +1 -1
  256. ksadk/server/static/assets/{dist-DI-oVAXw.js → dist-C99THtIA.js} +1 -1
  257. ksadk/server/static/assets/{dist-BkJjNih3.js → dist-CGcp2ex8.js} +1 -1
  258. ksadk/server/static/assets/{dist-D5JGcxBx.js → dist-CVQRZjPu.js} +1 -1
  259. ksadk/server/static/assets/{dist-CWiTDN_V.js → dist-Cf23NY4L.js} +1 -1
  260. ksadk/server/static/assets/{dist-DLeM1OCK.js → dist-CgF3fj15.js} +1 -1
  261. ksadk/server/static/assets/{dist-BmuTWcm7.js → dist-Cwt7koer.js} +1 -1
  262. ksadk/server/static/assets/{dist-C2DKRFVK.js → dist-D82mtD2s.js} +1 -1
  263. ksadk/server/static/assets/{dist-B67uF2uK.js → dist-DF06ORQo.js} +1 -1
  264. ksadk/server/static/assets/{dist-Cwhk4lwI.js → dist-DHn6nBzr.js} +1 -1
  265. ksadk/server/static/assets/{dist-S4klkK5W.js → dist-Djgl2wVZ.js} +1 -1
  266. ksadk/server/static/assets/{dist-DCpZrjaK.js → dist-DlzCRy0S.js} +1 -1
  267. ksadk/server/static/assets/{dist-Cp1_jKwf.js → dist-DqNnNFrw.js} +1 -1
  268. ksadk/server/static/assets/{dist-6kSZvCZh.js → dist-DyvYLMW9.js} +1 -1
  269. ksadk/server/static/assets/{dist-DM0rEQPk.js → dist-eoW6tZXA.js} +1 -1
  270. ksadk/server/static/assets/{dist-Bk1l05Ni.js → dist-ivOya1LD.js} +1 -1
  271. ksadk/server/static/assets/{erDiagram-TEJ5UH35-oJhaci7S.js → erDiagram-TEJ5UH35-CnfRcY2M.js} +1 -1
  272. ksadk/server/static/assets/eventmodeling-FCH6USID-ChYO-ijM.js +1 -0
  273. ksadk/server/static/assets/{flowDiagram-I6XJVG4X-DtTmUan-.js → flowDiagram-I6XJVG4X-DGaJjGNJ.js} +1 -1
  274. ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BSsb_eij.js → ganttDiagram-6RSMTGT7-DpCoB2g9.js} +1 -1
  275. ksadk/server/static/assets/gitGraph-WXDBUCRP-BN_0Fk-j.js +1 -0
  276. ksadk/server/static/assets/{gitGraphDiagram-PVQCEYII-BR5s6Pws.js → gitGraphDiagram-PVQCEYII-9IKVFBVT.js} +1 -1
  277. ksadk/server/static/assets/index-QSwCHuB8.js +177 -0
  278. ksadk/server/static/assets/index-jOPmTOZH.css +2 -0
  279. ksadk/server/static/assets/info-J43DQDTF-Cp6o3vId.js +1 -0
  280. ksadk/server/static/assets/{infoDiagram-5YYISTIA-DYPc5cvh.js → infoDiagram-5YYISTIA-CYIIsb8y.js} +1 -1
  281. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-MoBdALDg.js → ishikawaDiagram-YF4QCWOH-DLOHGvWw.js} +1 -1
  282. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-DgLiQT8l.js → journeyDiagram-JHISSGLW-BIzKeejT.js} +1 -1
  283. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-CsFx112F.js → kanban-definition-UN3LZRKU-BWHoFSw2.js} +1 -1
  284. ksadk/server/static/assets/{katex-DRsiwpM2.js → katex-DATssjp1.js} +1 -1
  285. ksadk/server/static/assets/{linear-TNRQ5Orv.js → linear-CvMBw9cA.js} +1 -1
  286. ksadk/server/static/assets/{mermaid-parser.core-D2eMVOgo.js → mermaid-parser.core-mGAcEwcq.js} +2 -2
  287. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-BtByYFhL.js → mindmap-definition-RKZ34NQL-CSAQxEAe.js} +1 -1
  288. ksadk/server/static/assets/packet-YPE3B663-wQzct3cT.js +1 -0
  289. ksadk/server/static/assets/pie-LRSECV5Y-BgTQiv-9.js +1 -0
  290. ksadk/server/static/assets/{pieDiagram-4H26LBE5-DPnuCB8c.js → pieDiagram-4H26LBE5-By-ra07M.js} +1 -1
  291. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-miFpCPv4.js → quadrantDiagram-W4KKPZXB-CxIR4JXI.js} +1 -1
  292. ksadk/server/static/assets/radar-GUYGQ44K-BIq5fXsn.js +1 -0
  293. ksadk/server/static/assets/{requirementDiagram-4Y6WPE33-mjB_yYkW.js → requirementDiagram-4Y6WPE33-DpW90iu3.js} +1 -1
  294. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-D7hSlZZZ.js → sankeyDiagram-5OEKKPKP-DpGa6EAV.js} +1 -1
  295. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-D9Cx6hC3.js → sequenceDiagram-3UESZ5HK-NTKNGGe5.js} +1 -1
  296. ksadk/server/static/assets/{stateDiagram-AJRCARHV-D_zrg47o.js → stateDiagram-AJRCARHV-DGpd9yFG.js} +1 -1
  297. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-B3o579IQ.js +1 -0
  298. ksadk/server/static/assets/{timeline-definition-PNZ67QCA-CwSec0Wa.js → timeline-definition-PNZ67QCA-jqoYs3jQ.js} +1 -1
  299. ksadk/server/static/assets/treeView-BLDUP644-Dqi-E0EM.js +1 -0
  300. ksadk/server/static/assets/treemap-LRROVOQU-ClQP9CJ9.js +1 -0
  301. ksadk/server/static/assets/{vennDiagram-CIIHVFJN-agCVobiF.js → vennDiagram-CIIHVFJN-l3Z7-wbP.js} +1 -1
  302. ksadk/server/static/assets/wardley-L42UT6IY-8XLvnNIs.js +1 -0
  303. ksadk/server/static/assets/{wardleyDiagram-YWT4CUSO-TmrlhWNt.js → wardleyDiagram-YWT4CUSO-BR54GPmr.js} +1 -1
  304. ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-DV71V-ah.js → xychartDiagram-2RQKCTM6-SkiH8GsK.js} +1 -1
  305. ksadk/server/static/index.html +2 -2
  306. ksadk/server/terminal_sessions.py +111 -58
  307. ksadk/sessions/__init__.py +86 -40
  308. ksadk/sessions/base.py +31 -11
  309. ksadk/sessions/continuity.py +28 -5
  310. ksadk/sessions/in_memory.py +46 -2
  311. ksadk/sessions/local_service.py +148 -17
  312. ksadk/sessions/postgres_service.py +216 -35
  313. ksadk/sessions/resilience.py +52 -0
  314. ksadk/sessions/resilient.py +388 -0
  315. ksadk/skills/models.py +17 -5
  316. ksadk/skills/package_store.py +5 -2
  317. ksadk/skills/runtime/agent.py +20 -5
  318. ksadk/skills/runtime/artifacts.py +1 -5
  319. ksadk/skills/runtime/backends/e2b.py +10 -4
  320. ksadk/skills/runtime/backends/local.py +15 -3
  321. ksadk/skills/runtime/base.py +1 -2
  322. ksadk/skills/runtime/executor.py +10 -3
  323. ksadk/skills/runtime/factory.py +4 -2
  324. ksadk/skills/runtime/loader.py +1 -2
  325. ksadk/skills/runtime/registry.py +15 -10
  326. ksadk/skills/runtime/request.py +3 -1
  327. ksadk/skills/service_client.py +38 -17
  328. ksadk/skills/tool_defs.py +15 -10
  329. ksadk/terminal_exec_policy.py +3 -4
  330. ksadk/tools/gateway.py +92 -15
  331. ksadk/tools/result_budget.py +12 -4
  332. ksadk/toolsets/__init__.py +136 -36
  333. ksadk/toolsets/platform.py +21 -5
  334. ksadk/toolsets/sandbox.py +82 -16
  335. ksadk/toolsets/skills.py +200 -39
  336. ksadk/toolsets/web.py +123 -26
  337. ksadk/toolsets/workspace.py +264 -114
  338. ksadk/tracing/exporters/inmemory_exporter.py +62 -51
  339. ksadk/tracing/exporters/langfuse_exporter.py +91 -76
  340. ksadk/tracing/setup.py +132 -53
  341. ksadk/tracing/span_utils.py +5 -2
  342. ksadk/tui/__init__.py +22 -7
  343. ksadk/tui/loop.py +1925 -0
  344. ksadk/tui/stream_render.py +69 -0
  345. ksadk/ui_config.py +5 -3
  346. ksadk/version.py +1 -1
  347. ksadk_runtime_common/schemas/runtime_event_v1.json +95 -0
  348. agentengine_sdk_python-0.6.9.dist-info/RECORD +0 -487
  349. ksadk/server/static/assets/CodeBlock-BbI4HPtJ.js +0 -9
  350. ksadk/server/static/assets/MathMessageMarkdown-DMLQYoLp.js +0 -8
  351. ksadk/server/static/assets/MermaidBlock-DCR8WZ17.js +0 -303
  352. ksadk/server/static/assets/NativeTerminalPanel-CKUvWhUz.js +0 -1
  353. ksadk/server/static/assets/architecture-7EHR7CIX-Cr6Xz1_c.js +0 -1
  354. ksadk/server/static/assets/channel-BSSOwFIb.js +0 -1
  355. ksadk/server/static/assets/chunk-QZHKN3VN-XXpdiwUL.js +0 -1
  356. ksadk/server/static/assets/classDiagram-4FO5ZUOK-C5HvGiyK.js +0 -1
  357. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-C5HvGiyK.js +0 -1
  358. ksadk/server/static/assets/eventmodeling-FCH6USID-CIgwHJLL.js +0 -1
  359. ksadk/server/static/assets/gitGraph-WXDBUCRP-BVAJ7uo0.js +0 -1
  360. ksadk/server/static/assets/index-BH7-pC0B.js +0 -165
  361. ksadk/server/static/assets/index-DAURbCsP.css +0 -1
  362. ksadk/server/static/assets/info-J43DQDTF-MT80V4ge.js +0 -1
  363. ksadk/server/static/assets/packet-YPE3B663-BLKhbXZB.js +0 -1
  364. ksadk/server/static/assets/pie-LRSECV5Y-DFOikjSY.js +0 -1
  365. ksadk/server/static/assets/radar-GUYGQ44K-095b1Ikw.js +0 -1
  366. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-J9kCb0nC.js +0 -1
  367. ksadk/server/static/assets/treeView-BLDUP644-yinfJEY6.js +0 -1
  368. ksadk/server/static/assets/treemap-LRROVOQU-DosUqN1Q.js +0 -1
  369. ksadk/server/static/assets/wardley-L42UT6IY-Bvp7-5uH.js +0 -1
  370. ksadk/tui/app.py +0 -400
  371. ksadk/tui/clipboard.py +0 -100
  372. ksadk/tui/widgets/__init__.py +0 -22
  373. ksadk/tui/widgets/assistant.py +0 -89
  374. ksadk/tui/widgets/base.py +0 -54
  375. ksadk/tui/widgets/chat_input.py +0 -246
  376. ksadk/tui/widgets/history.py +0 -156
  377. ksadk/tui/widgets/system.py +0 -87
  378. ksadk/tui/widgets/thinking.py +0 -110
  379. ksadk/tui/widgets/tool_call.py +0 -161
  380. ksadk/tui/widgets/user.py +0 -37
  381. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/WHEEL +0 -0
  382. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/entry_points.txt +0 -0
  383. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/licenses/LICENSE +0 -0
  384. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.8.0.dist-info}/top_level.txt +0 -0
@@ -6,38 +6,37 @@ Serverless Provider - 金山云 Serverless 计算引擎 (AgentEngine 托管)
6
6
  - Deploy 阶段: 客户端调用 AgentEngine Server API 发起部署
7
7
  """
8
8
 
9
- import os
10
9
  import json
11
10
  import logging
12
- import shutil
11
+ import os
13
12
  import time
14
- import yaml
15
13
  from pathlib import Path
16
- from typing import Dict, Any, List, Optional
14
+ from typing import Any, Dict, List, Optional
15
+
17
16
  import click
17
+ import yaml
18
18
 
19
- from ksadk.builders.ks3_uploader import KS3Uploader
19
+ from ksadk.api import AgentEngineClient, DryRunExit
20
20
  from ksadk.builders.code_builder import CodeBuilder
21
+ from ksadk.builders.container_builder import (
22
+ ContainerBuilder,
23
+ registry_kind_label,
24
+ resolve_registry_credentials,
25
+ )
26
+ from ksadk.builders.ks3_uploader import KS3Uploader
27
+ from ksadk.configs.env_registry import ENV_VAR_REGISTRY
28
+ from ksadk.configs.global_config import get_env_from_global_config
29
+ from ksadk.configs.settings import DEFAULT_RUNTIME_TIMEZONE
21
30
  from ksadk.deployment.agent_access import get_latest_agent_access
22
31
  from ksadk.deployment.base import (
23
32
  BaseDeployProvider,
24
- DeployTarget,
25
33
  DeployResult,
26
34
  DeployStatus,
35
+ DeployTarget,
27
36
  PackageInfo,
28
37
  )
29
38
  from ksadk.deployment.registry import DeployProviderRegistry
30
39
  from ksadk.deployment.ui_config import resolve_ui_config, ui_config_to_state_fields
31
- from ksadk.builders.container_builder import (
32
- ContainerBuilder,
33
- registry_kind_label,
34
- resolve_registry_credentials,
35
- )
36
- from ksadk.configs.settings import DEFAULT_RUNTIME_TIMEZONE
37
- from ksadk.configs.env_registry import ENV_VAR_REGISTRY
38
- from ksadk.configs.global_config import get_env_from_global_config
39
- from ksadk.api import AgentEngineClient, DryRunExit
40
-
41
40
 
42
41
  logger = logging.getLogger(__name__)
43
42
 
@@ -71,11 +70,7 @@ _DEPLOY_PROCESS_ENV_ALLOWLIST = frozenset(
71
70
  )
72
71
  _DEPLOY_PROCESS_ENV_PREFIXES = ("KSADK_", "OPENAI_", "KSYUN_", "E2B_")
73
72
  _DEPLOY_PROCESS_ENV_DENYLIST = frozenset(
74
- {
75
- spec.name
76
- for spec in ENV_VAR_REGISTRY
77
- if spec.module in {"builders", "cli", "configs", "web"}
78
- }
73
+ {spec.name for spec in ENV_VAR_REGISTRY if spec.module in {"builders", "cli", "configs", "web"}}
79
74
  ) | frozenset(
80
75
  {
81
76
  "KSADK_GLOBAL_CONFIG_ENV_KEYS",
@@ -96,7 +91,7 @@ def _should_forward_process_env(name: str) -> bool:
96
91
  @DeployProviderRegistry.register("kce")
97
92
  class ServerlessProvider(BaseDeployProvider):
98
93
  """金山云 Serverless 计算引擎 (AgentEngine Server 托管)
99
-
94
+
100
95
  重构后直接继承 BaseDeployProvider,不再依赖 DockerProvider。
101
96
  Container 模式使用 ContainerBuilder 进行打包和构建。
102
97
  """
@@ -109,7 +104,7 @@ class ServerlessProvider(BaseDeployProvider):
109
104
  supports_scaling = True
110
105
  requires_image_registry = False
111
106
 
112
- def __init__(self, config: Dict[str, Any] = None):
107
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
113
108
  self.config = config or {}
114
109
 
115
110
  @staticmethod
@@ -124,13 +119,15 @@ class ServerlessProvider(BaseDeployProvider):
124
119
 
125
120
  if password and not username and registry_kind != "personal_kcr":
126
121
  click.secho(
127
- f" ⚠️ 检测到 KCR_PASSWORD 但缺少 KCR_USERNAME,已忽略{registry_kind_label(registry_kind)}镜像凭证;"
122
+ f" ⚠️ 检测到 KCR_PASSWORD 但缺少 KCR_USERNAME,"
123
+ f"已忽略{registry_kind_label(registry_kind)}镜像凭证;"
128
124
  "企业版 KCR 和第三方镜像仓库必须配置 KCR_USERNAME + KCR_PASSWORD",
129
125
  fg="yellow",
130
126
  )
131
127
  elif registry_kind == "personal_kcr":
132
128
  click.secho(
133
- " ⚠️ 未配置个人版 KCR 镜像凭证 (KSYUN_ACCOUNT_ID/KCR_PASSWORD),私有镜像可能无法拉取",
129
+ " ⚠️ 未配置个人版 KCR 镜像凭证 "
130
+ "(KSYUN_ACCOUNT_ID/KCR_PASSWORD),私有镜像可能无法拉取",
134
131
  fg="yellow",
135
132
  )
136
133
  else:
@@ -261,22 +258,32 @@ class ServerlessProvider(BaseDeployProvider):
261
258
  json.dump(payload, f, indent=2, ensure_ascii=False)
262
259
 
263
260
  @staticmethod
264
- def _serialize_network_config(target: DeployTarget) -> Optional[Dict[str, Any]]:
261
+ def _serialize_network_config(
262
+ target: DeployTarget, *, is_update: bool = False
263
+ ) -> Optional[Dict[str, Any]]:
265
264
  network = getattr(target, "network", None)
266
265
  if network is None:
267
266
  return None
268
267
 
269
- payload: Dict[str, Any] = {
270
- "enable_public_access": bool(getattr(network, "enable_public_access", False)),
271
- "enable_vpc_access": bool(getattr(network, "enable_vpc_access", False)),
272
- }
268
+ public_access = getattr(network, "enable_public_access", None)
269
+ # 三态:None=未指定。create 默认开公网;update 未指定则不发 network 字段。
270
+ if public_access is None:
271
+ public_value: Optional[bool] = None if is_update else True
272
+ else:
273
+ public_value = bool(public_access)
274
+
275
+ payload: Dict[str, Any] = {}
276
+ if public_value is not None:
277
+ payload["enable_public_access"] = public_value
278
+ if bool(getattr(network, "enable_vpc_access", False)):
279
+ payload["enable_vpc_access"] = True
273
280
 
274
281
  for field in ("vpc_id", "subnet_id", "security_group_id", "availability_zone"):
275
282
  value = str(getattr(network, field, "") or "").strip()
276
283
  if value:
277
284
  payload[field] = value
278
285
 
279
- if not payload["enable_public_access"] and not payload["enable_vpc_access"] and len(payload) == 2:
286
+ if not payload:
280
287
  return None
281
288
  return payload
282
289
 
@@ -307,19 +314,19 @@ class ServerlessProvider(BaseDeployProvider):
307
314
  seconds = int(elapsed % 60)
308
315
  return f"{minutes}m{seconds:02d}s"
309
316
 
310
-
311
317
  async def validate_config(self, target: DeployTarget) -> tuple[bool, str]:
312
318
  """验证配置: 确保已配置 AgentEngine Server"""
313
-
319
+
314
320
  server_url = os.getenv("AGENTENGINE_SERVER_URL")
315
-
321
+
316
322
  if not server_url:
317
323
  click.echo("⚠️ 未配置 AGENTENGINE_SERVER_URL,将尝试使用默认 Region 配置")
318
-
324
+
319
325
  # Container 模式: 检查 Docker 是否可用
320
326
  artifact_type = target.extra.get("artifact_type", "Code")
321
327
  if artifact_type == "Container":
322
328
  import subprocess
329
+
323
330
  try:
324
331
  result = subprocess.run(["docker", "version"], capture_output=True, timeout=10)
325
332
  if result.returncode != 0:
@@ -331,14 +338,19 @@ class ServerlessProvider(BaseDeployProvider):
331
338
 
332
339
  return True, ""
333
340
 
334
- async def package(self, project_dir: str, detection_result: Any, config: Dict[str, Any] = None) -> PackageInfo:
341
+ async def package(
342
+ self,
343
+ project_dir: str,
344
+ detection_result: Any,
345
+ config: Optional[Dict[str, Any]] = None,
346
+ ) -> PackageInfo:
335
347
  """打包项目
336
-
348
+
337
349
  Code 模式: 返回基本 PackageInfo,实际构建在 build() 中进行
338
350
  Container 模式: 使用 ContainerBuilder 打包
339
351
  """
340
352
  project_path = Path(project_dir)
341
-
353
+
342
354
  # 创建基本 PackageInfo
343
355
  package_info = PackageInfo(
344
356
  name=detection_result.name or project_path.name,
@@ -346,14 +358,15 @@ class ServerlessProvider(BaseDeployProvider):
346
358
  build_dir=str(project_path / ".agentengine" / "build"),
347
359
  project_dir=str(project_path),
348
360
  entry_point=detection_result.entry_point,
349
- metadata={}
361
+ metadata={},
350
362
  )
351
-
363
+
352
364
  # 尝试加载之前保存的构建元数据 (ks3_path 等)
353
365
  try:
354
366
  metadata_file = project_path / ".agentengine" / "build-metadata.json"
355
367
  if metadata_file.exists():
356
368
  import json
369
+
357
370
  with open(metadata_file, "r") as f:
358
371
  saved_data = json.load(f)
359
372
  if "metadata" in saved_data:
@@ -366,15 +379,32 @@ class ServerlessProvider(BaseDeployProvider):
366
379
  click.echo(f" 📦 已加载上次构建元数据: {count} 项")
367
380
  except Exception as e:
368
381
  click.secho(f"⚠️ 加载构建元数据失败: {e}", fg="yellow")
369
-
370
- return package_info
371
-
372
382
 
383
+ return package_info
373
384
 
374
385
  async def build(self, package_info: PackageInfo, target: DeployTarget) -> PackageInfo:
375
386
  """构建 & 上传 (客户端直传 KS3)"""
376
387
  artifact_type = target.extra.get("artifact_type", "Code")
377
388
 
389
+ if artifact_type == "ManagedRuntime":
390
+ # Declarative runtimes are delivered by Server -> Runtime Service as
391
+ # an inline manifest. Keep a local bundle only for inspection; do
392
+ # not acquire credentials or upload it to KS3.
393
+ from ksadk.builders.managed_runtime_builder import ManagedRuntimeBuilder
394
+
395
+ builder = ManagedRuntimeBuilder(
396
+ Path(package_info.project_dir),
397
+ config=target.extra.copy(),
398
+ runtime_version=str(target.extra.get("runtime_version") or ""),
399
+ )
400
+ build_result = builder.build()
401
+ if not build_result.success:
402
+ raise Exception(f"构建失败: {build_result.error_message}")
403
+ package_info.metadata.update(build_result.metadata)
404
+ if build_result.artifact_path is not None:
405
+ package_info.metadata["managed_manifest_path"] = str(build_result.artifact_path)
406
+ return package_info
407
+
378
408
  if artifact_type == "Code":
379
409
  # 1. 检查是否已有 KS3 路径
380
410
  # 优先级: CLI传入 > Metadata缓存 (仅当 !no_cache)
@@ -382,58 +412,73 @@ class ServerlessProvider(BaseDeployProvider):
382
412
  cached_ks3_path = package_info.metadata.get("ks3_path")
383
413
  no_cache = target.extra.get("no_cache", False)
384
414
  repackage = target.extra.get("repackage", False)
385
-
415
+
386
416
  # 如果显式传入了 ks3-path,直接使用
387
417
  if cli_ks3_path:
388
418
  package_info.metadata["ks3_path"] = cli_ks3_path
389
419
  return package_info
390
-
420
+
391
421
  # 如果没有 no_cache 且有缓存,才使用缓存
392
422
  if not no_cache and not repackage and cached_ks3_path:
393
423
  logger.info(f"Using cached bundle: {cached_ks3_path}")
394
424
  return package_info
395
425
 
396
- # 2. 构建 ZIP 包 (委托给 CodeBuilder)
397
-
426
+ # 2. 构建 ZIP 包
427
+
398
428
  # 传递配置,包括 no_cache
399
429
  builder_config = target.extra.copy()
400
430
  builder_config["no_cache"] = no_cache
401
431
  builder_config["repackage"] = repackage
402
-
403
- # 实例化 Builder
404
- # 注意: CodeBuilder 目前设计为直接操作 project_dir,
405
- # 这里传入原始 project_dir (package_info.project_dir)
406
- builder = CodeBuilder(Path(package_info.project_dir), config=builder_config)
407
-
432
+
433
+ if artifact_type == "ManagedRuntime":
434
+ from ksadk.builders.managed_runtime_builder import ManagedRuntimeBuilder
435
+
436
+ builder = ManagedRuntimeBuilder(
437
+ Path(package_info.project_dir),
438
+ config=builder_config,
439
+ runtime_version=str(target.extra.get("runtime_version") or ""),
440
+ )
441
+ else:
442
+ # CodeBuilder 直接操作原始 project_dir。
443
+ builder = CodeBuilder(Path(package_info.project_dir), config=builder_config)
444
+
408
445
  # 执行构建
409
446
  build_result = builder.build()
410
-
447
+
411
448
  if not build_result.success:
412
449
  raise Exception(f"构建失败: {build_result.error_message}")
413
-
450
+
414
451
  zip_path = build_result.artifact_path
452
+ if zip_path is None:
453
+ raise RuntimeError("代码构建成功但未生成 artifact_path")
454
+ package_info.metadata.update(build_result.metadata)
455
+ if build_result.metadata.get("manifest_sha256"):
456
+ target.extra["manifest_sha256"] = build_result.metadata["manifest_sha256"]
415
457
  # click.echo(f" ✅ ZIP 已生成: {zip_path}")
416
-
458
+
417
459
  # 3. 直接上传 KS3 (使用本地 AK/SK)
418
460
  # 3. 直接上传 KS3 (使用本地 AK/SK)
419
- click.echo("\n正在上传代码包到 KS3...")
461
+ artifact_label = "Runtime manifest" if artifact_type == "ManagedRuntime" else "代码包"
462
+ click.echo(f"\n正在上传{artifact_label}到 KS3...")
420
463
  upload_started_at = time.monotonic()
421
-
464
+
422
465
  ks3_bucket = target.extra.get("ks3_bucket")
423
466
  upload_region = "cn-beijing-6" if target.region == "pre-online" else target.region
424
-
467
+
425
468
  uploader = KS3Uploader(region=upload_region, bucket=ks3_bucket)
426
-
469
+
427
470
  # 使用时间戳确保每次上传的代码包路径唯一,支持真正的版本回滚
428
471
  from datetime import datetime
472
+
429
473
  timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
430
- object_key = f"agents/{package_info.name}/code_{timestamp}.zip"
431
-
474
+ object_prefix = "runtime" if artifact_type == "ManagedRuntime" else "code"
475
+ object_key = f"agents/{package_info.name}/{object_prefix}_{timestamp}.zip"
476
+
432
477
  ks3_path = await uploader.upload(zip_path, object_key)
433
-
478
+
434
479
  if not ks3_path:
435
480
  raise Exception("KS3 上传失败")
436
-
481
+
437
482
  logger.info(f"Upload success: {ks3_path}")
438
483
  click.echo(f" ✓ 上传耗时: {self._format_elapsed(upload_started_at)}")
439
484
  package_info.metadata["ks3_path"] = ks3_path
@@ -456,45 +501,47 @@ class ServerlessProvider(BaseDeployProvider):
456
501
  logger.info(f"Using cached image: {cached_image}")
457
502
  package_info.image = cached_image
458
503
  return package_info
459
-
460
- builder = ContainerBuilder(
504
+
505
+ container_builder = ContainerBuilder(
461
506
  project_dir=Path(package_info.project_dir),
462
507
  tag=tag,
463
508
  registry=registry,
464
- no_cache=no_cache
509
+ no_cache=no_cache,
465
510
  )
466
-
511
+
467
512
  # 构建镜像
468
- build_result = builder.build()
513
+ build_result = container_builder.build()
469
514
  if not build_result.success:
470
515
  raise Exception(f"镜像构建失败: {build_result.error_message}")
471
-
472
- image_name = build_result.metadata.get("image")
516
+
517
+ image_name = str(build_result.metadata.get("image") or "").strip()
518
+ if not image_name:
519
+ raise RuntimeError("容器构建成功但未生成 image")
473
520
  package_info.image = image_name
474
521
  package_info.metadata["image"] = image_name
475
-
522
+
476
523
  # 推送镜像
477
- if not builder.push(image_name):
524
+ if not container_builder.push(image_name):
478
525
  raise Exception("镜像推送失败")
479
-
526
+
480
527
  click.secho(f"✅ 镜像已推送: {image_name}", fg="green")
481
528
 
482
529
  self._persist_build_metadata(package_info)
483
-
530
+
484
531
  return package_info
485
532
 
486
533
  async def deploy(self, package_info: PackageInfo, target: DeployTarget) -> DeployResult:
487
534
  """通过 AgentEngine Server 部署
488
-
535
+
489
536
  逻辑:
490
537
  - 读取本地 .agentengine.state 文件获取 agent_id
491
538
  - 如果有 agent_id → 执行更新 (endpoint 不变)
492
539
  - 如果没有 → 创建新 Agent,保存 agent_id 到状态文件
493
540
  """
494
-
541
+
495
542
  server_url = os.getenv("AGENTENGINE_SERVER_URL")
496
543
  click.echo(f"\n🚀 开始部署到 Serverless (Managed by {server_url})...")
497
-
544
+
498
545
  # 读取本地状态文件
499
546
  project_dir = package_info.project_dir
500
547
  state_file = Path(project_dir) / ".agentengine.state"
@@ -517,26 +564,29 @@ class ServerlessProvider(BaseDeployProvider):
517
564
  ui_state["ui_bundle_path"] = project_config.get("ui_bundle_path")
518
565
  if project_config.get("ui_bundle_dir") and not ui_state.get("ui_bundle_dir"):
519
566
  ui_state["ui_bundle_dir"] = project_config.get("ui_bundle_dir")
520
-
567
+
521
568
  # 构造请求 payload
522
569
  artifact_type = target.extra.get("artifact_type", "Code")
523
570
  artifact_path = ""
524
- if artifact_type == "Code":
571
+ code_backed = artifact_type == "Code"
572
+ if code_backed:
525
573
  original_artifact_path = package_info.metadata.get("ks3_path", "")
526
574
  artifact_path = original_artifact_path
527
-
575
+
528
576
  # 转换为内网地址 (如果 serverless 无法解析 ks3://)
529
577
  if artifact_path.startswith("ks3://"):
530
578
  try:
531
579
  from ksadk.common.constants import get_ks3_endpoints
532
-
580
+
533
581
  # 确定 KS3 Region code (pre-online -> cn-beijing)
534
582
  ks3_region = target.extra.get("ks3_region")
535
583
  if not ks3_region:
536
- ks3_region = "cn-beijing-6" if target.region == "pre-online" else target.region
537
-
584
+ ks3_region = (
585
+ "cn-beijing-6" if target.region == "pre-online" else target.region
586
+ )
587
+
538
588
  _, internal_endpoint = get_ks3_endpoints(ks3_region)
539
-
589
+
540
590
  if internal_endpoint:
541
591
  # ks3://bucket/key -> http://bucket.internal_endpoint/key
542
592
  bucket, key = self._parse_code_artifact_path(artifact_path)
@@ -545,32 +595,39 @@ class ServerlessProvider(BaseDeployProvider):
545
595
  click.echo(f" Converted artifact path: {artifact_path}")
546
596
  except Exception as e:
547
597
  logger.warning(f"Failed to convert ks3 path to internal URL: {e}")
548
-
598
+
549
599
  if not artifact_path:
550
600
  raise ValueError(
551
- "❌ 未找到代码包路径 (ks3_path)。\n"
552
- " 在 Serverless 模式下,必须先上传代码包。\n"
553
- " 👉 请先执行: agentengine build --mode code --push\n"
601
+ "❌ 未找到 KS3 artifact 路径 (ks3_path)。\n"
602
+ " 在 Serverless 模式下,必须先上传构建产物。\n"
603
+ " 👉 请先执行: agentengine build --push\n"
554
604
  " 或者在 deploy 命令中使用 --ks3-path 指定路径。"
555
605
  )
556
606
 
557
- parsed_bucket, parsed_object_key = self._parse_code_artifact_path(original_artifact_path)
607
+ parsed_bucket, parsed_object_key = self._parse_code_artifact_path(
608
+ original_artifact_path
609
+ )
558
610
  if not parsed_bucket or not parsed_object_key:
559
611
  raise ValueError(
560
- "❌ ks3_path 格式无效,缺少代码包对象路径。\n"
612
+ "❌ ks3_path 格式无效,缺少 artifact 对象路径。\n"
561
613
  f" 当前值: {original_artifact_path or '(empty)'}\n"
562
614
  " 期望格式: ks3://<bucket>/<object-key>\n"
563
- " 👉 请重新执行: agentengine build --mode code --push\n"
615
+ " 👉 请重新执行: agentengine build --push\n"
564
616
  " 或者传入完整的 --ks3-path。"
565
617
  )
566
-
567
- else:
568
- artifact_path = package_info.image
569
-
618
+
619
+ elif artifact_type == "Container":
620
+ artifact_path = str(package_info.image or "").strip()
621
+ if not artifact_path:
622
+ raise ValueError("Container 部署缺少 image")
623
+ elif artifact_type != "ManagedRuntime":
624
+ raise ValueError(f"Unsupported artifact type: {artifact_type}")
625
+
570
626
  # 构建 KS3 凭证
571
627
  ks3_config = None
572
- if artifact_type == "Code":
628
+ if code_backed:
573
629
  from ksadk.common.auth import AWSV4Auth
630
+
574
631
  auth = AWSV4Auth() # 读取本地 AK/SK
575
632
  if auth.access_key_id and auth.secret_access_key:
576
633
  # 智能推断 Bucket 和 Region
@@ -579,7 +636,7 @@ class ServerlessProvider(BaseDeployProvider):
579
636
  bucket_name, _ = self._parse_code_artifact_path(
580
637
  package_info.metadata.get("ks3_path", "")
581
638
  )
582
-
639
+
583
640
  # 如果没有提供,尝试从 artifact_path 解析
584
641
  if not bucket_name:
585
642
  if artifact_path.startswith("ks3://"):
@@ -589,35 +646,38 @@ class ServerlessProvider(BaseDeployProvider):
589
646
  logger.info(f"Extracted bucket from ks3:// path: {bucket_name}")
590
647
  except IndexError:
591
648
  pass
592
- elif artifact_path.startswith("http") and "." in artifact_path:
649
+ elif artifact_path.startswith("http") and "." in artifact_path:
593
650
  try:
594
651
  # http://bucket.endpoint/key -> bucket
595
652
  from urllib.parse import urlparse
653
+
596
654
  parsed = urlparse(artifact_path)
597
655
  bucket_name = parsed.netloc.split(".")[0]
598
656
  logger.info(f"Extracted bucket from HTTP URL: {bucket_name}")
599
657
  except Exception as e:
600
658
  logger.warning(f"Failed to extract bucket from URL: {e}")
601
-
659
+
602
660
  # 如果仍然没有,使用智能默认值(与 KS3Uploader 逻辑一致)
603
661
  if not bucket_name:
604
662
  account_id = os.getenv("KSYUN_ACCOUNT_ID")
605
- upload_region = "cn-beijing-6" if target.region == "pre-online" else target.region
606
-
663
+ upload_region = (
664
+ "cn-beijing-6" if target.region == "pre-online" else target.region
665
+ )
666
+
607
667
  if not account_id:
608
668
  raise ValueError(
609
669
  "❌ 缺少 KSYUN_ACCOUNT_ID 环境变量\n"
610
670
  " Bucket 名称格式必须为: agentengine-{account_id}-{region}\n"
611
671
  " 请在 .env 文件中设置: KSYUN_ACCOUNT_ID=你的账号ID"
612
672
  )
613
-
673
+
614
674
  bucket_name = f"agentengine-{account_id}-{upload_region}"
615
675
  logger.info(f"Using default bucket: {bucket_name}")
616
-
676
+
617
677
  # Region 逻辑需与 Build 阶段保持一致 (pre-online -> cn-beijing-6)
618
678
  ks3_region = target.extra.get("ks3_region")
619
679
  if not ks3_region:
620
- ks3_region = "cn-beijing-6" if target.region == "pre-online" else target.region
680
+ ks3_region = "cn-beijing-6" if target.region == "pre-online" else target.region
621
681
 
622
682
  ks3_config = {
623
683
  "access_key": auth.access_key_id,
@@ -626,18 +686,31 @@ class ServerlessProvider(BaseDeployProvider):
626
686
  "bucket": bucket_name,
627
687
  }
628
688
  logger.info(f"📦 KS3 Config: bucket={bucket_name}, region={ks3_region}")
629
-
689
+
690
+ runtime_config = None
691
+ if artifact_type == "ManagedRuntime":
692
+ runtime_config = {
693
+ "name": str(target.extra.get("runtime_name") or "").strip(),
694
+ "version": str(target.extra.get("runtime_version") or "").strip(),
695
+ }
696
+ missing = [key for key, value in runtime_config.items() if not value]
697
+ if missing:
698
+ raise ValueError(
699
+ "ManagedRuntime 部署缺少 "
700
+ + ", ".join(f"runtime_config.{key}" for key in missing)
701
+ )
702
+
630
703
  try:
631
704
  # 获取 dry_run 标识
632
705
  is_dry_run = target.extra.get("dry_run", False)
633
706
 
634
707
  async with AgentEngineClient(region=target.region, dry_run=is_dry_run) as client:
635
708
  agent_exists = False
636
-
709
+
637
710
  if existing_agent_id:
638
711
  # 有本地状态 → 先检查服务器上是否存在
639
712
  click.echo(f" 检测到本地状态: {existing_agent_id}")
640
-
713
+
641
714
  try:
642
715
  # 尝试获取 agent,确认是否存在
643
716
  existing_agent = await client.get_agent(existing_agent_id)
@@ -647,32 +720,37 @@ class ServerlessProvider(BaseDeployProvider):
647
720
  # Agent 不存在或查询失败
648
721
  err_msg = str(e).lower()
649
722
  if "not found" in err_msg or "404" in err_msg or "不存在" in err_msg:
650
- click.secho(f" ⚠️ 服务器上未找到 Agent {existing_agent_id},将创建新 Agent", fg="yellow")
723
+ click.secho(
724
+ f" ⚠️ 服务器上未找到 Agent {existing_agent_id},将创建新 Agent",
725
+ fg="yellow",
726
+ )
651
727
  agent_exists = False
652
- # 如果是 DryRun 抛出的异常(表明请求本来会发出去但被拦截了),我们认为 Agent 可能存在也可能不存在
653
- # 但为了安全起见,在 DryRun 模式下我们假设它存在并走更新路径,或者简单地打印日志
728
+ # DryRun 异常表示真实请求被拦截,无法确认 Agent 是否存在。
729
+ # 为安全起见,DryRun 假设它存在并走更新路径。
654
730
  elif "Dry Run" in str(e):
655
- click.secho(f" [Dry Run] 假设 Agent {existing_agent_id} 存在", fg="cyan")
656
- agent_exists = True
731
+ click.secho(
732
+ f" [Dry Run] 假设 Agent {existing_agent_id} 存在", fg="cyan"
733
+ )
734
+ agent_exists = True
657
735
  else:
658
736
  # 其他错误,重新抛出
659
737
  raise
660
-
738
+
661
739
  if agent_exists:
662
740
  # Agent 存在 → 执行更新
663
- click.echo(f" 执行热更新 (endpoint 保持不变)...")
664
-
741
+ click.echo(" 执行热更新 (endpoint 保持不变)...")
742
+
665
743
  update_data = {
666
744
  "artifact_type": artifact_type,
667
745
  "artifact_path": artifact_path,
668
746
  "resources": {
669
747
  "cpu": target.resources.cpu,
670
- "memory": target.resources.memory
748
+ "memory": target.resources.memory,
671
749
  },
672
750
  "scaling": {
673
751
  "min_replicas": target.scaling.min_replicas,
674
752
  "max_replicas": target.scaling.max_replicas,
675
- "concurrency": target.scaling.concurrency
753
+ "concurrency": target.scaling.concurrency,
676
754
  },
677
755
  "observability": {
678
756
  "langfuse_enabled": target.extra.get("enable_observability", True)
@@ -683,16 +761,20 @@ class ServerlessProvider(BaseDeployProvider):
683
761
  "url": resolved_ui.url,
684
762
  },
685
763
  }
686
-
764
+ if runtime_config:
765
+ update_data["runtime_config"] = runtime_config
766
+
687
767
  if ks3_config:
688
768
  update_data["ks3"] = ks3_config
689
769
  elif artifact_type == "Container":
690
770
  image_credential = self._image_credential_from_env(artifact_path)
691
771
  if image_credential:
692
772
  update_data["image_credential"] = image_credential
693
- click.echo(
694
- f" 🔑 镜像凭证: {image_credential['username']}@{image_credential['endpoint']}"
773
+ credential_target = (
774
+ f"{image_credential['username']}@"
775
+ f"{image_credential['endpoint']}"
695
776
  )
777
+ click.echo(f" 🔑 镜像凭证: {credential_target}")
696
778
 
697
779
  # 加载全局配置 + 本地 .env,并通过部署参数注入到运行时环境变量。
698
780
  env_vars, env_file_exists, project_env_count = self._load_deploy_env_vars(
@@ -710,24 +792,26 @@ class ServerlessProvider(BaseDeployProvider):
710
792
  else:
711
793
  click.echo(f" 📦 更新环境变量: {len(env_vars)} 项 from 全局配置")
712
794
 
713
- network_config = self._serialize_network_config(target)
795
+ network_config = self._serialize_network_config(target, is_update=True)
714
796
  if network_config:
715
797
  update_data["network"] = network_config
716
798
 
717
799
  storage_config = self._serialize_storage_config(target)
718
800
  if storage_config:
719
801
  update_data["storage"] = storage_config
720
-
802
+
721
803
  # 注入更新时间戳,强制触发 Rolling Update (Pod 重启)
722
804
  if "env_vars" not in update_data:
723
805
  update_data["env_vars"] = {}
724
-
806
+
725
807
  import time
808
+
726
809
  update_data["env_vars"]["KSADK_UPDATED_AT"] = str(int(time.time()))
727
-
810
+
728
811
  # DEBUG: 打印 Payload 确认 trigger 是否存在
729
- click.echo(f" 🔄 更新 Trigger: KSADK_UPDATED_AT={update_data['env_vars']['KSADK_UPDATED_AT']}")
730
-
812
+ updated_at_value = update_data["env_vars"]["KSADK_UPDATED_AT"]
813
+ click.echo(f" 🔄 更新 Trigger: KSADK_UPDATED_AT={updated_at_value}")
814
+
731
815
  res = await client.update_agent(existing_agent_id, update_data)
732
816
  latest_access = {}
733
817
  if not is_dry_run:
@@ -741,38 +825,40 @@ class ServerlessProvider(BaseDeployProvider):
741
825
  exc,
742
826
  ),
743
827
  )
744
-
828
+
745
829
  # 如果是 Dry Run,手动构造假响应以避免崩溃
746
830
  if is_dry_run and not res:
747
831
  res = {"name": package_info.name, "endpoint": "http://dry-run-endpoint"}
748
-
832
+
749
833
  # 更新本地状态 (保留旧字段如 api_key)
750
834
  new_state = local_state.copy()
751
- new_state.update({
752
- "agent_id": latest_access.get("agent_id") or existing_agent_id,
753
- "name": latest_access.get("name") or res.get("name"),
754
- "region": target.region,
755
- "endpoint": latest_access.get("endpoint") or res.get("endpoint"),
756
- "updated_at": self._now_iso(),
757
- **ui_state,
758
- })
835
+ new_state.update(
836
+ {
837
+ "agent_id": latest_access.get("agent_id") or existing_agent_id,
838
+ "name": latest_access.get("name") or res.get("name"),
839
+ "region": target.region,
840
+ "endpoint": latest_access.get("endpoint") or res.get("endpoint"),
841
+ "updated_at": self._now_iso(),
842
+ **ui_state,
843
+ }
844
+ )
759
845
  if latest_access.get("api_key"):
760
846
  new_state["api_key"] = latest_access["api_key"]
761
847
  self._save_state(state_file, new_state)
762
-
848
+
763
849
  return DeployResult(
764
- status=DeployStatus.DEPLOYING,
850
+ status=DeployStatus.DEPLOYING,
765
851
  agent_id=existing_agent_id,
766
852
  agent_name=res.get("name"),
767
853
  endpoint=res.get("endpoint"),
768
- message=f"✅ Agent 已更新: {existing_agent_id}"
854
+ message=f"✅ Agent 已更新: {existing_agent_id}",
769
855
  )
770
856
  # else: agent_exists = False,继续执行创建逻辑
771
-
857
+
772
858
  # 没有本地状态,或本地状态对应的 Agent 在服务器上不存在 → 创建新 Agent
773
859
  if not existing_agent_id or not agent_exists:
774
860
  click.echo(f" 创建新 Agent: {package_info.name}")
775
-
861
+
776
862
  request_data = {
777
863
  "name": package_info.name,
778
864
  "framework": package_info.framework,
@@ -782,12 +868,12 @@ class ServerlessProvider(BaseDeployProvider):
782
868
  "instance_id": target.extra.get("instance_id", "default"),
783
869
  "resources": {
784
870
  "cpu": target.resources.cpu,
785
- "memory": target.resources.memory
871
+ "memory": target.resources.memory,
786
872
  },
787
873
  "scaling": {
788
874
  "min_replicas": target.scaling.min_replicas,
789
875
  "max_replicas": target.scaling.max_replicas,
790
- "concurrency": target.scaling.concurrency
876
+ "concurrency": target.scaling.concurrency,
791
877
  },
792
878
  "observability": {
793
879
  "langfuse_enabled": target.extra.get("enable_observability", True)
@@ -798,7 +884,9 @@ class ServerlessProvider(BaseDeployProvider):
798
884
  "url": resolved_ui.url,
799
885
  },
800
886
  }
801
-
887
+ if runtime_config:
888
+ request_data["runtime_config"] = runtime_config
889
+
802
890
  if ks3_config:
803
891
  request_data["ks3"] = ks3_config
804
892
 
@@ -807,9 +895,10 @@ class ServerlessProvider(BaseDeployProvider):
807
895
  image_credential = self._image_credential_from_env(artifact_path)
808
896
  if image_credential:
809
897
  request_data["image_credential"] = image_credential
810
- click.echo(
811
- f" 🔑 镜像凭证: {image_credential['username']}@{image_credential['endpoint']}"
898
+ credential_target = (
899
+ f"{image_credential['username']}@" f"{image_credential['endpoint']}"
812
900
  )
901
+ click.echo(f" 🔑 镜像凭证: {credential_target}")
813
902
 
814
903
  # 加载全局配置 + 本地 .env,并通过部署参数注入到运行时环境变量。
815
904
  env_vars, env_file_exists, project_env_count = self._load_deploy_env_vars(
@@ -827,9 +916,9 @@ class ServerlessProvider(BaseDeployProvider):
827
916
  click.echo(f" 📦 加载环境变量: {len(env_vars)} 项 from 全局配置")
828
917
 
829
918
  if env_vars:
830
- request_data["env_vars"] = env_vars
919
+ request_data["env_vars"] = env_vars
831
920
 
832
- network_config = self._serialize_network_config(target)
921
+ network_config = self._serialize_network_config(target, is_update=False)
833
922
  if network_config:
834
923
  request_data["network"] = network_config
835
924
 
@@ -842,20 +931,22 @@ class ServerlessProvider(BaseDeployProvider):
842
931
  ksyun_account_id = os.getenv("KSYUN_ACCOUNT_ID")
843
932
  if ksyun_account_id:
844
933
  extra_headers["X-Ksc-Account-Id"] = ksyun_account_id
845
-
934
+
846
935
  # 重新构造一个带 Header 的 client
847
- async with AgentEngineClient(region=target.region, extra_headers=extra_headers, dry_run=is_dry_run) as new_client:
936
+ async with AgentEngineClient(
937
+ region=target.region, extra_headers=extra_headers, dry_run=is_dry_run
938
+ ) as new_client:
848
939
  res = await new_client.create_agent(request_data)
849
-
940
+
850
941
  # 如果是 Dry Run,手动构造假响应
851
942
  if is_dry_run and not res:
852
943
  res = {
853
- "agent_id": "dry-run-agent-id",
854
- "name": package_info.name,
855
- "endpoint": "http://dry-run-endpoint",
856
- "api_key": "dry-run-key"
944
+ "agent_id": "dry-run-agent-id",
945
+ "name": package_info.name,
946
+ "endpoint": "http://dry-run-endpoint",
947
+ "api_key": "dry-run-key",
857
948
  }
858
-
949
+
859
950
  # CreateAgentProduct 返回 order_id,需要轮询 list_agents 获取 agent_id
860
951
  new_agent_id = res.get("agent_id")
861
952
  order_id = res.get("order_id")
@@ -866,10 +957,13 @@ class ServerlessProvider(BaseDeployProvider):
866
957
  if order_id and not new_agent_id:
867
958
  click.echo(f" 📋 订单已创建: {order_id},等待实例创建...")
868
959
  import time
960
+
869
961
  for i in range(12): # 最多等 60s
870
962
  time.sleep(5)
871
963
  try:
872
- detail = await client.get_agent(name=package_info.name, include_api_key=True)
964
+ detail = await client.get_agent(
965
+ name=package_info.name, include_api_key=True
966
+ )
873
967
  qa = detail.get("quick_access", {})
874
968
  basic = detail.get("basic", {})
875
969
  new_agent_id = basic.get("agent_id")
@@ -884,7 +978,10 @@ class ServerlessProvider(BaseDeployProvider):
884
978
  click.echo(f" ⏳ 等待中... ({(i+1)*5}s)")
885
979
 
886
980
  if not new_agent_id:
887
- click.secho(" ⚠️ 实例仍在创建中,稍后使用 'agentengine status' 查看", fg="yellow")
981
+ click.secho(
982
+ " ⚠️ 实例仍在创建中,稍后使用 'agentengine status' 查看",
983
+ fg="yellow",
984
+ )
888
985
 
889
986
  latest_access = {}
890
987
  if new_agent_id and not is_dry_run:
@@ -903,54 +1000,63 @@ class ServerlessProvider(BaseDeployProvider):
903
1000
  agent_name = latest_access.get("name") or agent_name
904
1001
  agent_endpoint = latest_access.get("endpoint") or agent_endpoint
905
1002
  agent_api_key = latest_access.get("api_key") or agent_api_key
906
-
1003
+
907
1004
  # 保存 agent_id 到本地状态文件
908
- self._save_state(state_file, {
909
- "agent_id": new_agent_id,
910
- "name": agent_name,
911
- "region": target.region,
912
- "endpoint": agent_endpoint,
913
- "api_key": agent_api_key, # 只在首次保存
914
- "order_id": order_id,
915
- "created_at": self._now_iso(),
916
- **ui_state,
917
- })
918
-
919
- click.echo(f" 💾 已保存状态到 .agentengine.state")
920
-
1005
+ self._save_state(
1006
+ state_file,
1007
+ {
1008
+ "agent_id": new_agent_id,
1009
+ "name": agent_name,
1010
+ "region": target.region,
1011
+ "endpoint": agent_endpoint,
1012
+ "api_key": agent_api_key, # 只在首次保存
1013
+ "order_id": order_id,
1014
+ "created_at": self._now_iso(),
1015
+ **ui_state,
1016
+ },
1017
+ )
1018
+
1019
+ click.echo(" 💾 已保存状态到 .agentengine.state")
1020
+
921
1021
  return DeployResult(
922
- status=DeployStatus.DEPLOYING,
1022
+ status=DeployStatus.DEPLOYING,
923
1023
  agent_id=new_agent_id,
924
1024
  agent_name=agent_name,
925
- endpoint=agent_endpoint,
1025
+ endpoint=agent_endpoint,
926
1026
  api_key=agent_api_key,
927
- message=f"✅ Agent ID: {new_agent_id or '(创建中)'} (首次部署, 订单: {order_id or '-'})"
1027
+ message=(
1028
+ f"✅ Agent ID: {new_agent_id or '(创建中)'} "
1029
+ f"(首次部署, 订单: {order_id or '-'})"
1030
+ ),
928
1031
  )
929
1032
 
1033
+ return DeployResult(
1034
+ status=DeployStatus.FAILED,
1035
+ message="部署流程未返回 Agent 状态",
1036
+ )
1037
+
930
1038
  except DryRunExit as e:
931
1039
  return DeployResult(
932
1040
  status=DeployStatus.SKIPPED,
933
1041
  message="✅ Dry Run Completed: 请求已打印,未执行实际变更。",
934
1042
  metadata={"dry_run_request": e.payload or {}},
935
1043
  )
936
-
1044
+
937
1045
  except Exception as e:
938
1046
  logger.error(f"Deploy failed: {e}")
939
-
1047
+
940
1048
  # 检测名称冲突
941
1049
  err_msg = str(e)
942
1050
  if "Conflict" in err_msg or "409" in err_msg or "already exists" in err_msg:
943
1051
  return DeployResult(
944
1052
  status=DeployStatus.FAILED,
945
1053
  message=f"❌ 部署失败: Agent 名称 '{package_info.name}' 已存在。\n"
946
- f" 提示: 请检查是否重复创建。\n"
947
- f" 👉 解决方法: 请在 agentengine.yaml 中修改 'name' 字段 (如添加后缀) 后重试。"
1054
+ f" 提示: 请检查是否重复创建。\n"
1055
+ " 👉 解决方法: 请在 agentengine.yaml 中修改 'name' 字段 "
1056
+ "(如添加后缀) 后重试。",
948
1057
  )
949
-
950
- return DeployResult(
951
- status=DeployStatus.FAILED,
952
- message=f"Server 请求失败: {str(e)}"
953
- )
1058
+
1059
+ return DeployResult(status=DeployStatus.FAILED, message=f"Server 请求失败: {str(e)}")
954
1060
 
955
1061
  async def get_status(self, agent_id: str, target: DeployTarget) -> DeployResult:
956
1062
  """获取 Agent 状态"""
@@ -958,7 +1064,7 @@ class ServerlessProvider(BaseDeployProvider):
958
1064
  try:
959
1065
  async with AgentEngineClient(region=target.region, dry_run=dry_run) as client:
960
1066
  res = await client.get_agent(agent_id)
961
-
1067
+
962
1068
  status_map = {
963
1069
  "running": DeployStatus.RUNNING,
964
1070
  "ready": DeployStatus.RUNNING,
@@ -968,9 +1074,9 @@ class ServerlessProvider(BaseDeployProvider):
968
1074
  "scaling": DeployStatus.UPDATING,
969
1075
  "failed": DeployStatus.FAILED,
970
1076
  "error": DeployStatus.FAILED,
971
- "unknown": DeployStatus.UNKNOWN
1077
+ "unknown": DeployStatus.UNKNOWN,
972
1078
  }
973
-
1079
+
974
1080
  # 使状态匹配不区分大小写,以兼容服务端的改动 (Creating -> CREATING)
975
1081
  current_status = (res.get("status") or "").lower()
976
1082
  return DeployResult(
@@ -978,21 +1084,20 @@ class ServerlessProvider(BaseDeployProvider):
978
1084
  agent_id=res.get("agent_id"),
979
1085
  agent_name=res.get("name"),
980
1086
  endpoint=res.get("endpoint"),
981
- message=f"Status: {res.get('status')} ({res.get('phase')})"
1087
+ message=f"Status: {res.get('status')} ({res.get('phase')})",
982
1088
  )
983
1089
  except DryRunExit:
984
1090
  return DeployResult(status=DeployStatus.SKIPPED, message="Dry Run executed.")
985
1091
  except Exception as e:
986
- return DeployResult(
987
- status=DeployStatus.UNKNOWN,
988
- message=f"查询失败: {e}"
989
- )
1092
+ return DeployResult(status=DeployStatus.UNKNOWN, message=f"查询失败: {e}")
990
1093
 
991
1094
  async def destroy(self, agent_id: str, target: DeployTarget) -> bool:
992
1095
  """销毁 Agent"""
993
1096
  dry_run = target.extra.get("dry_run", False)
994
1097
  project_dir = str(target.extra.get("project_dir") or "").strip()
995
- state_file = (Path(project_dir).resolve() if project_dir else Path(".").resolve()) / ".agentengine.state"
1098
+ state_file = (
1099
+ Path(project_dir).resolve() if project_dir else Path(".").resolve()
1100
+ ) / ".agentengine.state"
996
1101
 
997
1102
  try:
998
1103
  async with AgentEngineClient(region=target.region, dry_run=dry_run) as client:
@@ -1005,8 +1110,10 @@ class ServerlessProvider(BaseDeployProvider):
1005
1110
  os.remove(state_file)
1006
1111
  logger.info(f"Deleted local state file: {state_file}")
1007
1112
  except Exception as cleanup_error:
1008
- logger.warning(f"Failed to delete local state file {state_file}: {cleanup_error}")
1009
- return success
1113
+ logger.warning(
1114
+ f"Failed to delete local state file {state_file}: {cleanup_error}"
1115
+ )
1116
+ return bool(success)
1010
1117
  except DryRunExit:
1011
1118
  # 让异常冒泡给 CLI 处理
1012
1119
  raise
@@ -1020,19 +1127,27 @@ class ServerlessProvider(BaseDeployProvider):
1020
1127
  try:
1021
1128
  async with AgentEngineClient(region=target.region, dry_run=dry_run) as client:
1022
1129
  res = await client.list_agents()
1023
-
1130
+
1024
1131
  results = []
1025
1132
  for agent in res.get("Agents", []):
1026
1133
  current_status = (agent.get("status") or "").lower()
1027
- results.append(DeployResult(
1028
- status=DeployStatus.RUNNING if current_status in ("running", "ready") else (
1029
- DeployStatus.DEPLOYING if current_status == "creating" else DeployStatus.UNKNOWN
1030
- ),
1031
- agent_id=agent.get("agent_id"),
1032
- agent_name=agent.get("name"),
1033
- endpoint=agent.get("endpoint"),
1034
- message=agent.get("status")
1035
- ))
1134
+ results.append(
1135
+ DeployResult(
1136
+ status=(
1137
+ DeployStatus.RUNNING
1138
+ if current_status in ("running", "ready")
1139
+ else (
1140
+ DeployStatus.DEPLOYING
1141
+ if current_status == "creating"
1142
+ else DeployStatus.UNKNOWN
1143
+ )
1144
+ ),
1145
+ agent_id=agent.get("agent_id"),
1146
+ agent_name=agent.get("name"),
1147
+ endpoint=agent.get("endpoint"),
1148
+ message=agent.get("status"),
1149
+ )
1150
+ )
1036
1151
  return results
1037
1152
  except DryRunExit:
1038
1153
  raise
@@ -1046,7 +1161,7 @@ class ServerlessProvider(BaseDeployProvider):
1046
1161
  try:
1047
1162
  async with AgentEngineClient(region=target.region, dry_run=dry_run) as client:
1048
1163
  response = await client.chat(agent_id, message, stream=False)
1049
- return response.get("output", "")
1164
+ return str(response.get("output", ""))
1050
1165
  except DryRunExit:
1051
1166
  raise
1052
1167
  except Exception as e:
@@ -1056,7 +1171,7 @@ class ServerlessProvider(BaseDeployProvider):
1056
1171
  """读取本地状态文件"""
1057
1172
  if state_file.exists():
1058
1173
  try:
1059
- with open(state_file, 'r', encoding='utf-8') as f:
1174
+ with open(state_file, "r", encoding="utf-8") as f:
1060
1175
  return yaml.safe_load(f) or {}
1061
1176
  except Exception as e:
1062
1177
  logger.warning(f"Failed to load state file: {e}")
@@ -1092,8 +1207,9 @@ class ServerlessProvider(BaseDeployProvider):
1092
1207
  def _save_state(self, state_file: Path, state: Dict[str, Any]) -> None:
1093
1208
  """保存状态到本地文件"""
1094
1209
  import yaml
1210
+
1095
1211
  try:
1096
- with open(state_file, 'w', encoding='utf-8') as f:
1212
+ with open(state_file, "w", encoding="utf-8") as f:
1097
1213
  yaml.dump(state, f, default_flow_style=False, allow_unicode=True)
1098
1214
  logger.debug(f"State saved to {state_file}")
1099
1215
  except Exception as e:
@@ -1102,4 +1218,5 @@ class ServerlessProvider(BaseDeployProvider):
1102
1218
  def _now_iso(self) -> str:
1103
1219
  """返回当前时间 ISO 格式"""
1104
1220
  from datetime import datetime
1221
+
1105
1222
  return datetime.now().isoformat()