agentengine-sdk-python 0.8.4__py3-none-any.whl → 0.8.5__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 (673) hide show
  1. {agentengine_sdk_python-0.8.4.dist-info → agentengine_sdk_python-0.8.5.dist-info}/METADATA +6 -3
  2. {agentengine_sdk_python-0.8.4.dist-info → agentengine_sdk_python-0.8.5.dist-info}/RECORD +591 -365
  3. ksadk/_build_provenance.json +1 -1
  4. ksadk/_process.py +83 -0
  5. ksadk/_str_enum.py +17 -0
  6. ksadk/a2ui/core.py +3 -3
  7. ksadk/api/client.py +6 -0
  8. ksadk/builders/code_builder.py +38 -12
  9. ksadk/builders/framework_requirements.py +20 -2
  10. ksadk/cli/__init__.py +36 -0
  11. ksadk/cli/cmd_config.py +4 -4
  12. ksadk/cli/cmd_create.py +12 -12
  13. ksadk/cli/cmd_hermes.py +24 -2
  14. ksadk/cli/cmd_invoke.py +28 -5
  15. ksadk/cli/cmd_openclaw.py +26 -1
  16. ksadk/cli/cmd_plugin.py +70 -12
  17. ksadk/cli/cmd_studio.py +64 -16
  18. ksadk/cli/openclaw_env.py +1 -1
  19. ksadk/cli/resource_common.py +26 -0
  20. ksadk/codex/client.py +3 -2
  21. ksadk/configs/__init__.py +1 -1
  22. ksadk/configs/env_registry.py +125 -31
  23. ksadk/configs/env_registry_studio.py +86 -0
  24. ksadk/configs/global_config.py +1 -1
  25. ksadk/configs/settings.py +3 -3
  26. ksadk/connector/__init__.py +17 -0
  27. ksadk/connector/channel.py +477 -0
  28. ksadk/context_engine/capabilities.py +52 -4
  29. ksadk/conversations/attachment_storage.py +10 -3
  30. ksadk/conversations/attachments.py +16 -5
  31. ksadk/conversations/message_projection.py +23 -32
  32. ksadk/conversations/normalize.py +28 -10
  33. ksadk/conversations/runtime_baseline.py +37 -0
  34. ksadk/conversations/runtime_invocation.py +17 -4
  35. ksadk/conversations/runtime_observability.py +39 -10
  36. ksadk/conversations/runtime_payloads.py +8 -0
  37. ksadk/conversations/runtime_persistence.py +105 -9
  38. ksadk/conversations/runtime_preparation.py +13 -7
  39. ksadk/conversations/runtime_stream_events.py +29 -35
  40. ksadk/conversations/runtime_streaming.py +8 -0
  41. ksadk/events/adapters/_codex_items.py +16 -8
  42. ksadk/events/adapters/_langgraph_support.py +34 -0
  43. ksadk/events/session_event.py +2 -2
  44. ksadk/harness/__init__.py +14 -0
  45. ksadk/harness/app.py +11 -0
  46. ksadk/harness/artifact_store.py +213 -0
  47. ksadk/harness/capabilities.py +115 -0
  48. ksadk/harness/capability_runtime.py +237 -0
  49. ksadk/harness/closure_contract.py +92 -0
  50. ksadk/harness/compaction_record.py +122 -0
  51. ksadk/harness/compiler.py +284 -0
  52. ksadk/harness/conformance/__init__.py +33 -0
  53. ksadk/harness/conformance/contract.py +506 -0
  54. ksadk/harness/conformance/fixtures.py +102 -0
  55. ksadk/harness/context_engine.py +403 -0
  56. ksadk/harness/context_maintenance.py +259 -0
  57. ksadk/harness/context_manifest.py +188 -0
  58. ksadk/harness/context_tuning.py +168 -0
  59. ksadk/harness/draft_runtime.py +322 -0
  60. ksadk/harness/engine/__init__.py +40 -0
  61. ksadk/harness/engine/base.py +122 -0
  62. ksadk/harness/engine/budgets.py +108 -0
  63. ksadk/harness/engine/capability_declarations.py +59 -0
  64. ksadk/harness/engine/completion_guard.py +42 -0
  65. ksadk/harness/engine/context_pipeline.py +579 -0
  66. ksadk/harness/engine/graph_builder.py +859 -0
  67. ksadk/harness/engine/langgraph.py +931 -0
  68. ksadk/harness/engine/mcp_disclosure.py +750 -0
  69. ksadk/harness/engine/policy_runtime.py +70 -0
  70. ksadk/harness/engine/postgres_checkpointer.py +63 -0
  71. ksadk/harness/engine/run_control_bridge.py +80 -0
  72. ksadk/harness/engine/run_types.py +88 -0
  73. ksadk/harness/engine/skill_disclosure.py +189 -0
  74. ksadk/harness/engine/spans.py +41 -0
  75. ksadk/harness/engine/subagents.py +343 -0
  76. ksadk/harness/engine/thread_ids.py +112 -0
  77. ksadk/harness/engine/tool_dispatch.py +144 -0
  78. ksadk/harness/event_tree.py +212 -0
  79. ksadk/harness/events.py +461 -0
  80. ksadk/harness/execution_policy.py +94 -0
  81. ksadk/harness/insights.py +210 -0
  82. ksadk/harness/lifecycle.py +578 -0
  83. ksadk/harness/lifecycle_conformance.py +233 -0
  84. ksadk/harness/lifecycle_local_plane.py +104 -0
  85. ksadk/harness/loop/__init__.py +44 -0
  86. ksadk/harness/loop/reason.py +454 -0
  87. ksadk/harness/loop/tools.py +762 -0
  88. ksadk/harness/managed_runtime.py +884 -0
  89. ksadk/harness/mcp_matrix.py +474 -0
  90. ksadk/harness/mcp_runtime.py +471 -0
  91. ksadk/harness/memory_consolidation.py +157 -0
  92. ksadk/harness/memory_loop.py +156 -0
  93. ksadk/harness/memory_runtime.py +541 -0
  94. ksadk/harness/metrics.py +90 -0
  95. ksadk/harness/model_capability.py +253 -0
  96. ksadk/harness/model_provider.py +220 -0
  97. ksadk/harness/observability/__init__.py +29 -0
  98. ksadk/harness/observability/inspection.py +528 -0
  99. ksadk/harness/observability/metrics.py +167 -0
  100. ksadk/harness/observability/otel_exporter.py +218 -0
  101. ksadk/harness/prompt_cache.py +70 -0
  102. ksadk/harness/public_activity.py +154 -0
  103. ksadk/harness/readiness.py +249 -0
  104. ksadk/harness/reasoner.py +466 -13
  105. ksadk/harness/release_readiness.py +140 -0
  106. ksadk/harness/resource_ref.py +69 -0
  107. ksadk/harness/run_control.py +668 -0
  108. ksadk/harness/runtime.py +146 -111
  109. ksadk/harness/runtime_capabilities.py +158 -0
  110. ksadk/harness/runtime_server.py +514 -0
  111. ksadk/harness/sandbox_adapters.py +739 -0
  112. ksadk/harness/sandbox_backend.py +530 -0
  113. ksadk/harness/sandbox_conformance.py +288 -0
  114. ksadk/harness/sandbox_lease.py +77 -0
  115. ksadk/harness/sandbox_lease_conformance.py +259 -0
  116. ksadk/harness/sandbox_matrix.py +170 -0
  117. ksadk/harness/sandbox_reconciliation.py +140 -0
  118. ksadk/harness/sandbox_recovery.py +586 -0
  119. ksadk/harness/sandbox_recovery_conformance.py +246 -0
  120. ksadk/harness/session_store.py +243 -0
  121. ksadk/harness/skill_composition.py +212 -0
  122. ksadk/harness/skill_feedback.py +123 -0
  123. ksadk/harness/skill_matrix.py +233 -0
  124. ksadk/harness/skill_runtime.py +259 -0
  125. ksadk/harness/spec.py +332 -0
  126. ksadk/harness/state.py +153 -0
  127. ksadk/harness/strategies.py +149 -0
  128. ksadk/harness/subagent.py +253 -0
  129. ksadk/harness/tool_policy.py +131 -0
  130. ksadk/harness/tool_receipts.py +96 -0
  131. ksadk/harness/tool_reliability.py +81 -0
  132. ksadk/harness/user_errors.py +69 -0
  133. ksadk/harness/working_context.py +81 -0
  134. ksadk/harness/working_context_patch.py +99 -0
  135. ksadk/identity/resolver.py +18 -0
  136. ksadk/interaction/__init__.py +1 -1
  137. ksadk/interaction/ledger.py +1 -1
  138. ksadk/interaction/provider.py +1 -1
  139. ksadk/interaction/providers/__init__.py +3 -1
  140. ksadk/interaction/providers/adk.py +1 -1
  141. ksadk/interaction/providers/codex.py +1 -1
  142. ksadk/interaction/providers/harness.py +50 -0
  143. ksadk/interaction/providers/langgraph.py +1 -1
  144. ksadk/kernel/__init__.py +10 -0
  145. ksadk/kernel/approval_presentation.py +91 -0
  146. ksadk/kernel/authorization.py +1 -1
  147. ksadk/kernel/bootstrap.py +86 -102
  148. ksadk/kernel/contract_fingerprints.py +1 -1
  149. ksadk/kernel/contracts.py +5 -0
  150. ksadk/kernel/control.py +32 -1
  151. ksadk/kernel/execution_grants.py +221 -0
  152. ksadk/kernel/execution_grants_memory.py +133 -0
  153. ksadk/kernel/execution_grants_postgres.py +217 -0
  154. ksadk/kernel/execution_grants_sqlite.py +209 -0
  155. ksadk/kernel/ingress.py +27 -27
  156. ksadk/kernel/mapping.py +1 -1
  157. ksadk/kernel/memory_store.py +24 -11
  158. ksadk/kernel/postgres_store.py +63 -33
  159. ksadk/kernel/recovery.py +78 -14
  160. ksadk/kernel/sql/001_agent_kernel.sql +23 -0
  161. ksadk/kernel/sqlite_store.py +304 -25
  162. ksadk/kernel/state.py +2 -3
  163. ksadk/kernel/store.py +69 -2
  164. ksadk/kernel/worker.py +79 -41
  165. ksadk/kernel/worker_history.py +72 -0
  166. ksadk/kernel/worker_identity.py +47 -0
  167. ksadk/memory/coordinator.py +22 -6
  168. ksadk/memory/extraction.py +40 -1
  169. ksadk/memory/models.py +77 -1
  170. ksadk/memory/provider_resolver.py +5 -0
  171. ksadk/memory/providers/local_postgres.py +200 -0
  172. ksadk/memory/providers/local_sqlite.py +42 -3
  173. ksadk/memory/retrieval.py +189 -0
  174. ksadk/memory/store_adapter.py +150 -0
  175. ksadk/model_policy.py +2 -2
  176. ksadk/model_proxy/detect.py +254 -0
  177. ksadk/model_proxy/server.py +4 -2
  178. ksadk/model_proxy/transform.py +40 -2
  179. ksadk/plugins/__init__.py +1 -1
  180. ksadk/plugins/bridges/dsh.py +11 -0
  181. ksadk/plugins/builtins.py +40 -2
  182. ksadk/plugins/companion_artifacts.py +83 -0
  183. ksadk/plugins/companions.py +368 -0
  184. ksadk/plugins/contracts.py +1 -1
  185. ksadk/plugins/dsh_channels.py +156 -0
  186. ksadk/plugins/dsh_home.py +140 -0
  187. ksadk/plugins/dsh_teams.py +105 -0
  188. ksadk/plugins/dsh_toolchain.py +101 -19
  189. ksadk/plugins/execution_host.py +99 -0
  190. ksadk/plugins/providers/bundles/dsh-channels-client/client.js +19 -0
  191. ksadk/plugins/providers/bundles/dsh-channels-client/cordis.patch.yml +3 -0
  192. ksadk/plugins/providers/bundles/dsh-channels-client/index.mjs +7 -0
  193. ksadk/plugins/providers/bundles/dsh-channels-client/package.json +12 -0
  194. ksadk/plugins/providers/bundles/dsh-knowledge/package.json +1 -1
  195. ksadk/plugins/providers/bundles/dsh-memory/package.json +1 -1
  196. ksadk/plugins/providers/bundles/dsh-skill-center/package.json +1 -1
  197. ksadk/plugins/providers/bundles/dsh-teams-client/client.js +29 -0
  198. ksadk/plugins/providers/bundles/dsh-teams-client/cordis.patch.yml +3 -0
  199. ksadk/plugins/providers/bundles/dsh-teams-client/index.mjs +11 -0
  200. ksadk/plugins/providers/bundles/dsh-teams-client/package.json +35 -0
  201. ksadk/plugins/providers/bundles/dsh-teams-leader-policy/cordis.patch.yml +3 -0
  202. ksadk/plugins/providers/bundles/dsh-teams-leader-policy/index.mjs +13 -0
  203. ksadk/plugins/providers/bundles/dsh-teams-leader-policy/package.json +27 -0
  204. ksadk/plugins/providers/bundles/dsh-teams-profile/cordis.patch.yml +3 -0
  205. ksadk/plugins/providers/bundles/dsh-teams-profile/index.mjs +12 -0
  206. ksadk/plugins/providers/bundles/dsh-teams-profile/package.json +26 -0
  207. ksadk/plugins/providers/bundles/dsh-teams-router/cordis.patch.yml +3 -0
  208. ksadk/plugins/providers/bundles/dsh-teams-router/index.mjs +16 -0
  209. ksadk/plugins/providers/bundles/dsh-teams-router/package.json +27 -0
  210. ksadk/plugins/providers/bundles/dsh-teams-tasks/cordis.patch.yml +3 -0
  211. ksadk/plugins/providers/bundles/dsh-teams-tasks/index.mjs +16 -0
  212. ksadk/plugins/providers/bundles/dsh-teams-tasks/package.json +27 -0
  213. ksadk/plugins/providers/bundles/dsh-teams-tools/cordis.patch.yml +3 -0
  214. ksadk/plugins/providers/bundles/dsh-teams-tools/index.mjs +25 -0
  215. ksadk/plugins/providers/bundles/dsh-teams-tools/package.json +29 -0
  216. ksadk/plugins/providers/bundles/dsh-teams-tools/tools.json +235 -0
  217. ksadk/plugins/providers/bundles/ksadk-codex/index.mjs +1 -1
  218. ksadk/plugins/providers/bundles/ksadk-dsh-capability-host/index.mjs +51 -1
  219. ksadk/plugins/providers/bundles/ksadk-dsh-capability-host/package.json +2 -2
  220. ksadk/plugins/providers/bundles/ksadk-dsh-companion-host/client.mjs +133 -0
  221. ksadk/plugins/providers/bundles/ksadk-dsh-companion-host/index.mjs +14 -0
  222. ksadk/plugins/providers/bundles/ksadk-dsh-studio/client.js +27 -4
  223. ksadk/plugins/providers/bundles/ksadk-dsh-studio/index.mjs +9 -5
  224. ksadk/plugins/providers/bundles/ksadk-harness/index.mjs +1 -1
  225. ksadk/plugins/providers/codex.py +10 -2
  226. ksadk/plugins/providers/dsh_capabilities.py +61 -0
  227. ksadk/plugins/providers/harness.py +181 -33
  228. ksadk/plugins/providers/harness_delegation.py +593 -0
  229. ksadk/plugins/providers/harness_managed.py +456 -0
  230. ksadk/plugins/providers/harness_tools.py +326 -0
  231. ksadk/plugins/providers/shipped_dsh.py +4 -1
  232. ksadk/plugins/subagent_providers/codex.py +28 -6
  233. ksadk/plugins/teams/__init__.py +5 -0
  234. ksadk/plugins/teams/api.py +405 -0
  235. ksadk/plugins/teams/application.py +680 -0
  236. ksadk/plugins/teams/artifacts.py +60 -0
  237. ksadk/plugins/teams/context.py +96 -0
  238. ksadk/plugins/teams/contracts.py +136 -0
  239. ksadk/plugins/teams/domain.py +1427 -0
  240. ksadk/plugins/teams/domain_values.py +24 -0
  241. ksadk/plugins/teams/errors.py +13 -0
  242. ksadk/plugins/teams/migration.py +311 -0
  243. ksadk/plugins/teams/migration_cli.py +322 -0
  244. ksadk/plugins/teams/postgres_store.py +138 -0
  245. ksadk/plugins/teams/runtime.py +777 -0
  246. ksadk/plugins/teams/source_projection.py +84 -0
  247. ksadk/plugins/teams/store.py +311 -0
  248. ksadk/plugins/teams/task_decisions.py +423 -0
  249. ksadk/plugins/teams/transport.py +91 -0
  250. ksadk/plugins/teams/workspaces.py +255 -0
  251. ksadk/runners/_langgraph_runner_streams.py +121 -45
  252. ksadk/runners/_langgraph_state_reader.py +106 -0
  253. ksadk/runners/_session_identity.py +285 -0
  254. ksadk/runners/adk_runner.py +25 -60
  255. ksadk/runners/langgraph_runner.py +128 -111
  256. ksadk/runners/patch_langchain.py +47 -0
  257. ksadk/runners/usage_accumulator.py +11 -3
  258. ksadk/runtime/_runner_adapter/stream_mapping.py +52 -2
  259. ksadk/runtime/conversation_execution.py +25 -1
  260. ksadk/runtime/executor.py +9 -1
  261. ksadk/runtime/factory.py +36 -1
  262. ksadk/runtime/preprocessing.py +11 -3
  263. ksadk/runtime/runner_adapter.py +38 -9
  264. ksadk/runtime/skill_eval_result.py +36 -0
  265. ksadk/runtime/timing.py +46 -0
  266. ksadk/runtime/usage.py +49 -18
  267. ksadk/runtime_context.py +103 -1
  268. ksadk/sandbox/backends/e2b.py +245 -23
  269. ksadk/sandbox/backends/local_process.py +63 -39
  270. ksadk/sandbox/base.py +62 -1
  271. ksadk/sandbox/diagnostics.py +82 -0
  272. ksadk/sandbox/local_controls.py +746 -0
  273. ksadk/sandbox/local_launcher.py +90 -0
  274. ksadk/scheduler/dispatcher.py +1 -1
  275. ksadk/server/invocation_identity.py +118 -0
  276. ksadk/server/routes/common.py +32 -3
  277. ksadk/server/routes/control.py +64 -9
  278. ksadk/server/routes/dependencies.py +2 -1
  279. ksadk/server/routes/kernel_ingress.py +9 -1
  280. ksadk/server/routes/misc.py +233 -25
  281. ksadk/server/routes/models.py +3 -3
  282. ksadk/server/routes/openai_compat.py +76 -13
  283. ksadk/server/routes/run.py +106 -35
  284. ksadk/server/routes/sessions.py +244 -68
  285. ksadk/server/routes/workspace.py +130 -21
  286. ksadk/server/static/assets/{ArtifactsPanel-CGbWlMNh.js → ArtifactsPanel-C1YoaKhx.js} +1 -1
  287. ksadk/server/static/assets/{CodeBlock-b4C7upmO.js → CodeBlock-DHuAWfr5.js} +10 -7
  288. ksadk/server/static/assets/MathMessageMarkdown-cvdrBwYr.js +8 -0
  289. ksadk/server/static/assets/{MermaidBlock-BEBN4Ksz.js → MermaidBlock-DeBkbJvI.js} +9 -9
  290. ksadk/server/static/assets/{NativeTerminalPanel-zvL-WpAn.js → NativeTerminalPanel-oJj44_sV.js} +1 -1
  291. ksadk/server/static/assets/{abnfDiagram-VCTEODGH-BYafW7Za.js → abnfDiagram-VCTEODGH-Dhfv25o2.js} +1 -1
  292. ksadk/server/static/assets/{arc-bTohInsS.js → arc-C35w-pn1.js} +1 -1
  293. ksadk/server/static/assets/architecture-7GRP2DOG-BCsa26zi.js +1 -0
  294. ksadk/server/static/assets/{architectureDiagram-5GKGNRK7-Ch5Lbzsi.js → architectureDiagram-5GKGNRK7-FoMY_u6D.js} +1 -1
  295. ksadk/server/static/assets/{blockDiagram-NRAW4CY4-kqF4MDOG.js → blockDiagram-NRAW4CY4-zD4Rt7oE.js} +1 -1
  296. ksadk/server/static/assets/{c4Diagram-UCG6FXSJ-8uSRIODv.js → c4Diagram-UCG6FXSJ-BCtGGkhN.js} +1 -1
  297. ksadk/server/static/assets/channel-_KDifNys.js +1 -0
  298. ksadk/server/static/assets/chunk-2Q5K7J3B-HdVkJ9F0.js +1 -0
  299. ksadk/server/static/assets/{chunk-5VM5RSS4-DkhehZDr.js → chunk-5VM5RSS4-C2Ms13_k.js} +1 -1
  300. ksadk/server/static/assets/{chunk-F27PBJKO-BdXhtPe9.js → chunk-F27PBJKO-BoTj-qgx.js} +1 -1
  301. ksadk/server/static/assets/{chunk-G27WJ6UU-DPTpvpwi.js → chunk-G27WJ6UU-DOMyyjHH.js} +1 -1
  302. ksadk/server/static/assets/{chunk-JWPE2WC7-Pqoh38el.js → chunk-JWPE2WC7-DbJJqDli.js} +1 -1
  303. ksadk/server/static/assets/{chunk-LCL6LL3I-DeU00qC-.js → chunk-LCL6LL3I-CLuRCt_W.js} +1 -1
  304. ksadk/server/static/assets/{chunk-POPQ4Y6H-CzxScFkr.js → chunk-POPQ4Y6H-D0Dz847e.js} +1 -1
  305. ksadk/server/static/assets/{chunk-RHFEMEQ7-DpqMx-ux.js → chunk-RHFEMEQ7-BWSRzEcd.js} +1 -1
  306. ksadk/server/static/assets/{chunk-SVP7TREG-C6t0SypQ.js → chunk-SVP7TREG-CPoHqkCY.js} +1 -1
  307. ksadk/server/static/assets/{chunk-XXDRQBXY-DVaeafZk.js → chunk-XXDRQBXY-Bx1jS-X-.js} +1 -1
  308. ksadk/server/static/assets/classDiagram-DTDB5LWJ-BVCW3ZqZ.js +1 -0
  309. ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-BVCW3ZqZ.js +1 -0
  310. ksadk/server/static/assets/{cose-bilkent-JH36ORCC-Bf9D3tzJ.js → cose-bilkent-JH36ORCC-LzyWm4m0.js} +1 -1
  311. ksadk/server/static/assets/cynefin-OW5HDTMX-CNXjFctL.js +1 -0
  312. ksadk/server/static/assets/{cynefinDiagram-5FMLGOSQ-B9cY3iTz.js → cynefinDiagram-5FMLGOSQ-BlW_bHED.js} +1 -1
  313. ksadk/server/static/assets/{dagre-3AP2YEHR-CSwlx3CB.js → dagre-3AP2YEHR-Bu_S86_i.js} +1 -1
  314. ksadk/server/static/assets/{dagre-UBpgon5q.js → dagre-B7nCawed.js} +1 -1
  315. ksadk/server/static/assets/{diagram-S7CK7UJ4-C9akhN9a.js → diagram-S7CK7UJ4-W_Zow2Km.js} +1 -1
  316. ksadk/server/static/assets/{diagram-UQ7AKVKN-CgedhTPI.js → diagram-UQ7AKVKN-CLIKAHON.js} +1 -1
  317. ksadk/server/static/assets/{diagram-VSXAHHWV-Cjucz5BY.js → diagram-VSXAHHWV-Dv6_QAlN.js} +1 -1
  318. ksadk/server/static/assets/{diagram-VX7I27RA-Dd4Vleyn.js → diagram-VX7I27RA-BWAWRDtZ.js} +1 -1
  319. ksadk/server/static/assets/{diagram-Z3DM3KII-CZ2jGtYW.js → diagram-Z3DM3KII-XA_rWMss.js} +1 -1
  320. ksadk/server/static/assets/dist-0X_j2Rcc.js +1 -0
  321. ksadk/server/static/assets/{dist-DiixqL0J.js → dist-1nXCdsej.js} +1 -1
  322. ksadk/server/static/assets/{dist-BIr4DSRq.js → dist-2vL2sAdF.js} +1 -1
  323. ksadk/server/static/assets/{dist-D_ctSS2i.js → dist-B3bA05-e.js} +1 -1
  324. ksadk/server/static/assets/{dist-ClYUxfeK.js → dist-BLpRy_Yx.js} +1 -1
  325. ksadk/server/static/assets/{dist-QfkEHOlb.js → dist-BO_UwatH.js} +1 -1
  326. ksadk/server/static/assets/{dist-B3FR1SKK.js → dist-BdNFAney.js} +1 -1
  327. ksadk/server/static/assets/{dist-BnaTiyzN.js → dist-C0J5_3qM.js} +1 -1
  328. ksadk/server/static/assets/{dist-Bj730Ib4.js → dist-CGt_UGeO.js} +1 -1
  329. ksadk/server/static/assets/{dist-CFzsn6BG.js → dist-CIRMvUM4.js} +1 -1
  330. ksadk/server/static/assets/{dist-BgV_owcB.js → dist-CIpWk_5f.js} +1 -1
  331. ksadk/server/static/assets/{dist-C5_ZAZsG.js → dist-CPyieXEf.js} +1 -1
  332. ksadk/server/static/assets/{dist-btbmmX5O.js → dist-D-92S-Fd.js} +1 -1
  333. ksadk/server/static/assets/{dist-fBO2v5Kt.js → dist-DNKuMzWc.js} +1 -1
  334. ksadk/server/static/assets/{dist-ufY3F5p6.js → dist-DWfdmz9s.js} +2 -2
  335. ksadk/server/static/assets/{dist-DVCOWtha.js → dist-DppE_r-D.js} +1 -1
  336. ksadk/server/static/assets/{dist-C1NMAY5a.js → dist-DyG6Ya-z.js} +1 -1
  337. ksadk/server/static/assets/{dist-CB9iJrvg.js → dist-M-SrdhhZ.js} +1 -1
  338. ksadk/server/static/assets/{dist-DvFK7p5S.js → dist-mqD5TZup.js} +1 -1
  339. ksadk/server/static/assets/{dist-Cf125P_m.js → dist-n3n6t_FO.js} +1 -1
  340. ksadk/server/static/assets/{dist-DFD6Qa6T.js → dist-qe8xjesy.js} +1 -1
  341. ksadk/server/static/assets/{dist-DoVSwoDb.js → dist-t6MzE0jU.js} +1 -1
  342. ksadk/server/static/assets/{ebnfDiagram-PWID7BFC-Ci8NqqZu.js → ebnfDiagram-PWID7BFC-BfCzF1Xi.js} +1 -1
  343. ksadk/server/static/assets/{erDiagram-SSCWMZ5O-CCIOhLZG.js → erDiagram-SSCWMZ5O-Dpz9_0zn.js} +1 -1
  344. ksadk/server/static/assets/eventmodeling-NTZA5JFV-B5-8RH9c.js +1 -0
  345. ksadk/server/static/assets/flowDiagram-A5DVABFB-BppFxEKw.js +1 -0
  346. ksadk/server/static/assets/{ganttDiagram-EL5Y4UJY-Cp_r9Hb6.js → ganttDiagram-EL5Y4UJY-DDaG1f4_.js} +1 -1
  347. ksadk/server/static/assets/gitGraph-4MIJSDKK-B-AhYj4C.js +1 -0
  348. ksadk/server/static/assets/{gitGraphDiagram-WWUBYQGX-Dlhc8Wr8.js → gitGraphDiagram-WWUBYQGX-DcxzRwYC.js} +1 -1
  349. ksadk/server/static/assets/index-BMd2mucF.css +2 -0
  350. ksadk/server/static/assets/index-D5qyyhTH.js +244 -0
  351. ksadk/server/static/assets/info-A6RAGUB7-0uiKrC35.js +1 -0
  352. ksadk/server/static/assets/{infoDiagram-RXCK75RN-CO0rRTRA.js → infoDiagram-RXCK75RN-CYMdn6lQ.js} +1 -1
  353. ksadk/server/static/assets/{ishikawaDiagram-5VMMS53U-CHiaedSS.js → ishikawaDiagram-5VMMS53U-CLHFAD6c.js} +1 -1
  354. ksadk/server/static/assets/{journeyDiagram-EYS64GPL-CHcCIB09.js → journeyDiagram-EYS64GPL-BHSKQGAl.js} +1 -1
  355. ksadk/server/static/assets/{kanban-definition-3QL26DDD-CJ_ebBZf.js → kanban-definition-3QL26DDD-D5W3tjTT.js} +1 -1
  356. ksadk/server/static/assets/{katex-B4weu383.js → katex-mtjOi4KM.js} +1 -1
  357. ksadk/server/static/assets/{linear-V-MgYHW1.js → linear-BETqsxE_.js} +1 -1
  358. ksadk/server/static/assets/{mermaid-parser.core-CBijEX_c.js → mermaid-parser.core-DPWrvs90.js} +2 -2
  359. ksadk/server/static/assets/{mindmap-definition-FBJOCRG2-Bc-pQvpp.js → mindmap-definition-FBJOCRG2-CJG2mfBP.js} +1 -1
  360. ksadk/server/static/assets/packet-AYTQ26CC-BVTmZ_cw.js +1 -0
  361. ksadk/server/static/assets/{pegDiagram-XKGWAZYB-D7genGLl.js → pegDiagram-XKGWAZYB-GjAPNaqn.js} +1 -1
  362. ksadk/server/static/assets/pie-WAS4IAKB-DyfxBU7V.js +1 -0
  363. ksadk/server/static/assets/{pieDiagram-E7YTZNPT-Bxj32zwD.js → pieDiagram-E7YTZNPT-ToIewrqr.js} +1 -1
  364. ksadk/server/static/assets/{quadrantDiagram-AXDQQJYC-BURv2mGh.js → quadrantDiagram-AXDQQJYC-4d78KiEY.js} +1 -1
  365. ksadk/server/static/assets/radar-RG4KPBEZ-FFontjL2.js +1 -0
  366. ksadk/server/static/assets/railroad-74A4TZTK-D0Uidzen.js +1 -0
  367. ksadk/server/static/assets/railroad-abnf-HS5TGJTU-BOo_9T2h.js +1 -0
  368. ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-D6-BjW7Z.js +1 -0
  369. ksadk/server/static/assets/railroad-peg-WCYAUIDC-CXx__yMm.js +1 -0
  370. ksadk/server/static/assets/{railroadDiagram-O6MQD6OU-CCsh3avW.js → railroadDiagram-O6MQD6OU-DKzx4xfv.js} +1 -1
  371. ksadk/server/static/assets/{requirementDiagram-EFPCY7ZU-_FRRBKEd.js → requirementDiagram-EFPCY7ZU-CJYljxQS.js} +1 -1
  372. ksadk/server/static/assets/{sankeyDiagram-P5KCCOFB-C-gr2o9M.js → sankeyDiagram-P5KCCOFB-DLU2fV1w.js} +1 -1
  373. ksadk/server/static/assets/{sequenceDiagram-WJ2MYXX4-D97NmfKg.js → sequenceDiagram-WJ2MYXX4-CVqysrA9.js} +1 -1
  374. ksadk/server/static/assets/{sizeCapture-X5ZJPWSS-DjrPC5FX.js → sizeCapture-X5ZJPWSS-BUxfmTHc.js} +1 -1
  375. ksadk/server/static/assets/{stateDiagram-HBIQ2CUA-DzMNg1hh.js → stateDiagram-HBIQ2CUA-CNFgYI9E.js} +1 -1
  376. ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BXAFScz8.js +1 -0
  377. ksadk/server/static/assets/{swimlanes-XN3QIQJK-Cssf_CsU.js → swimlanes-XN3QIQJK-B6qCMe8N.js} +1 -1
  378. ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-uIQ2lJKH.js +8 -0
  379. ksadk/server/static/assets/{timeline-definition-24CTP7MA-BWTHzyS1.js → timeline-definition-24CTP7MA-CMD3x_U_.js} +1 -1
  380. ksadk/server/static/assets/treeView-Q6P3EWNA-CK5GgucZ.js +1 -0
  381. ksadk/server/static/assets/treemap-WGGIJYW6-Bq1u1SpG.js +1 -0
  382. ksadk/server/static/assets/{vennDiagram-4TSXK5OY-BSKgJsmR.js → vennDiagram-4TSXK5OY-BqtUqvXM.js} +1 -1
  383. ksadk/server/static/assets/wardley-WFR3VGLG-QAt06uEz.js +1 -0
  384. ksadk/server/static/assets/{wardleyDiagram-VM6X3IG4-eflHNoaZ.js → wardleyDiagram-VM6X3IG4-B5KfDjI-.js} +1 -1
  385. ksadk/server/static/assets/{xychartDiagram-S5SC5T6Z-eYqrQ-v4.js → xychartDiagram-S5SC5T6Z-BaUlSZrn.js} +1 -1
  386. ksadk/server/static/index.html +2 -2
  387. ksadk/server/terminal_sessions.py +34 -2
  388. ksadk/sessions/invocation_identity.py +235 -0
  389. ksadk/skills/__init__.py +26 -0
  390. ksadk/skills/events.py +464 -0
  391. ksadk/skills/manifest_cache.py +263 -0
  392. ksadk/skills/mcp_server/__init__.py +0 -0
  393. ksadk/skills/mcp_server/_registry.py +33 -0
  394. ksadk/skills/mcp_server/_routing.py +26 -0
  395. ksadk/skills/mcp_server/_workspace_files.py +160 -0
  396. ksadk/skills/mcp_server/register.py +415 -0
  397. ksadk/skills/mcp_server/server.py +218 -0
  398. ksadk/skills/models.py +11 -0
  399. ksadk/skills/observability.py +132 -0
  400. ksadk/skills/package_store.py +75 -2
  401. ksadk/skills/runtime/agent.py +38 -5
  402. ksadk/skills/runtime/artifacts.py +87 -0
  403. ksadk/skills/runtime/backends/e2b.py +393 -52
  404. ksadk/skills/runtime/backends/local.py +621 -119
  405. ksadk/skills/runtime/base.py +78 -7
  406. ksadk/skills/runtime/executor.py +266 -1
  407. ksadk/skills/runtime/loader.py +108 -3
  408. ksadk/skills/runtime/request.py +18 -0
  409. ksadk/skills/tool_defs.py +108 -3
  410. ksadk/studio/api.py +592 -57
  411. ksadk/studio/api_contracts.py +8 -2
  412. ksadk/studio/api_plugin_routes.py +39 -12
  413. ksadk/studio/api_resource_connections.py +79 -41
  414. ksadk/studio/authoring.py +12 -6
  415. ksadk/studio/authoring_coordinator.py +4 -2
  416. ksadk/studio/builder.py +12 -0
  417. ksadk/studio/capabilities.py +23 -6
  418. ksadk/studio/channel_connections.py +333 -0
  419. ksadk/studio/cloud.py +30 -10
  420. ksadk/studio/cloud_shared_web.py +75 -2
  421. ksadk/studio/codex_agent_service.py +2 -2
  422. ksadk/studio/codex_builder.py +1 -1
  423. ksadk/studio/codex_run.py +6 -1
  424. ksadk/studio/compiler.py +10 -0
  425. ksadk/studio/configuration.py +182 -0
  426. ksadk/studio/contracts.py +33 -5
  427. ksadk/studio/dsh_application.py +135 -25
  428. ksadk/studio/dsh_capability_service.py +287 -20
  429. ksadk/studio/dsh_lifecycle.py +58 -0
  430. ksadk/studio/dsh_provider_registration.py +55 -16
  431. ksadk/studio/entry.py +53 -0
  432. ksadk/studio/event_store.py +36 -5
  433. ksadk/studio/execution_host.py +525 -0
  434. ksadk/studio/framework_run.py +32 -10
  435. ksadk/studio/hosted_kernel.py +1 -1
  436. ksadk/studio/kernel_registry.py +418 -0
  437. ksadk/studio/kernel_sqlite_migration.py +181 -0
  438. ksadk/studio/model_client.py +1178 -64
  439. ksadk/studio/model_profile_service.py +26 -11
  440. ksadk/studio/native_documents.py +172 -0
  441. ksadk/studio/plugin_composition.py +13 -2
  442. ksadk/studio/plugin_kernel_adapter.py +15 -0
  443. ksadk/studio/plugin_lifecycle.py +80 -0
  444. ksadk/studio/plugin_runtime.py +230 -17
  445. ksadk/studio/provider_recovery.py +59 -0
  446. ksadk/studio/recovery_page.py +70 -0
  447. ksadk/studio/resource_authority.py +59 -32
  448. ksadk/studio/resource_catalog.py +6 -0
  449. ksadk/studio/run_activity.py +895 -0
  450. ksadk/studio/run_documents.py +127 -0
  451. ksadk/studio/run_service.py +329 -33
  452. ksadk/studio/runtime_catalog.py +53 -10
  453. ksadk/studio/runtime_source.py +1 -1
  454. ksadk/studio/scheduler_runtime.py +32 -289
  455. ksadk/studio/service.py +398 -103
  456. ksadk/studio/shared_web.py +149 -36
  457. ksadk/studio/static/assets/ChannelsPage-D_PGmKVq.js +8 -0
  458. ksadk/studio/static/assets/CodeBlock-DMIf4h33-UIqo-BPW.js +44 -0
  459. ksadk/studio/static/assets/ExecutionTree-v3HexvPo-Bk8wzGSy.js +1 -0
  460. ksadk/studio/static/assets/MathMessageMarkdown-BesSeCYl-OhNwH4K9.js +8 -0
  461. ksadk/studio/static/assets/MermaidBlock-H_0qzQDf-BdXtxug_.js +11 -0
  462. ksadk/studio/static/assets/MessageMarkdown-CwMTvgFj-g0I-I4dD.js +59 -0
  463. ksadk/studio/static/assets/PrismRenderer-5pK6tUdb.js +3 -0
  464. ksadk/studio/static/assets/TeamsPage-DqX4HWKQ.js +3 -0
  465. ksadk/studio/static/assets/abnfDiagram-VCTEODGH-Bnb1O3tA.js +1 -0
  466. ksadk/studio/static/assets/architecture-7GRP2DOG-BMOPAkUC.js +1 -0
  467. ksadk/studio/static/assets/architectureDiagram-5GKGNRK7-5Zn1gqJv.js +36 -0
  468. ksadk/studio/static/assets/{blockDiagram-I7D4REHJ-CFmPl1de.js → blockDiagram-I7D4REHJ-CLB4L11u.js} +6 -6
  469. ksadk/studio/static/assets/{c4Diagram-7LVT6UL2-BRrGu8qt.js → c4Diagram-7LVT6UL2-BOma_Z_F.js} +7 -7
  470. ksadk/studio/static/assets/channel-r8itPEik.js +1 -0
  471. ksadk/studio/static/assets/chunk-2E4U76K2-BFf_huql.js +2 -0
  472. ksadk/studio/static/assets/chunk-2ZTRR5NV-C2qZJila.js +2 -0
  473. ksadk/studio/static/assets/chunk-3Z5EZCMW-DMniOlPd.js +1 -0
  474. ksadk/studio/static/assets/{chunk-4HAMMTFA-D220vUtq.js → chunk-4HAMMTFA-DVtDP4rm.js} +7 -7
  475. ksadk/studio/static/assets/chunk-5V3GS4D5-i-ZDOEnP.js +1 -0
  476. ksadk/studio/static/assets/chunk-6AZGARVD-BB6FqMl-.js +1 -0
  477. ksadk/studio/static/assets/chunk-6EIED4P4-DdUJkP0f.js +1 -0
  478. ksadk/studio/static/assets/chunk-6K3QC6MW-CqVsVn-y.js +1 -0
  479. ksadk/studio/static/assets/chunk-6TQVIW2G-DKlLbNIP.js +1 -0
  480. ksadk/studio/static/assets/chunk-747NJXEK-BFkZPlab.js +2 -0
  481. ksadk/studio/static/assets/chunk-75Z2AOVW-BZNhM4vB.js +2 -0
  482. ksadk/studio/static/assets/chunk-CLGD4ZFX-D-GTtyKs.js +1 -0
  483. ksadk/studio/static/assets/{chunk-DU6HZSFF-C4DtVpCu.js → chunk-DU6HZSFF-CpEZCUgg.js} +1 -1
  484. ksadk/studio/static/assets/chunk-F27PBJKO-D7pvKf0C.js +1 -0
  485. ksadk/studio/static/assets/{chunk-GMAD6QVW-DiQA-u2N.js → chunk-GMAD6QVW-DE_8H717.js} +6 -6
  486. ksadk/studio/static/assets/chunk-GVQU2GXP-BfEB0lNv.js +1 -0
  487. ksadk/studio/static/assets/chunk-I5DQTOEV-Hm2LfiYI.js +1 -0
  488. ksadk/studio/static/assets/chunk-ICYGCRZG-749o-pGJ.js +1 -0
  489. ksadk/studio/static/assets/chunk-IH6LHLGP-CC6I9F3i.js +1 -0
  490. ksadk/studio/static/assets/{chunk-IMKFNOWR-BSlbjT_b.js → chunk-IMKFNOWR-D38axYOR.js} +5 -5
  491. ksadk/studio/static/assets/chunk-KI3K4JFJ-Rma-0k_M.js +1 -0
  492. ksadk/studio/static/assets/chunk-L3NEJ4N5-Dq5xn7aL.js +1 -0
  493. ksadk/studio/static/assets/chunk-LNGE3PJU-DWpGwHrW.js +34 -0
  494. ksadk/studio/static/assets/{chunk-OSK3NFVY-M2m6G3j4.js → chunk-OSK3NFVY-DiCQjx9b.js} +5 -5
  495. ksadk/studio/static/assets/chunk-OUJLGHUK-D0j2JRSg.js +2 -0
  496. ksadk/studio/static/assets/{chunk-P2QGCYS3-DezaQxiK.js → chunk-P2QGCYS3-VdhKmfYq.js} +1 -1
  497. ksadk/studio/static/assets/chunk-POPQ4Y6H-C1_rzO5M.js +1 -0
  498. ksadk/studio/static/assets/chunk-PWAF6VOD-B45HvflI.js +1 -0
  499. ksadk/studio/static/assets/{chunk-SHT3W25Y-DAyIVp5H.js → chunk-SHT3W25Y-DsCFnRVE.js} +9 -9
  500. ksadk/studio/static/assets/{chunk-SVP7TREG-DulomOrS.js → chunk-SVP7TREG-DljApgRX.js} +1 -1
  501. ksadk/studio/static/assets/{chunk-TICWLB2K-bHJYoflj.js → chunk-TICWLB2K-DWqRtyqa.js} +6 -6
  502. ksadk/studio/static/assets/chunk-TLUHSLCS-DUeGgnD5.js +2 -0
  503. ksadk/studio/static/assets/chunk-UY3FDG6J-CnmaAkG5.js +1 -0
  504. ksadk/studio/static/assets/chunk-XHIXRSVI-BFQ6B8BE.js +1 -0
  505. ksadk/studio/static/assets/chunk-XXDRQBXY-DqAYeljN.js +1 -0
  506. ksadk/studio/static/assets/classDiagram-ZZMXUADV-DjeMNJmu.js +1 -0
  507. ksadk/studio/static/assets/classDiagram-v2-VYDZK3BY-DjeMNJmu.js +1 -0
  508. ksadk/studio/static/assets/clsx-DB0hHKMi.js +1 -0
  509. ksadk/studio/static/assets/cose-bilkent-JH36ORCC-om8AY0Mc.js +1 -0
  510. ksadk/studio/static/assets/createLucideIcon-DR0LGTP5.js +1 -0
  511. ksadk/studio/static/assets/cynefin-OW5HDTMX-7tP7QfM3.js +1 -0
  512. ksadk/studio/static/assets/cynefinDiagram-5FMLGOSQ-6BrmK52S.js +62 -0
  513. ksadk/studio/static/assets/dagre-GXQ25YYZ-buZBRlwz.js +4 -0
  514. ksadk/studio/static/assets/{diagram-S7CK7UJ4-0BZ98mya.js → diagram-S7CK7UJ4-C6aH78c4.js} +3 -3
  515. ksadk/studio/static/assets/diagram-UQ7AKVKN-DY_tQ6C_.js +41 -0
  516. ksadk/studio/static/assets/diagram-VSXAHHWV-DX9p5cQT.js +3 -0
  517. ksadk/studio/static/assets/diagram-VX7I27RA-DB2WSH8X.js +24 -0
  518. ksadk/studio/static/assets/diagram-Z3DM3KII-Ce1BJwHE.js +24 -0
  519. ksadk/studio/static/assets/ebnfDiagram-PWID7BFC-Daukxwqf.js +1 -0
  520. ksadk/studio/static/assets/{erDiagram-RLTQ6QDP-DLGry2Ap.js → erDiagram-RLTQ6QDP-CGthTMmJ.js} +6 -6
  521. ksadk/studio/static/assets/eventmodeling-NTZA5JFV-BR2FzK4R.js +1 -0
  522. ksadk/studio/static/assets/flowDiagram-HODETNUW-CZ1tEenp.js +1 -0
  523. ksadk/studio/static/assets/ganttDiagram-EL5Y4UJY-BfoAZW9X.js +292 -0
  524. ksadk/studio/static/assets/gitGraph-4MIJSDKK-DYlXv_yJ.js +1 -0
  525. ksadk/studio/static/assets/gitGraphDiagram-WWUBYQGX-S985OIJF.js +106 -0
  526. ksadk/studio/static/assets/iconfont-4HN3C9Pg.woff2 +0 -0
  527. ksadk/studio/static/assets/index-CfEnms6Y.css +2 -0
  528. ksadk/studio/static/assets/index-Dp61rOWZ.js +0 -0
  529. ksadk/studio/static/assets/index.dom-BQenBtFm.js +1 -0
  530. ksadk/studio/static/assets/info-A6RAGUB7-BlU-8_xu.js +1 -0
  531. ksadk/studio/static/assets/infoDiagram-27XIBGKW-CO3W8tFn.js +2 -0
  532. ksadk/studio/static/assets/{ishikawaDiagram-5VMMS53U-CljI2wyf.js → ishikawaDiagram-5VMMS53U-BUGEl8cO.js} +2 -2
  533. ksadk/studio/static/assets/{journeyDiagram-3NMN7TZE-D50Fo2sL.js → journeyDiagram-3NMN7TZE-Ch2ZYxvr.js} +6 -6
  534. ksadk/studio/static/assets/{kanban-definition-UXKFOSKX-DimfOTam.js → kanban-definition-UXKFOSKX-Da-Fp4vC.js} +3 -3
  535. ksadk/studio/static/assets/kingsoft-cloud-BOcLiQ3e.ico +0 -0
  536. ksadk/studio/static/assets/{line-CX5BOOUP.js → line-BDaeq300.js} +1 -1
  537. ksadk/studio/static/assets/linear-DmucvK78.js +1 -0
  538. ksadk/studio/static/assets/mermaid-parser.core-Cj5mYdBi.js +4 -0
  539. ksadk/studio/static/assets/{mindmap-definition-YA3MSWOX-Bizzqy5R.js → mindmap-definition-YA3MSWOX-CvlsVQ38.js} +1 -1
  540. ksadk/studio/static/assets/packet-AYTQ26CC-BSJEH9Lb.js +1 -0
  541. ksadk/studio/static/assets/pegDiagram-XKGWAZYB-BuHuNgT5.js +1 -0
  542. ksadk/studio/static/assets/pie-WAS4IAKB-qxv01Y8W.js +1 -0
  543. ksadk/studio/static/assets/{pieDiagram-E7YTZNPT-B9mrFSDF.js → pieDiagram-E7YTZNPT-DlrWiSOv.js} +1 -1
  544. ksadk/studio/static/assets/preload-helper-CO68xz6u.js +1 -0
  545. ksadk/studio/static/assets/presentation-yYw7xK60-BmcZriri.js +1 -0
  546. ksadk/studio/static/assets/{quadrantDiagram-AXDQQJYC-NzUfCbMR.js → quadrantDiagram-AXDQQJYC-BA3hpUZM.js} +6 -6
  547. ksadk/studio/static/assets/radar-RG4KPBEZ-B6h6pWOs.js +1 -0
  548. ksadk/studio/static/assets/railroad-74A4TZTK-CGtTWdGc.js +1 -0
  549. ksadk/studio/static/assets/railroad-abnf-HS5TGJTU-Cq7y22IA.js +1 -0
  550. ksadk/studio/static/assets/railroad-ebnf-LZEXJU2U-V0aQVv9m.js +1 -0
  551. ksadk/studio/static/assets/railroad-peg-WCYAUIDC-BQiIsX_p.js +1 -0
  552. ksadk/studio/static/assets/railroadDiagram-O6MQD6OU-BxpAazym.js +1 -0
  553. ksadk/studio/static/assets/{requirementDiagram-BXWQKSXE-BF3d103U.js → requirementDiagram-BXWQKSXE-E-Ge0FcV.js} +1 -1
  554. ksadk/studio/static/assets/sankeyDiagram-P5KCCOFB-CA6YKhQ3.js +40 -0
  555. ksadk/studio/static/assets/{sequenceDiagram-WJ2MYXX4-Dh4wZtUm.js → sequenceDiagram-WJ2MYXX4-CcElXGg4.js} +9 -9
  556. ksadk/studio/static/assets/src-B6xuSHsQ.js +1 -0
  557. ksadk/studio/static/assets/stateDiagram-D77RDMKH-DdXZOFqY.js +1 -0
  558. ksadk/studio/static/assets/stateDiagram-v2-MP3YSRHH-CJbOM-QK.js +1 -0
  559. ksadk/studio/static/assets/swimlanes-42K2YHIH-NtgEEHLY.js +1 -0
  560. ksadk/studio/static/assets/swimlanesDiagram-VR7AAH4N-D-AVlW8B.js +8 -0
  561. ksadk/studio/static/assets/{timeline-definition-24CTP7MA-DxmSSjRB.js → timeline-definition-24CTP7MA-CIuYhNy3.js} +7 -7
  562. ksadk/studio/static/assets/treeView-Q6P3EWNA-BTTYcvrF.js +1 -0
  563. ksadk/studio/static/assets/treemap-WGGIJYW6-Cg7tyfH8.js +1 -0
  564. ksadk/studio/static/assets/vennDiagram-4TSXK5OY-7HbApbrv.js +34 -0
  565. ksadk/studio/static/assets/wardley-WFR3VGLG-C6N1rULY.js +1 -0
  566. ksadk/studio/static/assets/{wardleyDiagram-VM6X3IG4-Ba1AOWoe.js → wardleyDiagram-VM6X3IG4-CMJiaedy.js} +1 -1
  567. ksadk/studio/static/assets/{xychartDiagram-S5SC5T6Z-Cr5pt5Y-.js → xychartDiagram-S5SC5T6Z-BdgtMvW2.js} +6 -6
  568. ksadk/studio/static/index.html +8 -2
  569. ksadk/studio/teams_catalog.py +133 -0
  570. ksadk/studio/teams_installation.py +214 -0
  571. ksadk/studio/teams_node.py +545 -0
  572. ksadk/studio/teams_node_runner.py +69 -0
  573. ksadk/studio/teams_remote.py +274 -0
  574. ksadk/studio/templates.py +40 -19
  575. ksadk/studio/validator.py +13 -0
  576. ksadk/studio/workspace.py +19 -4
  577. ksadk/studio/workspace_plugins.py +136 -0
  578. ksadk/studio/workspace_registry.py +286 -0
  579. ksadk/toolsets/__init__.py +11 -0
  580. ksadk/toolsets/skills.py +446 -378
  581. ksadk/toolsets/workspace.py +3 -3
  582. ksadk/toolsets/workspace_identity.py +15 -0
  583. ksadk/version.py +1 -1
  584. ksadk_runtime_common/memory_backend/providers/lancedb.py +56 -2
  585. ksadk_runtime_common/schemas/memory_backend_manifest.schema.json +148 -148
  586. ksadk_runtime_common/workspace_files/router.py +26 -13
  587. ksadk/server/static/assets/MathMessageMarkdown-DbdW5A4_.js +0 -8
  588. ksadk/server/static/assets/architecture-7GRP2DOG-BW5UtSnx.js +0 -1
  589. ksadk/server/static/assets/channel-CF5Up3hQ.js +0 -1
  590. ksadk/server/static/assets/chunk-2Q5K7J3B-KkXX47WI.js +0 -1
  591. ksadk/server/static/assets/classDiagram-DTDB5LWJ-Cz81acRQ.js +0 -1
  592. ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-Cz81acRQ.js +0 -1
  593. ksadk/server/static/assets/cynefin-OW5HDTMX-DVWs8a2t.js +0 -1
  594. ksadk/server/static/assets/dist-B2_CVgKv.js +0 -1
  595. ksadk/server/static/assets/eventmodeling-NTZA5JFV-CQqR2hg3.js +0 -1
  596. ksadk/server/static/assets/flowDiagram-A5DVABFB-DDRRbGPm.js +0 -1
  597. ksadk/server/static/assets/gitGraph-4MIJSDKK-fgWEf7UD.js +0 -1
  598. ksadk/server/static/assets/index-DcTML1Up.css +0 -2
  599. ksadk/server/static/assets/index-Dh2XHqPK.js +0 -244
  600. ksadk/server/static/assets/info-A6RAGUB7-BUecLTdt.js +0 -1
  601. ksadk/server/static/assets/packet-AYTQ26CC-BeW-btFv.js +0 -1
  602. ksadk/server/static/assets/pie-WAS4IAKB-X0xh4SaT.js +0 -1
  603. ksadk/server/static/assets/radar-RG4KPBEZ-DzRMSqjd.js +0 -1
  604. ksadk/server/static/assets/railroad-74A4TZTK-Cc44icX-.js +0 -1
  605. ksadk/server/static/assets/railroad-abnf-HS5TGJTU-D6S4BdV-.js +0 -1
  606. ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-vD3aVE2o.js +0 -1
  607. ksadk/server/static/assets/railroad-peg-WCYAUIDC-CYbW_b4T.js +0 -1
  608. ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BIcjKFhF.js +0 -1
  609. ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-tq3K3Ybl.js +0 -8
  610. ksadk/server/static/assets/treeView-Q6P3EWNA-DBXt3GdU.js +0 -1
  611. ksadk/server/static/assets/treemap-WGGIJYW6-DrCy4E_l.js +0 -1
  612. ksadk/server/static/assets/wardley-WFR3VGLG-CefhOliM.js +0 -1
  613. ksadk/studio/static/assets/CodeBlock-DCUUY0PL-BLjA7kbS.js +0 -41
  614. ksadk/studio/static/assets/MathMessageMarkdown-CBcDrAw--CUYIXwgI.js +0 -8
  615. ksadk/studio/static/assets/MermaidBlock-DBFTztfQ-BTYKWEbD.js +0 -44
  616. ksadk/studio/static/assets/PrismRenderer-wiXRHURw.js +0 -3
  617. ksadk/studio/static/assets/abnfDiagram-VCTEODGH-CLETGjKq.js +0 -1
  618. ksadk/studio/static/assets/architecture-7GRP2DOG-hXMiO_AU.js +0 -1
  619. ksadk/studio/static/assets/architectureDiagram-5GKGNRK7-B5EQysk6.js +0 -36
  620. ksadk/studio/static/assets/channel-BqO-gtEw.js +0 -1
  621. ksadk/studio/static/assets/chunk-75Z2AOVW-C8lLv_qc.js +0 -2
  622. ksadk/studio/static/assets/chunk-F27PBJKO-Cpw14SNG.js +0 -1
  623. ksadk/studio/static/assets/chunk-GVQU2GXP-DUdNBUCi.js +0 -1
  624. ksadk/studio/static/assets/chunk-L3NEJ4N5-Db8Ab22W.js +0 -1
  625. ksadk/studio/static/assets/chunk-POPQ4Y6H-DOpufJ3t.js +0 -1
  626. ksadk/studio/static/assets/chunk-PWAF6VOD-CfLb1gSc.js +0 -1
  627. ksadk/studio/static/assets/chunk-XXDRQBXY-7PcdR2B1.js +0 -1
  628. ksadk/studio/static/assets/classDiagram-ZZMXUADV-D_bW8JjA.js +0 -1
  629. ksadk/studio/static/assets/classDiagram-v2-VYDZK3BY-D_bW8JjA.js +0 -1
  630. ksadk/studio/static/assets/cose-bilkent-JH36ORCC-snL88wkX.js +0 -1
  631. ksadk/studio/static/assets/cynefin-OW5HDTMX-Bd1YhRo1.js +0 -1
  632. ksadk/studio/static/assets/cynefinDiagram-5FMLGOSQ-BBrIG_mI.js +0 -62
  633. ksadk/studio/static/assets/dagre-GXQ25YYZ-DtF1nGdY.js +0 -4
  634. ksadk/studio/static/assets/diagram-UQ7AKVKN-6mLKEo6L.js +0 -41
  635. ksadk/studio/static/assets/diagram-VSXAHHWV-CJBIaiZ2.js +0 -3
  636. ksadk/studio/static/assets/diagram-VX7I27RA-BohLEE1C.js +0 -24
  637. ksadk/studio/static/assets/diagram-Z3DM3KII-D8KIGonG.js +0 -24
  638. ksadk/studio/static/assets/ebnfDiagram-PWID7BFC-C99HU61t.js +0 -1
  639. ksadk/studio/static/assets/eventmodeling-NTZA5JFV-B1_5Ac8j.js +0 -1
  640. ksadk/studio/static/assets/flowDiagram-HODETNUW-DD5hmicn.js +0 -1
  641. ksadk/studio/static/assets/ganttDiagram-EL5Y4UJY-BPJrVLSG.js +0 -292
  642. ksadk/studio/static/assets/gitGraph-4MIJSDKK-Cy_ZQBGi.js +0 -1
  643. ksadk/studio/static/assets/gitGraphDiagram-WWUBYQGX-CZCUz4MT.js +0 -106
  644. ksadk/studio/static/assets/index-CeHL9ARV.css +0 -2
  645. ksadk/studio/static/assets/index-D9QEcVpL.js +0 -320
  646. ksadk/studio/static/assets/info-A6RAGUB7-DBzDQB_I.js +0 -1
  647. ksadk/studio/static/assets/infoDiagram-27XIBGKW-B-sq07Hi.js +0 -2
  648. ksadk/studio/static/assets/linear-BCxN9JLv.js +0 -1
  649. ksadk/studio/static/assets/mermaid-parser.core-BdCz8fkS.js +0 -7
  650. ksadk/studio/static/assets/packet-AYTQ26CC-gPln-_kG.js +0 -1
  651. ksadk/studio/static/assets/pegDiagram-XKGWAZYB-Dy3fB-gW.js +0 -1
  652. ksadk/studio/static/assets/pie-WAS4IAKB-qZdHMr7B.js +0 -1
  653. ksadk/studio/static/assets/radar-RG4KPBEZ-BZlnaea9.js +0 -1
  654. ksadk/studio/static/assets/railroad-74A4TZTK-Ce3__H9c.js +0 -1
  655. ksadk/studio/static/assets/railroad-abnf-HS5TGJTU-DqQn7EzD.js +0 -1
  656. ksadk/studio/static/assets/railroad-ebnf-LZEXJU2U-B55kCA8F.js +0 -1
  657. ksadk/studio/static/assets/railroad-peg-WCYAUIDC-DfQnvkHd.js +0 -1
  658. ksadk/studio/static/assets/railroadDiagram-O6MQD6OU-DnhMjZCk.js +0 -1
  659. ksadk/studio/static/assets/sankeyDiagram-P5KCCOFB-Bo-JTwt0.js +0 -40
  660. ksadk/studio/static/assets/src-SFn9St9H.js +0 -1
  661. ksadk/studio/static/assets/stateDiagram-D77RDMKH-DH774G6d.js +0 -1
  662. ksadk/studio/static/assets/stateDiagram-v2-MP3YSRHH-DZvVWPBo.js +0 -1
  663. ksadk/studio/static/assets/swimlanes-42K2YHIH-Bl13OvTX.js +0 -1
  664. ksadk/studio/static/assets/swimlanesDiagram-VR7AAH4N-BnFOTALJ.js +0 -8
  665. ksadk/studio/static/assets/treeView-Q6P3EWNA-D5Nn9Swz.js +0 -1
  666. ksadk/studio/static/assets/treemap-WGGIJYW6-CgHgnQhE.js +0 -1
  667. ksadk/studio/static/assets/vennDiagram-4TSXK5OY-B9QEXcJq.js +0 -34
  668. ksadk/studio/static/assets/wardley-WFR3VGLG-DXrK2RRY.js +0 -1
  669. {agentengine_sdk_python-0.8.4.dist-info → agentengine_sdk_python-0.8.5.dist-info}/WHEEL +0 -0
  670. {agentengine_sdk_python-0.8.4.dist-info → agentengine_sdk_python-0.8.5.dist-info}/entry_points.txt +0 -0
  671. {agentengine_sdk_python-0.8.4.dist-info → agentengine_sdk_python-0.8.5.dist-info}/licenses/LICENSE +0 -0
  672. {agentengine_sdk_python-0.8.4.dist-info → agentengine_sdk_python-0.8.5.dist-info}/top_level.txt +0 -0
  673. /ksadk/studio/static/assets/{MathMessageMarkdown-CBcDrAw--BEOyPC3w.css → MathMessageMarkdown-BesSeCYl-BEOyPC3w.css} +0 -0
