agentengine-sdk-python 0.8.5__py3-none-any.whl → 0.8.6__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 (851) hide show
  1. {agentengine_sdk_python-0.8.5.dist-info → agentengine_sdk_python-0.8.6.dist-info}/METADATA +12 -8
  2. {agentengine_sdk_python-0.8.5.dist-info → agentengine_sdk_python-0.8.6.dist-info}/RECORD +823 -388
  3. ksadk/_async_iterators.py +79 -0
  4. ksadk/_build_provenance.json +1 -1
  5. ksadk/_model_anthropic.py +49 -0
  6. ksadk/_model_input.py +86 -0
  7. ksadk/_model_json.py +40 -0
  8. ksadk/_model_reasoning.py +17 -0
  9. ksadk/_model_refusal.py +20 -0
  10. ksadk/_model_sse.py +163 -0
  11. ksadk/a2a/__init__.py +157 -167
  12. ksadk/a2a/_invocation_state.py +86 -0
  13. ksadk/a2a/_space_client_events.py +33 -22
  14. ksadk/a2a/adk.py +194 -0
  15. ksadk/a2a/adk_invocation_context.py +242 -0
  16. ksadk/a2a/adk_runtime.py +126 -0
  17. ksadk/a2a/adk_tools.py +319 -0
  18. ksadk/a2a/bootstrap.py +149 -23
  19. ksadk/a2a/build_validation.py +147 -0
  20. ksadk/a2a/card.py +23 -15
  21. ksadk/a2a/codex.py +160 -0
  22. ksadk/a2a/codex_checkpoint.py +111 -0
  23. ksadk/a2a/codex_recovery.py +96 -0
  24. ksadk/a2a/codex_recovery_events.py +123 -0
  25. ksadk/a2a/codex_runtime.py +198 -0
  26. ksadk/a2a/codex_session_checkpoint.py +162 -0
  27. ksadk/a2a/composition.py +88 -0
  28. ksadk/a2a/context_store.py +118 -19
  29. ksadk/a2a/control_plane.py +108 -106
  30. ksadk/a2a/event_adapter.py +92 -193
  31. ksadk/a2a/executor.py +257 -85
  32. ksadk/a2a/exposure.py +106 -0
  33. ksadk/a2a/external_transport.py +3 -4
  34. ksadk/a2a/identity.py +106 -12
  35. ksadk/a2a/ids.py +14 -3
  36. ksadk/a2a/inbound_kernel.py +133 -0
  37. ksadk/a2a/interaction_wire.py +76 -0
  38. ksadk/a2a/invocation_checkpoint.py +274 -0
  39. ksadk/a2a/invocation_policy.py +87 -0
  40. ksadk/a2a/invocation_publication.py +210 -0
  41. ksadk/a2a/invocation_service.py +972 -0
  42. ksadk/a2a/invocation_types.py +459 -0
  43. ksadk/a2a/kernel_task_adapter.py +259 -0
  44. ksadk/a2a/langgraph.py +20 -7
  45. ksadk/a2a/langgraph_invocation_context.py +136 -0
  46. ksadk/a2a/langgraph_managed.py +150 -0
  47. ksadk/a2a/langgraph_recovery.py +32 -0
  48. ksadk/a2a/langgraph_runtime.py +89 -0
  49. ksadk/a2a/langgraph_tool_runtime.py +132 -0
  50. ksadk/a2a/langgraph_tools.py +63 -0
  51. ksadk/a2a/message_digest.py +40 -0
  52. ksadk/a2a/platform.py +389 -0
  53. ksadk/a2a/policy_refs.py +13 -0
  54. ksadk/a2a/presentation.py +402 -0
  55. ksadk/a2a/product_kernel_facts.py +177 -0
  56. ksadk/a2a/product_key.py +99 -0
  57. ksadk/a2a/protocol_errors.py +116 -0
  58. ksadk/a2a/result_text.py +49 -0
  59. ksadk/a2a/resume_store.py +93 -3
  60. ksadk/a2a/routes.py +6 -1
  61. ksadk/a2a/runtime_approval.py +198 -0
  62. ksadk/a2a/runtime_input.py +151 -0
  63. ksadk/a2a/server.py +16 -7
  64. ksadk/a2a/space_client.py +101 -16
  65. ksadk/a2a/task_adapter.py +19 -5
  66. ksadk/a2a/task_event_dispatcher.py +39 -2
  67. ksadk/a2a/task_event_outbox.py +215 -0
  68. ksadk/a2a/task_event_outbox_owned.py +130 -0
  69. ksadk/a2a/task_store.py +37 -0
  70. ksadk/a2a/tool_activity.py +176 -0
  71. ksadk/a2a/workload_tokens.py +134 -0
  72. ksadk/api/client.py +32 -79
  73. ksadk/api/sse_stream.py +96 -0
  74. ksadk/builders/code_builder.py +83 -106
  75. ksadk/builders/container_builder.py +11 -29
  76. ksadk/builders/framework_requirements.py +2 -0
  77. ksadk/builders/managed_runtime_builder.py +12 -0
  78. ksadk/builders/source_provenance.py +87 -0
  79. ksadk/cli/cmd_a2a.py +43 -0
  80. ksadk/cli/cmd_build.py +9 -0
  81. ksadk/cli/cmd_deploy.py +27 -2
  82. ksadk/cli/cmd_invoke.py +45 -92
  83. ksadk/cli/cmd_studio.py +5 -1
  84. ksadk/cli/cmd_web.py +2 -0
  85. ksadk/cli/hosted_invoke.py +206 -0
  86. ksadk/cli/invoke_content.py +60 -0
  87. ksadk/cli/runtime_bootstrap.py +27 -5
  88. ksadk/codex/_expert_request_reader.py +48 -0
  89. ksadk/codex/a2a_tools.py +140 -0
  90. ksadk/codex/async_approval.py +169 -0
  91. ksadk/codex/async_client.py +938 -0
  92. ksadk/codex/async_proxy.py +216 -0
  93. ksadk/codex/base_runtime.py +25 -0
  94. ksadk/codex/client.py +3 -1048
  95. ksadk/codex/client_payloads.py +85 -0
  96. ksadk/codex/rollout_checkpoint.py +264 -0
  97. ksadk/codex/runtime.py +290 -54
  98. ksadk/common/auth.py +2 -2
  99. ksadk/common/cloud_headers.py +36 -0
  100. ksadk/common/diagnostics.py +34 -0
  101. ksadk/common/kop_client.py +2 -1
  102. ksadk/configs/env_registry.py +106 -127
  103. ksadk/configs/env_registry_a2a.py +173 -0
  104. ksadk/configs/env_registry_studio.py +53 -5
  105. ksadk/configs/global_config.py +1 -0
  106. ksadk/configs/settings.py +6 -0
  107. ksadk/context_engine/assembler.py +9 -0
  108. ksadk/context_engine/contributors.py +80 -17
  109. ksadk/context_engine/instruction_authority.py +18 -0
  110. ksadk/conversations/attachments.py +53 -15
  111. ksadk/conversations/contracts.py +28 -9
  112. ksadk/conversations/message_projection.py +30 -125
  113. ksadk/conversations/message_projection_blocks.py +137 -0
  114. ksadk/conversations/model_context.py +11 -3
  115. ksadk/conversations/projector.py +63 -0
  116. ksadk/conversations/runtime_compaction.py +7 -2
  117. ksadk/conversations/runtime_input.py +16 -4
  118. ksadk/conversations/runtime_payloads.py +4 -0
  119. ksadk/conversations/runtime_persistence.py +192 -24
  120. ksadk/conversations/runtime_preparation.py +85 -6
  121. ksadk/conversations/runtime_projection.py +217 -0
  122. ksadk/conversations/runtime_resume.py +141 -70
  123. ksadk/conversations/tool_receipt_resume.py +387 -0
  124. ksadk/conversations/tool_receipts.py +32 -0
  125. ksadk/deployment/env_forward.py +5 -0
  126. ksadk/deployment/manager.py +15 -6
  127. ksadk/deployment/providers/serverless.py +76 -136
  128. ksadk/deployment/providers/serverless_payloads.py +122 -0
  129. ksadk/evaluation/agent_eval_client.py +2 -1
  130. ksadk/evaluation/contracts.py +78 -3
  131. ksadk/evaluation/evaluators.py +166 -1
  132. ksadk/evaluation/evidence.py +271 -4
  133. ksadk/evaluation/local_adapter.py +76 -12
  134. ksadk/evaluation/studio_build_adapter.py +73 -9
  135. ksadk/events/adapters/_a2a_checkpoint.py +151 -0
  136. ksadk/events/adapters/_a2a_snapshot.py +18 -14
  137. ksadk/events/adapters/_a2a_support.py +15 -3
  138. ksadk/events/adapters/_a2a_tool_activity.py +123 -0
  139. ksadk/events/adapters/_codex_errors.py +49 -0
  140. ksadk/events/adapters/_langgraph_support.py +12 -1
  141. ksadk/events/adapters/a2a.py +54 -26
  142. ksadk/events/adapters/adk.py +188 -30
  143. ksadk/events/adapters/codex.py +33 -7
  144. ksadk/events/adapters/langgraph.py +16 -6
  145. ksadk/events/canonical.py +5 -0
  146. ksadk/events/canonical_store.py +200 -4
  147. ksadk/events/reducer.py +82 -0
  148. ksadk/events/run_authority.py +176 -0
  149. ksadk/harness/__init__.py +4 -0
  150. ksadk/harness/artifact_adapters/__init__.py +1 -0
  151. ksadk/harness/artifact_adapters/native_page_budget.py +172 -0
  152. ksadk/harness/artifact_adapters/native_search.py +249 -0
  153. ksadk/harness/artifact_adapters/native_search_budget.py +134 -0
  154. ksadk/harness/artifact_adapters/native_store.py +115 -0
  155. ksadk/harness/artifact_adapters/native_tool.py +222 -0
  156. ksadk/harness/artifact_store.py +26 -9
  157. ksadk/harness/browser_adapters/__init__.py +6 -0
  158. ksadk/harness/browser_adapters/native_browser_tool.py +92 -0
  159. ksadk/harness/browser_adapters/native_dom_snapshot.py +187 -0
  160. ksadk/harness/browser_adapters/native_playwright.py +953 -0
  161. ksadk/harness/browser_adapters/native_vision.py +295 -0
  162. ksadk/harness/capability_adapters/__init__.py +1 -0
  163. ksadk/harness/capability_adapters/native_dsh.py +289 -0
  164. ksadk/harness/capability_runtime.py +104 -9
  165. ksadk/harness/compaction_record.py +31 -0
  166. ksadk/harness/compiler.py +3 -3
  167. ksadk/harness/config.py +28 -5
  168. ksadk/harness/conformance/contract.py +9 -11
  169. ksadk/harness/context_engine.py +18 -33
  170. ksadk/harness/context_sections.py +397 -0
  171. ksadk/harness/diagnostic_adapters/__init__.py +1 -0
  172. ksadk/harness/diagnostic_adapters/contracts.py +70 -0
  173. ksadk/harness/diagnostic_adapters/lsp_rpc.py +210 -0
  174. ksadk/harness/diagnostic_adapters/native_lsp.py +286 -0
  175. ksadk/harness/engine/completion_guard.py +16 -1
  176. ksadk/harness/engine/context_pipeline.py +864 -92
  177. ksadk/harness/engine/graph_builder.py +123 -630
  178. ksadk/harness/engine/hooked_reasoner.py +380 -0
  179. ksadk/harness/engine/langgraph.py +126 -8
  180. ksadk/harness/engine/mcp_disclosure.py +65 -114
  181. ksadk/harness/engine/phases/__init__.py +6 -0
  182. ksadk/harness/engine/phases/context.py +48 -0
  183. ksadk/harness/engine/phases/interaction.py +31 -0
  184. ksadk/harness/engine/phases/reason.py +552 -0
  185. ksadk/harness/engine/phases/tools.py +638 -0
  186. ksadk/harness/engine/policy_runtime.py +21 -10
  187. ksadk/harness/engine/postgres_checkpointer.py +325 -2
  188. ksadk/harness/engine/run_control_bridge.py +21 -1
  189. ksadk/harness/engine/run_types.py +10 -0
  190. ksadk/harness/engine/skill_disclosure.py +98 -12
  191. ksadk/harness/engine/steering.py +177 -0
  192. ksadk/harness/engine/subagents.py +18 -1
  193. ksadk/harness/engine/tool_dispatch.py +112 -7
  194. ksadk/harness/events.py +39 -2
  195. ksadk/harness/execution_policy.py +10 -0
  196. ksadk/harness/hooks.py +259 -0
  197. ksadk/harness/loop/reason.py +281 -36
  198. ksadk/harness/loop/tools.py +157 -12
  199. ksadk/harness/managed_runtime.py +254 -94
  200. ksadk/harness/mcp_disclosure_contract.py +85 -0
  201. ksadk/harness/mcp_result_policy.py +44 -0
  202. ksadk/harness/mcp_runtime.py +33 -0
  203. ksadk/harness/micro_compact.py +69 -0
  204. ksadk/harness/model_adapters/__init__.py +1 -0
  205. ksadk/harness/model_adapters/native_mcp_media.py +100 -0
  206. ksadk/harness/model_adapters/native_mcp_result.py +36 -0
  207. ksadk/harness/model_adapters/native_openai.py +703 -0
  208. ksadk/harness/model_capability.py +223 -8
  209. ksadk/harness/model_provider.py +214 -4
  210. ksadk/harness/multimodal.py +57 -0
  211. ksadk/harness/native/__init__.py +1 -0
  212. ksadk/harness/native/activation_contract.py +25 -0
  213. ksadk/harness/native/activation_recovery.py +29 -0
  214. ksadk/harness/native/adapters.py +291 -0
  215. ksadk/harness/native/artifact_contract.py +14 -0
  216. ksadk/harness/native/artifact_partial_read_capacity.py +171 -0
  217. ksadk/harness/native/artifact_read_capacity.py +182 -0
  218. ksadk/harness/native/artifact_read_contract.py +27 -0
  219. ksadk/harness/native/artifact_runtime.py +408 -0
  220. ksadk/harness/native/artifact_search_capacity.py +206 -0
  221. ksadk/harness/native/artifact_search_contract.py +40 -0
  222. ksadk/harness/native/artifact_single_read_capacity.py +170 -0
  223. ksadk/harness/native/browser_contract.py +229 -0
  224. ksadk/harness/native/browser_recovery.py +183 -0
  225. ksadk/harness/native/capabilities.py +204 -0
  226. ksadk/harness/native/child_approval.py +60 -0
  227. ksadk/harness/native/child_budget.py +136 -0
  228. ksadk/harness/native/child_cancellation.py +80 -0
  229. ksadk/harness/native/child_input.py +98 -0
  230. ksadk/harness/native/child_input_runtime.py +59 -0
  231. ksadk/harness/native/child_interruption.py +81 -0
  232. ksadk/harness/native/child_lifecycle.py +246 -0
  233. ksadk/harness/native/child_lifecycle_contract.py +91 -0
  234. ksadk/harness/native/child_notifications.py +75 -0
  235. ksadk/harness/native/child_promotion.py +66 -0
  236. ksadk/harness/native/command_runtime.py +99 -0
  237. ksadk/harness/native/completion.py +302 -0
  238. ksadk/harness/native/completion_text.py +116 -0
  239. ksadk/harness/native/context.py +358 -0
  240. ksadk/harness/native/context_artifact_page.py +75 -0
  241. ksadk/harness/native/context_compaction_projection.py +131 -0
  242. ksadk/harness/native/context_contributions.py +320 -0
  243. ksadk/harness/native/context_contributor_contract.py +133 -0
  244. ksadk/harness/native/context_driver.py +820 -0
  245. ksadk/harness/native/context_fresh_tools.py +49 -0
  246. ksadk/harness/native/context_history.py +272 -0
  247. ksadk/harness/native/context_microcompact.py +61 -0
  248. ksadk/harness/native/context_partial_summary.py +58 -0
  249. ksadk/harness/native/context_partition.py +492 -0
  250. ksadk/harness/native/context_pruning.py +275 -0
  251. ksadk/harness/native/context_reduction.py +60 -0
  252. ksadk/harness/native/context_replay.py +148 -0
  253. ksadk/harness/native/context_request_projection.py +526 -0
  254. ksadk/harness/native/context_runtime.py +976 -0
  255. ksadk/harness/native/context_runtime_contracts.py +409 -0
  256. ksadk/harness/native/context_search.py +112 -0
  257. ksadk/harness/native/context_source_fragments.py +188 -0
  258. ksadk/harness/native/context_source_progress.py +119 -0
  259. ksadk/harness/native/context_summary_planning.py +788 -0
  260. ksadk/harness/native/context_summary_recovery.py +105 -0
  261. ksadk/harness/native/delivery_review.py +331 -0
  262. ksadk/harness/native/delivery_review_contract.py +54 -0
  263. ksadk/harness/native/effect_admission.py +82 -0
  264. ksadk/harness/native/effect_control.py +318 -0
  265. ksadk/harness/native/effect_receipts.py +729 -0
  266. ksadk/harness/native/effect_runtime.py +303 -0
  267. ksadk/harness/native/effective_configuration.py +28 -0
  268. ksadk/harness/native/engine.py +945 -0
  269. ksadk/harness/native/final_output.py +34 -0
  270. ksadk/harness/native/final_submission.py +58 -0
  271. ksadk/harness/native/finalization_workspace.py +96 -0
  272. ksadk/harness/native/fork.py +151 -0
  273. ksadk/harness/native/guidance.py +91 -0
  274. ksadk/harness/native/history.py +381 -0
  275. ksadk/harness/native/hook_executor.py +205 -0
  276. ksadk/harness/native/hook_runtime_contract.py +282 -0
  277. ksadk/harness/native/interaction_observation.py +117 -0
  278. ksadk/harness/native/lease.py +103 -0
  279. ksadk/harness/native/machine.py +974 -0
  280. ksadk/harness/native/mcp_result_governance.py +156 -0
  281. ksadk/harness/native/model.py +570 -0
  282. ksadk/harness/native/model_feedback.py +63 -0
  283. ksadk/harness/native/model_input.py +30 -0
  284. ksadk/harness/native/model_recovery_contract.py +35 -0
  285. ksadk/harness/native/model_replay.py +128 -0
  286. ksadk/harness/native/model_request.py +133 -0
  287. ksadk/harness/native/model_runtime.py +196 -0
  288. ksadk/harness/native/model_takeover.py +419 -0
  289. ksadk/harness/native/model_tool_batch.py +40 -0
  290. ksadk/harness/native/observed_progress.py +46 -0
  291. ksadk/harness/native/operator_guidance.py +201 -0
  292. ksadk/harness/native/output_contract.py +80 -0
  293. ksadk/harness/native/output_recovery.py +24 -0
  294. ksadk/harness/native/ports.py +749 -0
  295. ksadk/harness/native/precompact_hook_runtime.py +171 -0
  296. ksadk/harness/native/replay_context.py +149 -0
  297. ksadk/harness/native/request_media.py +112 -0
  298. ksadk/harness/native/restore_contract.py +16 -0
  299. ksadk/harness/native/run_clock.py +120 -0
  300. ksadk/harness/native/state.py +436 -0
  301. ksadk/harness/native/steering.py +170 -0
  302. ksadk/harness/native/stop_hook_runtime.py +151 -0
  303. ksadk/harness/native/studio_admission.py +29 -0
  304. ksadk/harness/native/subagents.py +86 -0
  305. ksadk/harness/native/task_policy.py +99 -0
  306. ksadk/harness/native/task_progress.py +86 -0
  307. ksadk/harness/native/terminal_verification.py +43 -0
  308. ksadk/harness/native/tool_argument_feedback.py +178 -0
  309. ksadk/harness/native/tool_budget.py +253 -0
  310. ksadk/harness/native/tool_error_streak.py +89 -0
  311. ksadk/harness/native/tool_hook_runtime.py +343 -0
  312. ksadk/harness/native/tool_input_validation.py +58 -0
  313. ksadk/harness/native/tool_interruption.py +96 -0
  314. ksadk/harness/native/tool_observation.py +259 -0
  315. ksadk/harness/native/tool_progress.py +223 -0
  316. ksadk/harness/native/tool_recovery.py +25 -0
  317. ksadk/harness/native/tool_scheduling.py +33 -0
  318. ksadk/harness/native/tool_schema.py +126 -0
  319. ksadk/harness/native/transitions.py +151 -0
  320. ksadk/harness/native/turn_control.py +98 -0
  321. ksadk/harness/native/user_document.py +98 -0
  322. ksadk/harness/native/user_document_capture.py +49 -0
  323. ksadk/harness/native/user_input.py +87 -0
  324. ksadk/harness/native/verification.py +432 -0
  325. ksadk/harness/native/verification_binding.py +40 -0
  326. ksadk/harness/native/verification_runtime.py +233 -0
  327. ksadk/harness/native/verification_workspace.py +67 -0
  328. ksadk/harness/native_driver_failure.py +123 -0
  329. ksadk/harness/native_facade.py +977 -0
  330. ksadk/harness/native_factory.py +244 -0
  331. ksadk/harness/native_projection.py +503 -0
  332. ksadk/harness/native_runtime.py +621 -0
  333. ksadk/harness/native_shadow.py +250 -0
  334. ksadk/harness/native_shadow_inputs.py +146 -0
  335. ksadk/harness/progress_adapters/__init__.py +5 -0
  336. ksadk/harness/progress_adapters/native_todo.py +75 -0
  337. ksadk/harness/prompt_cache.py +65 -3
  338. ksadk/harness/public_activity.py +59 -0
  339. ksadk/harness/public_output.py +30 -0
  340. ksadk/harness/public_summary.py +36 -0
  341. ksadk/harness/reasoner.py +331 -22
  342. ksadk/harness/rpc.py +200 -0
  343. ksadk/harness/run_control.py +107 -2
  344. ksadk/harness/runtime.py +11 -2
  345. ksadk/harness/runtime_capabilities.py +71 -1
  346. ksadk/harness/sandbox_backend.py +203 -0
  347. ksadk/harness/sdk.py +153 -0
  348. ksadk/harness/session_store.py +370 -24
  349. ksadk/harness/skill_adapters/__init__.py +1 -0
  350. ksadk/harness/skill_adapters/native_bundle.py +148 -0
  351. ksadk/harness/skill_adapters/native_disclosure.py +306 -0
  352. ksadk/harness/skill_intent.py +47 -0
  353. ksadk/harness/skill_runtime.py +74 -16
  354. ksadk/harness/spec.py +20 -10
  355. ksadk/harness/state.py +2 -0
  356. ksadk/harness/stores/__init__.py +14 -0
  357. ksadk/harness/stores/native_command_settlement.py +65 -0
  358. ksadk/harness/stores/native_contract.py +311 -0
  359. ksadk/harness/stores/native_memory.py +326 -0
  360. ksadk/harness/stores/native_memory_archive.py +70 -0
  361. ksadk/harness/stores/native_migration_cli.py +91 -0
  362. ksadk/harness/stores/native_outbox.py +56 -0
  363. ksadk/harness/stores/native_sqlite.py +782 -0
  364. ksadk/harness/stores/native_sqlite_audit.py +29 -0
  365. ksadk/harness/stores/native_sqlite_migration.py +252 -0
  366. ksadk/harness/subagent.py +12 -2
  367. ksadk/harness/subagent_adapters/checkpoint_recovery.py +120 -0
  368. ksadk/harness/subagent_adapters/native_background_child.py +803 -0
  369. ksadk/harness/subagent_adapters/native_child.py +819 -0
  370. ksadk/harness/subagent_adapters/native_child_approval.py +128 -0
  371. ksadk/harness/subagent_adapters/native_child_continuation.py +51 -0
  372. ksadk/harness/subagent_adapters/native_child_result.py +392 -0
  373. ksadk/harness/subagent_adapters/prepared_recovery.py +120 -0
  374. ksadk/harness/tool_display.py +75 -0
  375. ksadk/harness/tool_display_contract.py +134 -0
  376. ksadk/harness/tool_output.py +141 -0
  377. ksadk/harness/tool_policy.py +3 -0
  378. ksadk/harness/tool_result_policy.py +216 -0
  379. ksadk/harness/tools.py +52 -0
  380. ksadk/harness/workspace_adapters/__init__.py +6 -0
  381. ksadk/harness/workspace_adapters/native_directory.py +180 -0
  382. ksadk/harness/workspace_adapters/native_git.py +208 -0
  383. ksadk/harness/workspace_adapters/native_isolation.py +350 -0
  384. ksadk/interaction/providers/langgraph.py +8 -1
  385. ksadk/kernel/bootstrap.py +144 -160
  386. ksadk/kernel/contract_fingerprints.py +45 -1
  387. ksadk/kernel/contracts.py +25 -0
  388. ksadk/kernel/control.py +153 -33
  389. ksadk/kernel/execution_grants.py +209 -2
  390. ksadk/kernel/execution_grants_memory.py +127 -8
  391. ksadk/kernel/execution_grants_postgres.py +188 -10
  392. ksadk/kernel/execution_grants_sqlite.py +235 -9
  393. ksadk/kernel/execution_host_ingress.py +1071 -0
  394. ksadk/kernel/history_projection.py +120 -0
  395. ksadk/kernel/ingress.py +117 -204
  396. ksadk/kernel/ingress_bootstrap.py +268 -0
  397. ksadk/kernel/legacy_history.py +179 -0
  398. ksadk/kernel/memory_store.py +38 -48
  399. ksadk/kernel/memory_store_projection.py +63 -0
  400. ksadk/kernel/postgres_event_log.py +181 -0
  401. ksadk/kernel/postgres_store.py +167 -198
  402. ksadk/kernel/readiness.py +125 -0
  403. ksadk/kernel/recovery.py +89 -3
  404. ksadk/kernel/recovery_projection.py +124 -0
  405. ksadk/kernel/runtime_checkpoint.py +34 -0
  406. ksadk/kernel/sql/001_agent_kernel.sql +5 -0
  407. ksadk/kernel/sqlite_schema.py +100 -0
  408. ksadk/kernel/sqlite_store.py +147 -216
  409. ksadk/kernel/store.py +47 -15
  410. ksadk/kernel/teams_artifact_tools.py +392 -0
  411. ksadk/kernel/teams_effects.py +851 -0
  412. ksadk/kernel/teams_execution_context.py +408 -0
  413. ksadk/kernel/teams_host_http.py +237 -0
  414. ksadk/kernel/teams_material_transport.py +245 -0
  415. ksadk/kernel/teams_materials.py +318 -0
  416. ksadk/kernel/teams_runtime_bootstrap.py +604 -0
  417. ksadk/kernel/teams_tool_runtime.py +273 -0
  418. ksadk/kernel/teams_workspace.py +259 -0
  419. ksadk/kernel/worker.py +323 -332
  420. ksadk/kernel/worker_controls.py +454 -0
  421. ksadk/kernel/worker_identity.py +27 -0
  422. ksadk/kernel/worker_interactions.py +197 -0
  423. ksadk/knowledge_base/client.py +15 -1
  424. ksadk/managed_runtime.py +29 -2
  425. ksadk/memory/adk/backends/http_ltm_backend.py +2 -1
  426. ksadk/memory/adk/backends/sdk_ltm_backend.py +15 -1
  427. ksadk/memory/adk/bounded_database_session_service.py +232 -0
  428. ksadk/memory/adk/resilient_session_service.py +115 -15
  429. ksadk/memory/adk/short_term_memory.py +8 -9
  430. ksadk/memory/extraction.py +120 -7
  431. ksadk/memory/providers/local_candidates.py +183 -0
  432. ksadk/memory/providers/local_sqlite.py +252 -78
  433. ksadk/memory/resolved_policy.py +12 -7
  434. ksadk/model_gateway/__init__.py +29 -0
  435. ksadk/model_gateway/client.py +253 -0
  436. ksadk/model_gateway/events.py +113 -0
  437. ksadk/model_gateway/matrix.py +297 -0
  438. ksadk/model_proxy/compat.py +670 -0
  439. ksadk/model_proxy/server.py +10 -7
  440. ksadk/model_proxy/transform.py +37 -9
  441. ksadk/plugins/bridges/dsh.py +18 -20
  442. ksadk/plugins/bridges/dsh_profile_manifest.py +36 -0
  443. ksadk/plugins/builtin_resources.py +199 -0
  444. ksadk/plugins/builtins.py +380 -186
  445. ksadk/plugins/context_contributor.py +15 -4
  446. ksadk/plugins/dsh_dependency_policy.py +41 -0
  447. ksadk/plugins/dsh_toolchain.py +24 -35
  448. ksadk/plugins/execution_host.py +8 -0
  449. ksadk/plugins/host.py +73 -5
  450. ksadk/plugins/kernel_adapter.py +44 -1
  451. ksadk/plugins/memory_provider.py +180 -0
  452. ksadk/plugins/providers/_process_group.py +50 -0
  453. ksadk/plugins/providers/bundles/dsh-knowledge/index.mjs +24 -2
  454. ksadk/plugins/providers/bundles/dsh-knowledge/package.json +1 -1
  455. ksadk/plugins/providers/bundles/dsh-memory/package.json +1 -1
  456. ksadk/plugins/providers/bundles/dsh-skill-center/package.json +1 -1
  457. ksadk/plugins/providers/bundles/dsh-teams-tools/package.json +1 -1
  458. ksadk/plugins/providers/bundles/ksadk-dsh-capability-host/package.json +1 -1
  459. ksadk/plugins/providers/codex.py +42 -38
  460. ksadk/plugins/providers/codex_native.py +20 -0
  461. ksadk/plugins/providers/codex_skills.py +46 -0
  462. ksadk/plugins/providers/dsh_capabilities.py +16 -171
  463. ksadk/plugins/providers/dsh_connector_lease.py +225 -0
  464. ksadk/plugins/providers/dsh_mcp.py +163 -1
  465. ksadk/plugins/providers/harness.py +247 -34
  466. ksadk/plugins/providers/harness_context.py +61 -0
  467. ksadk/plugins/providers/harness_contributions.py +241 -0
  468. ksadk/plugins/providers/harness_delegation.py +91 -14
  469. ksadk/plugins/providers/harness_hooks.py +121 -0
  470. ksadk/plugins/providers/harness_managed.py +269 -175
  471. ksadk/plugins/providers/harness_tools.py +165 -36
  472. ksadk/plugins/providers/legacy_catalog.py +1 -1
  473. ksadk/plugins/providers/mcp_projection.py +100 -3
  474. ksadk/plugins/providers/platform_resources.py +16 -6
  475. ksadk/plugins/teams/application.py +3 -143
  476. ksadk/plugins/teams/build_artifacts.py +460 -0
  477. ksadk/plugins/teams/cloud_contract_fingerprints.py +22 -0
  478. ksadk/plugins/teams/cloud_contracts.py +928 -0
  479. ksadk/plugins/teams/cloud_permits.py +250 -0
  480. ksadk/plugins/teams/domain.py +201 -148
  481. ksadk/plugins/teams/effect_contracts.py +255 -0
  482. ksadk/plugins/teams/execution_projection.py +255 -0
  483. ksadk/plugins/teams/leader_decisions.py +106 -0
  484. ksadk/plugins/teams/postgres_store.py +299 -85
  485. ksadk/plugins/teams/release_contracts.py +60 -0
  486. ksadk/plugins/teams/runtime.py +116 -40
  487. ksadk/plugins/teams/store.py +214 -6
  488. ksadk/plugins/teams/task_decisions.py +21 -8
  489. ksadk/plugins/teams/tool_service.py +152 -0
  490. ksadk/plugins/teams/transport.py +8 -3
  491. ksadk/resource_runtime/skill_runtime.py +3 -0
  492. ksadk/resource_runtime/socket_server.py +11 -0
  493. ksadk/resource_runtime/worker.py +28 -4
  494. ksadk/runners/_adk_a2a_stream.py +122 -0
  495. ksadk/runners/_adk_canonical_stream.py +474 -0
  496. ksadk/runners/_langgraph_canonical_streams.py +527 -0
  497. ksadk/runners/_langgraph_runner_streams.py +6 -400
  498. ksadk/runners/_langgraph_tool_projection.py +86 -0
  499. ksadk/runners/_langgraph_tools_transformer.py +27 -0
  500. ksadk/runners/adk_runner.py +195 -290
  501. ksadk/runners/base_runner.py +6 -5
  502. ksadk/runners/langgraph_runner.py +82 -86
  503. ksadk/runners/utils/loader.py +28 -14
  504. ksadk/runtime/_runner_adapter/a2ui_surface.py +145 -0
  505. ksadk/runtime/_runner_adapter/gateway_approvals.py +93 -0
  506. ksadk/runtime/_runner_adapter/stream_mapping.py +83 -128
  507. ksadk/runtime/adapter.py +40 -3
  508. ksadk/runtime/conversation_execution.py +4 -2
  509. ksadk/runtime/executor.py +97 -7
  510. ksadk/runtime/factory.py +13 -6
  511. ksadk/runtime/framework_adapters.py +184 -10
  512. ksadk/runtime/hosted_finalizer.py +4 -6
  513. ksadk/runtime/launch.py +2 -0
  514. ksadk/runtime/runner_adapter.py +123 -13
  515. ksadk/runtime_context.py +2 -0
  516. ksadk/sandbox/backends/e2b.py +30 -4
  517. ksadk/sandbox/backends/local_process.py +10 -0
  518. ksadk/sandbox/e2b_connection.py +9 -1
  519. ksadk/sandbox/local_controls.py +32 -2
  520. ksadk/sandbox/local_launcher.py +34 -0
  521. ksadk/server/factory.py +178 -25
  522. ksadk/server/history_deadline.py +158 -0
  523. ksadk/server/history_page_cache.py +129 -0
  524. ksadk/server/routes/checkpoint_audit.py +88 -0
  525. ksadk/server/routes/checkpoint_listing.py +294 -0
  526. ksadk/server/routes/checkpoint_resolution.py +73 -20
  527. ksadk/server/routes/control.py +51 -3
  528. ksadk/server/routes/display_projection.py +154 -0
  529. ksadk/server/routes/kernel_ingress.py +17 -2
  530. ksadk/server/routes/misc.py +102 -12
  531. ksadk/server/routes/models.py +25 -1
  532. ksadk/server/routes/openai_compat.py +6 -0
  533. ksadk/server/routes/projection.py +159 -216
  534. ksadk/server/routes/run.py +6 -2
  535. ksadk/server/routes/sessions.py +301 -342
  536. ksadk/server/routes/streaming.py +68 -15
  537. ksadk/server/routes/tool_receipt_claim_routes.py +107 -0
  538. ksadk/server/routes/workspace.py +5 -2
  539. ksadk/server/static/assets/{ArtifactsPanel-C1YoaKhx.js → ArtifactsPanel-CILaW-fC.js} +1 -1
  540. ksadk/server/static/assets/{CodeBlock-DHuAWfr5.js → CodeBlock-BVrNVtJC.js} +1 -1
  541. ksadk/server/static/assets/{MathMessageMarkdown-cvdrBwYr.js → MathMessageMarkdown-Dp7c4wWD.js} +1 -1
  542. ksadk/server/static/assets/{MermaidBlock-DeBkbJvI.js → MermaidBlock-Dxop8Rd7.js} +6 -6
  543. ksadk/server/static/assets/{NativeTerminalPanel-oJj44_sV.js → NativeTerminalPanel-Cjjz8B30.js} +1 -1
  544. ksadk/server/static/assets/{abnfDiagram-VCTEODGH-Dhfv25o2.js → abnfDiagram-VCTEODGH-ckWDmOzw.js} +1 -1
  545. ksadk/server/static/assets/{arc-C35w-pn1.js → arc-DqxJede-.js} +1 -1
  546. ksadk/server/static/assets/architecture-7GRP2DOG-pn_DuLy6.js +1 -0
  547. ksadk/server/static/assets/{architectureDiagram-5GKGNRK7-FoMY_u6D.js → architectureDiagram-5GKGNRK7-SEQA4bVi.js} +1 -1
  548. ksadk/server/static/assets/{blockDiagram-NRAW4CY4-zD4Rt7oE.js → blockDiagram-NRAW4CY4-pM0B-6NJ.js} +1 -1
  549. ksadk/server/static/assets/{c4Diagram-UCG6FXSJ-BCtGGkhN.js → c4Diagram-UCG6FXSJ-m2tuCNVq.js} +1 -1
  550. ksadk/server/static/assets/channel-fySsnkft.js +1 -0
  551. ksadk/server/static/assets/chunk-2Q5K7J3B-BheGxKRQ.js +1 -0
  552. ksadk/server/static/assets/{chunk-5VM5RSS4-C2Ms13_k.js → chunk-5VM5RSS4-GJHDr9Ww.js} +1 -1
  553. ksadk/server/static/assets/{chunk-F27PBJKO-BoTj-qgx.js → chunk-F27PBJKO-CbAN45AV.js} +1 -1
  554. ksadk/server/static/assets/{chunk-G27WJ6UU-DOMyyjHH.js → chunk-G27WJ6UU-D5ngbbzF.js} +1 -1
  555. ksadk/server/static/assets/{chunk-JWPE2WC7-DbJJqDli.js → chunk-JWPE2WC7-Cc5IwuYC.js} +1 -1
  556. ksadk/server/static/assets/{chunk-LCL6LL3I-CLuRCt_W.js → chunk-LCL6LL3I-CprgDDdJ.js} +1 -1
  557. ksadk/server/static/assets/{chunk-POPQ4Y6H-D0Dz847e.js → chunk-POPQ4Y6H-CDZhSy82.js} +1 -1
  558. ksadk/server/static/assets/{chunk-RHFEMEQ7-BWSRzEcd.js → chunk-RHFEMEQ7-CZchUNro.js} +1 -1
  559. ksadk/server/static/assets/{chunk-SVP7TREG-CPoHqkCY.js → chunk-SVP7TREG-BVruqk6T.js} +1 -1
  560. ksadk/server/static/assets/{chunk-XXDRQBXY-Bx1jS-X-.js → chunk-XXDRQBXY-4ld9SxPN.js} +1 -1
  561. ksadk/server/static/assets/classDiagram-DTDB5LWJ-DSfLtNYm.js +1 -0
  562. ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-DSfLtNYm.js +1 -0
  563. ksadk/server/static/assets/{cose-bilkent-JH36ORCC-LzyWm4m0.js → cose-bilkent-JH36ORCC-Dmyyk_Vp.js} +1 -1
  564. ksadk/server/static/assets/cynefin-OW5HDTMX-PFWIGtvA.js +1 -0
  565. ksadk/server/static/assets/{cynefinDiagram-5FMLGOSQ-BlW_bHED.js → cynefinDiagram-5FMLGOSQ-BIyq3EJb.js} +1 -1
  566. ksadk/server/static/assets/{dagre-3AP2YEHR-Bu_S86_i.js → dagre-3AP2YEHR-eBw08Qdk.js} +1 -1
  567. ksadk/server/static/assets/{dagre-B7nCawed.js → dagre-DfvnEf45.js} +1 -1
  568. ksadk/server/static/assets/{diagram-S7CK7UJ4-W_Zow2Km.js → diagram-S7CK7UJ4-CUSrE3Mg.js} +1 -1
  569. ksadk/server/static/assets/{diagram-UQ7AKVKN-CLIKAHON.js → diagram-UQ7AKVKN-Dq5Zw2gy.js} +1 -1
  570. ksadk/server/static/assets/{diagram-VSXAHHWV-Dv6_QAlN.js → diagram-VSXAHHWV-DyUHfNwT.js} +1 -1
  571. ksadk/server/static/assets/{diagram-VX7I27RA-BWAWRDtZ.js → diagram-VX7I27RA-CREmM89O.js} +1 -1
  572. ksadk/server/static/assets/{diagram-Z3DM3KII-XA_rWMss.js → diagram-Z3DM3KII-Dw9zhHUK.js} +1 -1
  573. ksadk/server/static/assets/{dist-CGt_UGeO.js → dist-0uJ4tdx6.js} +1 -1
  574. ksadk/server/static/assets/{dist-C0J5_3qM.js → dist-AEv-vXf-.js} +1 -1
  575. ksadk/server/static/assets/{dist-CIpWk_5f.js → dist-B49GzZDb.js} +1 -1
  576. ksadk/server/static/assets/{dist-B3bA05-e.js → dist-B8Myiet9.js} +1 -1
  577. ksadk/server/static/assets/{dist-DWfdmz9s.js → dist-BBsYarrI.js} +1 -1
  578. ksadk/server/static/assets/{dist-BLpRy_Yx.js → dist-BQMrUwdI.js} +1 -1
  579. ksadk/server/static/assets/{dist-CIRMvUM4.js → dist-BX_-FENW.js} +1 -1
  580. ksadk/server/static/assets/{dist-BdNFAney.js → dist-CD4L6ymH.js} +1 -1
  581. ksadk/server/static/assets/{dist-n3n6t_FO.js → dist-CHpW1BHj.js} +1 -1
  582. ksadk/server/static/assets/{dist-DppE_r-D.js → dist-CVPau3tn.js} +1 -1
  583. ksadk/server/static/assets/{dist-qe8xjesy.js → dist-C_qdxcSH.js} +1 -1
  584. ksadk/server/static/assets/{dist-2vL2sAdF.js → dist-Co68Apq4.js} +1 -1
  585. ksadk/server/static/assets/{dist-1nXCdsej.js → dist-Cp2PaOMY.js} +1 -1
  586. ksadk/server/static/assets/{dist-DNKuMzWc.js → dist-CuR5Bgme.js} +1 -1
  587. ksadk/server/static/assets/{dist-t6MzE0jU.js → dist-DDSyLEOX.js} +1 -1
  588. ksadk/server/static/assets/{dist-CPyieXEf.js → dist-DUxO0R99.js} +1 -1
  589. ksadk/server/static/assets/{dist-DyG6Ya-z.js → dist-QzbGrED_.js} +1 -1
  590. ksadk/server/static/assets/{dist-0X_j2Rcc.js → dist-bupkyEBZ.js} +1 -1
  591. ksadk/server/static/assets/{dist-mqD5TZup.js → dist-cxTGfdqx.js} +1 -1
  592. ksadk/server/static/assets/{dist-M-SrdhhZ.js → dist-hL5p1til.js} +1 -1
  593. ksadk/server/static/assets/{dist-D-92S-Fd.js → dist-lOy23y43.js} +1 -1
  594. ksadk/server/static/assets/{dist-BO_UwatH.js → dist-pq-nD33R.js} +1 -1
  595. ksadk/server/static/assets/{ebnfDiagram-PWID7BFC-BfCzF1Xi.js → ebnfDiagram-PWID7BFC-BS5LdNoR.js} +1 -1
  596. ksadk/server/static/assets/{erDiagram-SSCWMZ5O-Dpz9_0zn.js → erDiagram-SSCWMZ5O-Bv1QtiY-.js} +1 -1
  597. ksadk/server/static/assets/eventmodeling-NTZA5JFV-DsM4upQX.js +1 -0
  598. ksadk/server/static/assets/flowDiagram-A5DVABFB-D5NA16k9.js +1 -0
  599. ksadk/server/static/assets/{ganttDiagram-EL5Y4UJY-DDaG1f4_.js → ganttDiagram-EL5Y4UJY-musi-e0G.js} +1 -1
  600. ksadk/server/static/assets/gitGraph-4MIJSDKK-DUCVz-ih.js +1 -0
  601. ksadk/server/static/assets/{gitGraphDiagram-WWUBYQGX-DcxzRwYC.js → gitGraphDiagram-WWUBYQGX-DH37EGkG.js} +1 -1
  602. ksadk/server/static/assets/index-CZdkAvsv.js +244 -0
  603. ksadk/server/static/assets/index-CvXdHgF3.css +2 -0
  604. ksadk/server/static/assets/info-A6RAGUB7-rw0ZNHZp.js +1 -0
  605. ksadk/server/static/assets/{infoDiagram-RXCK75RN-CYMdn6lQ.js → infoDiagram-RXCK75RN-C1kEtv2y.js} +1 -1
  606. ksadk/server/static/assets/{ishikawaDiagram-5VMMS53U-CLHFAD6c.js → ishikawaDiagram-5VMMS53U-BCNPyMka.js} +1 -1
  607. ksadk/server/static/assets/{journeyDiagram-EYS64GPL-BHSKQGAl.js → journeyDiagram-EYS64GPL--cDbKnjP.js} +1 -1
  608. ksadk/server/static/assets/{kanban-definition-3QL26DDD-D5W3tjTT.js → kanban-definition-3QL26DDD-R1rAVw2f.js} +1 -1
  609. ksadk/server/static/assets/{katex-mtjOi4KM.js → katex-BT-X-V7U.js} +1 -1
  610. ksadk/server/static/assets/{linear-BETqsxE_.js → linear-DTR631iG.js} +1 -1
  611. ksadk/server/static/assets/{mermaid-parser.core-DPWrvs90.js → mermaid-parser.core-DfFzJhX8.js} +2 -2
  612. ksadk/server/static/assets/{mindmap-definition-FBJOCRG2-CJG2mfBP.js → mindmap-definition-FBJOCRG2-D4by_rmk.js} +1 -1
  613. ksadk/server/static/assets/packet-AYTQ26CC-Ca4Hvki9.js +1 -0
  614. ksadk/server/static/assets/{pegDiagram-XKGWAZYB-GjAPNaqn.js → pegDiagram-XKGWAZYB-xWXPEh0c.js} +1 -1
  615. ksadk/server/static/assets/pie-WAS4IAKB-hwiz6fbw.js +1 -0
  616. ksadk/server/static/assets/{pieDiagram-E7YTZNPT-ToIewrqr.js → pieDiagram-E7YTZNPT-BudBDlCI.js} +1 -1
  617. ksadk/server/static/assets/{quadrantDiagram-AXDQQJYC-4d78KiEY.js → quadrantDiagram-AXDQQJYC-DXZ0PCJF.js} +1 -1
  618. ksadk/server/static/assets/radar-RG4KPBEZ-BR1oZXLM.js +1 -0
  619. ksadk/server/static/assets/railroad-74A4TZTK-DfgEISfX.js +1 -0
  620. ksadk/server/static/assets/railroad-abnf-HS5TGJTU-CWOC3rdk.js +1 -0
  621. ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-CiRvHCLY.js +1 -0
  622. ksadk/server/static/assets/railroad-peg-WCYAUIDC-DSDDyJ2f.js +1 -0
  623. ksadk/server/static/assets/{railroadDiagram-O6MQD6OU-DKzx4xfv.js → railroadDiagram-O6MQD6OU-BdAYRYB8.js} +1 -1
  624. ksadk/server/static/assets/{requirementDiagram-EFPCY7ZU-CJYljxQS.js → requirementDiagram-EFPCY7ZU-CBBmaL70.js} +1 -1
  625. ksadk/server/static/assets/{sankeyDiagram-P5KCCOFB-DLU2fV1w.js → sankeyDiagram-P5KCCOFB-Doc3rdwp.js} +1 -1
  626. ksadk/server/static/assets/{sequenceDiagram-WJ2MYXX4-CVqysrA9.js → sequenceDiagram-WJ2MYXX4-0J8R8hWW.js} +1 -1
  627. ksadk/server/static/assets/{sizeCapture-X5ZJPWSS-BUxfmTHc.js → sizeCapture-X5ZJPWSS-CfFLlWsK.js} +1 -1
  628. ksadk/server/static/assets/{stateDiagram-HBIQ2CUA-CNFgYI9E.js → stateDiagram-HBIQ2CUA-jBwkjtTy.js} +1 -1
  629. ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-Cw1fPb5w.js +1 -0
  630. ksadk/server/static/assets/{swimlanes-XN3QIQJK-B6qCMe8N.js → swimlanes-XN3QIQJK-B0WxTQf8.js} +1 -1
  631. ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-Cc3JMBAF.js +8 -0
  632. ksadk/server/static/assets/{timeline-definition-24CTP7MA-CMD3x_U_.js → timeline-definition-24CTP7MA-BfCT_75A.js} +1 -1
  633. ksadk/server/static/assets/treeView-Q6P3EWNA-C05SJHxo.js +1 -0
  634. ksadk/server/static/assets/treemap-WGGIJYW6-BqNLAhc3.js +1 -0
  635. ksadk/server/static/assets/{vennDiagram-4TSXK5OY-BqtUqvXM.js → vennDiagram-4TSXK5OY-9gX1VMDC.js} +1 -1
  636. ksadk/server/static/assets/wardley-WFR3VGLG-DokNktqB.js +1 -0
  637. ksadk/server/static/assets/{wardleyDiagram-VM6X3IG4-B5KfDjI-.js → wardleyDiagram-VM6X3IG4-sEs_BP6O.js} +1 -1
  638. ksadk/server/static/assets/{xychartDiagram-S5SC5T6Z-BaUlSZrn.js → xychartDiagram-S5SC5T6Z-Ll4jvUN9.js} +1 -1
  639. ksadk/server/static/index.html +2 -2
  640. ksadk/sessions/__init__.py +8 -1
  641. ksadk/sessions/_checkpoint_resume_audit.py +44 -0
  642. ksadk/sessions/_in_memory_query.py +112 -0
  643. ksadk/sessions/_local_service_sync.py +157 -156
  644. ksadk/sessions/_local_state_sync.py +152 -0
  645. ksadk/sessions/_local_tables.py +2 -0
  646. ksadk/sessions/_postgres_checkpoint_read.py +323 -0
  647. ksadk/sessions/_postgres_checkpoint_stats.py +330 -0
  648. ksadk/sessions/_postgres_history_read.py +678 -0
  649. ksadk/sessions/_postgres_schema.py +44 -2
  650. ksadk/sessions/_postgres_tables.py +2 -0
  651. ksadk/sessions/_postgres_tool_receipts.py +169 -0
  652. ksadk/sessions/_resilient_read.py +630 -0
  653. ksadk/sessions/_tool_receipt_sync.py +119 -0
  654. ksadk/sessions/agent_identity.py +16 -0
  655. ksadk/sessions/base.py +278 -0
  656. ksadk/sessions/db_budget.py +332 -0
  657. ksadk/sessions/errors.py +12 -0
  658. ksadk/sessions/history_budget.py +36 -0
  659. ksadk/sessions/history_identity.py +27 -0
  660. ksadk/sessions/in_memory.py +376 -113
  661. ksadk/sessions/local_service.py +345 -0
  662. ksadk/sessions/postgres_service.py +515 -549
  663. ksadk/sessions/resilience.py +21 -14
  664. ksadk/sessions/resilient.py +611 -504
  665. ksadk/skills/loader.py +44 -2
  666. ksadk/skills/runtime/backends/e2b.py +17 -14
  667. ksadk/skills/service_client.py +6 -1
  668. ksadk/studio/a2a_bindings.py +407 -0
  669. ksadk/studio/a2a_orchestration.py +178 -0
  670. ksadk/studio/active_run_output.py +92 -0
  671. ksadk/studio/activity_records.py +542 -0
  672. ksadk/studio/agent_configuration.py +173 -0
  673. ksadk/studio/api.py +219 -389
  674. ksadk/studio/api_a2a_routes.py +93 -0
  675. ksadk/studio/api_catalog_routes.py +13 -0
  676. ksadk/studio/api_cloud_chat_routes.py +382 -0
  677. ksadk/studio/api_contracts.py +23 -0
  678. ksadk/studio/attachment_store.py +14 -2
  679. ksadk/studio/build_resolution.py +117 -0
  680. ksadk/studio/build_singleflight.py +37 -0
  681. ksadk/studio/builder.py +20 -2
  682. ksadk/studio/capabilities.py +8 -3
  683. ksadk/studio/cloud.py +83 -377
  684. ksadk/studio/cloud_gateways.py +424 -0
  685. ksadk/studio/cloud_shared_web.py +7 -0
  686. ksadk/studio/codex_agent_service.py +17 -0
  687. ksadk/studio/codex_manifest.py +5 -1
  688. ksadk/studio/codex_provider_build.py +65 -0
  689. ksadk/studio/compiler.py +102 -6
  690. ksadk/studio/contract_base.py +19 -0
  691. ksadk/studio/contracts.py +231 -201
  692. ksadk/studio/dsh_application.py +58 -12
  693. ksadk/studio/dsh_capability_service.py +17 -171
  694. ksadk/studio/dsh_mcp_transport.py +191 -0
  695. ksadk/studio/event_store.py +17 -0
  696. ksadk/studio/execution_host.py +84 -11
  697. ksadk/studio/framework_run.py +18 -0
  698. ksadk/studio/interaction_submission.py +265 -0
  699. ksadk/studio/kernel_registry.py +12 -1
  700. ksadk/studio/lifecycle_status.py +43 -0
  701. ksadk/studio/model_buffered_wire.py +227 -0
  702. ksadk/studio/model_chat_tools.py +143 -0
  703. ksadk/studio/model_client.py +1241 -761
  704. ksadk/studio/model_content_stream.py +129 -0
  705. ksadk/studio/model_credentials.py +196 -0
  706. ksadk/studio/model_profile_service.py +65 -31
  707. ksadk/studio/model_request_projection.py +284 -0
  708. ksadk/studio/model_responses_content.py +296 -0
  709. ksadk/studio/model_usage.py +109 -0
  710. ksadk/studio/model_wire_failure.py +110 -0
  711. ksadk/studio/model_wire_identity.py +138 -0
  712. ksadk/studio/model_wire_snapshot.py +45 -0
  713. ksadk/studio/model_wire_tools.py +283 -0
  714. ksadk/studio/native_acceptance.py +68 -0
  715. ksadk/studio/native_approval.py +111 -0
  716. ksadk/studio/native_browser_port.py +861 -0
  717. ksadk/studio/native_child_mcp.py +87 -0
  718. ksadk/studio/native_child_publication.py +68 -0
  719. ksadk/studio/native_child_skills.py +72 -0
  720. ksadk/studio/native_child_workspace.py +176 -0
  721. ksadk/studio/native_command_policy.py +53 -0
  722. ksadk/studio/native_context_port.py +326 -0
  723. ksadk/studio/native_context_registration.py +54 -0
  724. ksadk/studio/native_document_input.py +88 -0
  725. ksadk/studio/native_final_shape.py +852 -0
  726. ksadk/studio/native_gateway_stream.py +69 -0
  727. ksadk/studio/native_hook_port.py +145 -0
  728. ksadk/studio/native_hook_registration.py +70 -0
  729. ksadk/studio/native_inventory_disclosure.py +223 -0
  730. ksadk/studio/native_kernel_admission.py +84 -0
  731. ksadk/studio/native_language_diagnostics.py +320 -0
  732. ksadk/studio/native_mcp_disclosure.py +198 -0
  733. ksadk/studio/native_mcp_tool_port.py +219 -0
  734. ksadk/studio/native_memory_acceptance.py +362 -0
  735. ksadk/studio/native_memory_context.py +266 -0
  736. ksadk/studio/native_memory_flush.py +174 -0
  737. ksadk/studio/native_memory_sources.py +80 -0
  738. ksadk/studio/native_memory_tool_port.py +360 -0
  739. ksadk/studio/native_memory_turn.py +183 -0
  740. ksadk/studio/native_model_port.py +765 -0
  741. ksadk/studio/native_model_recovery.py +126 -0
  742. ksadk/studio/native_options.py +75 -0
  743. ksadk/studio/native_output_fields.py +58 -0
  744. ksadk/studio/native_planning.py +156 -0
  745. ksadk/studio/native_product.py +988 -0
  746. ksadk/studio/native_read_budget.py +426 -0
  747. ksadk/studio/native_recovery.py +171 -0
  748. ksadk/studio/native_scope.py +35 -0
  749. ksadk/studio/native_session_context_port.py +141 -0
  750. ksadk/studio/native_session_engine.py +14 -0
  751. ksadk/studio/native_skill_source.py +74 -0
  752. ksadk/studio/native_subagent_port.py +700 -0
  753. ksadk/studio/native_terminal_recovery.py +95 -0
  754. ksadk/studio/native_test_action.py +610 -0
  755. ksadk/studio/native_tool_port.py +477 -0
  756. ksadk/studio/native_user_input.py +60 -0
  757. ksadk/studio/native_verification_presentation.py +65 -0
  758. ksadk/studio/native_workspace_diagnostics.py +183 -0
  759. ksadk/studio/native_workspace_port.py +43 -0
  760. ksadk/studio/operations.py +34 -9
  761. ksadk/studio/plugin_composition.py +26 -10
  762. ksadk/studio/plugin_kernel_adapter.py +75 -1
  763. ksadk/studio/plugin_native_activation.py +163 -0
  764. ksadk/studio/plugin_runtime.py +639 -119
  765. ksadk/studio/plugin_runtime_admission.py +57 -0
  766. ksadk/studio/presentation.py +92 -0
  767. ksadk/studio/public_output_store.py +69 -0
  768. ksadk/studio/resource_authority.py +11 -3
  769. ksadk/studio/resource_catalog.py +8 -12
  770. ksadk/studio/run_activity.py +338 -385
  771. ksadk/studio/run_controls.py +185 -0
  772. ksadk/studio/run_service.py +908 -319
  773. ksadk/studio/run_submission_receipts.py +68 -0
  774. ksadk/studio/runtime_catalog.py +23 -4
  775. ksadk/studio/runtime_prewarm.py +22 -0
  776. ksadk/studio/scheduler_assistant.py +4 -1
  777. ksadk/studio/service.py +356 -282
  778. ksadk/studio/service_settings.py +229 -0
  779. ksadk/studio/shared_web.py +393 -169
  780. ksadk/studio/shared_web_response_events.py +180 -0
  781. ksadk/studio/shared_web_run_state.py +133 -0
  782. ksadk/studio/skill_discovery.py +305 -3
  783. ksadk/studio/static/assets/ChannelsPage-xX5HlMId.js +8 -0
  784. ksadk/studio/static/assets/{CodeBlock-DMIf4h33-UIqo-BPW.js → CodeBlock-DMIf4h33-CkufcPYo.js} +3 -3
  785. ksadk/studio/static/assets/{MathMessageMarkdown-BesSeCYl-OhNwH4K9.js → MathMessageMarkdown-BesSeCYl-DYG1Wzbx.js} +1 -1
  786. ksadk/studio/static/assets/{MessageMarkdown-CwMTvgFj-g0I-I4dD.js → MessageMarkdown-CwMTvgFj-BeyLyQHu.js} +2 -2
  787. ksadk/studio/static/assets/TeamsPage-CCaaGaVp.js +3 -0
  788. ksadk/studio/static/assets/index-C7DNDik2.css +2 -0
  789. ksadk/studio/static/assets/index-Cy68gTlH.js +265 -0
  790. ksadk/studio/static/index.html +3 -3
  791. ksadk/studio/teams_catalog.py +33 -0
  792. ksadk/studio/teams_installation.py +4 -1
  793. ksadk/studio/teams_node.py +4 -0
  794. ksadk/studio/teams_node_factory.py +558 -0
  795. ksadk/studio/teams_node_runner.py +3 -0
  796. ksadk/studio/teams_node_v1.py +935 -0
  797. ksadk/studio/teams_remote.py +259 -37
  798. ksadk/studio/templates.py +1 -0
  799. ksadk/studio/workspace_plugins.py +3 -1
  800. ksadk/tools/a2a_policy.py +5 -0
  801. ksadk/tools/artifact_observation.py +53 -0
  802. ksadk/tools/captured_source_artifact.py +68 -0
  803. ksadk/tools/schema.py +34 -0
  804. ksadk/toolsets/__init__.py +82 -88
  805. ksadk/toolsets/a2a.py +89 -89
  806. ksadk/toolsets/sandbox.py +377 -6
  807. ksadk/toolsets/skill_management.py +315 -0
  808. ksadk/toolsets/tool_selection.py +94 -0
  809. ksadk/toolsets/workspace.py +342 -324
  810. ksadk/toolsets/workspace_editing.py +291 -0
  811. ksadk/toolsets/workspace_model_observation.py +104 -0
  812. ksadk/toolsets/workspace_mutation.py +75 -0
  813. ksadk/toolsets/workspace_read_budget.py +216 -0
  814. ksadk/toolsets/workspace_search.py +192 -0
  815. ksadk/toolsets/workspace_search_process.py +129 -0
  816. ksadk/toolsets/workspace_state.py +6 -0
  817. ksadk/version.py +1 -1
  818. ksadk_runtime_common/http_headers.py +18 -0
  819. ksadk_runtime_common/workspace_files/router.py +2 -1
  820. ksadk/server/static/assets/architecture-7GRP2DOG-BCsa26zi.js +0 -1
  821. ksadk/server/static/assets/channel-_KDifNys.js +0 -1
  822. ksadk/server/static/assets/chunk-2Q5K7J3B-HdVkJ9F0.js +0 -1
  823. ksadk/server/static/assets/classDiagram-DTDB5LWJ-BVCW3ZqZ.js +0 -1
  824. ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-BVCW3ZqZ.js +0 -1
  825. ksadk/server/static/assets/cynefin-OW5HDTMX-CNXjFctL.js +0 -1
  826. ksadk/server/static/assets/eventmodeling-NTZA5JFV-B5-8RH9c.js +0 -1
  827. ksadk/server/static/assets/flowDiagram-A5DVABFB-BppFxEKw.js +0 -1
  828. ksadk/server/static/assets/gitGraph-4MIJSDKK-B-AhYj4C.js +0 -1
  829. ksadk/server/static/assets/index-BMd2mucF.css +0 -2
  830. ksadk/server/static/assets/index-D5qyyhTH.js +0 -244
  831. ksadk/server/static/assets/info-A6RAGUB7-0uiKrC35.js +0 -1
  832. ksadk/server/static/assets/packet-AYTQ26CC-BVTmZ_cw.js +0 -1
  833. ksadk/server/static/assets/pie-WAS4IAKB-DyfxBU7V.js +0 -1
  834. ksadk/server/static/assets/radar-RG4KPBEZ-FFontjL2.js +0 -1
  835. ksadk/server/static/assets/railroad-74A4TZTK-D0Uidzen.js +0 -1
  836. ksadk/server/static/assets/railroad-abnf-HS5TGJTU-BOo_9T2h.js +0 -1
  837. ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-D6-BjW7Z.js +0 -1
  838. ksadk/server/static/assets/railroad-peg-WCYAUIDC-CXx__yMm.js +0 -1
  839. ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BXAFScz8.js +0 -1
  840. ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-uIQ2lJKH.js +0 -8
  841. ksadk/server/static/assets/treeView-Q6P3EWNA-CK5GgucZ.js +0 -1
  842. ksadk/server/static/assets/treemap-WGGIJYW6-Bq1u1SpG.js +0 -1
  843. ksadk/server/static/assets/wardley-WFR3VGLG-QAt06uEz.js +0 -1
  844. ksadk/studio/static/assets/ChannelsPage-D_PGmKVq.js +0 -8
  845. ksadk/studio/static/assets/TeamsPage-DqX4HWKQ.js +0 -3
  846. ksadk/studio/static/assets/index-CfEnms6Y.css +0 -2
  847. ksadk/studio/static/assets/index-Dp61rOWZ.js +0 -0
  848. {agentengine_sdk_python-0.8.5.dist-info → agentengine_sdk_python-0.8.6.dist-info}/WHEEL +0 -0
  849. {agentengine_sdk_python-0.8.5.dist-info → agentengine_sdk_python-0.8.6.dist-info}/entry_points.txt +0 -0
  850. {agentengine_sdk_python-0.8.5.dist-info → agentengine_sdk_python-0.8.6.dist-info}/licenses/LICENSE +0 -0
  851. {agentengine_sdk_python-0.8.5.dist-info → agentengine_sdk_python-0.8.6.dist-info}/top_level.txt +0 -0
