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
ksadk/toolsets/a2a.py ADDED
@@ -0,0 +1,426 @@
1
+ """A2A toolset: 把 A2A Space 下的远程 agent 暴露为固定几个元 tool(对齐 skill 模式)。
2
+
3
+ 不随 agent 数量增长:固定 3 个 tool —— list_a2a_agents / get_a2a_agent_card /
4
+ call_a2a_agent。LLM 操作哪个远程 agent 通过参数动态指定。
5
+
6
+ 容错(参考 ksadk/toolsets/skills.py 并加强误传容错):
7
+ - agent 参数同时接受 A2AAgentId 或 AgentCard.name,内部归一化(strip/lower、
8
+ 容忍 a2a-agent- 前缀有无);
9
+ - 找不到时返回 available_agents + difflib 模糊建议;
10
+ - 统一 {ok, error_type, error_message} 信封,永不抛异常;
11
+ - 未绑 A2A Space 时挂载 0 个 tool(空值守卫)。
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import difflib
17
+ import ipaddress
18
+ import os
19
+ import socket
20
+ import uuid
21
+ from typing import Any
22
+ from urllib.parse import urlsplit
23
+
24
+ import httpcore
25
+ import httpx
26
+
27
+ from ksadk.tools.gateway import ToolPolicy
28
+ from ksadk.toolsets._langchain import as_tool
29
+
30
+ ENV_A2A_SPACE_ID = "KSADK_A2A_SPACE_ID"
31
+ ENV_A2A_SPACE_IDS = "KSADK_A2A_SPACE_IDS"
32
+
33
+
34
+ def _a2a_space_id() -> str:
35
+ explicit = (os.getenv(ENV_A2A_SPACE_ID) or "").strip()
36
+ if explicit:
37
+ return explicit
38
+ raw = (os.getenv(ENV_A2A_SPACE_IDS) or "").strip()
39
+ if not raw:
40
+ return ""
41
+ try:
42
+ import json
43
+
44
+ ids = json.loads(raw)
45
+ if isinstance(ids, list) and ids and isinstance(ids[0], str):
46
+ return ids[0].strip()
47
+ except (ValueError, IndexError):
48
+ pass
49
+ return ""
50
+
51
+
52
+ def _normalize_agent_ref(value: str) -> str:
53
+ return (value or "").strip().lower().removeprefix("a2a-agent-")
54
+
55
+
56
+ def _discover_agents() -> list[dict[str, Any]]:
57
+ """同步发现 space 下的可用 A2A agent(经 KOP ListAToASpaceAgents)。
58
+
59
+ 用同步 KOPClient,不用 async A2ASpaceClient——这样 tool 可在 langgraph 的
60
+ async node 里安全调用(避免 sync 工具内部 asyncio.run 与运行中 event loop 冲突,
61
+ 也保证 OTel traceparent contextvar 沿 event loop 传递不断链)。
62
+ """
63
+ from ksadk.common.kop_client import KOPClient
64
+
65
+ space_id = _a2a_space_id()
66
+ if not space_id:
67
+ return []
68
+ try:
69
+ kop = KOPClient()
70
+ data = kop.post_action(
71
+ "ListAToASpaceAgents",
72
+ {"A2ASpaceId": space_id, "Status": "available", "PageSize": 100},
73
+ )
74
+ except Exception:
75
+ return []
76
+ result = []
77
+ for item in data.get("Agents") or []:
78
+ if not isinstance(item, dict):
79
+ continue
80
+ if str(item.get("InvocationStatus") or "") not in {"", "available"}:
81
+ continue
82
+ card = item.get("AgentCard") if isinstance(item.get("AgentCard"), dict) else {}
83
+ ifaces = card.get("supportedInterfaces") or []
84
+ card_url = str(ifaces[0].get("url")) if ifaces and isinstance(ifaces[0], dict) else ""
85
+ result.append(
86
+ {
87
+ "agent_id": str(item.get("A2AAgentId") or ""),
88
+ "name": str(card.get("name") or item.get("Name") or ""),
89
+ "description": str(card.get("description") or item.get("Description") or ""),
90
+ "version_id": str(item.get("VersionId") or item.get("LatestVersionId") or ""),
91
+ "card_sha256": str(item.get("CardSha256") or ""),
92
+ "source": str(item.get("Source") or "hosted"),
93
+ "url": card_url,
94
+ }
95
+ )
96
+ return result
97
+
98
+
99
+ def _match_agent(agents: list[dict[str, Any]], ref: str) -> tuple[dict[str, Any] | None, list[str]]:
100
+ target = _normalize_agent_ref(ref)
101
+ if not target:
102
+ return None, [a["agent_id"] for a in agents]
103
+ for a in agents:
104
+ if _normalize_agent_ref(a["agent_id"]) == target or _normalize_agent_ref(
105
+ a["name"]
106
+ ) == target:
107
+ return a, []
108
+ available = [a["agent_id"] for a in agents]
109
+ names = [_normalize_agent_ref(a["agent_id"]) for a in agents] + [
110
+ _normalize_agent_ref(a["name"]) for a in agents if a["name"]
111
+ ]
112
+ suggestions = difflib.get_close_matches(target, names, n=1, cutoff=0.6)
113
+ return None, available + (suggestions or [])
114
+
115
+
116
+ def _task_state_name(remote_task: Any) -> str:
117
+ status = getattr(remote_task, "status", None)
118
+ state = getattr(status, "state", None)
119
+ return str(state) if state is not None else ""
120
+
121
+
122
+ def _extract_reply_text(remote_task: Any) -> str:
123
+ """从 task 的 status.message.parts 与 artifacts 提取回复文本。
124
+
125
+ 兼容 proto Task 对象(属性访问)与 JSON dict(键访问)。
126
+ """
127
+ if isinstance(remote_task, dict):
128
+ status = remote_task.get("status") or {}
129
+ message = status.get("message") or {}
130
+ parts: list[str] = []
131
+ # 优先 status.message.parts(终态回复);无则退到 artifacts 分片。
132
+ for part in (message.get("parts") or []):
133
+ if isinstance(part, dict) and part.get("text"):
134
+ parts.append(part["text"])
135
+ if parts:
136
+ return "".join(parts)
137
+ for artifact in (remote_task.get("artifacts") or []):
138
+ for part in (artifact.get("parts") or []):
139
+ if isinstance(part, dict) and part.get("text"):
140
+ parts.append(part["text"])
141
+ return "".join(parts)
142
+ parts_text: list[str] = []
143
+ status = getattr(remote_task, "status", None)
144
+ message = getattr(status, "message", None)
145
+ if message is not None:
146
+ for part in (getattr(message, "parts", None) or []):
147
+ text = getattr(part, "text", None)
148
+ if isinstance(text, str) and text:
149
+ parts_text.append(text)
150
+ for artifact in (getattr(remote_task, "artifacts", None) or []):
151
+ for part in (getattr(artifact, "parts", None) or []):
152
+ text = getattr(part, "text", None)
153
+ if isinstance(text, str) and text:
154
+ parts_text.append(text)
155
+ return "".join(parts_text)
156
+
157
+
158
+ class _PinnedDNSNetworkBackend(httpcore.NetworkBackend):
159
+ """Dial the address selected by the URL validator while preserving TLS SNI."""
160
+
161
+ def __init__(
162
+ self,
163
+ delegate: httpcore.NetworkBackend,
164
+ *,
165
+ expected_hostname: str,
166
+ expected_port: int,
167
+ pinned_ip: str,
168
+ ) -> None:
169
+ self._delegate = delegate
170
+ self._expected_hostname = expected_hostname
171
+ self._expected_port = expected_port
172
+ self._pinned_ip = pinned_ip
173
+
174
+ def connect_tcp(
175
+ self,
176
+ host: str,
177
+ port: int,
178
+ timeout: float | None = None,
179
+ local_address: str | None = None,
180
+ socket_options: Any = None,
181
+ ) -> httpcore.NetworkStream:
182
+ if host.lower() != self._expected_hostname or port != self._expected_port:
183
+ raise RuntimeError("A2A tool transport attempted to dial an unapproved origin")
184
+ return self._delegate.connect_tcp(
185
+ self._pinned_ip,
186
+ port,
187
+ timeout=timeout,
188
+ local_address=local_address,
189
+ socket_options=socket_options,
190
+ )
191
+
192
+ def connect_unix_socket(
193
+ self,
194
+ path: str,
195
+ timeout: float | None = None,
196
+ socket_options: Any = None,
197
+ ) -> httpcore.NetworkStream:
198
+ return self._delegate.connect_unix_socket(
199
+ path,
200
+ timeout=timeout,
201
+ socket_options=socket_options,
202
+ )
203
+
204
+ def sleep(self, seconds: float) -> None:
205
+ self._delegate.sleep(seconds)
206
+
207
+
208
+ def _validated_card_origin(url: str) -> tuple[str, int, str] | dict[str, Any]:
209
+ """Validate a card URL and pin one address from its all-public DNS result."""
210
+ parsed = urlsplit(url)
211
+ try:
212
+ port = parsed.port
213
+ except ValueError:
214
+ port = None
215
+ if (
216
+ parsed.scheme not in {"http", "https"}
217
+ or not parsed.hostname
218
+ or parsed.username is not None
219
+ or parsed.password is not None
220
+ or parsed.netloc.rsplit("@", 1)[-1].endswith(":")
221
+ or port is not None and not 1 <= port <= 65535
222
+ ):
223
+ return {
224
+ "ok": False,
225
+ "error_type": "invalid_card_url",
226
+ "error_message": "card url must be an HTTP(S) URL without userinfo",
227
+ }
228
+ try:
229
+ hostname = parsed.hostname.encode("idna").decode("ascii").lower()
230
+ effective_port = port or (443 if parsed.scheme == "https" else 80)
231
+ addresses = socket.getaddrinfo(
232
+ hostname,
233
+ effective_port,
234
+ type=socket.SOCK_STREAM,
235
+ )
236
+ except (UnicodeError, socket.gaierror) as exc:
237
+ return {
238
+ "ok": False,
239
+ "error_type": "dns_resolution_failed",
240
+ "error_message": str(exc),
241
+ }
242
+ public_addresses: list[str] = []
243
+ for address in addresses:
244
+ ip = ipaddress.ip_address(address[4][0])
245
+ if not ip.is_global:
246
+ return {
247
+ "ok": False,
248
+ "error_type": "blocked_by_ssrf_policy",
249
+ "error_message": f"blocked non-public card address: {ip}",
250
+ }
251
+ public_addresses.append(str(ip))
252
+ if not public_addresses:
253
+ return {
254
+ "ok": False,
255
+ "error_type": "dns_resolution_failed",
256
+ "error_message": "card hostname resolved to no addresses",
257
+ }
258
+ return hostname, effective_port, public_addresses[0]
259
+
260
+
261
+ def _validate_card_url(url: str) -> dict[str, Any] | None:
262
+ validated = _validated_card_origin(url)
263
+ return validated if isinstance(validated, dict) else None
264
+
265
+
266
+ def _pinned_http_client(*, hostname: str, port: int, pinned_ip: str) -> httpx.Client:
267
+ transport = httpx.HTTPTransport(
268
+ trust_env=False,
269
+ http1=True,
270
+ http2=False,
271
+ limits=httpx.Limits(max_connections=1, max_keepalive_connections=0),
272
+ )
273
+ pool = getattr(transport, "_pool", None)
274
+ if not isinstance(pool, httpcore.ConnectionPool):
275
+ transport.close()
276
+ raise RuntimeError("httpx direct sync transport is unavailable for A2A DNS pinning")
277
+ pool._network_backend = _PinnedDNSNetworkBackend( # type: ignore[attr-defined]
278
+ pool._network_backend,
279
+ expected_hostname=hostname,
280
+ expected_port=port,
281
+ pinned_ip=pinned_ip,
282
+ )
283
+ return httpx.Client(
284
+ transport=transport,
285
+ follow_redirects=False,
286
+ trust_env=False,
287
+ timeout=120,
288
+ )
289
+
290
+
291
+ def list_a2a_agents() -> dict[str, Any]:
292
+ """List remote A2A agents in the configured A2A Space.
293
+
294
+ Returns each agent's id, name, description and version. No remote calls
295
+ are made to the agents themselves — only discovery metadata.
296
+ """
297
+ space_id = _a2a_space_id()
298
+ if not space_id:
299
+ return {"ok": False, "error_message": "A2A Space not configured"}
300
+ try:
301
+ agents = _discover_agents()
302
+ except Exception as exc:
303
+ return {"ok": False, "error_type": type(exc).__name__, "error_message": str(exc)}
304
+ return {"ok": True, "space_id": space_id, "agents": agents}
305
+
306
+
307
+ def get_a2a_agent_card(agent: str) -> dict[str, Any]:
308
+ """Get the AgentCard of a remote A2A agent by id or name.
309
+
310
+ ``agent`` accepts the A2AAgentId (a2a-agent-*) or the card name. When not
311
+ found, returns available_agents and a suggestion.
312
+ """
313
+ space_id = _a2a_space_id()
314
+ if not space_id:
315
+ return {"ok": False, "error_message": "A2A Space not configured"}
316
+ try:
317
+ agents = _discover_agents()
318
+ except Exception as exc:
319
+ return {"ok": False, "error_type": type(exc).__name__, "error_message": str(exc)}
320
+ matched, hint = _match_agent(agents, agent)
321
+ if matched is None:
322
+ return {
323
+ "ok": False,
324
+ "error_message": f"Agent not found: {agent}",
325
+ "available_agents": hint,
326
+ }
327
+ return {"ok": True, "agent": matched}
328
+
329
+
330
+ def call_a2a_agent(agent: str, message: str) -> dict[str, Any]:
331
+ """Call a remote A2A agent by id or name with a text message.
332
+
333
+ ``agent`` accepts A2AAgentId (a2a-agent-*) or card name. Synchronously waits
334
+ for the remote task to complete and returns the agent's text reply. If the
335
+ task does not reach a terminal state within the poll budget, returns the
336
+ last known state and task_id for follow-up.
337
+ """
338
+ space_id = _a2a_space_id()
339
+ if not space_id:
340
+ return {"ok": False, "error_message": "A2A Space not configured"}
341
+ if not (message or "").strip():
342
+ return {"ok": False, "error_message": "message is required"}
343
+ try:
344
+ agents = _discover_agents()
345
+ except Exception as exc:
346
+ return {"ok": False, "error_type": type(exc).__name__, "error_message": str(exc)}
347
+ matched, hint = _match_agent(agents, agent)
348
+ if matched is None:
349
+ return {
350
+ "ok": False,
351
+ "error_message": f"Agent not found: {agent}",
352
+ "available_agents": hint,
353
+ }
354
+ try:
355
+ card_url = str(matched.get("url") or "").strip()
356
+ if not card_url:
357
+ return {"ok": False, "error_message": f"agent {matched['agent_id']} card 无可用 url"}
358
+
359
+ def _send() -> dict[str, Any]:
360
+ # 直接对 card.url 发非流式 SendMessage(returnImmediately=false),同步拿终态回复。
361
+ # 不走 space_client 的流式 send_message/subscribe(其 task 状态跟踪在异步下不可靠)。
362
+ payload = {
363
+ "jsonrpc": "2.0",
364
+ "method": "SendMessage",
365
+ "id": "1",
366
+ "params": {
367
+ "message": {
368
+ "messageId": f"m-{uuid.uuid4().hex[:12]}",
369
+ "role": "ROLE_USER",
370
+ "parts": [{"text": message}],
371
+ },
372
+ "configuration": {"returnImmediately": False},
373
+ },
374
+ }
375
+ # 透传 OTel trace context:把当前 span 注入 traceparent header,
376
+ # 让被调 agent 的 span 挂到同一条分布式 trace 上。
377
+ headers = {"Content-Type": "application/json", "A2A-Version": "1.0"}
378
+ try:
379
+ from opentelemetry import propagate
380
+
381
+ propagate.inject(headers)
382
+ except Exception:
383
+ pass
384
+ validated = _validated_card_origin(card_url)
385
+ if isinstance(validated, dict):
386
+ return validated
387
+ hostname, port, pinned_ip = validated
388
+ with _pinned_http_client(
389
+ hostname=hostname,
390
+ port=port,
391
+ pinned_ip=pinned_ip,
392
+ ) as client:
393
+ resp = client.post(card_url, json=payload, headers=headers)
394
+ if resp.status_code != 200:
395
+ return {
396
+ "ok": False,
397
+ "error_type": "A2AClientError",
398
+ "error_message": f"HTTP {resp.status_code}: {resp.text[:200]}",
399
+ }
400
+ data = resp.json()
401
+ if "error" in data and data["error"]:
402
+ err = str(data["error"])[:300]
403
+ return {"ok": False, "error_type": "A2AError", "error_message": err}
404
+ task = (data.get("result") or {}).get("task") or {}
405
+ return {
406
+ "ok": True,
407
+ "agent": matched["agent_id"],
408
+ "task_id": task.get("id", ""),
409
+ "state": str((task.get("status") or {}).get("state", "")),
410
+ "reply": _extract_reply_text(task),
411
+ }
412
+
413
+ return _send()
414
+ except Exception as exc:
415
+ return {"ok": False, "error_type": type(exc).__name__, "error_message": str(exc)}
416
+
417
+
418
+ def get_a2a_tools() -> list:
419
+ return [as_tool(list_a2a_agents), as_tool(get_a2a_agent_card), as_tool(call_a2a_agent)]
420
+
421
+
422
+ _A2A_TOOL_POLICIES = {
423
+ "list_a2a_agents": ToolPolicy(risk_level="low"),
424
+ "get_a2a_agent_card": ToolPolicy(risk_level="low"),
425
+ "call_a2a_agent": ToolPolicy(risk_level="high", side_effects=("a2a_remote_call",)),
426
+ }
@@ -34,8 +34,16 @@ def component_status() -> dict:
34
34
  "knowledge_base_bound": bool(_env("KSADK_KB_DATASET_ID")),
35
35
  "long_term_memory_bound": bool(_env("KSADK_LTM_NAMESPACE")),
36
36
  "skill_space_bound": bool(spaces),
37
- "isolated_execution": "enabled" if skill_runtime_backend not in {"", "disabled", "none", "off"} else "not_enabled",
38
- "sandbox_direct_tools": "enabled" if sandbox_backend not in {"", "disabled", "none", "off"} else "not_enabled",
37
+ "isolated_execution": (
38
+ "enabled"
39
+ if skill_runtime_backend not in {"", "disabled", "none", "off"}
40
+ else "not_enabled"
41
+ ),
42
+ "sandbox_direct_tools": (
43
+ "enabled"
44
+ if sandbox_backend not in {"", "disabled", "none", "off"}
45
+ else "not_enabled"
46
+ ),
39
47
  },
40
48
  "skill_space": {
41
49
  "space_ids": spaces,
@@ -48,13 +56,17 @@ def component_status() -> dict:
48
56
  "skill_runtime": {
49
57
  "backend": skill_runtime_backend,
50
58
  "enabled": skill_runtime_backend not in {"", "disabled", "none", "off"},
51
- "template_bound": bool(_env("KSADK_SANDBOX_TEMPLATE_ID") or _env("KSADK_SKILL_RUNTIME_TEMPLATE_ID")),
59
+ "template_bound": bool(
60
+ _env("KSADK_SANDBOX_TEMPLATE_ID") or _env("KSADK_SKILL_RUNTIME_TEMPLATE_ID")
61
+ ),
52
62
  "request_protocol": "--request-file JSON envelope",
53
63
  },
54
64
  "sandbox": {
55
65
  "backend": sandbox_backend,
56
66
  "enabled": sandbox_backend not in {"", "disabled", "none", "off"},
57
- "template_bound": bool(_env("KSADK_SANDBOX_TEMPLATE_ID") or _env("KSADK_SKILL_RUNTIME_TEMPLATE_ID")),
67
+ "template_bound": bool(
68
+ _env("KSADK_SANDBOX_TEMPLATE_ID") or _env("KSADK_SKILL_RUNTIME_TEMPLATE_ID")
69
+ ),
58
70
  "tools": ["sandbox_status", "run_command", "run_code"],
59
71
  },
60
72
  "workspace": {
@@ -71,7 +83,11 @@ def component_status() -> dict:
71
83
  "delete_workspace_file",
72
84
  ],
73
85
  "boundary": "Workspace tools are confined to the AgentEngine UI workspace directory.",
74
- "editing_model": "write tools support whole-file writes; edit_workspace_file supports exact snippet replacement; lint_workspace_file provides lightweight built-in checks.",
86
+ "editing_model": (
87
+ "write tools support whole-file writes; edit_workspace_file supports "
88
+ "exact snippet replacement; lint_workspace_file provides lightweight "
89
+ "built-in checks."
90
+ ),
75
91
  },
76
92
  }
77
93
 
ksadk/toolsets/sandbox.py CHANGED
@@ -1,9 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import logging
3
4
  import os
4
5
  import shlex
5
6
  import time
6
- import logging
7
7
  from typing import Any
8
8
  from uuid import uuid4
9
9
 
@@ -15,7 +15,6 @@ from ksadk.tools.result_budget import budget_text_fields
15
15
  from ksadk.toolsets._langchain import as_tool
16
16
  from ksadk.toolsets.workspace import workspace_root
17
17
 
18
-
19
18
  logger = logging.getLogger(__name__)
20
19
 
21
20
 
@@ -30,11 +29,19 @@ def _gateway():
30
29
  return default_tool_gateway(_SANDBOX_TOOL_POLICIES)
31
30
 
32
31
 
32
+ def _dict_result(value: Any, *, tool_name: str) -> dict[str, Any]:
33
+ if not isinstance(value, dict):
34
+ raise TypeError(f"{tool_name} returned {type(value).__name__}, expected dict")
35
+ return dict(value)
36
+
37
+
33
38
  def sandbox_backend_name() -> str:
34
39
  explicit = os.environ.get("KSADK_SANDBOX_BACKEND", "").strip().lower()
35
40
  if explicit:
36
41
  return explicit
37
- if os.environ.get("KSADK_SANDBOX_TEMPLATE_ID") or os.environ.get("KSADK_SKILL_RUNTIME_TEMPLATE_ID"):
42
+ if os.environ.get("KSADK_SANDBOX_TEMPLATE_ID") or os.environ.get(
43
+ "KSADK_SKILL_RUNTIME_TEMPLATE_ID"
44
+ ):
38
45
  return "e2b"
39
46
  return "none"
40
47
 
@@ -47,15 +54,26 @@ def _sandbox_execution_backend_name() -> str:
47
54
  def sandbox_status() -> dict:
48
55
  """Report configured AgentEngine sandbox status and boundaries."""
49
56
 
50
- return _gateway().invoke("sandbox_status", _sandbox_status_impl)
57
+ return _dict_result(
58
+ _gateway().invoke("sandbox_status", _sandbox_status_impl),
59
+ tool_name="sandbox_status",
60
+ )
51
61
 
52
62
 
53
63
  def _sandbox_status_impl() -> dict[str, Any]:
54
64
  backend = sandbox_backend_name()
55
- timeout = int(os.environ.get("KSADK_SANDBOX_TIMEOUT") or os.environ.get("KSADK_SKILL_RUNTIME_TIMEOUT") or "600")
65
+ timeout = int(
66
+ os.environ.get("KSADK_SANDBOX_TIMEOUT")
67
+ or os.environ.get("KSADK_SKILL_RUNTIME_TIMEOUT")
68
+ or "600"
69
+ )
56
70
  idle_ttl = _int_env("KSADK_SANDBOX_IDLE_TTL_SECONDS", 300)
57
71
  max_sessions = _int_env("KSADK_SANDBOX_MAX_SESSIONS", 0)
58
- template_id = os.environ.get("KSADK_SANDBOX_TEMPLATE_ID") or os.environ.get("KSADK_SKILL_RUNTIME_TEMPLATE_ID") or ""
72
+ template_id = (
73
+ os.environ.get("KSADK_SANDBOX_TEMPLATE_ID")
74
+ or os.environ.get("KSADK_SKILL_RUNTIME_TEMPLATE_ID")
75
+ or ""
76
+ )
59
77
  isolated = backend not in {"local", "local_process", "pod", "pod_process"}
60
78
  latest = GLOBAL_SANDBOX_REGISTRY.latest()
61
79
  now = time.time()
@@ -89,7 +107,19 @@ def run_command(
89
107
  ) -> dict[str, Any]:
90
108
  """Run a shell command inside the configured isolated sandbox."""
91
109
 
92
- return _gateway().invoke("run_command", _run_command_impl, command, cwd, timeout, env, background, approval=approval)
110
+ return _dict_result(
111
+ _gateway().invoke(
112
+ "run_command",
113
+ _run_command_impl,
114
+ command,
115
+ cwd,
116
+ timeout,
117
+ env,
118
+ background,
119
+ approval=approval,
120
+ ),
121
+ tool_name="run_command",
122
+ )
93
123
 
94
124
 
95
125
  def _run_command_impl(
@@ -102,11 +132,15 @@ def _run_command_impl(
102
132
  command_text = str(command or "").strip()
103
133
  if not command_text:
104
134
  return {"ok": False, "error_message": "command is required"}
105
- policy = check_command_policy(command_text)
135
+ policy = _dict_result(check_command_policy(command_text), tool_name="check_command_policy")
106
136
  if not policy.get("ok"):
107
137
  return policy
108
138
  if background:
109
- return {"ok": False, "error_type": "background_not_supported", "error_message": "background commands are not supported in P0"}
139
+ return {
140
+ "ok": False,
141
+ "error_type": "background_not_supported",
142
+ "error_message": "background commands are not supported in P0",
143
+ }
110
144
  try:
111
145
  entry, _created = _sandbox_entry("ksadk-direct")
112
146
  result = entry.session.run_command(command_text, timeout=timeout, env=env, cwd=cwd)
@@ -119,7 +153,15 @@ def _run_command_impl(
119
153
  "stderr": result.stderr,
120
154
  "exit_code": result.exit_code,
121
155
  }
122
- return budget_text_fields(payload, tool_name="run_command", fields=("stdout", "stderr"), metadata={"tool_use_id": entry.sandbox_id})
156
+ return _dict_result(
157
+ budget_text_fields(
158
+ payload,
159
+ tool_name="run_command",
160
+ fields=("stdout", "stderr"),
161
+ metadata={"tool_use_id": entry.sandbox_id},
162
+ ),
163
+ tool_name="run_command",
164
+ )
123
165
  except SandboxError as exc:
124
166
  return {"ok": False, "error_type": type(exc).__name__, "error_message": str(exc)}
125
167
  except Exception as exc:
@@ -135,7 +177,12 @@ def run_code(
135
177
  ) -> dict[str, Any]:
136
178
  """Run a code snippet through the sandbox; this is not a shell replacement."""
137
179
 
138
- return _gateway().invoke("run_code", _run_code_impl, code, language, timeout, env, approval=approval)
180
+ return _dict_result(
181
+ _gateway().invoke(
182
+ "run_code", _run_code_impl, code, language, timeout, env, approval=approval
183
+ ),
184
+ tool_name="run_code",
185
+ )
139
186
 
140
187
 
141
188
  def _run_code_impl(
@@ -152,7 +199,10 @@ def _run_code_impl(
152
199
  return {
153
200
  "ok": False,
154
201
  "error_type": "isolated_sandbox_required",
155
- "error_message": "run_code requires an isolated sandbox backend; local_process and pod_process are not supported for snippet execution",
202
+ "error_message": (
203
+ "run_code requires an isolated sandbox backend; local_process and "
204
+ "pod_process are not supported for snippet execution"
205
+ ),
156
206
  "backend": backend_name,
157
207
  "boundary": _sandbox_boundary(backend_name),
158
208
  }
@@ -181,7 +231,15 @@ def _run_code_impl(
181
231
  "stderr": result.stderr,
182
232
  "exit_code": result.exit_code,
183
233
  }
184
- return budget_text_fields(payload, tool_name="run_code", fields=("stdout", "stderr"), metadata={"tool_use_id": entry.sandbox_id})
234
+ return _dict_result(
235
+ budget_text_fields(
236
+ payload,
237
+ tool_name="run_code",
238
+ fields=("stdout", "stderr"),
239
+ metadata={"tool_use_id": entry.sandbox_id},
240
+ ),
241
+ tool_name="run_code",
242
+ )
185
243
  except SandboxError as exc:
186
244
  return {"ok": False, "error_type": type(exc).__name__, "error_message": str(exc)}
187
245
  except Exception as exc:
@@ -300,10 +358,18 @@ def _int_env(name: str, default: int) -> int:
300
358
 
301
359
  def _sandbox_boundary(backend: str) -> str:
302
360
  if backend in {"local", "local_process"}:
303
- return "executes as a local child process; shares local filesystem, env allowlist, and network"
361
+ return (
362
+ "executes as a local child process; shares local filesystem, env allowlist, and network"
363
+ )
304
364
  if backend in {"pod", "pod_process"}:
305
- return "executes inside the agent pod; shares pod filesystem, env allowlist, network, and service account"
306
- return "Sandbox tools execute commands and code only through the configured isolated sandbox backend; they never expose the host shell."
365
+ return (
366
+ "executes inside the agent pod; shares pod filesystem, env allowlist, "
367
+ "network, and service account"
368
+ )
369
+ return (
370
+ "Sandbox tools execute commands and code only through the configured isolated "
371
+ "sandbox backend; they never expose the host shell."
372
+ )
307
373
 
308
374
 
309
375
  def get_sandbox_tools() -> list: