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
ksadk/codex/runtime.py CHANGED
@@ -22,6 +22,7 @@ import asyncio
22
22
  import json
23
23
  import logging
24
24
  import time
25
+ from contextlib import aclosing
25
26
  from collections.abc import Mapping
26
27
  from dataclasses import dataclass, field
27
28
  from typing import Any, AsyncIterator, Optional
@@ -37,6 +38,7 @@ from ksadk.events.canonical import (
37
38
  RunCompleted,
38
39
  RunFailed,
39
40
  RunInterrupted,
41
+ RunStarted,
40
42
  RuntimeEvent,
41
43
  SourceRef,
42
44
  )
@@ -55,28 +57,13 @@ from ksadk.runtime.adapter import (
55
57
  StartRequest,
56
58
  )
57
59
 
60
+ from ksadk.codex.base_runtime import _CodexAsBaseRuntime
61
+
58
62
  logger = logging.getLogger(__name__)
59
63
 
60
64
  _INTERRUPT_DRAIN_TIMEOUT_SECONDS = 5.0
61
65
 
62
66
 
63
- class _CodexAsBaseRuntime(BaseRuntime):
64
- """把 CodexClient 包装为 BaseRuntime(原生能力面)。"""
65
-
66
- def __init__(self, client: CodexClient) -> None:
67
- self._client = client
68
- self.runtime_type = "codex"
69
-
70
- def native_capabilities(self) -> dict[str, Any]:
71
- return {
72
- "Framework": "codex",
73
- "cancel": "thread",
74
- "pause": "interrupt_then_resume_thread",
75
- "resume": "thread_id",
76
- "live_interaction": True,
77
- }
78
-
79
-
80
67
  @dataclass
81
68
  class _CodexThread:
82
69
  thread_id: str
@@ -92,6 +79,7 @@ class _CodexThread:
92
79
  duration_ms: int | None = None
93
80
  goal_mode: bool = False
94
81
  continuation_preexisting: bool = False
82
+ cancel_experts: Any = None
95
83
 
96
84
 
97
85
  class CodexRuntimeAdapter(RuntimeAdapter):
@@ -106,9 +94,17 @@ class CodexRuntimeAdapter(RuntimeAdapter):
106
94
  bound_skill_paths: Mapping[str, str] | None = None,
107
95
  plugin_bootstrap: CodexPluginBootstrap | None = None,
108
96
  turn_projector: CodexTurnProjection | None = None,
97
+ a2a_experts=(),
98
+ managed_a2a_bootstrap=None,
109
99
  ) -> None:
110
100
  super().__init__(_CodexAsBaseRuntime(client))
111
101
  self._client = client
102
+ self._a2a_experts = tuple(a2a_experts)
103
+ self._managed_a2a_bootstrap = managed_a2a_bootstrap
104
+ if self._a2a_experts:
105
+ if managed_a2a_bootstrap is None:
106
+ raise ValueError("A2A_MANAGED_BOOTSTRAP_REQUIRED")
107
+ client.configure_a2a_experts(self._a2a_experts)
112
108
  self._sandbox_read_only = sandbox_read_only
113
109
  self._turn_timeout_seconds = turn_timeout_seconds
114
110
  if turn_projector is not None and bound_skill_paths:
@@ -125,6 +121,8 @@ class CodexRuntimeAdapter(RuntimeAdapter):
125
121
  self._plugins_bootstrapped = False
126
122
  self._threads: dict[str, _CodexThread] = {}
127
123
  self._requests: dict[str, StartRequest] = {}
124
+ self._canonical_roots: dict[str, RunStarted] = {}
125
+ self._recovery_records: dict[str, Any] = {}
128
126
  self._known_threads: set[str] = set()
129
127
  self._pending_cancels: set[str] = set()
130
128
  # 被杀/interrupt 的 session 不持久化(goal-09 契约 1)。
@@ -133,29 +131,61 @@ class CodexRuntimeAdapter(RuntimeAdapter):
133
131
  self.last_cancel_dropped_approvals: set[str] = set()
134
132
  self._seq = 0
135
133
  self._closed = False
134
+ self._owned_by_app = False
135
+
136
+ def configure_managed_a2a(self, bootstrap) -> None:
137
+ if self._managed_a2a_bootstrap is bootstrap:
138
+ return
139
+ if self._threads or self._known_threads:
140
+ raise RuntimeError("cannot change managed A2A after a native thread starts")
141
+ if self._managed_a2a_bootstrap is not None:
142
+ raise ValueError("A2A_BOOTSTRAP_IDENTITY_CONFLICT")
143
+ self._managed_a2a_bootstrap = bootstrap
136
144
 
137
145
  # ---- capability matrix(v1,诚实声明) ----
138
146
 
139
147
  def capabilities(self) -> RuntimeCapabilityMatrix:
140
- """Codex 真实矩阵:thread 级 cancel/pause/resume + 审批 submit + snapshot
141
- checkpoint 均为后端原生能力;attach/durable_restore 未实现(线程表在本进程,
142
- attach seam 缺失),steer/inject 无原生通道。
148
+ """Native controls plus explicitly configured managed-expert recovery.
149
+
150
+ Ordinary Codex threads retain their existing process-local declaration.
151
+ Recovery needs both an explicit native home and durable A2A storage.
143
152
  """
144
153
 
145
154
  def _unavailable(reason: str) -> RuntimeCapability:
146
155
  return RuntimeCapability(supported=False, mode="unavailable", reason=reason)
147
156
 
157
+ recovery = False
158
+ if self._a2a_experts and getattr(self._client, "_checkpoint_home", None):
159
+ bootstrap = self._managed_a2a_bootstrap
160
+ try:
161
+ recovery = bool(bootstrap.outbound_enabled
162
+ and bootstrap.invocation_checkpoint_store is not None)
163
+ except (AttributeError, RuntimeError):
164
+ recovery = False
165
+
166
+ bootstrap = self._managed_a2a_bootstrap
167
+ scoped_cancel = bool(
168
+ bootstrap is not None and getattr(bootstrap, "scoped_cancel_available", False)
169
+ )
148
170
  return RuntimeCapabilityMatrix(
171
+ scoped_cancel=(RuntimeCapability(supported=True, mode="emulated")
172
+ if scoped_cancel else None),
149
173
  cancel=RuntimeCapability(supported=True, mode="native"),
150
174
  pause=RuntimeCapability(supported=True, mode="native"),
151
175
  resume=RuntimeCapability(supported=True, mode="native"),
152
176
  submit_interaction=RuntimeCapability(supported=True, mode="native"),
153
- attach=_unavailable("codex_process_local_thread_table"),
177
+ attach=(RuntimeCapability(supported=True, mode="native") if recovery
178
+ else _unavailable("codex_process_local_thread_table")),
154
179
  steer=_unavailable("runtime_no_native_steer"),
155
180
  inject=_unavailable("runtime_no_native_inject"),
156
181
  checkpoint=RuntimeCapability(supported=True, mode="native"),
157
- durable_restore=_unavailable("codex_durable_restore_requires_attach_seam"),
158
- goal=RuntimeCapability(supported=True, mode="native"),
182
+ durable_restore=(RuntimeCapability(supported=True, mode="native") if recovery
183
+ else _unavailable("codex_durable_restore_requires_attach_seam")),
184
+ goal=(
185
+ _unavailable("a2a_codex_expert_goal_unsupported")
186
+ if self._a2a_experts
187
+ else RuntimeCapability(supported=True, mode="native")
188
+ ),
159
189
  loop=_unavailable("codex_loop_requires_run_control_spec"),
160
190
  plan=RuntimeCapability(supported=True, mode="native"),
161
191
  interaction_mode="live_submit",
@@ -192,14 +222,15 @@ class CodexRuntimeAdapter(RuntimeAdapter):
192
222
  cwd = request.config.get("cwd")
193
223
  if cwd:
194
224
  thread_config["cwd"] = str(cwd)
195
- # AgentKernel creates one adapter/transport per durable turn and
196
- # closes it after the canonical terminal event. The next turn
197
- # therefore resumes the native thread from a new app-server
198
- # process; an ephemeral Codex thread has no rollout and cannot be
199
- # resumed across that transport boundary.
225
+ # Independently owned adapters close their transport per turn;
226
+ # app-owned adapters may also cross a process boundary on restart.
227
+ # An ephemeral thread has no rollout to resume across either
228
+ # boundary, so retain the native durable thread by default.
200
229
  thread_config.setdefault("ephemeral", False)
201
230
  thread_id = await self._client.start_thread(thread_config)
231
+ self._do_not_persist.discard(thread_id)
202
232
  self._known_threads.add(thread_id)
233
+ self._canonical_roots.pop(thread_id, None)
203
234
  thread = _CodexThread(
204
235
  thread_id=thread_id,
205
236
  continuation_preexisting=bool(provided),
@@ -214,6 +245,91 @@ class CodexRuntimeAdapter(RuntimeAdapter):
214
245
  native_ref={"thread_id": thread_id, "user_id": request.user_id},
215
246
  )
216
247
 
248
+ async def prepare_session_continuation(
249
+ self, record, request: StartRequest, *, agent_instance_id: str, tenant_id: str,
250
+ ) -> None:
251
+ """Only Kernel-loaded completed history can authorize an expert follow-up."""
252
+ if not self._a2a_experts:
253
+ return
254
+ from ksadk.a2a.codex_session_checkpoint import validated_session_checkpoint
255
+
256
+ bridge = getattr(self._client, "_a2a_expert_bridge", None)
257
+ try:
258
+ handle = RunHandle.model_validate(record.metadata.get("handle"))
259
+ checkpoint = validated_session_checkpoint(
260
+ record, handle, request, agent_instance_id=agent_instance_id,
261
+ tenant_id=tenant_id, experts=bridge.experts,
262
+ )
263
+ except (ValueError, TypeError, AttributeError):
264
+ raise ValueError("A2A_CODEX_SESSION_CHECKPOINT_INVALID") from None
265
+ await self._bootstrap_plugins_once()
266
+ await self._client.restore_completed_a2a_thread(checkpoint)
267
+
268
+ async def prepare_attach_record(self, handle: RunHandle, record) -> None:
269
+ from ksadk.a2a.codex_recovery import validated_recovery_checkpoint
270
+
271
+ if not self._a2a_experts:
272
+ return
273
+ if self._managed_a2a_bootstrap is None:
274
+ raise ValueError("A2A_CODEX_RECOVERY_MANAGED_EXPERTS_REQUIRED")
275
+ if handle.run_id in self._known_threads or handle.run_id in self._recovery_records:
276
+ raise ValueError("A2A_CODEX_RECOVERY_THREAD_ALREADY_ATTACHED")
277
+ bridge = self._client._a2a_expert_bridge
278
+ if record.metadata.get("runtime_checkpoint", {}).get("schema") == "codex.a2a.session/v1":
279
+ from ksadk.a2a.codex_session_checkpoint import validated_terminal_recovery_checkpoint
280
+
281
+ self._recovery_records[handle.run_id] = validated_terminal_recovery_checkpoint(
282
+ record, handle, experts=bridge.experts,
283
+ )
284
+ return
285
+ recovery = validated_recovery_checkpoint(record, handle, experts=bridge.experts)
286
+ if recovery.continuation_started:
287
+ raise ValueError("A2A_CODEX_RECOVERY_CONTINUATION_UNCERTAIN")
288
+ self._recovery_records[handle.run_id] = recovery
289
+
290
+ async def attach(self, handle: RunHandle) -> RunHandle:
291
+ if not self._a2a_experts:
292
+ return await super().attach(handle)
293
+ recovery = self._recovery_records.pop(handle.run_id, None)
294
+ if recovery is None:
295
+ raise ValueError("A2A_CODEX_RECOVERY_RECORD_REQUIRED")
296
+ # No cached handle alone may authorize native access or a new turn.
297
+ if (handle.runtime_type != "codex" or handle.session_id != recovery.session_id
298
+ or handle.native_ref.get("thread_id") != recovery.thread_id
299
+ or handle.native_ref.get("user_id") != recovery.start_request.user_id):
300
+ raise ValueError("A2A_CODEX_RECOVERY_HANDLE_CONFLICT")
301
+ from ksadk.a2a.codex_session_checkpoint import CodexSessionCheckpoint
302
+
303
+ if isinstance(recovery, CodexSessionCheckpoint):
304
+ thread = _CodexThread(thread_id=handle.run_id, continuation_preexisting=True)
305
+ thread.__dict__["_terminal_recovery"] = recovery
306
+ self._threads[handle.run_id] = thread
307
+ self._requests[handle.run_id] = recovery.start_request
308
+ self._known_threads.add(handle.run_id)
309
+ return handle
310
+ await self._bootstrap_plugins_once()
311
+ restored = await self._client.restore_managed_a2a_thread(recovery)
312
+ if restored != handle.run_id:
313
+ raise ValueError("A2A_CODEX_RECOVERY_NATIVE_IDENTITY_INVALID")
314
+ thread = _CodexThread(thread_id=restored, continuation_preexisting=True)
315
+ thread.__dict__["_start_request"] = recovery.start_request
316
+ thread.__dict__["_recovery"] = recovery
317
+ self._threads[restored] = thread
318
+ self._requests[restored] = recovery.start_request
319
+ self._known_threads.add(restored)
320
+ return handle
321
+
322
+ async def durable_restore(self, handle: RunHandle) -> RunHandle:
323
+ if not self._a2a_experts:
324
+ return await super().durable_restore(handle)
325
+ return await self.attach(handle)
326
+
327
+ def requires_recovery_stream(self, handle: RunHandle) -> bool:
328
+ thread = self._threads.get(handle.run_id)
329
+ return thread is not None and any(
330
+ thread.__dict__.get(key) is not None for key in ("_recovery", "_terminal_recovery")
331
+ )
332
+
217
333
  async def _bootstrap_plugins_once(self) -> None:
218
334
  config = self._plugin_bootstrap
219
335
  if config is None or self._plugins_bootstrapped:
@@ -238,6 +354,10 @@ class CodexRuntimeAdapter(RuntimeAdapter):
238
354
  return CancelResult.NOT_RUNNING
239
355
  assert thread is not None
240
356
  try:
357
+ # Cancel bound experts while the Kernel still owns this live run.
358
+ # Native interruption can settle the parent before stream teardown.
359
+ if thread.cancel_experts is not None:
360
+ await thread.cancel_experts()
241
361
  # 级联丢弃 pending 工具审批(快照 runtime 自跟踪的 pending 集,供观测)。
242
362
  # 真实 SDK 无独立 drain API:interrupt 后 turn 停止,pending 审批随之失效。
243
363
  self.last_cancel_dropped_approvals = set(thread.pending_approvals)
@@ -252,14 +372,17 @@ class CodexRuntimeAdapter(RuntimeAdapter):
252
372
  if thread.goal_mode
253
373
  else await self._client.interrupt_active_turn(thread.thread_id)
254
374
  )
255
- if not interrupted:
375
+ if not interrupted and thread.__dict__.get("_recovery") is None:
256
376
  # 无活跃 handle 可 interrupt(竞态:turn 刚好结束)→ 视为未在运行。
257
377
  return CancelResult.NOT_RUNNING
258
378
  # 被中断的 session 不持久化(goal-09 契约 1)。
259
379
  self._do_not_persist.add(thread_id)
260
380
  thread.done = True
261
381
  self._threads.pop(thread_id, None)
262
- self._requests.pop(thread_id, None)
382
+ if thread.__dict__.get("_recovery") is None:
383
+ self._requests.pop(thread_id, None)
384
+ # Recovery has no native terminal notification yet. Its consumer
385
+ # still needs the original caller identity to emit run.canceled.
263
386
  self._pending_cancels.discard(thread_id)
264
387
  return CancelResult.INTERRUPTED_ACTIVE_TURN
265
388
  except Exception: # noqa: BLE001
@@ -327,6 +450,7 @@ class CodexRuntimeAdapter(RuntimeAdapter):
327
450
  if handle.run_id in self._do_not_persist:
328
451
  raise ValueError(f"thread {handle.run_id} 已被中断/杀进程,不持久化,不可 resume")
329
452
  self._pending_cancels.discard(handle.run_id)
453
+ self._canonical_roots.pop(handle.run_id, None)
330
454
  self._known_threads.add(target.id)
331
455
  thread = _CodexThread(
332
456
  thread_id=target.id,
@@ -379,9 +503,29 @@ class CodexRuntimeAdapter(RuntimeAdapter):
379
503
  ref={"thread_id": handle.native_ref.get("thread_id")},
380
504
  )
381
505
 
506
+ def adopt_app_lifetime(self) -> None:
507
+ """A shared composition root owns the native transport and projector."""
508
+ self._owned_by_app = True
509
+
510
+ async def close_app_resources(self) -> None:
511
+ if self._owned_by_app:
512
+ await self.close_all()
513
+
382
514
  async def close(self, handle: RunHandle) -> None:
383
515
  self._do_not_persist.add(handle.run_id)
384
- await self.close_all()
516
+ if not self._owned_by_app:
517
+ await self.close_all()
518
+ return
519
+ thread = self._threads.get(handle.run_id)
520
+ if thread is not None and thread.streaming and not thread.done:
521
+ thread.interrupt_event.set()
522
+ await self._client.interrupt_active_turn(thread.thread_id)
523
+ # Preserve the native client's completed-thread cache for the next
524
+ # turn; release only this Kernel run's projections and request.
525
+ self._threads.pop(handle.run_id, None)
526
+ self._requests.pop(handle.run_id, None)
527
+ self._canonical_roots.pop(handle.run_id, None)
528
+ self._pending_cancels.discard(handle.run_id)
385
529
 
386
530
  async def compact_session(self, thread_id: str, config: dict[str, Any]) -> dict[str, Any]:
387
531
  """Compact an existing native session using this build's client."""
@@ -392,10 +536,9 @@ class CodexRuntimeAdapter(RuntimeAdapter):
392
536
  async def close_all(self) -> None:
393
537
  """Dispose every thread and the activation-owned App Server process.
394
538
 
395
- One ``CodexRuntimeAdapter`` owns one client transport. Closing any
396
- attached Kernel handle therefore closes the transport as a unit; this
397
- additive helper also lets a draining AgentProvider clean up a runtime
398
- that has been created but not started yet.
539
+ An independently owned adapter closes the transport with its handle.
540
+ Shared app-owned adapters retain it until application shutdown. This
541
+ helper also lets a draining AgentProvider release an unused runtime.
399
542
  """
400
543
 
401
544
  if self._closed:
@@ -422,6 +565,8 @@ class CodexRuntimeAdapter(RuntimeAdapter):
422
565
  self._do_not_persist.update(thread_ids)
423
566
  self._threads.clear()
424
567
  self._requests.clear()
568
+ self._canonical_roots.clear()
569
+ self._recovery_records.clear()
425
570
  self._known_threads.clear()
426
571
  self._pending_cancels.clear()
427
572
 
@@ -441,25 +586,59 @@ class CodexRuntimeAdapter(RuntimeAdapter):
441
586
  thread = _CodexThread(thread_id=handle.run_id)
442
587
  self._threads[handle.run_id] = thread
443
588
 
589
+ terminal = thread.__dict__.get("_terminal_recovery")
590
+ if terminal is not None:
591
+ from ksadk.a2a.codex_session_checkpoint import terminal_recovery_event
592
+
593
+ try:
594
+ yield await terminal_recovery_event(terminal)
595
+ finally:
596
+ thread.done = True
597
+ self._threads.pop(handle.run_id, None)
598
+ return
599
+
600
+ recovery = thread.__dict__.get("_recovery")
601
+ if recovery is not None:
602
+ await self._restore_canonical_root(handle, recovery)
603
+
444
604
  if handle.run_id in self._pending_cancels:
445
605
  self._pending_cancels.discard(handle.run_id)
446
- yield self._make_run_canceled(
447
- handle,
448
- reason=f"pending_cancel:{CancelResult.PENDING_CANCEL_RECORDED.value}",
449
- )
606
+ try:
607
+ yield self._make_run_canceled(
608
+ handle,
609
+ reason=f"pending_cancel:{CancelResult.PENDING_CANCEL_RECORDED.value}",
610
+ )
611
+ finally:
612
+ self._canonical_roots.pop(handle.run_id, None)
450
613
  return
451
614
 
452
615
  request = thread.__dict__.get("_start_request")
453
616
  resume_state = thread.__dict__.get("_resume")
454
- projected = self._turn_projector.project(
455
- request, resume_state.get("payload") if resume_state is not None else None,
456
- )
457
- run_input = projected.input
617
+ if recovery is None:
618
+ projected = self._turn_projector.project(
619
+ request, resume_state.get("payload") if resume_state is not None else None,
620
+ )
621
+ run_input = projected.input
622
+ else:
623
+ # The original user input is already in the restored transcript.
624
+ # Only verified expert outputs precede the next native turn.
625
+ run_input = []
458
626
  thread.streaming = True
459
627
  thread.turn_id = thread.turn_id or f"turn_{thread.thread_id}"
460
628
  try:
461
- async for event in self._map_codex_stream(handle, thread, run_input):
462
- yield event
629
+ events = self._map_codex_stream(handle, thread, run_input)
630
+ if self._a2a_experts:
631
+ from ksadk.a2a.codex_runtime import managed_codex_events
632
+ events = managed_codex_events(self._client, self._managed_a2a_bootstrap, events, request=request, owner=thread, recovery=recovery)
633
+ async with aclosing(events):
634
+ async for event in events:
635
+ if (isinstance(event, RunStarted) and event.parent_scope_id is None
636
+ and event.run_id == self._event_run_id(handle)):
637
+ # Runtime fallbacks must terminate the public/native
638
+ # root already observed by consumers, even if cancel
639
+ # removes the request/thread before stream teardown.
640
+ self._canonical_roots.setdefault(handle.run_id, event)
641
+ yield event
463
642
  except asyncio.CancelledError:
464
643
  thread.interrupted = True
465
644
  self._do_not_persist.add(handle.run_id)
@@ -474,12 +653,31 @@ class CodexRuntimeAdapter(RuntimeAdapter):
474
653
  await self._client.close()
475
654
  yield self._make_run_failed(handle, "codex turn timed out")
476
655
  except Exception as exc: # noqa: BLE001 通用兜底:任何异常都发 RunFailed
656
+ from ksadk.a2a.codex_runtime import CodexRecoveryCancelled
657
+
477
658
  self._do_not_persist.add(handle.run_id)
478
- yield self._make_run_failed(handle, str(exc))
659
+ if isinstance(exc, CodexRecoveryCancelled):
660
+ yield self._make_run_canceled(handle, reason="recovery_canceled")
661
+ else:
662
+ yield self._make_run_failed(handle, str(exc))
479
663
  finally:
480
664
  thread.streaming = False
481
665
  thread.done = True
482
666
  self._threads.pop(handle.run_id, None)
667
+ self._canonical_roots.pop(handle.run_id, None)
668
+
669
+ async def _restore_canonical_root(self, handle: RunHandle, recovery: Any) -> None:
670
+ from ksadk.events.run_authority import current_run_event_authority
671
+
672
+ authority = current_run_event_authority()
673
+ if authority is None:
674
+ raise RuntimeError("A2A_KERNEL_AUTHORITY_REQUIRED")
675
+ # No tail limit: the root may precede hundreds of native observations.
676
+ # The capability enforces the validated public session/run identity.
677
+ for event in await authority.list(handle.session_id, run_id=recovery.run_id):
678
+ if isinstance(event, RunStarted) and event.parent_scope_id is None:
679
+ self._canonical_roots[handle.run_id] = event
680
+ return
483
681
 