@@ -7,38 +7,47 @@
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
- import os
11
- import re
12
- from typing import Any
10
+ import logging
13
11
 
14
12
  from langgraph.graph import END, START, StateGraph
15
13
  from langgraph.types import interrupt
16
14
 
17
15
  from ksadk.harness.engine import budgets
18
16
  from ksadk.harness.engine.base import ExecutionEngineError
17
+ from ksadk.harness.engine.hooked_reasoner import (
18
+ _HookedReasoner,
19
+ flush_hook_degradations,
20
+ )
19
21
  from ksadk.harness.engine.langgraph import _GraphState
20
- from ksadk.harness.engine.mcp_disclosure import MCP_CALL_TOOL_TOOL, McpDisclosureCursors
21
22
  from ksadk.harness.engine.policy_runtime import revalidate_policy
22
23
  from ksadk.harness.engine.spans import wrap_node_span
23
- from ksadk.harness.engine.subagents import ChildApprovalPending
24
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
25
  from ksadk.harness.reasoner import HarnessReasoningTurn
34
26
  from ksadk.harness.run_control import ControlAction, RunControlStop
35
27
 
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
- )
28
+ logger = logging.getLogger(__name__)
29
+
30
+
31
+ def _model_failure_error(error: ModelFailoverExhausted) -> RuntimeError:
32
+ """Preserve provider failure facts when the graph boundary wraps errors.
33
+
34
+ The graph engine exposes a plain exception to its outer runner for
35
+ compatibility, but Studio still needs the stable category and HTTP status
36
+ to render an actionable terminal card (especially rate limit 429).
37
+ """
38
+
39
+ wrapped = RuntimeError(str(error))
40
+ failure = error.failure
41
+ # Unknown-kind failures keep the legacy payload shape (no error_category)
42
+ # so sub-agent dispatch still falls back to "child_failed" and applies its
43
+ # retry policy; only classified failures carry their category upstream.
44
+ if failure.kind.value != "unknown":
45
+ setattr(wrapped, "error_category", failure.kind.value)
46
+ if failure.status_code is not None:
47
+ setattr(wrapped, "status_code", failure.status_code)
48
+ if failure.kind.value == "rate_limit":
49
+ setattr(wrapped, "error_code", "MODEL_RATE_LIMITED")
50
+ return wrapped
42
51
 
43
52
 
44
53
  def build_graph(engine, run):
@@ -56,8 +65,11 @@ def build_graph(engine, run):
56
65
  if event.event_type == EventType.USAGE_REPORTED:
57
66
  input_tokens = int(event.payload.get("input_tokens") or 0)
58
67
  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))
68
+ budgets.spend(
69
+ run,
70
+ "tokens",
71
+ int(event.payload.get("total_tokens") or input_tokens + output_tokens),
72
+ )
61
73
  budgets.spend(run, "input_tokens", input_tokens)
62
74
  budgets.spend(run, "output_tokens", output_tokens)
63
75
  if event.event_type == EventType.ARTIFACT_CREATED:
@@ -73,6 +85,9 @@ def build_graph(engine, run):
73
85
 
74
86
  def _before_model(state: _GraphState) -> None:
75
87
  _restore_controller(state)
88
+ if isinstance(engine._reasoner, _HookedReasoner):
89
+ # G5 §5.2:包装后的 Reasoner 从活跃 Run 取钩子上下文(模型调用节点统一设置)。
90
+ engine._active_hook_run = run
76
91
  if run.controller is None:
77
92
  budgets.check(run, "tokens", 0)
78
93
  budgets.check(run, "models")
@@ -124,262 +139,22 @@ def build_graph(engine, run):
124
139
  state["turn_count"] += 1
125
140
  # 收口 5:Turn 区间事件(模型/工具/usage 事件按 seq 落在区间内)。
126
141
  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)
142
+ # Native Harness Phase 1:reason 主体已抽出为引擎无关的纯函数 handler
143
+ # (engine/phases/reason.py)。本包装负责图内前置(策略重校验 / _before_model
144
+ # / turn_count / turn_id 构造),handler 负责 turn 区间内全部事件与状态推进。
145
+ from ksadk.harness.engine.phases.context import PhaseContext
146
+ from ksadk.harness.engine.phases.reason import reason_phase
147
+
148
+ ctx = PhaseContext(spec=spec, run=run, engine=engine, turn_id=turn_id, state=state)
149
+ result = await reason_phase(ctx)
150
+ state.update(result.state)
151
+ # Phase 1 过渡期:handler 仍直接写 run.events/run.seq,故此处仅作纵深防御
152
+ # (seq_id 已推进时不再重复 append;Phase 2 切断直写后此循环成为唯一出口)。
153
+ for ev in result.events:
154
+ if ev.seq_id > run.seq:
155
+ run.events.append(ev)
156
+ run.seq = max(run.seq, ev.seq_id)
157
+ _capture_control_event(ev)
383
158
  return state
384
159
 
385
160
  async def tool_calls(state: _GraphState) -> _GraphState:
@@ -387,361 +162,62 @@ def build_graph(engine, run):
387
162
  _restore_controller(state)
388
163
  run.child_approval_decision = state.get("child_approval_decision")
389
164
  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 []
165
+ # Native Harness Phase 1:tool_calls 主体已抽出为引擎无关的纯函数 handler
166
+ # (engine/phases/tools.py)。本包装负责图内前置(策略重校验 / 控制器恢复 /
167
+ # child-approval 决定接收)与**挂起原语翻译**:handler 返回 NeedInteraction
168
+ # 表示需要外部交互,LangGraph 侧在此调 interrupt() 挂起,resume 后把决定写回
169
+ # 状态并重新进入 handler(同一节点调用内循环,故前置只执行一次——与旧
170
+ # interrupt() 语义下"节点整体重放"的次数一致)。
171
+ from ksadk.harness.engine.phases.context import PhaseContext
172
+ from ksadk.harness.engine.phases.tools import (
173
+ TOOL_APPROVAL_DECISION_KEY,
174
+ TOOL_APPROVAL_REPLAY_ATTR,
175
+ tools_phase,
511
176
  )
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
177
 
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")
178
+ # 重放检测:引擎在挂起时把审批 call_id 记到 run.pending_approval_call_id,
179
+ # attach 亦会从 Checkpoint 重建它(跨进程恢复同样成立)。resume 后 LangGraph
180
+ # 整体重放本节点,而重放期间 handler 仍拿不到决定(决定要等下面 interrupt()
181
+ # 返回)会再走一次挂起分支;旧实现里那一次是 interrupt() 内联返回决定、不走
182
+ # 异常分支,故 handler 据此跳过重复补发的失败态进度。Phase 2 Native 引擎以
183
+ # NeedInteraction 表达挂起,不需该标记。
184
+ setattr(
185
+ run,
186
+ TOOL_APPROVAL_REPLAY_ATTR,
187
+ str(getattr(run, "pending_approval_call_id", "") or ""),
600
188
  )
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
189
 
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
190
+ ctx = PhaseContext(spec=spec, run=run, engine=engine, turn_id="", state=state)
191
+ while True:
192
+ result = await tools_phase(ctx)
193
+ state.update(result.state)
194
+ # Phase 1 过渡期:handler 仍直接写 run.events/run.seq,故此处仅作纵深防御
195
+ # (seq_id 已推进时不再重复 append;Phase 2 切断直写后此循环成为唯一出口)。
196
+ for ev in result.events:
197
+ if ev.seq_id > run.seq:
198
+ run.events.append(ev)
199
+ run.seq = max(run.seq, ev.seq_id)
200
+ _capture_control_event(ev)
201
+ if result.interaction is None:
202
+ return state
203
+ if result.interaction.kind == "child_approval":
204
+ # Phase 1:子 Agent 审批仍由专用 ``child_approval`` 节点挂起
205
+ # (route="child_approval" 由 handler 写入)。不在本节点内 interrupt 的
206
+ # 实测原因:在**已 resume 过**的 tool_calls 任务里再次 interrupt,
207
+ # LangGraph 会把 Checkpoint 的 ``next`` 清空,attach 无法从 snapshot
208
+ # 推断挂起(test_second_child_approval_never_reuses_first_decision)。
209
+ # handler 仍把 child_approval NeedInteraction 显式交还,供 Phase 2
210
+ # Native 引擎直接挂起(届时不再需要图节点)。
211
+ return state
212
+ payload = result.interaction.request_payload
213
+ # LangGraph suspension: interrupt() resumes with the approval decision.
214
+ decision = interrupt(payload)
215
+ state[TOOL_APPROVAL_DECISION_KEY] = {
216
+ "call_id": str(payload.get("call_id") or ""),
217
+ "decision": decision,
218
+ }
219
+ # Re-enter tools to execute the now-decided call.
694
220
 
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
221
 
