agentengine-sdk-python 0.7.0__py3-none-any.whl → 0.8.0__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 (371) hide show
  1. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/METADATA +36 -20
  2. agentengine_sdk_python-0.8.0.dist-info/RECORD +578 -0
  3. ksadk/a2a/__init__.py +178 -11
  4. ksadk/a2a/bootstrap.py +334 -0
  5. ksadk/a2a/card.py +120 -0
  6. ksadk/a2a/context_store.py +203 -0
  7. ksadk/a2a/control_plane.py +818 -0
  8. ksadk/a2a/credential.py +99 -0
  9. ksadk/a2a/event_adapter.py +149 -0
  10. ksadk/a2a/executor.py +406 -89
  11. ksadk/a2a/external_transport.py +516 -0
  12. ksadk/a2a/identity.py +304 -0
  13. ksadk/a2a/ids.py +22 -0
  14. ksadk/a2a/langgraph.py +285 -0
  15. ksadk/a2a/resume_store.py +201 -0
  16. ksadk/a2a/routes.py +105 -0
  17. ksadk/a2a/server.py +107 -60
  18. ksadk/a2a/space_client.py +1101 -0
  19. ksadk/a2a/task_adapter.py +423 -0
  20. ksadk/a2a/task_event_dispatcher.py +167 -0
  21. ksadk/a2a/task_event_outbox.py +326 -0
  22. ksadk/a2a/task_store.py +206 -0
  23. ksadk/a2ui/__init__.py +27 -0
  24. ksadk/a2ui/core.py +186 -0
  25. ksadk/a2ui/models.py +166 -0
  26. ksadk/a2ui/renderer.py +198 -0
  27. ksadk/agui/__init__.py +9 -0
  28. ksadk/agui/a2ui_projection.py +92 -0
  29. ksadk/agui/agent.py +857 -0
  30. ksadk/agui/config.py +86 -0
  31. ksadk/agui/routes.py +55 -0
  32. ksadk/api/client.py +233 -191
  33. ksadk/builders/__init__.py +2 -0
  34. ksadk/builders/base.py +16 -14
  35. ksadk/builders/code_builder.py +318 -236
  36. ksadk/builders/container_builder.py +187 -167
  37. ksadk/builders/framework_requirements.py +29 -11
  38. ksadk/builders/ks3_uploader.py +58 -27
  39. ksadk/builders/managed_runtime_builder.py +145 -0
  40. ksadk/builders/mcp_builder.py +91 -92
  41. ksadk/builders/requirements_utils.py +1 -5
  42. ksadk/cli/__init__.py +89 -171
  43. ksadk/cli/cmd_a2a.py +254 -70
  44. ksadk/cli/cmd_agent.py +19 -5
  45. ksadk/cli/cmd_build.py +158 -29
  46. ksadk/cli/cmd_completion.py +47 -35
  47. ksadk/cli/cmd_config.py +267 -198
  48. ksadk/cli/cmd_create.py +500 -720
  49. ksadk/cli/cmd_dashboard.py +120 -45
  50. ksadk/cli/cmd_deploy.py +117 -58
  51. ksadk/cli/cmd_destroy.py +44 -14
  52. ksadk/cli/cmd_files.py +63 -24
  53. ksadk/cli/cmd_hermes.py +168 -89
  54. ksadk/cli/cmd_invoke.py +184 -122
  55. ksadk/cli/cmd_launch.py +64 -28
  56. ksadk/cli/cmd_mcp.py +98 -64
  57. ksadk/cli/cmd_model.py +14 -5
  58. ksadk/cli/cmd_openclaw.py +483 -227
  59. ksadk/cli/cmd_replay.py +71 -0
  60. ksadk/cli/cmd_run.py +33 -10
  61. ksadk/cli/cmd_status.py +80 -31
  62. ksadk/cli/cmd_version.py +20 -13
  63. ksadk/cli/cmd_web.py +89 -13
  64. ksadk/cli/deploy_utils.py +20 -20
  65. ksadk/cli/dry_run.py +5 -3
  66. ksadk/cli/error_utils.py +37 -12
  67. ksadk/cli/global_options.py +1 -2
  68. ksadk/cli/local_runtime.py +1 -2
  69. ksadk/cli/model_catalog.py +6 -2
  70. ksadk/cli/network_options.py +34 -14
  71. ksadk/cli/resource_common.py +6 -2
  72. ksadk/cli/storage.py +0 -1
  73. ksadk/cli/ui.py +5 -3
  74. ksadk/cli/workflow_common.py +51 -17
  75. ksadk/codex/__init__.py +12 -0
  76. ksadk/codex/client.py +408 -0
  77. ksadk/codex/phase.py +143 -0
  78. ksadk/codex/runtime.py +449 -0
  79. ksadk/common/aicp_env.py +0 -1
  80. ksadk/common/auth.py +2 -2
  81. ksadk/common/constants.py +1 -3
  82. ksadk/common/llm_utils.py +17 -13
  83. ksadk/compat/__init__.py +5 -0
  84. ksadk/compat/adk_compat.py +200 -0
  85. ksadk/compat/copilotkit_a2ui.py +330 -0
  86. ksadk/configs/__init__.py +12 -12
  87. ksadk/configs/env_registry.py +500 -100
  88. ksadk/configs/global_config.py +2 -1
  89. ksadk/configs/settings.py +151 -124
  90. ksadk/conversations/__init__.py +7 -5
  91. ksadk/conversations/attachment_storage.py +16 -11
  92. ksadk/conversations/attachments.py +44 -36
  93. ksadk/conversations/compaction_pipeline.py +12 -12
  94. ksadk/conversations/compaction_prompt.py +9 -5
  95. ksadk/conversations/context.py +221 -6
  96. ksadk/conversations/message_projection.py +438 -51
  97. ksadk/conversations/model_context.py +12 -6
  98. ksadk/conversations/model_options.py +0 -1
  99. ksadk/conversations/normalize.py +20 -16
  100. ksadk/conversations/reasoning_markup.py +1 -1
  101. ksadk/conversations/runtime.py +339 -5255
  102. ksadk/conversations/runtime_compaction.py +333 -0
  103. ksadk/conversations/runtime_constants.py +27 -0
  104. ksadk/conversations/runtime_governance.py +185 -0
  105. ksadk/conversations/runtime_input.py +652 -0
  106. ksadk/conversations/runtime_invocation.py +395 -0
  107. ksadk/conversations/runtime_metadata.py +475 -0
  108. ksadk/conversations/runtime_observability.py +355 -0
  109. ksadk/conversations/runtime_payloads.py +272 -0
  110. ksadk/conversations/runtime_persistence.py +440 -0
  111. ksadk/conversations/runtime_preparation.py +418 -0
  112. ksadk/conversations/runtime_resume.py +816 -0
  113. ksadk/conversations/runtime_stream_events.py +902 -0
  114. ksadk/conversations/runtime_streaming.py +547 -0
  115. ksadk/conversations/semantic_summary.py +17 -5
  116. ksadk/conversations/session_title.py +6 -3
  117. ksadk/deployment/__init__.py +15 -16
  118. ksadk/deployment/agent_access.py +12 -23
  119. ksadk/deployment/base.py +51 -38
  120. ksadk/deployment/manager.py +63 -65
  121. ksadk/deployment/providers/serverless.py +309 -202
  122. ksadk/deployment/registry.py +38 -32
  123. ksadk/deployment/state.py +11 -11
  124. ksadk/deployment/ui_config.py +1 -1
  125. ksadk/detection/__init__.py +1 -1
  126. ksadk/detection/detector.py +146 -83
  127. ksadk/detection/mcp_detector.py +9 -6
  128. ksadk/events/__init__.py +25 -0
  129. ksadk/events/parser.py +191 -0
  130. ksadk/events/replay.py +37 -0
  131. ksadk/events/runtime_event.py +264 -0
  132. ksadk/events/store.py +317 -0
  133. ksadk/harness/__init__.py +34 -0
  134. ksadk/harness/app.py +278 -0
  135. ksadk/harness/config.py +181 -0
  136. ksadk/harness/reasoner.py +105 -0
  137. ksadk/harness/runner.py +211 -0
  138. ksadk/harness/sandbox.py +141 -0
  139. ksadk/harness/tools.py +222 -0
  140. ksadk/hermes_terminal.py +23 -12
  141. ksadk/identity/resolver.py +15 -9
  142. ksadk/ids.py +32 -0
  143. ksadk/knowledge_base/adk_tool.py +1 -1
  144. ksadk/knowledge_base/client.py +23 -29
  145. ksadk/managed_runtime.py +188 -0
  146. ksadk/markdown.py +5 -2
  147. ksadk/mcp_runtime/__init__.py +9 -6
  148. ksadk/memory/__init__.py +1 -1
  149. ksadk/memory/adk/__init__.py +2 -0
  150. ksadk/memory/adk/backends/base_ltm_backend.py +2 -6
  151. ksadk/memory/adk/backends/http_ltm_backend.py +13 -21
  152. ksadk/memory/adk/backends/inmemory_ltm_backend.py +7 -13
  153. ksadk/memory/adk/backends/sdk_ltm_backend.py +26 -33
  154. ksadk/memory/adk/long_term_memory.py +31 -35
  155. ksadk/memory/adk/resilient_session_service.py +4 -5
  156. ksadk/memory/adk/short_term_memory.py +12 -21
  157. ksadk/memory/adk_tool.py +3 -2
  158. ksadk/memory/backends/base.py +19 -19
  159. ksadk/memory/backends/memory.py +24 -25
  160. ksadk/memory/backends/redis.py +31 -30
  161. ksadk/memory/langchain_tool.py +2 -1
  162. ksadk/memory/manager.py +52 -47
  163. ksadk/memory/service.py +10 -8
  164. ksadk/model_policy.py +15 -7
  165. ksadk/model_proxy/__init__.py +51 -0
  166. ksadk/model_proxy/bootstrap.py +80 -0
  167. ksadk/model_proxy/cache.py +165 -0
  168. ksadk/model_proxy/config.py +45 -0
  169. ksadk/model_proxy/demo_webui.py +115 -0
  170. ksadk/model_proxy/detect.py +145 -0
  171. ksadk/model_proxy/gate.py +68 -0
  172. ksadk/model_proxy/namespace.py +173 -0
  173. ksadk/model_proxy/protocol_proxy.py +22 -0
  174. ksadk/model_proxy/server.py +300 -0
  175. ksadk/model_proxy/transform.py +691 -0
  176. ksadk/openclaw_gateway.py +29 -14
  177. ksadk/runners/adk_runner.py +345 -120
  178. ksadk/runners/base_runner.py +46 -29
  179. ksadk/runners/codex_runner.py +132 -0
  180. ksadk/runners/deepagents_runner.py +0 -1
  181. ksadk/runners/factory.py +17 -5
  182. ksadk/runners/langchain_runner.py +29 -742
  183. ksadk/runners/langgraph_runner.py +352 -90
  184. ksadk/runners/patch_langchain.py +16 -16
  185. ksadk/runners/remote_runner.py +134 -33
  186. ksadk/runners/usage_accumulator.py +1 -0
  187. ksadk/runners/utils/langfuse.py +3 -3
  188. ksadk/runners/utils/loader.py +7 -7
  189. ksadk/runtime/__init__.py +41 -0
  190. ksadk/runtime/adapter.py +345 -0
  191. ksadk/runtime/framework_adapters.py +124 -0
  192. ksadk/runtime/preprocessing.py +118 -0
  193. ksadk/runtime/runner_adapter.py +873 -0
  194. ksadk/runtime_context.py +18 -3
  195. ksadk/runtime_state.py +0 -1
  196. ksadk/sandbox/__init__.py +4 -1
  197. ksadk/sandbox/backends/__init__.py +4 -1
  198. ksadk/sandbox/backends/e2b.py +13 -5
  199. ksadk/sandbox/backends/local_process.py +11 -3
  200. ksadk/sandbox/base.py +10 -15
  201. ksadk/sandbox/factory.py +14 -4
  202. ksadk/sandbox/registry.py +57 -6
  203. ksadk/server/api_models.py +15 -5
  204. ksadk/server/app.py +455 -3885
  205. ksadk/server/composition.py +21 -0
  206. ksadk/server/factory.py +624 -0
  207. ksadk/server/routes/__init__.py +1 -0
  208. ksadk/server/routes/checkpoint_resolution.py +116 -0
  209. ksadk/server/routes/common.py +713 -0
  210. ksadk/server/routes/control.py +327 -0
  211. ksadk/server/routes/dependencies.py +98 -0
  212. ksadk/server/routes/misc.py +448 -0
  213. ksadk/server/routes/models.py +443 -0
  214. ksadk/server/routes/openai_compat.py +207 -0
  215. ksadk/server/routes/projection.py +795 -0
  216. ksadk/server/routes/routers.py +52 -0
  217. ksadk/server/routes/run.py +726 -0
  218. ksadk/server/routes/sessions.py +649 -0
  219. ksadk/server/routes/streaming.py +223 -0
  220. ksadk/server/routes/workspace.py +381 -0
  221. ksadk/server/static/assets/{ArtifactsPanel-D-SSn2ee.js → ArtifactsPanel-Cjnqt-8W.js} +1 -1
  222. ksadk/server/static/assets/CodeBlock-BqyqZfuB.js +9 -0
  223. ksadk/server/static/assets/MathMessageMarkdown-BpH7jiFM.js +8 -0
  224. ksadk/server/static/assets/MermaidBlock-DWuxETaJ.js +303 -0
  225. ksadk/server/static/assets/NativeTerminalPanel-MDTopWMO.js +1 -0
  226. ksadk/server/static/assets/{arc-BA13sHc9.js → arc-CFKwSk_B.js} +1 -1
  227. ksadk/server/static/assets/architecture-7EHR7CIX-CynqOWjZ.js +1 -0
  228. ksadk/server/static/assets/{architectureDiagram-3BPJPVTR-DzJzgVoL.js → architectureDiagram-3BPJPVTR-Dej8jX8c.js} +1 -1
  229. ksadk/server/static/assets/{blockDiagram-GPEHLZMM-UTdVcZhn.js → blockDiagram-GPEHLZMM-1F64jqvy.js} +1 -1
  230. ksadk/server/static/assets/{c4Diagram-AAUBKEIU-Dix0GD4m.js → c4Diagram-AAUBKEIU-9q7AUAKb.js} +1 -1
  231. ksadk/server/static/assets/channel-iUGv2N24.js +1 -0
  232. ksadk/server/static/assets/{chunk-2J33WTMH-C2JxuZ42.js → chunk-2J33WTMH-6qgwJm7Q.js} +1 -1
  233. ksadk/server/static/assets/{chunk-4BX2VUAB-Be57QPgD.js → chunk-4BX2VUAB-CsWIQRhc.js} +1 -1
  234. ksadk/server/static/assets/{chunk-55IACEB6-WjSuy4nv.js → chunk-55IACEB6-BK8paA20.js} +1 -1
  235. ksadk/server/static/assets/{chunk-727SXJPM-BOovP-b5.js → chunk-727SXJPM-B-JEhy7h.js} +1 -1
  236. ksadk/server/static/assets/{chunk-AQP2D5EJ-DOmZNmWj.js → chunk-AQP2D5EJ-DYBvMHAa.js} +1 -1
  237. ksadk/server/static/assets/{chunk-FMBD7UC4-DzwlodkU.js → chunk-FMBD7UC4-BGhvzDYt.js} +1 -1
  238. ksadk/server/static/assets/{chunk-ND2GUHAM-DMqAikEU.js → chunk-ND2GUHAM-D98vPd-E.js} +1 -1
  239. ksadk/server/static/assets/chunk-QZHKN3VN-YQIEXeF6.js +1 -0
  240. ksadk/server/static/assets/classDiagram-4FO5ZUOK-CDAUuoXK.js +1 -0
  241. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-CDAUuoXK.js +1 -0
  242. ksadk/server/static/assets/{cose-bilkent-S5V4N54A-Zhy2GBnP.js → cose-bilkent-S5V4N54A-CX3yotHq.js} +1 -1
  243. ksadk/server/static/assets/{dagre-BM42HDAG-CbKrtIUz.js → dagre-BM42HDAG-C7TguAWo.js} +1 -1
  244. ksadk/server/static/assets/{diagram-2AECGRRQ-BRApgQRK.js → diagram-2AECGRRQ-Dd17_kws.js} +1 -1
  245. ksadk/server/static/assets/{diagram-5GNKFQAL-D0xbnDiK.js → diagram-5GNKFQAL-DVIClIil.js} +1 -1
  246. ksadk/server/static/assets/{diagram-KO2AKTUF-CVugVI9L.js → diagram-KO2AKTUF-Df_3gYQa.js} +1 -1
  247. ksadk/server/static/assets/{diagram-LMA3HP47-DozUGgee.js → diagram-LMA3HP47-BhLk6ZtA.js} +1 -1
  248. ksadk/server/static/assets/{diagram-OG6HWLK6-ZOYsKlru.js → diagram-OG6HWLK6-D4GNGvuu.js} +1 -1
  249. ksadk/server/static/assets/{dist-DmFaq_Iy.js → dist-6CVKuTAe.js} +1 -1
  250. ksadk/server/static/assets/{dist-8EEyGgDx.js → dist-BUHRxvDG.js} +1 -1
  251. ksadk/server/static/assets/{dist-B0NP_Z0z.js → dist-BZ9n9AjO.js} +1 -1
  252. ksadk/server/static/assets/{dist-DMezvBGJ.js → dist-BibcG1V9.js} +1 -1
  253. ksadk/server/static/assets/{dist-C3HIIaN5.js → dist-Bs-jQcRX.js} +1 -1
  254. ksadk/server/static/assets/{dist-BrCcQ47T.js → dist-C2xjE7cO.js} +1 -1
  255. ksadk/server/static/assets/{dist-Dp0k7bWI.js → dist-C7-3jInC.js} +1 -1
  256. ksadk/server/static/assets/{dist-D-xOD048.js → dist-C99THtIA.js} +1 -1
  257. ksadk/server/static/assets/{dist-CkEKJFtk.js → dist-CGcp2ex8.js} +1 -1
  258. ksadk/server/static/assets/{dist-DyOkZmze.js → dist-CVQRZjPu.js} +1 -1
  259. ksadk/server/static/assets/{dist-BssVmjIo.js → dist-Cf23NY4L.js} +1 -1
  260. ksadk/server/static/assets/{dist-BIJVDMuB.js → dist-CgF3fj15.js} +1 -1
  261. ksadk/server/static/assets/{dist-2hInLS_J.js → dist-Cwt7koer.js} +1 -1
  262. ksadk/server/static/assets/{dist-DyWsb78Q.js → dist-D82mtD2s.js} +1 -1
  263. ksadk/server/static/assets/{dist-D-JMdWPQ.js → dist-DF06ORQo.js} +1 -1
  264. ksadk/server/static/assets/{dist-BmlMPsCc.js → dist-DHn6nBzr.js} +1 -1
  265. ksadk/server/static/assets/{dist-gX6GOLKq.js → dist-Djgl2wVZ.js} +1 -1
  266. ksadk/server/static/assets/{dist-DpKtt4Bo.js → dist-DlzCRy0S.js} +1 -1
  267. ksadk/server/static/assets/{dist-8_SfK1IP.js → dist-DqNnNFrw.js} +1 -1
  268. ksadk/server/static/assets/{dist-D57wnGWd.js → dist-DyvYLMW9.js} +1 -1
  269. ksadk/server/static/assets/{dist-CPBtqRTW.js → dist-eoW6tZXA.js} +1 -1
  270. ksadk/server/static/assets/{dist-DL_NKc_z.js → dist-ivOya1LD.js} +1 -1
  271. ksadk/server/static/assets/{erDiagram-TEJ5UH35-Bk-9bgE5.js → erDiagram-TEJ5UH35-CnfRcY2M.js} +1 -1
  272. ksadk/server/static/assets/eventmodeling-FCH6USID-ChYO-ijM.js +1 -0
  273. ksadk/server/static/assets/{flowDiagram-I6XJVG4X-Dn9kZVuZ.js → flowDiagram-I6XJVG4X-DGaJjGNJ.js} +1 -1
  274. ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BGxXlJqm.js → ganttDiagram-6RSMTGT7-DpCoB2g9.js} +1 -1
  275. ksadk/server/static/assets/gitGraph-WXDBUCRP-BN_0Fk-j.js +1 -0
  276. ksadk/server/static/assets/{gitGraphDiagram-PVQCEYII-C7Q0X-wi.js → gitGraphDiagram-PVQCEYII-9IKVFBVT.js} +1 -1
  277. ksadk/server/static/assets/index-QSwCHuB8.js +177 -0
  278. ksadk/server/static/assets/index-jOPmTOZH.css +2 -0
  279. ksadk/server/static/assets/info-J43DQDTF-Cp6o3vId.js +1 -0
  280. ksadk/server/static/assets/{infoDiagram-5YYISTIA-BO_CSqq5.js → infoDiagram-5YYISTIA-CYIIsb8y.js} +1 -1
  281. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-eYLPLd46.js → ishikawaDiagram-YF4QCWOH-DLOHGvWw.js} +1 -1
  282. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-De9geSb0.js → journeyDiagram-JHISSGLW-BIzKeejT.js} +1 -1
  283. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-Cs80kQto.js → kanban-definition-UN3LZRKU-BWHoFSw2.js} +1 -1
  284. ksadk/server/static/assets/{katex-DSrCFD8n.js → katex-DATssjp1.js} +1 -1
  285. ksadk/server/static/assets/{linear-CN9sbKTF.js → linear-CvMBw9cA.js} +1 -1
  286. ksadk/server/static/assets/{mermaid-parser.core-DlgSBU8l.js → mermaid-parser.core-mGAcEwcq.js} +2 -2
  287. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-9afz7Oug.js → mindmap-definition-RKZ34NQL-CSAQxEAe.js} +1 -1
  288. ksadk/server/static/assets/packet-YPE3B663-wQzct3cT.js +1 -0
  289. ksadk/server/static/assets/pie-LRSECV5Y-BgTQiv-9.js +1 -0
  290. ksadk/server/static/assets/{pieDiagram-4H26LBE5-DPv_VstN.js → pieDiagram-4H26LBE5-By-ra07M.js} +1 -1
  291. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-o77wZ1XK.js → quadrantDiagram-W4KKPZXB-CxIR4JXI.js} +1 -1
  292. ksadk/server/static/assets/radar-GUYGQ44K-BIq5fXsn.js +1 -0
  293. ksadk/server/static/assets/{requirementDiagram-4Y6WPE33-BYVw81VC.js → requirementDiagram-4Y6WPE33-DpW90iu3.js} +1 -1
  294. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-B7nZFMKT.js → sankeyDiagram-5OEKKPKP-DpGa6EAV.js} +1 -1
  295. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-BGRRrFw5.js → sequenceDiagram-3UESZ5HK-NTKNGGe5.js} +1 -1
  296. ksadk/server/static/assets/{stateDiagram-AJRCARHV-BUfHCoPs.js → stateDiagram-AJRCARHV-DGpd9yFG.js} +1 -1
  297. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-B3o579IQ.js +1 -0
  298. ksadk/server/static/assets/{timeline-definition-PNZ67QCA-FbCTGrf9.js → timeline-definition-PNZ67QCA-jqoYs3jQ.js} +1 -1
  299. ksadk/server/static/assets/treeView-BLDUP644-Dqi-E0EM.js +1 -0
  300. ksadk/server/static/assets/treemap-LRROVOQU-ClQP9CJ9.js +1 -0
  301. ksadk/server/static/assets/{vennDiagram-CIIHVFJN-DWgwtPsJ.js → vennDiagram-CIIHVFJN-l3Z7-wbP.js} +1 -1
  302. ksadk/server/static/assets/wardley-L42UT6IY-8XLvnNIs.js +1 -0
  303. ksadk/server/static/assets/{wardleyDiagram-YWT4CUSO-2x8G-tTM.js → wardleyDiagram-YWT4CUSO-BR54GPmr.js} +1 -1
  304. ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-Drma3AYl.js → xychartDiagram-2RQKCTM6-SkiH8GsK.js} +1 -1
  305. ksadk/server/static/index.html +2 -2
  306. ksadk/server/terminal_sessions.py +111 -58
  307. ksadk/sessions/__init__.py +69 -19
  308. ksadk/sessions/base.py +31 -11
  309. ksadk/sessions/continuity.py +2 -3
  310. ksadk/sessions/in_memory.py +46 -2
  311. ksadk/sessions/local_service.py +148 -17
  312. ksadk/sessions/postgres_service.py +143 -25
  313. ksadk/sessions/resilient.py +36 -8
  314. ksadk/skills/models.py +17 -5
  315. ksadk/skills/package_store.py +5 -2
  316. ksadk/skills/runtime/agent.py +20 -5
  317. ksadk/skills/runtime/artifacts.py +1 -5
  318. ksadk/skills/runtime/backends/e2b.py +10 -4
  319. ksadk/skills/runtime/backends/local.py +15 -3
  320. ksadk/skills/runtime/base.py +1 -2
  321. ksadk/skills/runtime/executor.py +10 -3
  322. ksadk/skills/runtime/factory.py +4 -2
  323. ksadk/skills/runtime/loader.py +1 -2
  324. ksadk/skills/runtime/registry.py +15 -10
  325. ksadk/skills/runtime/request.py +3 -1
  326. ksadk/skills/service_client.py +38 -17
  327. ksadk/skills/tool_defs.py +15 -10
  328. ksadk/terminal_exec_policy.py +3 -4
  329. ksadk/tools/gateway.py +92 -15
  330. ksadk/tools/result_budget.py +12 -4
  331. ksadk/toolsets/__init__.py +134 -36
  332. ksadk/toolsets/platform.py +21 -5
  333. ksadk/toolsets/sandbox.py +82 -16
  334. ksadk/toolsets/skills.py +200 -39
  335. ksadk/toolsets/web.py +123 -26
  336. ksadk/toolsets/workspace.py +212 -101
  337. ksadk/tracing/exporters/inmemory_exporter.py +62 -51
  338. ksadk/tracing/exporters/langfuse_exporter.py +91 -76
  339. ksadk/tracing/setup.py +69 -53
  340. ksadk/tracing/span_utils.py +5 -2
  341. ksadk/tui/loop.py +78 -30
  342. ksadk/tui/stream_render.py +1 -0
  343. ksadk/ui_config.py +5 -3
  344. ksadk/version.py +1 -1
  345. ksadk_runtime_common/schemas/runtime_event_v1.json +95 -0
  346. agentengine_sdk_python-0.7.0.dist-info/RECORD +0 -482
  347. ksadk/server/static/assets/CodeBlock-P16VOLYi.js +0 -9
  348. ksadk/server/static/assets/MathMessageMarkdown-CdGiQWHR.js +0 -8
  349. ksadk/server/static/assets/MermaidBlock-IOcal8cm.js +0 -303
  350. ksadk/server/static/assets/NativeTerminalPanel-DGSDWsei.js +0 -1
  351. ksadk/server/static/assets/architecture-7EHR7CIX-BvUlsA6l.js +0 -1
  352. ksadk/server/static/assets/channel-CTuydARX.js +0 -1
  353. ksadk/server/static/assets/chunk-QZHKN3VN-BnCydivS.js +0 -1
  354. ksadk/server/static/assets/classDiagram-4FO5ZUOK-BNHg45Be.js +0 -1
  355. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-BNHg45Be.js +0 -1
  356. ksadk/server/static/assets/eventmodeling-FCH6USID-DsAgWnlf.js +0 -1
  357. ksadk/server/static/assets/gitGraph-WXDBUCRP-BIao9Yjg.js +0 -1
  358. ksadk/server/static/assets/index-C00Lwg3L.js +0 -165
  359. ksadk/server/static/assets/index-DAURbCsP.css +0 -1
  360. ksadk/server/static/assets/info-J43DQDTF-CyipqGxU.js +0 -1
  361. ksadk/server/static/assets/packet-YPE3B663-CGkyRxlg.js +0 -1
  362. ksadk/server/static/assets/pie-LRSECV5Y-M0uWAf4n.js +0 -1
  363. ksadk/server/static/assets/radar-GUYGQ44K-Bdbm24-x.js +0 -1
  364. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-8xBN_gLM.js +0 -1
  365. ksadk/server/static/assets/treeView-BLDUP644-npMo0ak1.js +0 -1
  366. ksadk/server/static/assets/treemap-LRROVOQU-CHdzrrdo.js +0 -1
  367. ksadk/server/static/assets/wardley-L42UT6IY-BEQz7wyy.js +0 -1
  368. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/WHEEL +0 -0
  369. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/entry_points.txt +0 -0
  370. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/licenses/LICENSE +0 -0
  371. {agentengine_sdk_python-0.7.0.dist-info → agentengine_sdk_python-0.8.0.dist-info}/top_level.txt +0 -0