484
682
  async def _map_codex_stream(
485
683
  self,
@@ -607,6 +805,11 @@ class CodexRuntimeAdapter(RuntimeAdapter):
607
805
  chunk_method = (
608
806
  str((chunk or {}).get("method") or "") if isinstance(chunk, dict) else ""
609
807
  )
808
+ if chunk_method in {"ksadk/a2a/toolCall", "ksadk/a2a/toolCancel"}:
809
+ if not self._a2a_experts:
810
+ raise ValueError("A2A_CODEX_EXPERTS_NOT_CONFIGURED")
811
+ yield chunk
812
+ continue
610
813
  if chunk_method in {
611
814
  "item/autoApprovalReview/started",
612
815
  "item/autoApprovalReview/completed",
@@ -627,6 +830,14 @@ class CodexRuntimeAdapter(RuntimeAdapter):
627
830
  timestamp=time.time(),
628
831
  ):
629
832
  event = self._with_caller_scope(event, request)
833
+ if thread.__dict__.get("_recovery") is not None:
834
+ root = self._canonical_roots.get(handle.run_id)
835
+ if (root is not None and isinstance(event, RunStarted)
836
+ and event.run_id == root.run_id and event.parent_scope_id is None):
837
+ # The public run already started before recovery;
838
+ # a new native turn must not start it a second time.
839
+ continue
840
+ event = self._with_recovered_root(event, handle)
630
841
  # 跟踪 pending 审批(cancel 级联丢弃契约依赖该集合)。
631
842
  if isinstance(event, InteractionRequested):
632
843
  if event.interaction_id:
@@ -665,23 +876,31 @@ class CodexRuntimeAdapter(RuntimeAdapter):
665
876
  # ---- canonical run.* helpers (for runtime-owned lifecycle) ----
666
877
 
667
878
  def _event_run_id(self, handle: RunHandle) -> str:
668
- """事件的 canonical run_id:调用方 invocation_id 优先,退回 thread id。
879
+ """事件使用调用方 invocation_id 或 Kernel run_id,最后回退到 thread id。
669
880
 
670
881
  ``handle.run_id`` 是 codex 原生 thread id(resume/cancel 按 thread 寻址);
671
882
  但 canonical RuntimeEvent 的 run_id 必须与调用方
672
- ``StartRequest.metadata['invocation_id']`` 一致(conversation kernel 的
673
- event scope 校验),否则 hosted/web 执行路径会在首个事件上 fail。
883
+ ``StartRequest.metadata['invocation_id']`` 或 Kernel 的 ``run_id`` 一致,
884
+ 否则同一原生 thread 上的后续调用可能复用事件身份并被去重。
674
885
  """
886
+ root = self._canonical_roots.get(handle.run_id)
887
+ if root is not None:
888
+ return root.run_id
675
889
  request = self._requests.get(handle.run_id)
676
890
  if request is not None:
677
891
  invocation_id = str(
678
- (getattr(request, "metadata", None) or {}).get("invocation_id") or ""
892
+ (getattr(request, "metadata", None) or {}).get("invocation_id")
893
+ or (getattr(request, "metadata", None) or {}).get("run_id")
894
+ or ""
679
895
  ).strip()
680
896
  if invocation_id:
681
897
  return invocation_id
682
898
  return handle.run_id
683
899
 
684
900
  def _make_source(self, handle: RunHandle) -> SourceRef:
901
+ root = self._canonical_roots.get(handle.run_id)
902
+ if root is not None:
903
+ return root.source
685
904
  request = self._requests.get(handle.run_id)
686
905
  return SourceRef(
687
906
  framework="codex",
@@ -695,13 +914,26 @@ class CodexRuntimeAdapter(RuntimeAdapter):
695
914
  ),
696
915
  "session_id": handle.session_id,
697
916
  "invocation_id": (
698
- str(request.metadata.get("invocation_id") or handle.run_id)
917
+ self._event_run_id(handle)
699
918
  if request is not None
700
919
  else handle.run_id
701
920
  ),
702
921
  },
703
922
  )
704
923
 
924
+ def _with_recovered_root(self, event: RuntimeEvent, handle: RunHandle) -> RuntimeEvent:
925
+ root = self._canonical_roots.get(handle.run_id)
926
+ if (root is None or event.run_id != root.run_id
927
+ or event.parent_scope_id is not None
928
+ or not event.event_type.startswith("run.")):
929
+ return event
930
+ # Normalize before managed checkpointing, preserving native source and
931
+ # item/child identities of the continuation's new native turn.
932
+ updates = {"scope_id": root.scope_id}
933
+ if isinstance(event, RunFailed):
934
+ updates["error"] = event.error.model_copy(update={"scope_id": root.scope_id})
935
+ return event.model_copy(update=updates)
936
+
705
937
  def _with_caller_scope(self, event: RuntimeEvent, request: Any) -> RuntimeEvent:
706
938
  """把调用方 scope(request 的 agent/user/session/invocation)并入事件 source。"""
707
939
 
@@ -747,10 +979,14 @@ class CodexRuntimeAdapter(RuntimeAdapter):
747
979
  "source": self._make_source(handle),
748
980
  }
749
981
 
982
+ def _lifecycle_scope_id(self, handle: RunHandle) -> str:
983
+ root = self._canonical_roots.get(handle.run_id)
984
+ return root.scope_id if root is not None else stable_scope_id("codex", self._event_run_id(handle))
985
+
750
986
  def _make_run_canceled(self, handle: RunHandle, *, reason: str | None = None) -> RunCanceled:
751
987
  framework = "codex"
752
988
  run_id = self._event_run_id(handle)
753
- scope_id = stable_scope_id(framework, run_id)
989
+ scope_id = self._lifecycle_scope_id(handle)
754
990
  item_id = stable_item_id(framework, run_id, "$run")
755
991
  return RunCanceled(
756
992
  **self._canonical_kwargs(
@@ -769,7 +1005,7 @@ class CodexRuntimeAdapter(RuntimeAdapter):
769
1005
  ) -> RunInterrupted:
770
1006
  framework = "codex"
771
1007
  run_id = self._event_run_id(handle)
772
- scope_id = stable_scope_id(framework, run_id)
1008
+ scope_id = self._lifecycle_scope_id(handle)
773
1009
  item_id = stable_item_id(framework, run_id, "$run")
774
1010
  return RunInterrupted(
775
1011
  **self._canonical_kwargs(
@@ -786,7 +1022,7 @@ class CodexRuntimeAdapter(RuntimeAdapter):
786
1022
  def _make_run_failed(self, handle: RunHandle, error_message: str) -> RunFailed:
787
1023
  framework = "codex"
788
1024
  run_id = self._event_run_id(handle)
789
- scope_id = stable_scope_id(framework, run_id)
1025
+ scope_id = self._lifecycle_scope_id(handle)
790
1026
  item_id = stable_item_id(framework, run_id, "$run")
791
1027
  return RunFailed(
792
1028
  **self._canonical_kwargs(
ksadk/common/auth.py CHANGED
@@ -120,7 +120,7 @@ class AWSV4Auth:
120
120
  method: str,
121
121
  url: str,
122
122
  headers: Dict[str, str],
123
- body: str = "",
123
+ body: str | bytes = "",
124
124
  ) -> Dict[str, str]:
125
125
  """手动签名并返回带签名的 headers
126
126
 
@@ -145,7 +145,7 @@ class AWSV4Auth:
145
145
  method=method,
146
146
  url=url,
147
147
  headers=headers,
148
- data=body.encode("utf-8") if body else None,
148
+ data=(body.encode("utf-8") if isinstance(body, str) else body) if body else None,
149
149
  )
