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.
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/METADATA +52 -20
- agentengine_sdk_python-0.8.1.dist-info/RECORD +644 -0
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/WHEEL +1 -1
- ksadk/a2a/__init__.py +180 -11
- ksadk/a2a/bootstrap.py +332 -0
- ksadk/a2a/card.py +15 -0
- ksadk/a2a/context_store.py +203 -0
- ksadk/a2a/control_plane.py +1056 -0
- ksadk/a2a/credential.py +99 -0
- ksadk/a2a/event_adapter.py +149 -0
- ksadk/a2a/executor.py +361 -137
- ksadk/a2a/external_transport.py +516 -0
- ksadk/a2a/identity.py +304 -0
- ksadk/a2a/ids.py +22 -0
- ksadk/a2a/langgraph.py +285 -0
- ksadk/a2a/resume_store.py +201 -0
- ksadk/a2a/routes.py +112 -0
- ksadk/a2a/server.py +102 -62
- ksadk/a2a/service_env.py +33 -0
- ksadk/a2a/space_client.py +1122 -0
- ksadk/a2a/task_adapter.py +423 -0
- ksadk/a2a/task_event_dispatcher.py +167 -0
- ksadk/a2a/task_event_outbox.py +326 -0
- ksadk/a2a/task_store.py +206 -0
- ksadk/a2a_card.py +101 -0
- ksadk/a2ui/__init__.py +27 -0
- ksadk/a2ui/core.py +186 -0
- ksadk/a2ui/models.py +166 -0
- ksadk/a2ui/renderer.py +198 -0
- ksadk/agui/__init__.py +9 -0
- ksadk/agui/a2ui_projection.py +92 -0
- ksadk/agui/agent.py +872 -0
- ksadk/agui/config.py +92 -0
- ksadk/agui/routes.py +47 -0
- ksadk/api/client.py +238 -204
- ksadk/api/environment.py +27 -0
- ksadk/builders/__init__.py +2 -0
- ksadk/builders/base.py +16 -14
- ksadk/builders/code_builder.py +375 -270
- ksadk/builders/container_builder.py +240 -200
- ksadk/builders/framework_requirements.py +31 -12
- ksadk/builders/ks3_uploader.py +60 -29
- ksadk/builders/managed_runtime_builder.py +173 -0
- ksadk/builders/mcp_builder.py +91 -92
- ksadk/builders/requirements_utils.py +1 -5
- ksadk/cli/__init__.py +97 -171
- ksadk/cli/cmd_a2a.py +255 -84
- ksadk/cli/cmd_agent.py +19 -5
- ksadk/cli/cmd_build.py +158 -29
- ksadk/cli/cmd_completion.py +47 -35
- ksadk/cli/cmd_config.py +267 -198
- ksadk/cli/cmd_create.py +507 -740
- ksadk/cli/cmd_dashboard.py +120 -45
- ksadk/cli/cmd_deploy.py +135 -59
- ksadk/cli/cmd_destroy.py +44 -14
- ksadk/cli/cmd_eval.py +386 -0
- ksadk/cli/cmd_files.py +63 -24
- ksadk/cli/cmd_hermes.py +313 -135
- ksadk/cli/cmd_invoke.py +184 -122
- ksadk/cli/cmd_launch.py +76 -28
- ksadk/cli/cmd_mcp.py +98 -64
- ksadk/cli/cmd_model.py +14 -5
- ksadk/cli/cmd_openclaw.py +564 -268
- ksadk/cli/cmd_replay.py +71 -0
- ksadk/cli/cmd_run.py +58 -73
- ksadk/cli/cmd_status.py +80 -31
- ksadk/cli/cmd_studio.py +110 -0
- ksadk/cli/cmd_version.py +20 -13
- ksadk/cli/cmd_web.py +95 -18
- ksadk/cli/deploy_utils.py +20 -20
- ksadk/cli/dry_run.py +7 -4
- ksadk/cli/env_options.py +83 -0
- ksadk/cli/error_utils.py +37 -12
- ksadk/cli/global_options.py +4 -3
- ksadk/cli/local_runtime.py +1 -2
- ksadk/cli/model_catalog.py +6 -2
- ksadk/cli/network_options.py +41 -19
- ksadk/cli/resource_common.py +6 -2
- ksadk/cli/runtime_bootstrap.py +51 -0
- ksadk/cli/storage.py +0 -1
- ksadk/cli/ui.py +11 -3
- ksadk/cli/workflow_common.py +52 -18
- ksadk/codex/__init__.py +12 -0
- ksadk/codex/client.py +1042 -0
- ksadk/codex/phase.py +143 -0
- ksadk/codex/runtime.py +940 -0
- ksadk/common/aicp_env.py +0 -1
- ksadk/common/auth.py +2 -2
- ksadk/common/constants.py +1 -3
- ksadk/common/kop_client.py +232 -0
- ksadk/common/llm_utils.py +17 -13
- ksadk/compat/__init__.py +5 -0
- ksadk/compat/adk_compat.py +200 -0
- ksadk/compat/copilotkit_a2ui.py +330 -0
- ksadk/configs/__init__.py +12 -14
- ksadk/configs/env_registry.py +661 -116
- ksadk/configs/global_config.py +3 -2
- ksadk/configs/settings.py +133 -209
- ksadk/conversations/__init__.py +85 -92
- ksadk/conversations/attachment_storage.py +16 -11
- ksadk/conversations/attachments.py +44 -36
- ksadk/conversations/compaction_pipeline.py +12 -12
- ksadk/conversations/compaction_prompt.py +9 -5
- ksadk/conversations/context.py +221 -6
- ksadk/conversations/message_projection.py +438 -51
- ksadk/conversations/model_context.py +12 -6
- ksadk/conversations/model_options.py +0 -1
- ksadk/conversations/normalize.py +20 -16
- ksadk/conversations/reasoning_markup.py +1 -1
- ksadk/conversations/runtime.py +339 -5255
- ksadk/conversations/runtime_compaction.py +333 -0
- ksadk/conversations/runtime_constants.py +27 -0
- ksadk/conversations/runtime_governance.py +185 -0
- ksadk/conversations/runtime_input.py +660 -0
- ksadk/conversations/runtime_invocation.py +395 -0
- ksadk/conversations/runtime_metadata.py +475 -0
- ksadk/conversations/runtime_observability.py +367 -0
- ksadk/conversations/runtime_payloads.py +272 -0
- ksadk/conversations/runtime_persistence.py +440 -0
- ksadk/conversations/runtime_preparation.py +426 -0
- ksadk/conversations/runtime_resume.py +836 -0
- ksadk/conversations/runtime_stream_events.py +902 -0
- ksadk/conversations/runtime_streaming.py +700 -0
- ksadk/conversations/semantic_summary.py +17 -5
- ksadk/conversations/session_title.py +6 -3
- ksadk/deployment/__init__.py +15 -16
- ksadk/deployment/agent_access.py +12 -23
- ksadk/deployment/base.py +51 -38
- ksadk/deployment/managed_runtime.py +33 -0
- ksadk/deployment/manager.py +85 -71
- ksadk/deployment/providers/serverless.py +379 -210
- ksadk/deployment/registry.py +38 -32
- ksadk/deployment/state.py +11 -11
- ksadk/deployment/ui_config.py +1 -1
- ksadk/detection/__init__.py +1 -1
- ksadk/detection/detector.py +146 -83
- ksadk/detection/mcp_detector.py +9 -6
- ksadk/evaluation/__init__.py +84 -0
- ksadk/evaluation/a2a_adapter.py +444 -0
- ksadk/evaluation/adapters.py +54 -0
- ksadk/evaluation/contracts.py +409 -0
- ksadk/evaluation/evalset.py +351 -0
- ksadk/evaluation/evaluators.py +461 -0
- ksadk/evaluation/executor.py +118 -0
- ksadk/evaluation/storage.py +68 -0
- ksadk/evaluation/target.py +73 -0
- ksadk/events/__init__.py +25 -0
- ksadk/events/parser.py +191 -0
- ksadk/events/replay.py +37 -0
- ksadk/events/runtime_event.py +273 -0
- ksadk/events/store.py +317 -0
- ksadk/harness/__init__.py +35 -0
- ksadk/harness/app.py +269 -0
- ksadk/harness/config.py +181 -0
- ksadk/harness/reasoner.py +105 -0
- ksadk/harness/runtime.py +364 -0
- ksadk/harness/sandbox.py +141 -0
- ksadk/harness/tools.py +222 -0
- ksadk/hermes_terminal.py +28 -12
- ksadk/identity/resolver.py +15 -9
- ksadk/ids.py +32 -0
- ksadk/knowledge_base/adk_tool.py +1 -1
- ksadk/knowledge_base/client.py +38 -33
- ksadk/managed_a2a_card.py +160 -0
- ksadk/managed_runtime.py +195 -0
- ksadk/markdown.py +5 -2
- ksadk/mcp_runtime/__init__.py +9 -6
- ksadk/memory/__init__.py +1 -1
- ksadk/memory/adk/__init__.py +2 -0
- ksadk/memory/adk/backends/base_ltm_backend.py +2 -6
- ksadk/memory/adk/backends/http_ltm_backend.py +13 -21
- ksadk/memory/adk/backends/inmemory_ltm_backend.py +7 -13
- ksadk/memory/adk/backends/sdk_ltm_backend.py +36 -36
- ksadk/memory/adk/long_term_memory.py +34 -37
- ksadk/memory/adk/resilient_session_service.py +4 -5
- ksadk/memory/adk/short_term_memory.py +12 -21
- ksadk/memory/adk_tool.py +3 -2
- ksadk/memory/backends/base.py +19 -19
- ksadk/memory/backends/memory.py +24 -25
- ksadk/memory/backends/redis.py +31 -30
- ksadk/memory/langchain_tool.py +2 -1
- ksadk/memory/manager.py +52 -47
- ksadk/memory/service.py +16 -6
- ksadk/memory/tool.py +4 -0
- ksadk/model_policy.py +15 -7
- ksadk/model_proxy/__init__.py +51 -0
- ksadk/model_proxy/bootstrap.py +80 -0
- ksadk/model_proxy/cache.py +165 -0
- ksadk/model_proxy/config.py +66 -0
- ksadk/model_proxy/demo_webui.py +115 -0
- ksadk/model_proxy/detect.py +145 -0
- ksadk/model_proxy/gate.py +68 -0
- ksadk/model_proxy/namespace.py +201 -0
- ksadk/model_proxy/protocol_proxy.py +22 -0
- ksadk/model_proxy/server.py +443 -0
- ksadk/model_proxy/transform.py +735 -0
- ksadk/openclaw_gateway.py +29 -14
- ksadk/runners/adk_runner.py +354 -146
- ksadk/runners/base_runner.py +71 -29
- ksadk/runners/deepagents_runner.py +0 -1
- ksadk/runners/factory.py +18 -5
- ksadk/runners/langchain_runner.py +29 -742
- ksadk/runners/langgraph_runner.py +417 -97
- ksadk/runners/patch_langchain.py +16 -16
- ksadk/runners/remote_runner.py +134 -33
- ksadk/runners/usage_accumulator.py +1 -0
- ksadk/runners/utils/__init__.py +0 -10
- ksadk/runners/utils/loader.py +23 -7
- ksadk/runtime/__init__.py +67 -0
- ksadk/runtime/adapter.py +412 -0
- ksadk/runtime/conversation_execution.py +553 -0
- ksadk/runtime/executor.py +251 -0
- ksadk/runtime/factory.py +156 -0
- ksadk/runtime/framework_adapters.py +118 -0
- ksadk/runtime/launch.py +48 -0
- ksadk/runtime/preprocessing.py +131 -0
- ksadk/runtime/runner_adapter.py +1032 -0
- ksadk/runtime/runner_loading.py +36 -0
- ksadk/runtime_context.py +18 -3
- ksadk/runtime_state.py +0 -1
- ksadk/sandbox/__init__.py +4 -1
- ksadk/sandbox/backends/__init__.py +4 -1
- ksadk/sandbox/backends/e2b.py +13 -5
- ksadk/sandbox/backends/local_process.py +11 -3
- ksadk/sandbox/base.py +10 -15
- ksadk/sandbox/factory.py +14 -4
- ksadk/sandbox/registry.py +57 -6
- ksadk/server/__init__.py +39 -4
- ksadk/server/api_models.py +15 -5
- ksadk/server/app.py +24 -3928
- ksadk/server/composition.py +47 -0
- ksadk/server/factory.py +622 -0
- ksadk/server/routes/__init__.py +1 -0
- ksadk/server/routes/checkpoint_resolution.py +116 -0
- ksadk/server/routes/common.py +682 -0
- ksadk/server/routes/control.py +331 -0
- ksadk/server/routes/dependencies.py +98 -0
- ksadk/server/routes/misc.py +425 -0
- ksadk/server/routes/models.py +451 -0
- ksadk/server/routes/openai_compat.py +229 -0
- ksadk/server/routes/projection.py +848 -0
- ksadk/server/routes/routers.py +52 -0
- ksadk/server/routes/run.py +519 -0
- ksadk/server/routes/sessions.py +644 -0
- ksadk/server/routes/streaming.py +265 -0
- ksadk/server/routes/workspace.py +414 -0
- ksadk/server/static/assets/{ArtifactsPanel-D-SSn2ee.js → ArtifactsPanel-CakCqxEN.js} +1 -1
- ksadk/server/static/assets/CodeBlock-CynLknyO.js +9 -0
- ksadk/server/static/assets/MathMessageMarkdown-3sQdOysS.js +8 -0
- ksadk/server/static/assets/MermaidBlock-NERD_ef7.js +303 -0
- ksadk/server/static/assets/NativeTerminalPanel-CiHWf82q.js +1 -0
- ksadk/server/static/assets/{arc-BA13sHc9.js → arc-DrgjcKhl.js} +1 -1
- ksadk/server/static/assets/architecture-7EHR7CIX-CpZLRCcY.js +1 -0
- ksadk/server/static/assets/{architectureDiagram-3BPJPVTR-DzJzgVoL.js → architectureDiagram-3BPJPVTR-BXoVRbQ6.js} +1 -1
- ksadk/server/static/assets/{blockDiagram-GPEHLZMM-UTdVcZhn.js → blockDiagram-GPEHLZMM-BnaB3Ywv.js} +1 -1
- ksadk/server/static/assets/{c4Diagram-AAUBKEIU-Dix0GD4m.js → c4Diagram-AAUBKEIU-Dw4weZcp.js} +1 -1
- ksadk/server/static/assets/channel-Dt3rQTFI.js +1 -0
- ksadk/server/static/assets/{chunk-2J33WTMH-C2JxuZ42.js → chunk-2J33WTMH-ChjpLK7j.js} +1 -1
- ksadk/server/static/assets/{chunk-4BX2VUAB-Be57QPgD.js → chunk-4BX2VUAB-B9drTgOW.js} +1 -1
- ksadk/server/static/assets/{chunk-55IACEB6-WjSuy4nv.js → chunk-55IACEB6-CK4ROATy.js} +1 -1
- ksadk/server/static/assets/{chunk-727SXJPM-BOovP-b5.js → chunk-727SXJPM-B9KtTTgw.js} +1 -1
- ksadk/server/static/assets/{chunk-AQP2D5EJ-DOmZNmWj.js → chunk-AQP2D5EJ-1O1mki3m.js} +1 -1
- ksadk/server/static/assets/{chunk-FMBD7UC4-DzwlodkU.js → chunk-FMBD7UC4-B_fH2YuH.js} +1 -1
- ksadk/server/static/assets/{chunk-ND2GUHAM-DMqAikEU.js → chunk-ND2GUHAM-D6rFLaVV.js} +1 -1
- ksadk/server/static/assets/chunk-QZHKN3VN-iXEZtKQc.js +1 -0
- ksadk/server/static/assets/classDiagram-4FO5ZUOK-DIveV17Q.js +1 -0
- ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-DIveV17Q.js +1 -0
- ksadk/server/static/assets/{cose-bilkent-S5V4N54A-Zhy2GBnP.js → cose-bilkent-S5V4N54A-ChNs_Mdk.js} +1 -1
- ksadk/server/static/assets/{dagre-BM42HDAG-CbKrtIUz.js → dagre-BM42HDAG-D0QXg_MQ.js} +1 -1
- ksadk/server/static/assets/{diagram-2AECGRRQ-BRApgQRK.js → diagram-2AECGRRQ-DBDDKwBQ.js} +1 -1
- ksadk/server/static/assets/{diagram-5GNKFQAL-D0xbnDiK.js → diagram-5GNKFQAL-BkwDOxmO.js} +1 -1
- ksadk/server/static/assets/{diagram-KO2AKTUF-CVugVI9L.js → diagram-KO2AKTUF-Bv8waYvY.js} +1 -1
- ksadk/server/static/assets/{diagram-LMA3HP47-DozUGgee.js → diagram-LMA3HP47-e1VECDdu.js} +1 -1
- ksadk/server/static/assets/{diagram-OG6HWLK6-ZOYsKlru.js → diagram-OG6HWLK6-Ds9adfRl.js} +1 -1
- ksadk/server/static/assets/{dist-DpKtt4Bo.js → dist-4N2NMVYg.js} +1 -1
- ksadk/server/static/assets/{dist-CkEKJFtk.js → dist-B-jNR6Vn.js} +1 -1
- ksadk/server/static/assets/{dist-D57wnGWd.js → dist-B5J2uhNk.js} +1 -1
- ksadk/server/static/assets/{dist-8_SfK1IP.js → dist-B5oS1zcj.js} +1 -1
- ksadk/server/static/assets/{dist-DMezvBGJ.js → dist-BB3-OwTQ.js} +1 -1
- ksadk/server/static/assets/{dist-2hInLS_J.js → dist-BIlKoW0k.js} +1 -1
- ksadk/server/static/assets/{dist-BrCcQ47T.js → dist-BjOdlDjj.js} +1 -1
- ksadk/server/static/assets/{dist-BmlMPsCc.js → dist-C1sz5Eo3.js} +1 -1
- ksadk/server/static/assets/{dist-B0NP_Z0z.js → dist-CWV4lDr1.js} +1 -1
- ksadk/server/static/assets/{dist-D-xOD048.js → dist-CmZHzlwZ.js} +1 -1
- ksadk/server/static/assets/{dist-DL_NKc_z.js → dist-D6MpCZtz.js} +1 -1
- ksadk/server/static/assets/{dist-DyOkZmze.js → dist-DJN_6Y39.js} +1 -1
- ksadk/server/static/assets/{dist-DmFaq_Iy.js → dist-DQU9EWw-.js} +1 -1
- ksadk/server/static/assets/{dist-gX6GOLKq.js → dist-DWKByxWV.js} +1 -1
- ksadk/server/static/assets/{dist-DyWsb78Q.js → dist-Dli7qW-B.js} +1 -1
- ksadk/server/static/assets/{dist-BIJVDMuB.js → dist-DsHPdk3W.js} +1 -1
- ksadk/server/static/assets/{dist-Dp0k7bWI.js → dist-X4TnqwOK.js} +1 -1
- ksadk/server/static/assets/{dist-8EEyGgDx.js → dist-_pHPTyZ-.js} +1 -1
- ksadk/server/static/assets/{dist-CPBtqRTW.js → dist-fhOuWds5.js} +1 -1
- ksadk/server/static/assets/{dist-D-JMdWPQ.js → dist-gLnrGsoU.js} +1 -1
- ksadk/server/static/assets/{dist-BssVmjIo.js → dist-j6Dxmf_j.js} +1 -1
- ksadk/server/static/assets/{dist-C3HIIaN5.js → dist-o2OVmKvt.js} +1 -1
- ksadk/server/static/assets/{erDiagram-TEJ5UH35-Bk-9bgE5.js → erDiagram-TEJ5UH35-BPItFKH6.js} +1 -1
- ksadk/server/static/assets/eventmodeling-FCH6USID-DYFSxbqr.js +1 -0
- ksadk/server/static/assets/{flowDiagram-I6XJVG4X-Dn9kZVuZ.js → flowDiagram-I6XJVG4X-BbYB9w5Y.js} +1 -1
- ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BGxXlJqm.js → ganttDiagram-6RSMTGT7-TQXa0KXv.js} +1 -1
- ksadk/server/static/assets/gitGraph-WXDBUCRP-DyNYp6BN.js +1 -0
- ksadk/server/static/assets/{gitGraphDiagram-PVQCEYII-C7Q0X-wi.js → gitGraphDiagram-PVQCEYII-bKblCnpa.js} +1 -1
- ksadk/server/static/assets/index-R52pJJvg.js +177 -0
- ksadk/server/static/assets/index-jJr3_955.css +2 -0
- ksadk/server/static/assets/info-J43DQDTF-WUx7qTde.js +1 -0
- ksadk/server/static/assets/{infoDiagram-5YYISTIA-BO_CSqq5.js → infoDiagram-5YYISTIA-BGVT7J_i.js} +1 -1
- ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-eYLPLd46.js → ishikawaDiagram-YF4QCWOH-K0OOfVna.js} +1 -1
- ksadk/server/static/assets/{journeyDiagram-JHISSGLW-De9geSb0.js → journeyDiagram-JHISSGLW-8LeOJaZp.js} +1 -1
- ksadk/server/static/assets/{kanban-definition-UN3LZRKU-Cs80kQto.js → kanban-definition-UN3LZRKU-C6uCgZrW.js} +1 -1
- ksadk/server/static/assets/{katex-DSrCFD8n.js → katex-CYWseY5E.js} +1 -1
- ksadk/server/static/assets/{linear-CN9sbKTF.js → linear-hdBcYzM8.js} +1 -1
- ksadk/server/static/assets/{mermaid-parser.core-DlgSBU8l.js → mermaid-parser.core-BWrDjSZM.js} +2 -2
- ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-9afz7Oug.js → mindmap-definition-RKZ34NQL-Bjy_2v4_.js} +1 -1
- ksadk/server/static/assets/packet-YPE3B663-BN13y_xl.js +1 -0
- ksadk/server/static/assets/pie-LRSECV5Y-ftSLXhhc.js +1 -0
- ksadk/server/static/assets/{pieDiagram-4H26LBE5-DPv_VstN.js → pieDiagram-4H26LBE5-GmmqNiVH.js} +1 -1
- ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-o77wZ1XK.js → quadrantDiagram-W4KKPZXB-BmkRbwCg.js} +1 -1
- ksadk/server/static/assets/radar-GUYGQ44K-jaICkIeO.js +1 -0
- ksadk/server/static/assets/{requirementDiagram-4Y6WPE33-BYVw81VC.js → requirementDiagram-4Y6WPE33-DSWFqL7c.js} +1 -1
- ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-B7nZFMKT.js → sankeyDiagram-5OEKKPKP-l6iXmU65.js} +1 -1
- ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-BGRRrFw5.js → sequenceDiagram-3UESZ5HK-Cq6UBeQB.js} +1 -1
- ksadk/server/static/assets/{stateDiagram-AJRCARHV-BUfHCoPs.js → stateDiagram-AJRCARHV-qzTQpg3Y.js} +1 -1
- ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-BYCoYGAN.js +1 -0
- ksadk/server/static/assets/{timeline-definition-PNZ67QCA-FbCTGrf9.js → timeline-definition-PNZ67QCA-DEz24vV0.js} +1 -1
- ksadk/server/static/assets/treeView-BLDUP644-Cpib_vlz.js +1 -0
- ksadk/server/static/assets/treemap-LRROVOQU-DQxfmVHK.js +1 -0
- ksadk/server/static/assets/{vennDiagram-CIIHVFJN-DWgwtPsJ.js → vennDiagram-CIIHVFJN-CMCixki2.js} +1 -1
- ksadk/server/static/assets/wardley-L42UT6IY-BQYHnnkU.js +1 -0
- ksadk/server/static/assets/{wardleyDiagram-YWT4CUSO-2x8G-tTM.js → wardleyDiagram-YWT4CUSO-CVzB8F9K.js} +1 -1
- ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-Drma3AYl.js → xychartDiagram-2RQKCTM6-BFBqBLc1.js} +1 -1
- ksadk/server/static/index.html +2 -2
- ksadk/server/terminal_sessions.py +111 -58
- ksadk/sessions/__init__.py +69 -19
- ksadk/sessions/base.py +31 -11
- ksadk/sessions/continuity.py +2 -3
- ksadk/sessions/in_memory.py +46 -2
- ksadk/sessions/local_service.py +148 -17
- ksadk/sessions/postgres_service.py +143 -25
- ksadk/sessions/resilient.py +36 -8
- ksadk/skills/models.py +17 -5
- ksadk/skills/package_store.py +5 -2
- ksadk/skills/runtime/agent.py +20 -5
- ksadk/skills/runtime/artifacts.py +1 -5
- ksadk/skills/runtime/backends/e2b.py +10 -4
- ksadk/skills/runtime/backends/local.py +15 -3
- ksadk/skills/runtime/base.py +1 -2
- ksadk/skills/runtime/executor.py +10 -3
- ksadk/skills/runtime/factory.py +4 -2
- ksadk/skills/runtime/loader.py +1 -2
- ksadk/skills/runtime/registry.py +15 -10
- ksadk/skills/runtime/request.py +3 -1
- ksadk/skills/service_client.py +47 -16
- ksadk/skills/tool_defs.py +15 -10
- ksadk/studio/__init__.py +5 -0
- ksadk/studio/agent_avatar_assets.py +120 -0
- ksadk/studio/agent_lifecycle.py +51 -0
- ksadk/studio/api.py +1046 -0
- ksadk/studio/api_catalog_routes.py +324 -0
- ksadk/studio/api_contracts.py +177 -0
- ksadk/studio/api_helpers.py +132 -0
- ksadk/studio/authoring.py +524 -0
- ksadk/studio/authoring_coordinator.py +285 -0
- ksadk/studio/builder.py +320 -0
- ksadk/studio/capabilities.py +351 -0
- ksadk/studio/cloud.py +278 -0
- ksadk/studio/codex_agent_service.py +583 -0
- ksadk/studio/codex_builder.py +324 -0
- ksadk/studio/codex_manifest.py +278 -0
- ksadk/studio/codex_run.py +352 -0
- ksadk/studio/compiler.py +189 -0
- ksadk/studio/contracts.py +644 -0
- ksadk/studio/errors.py +47 -0
- ksadk/studio/evaluation.py +192 -0
- ksadk/studio/event_store.py +284 -0
- ksadk/studio/framework_run.py +114 -0
- ksadk/studio/identifiers.py +33 -0
- ksadk/studio/mcp_runtime.py +229 -0
- ksadk/studio/model_client.py +543 -0
- ksadk/studio/model_profile_service.py +214 -0
- ksadk/studio/operations.py +243 -0
- ksadk/studio/otel_trace.py +754 -0
- ksadk/studio/pagination.py +182 -0
- ksadk/studio/python_tool_inspection.py +183 -0
- ksadk/studio/repository.py +306 -0
- ksadk/studio/resource_catalog.py +1217 -0
- ksadk/studio/run_service.py +707 -0
- ksadk/studio/runtime_catalog.py +74 -0
- ksadk/studio/runtime_source.py +339 -0
- ksadk/studio/service.py +1141 -0
- ksadk/studio/shared_web.py +994 -0
- ksadk/studio/skill_discovery.py +628 -0
- ksadk/studio/static/assets/PrismRenderer-IYN-ffww.js +3 -0
- ksadk/studio/static/assets/index-CaEN0eTR.css +1 -0
- ksadk/studio/static/assets/index-DI2eY_NH.js +252 -0
- ksadk/studio/static/assets/react-vendor-BNmTiJ-I.js +9 -0
- ksadk/studio/static/assets/rolldown-runtime-hePW80VL.js +1 -0
- ksadk/studio/static/index.html +15 -0
- ksadk/studio/templates.py +630 -0
- ksadk/studio/validator.py +246 -0
- ksadk/studio/workspace.py +137 -0
- ksadk/terminal_exec_policy.py +3 -4
- ksadk/tools/gateway.py +101 -15
- ksadk/tools/result_budget.py +12 -4
- ksadk/toolsets/__init__.py +161 -37
- ksadk/toolsets/a2a.py +426 -0
- ksadk/toolsets/platform.py +21 -5
- ksadk/toolsets/sandbox.py +82 -16
- ksadk/toolsets/skills.py +200 -39
- ksadk/toolsets/web.py +123 -26
- ksadk/toolsets/workspace.py +212 -101
- ksadk/tracing/__init__.py +19 -21
- ksadk/tracing/exporters/inmemory_exporter.py +62 -51
- ksadk/tracing/setup.py +135 -506
- ksadk/tracing/span_utils.py +0 -41
- ksadk/tui/loop.py +78 -30
- ksadk/tui/stream_render.py +1 -0
- ksadk/ui_config.py +5 -3
- ksadk/version.py +1 -1
- ksadk_runtime_common/memory_backend/registry.py +56 -56
- ksadk_runtime_common/schemas/runtime_event_v1.json +95 -0
- ksadk_runtime_common/workspace_files/router.py +8 -2
- agentengine_sdk_python-0.7.0.dist-info/RECORD +0 -482
- ksadk/a2a/card_builder.py +0 -64
- ksadk/a2a/client.py +0 -309
- ksadk/runners/utils/langfuse.py +0 -230
- ksadk/server/static/assets/CodeBlock-P16VOLYi.js +0 -9
- ksadk/server/static/assets/MathMessageMarkdown-CdGiQWHR.js +0 -8
- ksadk/server/static/assets/MermaidBlock-IOcal8cm.js +0 -303
- ksadk/server/static/assets/NativeTerminalPanel-DGSDWsei.js +0 -1
- ksadk/server/static/assets/architecture-7EHR7CIX-BvUlsA6l.js +0 -1
- ksadk/server/static/assets/channel-CTuydARX.js +0 -1
- ksadk/server/static/assets/chunk-QZHKN3VN-BnCydivS.js +0 -1
- ksadk/server/static/assets/classDiagram-4FO5ZUOK-BNHg45Be.js +0 -1
- ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-BNHg45Be.js +0 -1
- ksadk/server/static/assets/eventmodeling-FCH6USID-DsAgWnlf.js +0 -1
- ksadk/server/static/assets/gitGraph-WXDBUCRP-BIao9Yjg.js +0 -1
- ksadk/server/static/assets/index-C00Lwg3L.js +0 -165
- ksadk/server/static/assets/index-DAURbCsP.css +0 -1
- ksadk/server/static/assets/info-J43DQDTF-CyipqGxU.js +0 -1
- ksadk/server/static/assets/packet-YPE3B663-CGkyRxlg.js +0 -1
- ksadk/server/static/assets/pie-LRSECV5Y-M0uWAf4n.js +0 -1
- ksadk/server/static/assets/radar-GUYGQ44K-Bdbm24-x.js +0 -1
- ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-8xBN_gLM.js +0 -1
- ksadk/server/static/assets/treeView-BLDUP644-npMo0ak1.js +0 -1
- ksadk/server/static/assets/treemap-LRROVOQU-CHdzrrdo.js +0 -1
- ksadk/server/static/assets/wardley-L42UT6IY-BEQz7wyy.js +0 -1
- ksadk/tracing/exporters/langfuse_exporter.py +0 -445
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/entry_points.txt +0 -0
- {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/licenses/LICENSE +0 -0
- {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
|
+
}
|
ksadk/toolsets/platform.py
CHANGED
|
@@ -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":
|
|
38
|
-
|
|
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(
|
|
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(
|
|
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":
|
|
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(
|
|
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
|
|
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(
|
|
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 =
|
|
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
|
|
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 {
|
|
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
|
|
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
|
|
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":
|
|
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
|
|
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
|
|
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
|
|
306
|
-
|
|
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:
|