agentengine-sdk-python 0.7.0__py3-none-any.whl → 0.8.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (450) hide show
  1. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/METADATA +52 -20
  2. agentengine_sdk_python-0.8.1.dist-info/RECORD +644 -0
  3. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/WHEEL +1 -1
  4. ksadk/a2a/__init__.py +180 -11
  5. ksadk/a2a/bootstrap.py +332 -0
  6. ksadk/a2a/card.py +15 -0
  7. ksadk/a2a/context_store.py +203 -0
  8. ksadk/a2a/control_plane.py +1056 -0
  9. ksadk/a2a/credential.py +99 -0
  10. ksadk/a2a/event_adapter.py +149 -0
  11. ksadk/a2a/executor.py +361 -137
  12. ksadk/a2a/external_transport.py +516 -0
  13. ksadk/a2a/identity.py +304 -0
  14. ksadk/a2a/ids.py +22 -0
  15. ksadk/a2a/langgraph.py +285 -0
  16. ksadk/a2a/resume_store.py +201 -0
  17. ksadk/a2a/routes.py +112 -0
  18. ksadk/a2a/server.py +102 -62
  19. ksadk/a2a/service_env.py +33 -0
  20. ksadk/a2a/space_client.py +1122 -0
  21. ksadk/a2a/task_adapter.py +423 -0
  22. ksadk/a2a/task_event_dispatcher.py +167 -0
  23. ksadk/a2a/task_event_outbox.py +326 -0
  24. ksadk/a2a/task_store.py +206 -0
  25. ksadk/a2a_card.py +101 -0
  26. ksadk/a2ui/__init__.py +27 -0
  27. ksadk/a2ui/core.py +186 -0
  28. ksadk/a2ui/models.py +166 -0
  29. ksadk/a2ui/renderer.py +198 -0
  30. ksadk/agui/__init__.py +9 -0
  31. ksadk/agui/a2ui_projection.py +92 -0
  32. ksadk/agui/agent.py +872 -0
  33. ksadk/agui/config.py +92 -0
  34. ksadk/agui/routes.py +47 -0
  35. ksadk/api/client.py +238 -204
  36. ksadk/api/environment.py +27 -0
  37. ksadk/builders/__init__.py +2 -0
  38. ksadk/builders/base.py +16 -14
  39. ksadk/builders/code_builder.py +375 -270
  40. ksadk/builders/container_builder.py +240 -200
  41. ksadk/builders/framework_requirements.py +31 -12
  42. ksadk/builders/ks3_uploader.py +60 -29
  43. ksadk/builders/managed_runtime_builder.py +173 -0
  44. ksadk/builders/mcp_builder.py +91 -92
  45. ksadk/builders/requirements_utils.py +1 -5
  46. ksadk/cli/__init__.py +97 -171
  47. ksadk/cli/cmd_a2a.py +255 -84
  48. ksadk/cli/cmd_agent.py +19 -5
  49. ksadk/cli/cmd_build.py +158 -29
  50. ksadk/cli/cmd_completion.py +47 -35
  51. ksadk/cli/cmd_config.py +267 -198
  52. ksadk/cli/cmd_create.py +507 -740
  53. ksadk/cli/cmd_dashboard.py +120 -45
  54. ksadk/cli/cmd_deploy.py +135 -59
  55. ksadk/cli/cmd_destroy.py +44 -14
  56. ksadk/cli/cmd_eval.py +386 -0
  57. ksadk/cli/cmd_files.py +63 -24
  58. ksadk/cli/cmd_hermes.py +313 -135
  59. ksadk/cli/cmd_invoke.py +184 -122
  60. ksadk/cli/cmd_launch.py +76 -28
  61. ksadk/cli/cmd_mcp.py +98 -64
  62. ksadk/cli/cmd_model.py +14 -5
  63. ksadk/cli/cmd_openclaw.py +564 -268
  64. ksadk/cli/cmd_replay.py +71 -0
  65. ksadk/cli/cmd_run.py +58 -73
  66. ksadk/cli/cmd_status.py +80 -31
  67. ksadk/cli/cmd_studio.py +110 -0
  68. ksadk/cli/cmd_version.py +20 -13
  69. ksadk/cli/cmd_web.py +95 -18
  70. ksadk/cli/deploy_utils.py +20 -20
  71. ksadk/cli/dry_run.py +7 -4
  72. ksadk/cli/env_options.py +83 -0
  73. ksadk/cli/error_utils.py +37 -12
  74. ksadk/cli/global_options.py +4 -3
  75. ksadk/cli/local_runtime.py +1 -2
  76. ksadk/cli/model_catalog.py +6 -2
  77. ksadk/cli/network_options.py +41 -19
  78. ksadk/cli/resource_common.py +6 -2
  79. ksadk/cli/runtime_bootstrap.py +51 -0
  80. ksadk/cli/storage.py +0 -1
  81. ksadk/cli/ui.py +11 -3
  82. ksadk/cli/workflow_common.py +52 -18
  83. ksadk/codex/__init__.py +12 -0
  84. ksadk/codex/client.py +1042 -0
  85. ksadk/codex/phase.py +143 -0
  86. ksadk/codex/runtime.py +940 -0
  87. ksadk/common/aicp_env.py +0 -1
  88. ksadk/common/auth.py +2 -2
  89. ksadk/common/constants.py +1 -3
  90. ksadk/common/kop_client.py +232 -0
  91. ksadk/common/llm_utils.py +17 -13
  92. ksadk/compat/__init__.py +5 -0
  93. ksadk/compat/adk_compat.py +200 -0
  94. ksadk/compat/copilotkit_a2ui.py +330 -0
  95. ksadk/configs/__init__.py +12 -14
  96. ksadk/configs/env_registry.py +661 -116
  97. ksadk/configs/global_config.py +3 -2
  98. ksadk/configs/settings.py +133 -209
  99. ksadk/conversations/__init__.py +85 -92
  100. ksadk/conversations/attachment_storage.py +16 -11
  101. ksadk/conversations/attachments.py +44 -36
  102. ksadk/conversations/compaction_pipeline.py +12 -12
  103. ksadk/conversations/compaction_prompt.py +9 -5
  104. ksadk/conversations/context.py +221 -6
  105. ksadk/conversations/message_projection.py +438 -51
  106. ksadk/conversations/model_context.py +12 -6
  107. ksadk/conversations/model_options.py +0 -1
  108. ksadk/conversations/normalize.py +20 -16
  109. ksadk/conversations/reasoning_markup.py +1 -1
  110. ksadk/conversations/runtime.py +339 -5255
  111. ksadk/conversations/runtime_compaction.py +333 -0
  112. ksadk/conversations/runtime_constants.py +27 -0
  113. ksadk/conversations/runtime_governance.py +185 -0
  114. ksadk/conversations/runtime_input.py +660 -0
  115. ksadk/conversations/runtime_invocation.py +395 -0
  116. ksadk/conversations/runtime_metadata.py +475 -0
  117. ksadk/conversations/runtime_observability.py +367 -0
  118. ksadk/conversations/runtime_payloads.py +272 -0
  119. ksadk/conversations/runtime_persistence.py +440 -0
  120. ksadk/conversations/runtime_preparation.py +426 -0
  121. ksadk/conversations/runtime_resume.py +836 -0
  122. ksadk/conversations/runtime_stream_events.py +902 -0
  123. ksadk/conversations/runtime_streaming.py +700 -0
  124. ksadk/conversations/semantic_summary.py +17 -5
  125. ksadk/conversations/session_title.py +6 -3
  126. ksadk/deployment/__init__.py +15 -16
  127. ksadk/deployment/agent_access.py +12 -23
  128. ksadk/deployment/base.py +51 -38
  129. ksadk/deployment/managed_runtime.py +33 -0
  130. ksadk/deployment/manager.py +85 -71
  131. ksadk/deployment/providers/serverless.py +379 -210
  132. ksadk/deployment/registry.py +38 -32
  133. ksadk/deployment/state.py +11 -11
  134. ksadk/deployment/ui_config.py +1 -1
  135. ksadk/detection/__init__.py +1 -1
  136. ksadk/detection/detector.py +146 -83
  137. ksadk/detection/mcp_detector.py +9 -6
  138. ksadk/evaluation/__init__.py +84 -0
  139. ksadk/evaluation/a2a_adapter.py +444 -0
  140. ksadk/evaluation/adapters.py +54 -0
  141. ksadk/evaluation/contracts.py +409 -0
  142. ksadk/evaluation/evalset.py +351 -0
  143. ksadk/evaluation/evaluators.py +461 -0
  144. ksadk/evaluation/executor.py +118 -0
  145. ksadk/evaluation/storage.py +68 -0
  146. ksadk/evaluation/target.py +73 -0
  147. ksadk/events/__init__.py +25 -0
  148. ksadk/events/parser.py +191 -0
  149. ksadk/events/replay.py +37 -0
  150. ksadk/events/runtime_event.py +273 -0
  151. ksadk/events/store.py +317 -0
  152. ksadk/harness/__init__.py +35 -0
  153. ksadk/harness/app.py +269 -0
  154. ksadk/harness/config.py +181 -0
  155. ksadk/harness/reasoner.py +105 -0
  156. ksadk/harness/runtime.py +364 -0
  157. ksadk/harness/sandbox.py +141 -0
  158. ksadk/harness/tools.py +222 -0
  159. ksadk/hermes_terminal.py +28 -12
  160. ksadk/identity/resolver.py +15 -9
  161. ksadk/ids.py +32 -0
  162. ksadk/knowledge_base/adk_tool.py +1 -1
  163. ksadk/knowledge_base/client.py +38 -33
  164. ksadk/managed_a2a_card.py +160 -0
  165. ksadk/managed_runtime.py +195 -0
  166. ksadk/markdown.py +5 -2
  167. ksadk/mcp_runtime/__init__.py +9 -6
  168. ksadk/memory/__init__.py +1 -1
  169. ksadk/memory/adk/__init__.py +2 -0
  170. ksadk/memory/adk/backends/base_ltm_backend.py +2 -6
  171. ksadk/memory/adk/backends/http_ltm_backend.py +13 -21
  172. ksadk/memory/adk/backends/inmemory_ltm_backend.py +7 -13
  173. ksadk/memory/adk/backends/sdk_ltm_backend.py +36 -36
  174. ksadk/memory/adk/long_term_memory.py +34 -37
  175. ksadk/memory/adk/resilient_session_service.py +4 -5
  176. ksadk/memory/adk/short_term_memory.py +12 -21
  177. ksadk/memory/adk_tool.py +3 -2
  178. ksadk/memory/backends/base.py +19 -19
  179. ksadk/memory/backends/memory.py +24 -25
  180. ksadk/memory/backends/redis.py +31 -30
  181. ksadk/memory/langchain_tool.py +2 -1
  182. ksadk/memory/manager.py +52 -47
  183. ksadk/memory/service.py +16 -6
  184. ksadk/memory/tool.py +4 -0
  185. ksadk/model_policy.py +15 -7
  186. ksadk/model_proxy/__init__.py +51 -0
  187. ksadk/model_proxy/bootstrap.py +80 -0
  188. ksadk/model_proxy/cache.py +165 -0
  189. ksadk/model_proxy/config.py +66 -0
  190. ksadk/model_proxy/demo_webui.py +115 -0
  191. ksadk/model_proxy/detect.py +145 -0
  192. ksadk/model_proxy/gate.py +68 -0
  193. ksadk/model_proxy/namespace.py +201 -0
  194. ksadk/model_proxy/protocol_proxy.py +22 -0
  195. ksadk/model_proxy/server.py +443 -0
  196. ksadk/model_proxy/transform.py +735 -0
  197. ksadk/openclaw_gateway.py +29 -14
  198. ksadk/runners/adk_runner.py +354 -146
  199. ksadk/runners/base_runner.py +71 -29
  200. ksadk/runners/deepagents_runner.py +0 -1
  201. ksadk/runners/factory.py +18 -5
  202. ksadk/runners/langchain_runner.py +29 -742
  203. ksadk/runners/langgraph_runner.py +417 -97
  204. ksadk/runners/patch_langchain.py +16 -16
  205. ksadk/runners/remote_runner.py +134 -33
  206. ksadk/runners/usage_accumulator.py +1 -0
  207. ksadk/runners/utils/__init__.py +0 -10
  208. ksadk/runners/utils/loader.py +23 -7
  209. ksadk/runtime/__init__.py +67 -0
  210. ksadk/runtime/adapter.py +412 -0
  211. ksadk/runtime/conversation_execution.py +553 -0
  212. ksadk/runtime/executor.py +251 -0
  213. ksadk/runtime/factory.py +156 -0
  214. ksadk/runtime/framework_adapters.py +118 -0
  215. ksadk/runtime/launch.py +48 -0
  216. ksadk/runtime/preprocessing.py +131 -0
  217. ksadk/runtime/runner_adapter.py +1032 -0
  218. ksadk/runtime/runner_loading.py +36 -0
  219. ksadk/runtime_context.py +18 -3
  220. ksadk/runtime_state.py +0 -1
  221. ksadk/sandbox/__init__.py +4 -1
  222. ksadk/sandbox/backends/__init__.py +4 -1
  223. ksadk/sandbox/backends/e2b.py +13 -5
  224. ksadk/sandbox/backends/local_process.py +11 -3
  225. ksadk/sandbox/base.py +10 -15
  226. ksadk/sandbox/factory.py +14 -4
  227. ksadk/sandbox/registry.py +57 -6
  228. ksadk/server/__init__.py +39 -4
  229. ksadk/server/api_models.py +15 -5
  230. ksadk/server/app.py +24 -3928
  231. ksadk/server/composition.py +47 -0
  232. ksadk/server/factory.py +622 -0
  233. ksadk/server/routes/__init__.py +1 -0
  234. ksadk/server/routes/checkpoint_resolution.py +116 -0
  235. ksadk/server/routes/common.py +682 -0
  236. ksadk/server/routes/control.py +331 -0
  237. ksadk/server/routes/dependencies.py +98 -0
  238. ksadk/server/routes/misc.py +425 -0
  239. ksadk/server/routes/models.py +451 -0
  240. ksadk/server/routes/openai_compat.py +229 -0
  241. ksadk/server/routes/projection.py +848 -0
  242. ksadk/server/routes/routers.py +52 -0
  243. ksadk/server/routes/run.py +519 -0
  244. ksadk/server/routes/sessions.py +644 -0
  245. ksadk/server/routes/streaming.py +265 -0
  246. ksadk/server/routes/workspace.py +414 -0
  247. ksadk/server/static/assets/{ArtifactsPanel-D-SSn2ee.js → ArtifactsPanel-CakCqxEN.js} +1 -1
  248. ksadk/server/static/assets/CodeBlock-CynLknyO.js +9 -0
  249. ksadk/server/static/assets/MathMessageMarkdown-3sQdOysS.js +8 -0
  250. ksadk/server/static/assets/MermaidBlock-NERD_ef7.js +303 -0
  251. ksadk/server/static/assets/NativeTerminalPanel-CiHWf82q.js +1 -0
  252. ksadk/server/static/assets/{arc-BA13sHc9.js → arc-DrgjcKhl.js} +1 -1
  253. ksadk/server/static/assets/architecture-7EHR7CIX-CpZLRCcY.js +1 -0
  254. ksadk/server/static/assets/{architectureDiagram-3BPJPVTR-DzJzgVoL.js → architectureDiagram-3BPJPVTR-BXoVRbQ6.js} +1 -1
  255. ksadk/server/static/assets/{blockDiagram-GPEHLZMM-UTdVcZhn.js → blockDiagram-GPEHLZMM-BnaB3Ywv.js} +1 -1
  256. ksadk/server/static/assets/{c4Diagram-AAUBKEIU-Dix0GD4m.js → c4Diagram-AAUBKEIU-Dw4weZcp.js} +1 -1
  257. ksadk/server/static/assets/channel-Dt3rQTFI.js +1 -0
  258. ksadk/server/static/assets/{chunk-2J33WTMH-C2JxuZ42.js → chunk-2J33WTMH-ChjpLK7j.js} +1 -1
  259. ksadk/server/static/assets/{chunk-4BX2VUAB-Be57QPgD.js → chunk-4BX2VUAB-B9drTgOW.js} +1 -1
  260. ksadk/server/static/assets/{chunk-55IACEB6-WjSuy4nv.js → chunk-55IACEB6-CK4ROATy.js} +1 -1
  261. ksadk/server/static/assets/{chunk-727SXJPM-BOovP-b5.js → chunk-727SXJPM-B9KtTTgw.js} +1 -1
  262. ksadk/server/static/assets/{chunk-AQP2D5EJ-DOmZNmWj.js → chunk-AQP2D5EJ-1O1mki3m.js} +1 -1
  263. ksadk/server/static/assets/{chunk-FMBD7UC4-DzwlodkU.js → chunk-FMBD7UC4-B_fH2YuH.js} +1 -1
  264. ksadk/server/static/assets/{chunk-ND2GUHAM-DMqAikEU.js → chunk-ND2GUHAM-D6rFLaVV.js} +1 -1
  265. ksadk/server/static/assets/chunk-QZHKN3VN-iXEZtKQc.js +1 -0
  266. ksadk/server/static/assets/classDiagram-4FO5ZUOK-DIveV17Q.js +1 -0
  267. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-DIveV17Q.js +1 -0
  268. ksadk/server/static/assets/{cose-bilkent-S5V4N54A-Zhy2GBnP.js → cose-bilkent-S5V4N54A-ChNs_Mdk.js} +1 -1
  269. ksadk/server/static/assets/{dagre-BM42HDAG-CbKrtIUz.js → dagre-BM42HDAG-D0QXg_MQ.js} +1 -1
  270. ksadk/server/static/assets/{diagram-2AECGRRQ-BRApgQRK.js → diagram-2AECGRRQ-DBDDKwBQ.js} +1 -1
  271. ksadk/server/static/assets/{diagram-5GNKFQAL-D0xbnDiK.js → diagram-5GNKFQAL-BkwDOxmO.js} +1 -1
  272. ksadk/server/static/assets/{diagram-KO2AKTUF-CVugVI9L.js → diagram-KO2AKTUF-Bv8waYvY.js} +1 -1
  273. ksadk/server/static/assets/{diagram-LMA3HP47-DozUGgee.js → diagram-LMA3HP47-e1VECDdu.js} +1 -1
  274. ksadk/server/static/assets/{diagram-OG6HWLK6-ZOYsKlru.js → diagram-OG6HWLK6-Ds9adfRl.js} +1 -1
  275. ksadk/server/static/assets/{dist-DpKtt4Bo.js → dist-4N2NMVYg.js} +1 -1
  276. ksadk/server/static/assets/{dist-CkEKJFtk.js → dist-B-jNR6Vn.js} +1 -1
  277. ksadk/server/static/assets/{dist-D57wnGWd.js → dist-B5J2uhNk.js} +1 -1
  278. ksadk/server/static/assets/{dist-8_SfK1IP.js → dist-B5oS1zcj.js} +1 -1
  279. ksadk/server/static/assets/{dist-DMezvBGJ.js → dist-BB3-OwTQ.js} +1 -1
  280. ksadk/server/static/assets/{dist-2hInLS_J.js → dist-BIlKoW0k.js} +1 -1
  281. ksadk/server/static/assets/{dist-BrCcQ47T.js → dist-BjOdlDjj.js} +1 -1
  282. ksadk/server/static/assets/{dist-BmlMPsCc.js → dist-C1sz5Eo3.js} +1 -1
  283. ksadk/server/static/assets/{dist-B0NP_Z0z.js → dist-CWV4lDr1.js} +1 -1
  284. ksadk/server/static/assets/{dist-D-xOD048.js → dist-CmZHzlwZ.js} +1 -1
  285. ksadk/server/static/assets/{dist-DL_NKc_z.js → dist-D6MpCZtz.js} +1 -1
  286. ksadk/server/static/assets/{dist-DyOkZmze.js → dist-DJN_6Y39.js} +1 -1
  287. ksadk/server/static/assets/{dist-DmFaq_Iy.js → dist-DQU9EWw-.js} +1 -1
  288. ksadk/server/static/assets/{dist-gX6GOLKq.js → dist-DWKByxWV.js} +1 -1
  289. ksadk/server/static/assets/{dist-DyWsb78Q.js → dist-Dli7qW-B.js} +1 -1
  290. ksadk/server/static/assets/{dist-BIJVDMuB.js → dist-DsHPdk3W.js} +1 -1
  291. ksadk/server/static/assets/{dist-Dp0k7bWI.js → dist-X4TnqwOK.js} +1 -1
  292. ksadk/server/static/assets/{dist-8EEyGgDx.js → dist-_pHPTyZ-.js} +1 -1
  293. ksadk/server/static/assets/{dist-CPBtqRTW.js → dist-fhOuWds5.js} +1 -1
  294. ksadk/server/static/assets/{dist-D-JMdWPQ.js → dist-gLnrGsoU.js} +1 -1
  295. ksadk/server/static/assets/{dist-BssVmjIo.js → dist-j6Dxmf_j.js} +1 -1
  296. ksadk/server/static/assets/{dist-C3HIIaN5.js → dist-o2OVmKvt.js} +1 -1
  297. ksadk/server/static/assets/{erDiagram-TEJ5UH35-Bk-9bgE5.js → erDiagram-TEJ5UH35-BPItFKH6.js} +1 -1
  298. ksadk/server/static/assets/eventmodeling-FCH6USID-DYFSxbqr.js +1 -0
  299. ksadk/server/static/assets/{flowDiagram-I6XJVG4X-Dn9kZVuZ.js → flowDiagram-I6XJVG4X-BbYB9w5Y.js} +1 -1
  300. ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BGxXlJqm.js → ganttDiagram-6RSMTGT7-TQXa0KXv.js} +1 -1
  301. ksadk/server/static/assets/gitGraph-WXDBUCRP-DyNYp6BN.js +1 -0
  302. ksadk/server/static/assets/{gitGraphDiagram-PVQCEYII-C7Q0X-wi.js → gitGraphDiagram-PVQCEYII-bKblCnpa.js} +1 -1
  303. ksadk/server/static/assets/index-R52pJJvg.js +177 -0
  304. ksadk/server/static/assets/index-jJr3_955.css +2 -0
  305. ksadk/server/static/assets/info-J43DQDTF-WUx7qTde.js +1 -0
  306. ksadk/server/static/assets/{infoDiagram-5YYISTIA-BO_CSqq5.js → infoDiagram-5YYISTIA-BGVT7J_i.js} +1 -1
  307. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-eYLPLd46.js → ishikawaDiagram-YF4QCWOH-K0OOfVna.js} +1 -1
  308. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-De9geSb0.js → journeyDiagram-JHISSGLW-8LeOJaZp.js} +1 -1
  309. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-Cs80kQto.js → kanban-definition-UN3LZRKU-C6uCgZrW.js} +1 -1
  310. ksadk/server/static/assets/{katex-DSrCFD8n.js → katex-CYWseY5E.js} +1 -1
  311. ksadk/server/static/assets/{linear-CN9sbKTF.js → linear-hdBcYzM8.js} +1 -1
  312. ksadk/server/static/assets/{mermaid-parser.core-DlgSBU8l.js → mermaid-parser.core-BWrDjSZM.js} +2 -2
  313. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-9afz7Oug.js → mindmap-definition-RKZ34NQL-Bjy_2v4_.js} +1 -1
  314. ksadk/server/static/assets/packet-YPE3B663-BN13y_xl.js +1 -0
  315. ksadk/server/static/assets/pie-LRSECV5Y-ftSLXhhc.js +1 -0
  316. ksadk/server/static/assets/{pieDiagram-4H26LBE5-DPv_VstN.js → pieDiagram-4H26LBE5-GmmqNiVH.js} +1 -1
  317. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-o77wZ1XK.js → quadrantDiagram-W4KKPZXB-BmkRbwCg.js} +1 -1
  318. ksadk/server/static/assets/radar-GUYGQ44K-jaICkIeO.js +1 -0
  319. ksadk/server/static/assets/{requirementDiagram-4Y6WPE33-BYVw81VC.js → requirementDiagram-4Y6WPE33-DSWFqL7c.js} +1 -1
  320. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-B7nZFMKT.js → sankeyDiagram-5OEKKPKP-l6iXmU65.js} +1 -1
  321. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-BGRRrFw5.js → sequenceDiagram-3UESZ5HK-Cq6UBeQB.js} +1 -1
  322. ksadk/server/static/assets/{stateDiagram-AJRCARHV-BUfHCoPs.js → stateDiagram-AJRCARHV-qzTQpg3Y.js} +1 -1
  323. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-BYCoYGAN.js +1 -0
  324. ksadk/server/static/assets/{timeline-definition-PNZ67QCA-FbCTGrf9.js → timeline-definition-PNZ67QCA-DEz24vV0.js} +1 -1
  325. ksadk/server/static/assets/treeView-BLDUP644-Cpib_vlz.js +1 -0
  326. ksadk/server/static/assets/treemap-LRROVOQU-DQxfmVHK.js +1 -0
  327. ksadk/server/static/assets/{vennDiagram-CIIHVFJN-DWgwtPsJ.js → vennDiagram-CIIHVFJN-CMCixki2.js} +1 -1
  328. ksadk/server/static/assets/wardley-L42UT6IY-BQYHnnkU.js +1 -0
  329. ksadk/server/static/assets/{wardleyDiagram-YWT4CUSO-2x8G-tTM.js → wardleyDiagram-YWT4CUSO-CVzB8F9K.js} +1 -1
  330. ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-Drma3AYl.js → xychartDiagram-2RQKCTM6-BFBqBLc1.js} +1 -1
  331. ksadk/server/static/index.html +2 -2
  332. ksadk/server/terminal_sessions.py +111 -58
  333. ksadk/sessions/__init__.py +69 -19
  334. ksadk/sessions/base.py +31 -11
  335. ksadk/sessions/continuity.py +2 -3
  336. ksadk/sessions/in_memory.py +46 -2
  337. ksadk/sessions/local_service.py +148 -17
  338. ksadk/sessions/postgres_service.py +143 -25
  339. ksadk/sessions/resilient.py +36 -8
  340. ksadk/skills/models.py +17 -5
  341. ksadk/skills/package_store.py +5 -2
  342. ksadk/skills/runtime/agent.py +20 -5
  343. ksadk/skills/runtime/artifacts.py +1 -5
  344. ksadk/skills/runtime/backends/e2b.py +10 -4
  345. ksadk/skills/runtime/backends/local.py +15 -3
  346. ksadk/skills/runtime/base.py +1 -2
  347. ksadk/skills/runtime/executor.py +10 -3
  348. ksadk/skills/runtime/factory.py +4 -2
  349. ksadk/skills/runtime/loader.py +1 -2
  350. ksadk/skills/runtime/registry.py +15 -10
  351. ksadk/skills/runtime/request.py +3 -1
  352. ksadk/skills/service_client.py +47 -16
  353. ksadk/skills/tool_defs.py +15 -10
  354. ksadk/studio/__init__.py +5 -0
  355. ksadk/studio/agent_avatar_assets.py +120 -0
  356. ksadk/studio/agent_lifecycle.py +51 -0
  357. ksadk/studio/api.py +1046 -0
  358. ksadk/studio/api_catalog_routes.py +324 -0
  359. ksadk/studio/api_contracts.py +177 -0
  360. ksadk/studio/api_helpers.py +132 -0
  361. ksadk/studio/authoring.py +524 -0
  362. ksadk/studio/authoring_coordinator.py +285 -0
  363. ksadk/studio/builder.py +320 -0
  364. ksadk/studio/capabilities.py +351 -0
  365. ksadk/studio/cloud.py +278 -0
  366. ksadk/studio/codex_agent_service.py +583 -0
  367. ksadk/studio/codex_builder.py +324 -0
  368. ksadk/studio/codex_manifest.py +278 -0
  369. ksadk/studio/codex_run.py +352 -0
  370. ksadk/studio/compiler.py +189 -0
  371. ksadk/studio/contracts.py +644 -0
  372. ksadk/studio/errors.py +47 -0
  373. ksadk/studio/evaluation.py +192 -0
  374. ksadk/studio/event_store.py +284 -0
  375. ksadk/studio/framework_run.py +114 -0
  376. ksadk/studio/identifiers.py +33 -0
  377. ksadk/studio/mcp_runtime.py +229 -0
  378. ksadk/studio/model_client.py +543 -0
  379. ksadk/studio/model_profile_service.py +214 -0
  380. ksadk/studio/operations.py +243 -0
  381. ksadk/studio/otel_trace.py +754 -0
  382. ksadk/studio/pagination.py +182 -0
  383. ksadk/studio/python_tool_inspection.py +183 -0
  384. ksadk/studio/repository.py +306 -0
  385. ksadk/studio/resource_catalog.py +1217 -0
  386. ksadk/studio/run_service.py +707 -0
  387. ksadk/studio/runtime_catalog.py +74 -0
  388. ksadk/studio/runtime_source.py +339 -0
  389. ksadk/studio/service.py +1141 -0
  390. ksadk/studio/shared_web.py +994 -0
  391. ksadk/studio/skill_discovery.py +628 -0
  392. ksadk/studio/static/assets/PrismRenderer-IYN-ffww.js +3 -0
  393. ksadk/studio/static/assets/index-CaEN0eTR.css +1 -0
  394. ksadk/studio/static/assets/index-DI2eY_NH.js +252 -0
  395. ksadk/studio/static/assets/react-vendor-BNmTiJ-I.js +9 -0
  396. ksadk/studio/static/assets/rolldown-runtime-hePW80VL.js +1 -0
  397. ksadk/studio/static/index.html +15 -0
  398. ksadk/studio/templates.py +630 -0
  399. ksadk/studio/validator.py +246 -0
  400. ksadk/studio/workspace.py +137 -0
  401. ksadk/terminal_exec_policy.py +3 -4
  402. ksadk/tools/gateway.py +101 -15
  403. ksadk/tools/result_budget.py +12 -4
  404. ksadk/toolsets/__init__.py +161 -37
  405. ksadk/toolsets/a2a.py +426 -0
  406. ksadk/toolsets/platform.py +21 -5
  407. ksadk/toolsets/sandbox.py +82 -16
  408. ksadk/toolsets/skills.py +200 -39
  409. ksadk/toolsets/web.py +123 -26
  410. ksadk/toolsets/workspace.py +212 -101
  411. ksadk/tracing/__init__.py +19 -21
  412. ksadk/tracing/exporters/inmemory_exporter.py +62 -51
  413. ksadk/tracing/setup.py +135 -506
  414. ksadk/tracing/span_utils.py +0 -41
  415. ksadk/tui/loop.py +78 -30
  416. ksadk/tui/stream_render.py +1 -0
  417. ksadk/ui_config.py +5 -3
  418. ksadk/version.py +1 -1
  419. ksadk_runtime_common/memory_backend/registry.py +56 -56
  420. ksadk_runtime_common/schemas/runtime_event_v1.json +95 -0
  421. ksadk_runtime_common/workspace_files/router.py +8 -2
  422. agentengine_sdk_python-0.7.0.dist-info/RECORD +0 -482
  423. ksadk/a2a/card_builder.py +0 -64
  424. ksadk/a2a/client.py +0 -309
  425. ksadk/runners/utils/langfuse.py +0 -230
  426. ksadk/server/static/assets/CodeBlock-P16VOLYi.js +0 -9
  427. ksadk/server/static/assets/MathMessageMarkdown-CdGiQWHR.js +0 -8
  428. ksadk/server/static/assets/MermaidBlock-IOcal8cm.js +0 -303
  429. ksadk/server/static/assets/NativeTerminalPanel-DGSDWsei.js +0 -1
  430. ksadk/server/static/assets/architecture-7EHR7CIX-BvUlsA6l.js +0 -1
  431. ksadk/server/static/assets/channel-CTuydARX.js +0 -1
  432. ksadk/server/static/assets/chunk-QZHKN3VN-BnCydivS.js +0 -1
  433. ksadk/server/static/assets/classDiagram-4FO5ZUOK-BNHg45Be.js +0 -1
  434. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-BNHg45Be.js +0 -1
  435. ksadk/server/static/assets/eventmodeling-FCH6USID-DsAgWnlf.js +0 -1
  436. ksadk/server/static/assets/gitGraph-WXDBUCRP-BIao9Yjg.js +0 -1
  437. ksadk/server/static/assets/index-C00Lwg3L.js +0 -165
  438. ksadk/server/static/assets/index-DAURbCsP.css +0 -1
  439. ksadk/server/static/assets/info-J43DQDTF-CyipqGxU.js +0 -1
  440. ksadk/server/static/assets/packet-YPE3B663-CGkyRxlg.js +0 -1
  441. ksadk/server/static/assets/pie-LRSECV5Y-M0uWAf4n.js +0 -1
  442. ksadk/server/static/assets/radar-GUYGQ44K-Bdbm24-x.js +0 -1
  443. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-8xBN_gLM.js +0 -1
  444. ksadk/server/static/assets/treeView-BLDUP644-npMo0ak1.js +0 -1
  445. ksadk/server/static/assets/treemap-LRROVOQU-CHdzrrdo.js +0 -1
  446. ksadk/server/static/assets/wardley-L42UT6IY-BEQz7wyy.js +0 -1
  447. ksadk/tracing/exporters/langfuse_exporter.py +0 -445
  448. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/entry_points.txt +0 -0
  449. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/licenses/LICENSE +0 -0
  450. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,181 @@