150
150
  prepared = req.prepare()
151
151
 
@@ -0,0 +1,36 @@
1
+ """Shared source attribution for KsADK-managed cloud resource requests."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from collections.abc import Mapping
7
+
8
+ DEFAULT_TRAFFIC_SOURCE = "agentkit"
9
+ TRAFFIC_SOURCE_HEADER = "X-Traffic-Source"
10
+
11
+
12
+ def resolve_traffic_source(value: str | None = None) -> str:
13
+ """Resolve an explicit snapshot or the process setting, with a stable default."""
14
+ if value is None:
15
+ value = os.getenv("KSADK_TRAFFIC_SOURCE", "")
16
+ # Reject controls before trimming so even trailing CR/LF cannot enter headers.
17
+ if not isinstance(value, str) or any(ord(char) < 32 or ord(char) > 126 for char in value):
18
+ raise ValueError("Traffic source must contain printable ASCII characters only")
19
+ return value.strip() or DEFAULT_TRAFFIC_SOURCE
20
+
21
+
22
+ def build_cloud_headers(
23
+ headers: Mapping[str, str] | None = None, *, traffic_source: str | None = None
24
+ ) -> dict[str, str]:
25
+ """Copy headers and set one canonical source header before request signing.
26
+
27
+ Source attribution is owned by KSADK_TRAFFIC_SOURCE (or an explicit worker
28
+ snapshot), including when a caller supplies differently cased extra headers.
29
+ """
30
+ result = {
31
+ name: value
32
+ for name, value in (headers or {}).items()
33
+ if name.lower() != TRAFFIC_SOURCE_HEADER.lower()
34
+ }
35
+ result[TRAFFIC_SOURCE_HEADER] = resolve_traffic_source(traffic_source)
36
+ return result
@@ -0,0 +1,34 @@
1
+ """可进入运行事件和日志的异常摘要。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+
7
+ _URL = re.compile(r"\b[A-Za-z][A-Za-z0-9+.-]*://[^\s<>\"']+")
8
+ _AUTH = re.compile(r"\b(?:bearer|basic)\s+[^\s,;\"']+", re.IGNORECASE)
9
+ _PRIVATE_HEADER = re.compile(
10
+ r"\b(?:[A-Za-z0-9]+[_-])*(?:set-cookie|cookie|authorization)[\"']?"
11
+ r"\s*[:=]\s*[^\r\n]+",
12
+ re.IGNORECASE,
13
+ )
14
+ _CREDENTIAL = re.compile(
15
+ r"\b(?:[A-Za-z0-9]+[_-])*(?:api[_-]?key|access[_-]?key(?:[_-]?id)?|"
16
+ r"secret[_-]?access[_-]?key|"
17
+ r"authorization|password|passwd|(?:access[_-]?|refresh[_-]?|session[_-]?)token|"
18
+ r"secret|client[_-]?secret|_authToken)[\"']?\s*[:=]\s*"
19
+ r"(?:\"(?:\\.|[^\"\\])*\"|'(?:\\.|[^'\\])*'|[^\s,;}\]]+)",
20
+ re.IGNORECASE,
21
+ )
22
+ _API_KEY = re.compile(r"\bsk-[A-Za-z0-9_-]{8,}\b", re.IGNORECASE)
23
+
24
+
25
+ def safe_exception_detail(error: Exception, *, limit: int = 2048) -> str:
26
+ """保留异常类型与诊断文本,去掉 URL、凭证和 cookie 后再截断。
27
+
28
+ URL 整体移除,避免遗漏签名下载地址中厂商各异的鉴权参数。
29
+ 不使用 exc_info,防止日志在安全摘要后再次打印原始异常。
30
+ """
31
+ detail = f"{type(error).__name__}: {error}"
32
+ for pattern in (_URL, _AUTH, _PRIVATE_HEADER, _CREDENTIAL, _API_KEY):
33
+ detail = pattern.sub("[REDACTED]", detail)
34
+ return detail[:limit]