746
222
  def route(state: _GraphState) -> str:
747
223
  return state["route"]
@@ -757,12 +233,16 @@ def build_graph(engine, run):
757
233
  async def plan_node(state: _GraphState) -> _GraphState:
758
234
  """plan-execute 拓扑的规划节点:一次无工具模型调用产出执行计划。"""
759
235
  _before_model(state)
236
+ reasoning_effort = str(run.request.config.get("effort") or "").strip().lower() or None
760
237
  turn: HarnessReasoningTurn = await engine._reasoner.complete(
761
238
  model=spec.model.profile_ref,
762
239
  prompt=(spec.prompt.instructions or "") + "\n请先给出分步执行计划,再开始执行。",
763
240
  messages=tuple(state["messages"]),
764
241
  tools=[],
242
+ **({"reasoning_effort": reasoning_effort} if reasoning_effort else {}),
765
243
  )
244
+ # 直接调 Reasoner 的节点也要先 flush 降级再发自己的后续事件,保持 seq 单调。
245
+ flush_hook_degradations(engine, run)
766
246
  state["messages"].append(
767
247
  {"role": "assistant", "content": "【执行计划】\n" + (turn.final_text or "")}
768
248
  )
@@ -782,6 +262,7 @@ def build_graph(engine, run):
782
262
  async def review_node(state: _GraphState) -> _GraphState:
783
263
  """plan-execute-review 拓扑的独立审查节点:对最终回答做一次批判。"""
784
264
  _before_model(state)
265
+ reasoning_effort = str(run.request.config.get("effort") or "").strip().lower() or None
785
266
  messages = list(state["messages"]) + [
786
267
  {"role": "user", "content": "请审查以上回答的准确性与遗漏,指出问题(如有)。"}
787
268
  ]
@@ -790,7 +271,9 @@ def build_graph(engine, run):
790
271
  prompt=spec.prompt.instructions or "",
791
272
  messages=tuple(messages),
792
273
  tools=[],
274
+ **({"reasoning_effort": reasoning_effort} if reasoning_effort else {}),
793
275
  )
276
+ flush_hook_degradations(engine, run)
794
277
  state["messages"].append(
795
278
  {"role": "assistant", "content": "【审查】\n" + (turn.final_text or "")}
796
279
  )
@@ -839,6 +322,10 @@ def build_graph(engine, run):
839
322
  reason_mapping = {}
840
323
  if "tool_calls" in reason_successors:
841
324
  reason_mapping["tool_calls"] = "tool_calls"
325
+ # A live user correction may arrive while the final model response is in
326
+ # flight. The reason handler rejects that stale candidate and re-enters
327
+ # itself with the accepted correction, even for text-only plans.
328
+ reason_mapping["reason"] = "reason"
842
329
  reason_mapping["final"] = END if exit_target == "final" else exit_target
843
330
  if "reason" in node_impls and "reason" in plan.nodes and "reason" != exit_target:
844
331
  builder.add_conditional_edges("reason", route, reason_mapping)
@@ -846,9 +333,15 @@ def build_graph(engine, run):
846
333
  if src == "reason":
847
334
  continue # 已由条件边覆盖。
848
335
  if src == "tool_calls":
849
- builder.add_conditional_edges("tool_calls", route, {
850
- "tool_calls": "tool_calls", "child_approval": "child_approval", "reason": dst,
851
- })
336
+ builder.add_conditional_edges(
337
+ "tool_calls",
338
+ route,
339
+ {
340
+ "tool_calls": "tool_calls",
341
+ "child_approval": "child_approval",
342
+ "reason": dst,
343
+ },
344
+ )
852
345
  continue
853
346
  if dst == "final":
854
347
  if src in node_impls and src != "reason":