agentengine-sdk-python 0.7.0__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 (371) hide show
  1. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/METADATA +36 -20
  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 +233 -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 +500 -720
  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 +168 -89
  54. ksadk/cli/cmd_invoke.py +184 -122
  55. ksadk/cli/cmd_launch.py +64 -28
  56. ksadk/cli/cmd_mcp.py +98 -64
  57. ksadk/cli/cmd_model.py +14 -5
  58. ksadk/cli/cmd_openclaw.py +483 -227
  59. ksadk/cli/cmd_replay.py +71 -0
  60. ksadk/cli/cmd_run.py +33 -10
  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 +34 -14
  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 +500 -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 +438 -51
  97. ksadk/conversations/model_context.py +12 -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 -5255
  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 +51 -38
  120. ksadk/deployment/manager.py +63 -65
  121. ksadk/deployment/providers/serverless.py +309 -202
  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 +4 -5
  156. ksadk/memory/adk/short_term_memory.py +12 -21
  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 +345 -120
  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 -742
  183. ksadk/runners/langgraph_runner.py +352 -90
  184. ksadk/runners/patch_langchain.py +16 -16
  185. ksadk/runners/remote_runner.py +134 -33
  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 -3885
  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-D-SSn2ee.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-BA13sHc9.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-DzJzgVoL.js → architectureDiagram-3BPJPVTR-Dej8jX8c.js} +1 -1
  229. ksadk/server/static/assets/{blockDiagram-GPEHLZMM-UTdVcZhn.js → blockDiagram-GPEHLZMM-1F64jqvy.js} +1 -1
  230. ksadk/server/static/assets/{c4Diagram-AAUBKEIU-Dix0GD4m.js → c4Diagram-AAUBKEIU-9q7AUAKb.js} +1 -1
  231. ksadk/server/static/assets/channel-iUGv2N24.js +1 -0
  232. ksadk/server/static/assets/{chunk-2J33WTMH-C2JxuZ42.js → chunk-2J33WTMH-6qgwJm7Q.js} +1 -1
  233. ksadk/server/static/assets/{chunk-4BX2VUAB-Be57QPgD.js → chunk-4BX2VUAB-CsWIQRhc.js} +1 -1
  234. ksadk/server/static/assets/{chunk-55IACEB6-WjSuy4nv.js → chunk-55IACEB6-BK8paA20.js} +1 -1
  235. ksadk/server/static/assets/{chunk-727SXJPM-BOovP-b5.js → chunk-727SXJPM-B-JEhy7h.js} +1 -1
  236. ksadk/server/static/assets/{chunk-AQP2D5EJ-DOmZNmWj.js → chunk-AQP2D5EJ-DYBvMHAa.js} +1 -1
  237. ksadk/server/static/assets/{chunk-FMBD7UC4-DzwlodkU.js → chunk-FMBD7UC4-BGhvzDYt.js} +1 -1
  238. ksadk/server/static/assets/{chunk-ND2GUHAM-DMqAikEU.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-Zhy2GBnP.js → cose-bilkent-S5V4N54A-CX3yotHq.js} +1 -1
  243. ksadk/server/static/assets/{dagre-BM42HDAG-CbKrtIUz.js → dagre-BM42HDAG-C7TguAWo.js} +1 -1
  244. ksadk/server/static/assets/{diagram-2AECGRRQ-BRApgQRK.js → diagram-2AECGRRQ-Dd17_kws.js} +1 -1
  245. ksadk/server/static/assets/{diagram-5GNKFQAL-D0xbnDiK.js → diagram-5GNKFQAL-DVIClIil.js} +1 -1
  246. ksadk/server/static/assets/{diagram-KO2AKTUF-CVugVI9L.js → diagram-KO2AKTUF-Df_3gYQa.js} +1 -1
  247. ksadk/server/static/assets/{diagram-LMA3HP47-DozUGgee.js → diagram-LMA3HP47-BhLk6ZtA.js} +1 -1
  248. ksadk/server/static/assets/{diagram-OG6HWLK6-ZOYsKlru.js → diagram-OG6HWLK6-D4GNGvuu.js} +1 -1
  249. ksadk/server/static/assets/{dist-DmFaq_Iy.js → dist-6CVKuTAe.js} +1 -1
  250. ksadk/server/static/assets/{dist-8EEyGgDx.js → dist-BUHRxvDG.js} +1 -1
  251. ksadk/server/static/assets/{dist-B0NP_Z0z.js → dist-BZ9n9AjO.js} +1 -1
  252. ksadk/server/static/assets/{dist-DMezvBGJ.js → dist-BibcG1V9.js} +1 -1
  253. ksadk/server/static/assets/{dist-C3HIIaN5.js → dist-Bs-jQcRX.js} +1 -1
  254. ksadk/server/static/assets/{dist-BrCcQ47T.js → dist-C2xjE7cO.js} +1 -1
  255. ksadk/server/static/assets/{dist-Dp0k7bWI.js → dist-C7-3jInC.js} +1 -1
  256. ksadk/server/static/assets/{dist-D-xOD048.js → dist-C99THtIA.js} +1 -1
  257. ksadk/server/static/assets/{dist-CkEKJFtk.js → dist-CGcp2ex8.js} +1 -1
  258. ksadk/server/static/assets/{dist-DyOkZmze.js → dist-CVQRZjPu.js} +1 -1
  259. ksadk/server/static/assets/{dist-BssVmjIo.js → dist-Cf23NY4L.js} +1 -1
  260. ksadk/server/static/assets/{dist-BIJVDMuB.js → dist-CgF3fj15.js} +1 -1
  261. ksadk/server/static/assets/{dist-2hInLS_J.js → dist-Cwt7koer.js} +1 -1
  262. ksadk/server/static/assets/{dist-DyWsb78Q.js → dist-D82mtD2s.js} +1 -1
  263. ksadk/server/static/assets/{dist-D-JMdWPQ.js → dist-DF06ORQo.js} +1 -1
  264. ksadk/server/static/assets/{dist-BmlMPsCc.js → dist-DHn6nBzr.js} +1 -1
  265. ksadk/server/static/assets/{dist-gX6GOLKq.js → dist-Djgl2wVZ.js} +1 -1
  266. ksadk/server/static/assets/{dist-DpKtt4Bo.js → dist-DlzCRy0S.js} +1 -1
  267. ksadk/server/static/assets/{dist-8_SfK1IP.js → dist-DqNnNFrw.js} +1 -1
  268. ksadk/server/static/assets/{dist-D57wnGWd.js → dist-DyvYLMW9.js} +1 -1
  269. ksadk/server/static/assets/{dist-CPBtqRTW.js → dist-eoW6tZXA.js} +1 -1
  270. ksadk/server/static/assets/{dist-DL_NKc_z.js → dist-ivOya1LD.js} +1 -1
  271. ksadk/server/static/assets/{erDiagram-TEJ5UH35-Bk-9bgE5.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-Dn9kZVuZ.js → flowDiagram-I6XJVG4X-DGaJjGNJ.js} +1 -1
  274. ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BGxXlJqm.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-C7Q0X-wi.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-BO_CSqq5.js → infoDiagram-5YYISTIA-CYIIsb8y.js} +1 -1
  281. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-eYLPLd46.js → ishikawaDiagram-YF4QCWOH-DLOHGvWw.js} +1 -1
  282. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-De9geSb0.js → journeyDiagram-JHISSGLW-BIzKeejT.js} +1 -1
  283. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-Cs80kQto.js → kanban-definition-UN3LZRKU-BWHoFSw2.js} +1 -1
  284. ksadk/server/static/assets/{katex-DSrCFD8n.js → katex-DATssjp1.js} +1 -1
  285. ksadk/server/static/assets/{linear-CN9sbKTF.js → linear-CvMBw9cA.js} +1 -1
  286. ksadk/server/static/assets/{mermaid-parser.core-DlgSBU8l.js → mermaid-parser.core-mGAcEwcq.js} +2 -2
  287. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-9afz7Oug.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-DPv_VstN.js → pieDiagram-4H26LBE5-By-ra07M.js} +1 -1
  291. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-o77wZ1XK.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-BYVw81VC.js → requirementDiagram-4Y6WPE33-DpW90iu3.js} +1 -1
  294. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-B7nZFMKT.js → sankeyDiagram-5OEKKPKP-DpGa6EAV.js} +1 -1
  295. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-BGRRrFw5.js → sequenceDiagram-3UESZ5HK-NTKNGGe5.js} +1 -1
  296. ksadk/server/static/assets/{stateDiagram-AJRCARHV-BUfHCoPs.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-FbCTGrf9.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-DWgwtPsJ.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-2x8G-tTM.js → wardleyDiagram-YWT4CUSO-BR54GPmr.js} +1 -1
  304. ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-Drma3AYl.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 +69 -19
  308. ksadk/sessions/base.py +31 -11
  309. ksadk/sessions/continuity.py +2 -3
  310. ksadk/sessions/in_memory.py +46 -2
  311. ksadk/sessions/local_service.py +148 -17
  312. ksadk/sessions/postgres_service.py +143 -25
  313. ksadk/sessions/resilient.py +36 -8
  314. ksadk/skills/models.py +17 -5
  315. ksadk/skills/package_store.py +5 -2
  316. ksadk/skills/runtime/agent.py +20 -5
  317. ksadk/skills/runtime/artifacts.py +1 -5
  318. ksadk/skills/runtime/backends/e2b.py +10 -4
  319. ksadk/skills/runtime/backends/local.py +15 -3
  320. ksadk/skills/runtime/base.py +1 -2
  321. ksadk/skills/runtime/executor.py +10 -3
  322. ksadk/skills/runtime/factory.py +4 -2
  323. ksadk/skills/runtime/loader.py +1 -2
  324. ksadk/skills/runtime/registry.py +15 -10
  325. ksadk/skills/runtime/request.py +3 -1
  326. ksadk/skills/service_client.py +38 -17
  327. ksadk/skills/tool_defs.py +15 -10
  328. ksadk/terminal_exec_policy.py +3 -4
  329. ksadk/tools/gateway.py +92 -15
  330. ksadk/tools/result_budget.py +12 -4
  331. ksadk/toolsets/__init__.py +134 -36
  332. ksadk/toolsets/platform.py +21 -5
  333. ksadk/toolsets/sandbox.py +82 -16
  334. ksadk/toolsets/skills.py +200 -39
  335. ksadk/toolsets/web.py +123 -26
  336. ksadk/toolsets/workspace.py +212 -101
  337. ksadk/tracing/exporters/inmemory_exporter.py +62 -51
  338. ksadk/tracing/exporters/langfuse_exporter.py +91 -76
  339. ksadk/tracing/setup.py +69 -53
  340. ksadk/tracing/span_utils.py +5 -2
  341. ksadk/tui/loop.py +78 -30
  342. ksadk/tui/stream_render.py +1 -0
  343. ksadk/ui_config.py +5 -3
  344. ksadk/version.py +1 -1
  345. ksadk_runtime_common/schemas/runtime_event_v1.json +95 -0
  346. agentengine_sdk_python-0.7.0.dist-info/RECORD +0 -482
  347. ksadk/server/static/assets/CodeBlock-P16VOLYi.js +0 -9
  348. ksadk/server/static/assets/MathMessageMarkdown-CdGiQWHR.js +0 -8
  349. ksadk/server/static/assets/MermaidBlock-IOcal8cm.js +0 -303
  350. ksadk/server/static/assets/NativeTerminalPanel-DGSDWsei.js +0 -1
  351. ksadk/server/static/assets/architecture-7EHR7CIX-BvUlsA6l.js +0 -1
  352. ksadk/server/static/assets/channel-CTuydARX.js +0 -1
  353. ksadk/server/static/assets/chunk-QZHKN3VN-BnCydivS.js +0 -1
  354. ksadk/server/static/assets/classDiagram-4FO5ZUOK-BNHg45Be.js +0 -1
  355. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-BNHg45Be.js +0 -1
  356. ksadk/server/static/assets/eventmodeling-FCH6USID-DsAgWnlf.js +0 -1
  357. ksadk/server/static/assets/gitGraph-WXDBUCRP-BIao9Yjg.js +0 -1
  358. ksadk/server/static/assets/index-C00Lwg3L.js +0 -165
  359. ksadk/server/static/assets/index-DAURbCsP.css +0 -1
  360. ksadk/server/static/assets/info-J43DQDTF-CyipqGxU.js +0 -1
  361. ksadk/server/static/assets/packet-YPE3B663-CGkyRxlg.js +0 -1
  362. ksadk/server/static/assets/pie-LRSECV5Y-M0uWAf4n.js +0 -1
  363. ksadk/server/static/assets/radar-GUYGQ44K-Bdbm24-x.js +0 -1
  364. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-8xBN_gLM.js +0 -1
  365. ksadk/server/static/assets/treeView-BLDUP644-npMo0ak1.js +0 -1
  366. ksadk/server/static/assets/treemap-LRROVOQU-CHdzrrdo.js +0 -1
  367. ksadk/server/static/assets/wardley-L42UT6IY-BEQz7wyy.js +0 -1
  368. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/WHEEL +0 -0
  369. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/entry_points.txt +0 -0
  370. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/licenses/LICENSE +0 -0
  371. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,516 @@