ksadk/tracing/setup.py CHANGED
@@ -5,12 +5,17 @@ Tracing 初始化 - 支持多 Exporter (InMemory + Langfuse + OTLP)
5
5
 
6
6
  import atexit
7
7
  import base64
8
+ import importlib
8
9
  import logging
9
10
  import os
11
+ from collections.abc import Callable, Sequence
10
12
  from dataclasses import dataclass
11
13
  from typing import Any, Optional
12
14
  from urllib.parse import unquote
13
15
 
16
+ from opentelemetry.sdk.trace import ReadableSpan
17
+ from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
18
+
14
19
  from ksadk.tracing.exporters.inmemory_exporter import InMemoryExporter
15
20
 
16
21
  logger = logging.getLogger(__name__)
@@ -49,18 +54,18 @@ class _OtlpHttpConfig:
49
54
  service_name: str
50
55
 
51
56
 
52
- class _LoggingSpanExporter:
57
+ class _LoggingSpanExporter(SpanExporter):
53
58
  """Small delegating exporter that logs external trace export flow."""
54
59
 
55
60
  def __init__(
56
61
  self,
57
- exporter: Any,
62
+ exporter: SpanExporter,
58
63
  *,
59
64
  name: str,
60
65
  endpoint: str,
61
66
  service_name: str,
62
67
  header_keys: list[str],
63
- span_transform: Any = None,
68
+ span_transform: Callable[[Sequence[ReadableSpan]], Sequence[ReadableSpan]] | None = None,
64
69
  ):
65
70
  self._exporter = exporter
66
71
  self._name = name
@@ -69,8 +74,8 @@ class _LoggingSpanExporter:
69
74
  self._header_keys = header_keys
70
75
  self._span_transform = span_transform
71
76
 
72
- def export(self, spans):
73
- span_count = len(spans) if hasattr(spans, "__len__") else "unknown"
77
+ def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
78
+ span_count = len(spans)
74
79
  logger.info(
75
80
  "%s export started: endpoint=%s service_name=%s spans=%s headers=%s",
76
81
  self._name,
@@ -101,24 +106,18 @@ class _LoggingSpanExporter:
101
106
  )
102
107
  return result
103
108
 
104
- def shutdown(self):
105
- shutdown = getattr(self._exporter, "shutdown", None)
106
- if shutdown is None:
107
- return None
109
+ def shutdown(self) -> None:
108
110
  logger.info("%s exporter shutdown: endpoint=%s", self._name, self._endpoint)
109
- return shutdown()
111
+ self._exporter.shutdown()
110
112
 
111
113
  def force_flush(self, timeout_millis: int = 30000) -> bool:
112
- force_flush = getattr(self._exporter, "force_flush", None)
113
- if force_flush is None:
114
- return True
115
114
  logger.info(
116
115
  "%s exporter force_flush: endpoint=%s timeout_millis=%s",
117
116
  self._name,
118
117
  self._endpoint,
119
118
  timeout_millis,
120
119
  )
