agentengine-sdk-python 0.8.0__py3-none-any.whl → 0.8.2__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 (494) hide show
  1. {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/METADATA +43 -7
  2. agentengine_sdk_python-0.8.2.dist-info/RECORD +778 -0
  3. {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/WHEEL +1 -1
  4. ksadk/a2a/__init__.py +2 -0
  5. ksadk/a2a/_space_client_events.py +381 -0
  6. ksadk/a2a/bootstrap.py +0 -2
  7. ksadk/a2a/card.py +6 -111
  8. ksadk/a2a/control_plane.py +239 -1
  9. ksadk/a2a/event_adapter.py +156 -64
  10. ksadk/a2a/executor.py +195 -172
  11. ksadk/a2a/routes.py +12 -5
  12. ksadk/a2a/server.py +1 -8
  13. ksadk/a2a/service_env.py +33 -0
  14. ksadk/a2a/space_client.py +43 -349
  15. ksadk/a2a_card.py +101 -0
  16. ksadk/a2ui/core.py +222 -53
  17. ksadk/agui/_agent_helpers.py +243 -0
  18. ksadk/agui/a2ui_projection.py +12 -5
  19. ksadk/agui/agent.py +474 -275
  20. ksadk/agui/config.py +11 -5
  21. ksadk/agui/routes.py +7 -15
  22. ksadk/api/client.py +530 -40
  23. ksadk/api/environment.py +27 -0
  24. ksadk/builders/code_builder.py +352 -53
  25. ksadk/builders/container_builder.py +71 -44
  26. ksadk/builders/framework_requirements.py +2 -1
  27. ksadk/builders/ks3_uploader.py +2 -2
  28. ksadk/builders/managed_runtime_builder.py +60 -28
  29. ksadk/cli/__init__.py +25 -0
  30. ksadk/cli/cmd_a2a.py +18 -31
  31. ksadk/cli/cmd_create.py +13 -26
  32. ksadk/cli/cmd_dashboard.py +15 -1
  33. ksadk/cli/cmd_deploy.py +28 -3
  34. ksadk/cli/cmd_eval.py +443 -0
  35. ksadk/cli/cmd_evalset.py +284 -0
  36. ksadk/cli/cmd_files.py +5 -1
  37. ksadk/cli/cmd_hermes.py +137 -163
  38. ksadk/cli/cmd_invoke.py +28 -38
  39. ksadk/cli/cmd_launch.py +12 -0
  40. ksadk/cli/cmd_managed_runtime.py +95 -0
  41. ksadk/cli/cmd_observe.py +85 -0
  42. ksadk/cli/cmd_openclaw.py +85 -479
  43. ksadk/cli/cmd_replay.py +28 -4
  44. ksadk/cli/cmd_run.py +29 -67
  45. ksadk/cli/cmd_studio.py +201 -0
  46. ksadk/cli/cmd_web.py +15 -6
  47. ksadk/cli/dry_run.py +2 -1
  48. ksadk/cli/env_options.py +83 -0
  49. ksadk/cli/global_options.py +3 -1
  50. ksadk/cli/hermes_env.py +151 -0
  51. ksadk/cli/invoke_payload.py +49 -0
  52. ksadk/cli/network_options.py +7 -5
  53. ksadk/cli/openclaw_env.py +458 -0
  54. ksadk/cli/runtime_bootstrap.py +51 -0
  55. ksadk/cli/ui.py +6 -0
  56. ksadk/cli/workflow_common.py +5 -5
  57. ksadk/codex/__init__.py +3 -3
  58. ksadk/codex/client.py +833 -49
  59. ksadk/codex/phase.py +1 -1
  60. ksadk/codex/runtime.py +720 -127
  61. ksadk/common/kop_client.py +232 -0
  62. ksadk/configs/__init__.py +1 -3
  63. ksadk/configs/env_registry.py +244 -45
  64. ksadk/configs/env_registry_pcm.py +181 -0
  65. ksadk/configs/env_var_spec.py +18 -0
  66. ksadk/configs/global_config.py +6 -1
  67. ksadk/configs/settings.py +5 -108
  68. ksadk/context_engine/__init__.py +98 -0
  69. ksadk/context_engine/assembler.py +176 -0
  70. ksadk/context_engine/baseline.py +419 -0
  71. ksadk/context_engine/cache_observability.py +228 -0
  72. ksadk/context_engine/capabilities.py +443 -0
  73. ksadk/context_engine/contributors.py +344 -0
  74. ksadk/context_engine/hosted_pipeline.py +417 -0
  75. ksadk/context_engine/models.py +117 -0
  76. ksadk/context_engine/planner.py +698 -0
  77. ksadk/context_engine/policies.py +319 -0
  78. ksadk/context_engine/projection.py +31 -0
  79. ksadk/context_engine/shadow_plan.py +266 -0
  80. ksadk/context_engine/tokenizer.py +164 -0
  81. ksadk/conversations/__init__.py +85 -94
  82. ksadk/conversations/context.py +126 -6
  83. ksadk/conversations/message_projection.py +292 -28
  84. ksadk/conversations/model_context.py +49 -3
  85. ksadk/conversations/runtime_compaction.py +207 -5
  86. ksadk/conversations/runtime_input.py +193 -3
  87. ksadk/conversations/runtime_invocation.py +81 -3
  88. ksadk/conversations/runtime_observability.py +157 -4
  89. ksadk/conversations/runtime_payloads.py +42 -0
  90. ksadk/conversations/runtime_preparation.py +280 -2
  91. ksadk/conversations/runtime_resume.py +71 -32
  92. ksadk/conversations/runtime_stream_events.py +94 -9
  93. ksadk/conversations/runtime_streaming.py +175 -22
  94. ksadk/conversations/semantic_summary.py +311 -1
  95. ksadk/conversations/session_lock.py +51 -0
  96. ksadk/deployment/env_forward.py +72 -0
  97. ksadk/deployment/managed_runtime.py +38 -0
  98. ksadk/deployment/manager.py +24 -8
  99. ksadk/deployment/providers/serverless.py +228 -92
  100. ksadk/evaluation/__init__.py +116 -0
  101. ksadk/evaluation/a2a_adapter.py +452 -0
  102. ksadk/evaluation/adapters.py +67 -0
  103. ksadk/evaluation/agent_eval_client.py +395 -0
  104. ksadk/evaluation/cloud_binding.py +80 -0
  105. ksadk/evaluation/cloud_converter.py +194 -0
  106. ksadk/evaluation/cloud_service.py +199 -0
  107. ksadk/evaluation/contracts.py +478 -0
  108. ksadk/evaluation/evalset.py +351 -0
  109. ksadk/evaluation/evaluators.py +627 -0
  110. ksadk/evaluation/evidence.py +258 -0
  111. ksadk/evaluation/executor.py +143 -0
  112. ksadk/evaluation/local_adapter.py +550 -0
  113. ksadk/evaluation/service_env.py +27 -0
  114. ksadk/evaluation/storage.py +68 -0
  115. ksadk/evaluation/studio_build_adapter.py +253 -0
  116. ksadk/evaluation/target.py +82 -0
  117. ksadk/events/__init__.py +13 -13
  118. ksadk/events/_v1_compat/__init__.py +1 -0
  119. ksadk/events/_v1_compat/models.py +299 -0
  120. ksadk/events/_v1_compat/parser.py +247 -0
  121. ksadk/events/_v1_compat/projection.py +753 -0
  122. ksadk/events/adapters/__init__.py +34 -0
  123. ksadk/events/adapters/_a2a_snapshot.py +622 -0
  124. ksadk/events/adapters/_a2a_support.py +275 -0
  125. ksadk/events/adapters/_codex_interactions.py +375 -0
  126. ksadk/events/adapters/_codex_items.py +801 -0
  127. ksadk/events/adapters/_codex_validators.py +311 -0
  128. ksadk/events/adapters/_langgraph_support.py +785 -0
  129. ksadk/events/adapters/a2a.py +841 -0
  130. ksadk/events/adapters/adk.py +503 -0
  131. ksadk/events/adapters/codex.py +717 -0
  132. ksadk/events/adapters/langgraph.py +745 -0
  133. ksadk/events/canonical.py +440 -0
  134. ksadk/events/canonical_replay.py +497 -0
  135. ksadk/events/canonical_store.py +468 -0
  136. ksadk/events/cold_recovery.py +257 -0
  137. ksadk/events/content.py +89 -0
  138. ksadk/events/identity.py +86 -0
  139. ksadk/events/pipeline.py +455 -0
  140. ksadk/events/projections.py +66 -0
  141. ksadk/events/reducer.py +661 -0
  142. ksadk/events/replay.py +3 -36
  143. ksadk/events/runtime_event.py +20 -256
  144. ksadk/events/session_event.py +324 -0
  145. ksadk/events/store.py +3 -316
  146. ksadk/events/v1_compat.py +41 -0
  147. ksadk/harness/__init__.py +3 -2
  148. ksadk/harness/app.py +114 -123
  149. ksadk/harness/config.py +1 -1
  150. ksadk/harness/runtime.py +467 -0
  151. ksadk/hermes_terminal.py +5 -0
  152. ksadk/identity/resolver.py +4 -3
  153. ksadk/interaction/__init__.py +28 -0
  154. ksadk/interaction/contracts.py +136 -0
  155. ksadk/interaction/ledger.py +189 -0
  156. ksadk/interaction/provider.py +122 -0
  157. ksadk/interaction/providers/__init__.py +52 -0
  158. ksadk/interaction/providers/adk.py +40 -0
  159. ksadk/interaction/providers/codex.py +94 -0
  160. ksadk/interaction/providers/langgraph.py +58 -0
  161. ksadk/kernel/__init__.py +117 -0
  162. ksadk/kernel/authorization.py +258 -0
  163. ksadk/kernel/bootstrap.py +1074 -0
  164. ksadk/kernel/contract_fingerprints.py +61 -0
  165. ksadk/kernel/contracts.py +362 -0
  166. ksadk/kernel/control.py +219 -0
  167. ksadk/kernel/errors.py +79 -0
  168. ksadk/kernel/ingress.py +1102 -0
  169. ksadk/kernel/mapping.py +111 -0
  170. ksadk/kernel/memory_store.py +1133 -0
  171. ksadk/kernel/postgres_store.py +1756 -0
  172. ksadk/kernel/recovery.py +452 -0
  173. ksadk/kernel/runtime_identity.py +103 -0
  174. ksadk/kernel/sql/001_agent_kernel.sql +120 -0
  175. ksadk/kernel/sqlite_store.py +1410 -0
  176. ksadk/kernel/state.py +145 -0
  177. ksadk/kernel/store.py +232 -0
  178. ksadk/kernel/worker.py +953 -0
  179. ksadk/knowledge_base/client.py +22 -4
  180. ksadk/knowledge_base/service.py +32 -5
  181. ksadk/managed_a2a_card.py +160 -0
  182. ksadk/managed_runtime.py +16 -9
  183. ksadk/memory/__init__.py +61 -0
  184. ksadk/memory/adk/backends/base_ltm_backend.py +96 -1
  185. ksadk/memory/adk/backends/http_ltm_backend.py +14 -5
  186. ksadk/memory/adk/backends/inmemory_ltm_backend.py +136 -3
  187. ksadk/memory/adk/backends/sdk_ltm_backend.py +394 -5
  188. ksadk/memory/adk/backends/sqlite_ltm_backend.py +95 -0
  189. ksadk/memory/adk/long_term_memory.py +3 -2
  190. ksadk/memory/coordinator.py +461 -0
  191. ksadk/memory/events.py +202 -0
  192. ksadk/memory/extraction.py +228 -0
  193. ksadk/memory/ltm_backend_factory.py +16 -3
  194. ksadk/memory/models.py +316 -0
  195. ksadk/memory/policy.py +215 -0
  196. ksadk/memory/provider.py +47 -0
  197. ksadk/memory/provider_adapter.py +159 -0
  198. ksadk/memory/provider_resolver.py +71 -0
  199. ksadk/memory/providers/__init__.py +5 -0
  200. ksadk/memory/providers/local_sqlite.py +490 -0
  201. ksadk/memory/resolved_policy.py +145 -0
  202. ksadk/memory/service.py +198 -8
  203. ksadk/memory/tool.py +4 -0
  204. ksadk/model_proxy/bootstrap.py +16 -9
  205. ksadk/model_proxy/config.py +22 -1
  206. ksadk/model_proxy/detect.py +202 -9
  207. ksadk/model_proxy/namespace.py +67 -15
  208. ksadk/model_proxy/server.py +168 -6
  209. ksadk/model_proxy/transform.py +105 -3
  210. ksadk/observability/__init__.py +25 -0
  211. ksadk/observability/session_log.py +420 -0
  212. ksadk/observability/trajectory.py +105 -0
  213. ksadk/prompts/__init__.py +71 -0
  214. ksadk/prompts/compiler.py +215 -0
  215. ksadk/prompts/models.py +82 -0
  216. ksadk/prompts/projection.py +89 -0
  217. ksadk/prompts/resolved.py +137 -0
  218. ksadk/prompts/sources.py +233 -0
  219. ksadk/runners/_langgraph_runner_streams.py +822 -0
  220. ksadk/runners/adk_runner.py +14 -31
  221. ksadk/runners/base_runner.py +44 -3
  222. ksadk/runners/factory.py +3 -3
  223. ksadk/runners/langgraph_runner.py +252 -433
  224. ksadk/runners/utils/__init__.py +0 -10
  225. ksadk/runners/utils/loader.py +16 -0
  226. ksadk/runtime/__init__.py +67 -41
  227. ksadk/runtime/_runner_adapter/__init__.py +1 -0
  228. ksadk/runtime/_runner_adapter/stream_mapping.py +788 -0
  229. ksadk/runtime/adapter.py +199 -16
  230. ksadk/runtime/conversation_execution.py +687 -0
  231. ksadk/runtime/executor.py +392 -0
  232. ksadk/runtime/factory.py +342 -0
  233. ksadk/runtime/framework_adapters.py +6 -12
  234. ksadk/runtime/hosted_finalizer.py +268 -0
  235. ksadk/runtime/launch.py +61 -0
  236. ksadk/runtime/preprocessing.py +49 -14
  237. ksadk/runtime/runner_adapter.py +330 -391
  238. ksadk/runtime/runner_loading.py +36 -0
  239. ksadk/runtime/usage.py +33 -0
  240. ksadk/sandbox/backends/e2b.py +36 -3
  241. ksadk/server/__init__.py +39 -4
  242. ksadk/server/app.py +18 -492
  243. ksadk/server/composition.py +36 -2
  244. ksadk/server/factory.py +210 -149
  245. ksadk/server/routes/common.py +15 -46
  246. ksadk/server/routes/control.py +13 -9
  247. ksadk/server/routes/kernel_ingress.py +258 -0
  248. ksadk/server/routes/misc.py +10 -33
  249. ksadk/server/routes/models.py +14 -6
  250. ksadk/server/routes/openai_compat.py +129 -27
  251. ksadk/server/routes/projection.py +84 -16
  252. ksadk/server/routes/run.py +347 -460
  253. ksadk/server/routes/sessions.py +37 -40
  254. ksadk/server/routes/streaming.py +52 -7
  255. ksadk/server/routes/workspace.py +54 -21
  256. ksadk/server/static/assets/{ArtifactsPanel-Cjnqt-8W.js → ArtifactsPanel-DSU1sWD_.js} +1 -1
  257. ksadk/server/static/assets/{CodeBlock-BqyqZfuB.js → CodeBlock-DOH6MVcn.js} +4 -4
  258. ksadk/server/static/assets/{MathMessageMarkdown-BpH7jiFM.js → MathMessageMarkdown-BL8my1R2.js} +4 -4
  259. ksadk/server/static/assets/MathMessageMarkdown-BorAY7qD.css +1 -0
  260. ksadk/server/static/assets/MermaidBlock-Dz4IP-Tx.js +312 -0
  261. ksadk/server/static/assets/NativeTerminalPanel-DUrK0JpZ.js +1 -0
  262. ksadk/server/static/assets/abnfDiagram-VCTEODGH-g20pFzNV.js +1 -0
  263. ksadk/server/static/assets/arc-C4FzinUA.js +1 -0
  264. ksadk/server/static/assets/architecture-7GRP2DOG-DjKratdf.js +1 -0
  265. ksadk/server/static/assets/architectureDiagram-5GKGNRK7-DpIqL5h4.js +36 -0
  266. ksadk/server/static/assets/blockDiagram-NRAW4CY4-BjkljTNz.js +129 -0
  267. ksadk/server/static/assets/c4Diagram-UCG6FXSJ-Dk_ieq2X.js +38 -0
  268. ksadk/server/static/assets/channel-4cQHKtx1.js +1 -0
  269. ksadk/server/static/assets/chunk-2Q5K7J3B-DmgWkESh.js +1 -0
  270. ksadk/server/static/assets/{chunk-FMBD7UC4-BGhvzDYt.js → chunk-5VM5RSS4-BVHAchFb.js} +1 -1
  271. ksadk/server/static/assets/{chunk-ND2GUHAM-D98vPd-E.js → chunk-F27PBJKO-C-ipQzuS.js} +1 -1
  272. ksadk/server/static/assets/chunk-G27WJ6UU-C2EyJbIK.js +231 -0
  273. ksadk/server/static/assets/{chunk-4BX2VUAB-CsWIQRhc.js → chunk-JWPE2WC7-vYvVJb_M.js} +1 -1
  274. ksadk/server/static/assets/chunk-LCL6LL3I-BcI6ysPT.js +206 -0
  275. ksadk/server/static/assets/chunk-POPQ4Y6H-C030x_Z1.js +1 -0
  276. ksadk/server/static/assets/chunk-RHFEMEQ7-yyGpsz4n.js +168 -0
  277. ksadk/server/static/assets/chunk-SVP7TREG-BLTlmMU7.js +88 -0
  278. ksadk/server/static/assets/chunk-XXDRQBXY-C_32ArgP.js +1 -0
  279. ksadk/server/static/assets/classDiagram-DTDB5LWJ-DLFd9Xi0.js +1 -0
  280. ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-DLFd9Xi0.js +1 -0
  281. ksadk/server/static/assets/{cose-bilkent-S5V4N54A-CX3yotHq.js → cose-bilkent-JH36ORCC-9YQYwdl0.js} +1 -1
  282. ksadk/server/static/assets/cynefin-OW5HDTMX-DjEM48Qp.js +1 -0
  283. ksadk/server/static/assets/cynefinDiagram-5FMLGOSQ-ErLF5N13.js +62 -0
  284. ksadk/server/static/assets/cytoscape.esm-CyCl8rPi.js +321 -0
  285. ksadk/server/static/assets/dagre-3AP2YEHR-DFiQF-6f.js +4 -0
  286. ksadk/server/static/assets/dagre-BuhGnRI1.js +1 -0
  287. ksadk/server/static/assets/diagram-S7CK7UJ4-Bt1v8-GC.js +30 -0
  288. ksadk/server/static/assets/diagram-UQ7AKVKN-DSCxJdBK.js +41 -0
  289. ksadk/server/static/assets/diagram-VSXAHHWV-DHfYwp_9.js +3 -0
  290. ksadk/server/static/assets/diagram-VX7I27RA-DdzD-4Le.js +24 -0
  291. ksadk/server/static/assets/diagram-Z3DM3KII-Du-nAJ9F.js +24 -0
  292. ksadk/server/static/assets/{dist-D82mtD2s.js → dist-8hqcx0sU.js} +1 -1
  293. ksadk/server/static/assets/dist-B7seoj3d.js +1 -0
  294. ksadk/server/static/assets/{dist-DHn6nBzr.js → dist-BE7bu-DL.js} +1 -1
  295. ksadk/server/static/assets/{dist-eoW6tZXA.js → dist-BVDffZ0U.js} +1 -1
  296. ksadk/server/static/assets/{dist-C2xjE7cO.js → dist-BX8z72IC.js} +1 -1
  297. ksadk/server/static/assets/{dist-CgF3fj15.js → dist-Bf2M8m3N.js} +1 -1
  298. ksadk/server/static/assets/{dist-Bs-jQcRX.js → dist-BjU6y-A2.js} +1 -1
  299. ksadk/server/static/assets/{dist-Djgl2wVZ.js → dist-BytlxIDT.js} +1 -1
  300. ksadk/server/static/assets/{dist-CVQRZjPu.js → dist-CSh_DE14.js} +1 -1
  301. ksadk/server/static/assets/{dist-6CVKuTAe.js → dist-CXYYBw3_.js} +1 -1
  302. ksadk/server/static/assets/{dist-DlzCRy0S.js → dist-C_wsv-Qd.js} +1 -1
  303. ksadk/server/static/assets/{dist-DF06ORQo.js → dist-ClxGviKw.js} +1 -1
  304. ksadk/server/static/assets/{dist-Cf23NY4L.js → dist-CttYUqzS.js} +1 -1
  305. ksadk/server/static/assets/{dist-ivOya1LD.js → dist-DNp6rLRA.js} +1 -1
  306. ksadk/server/static/assets/{dist-BibcG1V9.js → dist-DUX-id_F.js} +1 -1
  307. ksadk/server/static/assets/{dist-BUHRxvDG.js → dist-DmldrHd_.js} +1 -1
  308. ksadk/server/static/assets/{dist-BZ9n9AjO.js → dist-DnfXs8Vn.js} +2 -2
  309. ksadk/server/static/assets/{dist-DqNnNFrw.js → dist-Dq9gLD7L.js} +1 -1
  310. ksadk/server/static/assets/{dist-CGcp2ex8.js → dist-W-TIVntx.js} +1 -1
  311. ksadk/server/static/assets/{dist-Cwt7koer.js → dist-dDdADKFA.js} +1 -1
  312. ksadk/server/static/assets/{dist-C7-3jInC.js → dist-dydu68Fl.js} +1 -1
  313. ksadk/server/static/assets/{dist-C99THtIA.js → dist-ngtN0DJB.js} +1 -1
  314. ksadk/server/static/assets/ebnfDiagram-PWID7BFC-Da9C9NO1.js +1 -0
  315. ksadk/server/static/assets/erDiagram-SSCWMZ5O-EAGqqR79.js +99 -0
  316. ksadk/server/static/assets/eventmodeling-NTZA5JFV-CMgMaJrC.js +1 -0
  317. ksadk/server/static/assets/flowDiagram-A5DVABFB-BBKL0x3P.js +1 -0
  318. ksadk/server/static/assets/ganttDiagram-EL5Y4UJY-DqJsKb59.js +292 -0
  319. ksadk/server/static/assets/gitGraph-4MIJSDKK-Dw63mrhw.js +1 -0
  320. ksadk/server/static/assets/gitGraphDiagram-WWUBYQGX-B6g4dtDi.js +106 -0
  321. ksadk/server/static/assets/index-8ipRcQ-M.js +240 -0
  322. ksadk/server/static/assets/index-ByFCqjlh.css +2 -0
  323. ksadk/server/static/assets/info-A6RAGUB7-B6iAblII.js +1 -0
  324. ksadk/server/static/assets/infoDiagram-RXCK75RN-Djm_nbd5.js +2 -0
  325. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-DLOHGvWw.js → ishikawaDiagram-5VMMS53U-DWya9SG2.js} +6 -6
  326. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-BIzKeejT.js → journeyDiagram-EYS64GPL-DutxhSNI.js} +6 -6
  327. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-BWHoFSw2.js → kanban-definition-3QL26DDD-DJdOF8Fm.js} +10 -10
  328. ksadk/server/static/assets/katex-vFWytM5c.js +257 -0
  329. ksadk/server/static/assets/{linear-CvMBw9cA.js → linear-BHjG8b-J.js} +1 -1
  330. ksadk/server/static/assets/mermaid-parser.core-KGSy4jWT.js +166 -0
  331. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-CSAQxEAe.js → mindmap-definition-FBJOCRG2-9-HC88D4.js} +29 -29
  332. ksadk/server/static/assets/packet-AYTQ26CC-BOYAipuq.js +1 -0
  333. ksadk/server/static/assets/pegDiagram-XKGWAZYB-DrEZd4fD.js +1 -0
  334. ksadk/server/static/assets/pie-WAS4IAKB-Ctq0Kbku.js +1 -0
  335. ksadk/server/static/assets/pieDiagram-E7YTZNPT-DpjqOFFp.js +39 -0
  336. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-CxIR4JXI.js → quadrantDiagram-AXDQQJYC-BiNhgOXX.js} +6 -6
  337. ksadk/server/static/assets/radar-RG4KPBEZ-DN0-ul_0.js +1 -0
  338. ksadk/server/static/assets/railroad-74A4TZTK-C6cC_to0.js +1 -0
  339. ksadk/server/static/assets/railroad-abnf-HS5TGJTU-BimjIuRc.js +1 -0
  340. ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-CvY65ABq.js +1 -0
  341. ksadk/server/static/assets/railroad-peg-WCYAUIDC-D72Iq_Tv.js +1 -0
  342. ksadk/server/static/assets/railroadDiagram-O6MQD6OU-_Y3Ojg6G.js +1 -0
  343. ksadk/server/static/assets/requirementDiagram-EFPCY7ZU-DeCFdg9K.js +84 -0
  344. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-DpGa6EAV.js → sankeyDiagram-P5KCCOFB-CPH75rhw.js} +7 -7
  345. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-NTKNGGe5.js → sequenceDiagram-WJ2MYXX4-OEpQQdr4.js} +10 -10
  346. ksadk/server/static/assets/sizeCapture-X5ZJPWSS-heUErzhI.js +1 -0
  347. ksadk/server/static/assets/stateDiagram-HBIQ2CUA-DvA3jSMB.js +1 -0
  348. ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BgQY03nz.js +1 -0
  349. ksadk/server/static/assets/swimlanes-XN3QIQJK-CR-dfN_t.js +1 -0
  350. ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-DLVw3q5Q.js +8 -0
  351. ksadk/server/static/assets/{timeline-definition-PNZ67QCA-jqoYs3jQ.js → timeline-definition-24CTP7MA-DAVJf1TX.js} +7 -7
  352. ksadk/server/static/assets/treeView-Q6P3EWNA-BMpO0wsB.js +1 -0
  353. ksadk/server/static/assets/treemap-WGGIJYW6-Et0aqzU3.js +1 -0
  354. ksadk/server/static/assets/vennDiagram-4TSXK5OY-DwM8eDkh.js +34 -0
  355. ksadk/server/static/assets/wardley-WFR3VGLG-CbY0mhpl.js +1 -0
  356. ksadk/server/static/assets/wardleyDiagram-VM6X3IG4-DZL-Zz2t.js +78 -0
  357. ksadk/server/static/assets/xychartDiagram-S5SC5T6Z-Dy6Yh_hu.js +7 -0
  358. ksadk/server/static/index.html +2 -2
  359. ksadk/sessions/__init__.py +26 -9
  360. ksadk/sessions/_local_service_sync.py +688 -0
  361. ksadk/sessions/_local_tables.py +21 -0
  362. ksadk/sessions/_postgres_schema.py +305 -0
  363. ksadk/sessions/_postgres_tables.py +15 -0
  364. ksadk/sessions/base.py +80 -1
  365. ksadk/sessions/in_memory.py +53 -2
  366. ksadk/sessions/local_service.py +63 -604
  367. ksadk/sessions/postgres_service.py +58 -148
  368. ksadk/sessions/resilient.py +35 -1
  369. ksadk/skills/runtime/backends/e2b.py +1 -1
  370. ksadk/skills/service_client.py +13 -3
  371. ksadk/studio/__init__.py +5 -0
  372. ksadk/studio/agent_avatar_assets.py +120 -0
  373. ksadk/studio/agent_lifecycle.py +51 -0
  374. ksadk/studio/api.py +1598 -0
  375. ksadk/studio/api_catalog_routes.py +324 -0
  376. ksadk/studio/api_contracts.py +286 -0
  377. ksadk/studio/api_helpers.py +132 -0
  378. ksadk/studio/api_memory_routes.py +82 -0
  379. ksadk/studio/authoring.py +749 -0
  380. ksadk/studio/authoring_coordinator.py +1017 -0
  381. ksadk/studio/builder.py +397 -0
  382. ksadk/studio/capabilities.py +368 -0
  383. ksadk/studio/cloud.py +2099 -0
  384. ksadk/studio/codex_agent_service.py +694 -0
  385. ksadk/studio/codex_authoring.py +469 -0
  386. ksadk/studio/codex_builder.py +437 -0
  387. ksadk/studio/codex_manifest.py +307 -0
  388. ksadk/studio/codex_run.py +395 -0
  389. ksadk/studio/compiler.py +190 -0
  390. ksadk/studio/contracts.py +755 -0
  391. ksadk/studio/errors.py +47 -0
  392. ksadk/studio/event_store.py +214 -0
  393. ksadk/studio/framework_run.py +342 -0
  394. ksadk/studio/hosted_kernel.py +317 -0
  395. ksadk/studio/identifiers.py +33 -0
  396. ksadk/studio/manifest_resolver.py +131 -0
  397. ksadk/studio/mcp_runtime.py +229 -0
  398. ksadk/studio/model_client.py +629 -0
  399. ksadk/studio/model_profile_service.py +214 -0
  400. ksadk/studio/operations.py +275 -0
  401. ksadk/studio/otel_trace.py +1060 -0
  402. ksadk/studio/pagination.py +182 -0
  403. ksadk/studio/pcm_memory.py +85 -0
  404. ksadk/studio/python_tool_inspection.py +183 -0
  405. ksadk/studio/repository.py +327 -0
  406. ksadk/studio/resource_catalog.py +1260 -0
  407. ksadk/studio/run_service.py +1133 -0
  408. ksadk/studio/runtime_catalog.py +74 -0
  409. ksadk/studio/runtime_source.py +365 -0
  410. ksadk/studio/service.py +2249 -0
  411. ksadk/studio/shared_web.py +1004 -0
  412. ksadk/studio/skill_discovery.py +628 -0
  413. ksadk/studio/static/assets/PrismRenderer-IYN-ffww.js +3 -0
  414. ksadk/studio/static/assets/index-BLpcdrgW.css +1 -0
  415. ksadk/studio/static/assets/index-CYekR2Xv.js +260 -0
  416. ksadk/studio/static/assets/react-vendor-BNmTiJ-I.js +9 -0
  417. ksadk/studio/static/assets/rolldown-runtime-hePW80VL.js +1 -0
  418. ksadk/studio/static/index.html +15 -0
  419. ksadk/studio/templates.py +722 -0
  420. ksadk/studio/validator.py +265 -0
  421. ksadk/studio/workspace.py +135 -0
  422. ksadk/tools/gateway.py +9 -0
  423. ksadk/toolsets/__init__.py +27 -1
  424. ksadk/toolsets/a2a.py +426 -0
  425. ksadk/tracing/__init__.py +19 -21
  426. ksadk/tracing/setup.py +317 -403
  427. ksadk/tracing/span_utils.py +0 -44
  428. ksadk/version.py +1 -1
  429. ksadk_runtime_common/memory_backend/registry.py +56 -56
  430. ksadk_runtime_common/schemas/runtime_event_v1.json +37 -0
  431. ksadk_runtime_common/schemas/runtime_event_v2.json +271 -0
  432. ksadk_runtime_common/workspace_files/router.py +8 -2
  433. agentengine_sdk_python-0.8.0.dist-info/RECORD +0 -578
  434. ksadk/a2a/card_builder.py +0 -64
  435. ksadk/a2a/client.py +0 -309
  436. ksadk/events/parser.py +0 -191
  437. ksadk/harness/runner.py +0 -211
  438. ksadk/runners/codex_runner.py +0 -132
  439. ksadk/runners/utils/langfuse.py +0 -230
  440. ksadk/server/static/assets/MathMessageMarkdown-DeYh8QPr.css +0 -1
  441. ksadk/server/static/assets/MermaidBlock-DWuxETaJ.js +0 -303
  442. ksadk/server/static/assets/NativeTerminalPanel-MDTopWMO.js +0 -1
  443. ksadk/server/static/assets/arc-CFKwSk_B.js +0 -1
  444. ksadk/server/static/assets/architecture-7EHR7CIX-CynqOWjZ.js +0 -1
  445. ksadk/server/static/assets/architectureDiagram-3BPJPVTR-Dej8jX8c.js +0 -36
  446. ksadk/server/static/assets/blockDiagram-GPEHLZMM-1F64jqvy.js +0 -132
  447. ksadk/server/static/assets/c4Diagram-AAUBKEIU-9q7AUAKb.js +0 -10
  448. ksadk/server/static/assets/channel-iUGv2N24.js +0 -1
  449. ksadk/server/static/assets/chunk-2J33WTMH-6qgwJm7Q.js +0 -1
  450. ksadk/server/static/assets/chunk-55IACEB6-BK8paA20.js +0 -1
  451. ksadk/server/static/assets/chunk-727SXJPM-B-JEhy7h.js +0 -206
  452. ksadk/server/static/assets/chunk-AQP2D5EJ-DYBvMHAa.js +0 -231
  453. ksadk/server/static/assets/chunk-QZHKN3VN-YQIEXeF6.js +0 -1
  454. ksadk/server/static/assets/classDiagram-4FO5ZUOK-CDAUuoXK.js +0 -1
  455. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-CDAUuoXK.js +0 -1
  456. ksadk/server/static/assets/cytoscape.esm-DelgaX4f.js +0 -321
  457. ksadk/server/static/assets/dagre-BM42HDAG-C7TguAWo.js +0 -4
  458. ksadk/server/static/assets/dagre-Bx709z4p.js +0 -1
  459. ksadk/server/static/assets/diagram-2AECGRRQ-Dd17_kws.js +0 -43
  460. ksadk/server/static/assets/diagram-5GNKFQAL-DVIClIil.js +0 -10
  461. ksadk/server/static/assets/diagram-KO2AKTUF-Df_3gYQa.js +0 -3
  462. ksadk/server/static/assets/diagram-LMA3HP47-BhLk6ZtA.js +0 -24
  463. ksadk/server/static/assets/diagram-OG6HWLK6-D4GNGvuu.js +0 -24
  464. ksadk/server/static/assets/dist-DyvYLMW9.js +0 -1
  465. ksadk/server/static/assets/erDiagram-TEJ5UH35-CnfRcY2M.js +0 -85
  466. ksadk/server/static/assets/eventmodeling-FCH6USID-ChYO-ijM.js +0 -1
  467. ksadk/server/static/assets/flowDiagram-I6XJVG4X-DGaJjGNJ.js +0 -162
  468. ksadk/server/static/assets/ganttDiagram-6RSMTGT7-DpCoB2g9.js +0 -292
  469. ksadk/server/static/assets/gitGraph-WXDBUCRP-BN_0Fk-j.js +0 -1
  470. ksadk/server/static/assets/gitGraphDiagram-PVQCEYII-9IKVFBVT.js +0 -106
  471. ksadk/server/static/assets/graphlib-B8gBHxth.js +0 -1
  472. ksadk/server/static/assets/index-QSwCHuB8.js +0 -177
  473. ksadk/server/static/assets/index-jOPmTOZH.css +0 -2
  474. ksadk/server/static/assets/info-J43DQDTF-Cp6o3vId.js +0 -1
  475. ksadk/server/static/assets/infoDiagram-5YYISTIA-CYIIsb8y.js +0 -2
  476. ksadk/server/static/assets/katex-DATssjp1.js +0 -265
  477. ksadk/server/static/assets/mermaid-parser.core-mGAcEwcq.js +0 -161
  478. ksadk/server/static/assets/packet-YPE3B663-wQzct3cT.js +0 -1
  479. ksadk/server/static/assets/pie-LRSECV5Y-BgTQiv-9.js +0 -1
  480. ksadk/server/static/assets/pieDiagram-4H26LBE5-By-ra07M.js +0 -30
  481. ksadk/server/static/assets/radar-GUYGQ44K-BIq5fXsn.js +0 -1
  482. ksadk/server/static/assets/requirementDiagram-4Y6WPE33-DpW90iu3.js +0 -84
  483. ksadk/server/static/assets/stateDiagram-AJRCARHV-DGpd9yFG.js +0 -1
  484. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-B3o579IQ.js +0 -1
  485. ksadk/server/static/assets/treeView-BLDUP644-Dqi-E0EM.js +0 -1
  486. ksadk/server/static/assets/treemap-LRROVOQU-ClQP9CJ9.js +0 -1
  487. ksadk/server/static/assets/vennDiagram-CIIHVFJN-l3Z7-wbP.js +0 -34
  488. ksadk/server/static/assets/wardley-L42UT6IY-8XLvnNIs.js +0 -1
  489. ksadk/server/static/assets/wardleyDiagram-YWT4CUSO-BR54GPmr.js +0 -78
  490. ksadk/server/static/assets/xychartDiagram-2RQKCTM6-SkiH8GsK.js +0 -7
  491. ksadk/tracing/exporters/langfuse_exporter.py +0 -460
  492. {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/entry_points.txt +0 -0
  493. {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/licenses/LICENSE +0 -0
  494. {agentengine_sdk_python-0.8.0.dist-info → agentengine_sdk_python-0.8.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,698 @@
1
+ """ContextPlanner —— 预算、required/group 原子性与确定性缩减(方案 §8.4 / §8.5)。
2
+
3
+ Planner 是 Context Engine 的决策核心:输入候选项 ``ContextItem`` 与 ``ContextBudget``,
4
+ 输出 ``ContextPlan``(selected + decisions)。强制优先级遵循方案 §8.4 第 11 条;group 原子性
5
+ 保证 tool call/result、approval request/response、Responses call/output 不被拆散
6
+ (方案 §8.1 ``group_id``)。
7
+
8
+ 本模块纯计算、无副作用、可复用;不接触 Session Store、不调用模型。replan 由调用方在
9
+ compaction/PTL 后通过新 ``plan_id`` 重新调用(方案 §11.1 / ADR-016)。
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import uuid
15
+ from typing import Iterable
16
+
17
+ from ksadk.context_engine.models import (
18
+ CONTEXT_POLICY_VERSION,
19
+ ContextBudget,
20
+ ContextDecision,
21
+ ContextItem,
22
+ ContextKind,
23
+ ContextPlan,
24
+ )
25
+ from ksadk.context_engine.policies import ContextBudgetPolicy, SectionBudget
26
+
27
+ # 强制优先级(方案 §8.4):数字越小越优先保留。
28
+ # platform_safety(1) → current_input(2) → pending approval/tool(3) → receipt/framework ref(4)
29
+ # → agent identity/policy(5) → checkpoint summary(6) → working state(7) → recent rounds(8)
30
+ # → core memory(9) → recall memory(10) → optional skill/旧 tool/附件(11)
31
+ _PRIORITY_RANK: dict[ContextKind, int] = {
32
+ "compiled_prompt": 1, # 含 platform_safety/agent_identity/agent_policy
33
+ "current_input": 2,
34
+ "tool_result": 3, # pending tool 状态随 round 保留
35
+ "working_state": 7,
36
+ "checkpoint_summary": 6,
37
+ "core_memory": 9,
38
+ "recalled_memory": 10,
39
+ "history_round": 8,
40
+ "skill_content": 11,
41
+ "attachment_context": 11,
42
+ "resource_manifest": 5,
43
+ }
44
+
45
+ # 分区 key 映射(方案 §8.3 分区预算表 → ContextKind)。
46
+ _KIND_TO_SECTION: dict[ContextKind, str] = {
47
+ "compiled_prompt": "prompt",
48
+ "resource_manifest": "resource_manifest",
49
+ "core_memory": "core_memory",
50
+ "recalled_memory": "recalled_memory",
51
+ "checkpoint_summary": "checkpoint_summary",
52
+ "working_state": "working_state",
53
+ "history_round": "recent_history",
54
+ "tool_result": "tool_and_attachment",
55
+ "attachment_context": "tool_and_attachment",
56
+ "skill_content": "tool_and_attachment",
57
+ "current_input": "recent_history",
58
+ }
59
+
60
+
61
+ def _tokens(items: Iterable[ContextItem]) -> int:
62
+ return sum(i.estimated_tokens for i in items)
63
+
64
+
65
+ def _group_groups(items: list[ContextItem]) -> dict[str, list[ContextItem]]:
66
+ """按 ``group_id`` 聚合,无 group_id 的各自成组。"""
67
+ groups: dict[str, list[ContextItem]] = {}
68
+ for item in items:
69
+ key = item.group_id or item.item_id
70
+ groups.setdefault(key, []).append(item)
71
+ return groups
72
+
73
+
74
+ def _section_for(kind: ContextKind) -> str:
75
+ return _KIND_TO_SECTION.get(kind, "tool_and_attachment")
76
+
77
+
78
+ class ContextPlanner:
79
+ """确定性 Context 规划器(方案 §8.4 / §8.5)。
80
+
81
+ ``plan()`` 对相同输入产生相同输出(确定性排序 + 确定性缩减)。无状态、可复用。
82
+ """
83
+
84
+ def __init__(self, *, policy: ContextBudgetPolicy | None = None) -> None:
85
+ self._policy = policy or ContextBudgetPolicy()
86
+
87
+ def plan(
88
+ self,
89
+ candidates: list[ContextItem],
90
+ *,
91
+ budget: ContextBudget,
92
+ integration_mode: str = "ksadk_hosted",
93
+ accounting_accuracy: str = "estimated",
94
+ tokenizer: str = "heuristic_cjk_ascii",
95
+ stable_prefix_hash: str = "",
96
+ ) -> ContextPlan:
97
+ plan_id = f"ctxplan_{uuid.uuid4().hex[:16]}"
98
+ decisions: list[ContextDecision] = []
99
+
100
+ # 1. required 先锁定(方案 §8.4)。required 超 hard_limit → 配置错误,仍返回 plan 但标 dropped。 # noqa: E501
101
+ required = self._select_required(candidates, budget.hard_limit_tokens, decisions)
102
+ selected = list(required)
103
+
104
+ # 2. 非 required 按 §8.4 优先级排序后增量加入,遵守 group 原子性 + 分区预算。
105
+ non_required = [
106
+ c
107
+ for c in candidates
108
+ if not c.required and c.item_id not in {i.item_id for i in selected}
109
+ ]
110
+ non_required.sort(key=self._sort_key)
111
+ selected = self._add_within_budget(
112
+ selected, non_required, budget, decisions, strict_section_limits=True
113
+ )
114
+
115
+ # 3. soft limit → 确定性缩减(零 LLM 成本,方案 §8.5)
116
+ if _tokens(selected) > budget.soft_limit_tokens:
117
+ selected = self._deterministic_reduce(selected, budget.soft_limit_tokens, decisions)
118
+
119
+ # 4. hard limit → 紧急缩减(仍零 LLM;semantic compaction 由调用方在后续触发,方案 §8.4 末)
120
+ if _tokens(selected) > budget.hard_limit_tokens:
121
+ selected = self._emergency_reduce(selected, budget.hard_limit_tokens, decisions)
122
+
123
+ tokens_by_kind = self._tokens_by_kind(selected)
124
+ return ContextPlan(
125
+ plan_id=plan_id,
126
+ policy_version=CONTEXT_POLICY_VERSION,
127
+ tokenizer=tokenizer,
128
+ integration_mode=integration_mode, # type: ignore[arg-type]
129
+ accounting_accuracy=accounting_accuracy, # type: ignore[arg-type]
130
+ budget=budget,
131
+ selected=selected,
132
+ decisions=decisions,
133
+ tokens_by_kind=tokens_by_kind,
134
+ planned_input_tokens=_tokens(selected),
135
+ projected_input_tokens=None,
136
+ runtime_reported_input_tokens=None,
137
+ stable_prefix_hash=stable_prefix_hash,
138
+ projection_id=None,
139
+ contributor_status={},
140
+ )
141
+
142
+ # ---- 步骤实现 ----
143
+
144
+ def _select_required(
145
+ self,
146
+ candidates: list[ContextItem],
147
+ hard_limit: int,
148
+ decisions: list[ContextDecision],
149
+ ) -> list[ContextItem]:
150
+ """required 先锁定,并按 group 原子性拉入同组非 required 成员(方案 §8.1)。
151
+
152
+ group 原子性是跨全体 candidates 的:只要 group 中有 required,整组进入;非 required
153
+ 成员不进非 required 增量阶段,避免孤儿 group。
154
+ """
155
+ required = [c for c in candidates if c.required]
156
+ required.sort(key=self._sort_key)
157
+ all_groups = _group_groups(candidates)
158
+ selected: list[ContextItem] = []
159
+ seen: set[str] = set()
160
+ # required 本身总是进入(即使超 hard_limit,标 included;调用方据 hard_limit 判配置错误)。
161
+ for item in required:
162
+ selected.append(item)
163
+ seen.add(item.item_id)
164
+ decisions.append(
165
+ ContextDecision(
166
+ item_id=item.item_id,
167
+ action="included",
168
+ reason="required",
169
+ tokens_before=item.estimated_tokens,
170
+ tokens_after=item.estimated_tokens,
171
+ )
172
+ )
173
+ # 拉入 required 所在 group 的非 required 成员(原子保留,方案 §8.1)。
174
+ for item in required:
175
+ if not item.group_id:
176
+ continue
177
+ for mate in all_groups.get(item.group_id, []):
178
+ if mate.item_id in seen:
179
+ continue
180
+ selected.append(mate)
181
+ seen.add(mate.item_id)
182
+ decisions.append(
183
+ ContextDecision(
184
+ item_id=mate.item_id,
185
+ action="included",
186
+ reason="required_group_atomic",
187
+ tokens_before=mate.estimated_tokens,
188
+ tokens_after=mate.estimated_tokens,
189
+ )
190
+ )
191
+ return selected
192
+
193
+ def _sort_key(self, item: ContextItem) -> tuple[int, int, str]:
194
+ rank = _PRIORITY_RANK.get(item.kind, 99)
195
+ # 同优先级:required 先、score 高先、seq 小先
196
+ score = item.score if item.score is not None else 0.0
197
+ seq = item.seq_start if item.seq_start is not None else 0
198
+ return (rank, -int(score * 1000), seq, item.item_id)
199
+
200
+ def _add_within_budget(
201
+ self,
202
+ selected: list[ContextItem],
203
+ candidates: list[ContextItem],
204
+ budget: ContextBudget,
205
+ decisions: list[ContextDecision],
206
+ *,
207
+ strict_section_limits: bool,
208
+ ) -> list[ContextItem]:
209
+ chosen_ids = {i.item_id for i in selected}
210
+ section_used: dict[str, int] = self._section_used(selected)
211
+ groups = _group_groups(candidates)
212
+ # 按 group 的最小优先级排序,保证整组按优先级进入
213
+ group_order = sorted(
214
+ groups.items(),
215
+ key=lambda kv: min(self._sort_key(m) for m in kv[1]),
216
+ )
217
+ for _gkey, members in group_order:
218
+ group_tokens = _tokens(members)
219
+ section = _section_for(members[0].kind)
220
+ limit = budget.section_limits.get(section)
221
+ # group 原子性:整组进或不进(除非单组就超 hard_limit,则尝试截断 truncatable)
222
+ if _tokens(selected) + group_tokens > budget.hard_limit_tokens:
223
+ # 尝试 truncatable 单项截断
224
+ added = self._try_truncate_into(selected, members, budget, decisions)
225
+ selected.extend(added)
226
+ if not added:
227
+ # 整组因 hard_limit 被跳过 → 记录 dropped 决策(方案 §8.8)
228
+ for m in members:
229
+ if m.item_id not in chosen_ids:
230
+ self._drop(
231
+ decisions,
232
+ m,
233
+ "hard_limit_exceeded",
234
+ )
235
+ continue
236
+ if (
237
+ strict_section_limits
238
+ and limit is not None
239
+ and section_used.get(section, 0) + group_tokens > limit
240
+ ):
241
+ # 分区预算超限:整组跳过 → 记录 dropped 决策(方案 §8.8)
242
+ for m in members:
243
+ if m.item_id not in chosen_ids:
244
+ self._drop(
245
+ decisions,
246
+ m,
247
+ f"section_limit:{section}",
248
+ )
249
+ continue
250
+ # 全组成员未选 → 整组进入
251
+ new_members = [m for m in members if m.item_id not in chosen_ids]
252
+ if not new_members:
253
+ continue
254
+ selected.extend(new_members)
255
+ section_used[section] = section_used.get(section, 0) + _tokens(new_members)
256
+ for m in new_members:
257
+ decisions.append(
258
+ ContextDecision(
259
+ item_id=m.item_id,
260
+ action="included",
261
+ reason=f"section:{section}",
262
+ tokens_before=m.estimated_tokens,
263
+ tokens_after=m.estimated_tokens,
264
+ )
265
+ )
266
+ return selected
267
+
268
+ def _try_truncate_into(
269
+ self,
270
+ selected: list[ContextItem],
271
+ members: list[ContextItem],
272
+ budget: ContextBudget,
273
+ decisions: list[ContextDecision],
274
+ ) -> list[ContextItem]:
275
+ """整组超 hard_limit 时原子抢救:固定成员保留,其余截断或摘要(方案 §8.1/§8.6)。
276
+
277
+ 即使 Tool Result 可降载,Tool Call/Result 仍是一个协议组,不能只留下 Result。
278
+ 因此先为不可缩减成员预留预算,再处理可缩减成员;任一成员无法进入时整组放弃。
279
+ """
280
+ from dataclasses import replace
281
+
282
+ def _reducible(item: ContextItem) -> bool:
283
+ return bool(
284
+ item.truncatable
285
+ or (item.kind == "tool_result" and item.droppable and not item.required)
286
+ )
287
+
288
+ remaining_total = budget.hard_limit_tokens - _tokens(selected)
289
+ fixed = [item for item in members if not _reducible(item)]
290
+ fixed_tokens = _tokens(fixed)
291
+ reducible = [item for item in members if _reducible(item)]
292
+ if fixed_tokens > remaining_total or (reducible and fixed_tokens >= remaining_total):
293
+ return []
294
+
295
+ added: list[ContextItem] = list(fixed)
296
+ pending_decisions: list[ContextDecision] = [
297
+ ContextDecision(
298
+ item_id=item.item_id,
299
+ action="included",
300
+ reason="group_atomic_fixed",
301
+ tokens_before=item.estimated_tokens,
302
+ tokens_after=item.estimated_tokens,
303
+ )
304
+ for item in fixed
305
+ ]
306
+ for index, m in enumerate(reducible):
307
+ remaining = budget.hard_limit_tokens - _tokens(selected) - _tokens(added)
308
+ # 至少给后续每个可缩减成员留 1 token,保证整组原子进入。
309
+ available = remaining - (len(reducible) - index - 1)
310
+ if available <= 0:
311
+ return []
312
+
313
+ # 大 tool_result → artifact summary(方案 §8.6:保留 error tail + 引用)
314
+ if (
315
+ m.kind == "tool_result"
316
+ and m.droppable
317
+ and not m.required
318
+ and m.estimated_tokens > available
319
+ ):
320
+ after = max(1, min(available, m.estimated_tokens // 8 + 200))
321
+ added.append(
322
+ replace(
323
+ m,
324
+ estimated_tokens=after,
325
+ metadata={**m.metadata, "replaced_with_artifact_summary": True},
326
+ )
327
+ )
328
+ pending_decisions.append(
329
+ ContextDecision(
330
+ item_id=m.item_id,
331
+ action="summarized",
332
+ reason="large_tool_result_to_artifact",
333
+ tokens_before=m.estimated_tokens,
334
+ tokens_after=after,
335
+ )
336
+ )
337
+ continue
338
+ if not m.truncatable or m.estimated_tokens == 0:
339
+ # 可缩减集合里的非 truncatable 项只能是尚未超过 available 的 Tool Result。
340
+ if m.estimated_tokens > available:
341
+ return []
342
+ added.append(m)
343
+ pending_decisions.append(
344
+ ContextDecision(
345
+ item_id=m.item_id,
346
+ action="included",
347
+ reason="group_atomic_fit",
348
+ tokens_before=m.estimated_tokens,
349
+ tokens_after=m.estimated_tokens,
350
+ )
351
+ )
352
+ continue
353
+ # 截断到剩余预算(启发式按 token 比例截字符;实际截断由 assembler 处理)
354
+ ratio = available / max(m.estimated_tokens, 1)
355
+ after = max(0, int(m.estimated_tokens * ratio))
356
+ if after == 0:
357
+ return []
358
+ added.append(
359
+ replace(
360
+ m, estimated_tokens=after, metadata={**m.metadata, "truncated_to_tokens": after}
361
+ )
362
+ )
363
+ pending_decisions.append(
364
+ ContextDecision(
365
+ item_id=m.item_id,
366
+ action="truncated",
367
+ reason="hard_limit_truncate",
368
+ tokens_before=m.estimated_tokens,
369
+ tokens_after=after,
370
+ )
371
+ )
372
+ if len(added) != len(members):
373
+ return []
374
+ decisions.extend(pending_decisions)
375
+ return added
376
+
377
+ def _deterministic_reduce(
378
+ self, selected: list[ContextItem], soft_limit: int, decisions: list[ContextDecision]
379
+ ) -> list[ContextItem]:
380
+ """零 LLM 成本的确定性缩减(方案 §8.5 1-6 步)。
381
+
382
+ 顺序:删除重复 manifest → 大 tool result 转 artifact reference → 移除被覆盖旧
383
+ tool call/result → 去二进制/重复日志 → 旧冷轮次 microcompact(此处只 drop 冷轮)→
384
+ 降低 recall top_k。required 与 current_input 不动。
385
+ """
386
+ kept = list(selected)
387
+ {i.item_id for i in kept}
388
+
389
+ # 1. 重复 resource_manifest(同 content_hash 去重)
390
+ seen_hashes: set[str] = set()
391
+ new_kept: list[ContextItem] = []
392
+ for item in kept:
393
+ if item.kind == "resource_manifest" and item.content_hash:
394
+ if item.content_hash in seen_hashes:
395
+ self._drop(decisions, item, "dedupe_manifest")
396
+ continue
397
+ seen_hashes.add(item.content_hash)
398
+ new_kept.append(item)
399
+ kept = new_kept
400
+ if _tokens(kept) <= soft_limit:
401
+ return kept
402
+
403
+ # 2. 大 tool_result 转摘要(droppable 且非 required)
404
+ kept = self._reduce_large_tool_results(kept, decisions)
405
+ if _tokens(kept) <= soft_limit:
406
+ return kept
407
+
408
+ # 3. 移除被同参数覆盖的旧 tool call/result(metadata.overwritten_by)
409
+ kept = self._drop_overwritten_tools(kept, decisions)
410
+ if _tokens(kept) <= soft_limit:
411
+ return kept
412
+
413
+ # 5. 旧冷轮次 drop(非 required、非 current_input、seq 最早的 history_round)
414
+ kept = self._drop_cold_rounds(kept, decisions, soft_limit)
415
+ if _tokens(kept) <= soft_limit:
416
+ return kept
417
+
418
+ # 6. 降低 recall memory top_k(按 score 最低先丢)
419
+ kept = self._reduce_recall(kept, decisions, soft_limit)
420
+ return kept
421
+
422
+ def _emergency_reduce(
423
+ self, selected: list[ContextItem], hard_limit: int, decisions: list[ContextDecision]
424
+ ) -> list[ContextItem]:
425
+ """紧急缩减:在确定性缩减基础上按优先级逆序丢非 required 可丢项(方案 §8.4)。
426
+
427
+ group 原子性:只有当一个 group 的**全体**成员都是可丢且非 required 时才整组丢;否则该
428
+ group 保持完整(避免孤儿 group)。required/current_input 永不丢。
429
+ """
430
+ kept = list(selected)
431
+ # 计算每个 group 是否可整组丢(全体 droppable 且非 required 且非 current_input)。
432
+ all_groups = _group_groups(kept)
433
+ droppable_groups: set[str] = set()
434
+ for gkey, members in all_groups.items():
435
+ if all(m.droppable and not m.required and m.kind != "current_input" for m in members):
436
+ droppable_groups.add(gkey)
437
+ # 候选丢弃单元:可整组丢的 group + 无 group 的可丢单项
438
+ drop_candidates: list[tuple[int, list[ContextItem]]] = []
439
+ for gkey, members in all_groups.items():
440
+ if gkey in droppable_groups:
441
+ drop_candidates.append(
442
+ (min(_PRIORITY_RANK.get(m.kind, 99) for m in members), members)
443
+ )
444
+ else:
445
+ # 无 group 的可丢单项
446
+ for m in members:
447
+ if (
448
+ not m.group_id
449
+ and m.droppable
450
+ and not m.required
451
+ and m.kind != "current_input"
452
+ ):
453
+ drop_candidates.append((_PRIORITY_RANK.get(m.kind, 99), [m]))
454
+ # 按优先级逆序丢(rank 大先丢)
455
+ drop_candidates.sort(key=lambda x: (-x[0],))
456
+ for _rank, members in drop_candidates:
457
+ if _tokens(kept) <= hard_limit:
458
+ break
459
+ for m in members:
460
+ if m in kept:
461
+ kept.remove(m)
462
+ self._drop(decisions, m, "emergency_drop")
463
+ return kept
464
+
465
+ # ---- 缩减子步骤 ----
466
+
467
+ def _reduce_large_tool_results(
468
+ self, kept: list[ContextItem], decisions: list[ContextDecision]
469
+ ) -> list[ContextItem]:
470
+ new_kept: list[ContextItem] = []
471
+ for item in kept:
472
+ if (
473
+ item.kind == "tool_result"
474
+ and item.droppable
475
+ and not item.required
476
+ and item.estimated_tokens
477
+ > self._policy.sections.get(
478
+ "tool_and_attachment", SectionBudget(10, 16000)
479
+ ).max_tokens
480
+ ):
481
+ # 转摘要:保留 error tail + artifact reference(这里以估算 1/8 表达,assembler 真正截断) # noqa: E501
482
+ from dataclasses import replace
483
+
484
+ after = max(item.estimated_tokens // 8, 200)
485
+ new_kept.append(
486
+ replace(
487
+ item,
488
+ estimated_tokens=after,
489
+ metadata={**item.metadata, "replaced_with_artifact_summary": True},
490
+ )
491
+ )
492
+ decisions.append(
493
+ ContextDecision(
494
+ item_id=item.item_id,
495
+ action="summarized",
496
+ reason="large_tool_result_to_artifact",
497
+ tokens_before=item.estimated_tokens,
498
+ tokens_after=after,
499
+ )
500
+ )
501
+ else:
502
+ new_kept.append(item)
503
+ return new_kept
504
+
505
+ def _drop_overwritten_tools(
506
+ self, kept: list[ContextItem], decisions: list[ContextDecision]
507
+ ) -> list[ContextItem]:
508
+ overwritten = {
509
+ item.metadata.get("overwritten_by")
510
+ for item in kept
511
+ if item.kind == "tool_result" and item.metadata.get("overwritten_by")
512
+ }
513
+ if not overwritten:
514
+ return kept
515
+ all_groups = _group_groups(kept)
516
+ droppable_groups: set[str] = set()
517
+ for gkey, members in all_groups.items():
518
+ if all(m.droppable and not m.required for m in members):
519
+ droppable_groups.add(gkey)
520
+ # 标记要丢弃的 group 与单项
521
+ drop_groups: set[str] = set()
522
+ drop_items: set[str] = set()
523
+ for item in kept:
524
+ if not (
525
+ item.kind == "tool_result"
526
+ and item.content_hash in overwritten
527
+ and not item.required
528
+ ):
529
+ continue
530
+ if item.group_id:
531
+ if item.group_id in droppable_groups:
532
+ drop_groups.add(item.group_id)
533
+ else:
534
+ drop_items.add(item.item_id)
535
+ if not drop_groups and not drop_items:
536
+ return kept
537
+ new_kept: list[ContextItem] = []
538
+ for item in kept:
539
+ if item.item_id in drop_items or (item.group_id and item.group_id in drop_groups):
540
+ self._drop(decisions, item, "overwritten_tool_result")
541
+ continue
542
+ new_kept.append(item)
543
+ return new_kept
544
+
545
+ def _drop_cold_rounds(
546
+ self, kept: list[ContextItem], decisions: list[ContextDecision], soft_limit: int
547
+ ) -> list[ContextItem]:
548
+ all_groups = _group_groups(kept)
549
+ # 只丢全体可丢且非 required 的 group(避免孤儿)。
550
+ droppable_groups: set[str] = set()
551
+ for gkey, members in all_groups.items():
552
+ if all(m.droppable and not m.required for m in members):
553
+ droppable_groups.add(gkey)
554
+ rounds = [i for i in kept if i.kind == "history_round" and i.droppable and not i.required]
555
+ rounds.sort(key=lambda i: (i.seq_start if i.seq_start is not None else 0,))
556
+ for item in rounds:
557
+ if _tokens(kept) <= soft_limit:
558
+ break
559
+ if item.group_id and item.group_id not in droppable_groups:
560
+ continue # 组内有 required/非可丢成员,保持完整
561
+ if item.group_id:
562
+ group = [i for i in kept if i.group_id == item.group_id]
563
+ for g in group:
564
+ if g in kept:
565
+ kept.remove(g)
566
+ self._drop(decisions, g, "cold_round_drop")
567
+ else:
568
+ if item in kept:
569
+ kept.remove(item)
570
+ self._drop(decisions, item, "cold_round_drop")
571
+ return kept
572
+
573
+ def _reduce_recall(
574
+ self, kept: list[ContextItem], decisions: list[ContextDecision], limit: int
575
+ ) -> list[ContextItem]:
576
+ all_groups = _group_groups(kept)
577
+ droppable_groups: set[str] = set()
578
+ for gkey, members in all_groups.items():
579
+ if all(m.droppable and not m.required for m in members):
580
+ droppable_groups.add(gkey)
581
+ recalls = [
582
+ i for i in kept if i.kind == "recalled_memory" and i.droppable and not i.required
583
+ ]
584
+ recalls.sort(key=lambda i: i.score if i.score is not None else 0.0)
585
+ for item in recalls:
586
+ if _tokens(kept) <= limit:
587
+ break
588
+ if item.group_id and item.group_id not in droppable_groups:
589
+ continue # 组内有 required/非可丢成员,保持完整
590
+ if item.group_id:
591
+ # 原子丢弃整组(方案 §8.1)
592
+ for g in [i for i in kept if i.group_id == item.group_id]:
593
+ kept.remove(g)
594
+ self._drop(decisions, g, "recall_topk_reduce")
595
+ elif item in kept:
596
+ kept.remove(item)
597
+ self._drop(decisions, item, "recall_topk_reduce")
598
+ return kept
599
+
600
+ # ---- helpers ----
601
+
602
+ @staticmethod
603
+ def _drop(decisions: list[ContextDecision], item: ContextItem, reason: str) -> None:
604
+ decisions.append(
605
+ ContextDecision(
606
+ item_id=item.item_id,
607
+ action="dropped",
608
+ reason=reason,
609
+ tokens_before=item.estimated_tokens,
610
+ tokens_after=0,
611
+ )
612
+ )
613
+
614
+ @staticmethod
615
+ def _section_used(selected: list[ContextItem]) -> dict[str, int]:
616
+ used: dict[str, int] = {}
617
+ for item in selected:
618
+ section = _section_for(item.kind)
619
+ used[section] = used.get(section, 0) + item.estimated_tokens
620
+ return used
621
+
622
+ @staticmethod
623
+ def _tokens_by_kind(selected: list[ContextItem]) -> dict[str, int]:
624
+ by_kind: dict[str, int] = {}
625
+ for item in selected:
626
+ by_kind[item.kind] = by_kind.get(item.kind, 0) + item.estimated_tokens
627
+ return by_kind
628
+
629
+
630
+ def build_budget(
631
+ *,
632
+ policy: ContextBudgetPolicy,
633
+ context_window_tokens: int,
634
+ reserved_output_tokens: int = 0,
635
+ reserved_reasoning_tokens: int = 0,
636
+ ) -> ContextBudget:
637
+ """从 policy 与模型窗口构造 ``ContextBudget``(方案 §8.2 / §8.3)。"""
638
+ tokens = compute_section_budget_tokens(
639
+ policy,
640
+ context_window_tokens=context_window_tokens,
641
+ reserved_output_tokens=reserved_output_tokens,
642
+ reserved_reasoning_tokens=reserved_reasoning_tokens,
643
+ )
644
+ max_input = tokens["max_input_tokens"]
645
+ # 小窗口(≤8K)动态调整:Prompt 占比提高到 30%,History 降到 25%
646
+ if max_input <= 8192:
647
+ from dataclasses import replace as _replace
648
+
649
+ small_policy = _replace(
650
+ policy,
651
+ sections={
652
+ "prompt": _replace(policy.sections["prompt"], percent=30, max_tokens=24000),
653
+ "recent_history": _replace(
654
+ policy.sections["recent_history"],
655
+ percent=25,
656
+ max_tokens=64000,
657
+ ),
658
+ },
659
+ )
660
+ section_limits = {
661
+ name: min(int(max_input * sb.percent / 100.0), sb.max_tokens)
662
+ for name, sb in small_policy.sections.items()
663
+ }
664
+ else:
665
+ section_limits = {
666
+ name: min(int(max_input * sb.percent / 100.0), sb.max_tokens)
667
+ for name, sb in policy.sections.items()
668
+ }
669
+ return ContextBudget(
670
+ context_window_tokens=context_window_tokens,
671
+ reserved_output_tokens=reserved_output_tokens,
672
+ reserved_reasoning_tokens=reserved_reasoning_tokens,
673
+ safety_buffer_tokens=tokens["safety_buffer_tokens"],
674
+ max_input_tokens=tokens["max_input_tokens"],
675
+ soft_limit_tokens=tokens["soft_limit_tokens"],
676
+ hard_limit_tokens=tokens["hard_limit_tokens"],
677
+ section_limits=section_limits,
678
+ )
679
+
680
+
681
+ def compute_section_budget_tokens(
682
+ policy: ContextBudgetPolicy,
683
+ *,
684
+ context_window_tokens: int,
685
+ reserved_output_tokens: int,
686
+ reserved_reasoning_tokens: int,
687
+ ) -> dict[str, int]:
688
+ from ksadk.context_engine.policies import compute_budget_tokens
689
+
690
+ return compute_budget_tokens(
691
+ policy,
692
+ context_window_tokens=context_window_tokens,
693
+ reserved_output_tokens=reserved_output_tokens,
694
+ reserved_reasoning_tokens=reserved_reasoning_tokens,
695
+ )
696
+
697
+
698
+ __all__ = ["ContextPlanner", "build_budget"]