1
+ """Lease-based external A2A transport boundary owned by the Runtime network guard."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import ipaddress
7
+ import socket
8
+ from abc import ABC, abstractmethod
9
+ from collections.abc import Awaitable, Sequence
10
+ from contextlib import AbstractAsyncContextManager, asynccontextmanager
11
+ from dataclasses import dataclass
12
+ from typing import Any, AsyncIterator, Callable
13
+ from urllib.parse import urlsplit
14
+
15
+ import httpcore
16
+ import httpx
17
+
18
+ from ksadk.a2a.control_plane import A2ARouteInterface
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class A2ATransportLease:
23
+ """A single validated external route operation."""
24
+
25
+ httpx_client: httpx.AsyncClient
26
+ effective_interface: A2ARouteInterface
27
+ route_kind: str
28
+ policy_revision: str
29
+
30
+
31
+ class A2AExternalTransport(ABC):
32
+ """SSRF-safe external transport supplied by the Runtime network guard."""
33
+
34
+ @abstractmethod
35
+ def open_for_route(
36
+ self,
37
+ route: A2ARouteInterface,
38
+ *,
39
+ route_kind: str,
40
+ ) -> AbstractAsyncContextManager[A2ATransportLease]:
41
+ raise NotImplementedError
42
+
43
+
44
+ class A2ARouteOpener(ABC):
45
+ """Platform network guard operation that has completed DNS/VPC policy validation."""
46
+
47
+ @abstractmethod
48
+ def open_validated_route(
49
+ self,
50
+ route: A2ARouteInterface,
51
+ *,
52
+ route_kind: str,
53
+ ) -> AbstractAsyncContextManager[A2ATransportLease]:
54
+ raise NotImplementedError
55
+
56
+
57
+ class CallableA2ARouteOpener(A2ARouteOpener):
58
+ """Adapter for an injected Runtime network guard operation."""
59
+
60
+ def __init__(
61
+ self,
62
+ open_route: Callable[..., AbstractAsyncContextManager[A2ATransportLease]],
63
+ ) -> None:
64
+ self._open_route = open_route
65
+
66
+ def open_validated_route(
67
+ self,
68
+ route: A2ARouteInterface,
69
+ *,
70
+ route_kind: str,
71
+ ) -> AbstractAsyncContextManager[A2ATransportLease]:
72
+ return self._open_route(route, route_kind=route_kind)
73
+
74
+
75
+ class GuardedA2AExternalTransport(A2AExternalTransport):
76
+ """Validates a Runtime network-guard lease before exposing its HTTP client."""
77
+
78
+ def __init__(self, route_opener: A2ARouteOpener) -> None:
79
+ self._route_opener = route_opener
80
+
81
+ @asynccontextmanager
82
+ async def open_for_route(
83
+ self,
84
+ route: A2ARouteInterface,
85
+ *,
86
+ route_kind: str,
87
+ ) -> AsyncIterator[A2ATransportLease]:
88
+ if route_kind not in {"external_public", "external_vpc"}:
89
+ raise ValueError(f"external transport does not support route kind {route_kind!r}")
90
+ async with self._route_opener.open_validated_route(
91
+ route,
92
+ route_kind=route_kind,
93
+ ) as lease:
94
+ self._validate_lease(route, route_kind, lease)
95
+ yield lease
96
+
97
+ @staticmethod
98
+ def _validate_lease(
99
+ requested: A2ARouteInterface,
100
+ route_kind: str,
101
+ lease: A2ATransportLease,
102
+ ) -> None:
103
+ if lease.route_kind != route_kind:
104
+ raise ValueError("network guard lease route kind does not match requested route")
105
+ if not lease.policy_revision.strip():
106
+ raise ValueError("network guard lease is missing policy revision")
107
+ if _canonical_url(lease.effective_interface.url) != _canonical_url(requested.url):
108
+ raise ValueError("network guard lease route does not match requested route")
109
+ if lease.httpx_client.follow_redirects:
110
+ raise ValueError("external A2A transport must not follow redirects automatically")
111
+ if getattr(lease.httpx_client, "_trust_env", None) is not False:
112
+ raise ValueError("external A2A transport must disable environment proxies")
113
+
114
+
115
+ _RUNTIME_LOCAL_POLICY_REVISION = "runtime-local-dns-pinned-v1"
116
+ _EXTERNAL_CONNECT_TIMEOUT_SECONDS = 5.0
117
+ _EXTERNAL_READ_TIMEOUT_SECONDS = 30.0
118
+ _EXTERNAL_OPERATION_TIMEOUT_SECONDS = 300.0
119
+ _MAX_EXTERNAL_RESPONSE_BYTES = 8 * 1024 * 1024
120
+ _MAX_EXTERNAL_JSON_DEPTH = 64
121
+ _MAX_EXTERNAL_JSON_STRING_BYTES = 1024 * 1024
122
+ ERR_VPC_EGRESS_DIALER_REQUIRED = "A2A_VPC_EGRESS_DIALER_REQUIRED"
123
+
124
+
125
+ async def _resolve_hostname(host: str, port: int) -> Sequence[str]:
126
+ """Resolve one operation hostname without retaining an SDK-level DNS cache."""
127
+
128
+ loop = asyncio.get_running_loop()
129
+ results = await loop.getaddrinfo(host, port, type=socket.SOCK_STREAM)
130
+ addresses = tuple(dict.fromkeys(str(item[4][0]) for item in results))
131
+ if not addresses:
132
+ raise OSError(f"DNS lookup returned no addresses for {host!r}")
133
+ return addresses
134
+
135
+
136
+ class _PinnedDNSNetworkBackend(httpcore.AsyncNetworkBackend):
137
+ """Dials a verified IP while httpcore retains the hostname for TLS/SNI."""
138
+
139
+ def __init__(
140
+ self,
141
+ delegate: httpcore.AsyncNetworkBackend,
142
+ *,
143
+ expected_hostname: str,
144
+ expected_port: int,
145
+ pinned_ip: str,
146
+ ) -> None:
147
+ self._delegate = delegate
148
+ self._expected_hostname = expected_hostname
149
+ self._expected_port = expected_port
150
+ self._pinned_ip = pinned_ip
151
+
152
+ async def connect_tcp(
153
+ self,
154
+ host: str,
155
+ port: int,
156
+ timeout: float | None = None,
157
+ local_address: str | None = None,
158
+ socket_options: Any = None,
159
+ ) -> httpcore.AsyncNetworkStream:
160
+ if host.lower() != self._expected_hostname or port != self._expected_port:
161
+ raise RuntimeError("external A2A transport attempted to dial an unapproved origin")
162
+ return await self._delegate.connect_tcp(
163
+ self._pinned_ip,
164
+ port,
165
+ timeout=timeout,
166
+ local_address=local_address,
167
+ socket_options=socket_options,
168
+ )
169
+
170
+ async def connect_unix_socket(
171
+ self,
172
+ path: str,
173
+ timeout: float | None = None,
174
+ socket_options: Any = None,
175
+ ) -> httpcore.AsyncNetworkStream:
176
+ return await self._delegate.connect_unix_socket(
177
+ path,
178
+ timeout=timeout,
179
+ socket_options=socket_options,
180
+ )
181
+
182
+ async def sleep(self, seconds: float) -> None:
183
+ await self._delegate.sleep(seconds)
184
+
185
+
186
+ class _BoundedResponseStream(httpx.AsyncByteStream):
187
+ """Enforces one operation deadline and a raw response-body limit."""
188
+
189
+ def __init__(
190
+ self,
191
+ delegate: httpx.AsyncByteStream,
192
+ *,
193
+ max_response_bytes: int,
194
+ deadline: float,
195
+ ) -> None:
196
+ self._delegate = delegate
197
+ self._max_response_bytes = max_response_bytes
198
+ self._deadline = deadline
199
+ self._received_bytes = 0
200
+ self._json_limits = _JSONPayloadLimits()
201
+
202
+ async def __aiter__(self) -> AsyncIterator[bytes]:
203
+ iterator = self._delegate.__aiter__()
204
+ while True:
205
+ remaining = self._deadline - asyncio.get_running_loop().time()
206
+ if remaining <= 0:
207
+ await self.aclose()
208
+ raise httpx.ReadTimeout(
209
+ "external A2A operation exceeded its total response deadline"
210
+ )
211
+ try:
212
+ chunk = await asyncio.wait_for(anext(iterator), timeout=remaining)
213
+ except StopAsyncIteration:
214
+ return
215
+ except TimeoutError as exc:
216
+ await self.aclose()
217
+ raise httpx.ReadTimeout(
218
+ "external A2A operation exceeded its total response deadline"
219
+ ) from exc
220
+ self._received_bytes += len(chunk)
221
+ if self._received_bytes > self._max_response_bytes:
222
+ await self.aclose()
223
+ raise httpx.RemoteProtocolError(
224
+ "external A2A response exceeds configured size limit"
225
+ )
226
+ self._json_limits.observe(chunk)
227
+ yield chunk
228
+
229
+ async def aclose(self) -> None:
230
+ await self._delegate.aclose()
231
+
232
+
233
+ class _JSONPayloadLimits:
234
+ """Lightweight structural guard before the A2A SDK parses external JSON/SSE."""
235
+
236
+ def __init__(self) -> None:
237
+ self._depth = 0
238
+ self._in_string = False
239
+ self._escaped = False
240
+ self._string_bytes = 0
241
+
242
+ def observe(self, chunk: bytes) -> None:
243
+ for byte in chunk:
244
+ if self._in_string:
245
+ self._string_bytes += 1
246
+ if self._string_bytes > _MAX_EXTERNAL_JSON_STRING_BYTES:
247
+ raise httpx.RemoteProtocolError(
248
+ "external A2A JSON string exceeds 1 MiB"
249
+ )
250
+ if self._escaped:
251
+ self._escaped = False
252
+ elif byte == ord("\\"):
253
+ self._escaped = True
254
+ elif byte == ord('"'):
255
+ self._in_string = False
256
+ self._string_bytes = 0
257
+ continue
258
+ if byte == ord('"'):
259
+ self._in_string = True
260
+ self._string_bytes = 0
261
+ elif byte in (ord("{"), ord("[")):
262
+ self._depth += 1
263
+ if self._depth > _MAX_EXTERNAL_JSON_DEPTH:
264
+ raise httpx.RemoteProtocolError("external A2A JSON exceeds depth 64")
265
+ elif byte in (ord("}"), ord("]")):
266
+ self._depth = max(0, self._depth - 1)
267
+
268
+
269
+ class _OriginLockedRedirectRejectingTransport(httpx.AsyncBaseTransport):
270
+ """Restricts a lease client to its resolved origin and rejects all redirects."""
271
+
272
+ def __init__(
273
+ self,
274
+ delegate: httpx.AsyncBaseTransport,
275
+ *,
276
+ hostname: str,
277
+ port: int,
278
+ max_response_bytes: int,
279
+ operation_timeout_seconds: float,
280
+ ) -> None:
281
+ self._delegate = delegate
282
+ self._hostname = hostname
283
+ self._port = port
284
+ self._max_response_bytes = max_response_bytes
285
+ self._operation_timeout_seconds = operation_timeout_seconds
286
+
287
+ async def handle_async_request(self, request: httpx.Request) -> httpx.Response:
288
+ request_port = request.url.port or (443 if request.url.scheme == "https" else 80)
289
+ if (
290
+ request.url.scheme != "https"
291
+ or request.url.host.lower() != self._hostname
292
+ or request_port != self._port
293
+ ):
294
+ raise RuntimeError("external A2A transport lease attempted to use an unapproved origin")
295
+ request.headers["Accept-Encoding"] = "identity"
296
+ deadline = asyncio.get_running_loop().time() + self._operation_timeout_seconds
297
+ try:
298
+ response = await asyncio.wait_for(
299
+ self._delegate.handle_async_request(request),
300
+ timeout=self._operation_timeout_seconds,
301
+ )
302
+ except TimeoutError as exc:
303
+ raise httpx.ReadTimeout(
304
+ "external A2A operation exceeded its total response deadline"
305
+ ) from exc
306
+ if 300 <= response.status_code < 400:
307
+ await response.aclose()
308
+ raise httpx.RemoteProtocolError("A2A external redirect responses are not allowed")
309
+ declared_length = response.headers.get("content-length")
310
+ if declared_length is not None:
311
+ try:
312
+ exceeds_limit = int(declared_length) > self._max_response_bytes
313
+ except ValueError as exc:
314
+ await response.aclose()
315
+ raise httpx.RemoteProtocolError(
316
+ "external A2A response has an invalid Content-Length"
317
+ ) from exc
318
+ if exceeds_limit:
319
+ await response.aclose()
320
+ raise httpx.RemoteProtocolError(
321
+ "external A2A response exceeds configured size limit"
322
+ )
323
+ if not isinstance(response.stream, httpx.AsyncByteStream):
324
+ await response.aclose()
325
+ raise RuntimeError("external A2A transport received a non-async response stream")
326
+ response.stream = _BoundedResponseStream(
327
+ response.stream,
328
+ max_response_bytes=self._max_response_bytes,
329
+ deadline=deadline,
330
+ )
331
+ return response
332
+
333
+ async def aclose(self) -> None:
334
+ await self._delegate.aclose()
335
+
336
+
337
+ class RuntimeLocalA2AExternalTransport(A2AExternalTransport):
338
+ """Runtime-local guarded HTTPS transport for public external Agents over NAT.
339
+
340
+ The Runtime resolves every external operation itself, rejects any non-global
341
+ DNS result, then pins the selected IP in the actual TCP backend. The HTTP
342
+ origin remains the original hostname so certificate validation, ``Host``,
343
+ and TLS SNI are not weakened by the pinning.
344
+ """
345
+
346
+ def __init__(
347
+ self,
348
+ *,
349
+ resolve_hostname: Callable[[str, int], Awaitable[Sequence[str]]] = _resolve_hostname,
350
+ network_backend: httpcore.AsyncNetworkBackend | None = None,
351
+ connect_timeout_seconds: float = _EXTERNAL_CONNECT_TIMEOUT_SECONDS,
352
+ read_timeout_seconds: float = _EXTERNAL_READ_TIMEOUT_SECONDS,
353
+ operation_timeout_seconds: float = _EXTERNAL_OPERATION_TIMEOUT_SECONDS,
354
+ max_response_bytes: int = _MAX_EXTERNAL_RESPONSE_BYTES,
355
+ ) -> None:
356
+ if (
357
+ connect_timeout_seconds <= 0
358
+ or read_timeout_seconds <= 0
359
+ or operation_timeout_seconds <= 0
360
+ or max_response_bytes <= 0
361
+ ):
362
+ raise ValueError("external A2A transport limits must be positive")
363
+ self._resolve_hostname = resolve_hostname
364
+ self._network_backend = network_backend or httpcore.AnyIOBackend()
365
+ self._connect_timeout_seconds = connect_timeout_seconds
366
+ self._read_timeout_seconds = read_timeout_seconds
367
+ self._operation_timeout_seconds = operation_timeout_seconds
368
+ self._max_response_bytes = max_response_bytes
369
+
370
+ @asynccontextmanager
371
+ async def open_for_route(
372
+ self,
373
+ route: A2ARouteInterface,
374
+ *,
375
+ route_kind: str,
376
+ ) -> AsyncIterator[A2ATransportLease]:
377
+ hostname, port = self._validated_public_origin(route, route_kind=route_kind)
378
+ try:
379
+ resolved = await asyncio.wait_for(
380
+ self._resolve_hostname(hostname, port),
381
+ timeout=self._connect_timeout_seconds,
382
+ )
383
+ except TimeoutError as exc:
384
+ raise httpx.ConnectTimeout("external A2A DNS resolution timed out") from exc
385
+ pinned_ip = self._select_global_ip(resolved, hostname=hostname)
386
+ client = self._build_client(hostname=hostname, port=port, pinned_ip=pinned_ip)
387
+ try:
388
+ yield A2ATransportLease(
389
+ httpx_client=client,
390
+ effective_interface=route,
391
+ route_kind=route_kind,
392
+ policy_revision=_RUNTIME_LOCAL_POLICY_REVISION,
393
+ )
394
+ finally:
395
+ await client.aclose()
396
+
397
+ @staticmethod
398
+ def _validated_public_origin(
399
+ route: A2ARouteInterface,
400
+ *,
401
+ route_kind: str,
402
+ ) -> tuple[str, int]:
403
+ if route_kind == "external_vpc":
404
+ raise RuntimeError(
405
+ f"{ERR_VPC_EGRESS_DIALER_REQUIRED}: external_vpc routes require a "
406
+ "platform-injected VPC dialer"
407
+ )
408
+ if route_kind != "external_public":
409
+ raise ValueError(
410
+ "RuntimeLocalA2AExternalTransport supports only external_public routes"
411
+ )
412
+ parsed = urlsplit(route.url)
413
+ try:
414
+ port = parsed.port
415
+ except ValueError as exc:
416
+ raise ValueError("external public A2A route has an invalid port") from exc
417
+ if (
418
+ parsed.scheme != "https"
419
+ or not parsed.hostname
420
+ or parsed.username is not None
421
+ or parsed.password is not None
422
+ or parsed.fragment
423
+ or parsed.netloc.rsplit("@", 1)[-1].endswith(":")
424
+ or port is not None and not 1 <= port <= 65535
425
+ ):
426
+ raise ValueError("external public A2A route must be an absolute HTTPS URL")
427
+ try:
428
+ hostname = parsed.hostname.encode("idna").decode("ascii").lower()
429
+ except UnicodeError as exc:
430
+ raise ValueError("external public A2A route has an invalid hostname") from exc
431
+ return hostname, port or 443
432
+
433
+ @staticmethod
434
+ def _select_global_ip(addresses: Sequence[str], *, hostname: str) -> str:
435
+ if not addresses:
436
+ raise PermissionError(f"external public A2A DNS returned no addresses for {hostname!r}")
437
+ normalized: list[str] = []
438
+ for value in addresses:
439
+ try:
440
+ address = ipaddress.ip_address(value)
441
+ except ValueError as exc:
442
+ raise PermissionError(
443
+ f"external public A2A DNS returned an invalid address for {hostname!r}"
444
+ ) from exc
445
+ if not address.is_global:
446
+ raise PermissionError(
447
+ "external public A2A DNS results must contain only globally routable IPs"
448
+ )
449
+ normalized.append(str(address))
450
+ return normalized[0]
451
+
452
+ def _build_client(self, *, hostname: str, port: int, pinned_ip: str) -> httpx.AsyncClient:
453
+ # httpx has no public network-backend hook. Rebuild its direct httpcore pool
454
+ # to preserve httpx's SSL configuration while replacing only TCP dialing.
455
+ base_transport = httpx.AsyncHTTPTransport(
456
+ trust_env=False,
457
+ http1=True,
458
+ http2=False,
459
+ limits=httpx.Limits(max_connections=1, max_keepalive_connections=0),
460
+ )
461
+ existing_pool = getattr(base_transport, "_pool", None)
462
+ if not isinstance(existing_pool, httpcore.AsyncConnectionPool):
463
+ raise RuntimeError("httpx direct async transport is unavailable for A2A DNS pinning")
464
+ base_transport._pool = httpcore.AsyncConnectionPool( # type: ignore[attr-defined]
465
+ ssl_context=existing_pool._ssl_context,
466
+ max_connections=1,
467
+ max_keepalive_connections=0,
468
+ keepalive_expiry=existing_pool._keepalive_expiry,
469
+ http1=True,
470
+ http2=False,
471
+ retries=0,
472
+ network_backend=_PinnedDNSNetworkBackend(
473
+ self._network_backend,
474
+ expected_hostname=hostname,
475
+ expected_port=port,
476
+ pinned_ip=pinned_ip,
477
+ ),
478
+ )
479
+ transport = _OriginLockedRedirectRejectingTransport(
480
+ base_transport,
481
+ hostname=hostname,
482
+ port=port,
483
+ max_response_bytes=self._max_response_bytes,
484
+ operation_timeout_seconds=self._operation_timeout_seconds,
485
+ )
486
+ return httpx.AsyncClient(
487
+ transport=transport,
488
+ follow_redirects=False,
489
+ trust_env=False,
490
+ timeout=httpx.Timeout(
491
+ connect=self._connect_timeout_seconds,
492
+ read=self._read_timeout_seconds,
493
+ write=self._read_timeout_seconds,
494
+ pool=self._connect_timeout_seconds,
495
+ ),
496
+ )
497
+
498
+
499
+ def _canonical_url(value: str) -> tuple[str, str, int | None, str, str]:
500
+ parsed = urlsplit(value)
501
+ if parsed.scheme not in {"http", "https"} or not parsed.hostname:
502
+ raise ValueError("external A2A route must use an absolute HTTP(S) URL")
503
+ default_port = 443 if parsed.scheme == "https" else 80
504
+ port = parsed.port or default_port
505
+ return parsed.scheme, parsed.hostname.lower(), port, parsed.path or "/", parsed.query
506
+
507
+
508
+ __all__ = [
509
+ "A2AExternalTransport",
510
+ "A2ARouteOpener",
511
+ "A2ATransportLease",
512
+ "CallableA2ARouteOpener",
513
+ "ERR_VPC_EGRESS_DIALER_REQUIRED",
514
+ "GuardedA2AExternalTransport",
515
+ "RuntimeLocalA2AExternalTransport",
516
+ ]