121
- return force_flush(timeout_millis)
120
+ return self._exporter.force_flush(timeout_millis)
122
121
 
123
122
 
124
123
  def _coerce_int(value: Any) -> int:
@@ -164,9 +163,7 @@ def _span_token_usage(span: Any) -> dict[str, int]:
164
163
  "input": _coerce_int(attributes.get("gen_ai.usage.input_tokens")),
165
164
  "output": _coerce_int(attributes.get("gen_ai.usage.output_tokens")),
166
165
  "cache_read": _coerce_int(attributes.get("gen_ai.usage.cache_read.input_tokens")),
167
- "reasoning_output": _coerce_int(
168
- attributes.get("gen_ai.usage.reasoning.output_tokens")
169
- ),
166
+ "reasoning_output": _coerce_int(attributes.get("gen_ai.usage.reasoning.output_tokens")),
170
167
  }
171
168
 
172
169
 
@@ -294,13 +291,16 @@ def _prepare_cloud_monitor_spans(spans: Any) -> Any:
294
291
 
295
292
  def has_token_descendant(span: Any) -> bool:
296
293
  sid = _span_id(span)
297
- key = (_trace_id(span), sid)
298
294
  if sid is None:
299
295
  return False
296
+ key = (_trace_id(span), sid)
300
297
  if key in has_token_descendant_cache:
301
298
  return has_token_descendant_cache[key]
302
299
  for child in children_by_parent.get(key, []):
303
- child_key = (_trace_id(child), _span_id(child))
300
+ child_sid = _span_id(child)
301
+ if child_sid is None:
302
+ continue
303
+ child_key = (_trace_id(child), child_sid)
304
304
  if _has_nonzero_token_usage(token_usage_by_id.get(child_key, {})):
305
305
  has_token_descendant_cache[key] = True
306
306
  return True
@@ -312,6 +312,8 @@ def _prepare_cloud_monitor_spans(spans: Any) -> Any:
312
312
 
313
313
  def collect_leaf_usage(span: Any, usage: dict[str, int]) -> None:
314
314
  sid = _span_id(span)
315
+ if sid is None:
316
+ return
315
317
  key = (_trace_id(span), sid)
316
318
  own_usage = token_usage_by_id.get(key, {})
317
319
  if _has_nonzero_token_usage(own_usage) and not has_token_descendant(span):
@@ -349,7 +351,9 @@ def _prepare_cloud_monitor_spans(spans: Any) -> Any:
349
351
  return [replacements.get(id(span), span) for span in span_list]
350
352
 
351
353
 
352
- def _build_langfuse_otlp_config(langfuse_config: dict = None) -> Optional[dict]:
354
+ def _build_langfuse_otlp_config(
355
+ langfuse_config: dict[str, Any] | None = None,
356
+ ) -> dict[str, Any] | None:
353
357
  """Build Langfuse OTLP direct exporter config from explicit config or env."""
354
358
  if langfuse_config:
355
359
  public_key = langfuse_config.get("public_key") or ""
@@ -358,7 +362,9 @@ def _build_langfuse_otlp_config(langfuse_config: dict = None) -> Optional[dict]:
358
362
  else:
359
363
  public_key = os.getenv("LANGFUSE_PUBLIC_KEY", "")
360
364
  secret_key = os.getenv("LANGFUSE_SECRET_KEY", "")
361
- host = os.getenv("LANGFUSE_BASE_URL") or os.getenv("LANGFUSE_HOST") or "http://localhost:3000"
365
+ host = (
366
+ os.getenv("LANGFUSE_BASE_URL") or os.getenv("LANGFUSE_HOST") or "http://localhost:3000"
367
+ )
362
368
 
363
369
  if not public_key or not secret_key:
364
370
  return None
@@ -561,9 +567,7 @@ def _apply_langfuse_auth_fallback(endpoint: str, headers: dict[str, str]) -> boo
561
567
  langfuse_headers = _build_langfuse_otlp_headers(public_key, secret_key)
562
568
  headers["Authorization"] = langfuse_headers["Authorization"]
563
569
  if not _has_header(headers, "x-langfuse-ingestion-version"):
564
- headers["x-langfuse-ingestion-version"] = langfuse_headers[
565
- "x-langfuse-ingestion-version"
566
- ]
570
+ headers["x-langfuse-ingestion-version"] = langfuse_headers["x-langfuse-ingestion-version"]
567
571
  return True
568
572
 
569
573
 
@@ -595,9 +599,8 @@ def _build_generic_otlp_http_config() -> Optional[dict]:
595
599
  )
596
600
  return None
597
601
 