@@ -0,0 +1,859 @@
1
+ """ManagedLangGraphEngine 的图构建模块(从 langgraph.py 拆出)。
2
+
3
+ 按 ExecutionPlan 组装 LangGraph 图(节点实现 + 边 + 条件路由),
4
+ 并随引擎注入 Checkpointer。langgraph 类型不越出 engine 包
5
+ (tests/architecture/test_harness_contract.py 守卫)。
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ import re
12
+ from typing import Any
13
+
14
+ from langgraph.graph import END, START, StateGraph
15
+ from langgraph.types import interrupt
16
+
17
+ from ksadk.harness.engine import budgets
18
+ from ksadk.harness.engine.base import ExecutionEngineError
19
+ from ksadk.harness.engine.langgraph import _GraphState
20
+ from ksadk.harness.engine.mcp_disclosure import MCP_CALL_TOOL_TOOL, McpDisclosureCursors
21
+ from ksadk.harness.engine.policy_runtime import revalidate_policy
22
+ from ksadk.harness.engine.spans import wrap_node_span
23
+ from ksadk.harness.engine.subagents import ChildApprovalPending
24
+ from ksadk.harness.events import EventType
25
+ from ksadk.harness.loop import (
26
+ ModelFailoverExhausted,
27
+ ReasonInput,
28
+ ToolCallInput,
29
+ execute_tool_calls,
30
+ reason_turn_async,
31
+ )
32
+ from ksadk.harness.loop.reason import ReasoningLimitError
33
+ from ksadk.harness.reasoner import HarnessReasoningTurn
34
+ from ksadk.harness.run_control import ControlAction, RunControlStop
35
+
36
+ _DELEGATION_PREFACE = re.compile(
37
+ r"(?:\blet\s+me\s+(?:delegate|dispatch)|"
38
+ r"\bi\s+(?:will|'ll|am\s+going\s+to)\s+(?:delegate|dispatch)|"
39
+ r"(?:让我|我会|我将|接下来|准备|正在)(?:先)?(?:并行)?(?:派发|分派|调度|处理).{0,80}(?:子任务|子智能体|subtasks?))",
40
+ re.IGNORECASE,
41
+ )
42
+
43
+
44
+ def build_graph(engine, run):
45
+ spec = run.compiled.spec
46
+
47
+ def _restore_controller(state: _GraphState) -> None:
48
+ if run.controller is not None and state.get("run_control"):
49
+ run.controller.restore(state["run_control"])
50
+ budgets.restore(run, state.get("budget") or {})
51
+
52
+ def _capture_control_event(event) -> ControlAction:
53
+ if event.event_id in run.observed_event_ids:
54
+ return ControlAction.CONTINUE
55
+ run.observed_event_ids.add(event.event_id)
56
+ if event.event_type == EventType.USAGE_REPORTED:
57
+ input_tokens = int(event.payload.get("input_tokens") or 0)
58
+ output_tokens = int(event.payload.get("output_tokens") or 0)
59
+ budgets.spend(run, "tokens", int(event.payload.get("total_tokens") or
60
+ input_tokens + output_tokens))
61
+ budgets.spend(run, "input_tokens", input_tokens)
62
+ budgets.spend(run, "output_tokens", output_tokens)
63
+ if event.event_type == EventType.ARTIFACT_CREATED:
64
+ uri = str(event.payload.get("uri") or "")
65
+ if uri and uri not in run.artifact_refs:
66
+ run.artifact_refs.append(uri)
67
+ if run.controller is None:
68
+ return ControlAction.CONTINUE
69
+ if not any(item.event_id == event.event_id for item in run.control_events):
70
+ run.control_events.append(event)
71
+ decision = run.controller.observe(event)
72
+ return decision.action
73
+
74
+ def _before_model(state: _GraphState) -> None:
75
+ _restore_controller(state)
76
+ if run.controller is None:
77
+ budgets.check(run, "tokens", 0)
78
+ budgets.check(run, "models")
79
+ budgets.spend(run, "models")
80
+ return
81
+ decision = run.controller.before_model()
82
+ if decision.action == ControlAction.STOP:
83
+ state["run_control"] = run.controller.snapshot()
84
+ raise RunControlStop(decision.reason)
85
+ budgets.check(run, "tokens", 0)
86
+ budgets.check(run, "models")
87
+ budgets.spend(run, "models")
88
+ guidance = run.controller.take_execution_instruction()
89
+ if guidance:
90
+ state["messages"].append({"role": "system", "content": guidance})
91
+ run.events.append(
92
+ engine._event(
93
+ run,
94
+ EventType.RUN_PROGRESS,
95
+ {
96
+ "kind": "run_control",
97
+ "status": "in_progress",
98
+ "action": "guidance",
99
+ "plan_revision": run.controller.guidance_revision,
100
+ },
101
+ )
102
+ )
103
+ if decision.action == ControlAction.CLOSE:
104
+ instruction = run.controller.take_closing_instruction(decision.reason)
105
+ if instruction:
106
+ state["messages"].append({"role": "system", "content": instruction})
107
+ run.events.append(
108
+ engine._event(
109
+ run,
110
+ EventType.RUN_PROGRESS,
111
+ {
112
+ "kind": "run_control",
113
+ "status": "in_progress",
114
+ "action": "close",
115
+ "reason": decision.reason,
116
+ },
117
+ )
118
+ )
119
+ state["run_control"] = run.controller.snapshot()
120
+
121
+ async def reason(state: _GraphState) -> _GraphState:
122
+ await revalidate_policy(engine, run)
123
+ _before_model(state)
124
+ state["turn_count"] += 1
125
+ # 收口 5:Turn 区间事件(模型/工具/usage 事件按 seq 落在区间内)。
126
+ turn_id = f"{run.handle.run_id}:t{state['turn_count']}"
127
+ run.events.append(
128
+ engine._event(
129
+ run,
130
+ EventType.TURN_STARTED,
131
+ {"turn_id": turn_id, "turn_number": state["turn_count"]},
132
+ )
133
+ )
134
+
135
+ def _reason_input() -> ReasonInput:
136
+ max_total_tokens = budgets.limit(run, "tokens")
137
+ if (
138
+ run.controller is not None
139
+ and run.controller.spec.limits.max_total_tokens is not None
140
+ ):
141
+ controlled_limit = run.controller.spec.limits.max_total_tokens
142
+ max_total_tokens = (
143
+ min(int(max_total_tokens), controlled_limit)
144
+ if max_total_tokens is not None
145
+ else controlled_limit
146
+ )
147
+ # Model Profile 的输出上限是每次调用的硬限制;Run 总预算则是
148
+ # 随执行消耗递减的剩余额度。两者同时存在时必须取更小值。
149
+ max_output_tokens = spec.model.max_output_tokens
150
+ if max_total_tokens is not None:
151
+ from ksadk.harness.engine.context_pipeline import count_tokens
152
+ from ksadk.harness.subagent import SubAgentExecutionError
153
+
154
+ used = max(
155
+ int(state.get("usage_tokens") or 0),
156
+ run.budget_usage.get("tokens", 0),
157
+ run.controller.total_tokens if run.controller is not None else 0,
158
+ )
159
+ estimated_input = sum(
160
+ count_tokens(str(message.get("content") or "")) for message in state["messages"]
161
+ ) + count_tokens(spec.prompt.instructions or "")
162
+ remaining = int(max_total_tokens) - used - estimated_input
163
+ if remaining < 1:
164
+ if run.controller is not None:
165
+ run.controller.stop_reason = "total_tokens_hard_limit"
166
+ run.controller.budget_exhausted = True
167
+ state["run_control"] = run.controller.snapshot()
168
+ raise RunControlStop("total_tokens_hard_limit")
169
+ raise SubAgentExecutionError(
170
+ "budget_exhausted",
171
+ "sub-agent token budget exhausted before model invocation "
172
+ f"(limit={max_total_tokens}, used={used}, "
173
+ f"estimated_input={estimated_input})",
174
+ )
175
+ max_output_tokens = (
176
+ min(max_output_tokens, remaining)
177
+ if max_output_tokens is not None
178
+ else remaining
179
+ )
180
+ streaming = getattr(engine._reasoner, "_streaming", None) is True or os.getenv(
181
+ "KSADK_MODEL_STREAMING", ""
182
+ ).strip().lower() in {"1", "true", "yes", "on"}
183
+
184
+ def _live_event_sink(event):
185
+ run.events.append(event)
186
+ run.seq = max(run.seq, event.seq_id)
187
+ _capture_control_event(event)
188
+
189
+ delegation_synthesis = bool(state.get("delegation_synthesis_pending"))
190
+ closing_turn = state["turn_count"] >= engine._max_reasoning_turns and any(
191
+ message.get("role") == "tool" for message in state["messages"]
192
+ )
193
+ instructions = spec.prompt.instructions or ""
194
+ if delegation_synthesis:
195
+ instructions += (
196
+ "\n\n已调度的子任务均已结束。现在必须直接综合已有子任务结果回答"
197
+ "用户;不得再次派发、描述将要执行的计划或把处理中话术当成答案。"
198
+ "若部分子任务失败,应明确指出失败项,并基于其余结果给出当前最佳结论。"
199
+ )
200
+ if closing_turn:
201
+ instructions += (
202
+ "\n\n这是本次执行的最后一轮。禁止再调用或模拟调用工具,禁止描述"
203
+ "后续计划或说将继续查找。必须现在基于已有证据输出完整、可交付的"
204
+ "当前最佳答案;证据不足的部分直接标为未验证。"
205
+ )
206
+ reason_messages = list(state["messages"])
207
+ if closing_turn:
208
+ # Several OpenAI-compatible models follow the newest user
209
+ # message more reliably than a system-prompt suffix. Without
210
+ # this explicit hand-off GLM can keep emitting a textual call
211
+ # to a tool used in the previous turn even though the closing
212
+ # turn deliberately has no tool schema. The provider then
213
+ # (correctly) rejects that call as undeclared and turns an
214
+ # otherwise useful research run into a failure. Keep the
215
+ # safety boundary -- no tools are disclosed -- but make the
216
+ # required transition from evidence collection to synthesis
217
+ # unambiguous in the conversational context as well.
218
+ reason_messages.append(
219
+ {
220
+ "role": "user",
221
+ "content": (
222
+ "资料收集阶段已经结束。不要再搜索、查看网页或调用任何工具;"
223
+ "请只使用上文已有证据,现在直接给出最终结论。若证据不足,"
224
+ "明确标为未验证。"
225
+ ),
226
+ }
227
+ )
228
+ synthesis_tools = (
229
+ list(run.tools.values())
230
+ + engine._skill_disclosure.tools(run.skill_catalog)
231
+ + engine._mcp_disclosure.tools(run.mcp_catalog)
232
+ )
233
+ available_tools = (
234
+ synthesis_tools
235
+ + list(run.sub_agents.values())
236
+ + (
237
+ [engine._delegation_runtime]
238
+ if engine._delegation_runtime is not None
239
+ else []
240
+ )
241
+ )
242
+
243
+ return ReasonInput(
244
+ model_ref=spec.model.profile_ref,
245
+ fallback_model_refs=spec.model.fallback_profile_refs,
246
+ provider_policy=spec.model.provider_policy,
247
+ instructions=instructions,
248
+ messages=reason_messages,
249
+ # A synthesis turn must not spawn more children, but it still
250
+ # needs ordinary tools to produce the deliverable the user
251
+ # requested (for example, save a Markdown report). Removing
252
+ # every tool here made GLM emit a textual write call against an
253
+ # empty schema, which could only fail after all children had
254
+ # already completed.
255
+ tools=(
256
+ []
257
+ if closing_turn
258
+ else synthesis_tools if delegation_synthesis else available_tools
259
+ ),
260
+ reasoner=engine._reasoner,
261
+ agent_id=run.state.agent_id,
262
+ user_id=run.state.user_id,
263
+ session_id=run.state.session_id,
264
+ run_id=run.handle.run_id,
265
+ seq_start=run.seq,
266
+ max_turns=engine._max_reasoning_turns,
267
+ max_output_tokens=max_output_tokens,
268
+ streaming=streaming,
269
+ live_event_sink=_live_event_sink if streaming else None,
270
+ )
271
+
272
+ try:
273
+ out = await reason_turn_async(state["turn_count"], _reason_input())
274
+ except ModelFailoverExhausted as exc:
275
+ # reason_turn 在最后一次失败时仍必须把每次 started/failed
276
+ # 审计事件交还引擎,不能因异常路径丢失配对事实。
277
+ for event in exc.events:
278
+ if event.seq_id <= run.seq:
279
+ continue
280
+ run.events.append(event)
281
+ run.seq = max(run.seq, event.seq_id)
282
+ if exc.stop_reason.value == "recover_context" and engine._context_engine is not None:
283
+ state["messages"] = await engine._context_pipeline.recover_model_overflow(
284
+ run,
285
+ state["messages"],
286
+ spec.prompt.instructions or "",
287
+ )
288
+ # 恢复调用仍属于同一个 Turn,但使用压缩后输入及新的 seq 起点。
289
+ try:
290
+ out = await reason_turn_async(state["turn_count"], _reason_input())
291
+ except ModelFailoverExhausted as retry_exc:
292
+ for event in retry_exc.events:
293
+ if event.seq_id <= run.seq:
294
+ continue
295
+ run.events.append(event)
296
+ run.seq = max(run.seq, event.seq_id)
297
+ raise RuntimeError(str(retry_exc)) from retry_exc
298
+ else:
299
+ raise RuntimeError(str(exc)) from exc
300
+ except ReasoningLimitError as exc:
301
+ raise RuntimeError(str(exc)) from exc
302
+ # 部分 Provider 在最后一个披露工具返回后会给出空 assistant 消息,
303
+ # 随即把 Run 当成正常结束。只在本 Run 已执行过工具且尚未重试时,
304
+ # 增加一次无工具收口轮;避免 Skill/MCP 已拿到 L3 证据却没有最终答案。
305
+ assistant_text = "\n".join(
306
+ str(message.get("content") or "").strip()
307
+ for message in out.new_messages
308
+ if message.get("role") == "assistant"
309
+ ).strip()
310
+ unfinished_delegation_answer = bool(
311
+ state.get("delegation_synthesis_pending")
312
+ and _DELEGATION_PREFACE.search(assistant_text)
313
+ )
314
+ if (
315
+ out.route == "final"
316
+ and (not assistant_text or unfinished_delegation_answer)
317
+ and any(message.get("role") == "tool" for message in state["messages"])
318
+ and int(state.get("finalization_retries") or 0) < 1
319
+ and ("reason", "tool_calls") in set(run.compiled.plan.edges or ())
320
+ ):
321
+ state["finalization_retries"] = int(state.get("finalization_retries") or 0) + 1
322
+ out.events = [
323
+ event
324
+ for event in out.events
325
+ if not (
326
+ event.event_type == EventType.TEXT_COMPLETED and event.phase == "final_answer"
327
+ )
328
+ ]
329
+ out.new_messages = []
330
+ out.pending_tool_calls = []
331
+ out.route = "tool_calls"
332
+ state["messages"].append(
333
+ {
334
+ "role": "user",
335
+ "content": (
336
+ "上一轮没有形成可交付答案。所有已派发任务都已经结束,"
337
+ "请立即基于现有工具和子任务结果给出完整答案;不得描述"
338
+ "将要派发、继续处理或稍后完成。"
339
+ ),
340
+ }
341
+ )
342
+ if out.route == "final" and state.get("delegation_synthesis_pending"):
343
+ state["delegation_synthesis_pending"] = False
344
+ for ev in out.events:
345
+ if ev.seq_id <= run.seq:
346
+ continue
347
+ # 长任务方案 §6.3:Actual Token 回填到最近一次 ContextManifest
348
+ # (actual_usage_ref 指向 usage.reported 事件)。同时在 usage
349
+ # 事件 payload 落 manifest_id —— 事件流层面可直接查询
350
+ # 「某次模型调用实际对应哪个 Manifest」(§6.2 闭环)。
351
+ if ev.event_type == EventType.USAGE_REPORTED and run.context_manifest:
352
+ ev.payload["manifest_id"] = run.context_manifest.manifest_id
353
+ run.context_manifest = run.context_manifest.with_actual(
354
+ input_tokens=int(ev.payload.get("input_tokens") or 0),
355
+ output_tokens=int(ev.payload.get("output_tokens") or 0),
356
+ usage_ref=ev.event_id,
357
+ )
358
+ run.events.append(ev)
359
+ run.seq = max(run.seq, ev.seq_id)
360
+ _capture_control_event(ev)
361
+ if ev.event_type == EventType.USAGE_REPORTED:
362
+ state["usage_tokens"] = (
363
+ int(state.get("usage_tokens") or 0)
364
+ + int(ev.payload.get("input_tokens") or 0)
365
+ + int(ev.payload.get("output_tokens") or 0)
366
+ )
367
+ state["messages"].extend(out.new_messages)
368
+ state["pending_tool_calls"] = out.pending_tool_calls
369
+ state["tool_batch_succeeded"] = []
370
+ state["tool_batch_failed"] = False
371
+ state["route"] = out.route
372
+ run.events.append(
373
+ engine._event(
374
+ run,
375
+ EventType.TURN_COMPLETED,
376
+ {"turn_id": turn_id, "turn_number": state["turn_count"]},
377
+ )
378
+ )
379
+ if run.controller is not None:
380
+ state["run_control"] = run.controller.snapshot()
381
+ if run.controller.stop_reason:
382
+ raise RunControlStop(run.controller.stop_reason)
383
+ return state
384
+
385
+ async def tool_calls(state: _GraphState) -> _GraphState:
386
+ await revalidate_policy(engine, run)
387
+ _restore_controller(state)
388
+ run.child_approval_decision = state.get("child_approval_decision")
389
+ state["child_approval_decision"] = {}
390
+ class _GraphApprovalResolver:
391
+ # LangGraph interrupt 同步语义:首次抛 GraphInterrupt,resume 后返回审批决定。
392
+ def request(self, *, call_id, name, arguments): # type: ignore[no-untyped-def]
393
+ return interrupt(
394
+ {"call_id": call_id, "name": name, "args": arguments, "risk": "high"}
395
+ )
396
+
397
+ # 披露游标活在图状态(随 Checkpoint 持久化);节点结束写回。
398
+ cursors = McpDisclosureCursors(
399
+ # Checkpoint 反序列化会把 tuple 变 list(不可哈希),统一还原。
400
+ listed={tuple(item) for item in (state.get("mcp_listed") or ())},
401
+ schema_read={tuple(item) for item in (state.get("mcp_schema_read") or ())},
402
+ )
403
+
404
+ def _mcp_approval_decider(name: str, arguments: dict[str, Any]) -> bool:
405
+ # P0.1:按实际目标 Server 动态决策——只有 mcp_call_tool 指向
406
+ # high/critical 风险 Server 时才要求审批(高低风险混用不误伤)。
407
+ return name == MCP_CALL_TOOL_TOOL and engine._mcp_disclosure.approval_decider(arguments)
408
+
409
+ class _EngineToolExecutor:
410
+ @staticmethod
411
+ def validate_arguments(name, arguments): # type: ignore[no-untyped-def]
412
+ tool = run.tools.get(name)
413
+ if tool is None:
414
+ return None
415
+ raw_schema = getattr(tool, "parameters", None)
416
+ # Host tests and backward-compatible integrations may still
417
+ # register a plain callable. With no published schema there
418
+ # is nothing to validate here; execution retains its previous
419
+ # behaviour.
420
+ if not isinstance(raw_schema, dict):
421
+ return None
422
+ schema = raw_schema
423
+ required = schema.get("required") or ()
424
+ missing = [
425
+ str(field)
426
+ for field in required
427
+ if str(field) not in arguments
428
+ or arguments.get(str(field)) is None
429
+ ]
430
+ if missing:
431
+ return "missing required fields: " + ", ".join(missing)
432
+ try:
433
+ from jsonschema.validators import validator_for
434
+
435
+ validator = validator_for(schema)(schema)
436
+ error = next(validator.iter_errors(arguments), None)
437
+ except Exception as exc: # malformed host schema is not a model error
438
+ return f"tool schema validation failed: {exc}"
439
+ return f"schema mismatch: {error.message}" if error is not None else None
440
+
441
+ async def execute(self, name, arguments): # type: ignore[no-untyped-def]
442
+ return await engine._invoke_tool(name, arguments, run=run, mcp_cursors=cursors)
443
+
444
+ async def execute_with_context( # type: ignore[no-untyped-def]
445
+ self, name, arguments, context
446
+ ):
447
+ return await engine._invoke_tool(
448
+ name,
449
+ arguments,
450
+ run=run,
451
+ mcp_cursors=cursors,
452
+ call_id=context.call_id,
453
+ )
454
+
455
+ def reliability(self, name, arguments, *, receipt_enabled): # type: ignore[no-untyped-def]
456
+ if engine._mcp_disclosure.is_tool(name):
457
+ return engine._mcp_disclosure.reliability(
458
+ name,
459
+ arguments,
460
+ receipt_enabled=receipt_enabled,
461
+ )
462
+ if engine._capability_runtime is not None:
463
+ return engine._capability_runtime.reliability(name)
464
+ from ksadk.harness.tool_reliability import classify_tool_reliability
465
+
466
+ return classify_tool_reliability(side_effect="unknown")
467
+
468
+ from ksadk.harness.subagent import order_subagent_tool_calls
469
+
470
+ pending_tool_calls = order_subagent_tool_calls(
471
+ list(state["pending_tool_calls"]), run.sub_agents
472
+ )
473
+ def _parallel_safe(name, arguments):
474
+ sub = run.sub_agents.get(name)
475
+ if sub is not None:
476
+ return not (sub.tools or (sub.inherit_skills and run.skill_catalog)
477
+ or (sub.inherit_mcp and run.mcp_catalog))
478
+ return bool(
479
+ engine._delegation_runtime is not None
480
+ and engine._delegation_runtime.is_tool(name)
481
+ and engine._capability_runtime is None
482
+ and engine._delegation_runtime.parallel_safe(
483
+ arguments,
484
+ parent_run=run,
485
+ )
486
+ )
487
+
488
+ # Commit sequential siblings at separate checkpoint boundaries. Only
489
+ # children proven unable to request approval may share a parallel node.
490
+ parallel = len(pending_tool_calls) > 1 and engine._capability_runtime is None and all(
491
+ _parallel_safe(p["name"], p["arguments"])
492
+ and p["name"] not in run.approval_required
493
+ and not (run.sub_agents.get(p["name"]) and run.sub_agents[p["name"]].depends_on)
494
+ for p in pending_tool_calls
495
+ ) and spec.execution_strategy.config.get("subagent_failure_mode", "partial") != "fail_fast"
496
+ remaining_calls = [] if parallel else pending_tool_calls[1:]
497
+ pending_tool_calls = pending_tool_calls if parallel else pending_tool_calls[:1]
498
+ # Chat only needs an aggregate lifecycle signal. Provider selection,
499
+ # child labels and task bodies remain available in the execution trace
500
+ # and tool receipts, but are deliberately not expanded into one visible
501
+ # progress card per child.
502
+ delegation_calls = (
503
+ [
504
+ pending
505
+ for pending in pending_tool_calls
506
+ if engine._delegation_runtime is not None
507
+ and engine._delegation_runtime.is_tool(str(pending.get("name") or ""))
508
+ ]
509
+ if engine._delegation_runtime is not None
510
+ else []
511
+ )
512
+ delegation_labels: list[str] = []
513
+ ordinary_calls = [
514
+ pending for pending in pending_tool_calls if pending not in delegation_calls
515
+ ]
516
+
517
+ def _emit_tool_progress(status: str) -> None:
518
+ if not ordinary_calls:
519
+ return
520
+ tools = [str(pending.get("name") or "tool") for pending in ordinary_calls]
521
+ lowered = " ".join(tools).lower()
522
+ activity_kind = (
523
+ "write"
524
+ if any(token in lowered for token in ("write", "edit", "save"))
525
+ else "search"
526
+ if "search" in lowered
527
+ else "fetch"
528
+ if "fetch" in lowered
529
+ else "read"
530
+ if any(token in lowered for token in ("read", "list"))
531
+ else "command"
532
+ if any(token in lowered for token in ("command", "shell", "exec"))
533
+ else "tool"
534
+ )
535
+ batch_id = ":".join(
536
+ str(pending.get("call_id") or index)
537
+ for index, pending in enumerate(ordinary_calls, start=1)
538
+ )
539
+ # Repeated search/fetch/read turns are useful trace evidence but
540
+ # become dozens of duplicate chat rows during long research. Keep
541
+ # one public lifecycle per human activity class.
542
+ if status == "running":
543
+ if activity_kind in run.public_tool_activity_kinds:
544
+ return
545
+ run.public_tool_activity_kinds.add(activity_kind)
546
+ run.public_tool_activity_batches.add(batch_id)
547
+ elif batch_id not in run.public_tool_activity_batches:
548
+ return
549
+ progress_event = engine._event(
550
+ run,
551
+ EventType.RUN_PROGRESS,
552
+ {
553
+ "kind": "tool.batch",
554
+ "status": status,
555
+ "count": len(ordinary_calls),
556
+ "batch_id": batch_id,
557
+ "activity_kind": activity_kind,
558
+ # Tool arguments and results stay in Trace/receipts. The
559
+ # chat surface only receives stable capability names and
560
+ # turns them into human activity summaries.
561
+ "tools": tools,
562
+ },
563
+ )
564
+ run.events.append(progress_event)
565
+ _capture_control_event(progress_event)
566
+
567
+ def _emit_delegation_progress(status: str) -> None:
568
+ if not delegation_calls:
569
+ return
570
+ progress_event = engine._event(
571
+ run,
572
+ EventType.RUN_PROGRESS,
573
+ {
574
+ "kind": "delegation.batch",
575
+ "status": status,
576
+ "count": len(delegation_calls),
577
+ "labels": delegation_labels,
578
+ },
579
+ )
580
+ run.events.append(progress_event)
581
+ _capture_control_event(progress_event)
582
+
583
+ if delegation_calls:
584
+ delegation_labels = [
585
+ engine._delegation_runtime.public_label(
586
+ dict(pending.get("arguments") or {}),
587
+ call_id=str(pending.get("call_id") or ""),
588
+ )
589
+ for pending in delegation_calls
590
+ ]
591
+ _emit_delegation_progress("running")
592
+ _emit_tool_progress("running")
593
+ has_subagent_dependencies = any(
594
+ run.sub_agents[name].depends_on
595
+ for name in (str(call.get("name") or "") for call in pending_tool_calls)
596
+ if name in run.sub_agents
597
+ )
598
+ subagent_failure_mode = str(
599
+ run.compiled.spec.execution_strategy.config.get("subagent_failure_mode", "partial")
600
+ )
601
+ def _live_tool_event(event):
602
+ if (event.event_type == EventType.TOOL_CALL_BEGIN
603
+ and str(event.payload.get("call_id") or "") in run.budget_tool_calls):
604
+ return # The same pending delegate resumes; it does not start twice.
605
+ run.seq += 1
606
+ event.seq_id = run.seq
607
+ run.events.append(event)
608
+ _capture_control_event(event)
609
+
610
+ try:
611
+ out = await execute_tool_calls(
612
+ ToolCallInput(
613
+ pending_tool_calls=pending_tool_calls,
614
+ approval_required=frozenset(run.approval_required),
615
+ approval_decider=_mcp_approval_decider,
616
+ parallel_safe_decider=(
617
+ lambda name, _arguments: (
618
+ (
619
+ name in run.sub_agents and _parallel_safe(name, _arguments)
620
+ or (
621
+ engine._delegation_runtime is not None
622
+ and engine._delegation_runtime.is_tool(name)
623
+ )
624
+ )
625
+ and not has_subagent_dependencies
626
+ and subagent_failure_mode != "fail_fast"
627
+ )
628
+ ),
629
+ stop_on_error_decider=(
630
+ lambda name, _arguments: (
631
+ subagent_failure_mode == "fail_fast" and name in run.sub_agents
632
+ )
633
+ ),
634
+ cancel_pending_decider=lambda name, _arguments: (
635
+ name in run.sub_agents
636
+ or (
637
+ engine._delegation_runtime is not None
638
+ and engine._delegation_runtime.is_tool(name)
639
+ )
640
+ ),
641
+ dependencies={
642
+ name: tuple(spec.depends_on)
643
+ for name, spec in run.sub_agents.items()
644
+ if spec.depends_on
645
+ },
646
+ max_parallelism=int(
647
+ run.compiled.spec.execution_strategy.config.get("max_parallel_subagents", 4)
648
+ ),
649
+ capability_runtime=engine._capability_runtime,
650
+ tenant_id=engine._tenant_id,
651
+ authorization_agent_id=run.request.metadata.get("authorization_agent_id"),
652
+ succeeded_tools=frozenset(state.get("tool_batch_succeeded") or ()),
653
+ prior_failure=bool(state.get("tool_batch_failed")),
654
+ live_event_sink=_live_tool_event,
655
+ argument_validator=_EngineToolExecutor.validate_arguments,
656
+ approval_resolver=_GraphApprovalResolver(),
657
+ tool_executor=_EngineToolExecutor(),
658
+ agent_id=run.state.agent_id,
659
+ user_id=run.state.user_id,
660
+ session_id=run.state.session_id,
661
+ run_id=run.handle.run_id,
662
+ seq_start=run.seq,
663
+ working_context=run.state.working_context,
664
+ )
665
+ )
666
+ except ChildApprovalPending as exc:
667
+ state["child_approval"] = exc.detail
668
+ state["route"] = "child_approval"
669
+ state["budget"] = budgets.snapshot(run)
670
+ return state
671
+ except BaseException:
672
+ _emit_delegation_progress("failed")
673
+ _emit_tool_progress("failed")
674
+ raise
675
+ for ev in out.events:
676
+ _capture_control_event(ev)
677
+ if ev.event_type == EventType.TOOL_CALL_END:
678
+ if not ev.payload.get("error"):
679
+ state.setdefault("tool_batch_succeeded", []).append(ev.payload["name"])
680
+ elif (
681
+ subagent_failure_mode == "fail_fast"
682
+ and ev.payload.get("name") in run.sub_agents
683
+ ):
684
+ state["tool_batch_failed"] = True
685
+ tool_errors = any(
686
+ event.event_type == EventType.TOOL_CALL_END and event.payload.get("error")
687
+ for event in out.events
688
+ if str(event.payload.get("name") or "")
689
+ not in {str(call.get("name") or "") for call in delegation_calls}
690
+ )
691
+ _emit_tool_progress("failed" if tool_errors else "completed")
692
+ # 收口 6:子 Agent 事件统一重排并入(tool.call.begin/end 之后)。
693
+ from ksadk.harness.subagent import resequence_child_events
694
+
695
+ child_events = engine._pending_child_events.pop(run.handle.run_id, [])
696
+ for event in child_events:
697
+ _capture_control_event(event)
698
+ resequence_child_events(run, child_events)
699
+ subagent_events = engine._pending_subagent_events.pop(run.handle.run_id, {})
700
+ for pending in pending_tool_calls:
701
+ pending_events = subagent_events.get(str(pending.get("call_id") or ""), [])
702
+ for event in pending_events:
703
+ _capture_control_event(event)
704
+ resequence_child_events(
705
+ run,
706
+ pending_events,
707
+ )
708
+ if delegation_calls:
709
+ delegated_call_ids = {str(pending.get("call_id") or "") for pending in delegation_calls}
710
+ failed = any(
711
+ event.event_type == EventType.TOOL_CALL_END
712
+ and str(event.payload.get("call_id") or "") in delegated_call_ids
713
+ and bool(event.payload.get("error"))
714
+ for event in out.events
715
+ )
716
+ _emit_delegation_progress("failed" if failed else "completed")
717
+ state["delegation_synthesis_pending"] = True
718
+ state["messages"].extend(out.new_messages)
719
+ if out.working_context is not None:
720
+ run.state.working_context = out.working_context
721
+ state["pending_tool_calls"] = remaining_calls
722
+ state["mcp_listed"] = sorted(cursors.listed)
723
+ state["mcp_schema_read"] = sorted(cursors.schema_read)
724
+ state["route"] = "tool_calls" if remaining_calls else out.route
725
+ if run.controller is not None:
726
+ if (
727
+ run.controller.replan_reason
728
+ and run.controller.guidance_emitted_revision != run.controller.guidance_revision
729
+ ):
730
+ run.events.append(
731
+ engine._event(
732
+ run,
733
+ EventType.RUN_PROGRESS,
734
+ {
735
+ "kind": "run_control",
736
+ "status": "in_progress",
737
+ "action": "replan",
738
+ "reason": run.controller.replan_reason,
739
+ "plan_revision": run.controller.guidance_revision,
740
+ },
741
+ )
742
+ )
743
+ state["run_control"] = run.controller.snapshot()
744
+ return state
745
+
746
+ def route(state: _GraphState) -> str:
747
+ return state["route"]
748
+
749
+ def child_approval(state: _GraphState) -> _GraphState:
750
+ info = state["child_approval"]
751
+ decision = interrupt(info)
752
+ state["child_approval_decision"] = {**info, "decision": decision}
753
+ state["child_approval"] = {}
754
+ state["route"] = "tool_calls"
755
+ return state
756
+
757
+ async def plan_node(state: _GraphState) -> _GraphState:
758
+ """plan-execute 拓扑的规划节点:一次无工具模型调用产出执行计划。"""
759
+ _before_model(state)
760
+ turn: HarnessReasoningTurn = await engine._reasoner.complete(
761
+ model=spec.model.profile_ref,
762
+ prompt=(spec.prompt.instructions or "") + "\n请先给出分步执行计划,再开始执行。",
763
+ messages=tuple(state["messages"]),
764
+ tools=[],
765
+ )
766
+ state["messages"].append(
767
+ {"role": "assistant", "content": "【执行计划】\n" + (turn.final_text or "")}
768
+ )
769
+ run.events.append(
770
+ engine._event(
771
+ run,
772
+ EventType.RUN_PROGRESS,
773
+ {
774
+ "kind": "plan",
775
+ "status": "in_progress",
776
+ "message": turn.final_text or "计划已生成",
777
+ },
778
+ )
779
+ )
780
+ return state
781
+
782
+ async def review_node(state: _GraphState) -> _GraphState:
783
+ """plan-execute-review 拓扑的独立审查节点:对最终回答做一次批判。"""
784
+ _before_model(state)
785
+ messages = list(state["messages"]) + [
786
+ {"role": "user", "content": "请审查以上回答的准确性与遗漏,指出问题(如有)。"}
787
+ ]
788
+ turn: HarnessReasoningTurn = await engine._reasoner.complete(
789
+ model=spec.model.profile_ref,
790
+ prompt=spec.prompt.instructions or "",
791
+ messages=tuple(messages),
792
+ tools=[],
793
+ )
794
+ state["messages"].append(
795
+ {"role": "assistant", "content": "【审查】\n" + (turn.final_text or "")}
796
+ )
797
+ return state
798
+
799
+ def passthrough(state: _GraphState) -> _GraphState:
800
+ # prepare_context / execute:上下文规划已在 _emit_context_plan 完成,
801
+ # execute 由 reason 循环承担——节点存在是为了拓扑可观测。
802
+ return state
803
+
804
+ # 集成项 4:按 ExecutionPlan 组图(不再硬编码 single-agent 拓扑)。
805
+ node_impls = {
806
+ "reason": reason,
807
+ "tool_calls": tool_calls,
808
+ "plan": plan_node,
809
+ "review": review_node,
810
+ "prepare_context": passthrough,
811
+ "execute": passthrough,
812
+ }
813
+
814
+ node_impls = {
815
+ name: wrap_node_span(engine, run, name, impl) for name, impl in node_impls.items()
816
+ }
817
+ plan = run.compiled.plan
818
+ builder = StateGraph(_GraphState)
819
+ builder.add_node("child_approval", child_approval)
820
+ builder.add_edge("child_approval", "tool_calls")
821
+ for node in plan.nodes:
822
+ if node == "final":
823
+ continue # final 是出口,不是图节点。
824
+ impl = node_impls.get(node)
825
+ if impl is None:
826
+ raise ExecutionEngineError(f"引擎不支持拓扑节点: {node!r}")
827
+ builder.add_node(node, impl)
828
+ edges = plan.edges or ()
829
+ successors_of = {src: [dst for s, dst in edges if s == src] for src, _ in edges}
830
+ # 入口:无入边的节点接 START(保持 plan.nodes 顺序稳定)。
831
+ incoming = {dst for _, dst in edges}
832
+ for node in plan.nodes:
833
+ if node != "final" and node not in incoming:
834
+ builder.add_edge(START, node)
835
+ # reason 的出边是条件路由:有工具调用走 tool_calls,否则走出口/审查。
836
+ reason_successors = successors_of.get("reason", [])
837
+ exit_targets = [dst for dst in reason_successors if dst != "tool_calls"]
838
+ exit_target = exit_targets[0] if exit_targets else "final"
839
+ reason_mapping = {}
840
+ if "tool_calls" in reason_successors:
841
+ reason_mapping["tool_calls"] = "tool_calls"
842
+ reason_mapping["final"] = END if exit_target == "final" else exit_target
843
+ if "reason" in node_impls and "reason" in plan.nodes and "reason" != exit_target:
844
+ builder.add_conditional_edges("reason", route, reason_mapping)
845
+ for src, dst in edges:
846
+ if src == "reason":
847
+ continue # 已由条件边覆盖。
848
+ if src == "tool_calls":
849
+ builder.add_conditional_edges("tool_calls", route, {
850
+ "tool_calls": "tool_calls", "child_approval": "child_approval", "reason": dst,
851
+ })
852
+ continue
853
+ if dst == "final":
854
+ if src in node_impls and src != "reason":
855
+ builder.add_edge(src, END)
856
+ continue
857
+ builder.add_edge(src, dst)
858
+ graph = builder.compile(checkpointer=engine._checkpointer)
859
+ return graph