1
+ """HarnessApp yaml 配置 — 最小子集 + 严格校验 (goal-08)。
2
+
3
+ 本期硬边界(防膨胀):**只支持最小子集** ``model`` / ``prompt``(instruction)/
4
+ ``mcp_tools`` / ``sandbox``。超出子集的字段(memory/knowledge/workflow/tracing 等)
5
+ **明确报"暂不支持"**,不硬翻、不静默忽略。
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass, field
11
+ from pathlib import Path
12
+ from typing import Any, Optional
13
+
14
+ import yaml # type: ignore[import-untyped]
15
+
16
+ #: 顶层允许的最小子集字段。
17
+ _ALLOWED_TOP_LEVEL = frozenset({"model", "prompt", "mcp_tools", "sandbox", "runtime"})
18
+ #: runtime 允许的值。
19
+ _ALLOWED_RUNTIME = frozenset({"yaml", "codex"})
20
+ #: mcp_tools 单项允许字段。
21
+ _ALLOWED_MCP_TOOL = frozenset({"name", "url", "api_key", "tool_filter", "tool_name_prefix"})
22
+ #: sandbox 允许字段。
23
+ _ALLOWED_SANDBOX = frozenset({"read_only"})
24
+
25
+ #: 已知但本期不支持的字段,给出更明确的指引。
26
+ _KNOWN_UNSUPPORTED = {
27
+ "memory": "memory(记忆)暂不支持,后续阶段提供",
28
+ "knowledge": "knowledge(知识库)暂不支持,后续阶段提供",
29
+ "workflow": "workflow(工作流编排)暂不支持,后续阶段提供",
30
+ "tracing": "tracing(可观测)暂不支持,后续阶段提供",
31
+ "skills": "skills(技能)暂不支持,后续阶段提供",
32
+ }
33
+
34
+
35
+ class HarnessConfigError(ValueError):
36
+ """HarnessApp yaml 配置错误(含"暂不支持"指引)。"""
37
+
38
+
39
+ @dataclass(frozen=True)
40
+ class McpToolSpec:
41
+ """MCP 工具条目(最小子集)。"""
42
+
43
+ name: str
44
+ url: str
45
+ api_key: Optional[str] = None
46
+ tool_filter: tuple[str, ...] = ()
47
+ tool_name_prefix: Optional[str] = None
48
+
49
+
50
+ @dataclass(frozen=True)
51
+ class SandboxPolicy:
52
+ """sandbox 策略。默认 read-only(goal-08:sandbox 默认 read-only)。"""
53
+
54
+ read_only: bool = True
55
+
56
+
57
+ @dataclass(frozen=True)
58
+ class HarnessConfig:
59
+ """HarnessApp 的最小 yaml 配置。"""
60
+
61
+ model: str
62
+ prompt: str
63
+ mcp_tools: tuple[McpToolSpec, ...] = ()
64
+ sandbox: SandboxPolicy = field(default_factory=SandboxPolicy)
65
+ runtime: str = "yaml"
66
+ """runtime 后端:``yaml``(HarnessRuntimeAdapter)| ``codex``(CodexRuntimeAdapter)。"""
67
+
68
+ @classmethod
69
+ def from_dict(cls, data: dict[str, Any], *, source: str = "yaml") -> "HarnessConfig":
70
+ if not isinstance(data, dict):
71
+ raise HarnessConfigError(f"{source}: 顶层必须是 mapping,得到 {type(data).__name__}")
72
+
73
+ _reject_unknown_keys(data, _ALLOWED_TOP_LEVEL, where=source)
74
+
75
+ model = _require_str(data, "model", source)
76
+ prompt = _require_str(data, "prompt", source)
77
+
78
+ runtime = str(data.get("runtime", "yaml")).strip().lower()
79
+ if runtime not in _ALLOWED_RUNTIME:
80
+ raise HarnessConfigError(
81
+ f"{source}: runtime 仅支持 {sorted(_ALLOWED_RUNTIME)},得到 {runtime!r}"
82
+ )
83
+
84
+ mcp_tools = tuple(
85
+ _parse_mcp_tool(item, index, source)
86
+ for index, item in enumerate(
87
+ _require_list(data.get("mcp_tools", []), "mcp_tools", source)
88
+ )
89
+ )
90
+ sandbox = _parse_sandbox(data.get("sandbox", {}), source)
91
+ return cls(
92
+ model=model, prompt=prompt, mcp_tools=mcp_tools, sandbox=sandbox, runtime=runtime
93
+ )
94
+
95
+ @classmethod
96
+ def from_yaml(cls, path: str | Path) -> "HarnessConfig":
97
+ path = Path(path)
98
+ try:
99
+ data = yaml.safe_load(path.read_text(encoding="utf-8"))
100
+ except yaml.YAMLError as exc:
101
+ raise HarnessConfigError(f"{path}: YAML 解析失败: {exc}") from exc
102
+ if data is None:
103
+ data = {}
104
+ return cls.from_dict(data, source=str(path))
105
+
106
+
107
+ def _reject_unknown_keys(data: dict[str, Any], allowed: frozenset[str], *, where: str) -> None:
108
+ for key in data:
109
+ if key in allowed:
110
+ continue
111
+ if key in _KNOWN_UNSUPPORTED:
112
+ raise HarnessConfigError(f"{where}: 字段 {key!r} —— {_KNOWN_UNSUPPORTED[key]}")
113
+ raise HarnessConfigError(
114
+ f"{where}: 未知字段 {key!r};本期最小子集仅支持 {sorted(allowed)},"
115
+ "超出子集的字段不予支持(不静默忽略)"
116
+ )
117
+
118
+
119
+ def _require_str(data: dict[str, Any], key: str, where: str) -> str:
120
+ value = data.get(key)
121
+ if not isinstance(value, str) or not value.strip():
122
+ raise HarnessConfigError(f"{where}: 字段 {key!r} 必填且必须是非空字符串")
123
+ return value.strip()
124
+
125
+
126
+ def _require_list(value: Any, key: str, where: str) -> list:
127
+ if value is None:
128
+ return []
129
+ if not isinstance(value, list):
130
+ raise HarnessConfigError(f"{where}: 字段 {key!r} 必须是 list")
131
+ return value
132
+
133
+
134
+ def _parse_mcp_tool(item: Any, index: int, where: str) -> McpToolSpec:
135
+ if not isinstance(item, dict):
136
+ raise HarnessConfigError(f"{where}: mcp_tools[{index}] 必须是 mapping")
137
+ _reject_unknown_keys(item, _ALLOWED_MCP_TOOL, where=f"{where}.mcp_tools[{index}]")
138
+ name = _require_str(item, "name", f"{where}.mcp_tools[{index}]")
139
+ url = _require_str(item, "url", f"{where}.mcp_tools[{index}]")
140
+ tool_filter = item.get("tool_filter", ())
141
+ if not isinstance(tool_filter, (list, tuple)) or not all(
142
+ isinstance(t, str) for t in tool_filter
143
+ ):
144
+ raise HarnessConfigError(f"{where}.mcp_tools[{index}].tool_filter 必须是字符串 list")
145
+ api_key = item.get("api_key")
146
+ if api_key is not None and not isinstance(api_key, str):
147
+ raise HarnessConfigError(f"{where}.mcp_tools[{index}].api_key 必须是字符串")
148
+ prefix = item.get("tool_name_prefix")
149
+ if prefix is not None and not isinstance(prefix, str):
150
+ raise HarnessConfigError(f"{where}.mcp_tools[{index}].tool_name_prefix 必须是字符串")
151
+ return McpToolSpec(
152
+ name=name,
153
+ url=url,
154
+ api_key=api_key,
155
+ tool_filter=tuple(tool_filter),
156
+ tool_name_prefix=prefix,
157
+ )
158
+
159
+
160
+ def _parse_sandbox(value: Any, where: str) -> SandboxPolicy:
161
+ if value is None:
162
+ return SandboxPolicy()
163
+ if not isinstance(value, dict):
164
+ raise HarnessConfigError(f"{where}: sandbox 必须是 mapping")
165
+ _reject_unknown_keys(value, _ALLOWED_SANDBOX, where=f"{where}.sandbox")
166
+ read_only = value.get("read_only", True)
167
+ if not isinstance(read_only, bool):
168
+ raise HarnessConfigError(f"{where}.sandbox.read_only 必须是 bool")
169
+ if not read_only:
170
+ raise HarnessConfigError(
171
+ f"{where}.sandbox.read_only=false 暂不支持;Harness 当前只提供执行层强制的只读策略"
172
+ )
173
+ return SandboxPolicy(read_only=read_only)
174
+
175
+
176
+ __all__ = [
177
+ "HarnessConfig",
178
+ "HarnessConfigError",
179
+ "McpToolSpec",
180
+ "SandboxPolicy",
181
+ ]
@@ -0,0 +1,105 @@
1
+ """Reasoning boundary used by the Harness runner."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ from dataclasses import dataclass
8
+ from typing import Any, Protocol, Sequence
9
+
10
+
11
+ @dataclass(frozen=True)
12
+ class HarnessToolCall:
13
+ call_id: str
14
+ name: str
15
+ arguments: dict[str, Any]
16
+
17
+
18
+ @dataclass(frozen=True)
19
+ class HarnessReasoningTurn:
20
+ final_text: str | None = None
21
+ tool_calls: tuple[HarnessToolCall, ...] = ()
22
+
23
+
24
+ class HarnessReasoner(Protocol):
25
+ async def complete(
26
+ self,
27
+ *,
28
+ model: str,
29
+ prompt: str,
30
+ messages: Sequence[dict[str, Any]],
31
+ tools: Sequence[Any],
32
+ ) -> HarnessReasoningTurn: ...
33
+
34
+
35
+ class LiteLLMHarnessReasoner:
36
+ """Use the project's OpenAI-compatible LiteLLM configuration for tool reasoning."""
37
+
38
+ async def complete(
39
+ self,
40
+ *,
41
+ model: str,
42
+ prompt: str,
43
+ messages: Sequence[dict[str, Any]],
44
+ tools: Sequence[Any],
45
+ ) -> HarnessReasoningTurn:
46
+ del prompt
47
+ try:
48
+ from litellm import acompletion
49
+ except ImportError as exc: # pragma: no cover - depends on optional install
50
+ raise RuntimeError(
51
+ "Harness reasoning requires the 'adk' extra (litellm); "
52
+ "install ksadk[adk] or inject a HarnessReasoner"
53
+ ) from exc
54
+
55
+ resolved_model = model if "/" in model else f"openai/{model}"
56
+ kwargs: dict[str, Any] = {
57
+ "model": resolved_model,
58
+ "messages": list(messages),
59
+ "tools": [tool.openai_schema for tool in tools],
60
+ "tool_choice": "auto",
61
+ }
62
+ base_url = os.getenv("OPENAI_BASE_URL")
63
+ api_key = os.getenv("OPENAI_API_KEY")
64
+ if base_url:
65
+ kwargs["base_url"] = base_url
66
+ if api_key:
67
+ kwargs["api_key"] = api_key
68
+ response = await acompletion(**kwargs)
69
+ choices = getattr(response, "choices", None) or []
70
+ if not choices:
71
+ raise RuntimeError(f"Harness model {model!r} returned no choices")
72
+ message = choices[0].message
73
+ calls: list[HarnessToolCall] = []
74
+ for index, call in enumerate(getattr(message, "tool_calls", None) or []):
75
+ function = getattr(call, "function", None)
76
+ name = str(getattr(function, "name", "") or "").strip()
77
+ raw_arguments = getattr(function, "arguments", None) or "{}"
78
+ try:
79
+ arguments = (
80
+ json.loads(raw_arguments) if isinstance(raw_arguments, str) else raw_arguments
81
+ )
82
+ except json.JSONDecodeError as exc:
83
+ raise RuntimeError(
84
+ f"Harness model emitted invalid JSON arguments for tool {name!r}"
85
+ ) from exc
86
+ if not name or not isinstance(arguments, dict):
87
+ raise RuntimeError("Harness model emitted an invalid tool call")
88
+ calls.append(
89
+ HarnessToolCall(
90
+ call_id=str(getattr(call, "id", "") or f"tool-call-{index}"),
91
+ name=name,
92
+ arguments=dict(arguments),
93
+ )
94
+ )
95
+ content = getattr(message, "content", None)
96
+ final_text = str(content) if content is not None else None
97
+ return HarnessReasoningTurn(final_text=final_text, tool_calls=tuple(calls))
98
+
99
+
100
+ __all__ = [
101
+ "HarnessReasoner",
102
+ "HarnessReasoningTurn",
103
+ "HarnessToolCall",
104
+ "LiteLLMHarnessReasoner",
105
+ ]
@@ -0,0 +1,364 @@
1
+ """Native RuntimeAdapter for YAML Harness agents."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import json
7
+ import uuid
8
+ from dataclasses import dataclass
9
+ from pathlib import Path
10
+ from typing import Any
11
+
12
+ from ksadk.events import EventPhase, EventType, RuntimeEvent
13
+ from ksadk.harness.config import HarnessConfig
14
+ from ksadk.harness.reasoner import HarnessReasoner, LiteLLMHarnessReasoner
15
+ from ksadk.harness.sandbox import HarnessSandboxExecutor
16
+ from ksadk.harness.tools import HarnessTool, load_mcp_tools, sandbox_tools, tool_result_text
17
+ from ksadk.runtime import (
18
+ BaseRuntime,
19
+ CancelResult,
20
+ CheckpointCapability,
21
+ CheckpointDescriptor,
22
+ ResumePayload,
23
+ ResumeTarget,
24
+ RunHandle,
25
+ RuntimeAdapter,
26
+ StartRequest,
27
+ )
28
+
29
+ _MAX_REASONING_TURNS = 8
30
+
31
+
32
+ class HarnessRuntime(BaseRuntime):
33
+ runtime_type = "harness"
34
+
35
+ def native_capabilities(self) -> dict[str, Any]:
36
+ return {
37
+ "cancel": {"supported": True},
38
+ "resume": {"supported": False},
39
+ "checkpoint": {"supported": False, "granularity": "none"},
40
+ "session_continuity": {"durable": False, "scope": "process"},
41
+ }
42
+
43
+
44
+ @dataclass
45
+ class _HarnessRun:
46
+ request: StartRequest
47
+ task: asyncio.Task[dict[str, Any]] | None = None
48
+ pending_cancel: bool = False
49
+ done: bool = False
50
+
51
+
52
+ class HarnessRuntimeAdapter(RuntimeAdapter):
53
+ """Execute a YAML Harness config directly as RuntimeEvent streams."""
54
+
55
+ def __init__(
56
+ self,
57
+ config: HarnessConfig,
58
+ *,
59
+ agent_name: str = "harness-agent",
60
+ reasoner: HarnessReasoner | None = None,
61
+ workspace_root: str | Path = ".",
62
+ ) -> None:
63
+ super().__init__(HarnessRuntime())
64
+ self._config = config
65
+ self._agent_name = agent_name
66
+ self._reasoner = reasoner or LiteLLMHarnessReasoner()
67
+ self._sandbox = HarnessSandboxExecutor(
68
+ workspace_root=workspace_root,
69
+ read_only=config.sandbox.read_only,
70
+ )
71
+ self._runs: dict[str, _HarnessRun] = {}
72
+ self._tools: tuple[HarnessTool, ...] | None = None
73
+ self._tool_lock = asyncio.Lock()
74
+ self._mcp_toolsets: list[Any] = []
75
+
76
+ @property
77
+ def harness_config(self) -> HarnessConfig:
78
+ return self._config
79
+
80
+ @property
81
+ def sandbox(self) -> HarnessSandboxExecutor:
82
+ return self._sandbox
83
+
84
+ @property
85
+ def workspace_root(self) -> Path:
86
+ return self._sandbox.workspace_root
87
+
88
+ async def start(self, request: StartRequest) -> RunHandle:
89
+ run_id = str(request.metadata.get("invocation_id") or f"harness_{uuid.uuid4().hex}")
90
+ if run_id in self._runs:
91
+ raise ValueError(f"duplicate Harness invocation: {run_id}")
92
+ self._runs[run_id] = _HarnessRun(request=request)
93
+ return RunHandle(
94
+ run_id=run_id,
95
+ session_id=request.session_id,
96
+ runtime_type="harness",
97
+ native_ref={"user_id": request.user_id, "agent_id": request.agent_id},
98
+ )
99
+
100
+ def stream(self, handle: RunHandle):
101
+ return self._stream(handle)
102
+
103
+ async def cancel(self, handle: RunHandle) -> CancelResult:
104
+ run = self._runs.get(handle.run_id)
105
+ if run is None or run.done:
106
+ return CancelResult.NOT_RUNNING
107
+ if run.task is None:
108
+ run.pending_cancel = True
109
+ return CancelResult.PENDING_CANCEL_RECORDED
110
+ if run.task.done():
111
+ return CancelResult.NOT_RUNNING
112
+ run.task.cancel()
113
+ return CancelResult.INTERRUPTED_ACTIVE_TURN
114
+
115
+ async def resume(
116
+ self,
117
+ handle: RunHandle,
118
+ target: ResumeTarget,
119
+ payload: ResumePayload | None,
120
+ ) -> RunHandle:
121
+ del handle, target, payload
122
+ raise RuntimeError("Harness runtime does not support resume")
123
+
124
+ async def checkpoint(self, handle: RunHandle) -> CheckpointDescriptor:
125
+ return CheckpointDescriptor(
126
+ checkpoint_id=f"unsupported:{handle.run_id}",
127
+ invocation_id=handle.run_id,
128
+ capability=CheckpointCapability(
129
+ supported=False,
130
+ granularity="none",
131
+ rollback_scope="none",
132
+ fork_supported=False,
133
+ durable=False,
134
+ shared_across_pods=False,
135
+ reason="Harness runtime has no checkpoint backend",
136
+ ),
137
+ )
138
+
139
+ async def close(self, handle: RunHandle) -> None:
140
+ run = self._runs.pop(handle.run_id, None)
141
+ if run is not None and run.task is not None and not run.task.done():
142
+ run.task.cancel()
143
+ await asyncio.gather(run.task, return_exceptions=True)
144
+ if not self._runs:
145
+ await self._close_tools()
146
+
147
+ def is_handle_attached(self, handle: RunHandle) -> bool:
148
+ return handle.run_id in self._runs
149
+
150
+ async def execute_request(self, request: StartRequest) -> dict[str, Any]:
151
+ tools = await self._ensure_tools()
152
+ model, prompt = self._effective(request)
153
+ messages: list[dict[str, Any]] = [
154
+ {"role": "system", "content": prompt},
155
+ {"role": "user", "content": str(request.input or "")},
156
+ ]
157
+ execution_log: list[dict[str, Any]] = []
158
+
159
+ for _turn_number in range(_MAX_REASONING_TURNS):
160
+ turn = await self._reasoner.complete(
161
+ model=model,
162
+ prompt=prompt,
163
+ messages=tuple(messages),
164
+ tools=tools,
165
+ )
166
+ if turn.tool_calls:
167
+ messages.append(
168
+ {
169
+ "role": "assistant",
170
+ "content": turn.final_text,
171
+ "tool_calls": [
172
+ {
173
+ "id": call.call_id,
174
+ "type": "function",
175
+ "function": {
176
+ "name": call.name,
177
+ "arguments": json.dumps(call.arguments, ensure_ascii=False),
178
+ },
179
+ }
180
+ for call in turn.tool_calls
181
+ ],
182
+ }
183
+ )
184
+ for call in turn.tool_calls:
185
+ tool = next((item for item in tools if item.name == call.name), None)
186
+ if tool is None:
187
+ raise RuntimeError(
188
+ f"Harness tool {call.name!r} is not available; it may be filtered "
189
+ "or was not exposed by its MCP server"
190
+ )
191
+ result = await tool.call(call.arguments, call_id=call.call_id)
192
+ content = tool_result_text(result)
193
+ execution_log.append(
194
+ {
195
+ "call_id": call.call_id,
196
+ "name": call.name,
197
+ "source": tool.source,
198
+ "arguments": dict(call.arguments),
199
+ "result": result,
200
+ }
201
+ )
202
+ messages.append(
203
+ {
204
+ "role": "tool",
205
+ "tool_call_id": call.call_id,
206
+ "name": call.name,
207
+ "content": content,
208
+ }
209
+ )
210
+ continue
211
+ if turn.final_text is None:
212
+ raise RuntimeError(
213
+ "Harness reasoner returned neither a final response nor a tool call"
214
+ )
215
+ return {
216
+ "output": turn.final_text,
217
+ "model": model,
218
+ "prompt": prompt,
219
+ "tool_calls": execution_log,
220
+ "sandbox_read_only": self._config.sandbox.read_only,
221
+ }
222
+ raise RuntimeError(f"Harness reasoning exceeded {_MAX_REASONING_TURNS} turns")
223
+
224
+ def _effective(self, request: StartRequest) -> tuple[str, str]:
225
+ metadata = request.metadata or {}
226
+ model = str(
227
+ metadata.get("model_override") or request.model or self._config.model
228
+ ).strip()
229
+ prompt = str(
230
+ metadata.get("prompt_override")
231
+ or request.config.get("base_instructions")
232
+ or request.config.get("instructions")
233
+ or request.config.get("prompt")
234
+ or self._config.prompt
235
+ ).strip()
236
+ return model, prompt
237
+
238
+ async def _stream(self, handle: RunHandle):
239
+ run = self._require_run(handle)
240
+ seq = 0
241
+
242
+ def event(
243
+ event_type: str,
244
+ payload: dict[str, Any],
245
+ *,
246
+ phase: str | None = None,
247
+ ) -> RuntimeEvent:
248
+ nonlocal seq
249
+ seq += 1
250
+ request = run.request
251
+ return RuntimeEvent.create(
252
+ event_type,
253
+ agent_id=str(request.agent_id or self._agent_name),
254
+ user_id=request.user_id,
255
+ session_id=request.session_id,
256
+ invocation_id=handle.run_id,
257
+ seq_id=seq,
258
+ payload=payload,
259
+ phase=phase,
260
+ )
261
+
262
+ if run.pending_cancel:
263
+ run.done = True
264
+ yield event(
265
+ EventType.RUN_CANCELED,
266
+ {
267
+ "status": "cancelled",
268
+ "cancel_result": CancelResult.PENDING_CANCEL_RECORDED.value,
269
+ },
270
+ )
271
+ return
272
+ yield event(EventType.RUN_STARTED, {"status": "in_progress"})
273
+ run.task = asyncio.create_task(self.execute_request(run.request))
274
+ try:
275
+ result = await run.task
276
+ for call in result["tool_calls"]:
277
+ yield event(
278
+ EventType.TOOL_CALL_BEGIN,
279
+ {
280
+ "call_id": call["call_id"],
281
+ "name": call["name"],
282
+ "args": call["arguments"],
283
+ },
284
+ )
285
+ yield event(
286
+ EventType.TOOL_CALL_END,
287
+ {
288
+ "call_id": call["call_id"],
289
+ "name": call["name"],
290
+ "result": call["result"],
291
+ },
292
+ )
293
+ text = str(result["output"])
294
+ yield event(
295
+ EventType.TEXT_DELTA,
296
+ {"text": text},
297
+ phase=EventPhase.FINAL_ANSWER.value,
298
+ )
299
+ yield event(
300
+ EventType.TEXT_COMPLETED,
301
+ {"text": text},
302
+ phase=EventPhase.FINAL_ANSWER.value,
303
+ )
304
+ run.done = True
305
+ yield event(EventType.RUN_COMPLETED, {"status": "completed"})
306
+ except asyncio.CancelledError:
307
+ run.done = True
308
+ yield event(
309
+ EventType.RUN_CANCELED,
310
+ {
311
+ "status": "cancelled",
312
+ "cancel_result": CancelResult.INTERRUPTED_ACTIVE_TURN.value,
313
+ },
314
+ )
315
+ except Exception as exc: # noqa: BLE001
316
+ run.done = True
317
+ yield event(
318
+ EventType.RUN_FAILED,
319
+ {"status": "failed", "error": str(exc)},
320
+ )
321
+
322
+ def _require_run(self, handle: RunHandle) -> _HarnessRun:
323
+ if handle.runtime_type != "harness":
324
+ raise ValueError(f"Harness adapter cannot stream {handle.runtime_type!r}")
325
+ try:
326
+ return self._runs[handle.run_id]
327
+ except KeyError:
328
+ raise KeyError(f"unknown Harness run: {handle.run_id}") from None
329
+
330
+ async def _ensure_tools(self) -> tuple[HarnessTool, ...]:
331
+ if self._tools is not None:
332
+ return self._tools
333
+ async with self._tool_lock:
334
+ if self._tools is not None:
335
+ return self._tools
336
+ tools = sandbox_tools(self._sandbox)
337
+ try:
338
+ for spec in self._config.mcp_tools:
339
+ toolset, discovered = await load_mcp_tools(spec)
340
+ self._mcp_toolsets.append(toolset)
341
+ tools.extend(discovered)
342
+ duplicates = sorted(
343
+ {
344
+ tool.name
345
+ for tool in tools
346
+ if sum(item.name == tool.name for item in tools) > 1
347
+ }
348
+ )
349
+ if duplicates:
350
+ raise RuntimeError(f"Harness tool names are not unique: {duplicates}")
351
+ except Exception:
352
+ await self._close_tools()
353
+ raise
354
+ self._tools = tuple(tools)
355
+ return self._tools
356
+
357
+ async def _close_tools(self) -> None:
358
+ toolsets, self._mcp_toolsets = self._mcp_toolsets, []
359
+ self._tools = None
360
+ if toolsets:
361
+ await asyncio.gather(*(toolset.close() for toolset in toolsets), return_exceptions=True)
362
+
363
+
364
+ __all__ = ["HarnessRuntime", "HarnessRuntimeAdapter"]