598
- raw_headers = (
599
- os.getenv("OTEL_EXPORTER_OTLP_TRACES_HEADERS", "").strip()
600
- or os.getenv("OTEL_EXPORTER_OTLP_HEADERS", "")
602
+ raw_headers = os.getenv("OTEL_EXPORTER_OTLP_TRACES_HEADERS", "").strip() or os.getenv(
603
+ "OTEL_EXPORTER_OTLP_HEADERS", ""
601
604
  )
602
605
  headers = _parse_otlp_headers(raw_headers)
603
606
  langfuse_auth_fallback = _apply_langfuse_auth_fallback(endpoint, headers)
@@ -618,13 +621,13 @@ def _build_generic_otlp_http_config() -> Optional[dict]:
618
621
 
619
622
  def setup_tracing(
620
623
  enable_inmemory: bool = True,
621
- enable_langfuse: bool = None, # Auto-detect from env
622
- langfuse_config: dict = None,
624
+ enable_langfuse: bool | None = None, # Auto-detect from env
625
+ langfuse_config: dict[str, Any] | None = None,
623
626
  enable_otlp: bool = False,
624
627
  otlp_endpoint: str = "localhost:4317",
625
628
  enable_adk_instrumentation: bool = True, # Auto-instrument ADK
626
- use_callback_only: bool = None, # Explicit override
627
- **kwargs
629
+ use_callback_only: bool | None = None, # Explicit override
630
+ **kwargs,
628
631
  ) -> Optional[InMemoryExporter]:
629
632
  """初始化 Tracing (支持多 Exporter)
630
633
 
@@ -657,7 +660,7 @@ def setup_tracing(
657
660
 
658
661
  # 检查是否已有 TracerProvider (避免覆盖)
659
662
  existing_provider = trace.get_tracer_provider()
660
- if existing_provider and hasattr(existing_provider, 'add_span_processor'):
663
+ if existing_provider and hasattr(existing_provider, "add_span_processor"):
661
664
  # 使用现有 provider,直接添加 processor
662
665
  provider = existing_provider
663
666
  logger.debug("Using existing TracerProvider")
@@ -679,14 +682,16 @@ def setup_tracing(
679
682
  generic_otlp_config = _build_generic_otlp_http_config()
680
683
  if generic_otlp_config:
681
684
  try:
682
- from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
685
+ from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
686
+ OTLPSpanExporter as HttpOTLPSpanExporter,
687
+ )
683
688
 
684
- otlp_exporter = OTLPSpanExporter(
689
+ raw_otlp_exporter = HttpOTLPSpanExporter(
685
690
  endpoint=generic_otlp_config["endpoint"],
686
691
  headers=generic_otlp_config["headers"],
687
692
  )
688
693
  otlp_exporter = _LoggingSpanExporter(
689
- otlp_exporter,
694
+ raw_otlp_exporter,
690
695
  name="Generic OTLP",
691
696
  endpoint=generic_otlp_config["endpoint"],
692
697
  service_name=_get_service_name(),
@@ -720,14 +725,16 @@ def setup_tracing(
720
725
  cloud_monitor_config = _build_cloud_monitor_otlp_http_config()
721
726
  if cloud_monitor_config:
722
727
  try:
723
- from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
728
+ from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
729
+ OTLPSpanExporter as HttpOTLPSpanExporter,
730
+ )
724
731
 
725
- cloud_monitor_exporter = OTLPSpanExporter(
732
+ raw_cloud_monitor_exporter = HttpOTLPSpanExporter(
726
733
  endpoint=cloud_monitor_config.endpoint,
727
734
  headers=cloud_monitor_config.headers,
728
735
  )
729
736
  cloud_monitor_exporter = _LoggingSpanExporter(
730
- cloud_monitor_exporter,
737
+ raw_cloud_monitor_exporter,
731
738
  name="CloudMonitor OTLP",
732
739
  endpoint=cloud_monitor_config.endpoint,
733
740
  service_name=cloud_monitor_config.service_name,
@@ -757,9 +764,8 @@ def setup_tracing(
757
764
  langfuse_enabled = enable_langfuse
758
765
  if langfuse_enabled is None:
759
766
  # Auto-detect from environment variables
760
- langfuse_enabled = (
761
- not generic_otlp_config
762
- and bool(os.getenv("LANGFUSE_PUBLIC_KEY") or (langfuse_config or {}).get("public_key"))
767
+ langfuse_enabled = not generic_otlp_config and bool(
768
+ os.getenv("LANGFUSE_PUBLIC_KEY") or (langfuse_config or {}).get("public_key")
763
769
  )
764
770
 
765
771
  # 检查是否应该禁用 LangfuseExporter (当使用 LangChain/LangGraph 时)
@@ -769,16 +775,18 @@ def setup_tracing(
769
775
 
770
776
  if langfuse_enabled and not use_callback_only:
771
777
  try:
772
- from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
778
+ from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
779
+ OTLPSpanExporter as HttpOTLPSpanExporter,
780
+ )
773
781
 
774
782
  config = _build_langfuse_otlp_config(langfuse_config)
775
783
  if config:
776
- otlp_exporter = OTLPSpanExporter(
784
+ raw_langfuse_otlp_exporter = HttpOTLPSpanExporter(
777
785
  endpoint=config["endpoint"],
778
786
  headers=config["headers"],
779
787
  )
780
788
  otlp_exporter = _LoggingSpanExporter(
781
- otlp_exporter,
789
+ raw_langfuse_otlp_exporter,
782
790
  name="Langfuse OTLP",
783
791
  endpoint=config["endpoint"],
784
792
  service_name=_get_service_name(),
@@ -786,11 +794,11 @@ def setup_tracing(
786
794
  span_transform=_prepare_langfuse_spans,
787
795
  )
788
796
  provider.add_span_processor(
789
- BatchSpanProcessor(
790
- otlp_exporter,
791
- **_batch_span_processor_kwargs(),
797
+ BatchSpanProcessor(
798
+ otlp_exporter,
799
+ **_batch_span_processor_kwargs(),
800
+ )
792
801
  )
793
- )
794
802
  logger.info(
795
803
  "Langfuse OTLP exporter enabled: %s (%s) headers=%s",
796
804
  config["endpoint"],
@@ -810,11 +818,14 @@ def setup_tracing(
810
818
  # 5. OTLP Exporter (optional)
811
819
  if enable_otlp:
812
820
  try:
813
- from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
814
- otlp_exporter = OTLPSpanExporter(endpoint=otlp_endpoint)
821
+ from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
822
+ OTLPSpanExporter as GrpcOTLPSpanExporter,
823
+ )
824
+
825
+ grpc_otlp_exporter = GrpcOTLPSpanExporter(endpoint=otlp_endpoint)
815
826
  provider.add_span_processor(
816
827
  BatchSpanProcessor(
817
- otlp_exporter,
828
+ grpc_otlp_exporter,
818
829
  **_batch_span_processor_kwargs(),
819
830
  )
820
831
  )
@@ -825,7 +836,10 @@ def setup_tracing(
825
836
  # 6. ADK Auto-Instrumentation (for Google ADK projects)
826
837
  if enable_adk_instrumentation and not _adk_instrumented:
827
838
  try:
828
- from openinference.instrumentation.google_adk import GoogleADKInstrumentor
839
+ instrumentation_module = importlib.import_module(
840
+ "openinference.instrumentation.google_adk"
841
+ )
842
+ GoogleADKInstrumentor = getattr(instrumentation_module, "GoogleADKInstrumentor")
829
843
  GoogleADKInstrumentor().instrument()
830
844
  _adk_instrumented = True
831
845
  logger.info("Google ADK instrumentation enabled")
@@ -841,6 +855,7 @@ def setup_tracing(
841
855
  if enable_adk_instrumentation:
842
856
  try:
843
857
  from openinference.instrumentation.langchain import LangChainInstrumentor
858
+
844
859
  LangChainInstrumentor().instrument()
845
860
  logger.info("LangChain instrumentation enabled")
846
861
  except ImportError:
@@ -864,7 +879,7 @@ def shutdown_tracing():
864
879
 
865
880
  if _langfuse_exporter is not None:
866
881
  try:
867
- if hasattr(_langfuse_exporter, '_exporter'):
882
+ if hasattr(_langfuse_exporter, "_exporter"):
868
883
  _langfuse_exporter._exporter.shutdown()
869
884
  logger.debug("Langfuse exporter shutdown gracefully")
870
885
  except Exception:
@@ -887,6 +902,7 @@ def get_tracer(name: str = "ksadk"):
887
902
  """获取 Tracer"""
888
903
  try:
889
904
  from opentelemetry import trace
905
+
890
906
  return trace.get_tracer(name)
891
907
  except ImportError:
892
908
  return None
@@ -93,9 +93,12 @@ def get_langfuse_callback(
93
93
  Langfuse callback handler 或 None
94
94
  """
95
95
  try:
96
- from ksadk.tracing import get_langfuse_callback_handler
96
+ import ksadk.tracing as tracing_module
97
97
 
98
- handler = get_langfuse_callback_handler()
98
+ factory = getattr(tracing_module, "get_langfuse_callback_handler", None)
99
+ if not callable(factory):
100
+ return None
101
+ handler = factory()
99
102
  if handler:
100
103
  # 设置 session 和 metadata
101
104
  handler.session_id = session_id
ksadk/tui/loop.py CHANGED
@@ -3,6 +3,7 @@
3
3
  Transcript 使用 ANSI 保留 rich 颜色和 Markdown 落定格式,composer 紧跟内容,
4
4
  终端原生 scrollback 保留。支持键盘滚动、流式跟底和输入排队。
5
5
  """
6
+
6
7
  from __future__ import annotations
7
8
 
8
9
  import asyncio
@@ -105,7 +106,11 @@ def _codex_surface_rgb(
105
106
  # so 4% black on a light terminal is visually imperceptible; 8% gives the
106
107
  # input surface a stable edge without introducing a border.
107
108
  alpha = 0.08 if is_light and composer else 0.04 if is_light else 0.12
108
- return tuple(int(top[i] * alpha + background[i] * (1.0 - alpha)) for i in range(3))
109
+ return (
110
+ int(top[0] * alpha + background[0] * (1.0 - alpha)),
111
+ int(top[1] * alpha + background[1] * (1.0 - alpha)),
112
+ int(top[2] * alpha + background[2] * (1.0 - alpha)),
113
+ )
109
114
 
110
115
 
111
116
  def _codex_surface_style(background: tuple[int, int, int] | None) -> str:
@@ -319,12 +324,16 @@ class SlashCommandCompleter(Completer):
319
324
  return
320
325
  # /model <prefix> → 补全可选模型 id
321
326
  if prefix.startswith("/model ") and self.runner is not None:
322
- model_prefix = prefix[len("/model "):].strip()
327
+ model_prefix = prefix[len("/model ") :].strip()
323
328
  available = getattr(self.runner, "available_models", None) or []
324
329
  for m in available:
325
330
  mid = str(m.get("id") or m.get("name") or "")
326
331
  if mid and mid.startswith(model_prefix):
327
- yield Completion(mid, start_position=-len(model_prefix), display_meta=m.get("display_name") or "")
332
+ yield Completion(
333
+ mid,
334
+ start_position=-len(model_prefix),
335
+ display_meta=m.get("display_name") or "",
336
+ )
328
337
  return
329
338
  for command, meta in self._COMMANDS.items():
330
339
  if command.startswith(prefix):
@@ -372,7 +381,7 @@ class RichLiveRenderer:
372
381
  self.assistant_entry.content = full_text
373
382
  self.assistant_entry.status = "streaming"
374
383
  if full_text.startswith(self._last_full_text):
375
- delta = full_text[len(self._last_full_text):]
384
+ delta = full_text[len(self._last_full_text) :]
376
385
  else:
377
386
  # A final/snapshot event can replace the cumulative text. Preserve
378
387
  # ordering when possible; without tools it is safe to replace the
@@ -398,7 +407,9 @@ class RichLiveRenderer:
398
407
  self.assistant_entry.thinking = full_thinking
399
408
  # thinking 不进动态区,落定时随 assistant 一起渲染(show_thinking)
400
409
 
401
- async def on_tool_call(self, tool_name: str, status: str, args: Any = None, call_id: str = "") -> None:
410
+ async def on_tool_call(
411
+ self, tool_name: str, status: str, args: Any = None, call_id: str = ""
412
+ ) -> None:
402
413
  if self.loop is None:
403
414
  return
404
415
  content = f"{tool_name} [{status}]"
@@ -441,7 +452,7 @@ class RichLiveRenderer:
441
452
  def final_entries(self, response: str) -> list[TranscriptEntry]:
442
453
  """Return finalized display cells in the original stream event order."""
443
454
  if response and response.startswith(self._last_full_text):
444
- delta = response[len(self._last_full_text):]
455
+ delta = response[len(self._last_full_text) :]
445
456
  if delta:
446
457
  if self._active_text_entry is None:
447
458
  self._active_text_entry = TranscriptEntry(role="assistant")
@@ -507,7 +518,9 @@ class InteractionLoop:
507
518
  self._pin_to_bottom = True
508
519
  self._last_max_scroll = 0
509
520
  self._history_pager_active = False
510
- self._entry_ansi_cache: dict[int, str] = {} # 历史 entries ANSI 缓存(避免 streaming 全量重渲)
521
+ self._entry_ansi_cache: dict[int, str] = (
522
+ {}
523
+ ) # 历史 entries ANSI 缓存(避免 streaming 全量重渲)
511
524
  self._last_usage: dict[str, Any] | None = None
512
525
  self._showed_help = False # 首次运行显示帮助列表
513
526
  self._welcome_ansi: str | None = None
@@ -600,6 +613,7 @@ class InteractionLoop:
600
613
  multiline=True,
601
614
  history=InMemoryHistory(),
602
615
  )
616
+ input_buffer = self._input_buffer
603
617
 
604
618
  # Codex uses an inline, content-first viewport: short transcripts keep
605
619
  # the composer directly below the content and a filler absorbs the rest
@@ -771,13 +785,13 @@ class InteractionLoop:
771
785
 
772
786
  @bindings.add("enter")
773
787
  def _submit(event) -> None:
774
- text = self._input_buffer.text
775
- self._input_buffer.reset(append_to_history=bool(text.strip()))
788
+ text = input_buffer.text
789
+ input_buffer.reset(append_to_history=bool(text.strip()))
776
790
  self._submit_text(text)
777
791
 
778
792
  @bindings.add("escape", "enter")
779
793
  def _newline(event) -> None:
780
- self._input_buffer.insert_text("\n")
794
+ input_buffer.insert_text("\n")
781
795
 
782
796
  @bindings.add("c-c")
783
797
  def _interrupt_or_exit(event) -> None:
@@ -796,7 +810,7 @@ class InteractionLoop:
796
810
 
797
811
  @bindings.add("c-d")
798
812
  def _exit_on_empty(event) -> None:
799
- if not self._input_buffer.text:
813
+ if not input_buffer.text:
800
814
  event.app.exit()
801
815
 
802
816
  @bindings.add("pageup")
@@ -835,14 +849,18 @@ class InteractionLoop:
835
849
  @bindings.add("up", filter=self._picker_condition)
836
850
  def _picker_up(event) -> None:
837
851
  if self._model_picker_models:
838
- self._model_picker_index = (self._model_picker_index - 1) % len(self._model_picker_models)
852
+ self._model_picker_index = (self._model_picker_index - 1) % len(
853
+ self._model_picker_models
854
+ )
839
855
  self._scroll_picker_to_selected()
840
856
  event.app.invalidate()
841
857
 
842
858
  @bindings.add("down", filter=self._picker_condition)
843
859
  def _picker_down(event) -> None:
844
860
  if self._model_picker_models:
845
- self._model_picker_index = (self._model_picker_index + 1) % len(self._model_picker_models)
861
+ self._model_picker_index = (self._model_picker_index + 1) % len(
862
+ self._model_picker_models
863
+ )
846
864
  self._scroll_picker_to_selected()
847
865
  event.app.invalidate()
848
866
 
@@ -850,7 +868,11 @@ class InteractionLoop:
850
868
  def _picker_select(event) -> None:
851
869
  models = self._model_picker_models
852
870
  if models and 0 <= self._model_picker_index < len(models):
853
- mid = str(models[self._model_picker_index].get("id") or models[self._model_picker_index].get("name") or "")
871
+ mid = str(
872
+ models[self._model_picker_index].get("id")
873
+ or models[self._model_picker_index].get("name")
874
+ or ""
875
+ )
854
876
  if mid:
855
877
  self._apply_model_switch(mid)
856
878
  self._close_model_picker()
@@ -980,7 +1002,9 @@ class InteractionLoop:
980
1002
  for entry in renderer.final_entries(renderer._last_full_text):
981
1003
  await self._commit_entry(entry)
982
1004
  if is_resume:
983
- await self._commit_entry(TranscriptEntry(role="system", content="该 runtime 暂不支持审批续跑,已取消"))
1005
+ await self._commit_entry(
1006
+ TranscriptEntry(role="system", content="该 runtime 暂不支持审批续跑,已取消")
1007
+ )
984
1008
  self._clear_current_task()
985
1009
  self._drain_queue()
986
1010
  else:
@@ -992,7 +1016,9 @@ class InteractionLoop:
992
1016
  assistant_entry.status = ""
993
1017
  for entry in renderer.final_entries(renderer._last_full_text):
994
1018
  await self._commit_entry(entry)
995
- await self._commit_entry(TranscriptEntry(role="system", content="已取消(保留已产生内容)"))
1019
+ await self._commit_entry(
1020
+ TranscriptEntry(role="system", content="已取消(保留已产生内容)")
1021
+ )
996
1022
  self._clear_current_task()
997
1023
  self._drain_queue()
998
1024
  return
@@ -1018,7 +1044,9 @@ class InteractionLoop:
1018
1044
 
1019
1045
  async def _prompt():
1020
1046
  await self._commit_entry(
1021
- TranscriptEntry(role="system", content=f"确认执行 {tool_name}? 输入 y 确认,其他内容取消")
1047
+ TranscriptEntry(
1048
+ role="system", content=f"确认执行 {tool_name}? 输入 y 确认,其他内容取消"
1049
+ )
1022
1050
  )
1023
1051
 
1024
1052
  self._create_background_task(_prompt())
@@ -1032,7 +1060,9 @@ class InteractionLoop:
1032
1060
  if user_input.lower() in {"y", "yes"}:
1033
1061
 
1034
1062
  async def _resume():
1035
- await self._commit_entry(TranscriptEntry(role="system", content="已确认,尝试续跑..."))
1063
+ await self._commit_entry(
1064
+ TranscriptEntry(role="system", content="已确认,尝试续跑...")
1065
+ )
1036
1066
  self._start_turn(
1037
1067
  "",
1038
1068
  input_data={**input_data, "resume": True},
@@ -1049,7 +1079,11 @@ class InteractionLoop:
1049
1079
  self._create_background_task(_cancel())
1050
1080
 
1051
1081
  def _drain_queue(self) -> None:
1052
- if self._pending_interrupt is not None or self._has_active_turn() or not self._queued_inputs:
1082
+ if (
1083
+ self._pending_interrupt is not None
1084
+ or self._has_active_turn()
1085
+ or not self._queued_inputs
1086
+ ):
1053
1087
  return
1054
1088
  item = self._queued_inputs.pop(0)
1055
1089
  self._start_turn(item.text, user_entry=item.entry)
@@ -1167,7 +1201,7 @@ class InteractionLoop:
1167
1201
 
1168
1202
  def _handle_model_command(self, user_input: str) -> None:
1169
1203
  """/model:弹出交互式选择器(↑↓ Enter Esc);/model <name>:直接切换(runnable 级,下轮生效)。"""
1170
- arg = user_input[len("/model"):].strip()
1204
+ arg = user_input[len("/model") :].strip()
1171
1205
  if arg:
1172
1206
  # 直接切换:改 runner.model,下轮请求带新 model
1173
1207
  self._apply_model_switch(arg)
@@ -1192,7 +1226,9 @@ class InteractionLoop:
1192
1226
  """
1193
1227
  self.runner.model = model_id
1194
1228
  available = getattr(self.runner, "available_models", None) or []
1195
- matched = next((m for m in available if str(m.get("id") or m.get("name") or "") == model_id), None)
1229
+ matched = next(
1230
+ (m for m in available if str(m.get("id") or m.get("name") or "") == model_id), None
1231
+ )
1196
1232
  if matched:
1197
1233
  self.runner.model_metadata = matched
1198
1234
  # 清旧观察名,让 _current_model_name 用新 runner.model
@@ -1263,8 +1299,8 @@ class InteractionLoop:
1263
1299
  frags: list[tuple[str, str]] = []
1264
1300
  for i, m in enumerate(self._model_picker_models):
1265
1301
  mid = str(m.get("id") or m.get("name") or "")
1266
- is_cur_model = (mid == current)
1267
- is_selected = (i == self._model_picker_index)
1302
+ is_cur_model = mid == current
1303
+ is_selected = i == self._model_picker_index
1268
1304
  marker = "›" if is_selected else " "
1269
1305
  line = f"{marker} {i + 1}. {mid}"
1270
1306
  if is_cur_model:
@@ -1276,7 +1312,7 @@ class InteractionLoop:
1276
1312
  # ---- streaming / transcript 渲染 ----
1277
1313
 
1278
1314
  def _set_streaming(self, full_text: str) -> None:
1279
- """streaming 文本更新到当前 streaming entry(动态区 = transcript 末尾的 streaming entry)。"""
1315
+ """更新 transcript 末尾的 streaming entry。"""
1280
1316
  if self._streaming_entry is not None:
1281
1317
  self._streaming_entry.content = full_text
1282
1318
  self._streaming_entry.status = "streaming"
@@ -1365,6 +1401,7 @@ class InteractionLoop:
1365
1401
  height = int(getattr(ri, "window_height", 0) or 0)
1366
1402
  if height <= 0:
1367
1403
  import shutil
1404
+
1368
1405
  height = max(10, (shutil.get_terminal_size(fallback=(80, 24)).lines or 24) - 6)
1369
1406
  line_count = self._transcript_line_count()
1370
1407
  return max(0, line_count - height)
@@ -1522,18 +1559,22 @@ def _resolve_model_name(runner) -> str:
1522
1559
  return str(meta["id"])
1523
1560
  observed = getattr(runner, "_observed_model", None)
1524
1561
  if observed:
1525
- return observed
1526
- return getattr(runner, "model", None) or os.getenv("MODEL_NAME") or "unknown"
1562
+ return str(observed)
1563
+ return str(getattr(runner, "model", None) or os.getenv("MODEL_NAME") or "unknown")
1527
1564
 
1528
1565
 
1529
1566
  def _help_text() -> str:
1530
- return "命令: /new 新会话 · /clear 清屏 · /session 查看 · /tools 展开工具 · exit 退出 · Ctrl-C 中断/退出"
1567
+ return (
1568
+ "命令: /new 新会话 · /clear 清屏 · /session 查看 · /tools 展开工具 "
1569
+ "· exit 退出 · Ctrl-C 中断/退出"
1570
+ )
1531
1571
 
1532
1572
 
1533
1573
  def _welcome_block(session_id: str, model_name: str, project_dir: Path, *, show_help: bool) -> str:
1534
1574
  """Render the compact Codex-style session card and one-line tip."""
1535
1575
  try:
1536
1576
  from ksadk.version import VERSION
1577
+
1537
1578
  version = VERSION
1538
1579
  except Exception:
1539
1580
  version = ""
@@ -1552,6 +1593,7 @@ def _welcome_block(session_id: str, model_name: str, project_dir: Path, *, show_
1552
1593
  # 按终端宽度算框宽,留 transcript 左缩进 2 + 右 margin 2,上限 56(Codex 同款)。
1553
1594
  try:
1554
1595
  import shutil
1596
+
1555
1597
  cols = shutil.get_terminal_size(fallback=(80, 24)).columns
1556
1598
  except Exception:
1557
1599
  cols = 80
@@ -1698,7 +1740,9 @@ def _render_entry_ansi(
1698
1740
  else:
1699
1741
  console.print(Markdown(_normalize_markdown(f"{body}{suffix}")))
1700
1742
  else:
1701
- console.print(Text(suffix.lstrip(), style="dim") if suffix else Text("...", style="dim"))
1743
+ console.print(
1744
+ Text(suffix.lstrip(), style="dim") if suffix else Text("...", style="dim")
1745
+ )
1702
1746
  return _strip_ansi_backgrounds(console.export_text(styles=True)).rstrip() + "\n"
1703
1747
 
1704
1748
  if entry.role == "tool":
@@ -1718,6 +1762,7 @@ def _render_entry_ansi(
1718
1762
  display_output = output
1719
1763
  try:
1720
1764
  import json as _json
1765
+
1721
1766
  parsed = _json.loads(output)
1722
1767
  # Tool adapters often JSON-encode an already serialized result.
1723
1768
  # Unwrap that second layer so structured output folds by fields
@@ -1729,7 +1774,7 @@ def _render_entry_ansi(
1729
1774
  if nested.startswith("content="):
1730
1775
  import ast as _ast
1731
1776
 
1732
- quote = nested[len("content="):len("content=") + 1]
1777
+ quote = nested[len("content=") : len("content=") + 1]
1733
1778
  if quote in {"'", '"'}:
1734
1779
  body_start = len("content=") + 1
1735
1780
  body_end = body_start
@@ -1756,6 +1801,7 @@ def _render_entry_ansi(
1756
1801
  pass
1757
1802
  # 估算显示行数:每行按 console width 折算(长字符串占多行)
1758
1803
  import shutil as _su
1804
+
1759
1805
  cw = max(40, (_su.get_terminal_size(fallback=(80, 24)).columns or 80) - 6)
1760
1806
  logical_lines = display_output.split("\n")
1761
1807
  # 估算显示行数:按显示宽度(CJK 占 2)折算,非 len
@@ -1864,7 +1910,9 @@ def _strip_ansi_backgrounds(text: str) -> str:
1864
1910
  return re.sub(r"\x1b\[([0-9;]*)m", _clean, text)
1865
1911
 
1866
1912
 
1867
- def run_tui(runner, *, show_thinking: bool = False, project_dir: str = ".", no_alt_screen: bool = False) -> None:
1913
+ def run_tui(
1914
+ runner, *, show_thinking: bool = False, project_dir: str = ".", no_alt_screen: bool = False
1915
+ ) -> None:
1868
1916
  """TUI 入口:被 cmd_invoke._invoke_tui / cmd_run._run_custom 调用。
1869
1917
 
1870
1918
  ``no_alt_screen`` 作为兼容参数保留;TUI 现在默认使用 Codex-style inline viewport。
@@ -3,6 +3,7 @@
3
3
  从 RemoteRunner.stream 的归一化 chunk 提取 delta/usage/终止信号,格式化 usage 文本。
4
4
  被 loop.py(交互 TUI)和 cmd_invoke._invoke_once(-m 单次)共用,保证两路径渲染口径一致。
5
5
  """
6
+
6
7
  from __future__ import annotations
7
8
 
8
9
  import re