agentengine-sdk-python 0.8.1__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 (411) hide show
  1. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/METADATA +22 -2
  2. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/RECORD +356 -222
  3. ksadk/a2a/_space_client_events.py +381 -0
  4. ksadk/a2a/event_adapter.py +156 -64
  5. ksadk/a2a/executor.py +179 -63
  6. ksadk/a2a/space_client.py +6 -333
  7. ksadk/a2ui/core.py +222 -53
  8. ksadk/agui/_agent_helpers.py +243 -0
  9. ksadk/agui/a2ui_projection.py +12 -5
  10. ksadk/agui/agent.py +443 -259
  11. ksadk/api/client.py +525 -27
  12. ksadk/builders/code_builder.py +284 -8
  13. ksadk/builders/container_builder.py +8 -1
  14. ksadk/builders/managed_runtime_builder.py +39 -35
  15. ksadk/cli/__init__.py +17 -0
  16. ksadk/cli/cmd_dashboard.py +15 -1
  17. ksadk/cli/cmd_deploy.py +10 -2
  18. ksadk/cli/cmd_eval.py +66 -9
  19. ksadk/cli/cmd_evalset.py +284 -0
  20. ksadk/cli/cmd_files.py +5 -1
  21. ksadk/cli/cmd_hermes.py +1 -126
  22. ksadk/cli/cmd_invoke.py +28 -38
  23. ksadk/cli/cmd_managed_runtime.py +95 -0
  24. ksadk/cli/cmd_observe.py +85 -0
  25. ksadk/cli/cmd_openclaw.py +1 -435
  26. ksadk/cli/cmd_replay.py +28 -4
  27. ksadk/cli/cmd_studio.py +99 -8
  28. ksadk/cli/cmd_web.py +10 -2
  29. ksadk/cli/hermes_env.py +151 -0
  30. ksadk/cli/invoke_payload.py +49 -0
  31. ksadk/cli/openclaw_env.py +458 -0
  32. ksadk/codex/client.py +180 -30
  33. ksadk/codex/runtime.py +428 -326
  34. ksadk/configs/env_registry.py +66 -12
  35. ksadk/configs/env_registry_pcm.py +181 -0
  36. ksadk/configs/env_var_spec.py +18 -0
  37. ksadk/configs/global_config.py +5 -0
  38. ksadk/context_engine/__init__.py +98 -0
  39. ksadk/context_engine/assembler.py +176 -0
  40. ksadk/context_engine/baseline.py +419 -0
  41. ksadk/context_engine/cache_observability.py +228 -0
  42. ksadk/context_engine/capabilities.py +443 -0
  43. ksadk/context_engine/contributors.py +344 -0
  44. ksadk/context_engine/hosted_pipeline.py +417 -0
  45. ksadk/context_engine/models.py +117 -0
  46. ksadk/context_engine/planner.py +698 -0
  47. ksadk/context_engine/policies.py +319 -0
  48. ksadk/context_engine/projection.py +31 -0
  49. ksadk/context_engine/shadow_plan.py +266 -0
  50. ksadk/context_engine/tokenizer.py +164 -0
  51. ksadk/conversations/context.py +126 -6
  52. ksadk/conversations/message_projection.py +292 -28
  53. ksadk/conversations/model_context.py +49 -3
  54. ksadk/conversations/runtime_compaction.py +207 -5
  55. ksadk/conversations/runtime_input.py +184 -2
  56. ksadk/conversations/runtime_invocation.py +81 -3
  57. ksadk/conversations/runtime_observability.py +141 -0
  58. ksadk/conversations/runtime_payloads.py +42 -0
  59. ksadk/conversations/runtime_preparation.py +272 -2
  60. ksadk/conversations/runtime_resume.py +20 -1
  61. ksadk/conversations/runtime_stream_events.py +94 -9
  62. ksadk/conversations/semantic_summary.py +311 -1
  63. ksadk/conversations/session_lock.py +51 -0
  64. ksadk/deployment/env_forward.py +72 -0
  65. ksadk/deployment/managed_runtime.py +5 -0
  66. ksadk/deployment/providers/serverless.py +143 -69
  67. ksadk/evaluation/__init__.py +32 -0
  68. ksadk/evaluation/a2a_adapter.py +9 -1
  69. ksadk/evaluation/adapters.py +18 -5
  70. ksadk/evaluation/agent_eval_client.py +395 -0
  71. ksadk/evaluation/cloud_binding.py +80 -0
  72. ksadk/evaluation/cloud_converter.py +194 -0
  73. ksadk/evaluation/cloud_service.py +199 -0
  74. ksadk/evaluation/contracts.py +73 -4
  75. ksadk/evaluation/evaluators.py +183 -17
  76. ksadk/evaluation/evidence.py +258 -0
  77. ksadk/evaluation/executor.py +31 -6
  78. ksadk/evaluation/local_adapter.py +550 -0
  79. ksadk/evaluation/service_env.py +27 -0
  80. ksadk/evaluation/studio_build_adapter.py +253 -0
  81. ksadk/evaluation/target.py +15 -6
  82. ksadk/events/__init__.py +13 -13
  83. ksadk/events/_v1_compat/__init__.py +1 -0
  84. ksadk/events/_v1_compat/models.py +299 -0
  85. ksadk/events/_v1_compat/parser.py +247 -0
  86. ksadk/events/_v1_compat/projection.py +753 -0
  87. ksadk/events/adapters/__init__.py +34 -0
  88. ksadk/events/adapters/_a2a_snapshot.py +622 -0
  89. ksadk/events/adapters/_a2a_support.py +275 -0
  90. ksadk/events/adapters/_codex_interactions.py +375 -0
  91. ksadk/events/adapters/_codex_items.py +801 -0
  92. ksadk/events/adapters/_codex_validators.py +311 -0
  93. ksadk/events/adapters/_langgraph_support.py +785 -0
  94. ksadk/events/adapters/a2a.py +841 -0
  95. ksadk/events/adapters/adk.py +503 -0
  96. ksadk/events/adapters/codex.py +717 -0
  97. ksadk/events/adapters/langgraph.py +745 -0
  98. ksadk/events/canonical.py +440 -0
  99. ksadk/events/canonical_replay.py +497 -0
  100. ksadk/events/canonical_store.py +468 -0
  101. ksadk/events/cold_recovery.py +257 -0
  102. ksadk/events/content.py +89 -0
  103. ksadk/events/identity.py +86 -0
  104. ksadk/events/pipeline.py +455 -0
  105. ksadk/events/projections.py +66 -0
  106. ksadk/events/reducer.py +661 -0
  107. ksadk/events/replay.py +3 -36
  108. ksadk/events/runtime_event.py +20 -265
  109. ksadk/events/session_event.py +324 -0
  110. ksadk/events/store.py +3 -316
  111. ksadk/events/v1_compat.py +41 -0
  112. ksadk/harness/runtime.py +160 -57
  113. ksadk/identity/resolver.py +4 -3
  114. ksadk/interaction/__init__.py +28 -0
  115. ksadk/interaction/contracts.py +136 -0
  116. ksadk/interaction/ledger.py +189 -0
  117. ksadk/interaction/provider.py +122 -0
  118. ksadk/interaction/providers/__init__.py +52 -0
  119. ksadk/interaction/providers/adk.py +40 -0
  120. ksadk/interaction/providers/codex.py +94 -0
  121. ksadk/interaction/providers/langgraph.py +58 -0
  122. ksadk/kernel/__init__.py +117 -0
  123. ksadk/kernel/authorization.py +258 -0
  124. ksadk/kernel/bootstrap.py +1074 -0
  125. ksadk/kernel/contract_fingerprints.py +61 -0
  126. ksadk/kernel/contracts.py +362 -0
  127. ksadk/kernel/control.py +219 -0
  128. ksadk/kernel/errors.py +79 -0
  129. ksadk/kernel/ingress.py +1102 -0
  130. ksadk/kernel/mapping.py +111 -0
  131. ksadk/kernel/memory_store.py +1133 -0
  132. ksadk/kernel/postgres_store.py +1756 -0
  133. ksadk/kernel/recovery.py +452 -0
  134. ksadk/kernel/runtime_identity.py +103 -0
  135. ksadk/kernel/sql/001_agent_kernel.sql +120 -0
  136. ksadk/kernel/sqlite_store.py +1410 -0
  137. ksadk/kernel/state.py +145 -0
  138. ksadk/kernel/store.py +232 -0
  139. ksadk/kernel/worker.py +953 -0
  140. ksadk/knowledge_base/client.py +7 -0
  141. ksadk/knowledge_base/service.py +32 -5
  142. ksadk/memory/__init__.py +61 -0
  143. ksadk/memory/adk/backends/base_ltm_backend.py +96 -1
  144. ksadk/memory/adk/backends/http_ltm_backend.py +14 -5
  145. ksadk/memory/adk/backends/inmemory_ltm_backend.py +136 -3
  146. ksadk/memory/adk/backends/sdk_ltm_backend.py +384 -2
  147. ksadk/memory/adk/backends/sqlite_ltm_backend.py +95 -0
  148. ksadk/memory/coordinator.py +461 -0
  149. ksadk/memory/events.py +202 -0
  150. ksadk/memory/extraction.py +228 -0
  151. ksadk/memory/ltm_backend_factory.py +16 -3
  152. ksadk/memory/models.py +316 -0
  153. ksadk/memory/policy.py +215 -0
  154. ksadk/memory/provider.py +47 -0
  155. ksadk/memory/provider_adapter.py +159 -0
  156. ksadk/memory/provider_resolver.py +71 -0
  157. ksadk/memory/providers/__init__.py +5 -0
  158. ksadk/memory/providers/local_sqlite.py +490 -0
  159. ksadk/memory/resolved_policy.py +145 -0
  160. ksadk/memory/service.py +188 -6
  161. ksadk/model_proxy/bootstrap.py +16 -9
  162. ksadk/model_proxy/detect.py +202 -9
  163. ksadk/model_proxy/namespace.py +39 -15
  164. ksadk/model_proxy/server.py +24 -5
  165. ksadk/model_proxy/transform.py +60 -2
  166. ksadk/observability/__init__.py +25 -0
  167. ksadk/observability/session_log.py +420 -0
  168. ksadk/observability/trajectory.py +105 -0
  169. ksadk/prompts/__init__.py +71 -0
  170. ksadk/prompts/compiler.py +215 -0
  171. ksadk/prompts/models.py +82 -0
  172. ksadk/prompts/projection.py +89 -0
  173. ksadk/prompts/resolved.py +137 -0
  174. ksadk/prompts/sources.py +233 -0
  175. ksadk/runners/_langgraph_runner_streams.py +822 -0
  176. ksadk/runners/adk_runner.py +5 -5
  177. ksadk/runners/base_runner.py +16 -0
  178. ksadk/runners/factory.py +1 -2
  179. ksadk/runners/langgraph_runner.py +198 -437
  180. ksadk/runtime/_runner_adapter/__init__.py +1 -0
  181. ksadk/runtime/_runner_adapter/stream_mapping.py +788 -0
  182. ksadk/runtime/adapter.py +118 -2
  183. ksadk/runtime/conversation_execution.py +316 -182
  184. ksadk/runtime/executor.py +144 -3
  185. ksadk/runtime/factory.py +193 -7
  186. ksadk/runtime/hosted_finalizer.py +268 -0
  187. ksadk/runtime/launch.py +14 -1
  188. ksadk/runtime/preprocessing.py +25 -3
  189. ksadk/runtime/runner_adapter.py +288 -508
  190. ksadk/runtime/usage.py +33 -0
  191. ksadk/sandbox/backends/e2b.py +35 -2
  192. ksadk/server/composition.py +8 -0
  193. ksadk/server/factory.py +69 -6
  194. ksadk/server/routes/kernel_ingress.py +258 -0
  195. ksadk/server/routes/openai_compat.py +88 -8
  196. ksadk/server/routes/projection.py +35 -20
  197. ksadk/server/routes/run.py +94 -0
  198. ksadk/server/routes/sessions.py +2 -0
  199. ksadk/server/routes/streaming.py +5 -2
  200. ksadk/server/static/assets/{ArtifactsPanel-CakCqxEN.js → ArtifactsPanel-DSU1sWD_.js} +1 -1
  201. ksadk/server/static/assets/{CodeBlock-CynLknyO.js → CodeBlock-DOH6MVcn.js} +4 -4
  202. ksadk/server/static/assets/{MathMessageMarkdown-3sQdOysS.js → MathMessageMarkdown-BL8my1R2.js} +4 -4
  203. ksadk/server/static/assets/MathMessageMarkdown-BorAY7qD.css +1 -0
  204. ksadk/server/static/assets/MermaidBlock-Dz4IP-Tx.js +312 -0
  205. ksadk/server/static/assets/NativeTerminalPanel-DUrK0JpZ.js +1 -0
  206. ksadk/server/static/assets/abnfDiagram-VCTEODGH-g20pFzNV.js +1 -0
  207. ksadk/server/static/assets/arc-C4FzinUA.js +1 -0
  208. ksadk/server/static/assets/architecture-7GRP2DOG-DjKratdf.js +1 -0
  209. ksadk/server/static/assets/architectureDiagram-5GKGNRK7-DpIqL5h4.js +36 -0
  210. ksadk/server/static/assets/blockDiagram-NRAW4CY4-BjkljTNz.js +129 -0
  211. ksadk/server/static/assets/c4Diagram-UCG6FXSJ-Dk_ieq2X.js +38 -0
  212. ksadk/server/static/assets/channel-4cQHKtx1.js +1 -0
  213. ksadk/server/static/assets/chunk-2Q5K7J3B-DmgWkESh.js +1 -0
  214. ksadk/server/static/assets/{chunk-FMBD7UC4-B_fH2YuH.js → chunk-5VM5RSS4-BVHAchFb.js} +1 -1
  215. ksadk/server/static/assets/{chunk-ND2GUHAM-D6rFLaVV.js → chunk-F27PBJKO-C-ipQzuS.js} +1 -1
  216. ksadk/server/static/assets/chunk-G27WJ6UU-C2EyJbIK.js +231 -0
  217. ksadk/server/static/assets/{chunk-4BX2VUAB-B9drTgOW.js → chunk-JWPE2WC7-vYvVJb_M.js} +1 -1
  218. ksadk/server/static/assets/chunk-LCL6LL3I-BcI6ysPT.js +206 -0
  219. ksadk/server/static/assets/chunk-POPQ4Y6H-C030x_Z1.js +1 -0
  220. ksadk/server/static/assets/chunk-RHFEMEQ7-yyGpsz4n.js +168 -0
  221. ksadk/server/static/assets/chunk-SVP7TREG-BLTlmMU7.js +88 -0
  222. ksadk/server/static/assets/chunk-XXDRQBXY-C_32ArgP.js +1 -0
  223. ksadk/server/static/assets/classDiagram-DTDB5LWJ-DLFd9Xi0.js +1 -0
  224. ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-DLFd9Xi0.js +1 -0
  225. ksadk/server/static/assets/{cose-bilkent-S5V4N54A-ChNs_Mdk.js → cose-bilkent-JH36ORCC-9YQYwdl0.js} +1 -1
  226. ksadk/server/static/assets/cynefin-OW5HDTMX-DjEM48Qp.js +1 -0
  227. ksadk/server/static/assets/cynefinDiagram-5FMLGOSQ-ErLF5N13.js +62 -0
  228. ksadk/server/static/assets/cytoscape.esm-CyCl8rPi.js +321 -0
  229. ksadk/server/static/assets/dagre-3AP2YEHR-DFiQF-6f.js +4 -0
  230. ksadk/server/static/assets/dagre-BuhGnRI1.js +1 -0
  231. ksadk/server/static/assets/diagram-S7CK7UJ4-Bt1v8-GC.js +30 -0
  232. ksadk/server/static/assets/diagram-UQ7AKVKN-DSCxJdBK.js +41 -0
  233. ksadk/server/static/assets/diagram-VSXAHHWV-DHfYwp_9.js +3 -0
  234. ksadk/server/static/assets/diagram-VX7I27RA-DdzD-4Le.js +24 -0
  235. ksadk/server/static/assets/diagram-Z3DM3KII-Du-nAJ9F.js +24 -0
  236. ksadk/server/static/assets/{dist-Dli7qW-B.js → dist-8hqcx0sU.js} +1 -1
  237. ksadk/server/static/assets/dist-B7seoj3d.js +1 -0
  238. ksadk/server/static/assets/{dist-C1sz5Eo3.js → dist-BE7bu-DL.js} +1 -1
  239. ksadk/server/static/assets/{dist-fhOuWds5.js → dist-BVDffZ0U.js} +1 -1
  240. ksadk/server/static/assets/{dist-BjOdlDjj.js → dist-BX8z72IC.js} +1 -1
  241. ksadk/server/static/assets/{dist-DsHPdk3W.js → dist-Bf2M8m3N.js} +1 -1
  242. ksadk/server/static/assets/{dist-o2OVmKvt.js → dist-BjU6y-A2.js} +1 -1
  243. ksadk/server/static/assets/{dist-DWKByxWV.js → dist-BytlxIDT.js} +1 -1
  244. ksadk/server/static/assets/{dist-DJN_6Y39.js → dist-CSh_DE14.js} +1 -1
  245. ksadk/server/static/assets/{dist-DQU9EWw-.js → dist-CXYYBw3_.js} +1 -1
  246. ksadk/server/static/assets/{dist-4N2NMVYg.js → dist-C_wsv-Qd.js} +1 -1
  247. ksadk/server/static/assets/{dist-gLnrGsoU.js → dist-ClxGviKw.js} +1 -1
  248. ksadk/server/static/assets/{dist-j6Dxmf_j.js → dist-CttYUqzS.js} +1 -1
  249. ksadk/server/static/assets/{dist-D6MpCZtz.js → dist-DNp6rLRA.js} +1 -1
  250. ksadk/server/static/assets/{dist-BB3-OwTQ.js → dist-DUX-id_F.js} +1 -1
  251. ksadk/server/static/assets/{dist-_pHPTyZ-.js → dist-DmldrHd_.js} +1 -1
  252. ksadk/server/static/assets/{dist-CWV4lDr1.js → dist-DnfXs8Vn.js} +2 -2
  253. ksadk/server/static/assets/{dist-B5oS1zcj.js → dist-Dq9gLD7L.js} +1 -1
  254. ksadk/server/static/assets/{dist-B-jNR6Vn.js → dist-W-TIVntx.js} +1 -1
  255. ksadk/server/static/assets/{dist-BIlKoW0k.js → dist-dDdADKFA.js} +1 -1
  256. ksadk/server/static/assets/{dist-X4TnqwOK.js → dist-dydu68Fl.js} +1 -1
  257. ksadk/server/static/assets/{dist-CmZHzlwZ.js → dist-ngtN0DJB.js} +1 -1
  258. ksadk/server/static/assets/ebnfDiagram-PWID7BFC-Da9C9NO1.js +1 -0
  259. ksadk/server/static/assets/erDiagram-SSCWMZ5O-EAGqqR79.js +99 -0
  260. ksadk/server/static/assets/eventmodeling-NTZA5JFV-CMgMaJrC.js +1 -0
  261. ksadk/server/static/assets/flowDiagram-A5DVABFB-BBKL0x3P.js +1 -0
  262. ksadk/server/static/assets/ganttDiagram-EL5Y4UJY-DqJsKb59.js +292 -0
  263. ksadk/server/static/assets/gitGraph-4MIJSDKK-Dw63mrhw.js +1 -0
  264. ksadk/server/static/assets/gitGraphDiagram-WWUBYQGX-B6g4dtDi.js +106 -0
  265. ksadk/server/static/assets/index-8ipRcQ-M.js +240 -0
  266. ksadk/server/static/assets/index-ByFCqjlh.css +2 -0
  267. ksadk/server/static/assets/info-A6RAGUB7-B6iAblII.js +1 -0
  268. ksadk/server/static/assets/infoDiagram-RXCK75RN-Djm_nbd5.js +2 -0
  269. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-K0OOfVna.js → ishikawaDiagram-5VMMS53U-DWya9SG2.js} +6 -6
  270. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-8LeOJaZp.js → journeyDiagram-EYS64GPL-DutxhSNI.js} +6 -6
  271. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-C6uCgZrW.js → kanban-definition-3QL26DDD-DJdOF8Fm.js} +10 -10
  272. ksadk/server/static/assets/katex-vFWytM5c.js +257 -0
  273. ksadk/server/static/assets/{linear-hdBcYzM8.js → linear-BHjG8b-J.js} +1 -1
  274. ksadk/server/static/assets/mermaid-parser.core-KGSy4jWT.js +166 -0
  275. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-Bjy_2v4_.js → mindmap-definition-FBJOCRG2-9-HC88D4.js} +29 -29
  276. ksadk/server/static/assets/packet-AYTQ26CC-BOYAipuq.js +1 -0
  277. ksadk/server/static/assets/pegDiagram-XKGWAZYB-DrEZd4fD.js +1 -0
  278. ksadk/server/static/assets/pie-WAS4IAKB-Ctq0Kbku.js +1 -0
  279. ksadk/server/static/assets/pieDiagram-E7YTZNPT-DpjqOFFp.js +39 -0
  280. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-BmkRbwCg.js → quadrantDiagram-AXDQQJYC-BiNhgOXX.js} +6 -6
  281. ksadk/server/static/assets/radar-RG4KPBEZ-DN0-ul_0.js +1 -0
  282. ksadk/server/static/assets/railroad-74A4TZTK-C6cC_to0.js +1 -0
  283. ksadk/server/static/assets/railroad-abnf-HS5TGJTU-BimjIuRc.js +1 -0
  284. ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-CvY65ABq.js +1 -0
  285. ksadk/server/static/assets/railroad-peg-WCYAUIDC-D72Iq_Tv.js +1 -0
  286. ksadk/server/static/assets/railroadDiagram-O6MQD6OU-_Y3Ojg6G.js +1 -0
  287. ksadk/server/static/assets/requirementDiagram-EFPCY7ZU-DeCFdg9K.js +84 -0
  288. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-l6iXmU65.js → sankeyDiagram-P5KCCOFB-CPH75rhw.js} +7 -7
  289. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-Cq6UBeQB.js → sequenceDiagram-WJ2MYXX4-OEpQQdr4.js} +10 -10
  290. ksadk/server/static/assets/sizeCapture-X5ZJPWSS-heUErzhI.js +1 -0
  291. ksadk/server/static/assets/stateDiagram-HBIQ2CUA-DvA3jSMB.js +1 -0
  292. ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BgQY03nz.js +1 -0
  293. ksadk/server/static/assets/swimlanes-XN3QIQJK-CR-dfN_t.js +1 -0
  294. ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-DLVw3q5Q.js +8 -0
  295. ksadk/server/static/assets/{timeline-definition-PNZ67QCA-DEz24vV0.js → timeline-definition-24CTP7MA-DAVJf1TX.js} +7 -7
  296. ksadk/server/static/assets/treeView-Q6P3EWNA-BMpO0wsB.js +1 -0
  297. ksadk/server/static/assets/treemap-WGGIJYW6-Et0aqzU3.js +1 -0
  298. ksadk/server/static/assets/vennDiagram-4TSXK5OY-DwM8eDkh.js +34 -0
  299. ksadk/server/static/assets/wardley-WFR3VGLG-CbY0mhpl.js +1 -0
  300. ksadk/server/static/assets/wardleyDiagram-VM6X3IG4-DZL-Zz2t.js +78 -0
  301. ksadk/server/static/assets/xychartDiagram-S5SC5T6Z-Dy6Yh_hu.js +7 -0
  302. ksadk/server/static/index.html +2 -2
  303. ksadk/sessions/__init__.py +26 -9
  304. ksadk/sessions/_local_service_sync.py +688 -0
  305. ksadk/sessions/_local_tables.py +21 -0
  306. ksadk/sessions/_postgres_schema.py +305 -0
  307. ksadk/sessions/_postgres_tables.py +15 -0
  308. ksadk/sessions/base.py +80 -1
  309. ksadk/sessions/in_memory.py +53 -2
  310. ksadk/sessions/local_service.py +63 -604
  311. ksadk/sessions/postgres_service.py +58 -148
  312. ksadk/sessions/resilient.py +35 -1
  313. ksadk/studio/api.py +577 -25
  314. ksadk/studio/api_contracts.py +114 -5
  315. ksadk/studio/api_memory_routes.py +82 -0
  316. ksadk/studio/authoring.py +252 -27
  317. ksadk/studio/authoring_coordinator.py +799 -67
  318. ksadk/studio/builder.py +88 -11
  319. ksadk/studio/capabilities.py +17 -0
  320. ksadk/studio/cloud.py +1916 -95
  321. ksadk/studio/codex_agent_service.py +125 -14
  322. ksadk/studio/codex_authoring.py +469 -0
  323. ksadk/studio/codex_builder.py +138 -25
  324. ksadk/studio/codex_manifest.py +51 -22
  325. ksadk/studio/codex_run.py +46 -3
  326. ksadk/studio/compiler.py +1 -0
  327. ksadk/studio/contracts.py +114 -3
  328. ksadk/studio/event_store.py +25 -95
  329. ksadk/studio/framework_run.py +231 -3
  330. ksadk/studio/hosted_kernel.py +317 -0
  331. ksadk/studio/manifest_resolver.py +131 -0
  332. ksadk/studio/model_client.py +95 -9
  333. ksadk/studio/model_profile_service.py +1 -1
  334. ksadk/studio/operations.py +37 -5
  335. ksadk/studio/otel_trace.py +348 -42
  336. ksadk/studio/pcm_memory.py +85 -0
  337. ksadk/studio/repository.py +23 -2
  338. ksadk/studio/resource_catalog.py +43 -0
  339. ksadk/studio/run_service.py +563 -137
  340. ksadk/studio/runtime_source.py +42 -16
  341. ksadk/studio/service.py +1180 -72
  342. ksadk/studio/shared_web.py +32 -22
  343. ksadk/studio/static/assets/index-BLpcdrgW.css +1 -0
  344. ksadk/studio/static/assets/index-CYekR2Xv.js +260 -0
  345. ksadk/studio/static/index.html +2 -2
  346. ksadk/studio/templates.py +100 -8
  347. ksadk/studio/validator.py +19 -0
  348. ksadk/studio/workspace.py +30 -32
  349. ksadk/tracing/setup.py +304 -3
  350. ksadk/version.py +1 -1
  351. ksadk_runtime_common/schemas/runtime_event_v1.json +37 -0
  352. ksadk_runtime_common/schemas/runtime_event_v2.json +271 -0
  353. ksadk/events/parser.py +0 -191
  354. ksadk/server/static/assets/MathMessageMarkdown-DeYh8QPr.css +0 -1
  355. ksadk/server/static/assets/MermaidBlock-NERD_ef7.js +0 -303
  356. ksadk/server/static/assets/NativeTerminalPanel-CiHWf82q.js +0 -1
  357. ksadk/server/static/assets/arc-DrgjcKhl.js +0 -1
  358. ksadk/server/static/assets/architecture-7EHR7CIX-CpZLRCcY.js +0 -1
  359. ksadk/server/static/assets/architectureDiagram-3BPJPVTR-BXoVRbQ6.js +0 -36
  360. ksadk/server/static/assets/blockDiagram-GPEHLZMM-BnaB3Ywv.js +0 -132
  361. ksadk/server/static/assets/c4Diagram-AAUBKEIU-Dw4weZcp.js +0 -10
  362. ksadk/server/static/assets/channel-Dt3rQTFI.js +0 -1
  363. ksadk/server/static/assets/chunk-2J33WTMH-ChjpLK7j.js +0 -1
  364. ksadk/server/static/assets/chunk-55IACEB6-CK4ROATy.js +0 -1
  365. ksadk/server/static/assets/chunk-727SXJPM-B9KtTTgw.js +0 -206
  366. ksadk/server/static/assets/chunk-AQP2D5EJ-1O1mki3m.js +0 -231
  367. ksadk/server/static/assets/chunk-QZHKN3VN-iXEZtKQc.js +0 -1
  368. ksadk/server/static/assets/classDiagram-4FO5ZUOK-DIveV17Q.js +0 -1
  369. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-DIveV17Q.js +0 -1
  370. ksadk/server/static/assets/cytoscape.esm-DelgaX4f.js +0 -321
  371. ksadk/server/static/assets/dagre-BM42HDAG-D0QXg_MQ.js +0 -4
  372. ksadk/server/static/assets/dagre-Bx709z4p.js +0 -1
  373. ksadk/server/static/assets/diagram-2AECGRRQ-DBDDKwBQ.js +0 -43
  374. ksadk/server/static/assets/diagram-5GNKFQAL-BkwDOxmO.js +0 -10
  375. ksadk/server/static/assets/diagram-KO2AKTUF-Bv8waYvY.js +0 -3
  376. ksadk/server/static/assets/diagram-LMA3HP47-e1VECDdu.js +0 -24
  377. ksadk/server/static/assets/diagram-OG6HWLK6-Ds9adfRl.js +0 -24
  378. ksadk/server/static/assets/dist-B5J2uhNk.js +0 -1
  379. ksadk/server/static/assets/erDiagram-TEJ5UH35-BPItFKH6.js +0 -85
  380. ksadk/server/static/assets/eventmodeling-FCH6USID-DYFSxbqr.js +0 -1
  381. ksadk/server/static/assets/flowDiagram-I6XJVG4X-BbYB9w5Y.js +0 -162
  382. ksadk/server/static/assets/ganttDiagram-6RSMTGT7-TQXa0KXv.js +0 -292
  383. ksadk/server/static/assets/gitGraph-WXDBUCRP-DyNYp6BN.js +0 -1
  384. ksadk/server/static/assets/gitGraphDiagram-PVQCEYII-bKblCnpa.js +0 -106
  385. ksadk/server/static/assets/graphlib-B8gBHxth.js +0 -1
  386. ksadk/server/static/assets/index-R52pJJvg.js +0 -177
  387. ksadk/server/static/assets/index-jJr3_955.css +0 -2
  388. ksadk/server/static/assets/info-J43DQDTF-WUx7qTde.js +0 -1
  389. ksadk/server/static/assets/infoDiagram-5YYISTIA-BGVT7J_i.js +0 -2
  390. ksadk/server/static/assets/katex-CYWseY5E.js +0 -265
  391. ksadk/server/static/assets/mermaid-parser.core-BWrDjSZM.js +0 -161
  392. ksadk/server/static/assets/packet-YPE3B663-BN13y_xl.js +0 -1
  393. ksadk/server/static/assets/pie-LRSECV5Y-ftSLXhhc.js +0 -1
  394. ksadk/server/static/assets/pieDiagram-4H26LBE5-GmmqNiVH.js +0 -30
  395. ksadk/server/static/assets/radar-GUYGQ44K-jaICkIeO.js +0 -1
  396. ksadk/server/static/assets/requirementDiagram-4Y6WPE33-DSWFqL7c.js +0 -84
  397. ksadk/server/static/assets/stateDiagram-AJRCARHV-qzTQpg3Y.js +0 -1
  398. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-BYCoYGAN.js +0 -1
  399. ksadk/server/static/assets/treeView-BLDUP644-Cpib_vlz.js +0 -1
  400. ksadk/server/static/assets/treemap-LRROVOQU-DQxfmVHK.js +0 -1
  401. ksadk/server/static/assets/vennDiagram-CIIHVFJN-CMCixki2.js +0 -34
  402. ksadk/server/static/assets/wardley-L42UT6IY-BQYHnnkU.js +0 -1
  403. ksadk/server/static/assets/wardleyDiagram-YWT4CUSO-CVzB8F9K.js +0 -78
  404. ksadk/server/static/assets/xychartDiagram-2RQKCTM6-BFBqBLc1.js +0 -7
  405. ksadk/studio/evaluation.py +0 -192
  406. ksadk/studio/static/assets/index-CaEN0eTR.css +0 -1
  407. ksadk/studio/static/assets/index-DI2eY_NH.js +0 -252
  408. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/WHEEL +0 -0
  409. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/entry_points.txt +0 -0
  410. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/licenses/LICENSE +0 -0
  411. {agentengine_sdk_python-0.8.1.dist-info → agentengine_sdk_python-0.8.2.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,4 @@
1
- """Persistent local Run and Event store."""
1
+ """Persistent local Run record store and derived trace access."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -6,11 +6,11 @@ import json
6
6
  import shutil
7
7
  from datetime import datetime, timezone
8
8
  from pathlib import Path
9
- from typing import List
9
+ from typing import Any
10
10
 
11
11
  from pydantic import ValidationError
12
12
 
13
- from ksadk.studio.contracts import RunEvent, RunRecord, RunStatus
13
+ from ksadk.studio.contracts import RunEvent, RunRecord
14
14
  from ksadk.studio.errors import StudioError, not_found
15
15
  from ksadk.studio.otel_trace import OtlpTraceStore
16
16
  from ksadk.studio.workspace import Workspace
@@ -28,15 +28,21 @@ class RunEventStore:
28
28
  path = self._path(record.id)
29
29
  if path.exists():
30
30
  raise StudioError("RUN_ALREADY_EXISTS", "Run 已存在", status_code=409)
31
- self._write(record, [])
31
+ self._write(record)
32
32
  return record
33
33
 
34
34
  def save(self, record: RunRecord) -> RunRecord:
35
35
  _, events = self._read(record.id)
36
- self._write(record, events)
36
+ self._write(record, events or None)
37
37
  return record
38
38
 
39
39
  def append(self, run_id: str, event_type: str, data: dict) -> RunEvent:
40
+ """Persist a Studio lifecycle RunEvent (run.created, memory.recall.projected, …).
41
+
42
+ These Studio-level events (as opposed to RuntimeEvents) are durably
43
+ stored in the run JSON so the Studio events timeline survives restarts.
44
+ Runs that never call ``append`` keep ``set(run_payload) == {"record"}``.
45
+ """
40
46
  record, events = self._read(run_id)
41
47
  event = RunEvent(
42
48
  id=len(events) + 1,
@@ -48,20 +54,20 @@ class RunEventStore:
48
54
  self._write(record, events)
49
55
  return event
50
56
 
51
- def get(self, run_id: str) -> RunRecord:
52
- record, _ = self._read(run_id)
53
- return record
54
-
55
57
  def events(self, run_id: str, *, after: int = 0) -> list[RunEvent]:
56
58
  _, events = self._read(run_id)
57
59
  return [event for event in events if event.id > after]
58
60
 
61
+ def get(self, run_id: str) -> RunRecord:
62
+ record, _ = self._read(run_id)
63
+ return record
64
+
59
65
  def list_runs(
60
66
  self,
61
67
  *,
62
68
  session_id: str | None = None,
63
69
  agent_id: str | None = None,
64
- ) -> List[RunRecord]:
70
+ ) -> list[RunRecord]:
65
71
  records: list[RunRecord] = []
66
72
  directory = self.workspace.resolve(".agentkit/runs")
67
73
  for path in sorted(directory.glob("run_*.json")):
@@ -126,82 +132,6 @@ class RunEventStore:
126
132
  deleted += 1
127
133
  return deleted
128
134
 
129
- def recover_interrupted(self) -> int:
130
- """Reconcile non-terminal records left behind by a stopped Studio.
131
-
132
- Events are persisted before the final RunRecord update. A browser
133
- disconnect or process stop can therefore leave a terminal event next
134
- to a stale ``RUNNING`` record. On startup the event log wins; a run
135
- without any terminal event is explicitly marked interrupted because
136
- its in-memory runtime task cannot survive a Studio restart.
137
- """
138
-
139
- recovered = 0
140
- for record in self.list_runs():
141
- if record.status not in {
142
- RunStatus.CREATED,
143
- RunStatus.RUNNING,
144
- RunStatus.PAUSED,
145
- RunStatus.WAITING_INPUT,
146
- }:
147
- continue
148
- events = self.events(record.id)
149
- terminal = next(
150
- (
151
- event
152
- for event in reversed(events)
153
- if event.type
154
- in {
155
- "run.completed",
156
- "run.failed",
157
- "run.cancelled",
158
- "run.interrupted",
159
- }
160
- ),
161
- None,
162
- )
163
- if terminal is None:
164
- terminal = self.append(
165
- record.id,
166
- "run.interrupted",
167
- {
168
- "status": "interrupted",
169
- "reason": "studio_restarted",
170
- },
171
- )
172
-
173
- if terminal.type == "run.completed":
174
- record.status = RunStatus.COMPLETED
175
- record.error = None
176
- elif terminal.type == "run.failed":
177
- record.status = RunStatus.FAILED
178
- record.error = {
179
- "code": "RUNTIME_RUN_FAILED",
180
- "message": str(
181
- terminal.data.get("error")
182
- or terminal.data.get("message")
183
- or "Agent 运行失败"
184
- ),
185
- }
186
- elif terminal.type == "run.cancelled":
187
- record.status = RunStatus.CANCELLED
188
- record.error = {"code": "RUN_CANCELLED", "message": "运行已取消"}
189
- else:
190
- record.status = RunStatus.INTERRUPTED
191
- record.error = {
192
- "code": "RUN_INTERRUPTED",
193
- "message": "Studio 重启后无法重新 attach 上一次本地运行",
194
- }
195
- record.completed_at = terminal.created_at
196
- if record.started_at is not None:
197
- record.duration_ms = max(
198
- 0,
199
- int((record.completed_at - record.started_at).total_seconds() * 1000),
200
- )
201
- self.save(record)
202
- recovered += 1
203
- return recovered
204
-
205
135
  def trace(self, trace_id: str) -> dict:
206
136
  return self.trace_store.get_trace_view(trace_id)
207
137
 
@@ -253,14 +183,14 @@ class RunEventStore:
253
183
  status=status,
254
184
  )
255
185
 
256
- def _read(self, run_id: str) -> tuple[RunRecord, List[RunEvent]]:
186
+ def _read(self, run_id: str) -> tuple[RunRecord, list[RunEvent]]:
257
187
  path = self._path(run_id)
258
188
  if not path.is_file():
259
189
  raise not_found("run", run_id)
260
190
  try:
261
191
  payload = json.loads(path.read_text(encoding="utf-8"))
262
192
  record = RunRecord.model_validate(payload["record"])
263
- events = [RunEvent.model_validate(item) for item in payload["events"]]
193
+ events = [RunEvent.model_validate(item) for item in payload.get("events", [])]
264
194
  return record, events
265
195
  except (OSError, ValueError, KeyError, ValidationError) as exc:
266
196
  raise StudioError(
@@ -270,15 +200,15 @@ class RunEventStore:
270
200
  details={"id": run_id},
271
201
  ) from exc
272
202
 
273
- def _write(self, record: RunRecord, events: List[RunEvent]) -> None:
274
- payload = {
275
- "record": record.model_dump(by_alias=True, exclude_none=True, mode="json"),
276
- "events": [
277
- event.model_dump(by_alias=True, exclude_none=True, mode="json") for event in events
278
- ],
203
+ def _write(self, record: RunRecord, events: list[RunEvent] | None = None) -> None:
204
+ payload: dict[str, Any] = {
205
+ "record": record.model_dump(by_alias=True, exclude_none=True, mode="json")
279
206
  }
207
+ if events is not None:
208
+ payload["events"] = [
209
+ event.model_dump(by_alias=True, exclude_none=True, mode="json") for event in events
210
+ ]
280
211
  self.workspace.atomic_write_text(
281
212
  self._path(record.id),
282
213
  json.dumps(payload, ensure_ascii=False, sort_keys=True, indent=2) + "\n",
283
214
  )
284
- self.trace_store.sync(record, events)
@@ -2,10 +2,15 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import hashlib
5
6
  import json
7
+ from pathlib import Path
8
+ from typing import Any
6
9
 
7
10
  from ksadk.detection.detector import FrameworkDetector
8
11
  from ksadk.runtime import RuntimeLaunchContext
12
+ from ksadk.studio.capabilities import compute_bundle_digest
13
+ from ksadk.studio.contracts import BundleManifest
9
14
  from ksadk.studio.errors import StudioError
10
15
  from ksadk.studio.repository import BuildRepository
11
16
  from ksadk.studio.run_service import StudioRunSpec
@@ -13,7 +18,79 @@ from ksadk.studio.workspace import Workspace
13
18
  from ksadk.tools.gateway import normalize_tool_approval_mode
14
19
 
15
20
 
21
+ def _resolved_prompt_ownership(resolved: Any) -> str:
22
+ """从 resolved-agent-spec.json 的 context 块读 prompt_ownership。
23
+
24
+ resolved spec 由 ContractModel 以 ``by_alias=True`` 序列化,alias_generator 为
25
+ camelCase,故字段名为 ``promptOwnership``;``populate_by_name`` 仅作用于输入,JSON
26
+ 输出仍为 alias。此处 camelCase 与 snake 两种写法都查,稳妥兼容。非 dict / 缺失时
27
+ 返回空串(== framework 默认,不接管 Runner 输入)。
28
+ """
29
+ if not isinstance(resolved, dict):
30
+ return ""
31
+ context = resolved.get("context")
32
+ if not isinstance(context, dict):
33
+ return ""
34
+ return str(
35
+ context.get("promptOwnership")
36
+ or context.get("prompt_ownership")
37
+ or context.get("ownership")
38
+ or ""
39
+ )
40
+
41
+
42
+ def _resolved_context_engine_rollout(resolved: Any) -> str:
43
+ """读取 AgentVersion 固化的 Context Engine rollout。"""
44
+ if not isinstance(resolved, dict):
45
+ return ""
46
+ context = resolved.get("context")
47
+ if not isinstance(context, dict):
48
+ return ""
49
+ rollout = context.get("rollout")
50
+ if not isinstance(rollout, dict):
51
+ return ""
52
+ return str(rollout.get("contextEngine") or rollout.get("context_engine") or "")
53
+
54
+
55
+ def _resolved_memory_recall_enabled(resolved: Any) -> bool | None:
56
+ """读取 AgentVersion 的 Memory 召回开关;缺失时保留旧环境策略。"""
57
+ if not isinstance(resolved, dict):
58
+ return None
59
+ memory = resolved.get("memory")
60
+ if not isinstance(memory, dict) or "enabled" not in memory:
61
+ return None
62
+ enabled = bool(memory.get("enabled"))
63
+ recall = memory.get("recall")
64
+ if isinstance(recall, dict) and "enabled" in recall:
65
+ enabled = enabled and bool(recall.get("enabled"))
66
+ context = resolved.get("context")
67
+ contributors = context.get("contributors") if isinstance(context, dict) else None
68
+ if isinstance(contributors, dict):
69
+ explicit = contributors.get("memoryRecall", contributors.get("memory_recall"))
70
+ if explicit is not None:
71
+ enabled = enabled and bool(explicit)
72
+ return enabled
73
+
74
+
75
+ def _resolved_memory_write_rollout(resolved: Any) -> str:
76
+ """读取 AgentVersion 固化的 Memory 写入 rollout。"""
77
+ if not isinstance(resolved, dict):
78
+ return ""
79
+ context = resolved.get("context")
80
+ if not isinstance(context, dict):
81
+ return ""
82
+ rollout = context.get("rollout")
83
+ if not isinstance(rollout, dict):
84
+ return ""
85
+ return str(rollout.get("memoryWrite") or rollout.get("memory_write") or "")
86
+
87
+
16
88
  class FrameworkRunSpecResolver:
89
+ # manifest.json 是 bundle 自身的描述文件,不进入 manifest.files 清单
90
+ # (builder 在扫描文件列表之后才写它),校验时排除。checksums.txt 在本仓
91
+ # bundle 格式中是 manifest 声明的普通成员,参与校验。
92
+ _BUNDLE_META_FILES = frozenset({"manifest.json"})
93
+
17
94
  def __init__(
18
95
  self,
19
96
  workspace: Workspace,
@@ -43,6 +120,9 @@ class FrameworkRunSpecResolver:
43
120
  raise StudioError("BUILD_NOT_READY", "Build 尚未生成制品", status_code=409)
44
121
  artifact_root = self.workspace.resolve(build.artifact_path, must_exist=True).parent
45
122
  bundle_root = artifact_root / "agent-bundle"
123
+ self._verify_bundle_integrity(
124
+ bundle_root, expected_bundle_digest=build.bundle_digest
125
+ )
46
126
  project_dir = bundle_root / "runtime"
47
127
  if not project_dir.is_dir():
48
128
  raise StudioError(
@@ -67,13 +147,38 @@ class FrameworkRunSpecResolver:
67
147
  instructions = resolved.get("instructions") if isinstance(resolved, dict) else {}
68
148
  request_config = {
69
149
  "base_instructions": str((instructions or {}).get("system") or ""),
150
+ # Preserve system/task as separate PCM sources while keeping the
151
+ # framework runner's existing base_instructions projection.
152
+ "agent_system": str((instructions or {}).get("system") or ""),
153
+ "agent_task": str((instructions or {}).get("task") or ""),
154
+ **(
155
+ {"prompt_integration_mode": "ksadk_hosted"}
156
+ if _resolved_prompt_ownership(resolved) == "ksadk"
157
+ else {}
158
+ ),
159
+ "context_engine_rollout": _resolved_context_engine_rollout(resolved),
160
+ "memory_recall_enabled": _resolved_memory_recall_enabled(resolved),
161
+ "memory_write_rollout": _resolved_memory_write_rollout(resolved),
162
+ "memory_enabled": _resolved_memory_enabled(resolved),
163
+ "memory_write_mode": _resolved_memory_write_mode(resolved),
164
+ "flush_before_compaction": _resolved_memory_flush_before_compaction(resolved),
165
+ "provider_ref": _resolved_memory_provider_ref(resolved),
70
166
  "entry_point": detection.entry_point,
71
167
  "agent_variable": detection.agent_variable,
72
168
  }
73
- if approval_mode:
74
- request_config["tool_approval_mode"] = normalize_tool_approval_mode(
75
- approval_mode
169
+ # AgentVersion 的 ContextSpec 预算传到 Planner(方案 §8.2)
170
+ context_spec = resolved.get("context") if isinstance(resolved, dict) else {}
171
+ if isinstance(context_spec, dict):
172
+ max_input = context_spec.get("maxInputTokens") or context_spec.get("max_input_tokens")
173
+ reserve_output = context_spec.get("reserveOutputTokens") or context_spec.get(
174
+ "reserve_output_tokens"
76
175
  )
176
+ if max_input is not None:
177
+ request_config["max_input_tokens"] = int(max_input)
178
+ if reserve_output is not None:
179
+ request_config["reserve_output_tokens"] = int(reserve_output)
180
+ if approval_mode:
181
+ request_config["tool_approval_mode"] = normalize_tool_approval_mode(approval_mode)
77
182
  return StudioRunSpec(
78
183
  launch_context=RuntimeLaunchContext(
79
184
  runtime_type=runtime_type,
@@ -110,5 +215,128 @@ class FrameworkRunSpecResolver:
110
215
  )
111
216
  return selected
112
217
 
218
+ def _verify_bundle_integrity(
219
+ self,
220
+ bundle_dir: Path,
221
+ *,
222
+ expected_bundle_digest: str = "",
223
+ ) -> None:
224
+ """加载前校验 bundle 未被篡改:manifest 自身摘要、与 Build 记录一致、
225
+ 文件清单无增删、每个文件 sha256/size 匹配。"""
226
+ try:
227
+ manifest = BundleManifest.model_validate_json(
228
+ (bundle_dir / "manifest.json").read_text(encoding="utf-8")
229
+ )
230
+ except (OSError, ValueError) as exc:
231
+ raise StudioError(
232
+ "BUILD_ARTIFACT_INVALID",
233
+ "Build 缺少有效的 Bundle Manifest",
234
+ status_code=500,
235
+ ) from exc
236
+ if manifest.bundle_digest != compute_bundle_digest(manifest):
237
+ raise StudioError(
238
+ "BUILD_ARTIFACT_INVALID",
239
+ "Bundle Manifest 摘要不匹配,Bundle 可能已被篡改",
240
+ status_code=500,
241
+ details={"bundleDigest": manifest.bundle_digest},
242
+ )
243
+ if expected_bundle_digest and manifest.bundle_digest != expected_bundle_digest:
244
+ raise StudioError(
245
+ "BUILD_ARTIFACT_INVALID",
246
+ "Bundle 与 Build 记录的摘要不一致,Bundle 可能已被篡改",
247
+ status_code=500,
248
+ details={
249
+ "expected": expected_bundle_digest,
250
+ "actual": manifest.bundle_digest,
251
+ },
252
+ )
253
+ declared = {entry.path for entry in manifest.files}
254
+ actual: dict[str, Path] = {}
255
+ for path in bundle_dir.rglob("*"):
256
+ if not path.is_file():
257
+ continue
258
+ relative = path.relative_to(bundle_dir).as_posix()
259
+ if relative in self._BUNDLE_META_FILES:
260
+ continue
261
+ actual[relative] = path
262
+ extra = sorted(set(actual) - declared)
263
+ if extra:
264
+ raise StudioError(
265
+ "BUILD_ARTIFACT_INVALID",
266
+ "Bundle 含未在 Manifest 中声明的文件",
267
+ status_code=500,
268
+ details={"extraFiles": extra},
269
+ )
270
+ for entry in manifest.files:
271
+ file_path = actual.get(entry.path)
272
+ if file_path is None:
273
+ raise StudioError(
274
+ "BUILD_ARTIFACT_INVALID",
275
+ "Bundle 缺少 Manifest 声明的文件",
276
+ status_code=500,
277
+ details={"missingFile": entry.path},
278
+ )
279
+ content = file_path.read_bytes()
280
+ actual_sha = f"sha256:{hashlib.sha256(content).hexdigest()}"
281
+ if actual_sha != entry.sha256:
282
+ raise StudioError(
283
+ "BUILD_ARTIFACT_INVALID",
284
+ "Bundle 文件摘要不匹配",
285
+ status_code=500,
286
+ details={
287
+ "path": entry.path,
288
+ "expected": entry.sha256,
289
+ "actual": actual_sha,
290
+ },
291
+ )
292
+ if len(content) != entry.size:
293
+ raise StudioError(
294
+ "BUILD_ARTIFACT_INVALID",
295
+ "Bundle 文件大小不匹配",
296
+ status_code=500,
297
+ details={
298
+ "path": entry.path,
299
+ "expected": entry.size,
300
+ "actual": len(content),
301
+ },
302
+ )
303
+
113
304
 
114
305
  __all__ = ["FrameworkRunSpecResolver"]
306
+
307
+
308
+ def _resolved_memory_enabled(resolved: Any) -> bool:
309
+ memory = resolved.get("memory") if isinstance(resolved, dict) else {}
310
+ return bool(memory.get("enabled", False)) if isinstance(memory, dict) else False
311
+
312
+
313
+ memory = resolved.get("memory") if isinstance(resolved, dict) else {}
314
+ if not isinstance(memory, dict) or not memory.get("enabled", False):
315
+ return False
316
+ recall = memory.get("recall", {})
317
+ return bool(recall.get("enabled", True)) if isinstance(recall, dict) else True
318
+
319
+
320
+ def _resolved_memory_write_mode(resolved: Any) -> str:
321
+ memory = resolved.get("memory") if isinstance(resolved, dict) else {}
322
+ write = memory.get("write", {}) if isinstance(memory, dict) else {}
323
+ return (
324
+ str(write.get("mode", "candidate") or "candidate")
325
+ if isinstance(write, dict)
326
+ else "candidate"
327
+ )
328
+
329
+
330
+ def _resolved_memory_flush_before_compaction(resolved: Any) -> bool:
331
+ memory = resolved.get("memory") if isinstance(resolved, dict) else {}
332
+ write = memory.get("write", {}) if isinstance(memory, dict) else {}
333
+ return bool(write.get("flushBeforeCompaction", True)) if isinstance(write, dict) else True
334
+
335
+
336
+ def _resolved_memory_provider_ref(resolved: Any) -> str:
337
+ memory = resolved.get("memory") if isinstance(resolved, dict) else {}
338
+ return (
339
+ str(memory.get("providerRef", "local-default") or "local-default")
340
+ if isinstance(memory, dict)
341
+ else "local-default"
342
+ )