agentengine-sdk-python 0.6.2__py3-none-any.whl → 0.6.9__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 (490) hide show
  1. agentengine_sdk_python-0.6.9.dist-info/METADATA +179 -0
  2. agentengine_sdk_python-0.6.9.dist-info/RECORD +487 -0
  3. {agentengine_sdk_python-0.6.2.dist-info → agentengine_sdk_python-0.6.9.dist-info}/WHEEL +1 -1
  4. agentengine_sdk_python-0.6.9.dist-info/entry_points.txt +3 -0
  5. agentengine_sdk_python-0.6.9.dist-info/licenses/LICENSE +175 -0
  6. agentengine_sdk_python-0.6.9.dist-info/top_level.txt +2 -0
  7. ksadk/__init__.py +13 -0
  8. ksadk/__main__.py +4 -0
  9. ksadk/a2a/__init__.py +15 -0
  10. ksadk/a2a/card_builder.py +64 -0
  11. ksadk/a2a/client.py +309 -0
  12. ksadk/a2a/executor.py +201 -0
  13. ksadk/a2a/server.py +79 -0
  14. ksadk/agents/__init__.py +20 -0
  15. ksadk/agents/base.py +370 -0
  16. ksadk/agents/context.py +38 -0
  17. ksadk/agents/event.py +35 -0
  18. ksadk/agents/loop_agent.py +71 -0
  19. ksadk/agents/parallel_agent.py +109 -0
  20. ksadk/agents/runner_adapter.py +24 -0
  21. ksadk/agents/sequential_agent.py +34 -0
  22. ksadk/api/__init__.py +3 -0
  23. ksadk/api/client.py +2227 -0
  24. ksadk/builders/__init__.py +20 -0
  25. ksadk/builders/base.py +66 -0
  26. ksadk/builders/code_builder.py +1990 -0
  27. ksadk/builders/container_builder.py +786 -0
  28. ksadk/builders/framework_requirements.py +59 -0
  29. ksadk/builders/ks3_uploader.py +389 -0
  30. ksadk/builders/mcp_builder.py +476 -0
  31. ksadk/builders/requirements_utils.py +75 -0
  32. ksadk/cli/__init__.py +531 -0
  33. ksadk/cli/__main__.py +4 -0
  34. ksadk/cli/agent_ref.py +204 -0
  35. ksadk/cli/cmd_a2a.py +167 -0
  36. ksadk/cli/cmd_agent.py +201 -0
  37. ksadk/cli/cmd_build.py +270 -0
  38. ksadk/cli/cmd_completion.py +304 -0
  39. ksadk/cli/cmd_config.py +758 -0
  40. ksadk/cli/cmd_create.py +2277 -0
  41. ksadk/cli/cmd_dashboard.py +900 -0
  42. ksadk/cli/cmd_deploy.py +704 -0
  43. ksadk/cli/cmd_destroy.py +345 -0
  44. ksadk/cli/cmd_files.py +1227 -0
  45. ksadk/cli/cmd_hermes.py +1259 -0
  46. ksadk/cli/cmd_invoke.py +1336 -0
  47. ksadk/cli/cmd_launch.py +564 -0
  48. ksadk/cli/cmd_mcp.py +1174 -0
  49. ksadk/cli/cmd_model.py +302 -0
  50. ksadk/cli/cmd_openclaw.py +3918 -0
  51. ksadk/cli/cmd_run.py +232 -0
  52. ksadk/cli/cmd_status.py +588 -0
  53. ksadk/cli/cmd_version.py +522 -0
  54. ksadk/cli/cmd_web.py +288 -0
  55. ksadk/cli/deploy_utils.py +87 -0
  56. ksadk/cli/dry_run.py +214 -0
  57. ksadk/cli/env_options.py +82 -0
  58. ksadk/cli/error_utils.py +525 -0
  59. ksadk/cli/global_options.py +31 -0
  60. ksadk/cli/local_runtime.py +97 -0
  61. ksadk/cli/model_catalog.py +136 -0
  62. ksadk/cli/network_options.py +410 -0
  63. ksadk/cli/resource_common.py +621 -0
  64. ksadk/cli/storage.py +84 -0
  65. ksadk/cli/ui.py +431 -0
  66. ksadk/cli/workflow_common.py +552 -0
  67. ksadk/common/__init__.py +14 -0
  68. ksadk/common/aicp_env.py +128 -0
  69. ksadk/common/auth.py +190 -0
  70. ksadk/common/constants.py +84 -0
  71. ksadk/common/llm_utils.py +61 -0
  72. ksadk/configs/__init__.py +61 -0
  73. ksadk/configs/env_registry.py +342 -0
  74. ksadk/configs/global_config.py +200 -0
  75. ksadk/configs/settings.py +706 -0
  76. ksadk/conversations/__init__.py +95 -0
  77. ksadk/conversations/attachment_storage.py +379 -0
  78. ksadk/conversations/attachments.py +774 -0
  79. ksadk/conversations/compaction_pipeline.py +387 -0
  80. ksadk/conversations/compaction_prompt.py +128 -0
  81. ksadk/conversations/context.py +303 -0
  82. ksadk/conversations/model_context.py +360 -0
  83. ksadk/conversations/model_options.py +136 -0
  84. ksadk/conversations/normalize.py +446 -0
  85. ksadk/conversations/reasoning_markup.py +91 -0
  86. ksadk/conversations/run_kinds.py +61 -0
  87. ksadk/conversations/run_status.py +53 -0
  88. ksadk/conversations/runtime.py +5208 -0
  89. ksadk/conversations/semantic_summary.py +359 -0
  90. ksadk/conversations/session_title.py +254 -0
  91. ksadk/deployment/__init__.py +83 -0
  92. ksadk/deployment/agent_access.py +226 -0
  93. ksadk/deployment/base.py +229 -0
  94. ksadk/deployment/manager.py +332 -0
  95. ksadk/deployment/providers/__init__.py +10 -0
  96. ksadk/deployment/providers/serverless.py +1105 -0
  97. ksadk/deployment/registry.py +120 -0
  98. ksadk/deployment/state.py +78 -0
  99. ksadk/deployment/ui_config.py +21 -0
  100. ksadk/detection/__init__.py +7 -0
  101. ksadk/detection/detector.py +431 -0
  102. ksadk/detection/mcp_detector.py +250 -0
  103. ksadk/hermes_terminal.py +467 -0
  104. ksadk/identity/__init__.py +17 -0
  105. ksadk/identity/resolver.py +385 -0
  106. ksadk/knowledge_base/__init__.py +62 -0
  107. ksadk/knowledge_base/adk_tool.py +47 -0
  108. ksadk/knowledge_base/client.py +297 -0
  109. ksadk/knowledge_base/langchain_tool.py +55 -0
  110. ksadk/knowledge_base/service.py +66 -0
  111. ksadk/knowledge_base/tool.py +50 -0
  112. ksadk/markdown.py +169 -0
  113. ksadk/mcp_runtime/__init__.py +231 -0
  114. ksadk/memory/__init__.py +57 -0
  115. ksadk/memory/adk/__init__.py +51 -0
  116. ksadk/memory/adk/backends/__init__.py +5 -0
  117. ksadk/memory/adk/backends/base_ltm_backend.py +52 -0
  118. ksadk/memory/adk/backends/http_ltm_backend.py +183 -0
  119. ksadk/memory/adk/backends/inmemory_ltm_backend.py +96 -0
  120. ksadk/memory/adk/backends/sdk_ltm_backend.py +456 -0
  121. ksadk/memory/adk/long_term_memory.py +273 -0
  122. ksadk/memory/adk/short_term_memory.py +289 -0
  123. ksadk/memory/adk_tool.py +31 -0
  124. ksadk/memory/backends/__init__.py +6 -0
  125. ksadk/memory/backends/base.py +103 -0
  126. ksadk/memory/backends/memory.py +112 -0
  127. ksadk/memory/backends/redis.py +146 -0
  128. ksadk/memory/langchain_tool.py +39 -0
  129. ksadk/memory/ltm_backend_factory.py +24 -0
  130. ksadk/memory/manager.py +215 -0
  131. ksadk/memory/service.py +172 -0
  132. ksadk/memory/tool.py +96 -0
  133. ksadk/model_policy.py +223 -0
  134. ksadk/openclaw_gateway.py +378 -0
  135. ksadk/runners/__init__.py +8 -0
  136. ksadk/runners/adk_runner.py +1135 -0
  137. ksadk/runners/base_runner.py +305 -0
  138. ksadk/runners/deepagents_runner.py +13 -0
  139. ksadk/runners/factory.py +73 -0
  140. ksadk/runners/langchain_runner.py +539 -0
  141. ksadk/runners/langgraph_runner.py +852 -0
  142. ksadk/runners/patch_langchain.py +187 -0
  143. ksadk/runners/remote_runner.py +667 -0
  144. ksadk/runners/usage_accumulator.py +38 -0
  145. ksadk/runners/utils/__init__.py +19 -0
  146. ksadk/runners/utils/langfuse.py +230 -0
  147. ksadk/runners/utils/loader.py +62 -0
  148. ksadk/runtime_context.py +166 -0
  149. ksadk/runtime_state.py +25 -0
  150. ksadk/sandbox/__init__.py +28 -0
  151. ksadk/sandbox/backends/__init__.py +9 -0
  152. ksadk/sandbox/backends/e2b.py +154 -0
  153. ksadk/sandbox/backends/local_process.py +131 -0
  154. ksadk/sandbox/base.py +101 -0
  155. ksadk/sandbox/factory.py +53 -0
  156. ksadk/sandbox/registry.py +187 -0
  157. ksadk/server/__init__.py +7 -0
  158. ksadk/server/api_models.py +73 -0
  159. ksadk/server/app.py +3789 -0
  160. ksadk/server/static/assets/ArtifactsPanel-C0meuOdi.js +1 -0
  161. ksadk/server/static/assets/CodeBlock-BbI4HPtJ.js +9 -0
  162. ksadk/server/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  163. ksadk/server/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  164. ksadk/server/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  165. ksadk/server/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  166. ksadk/server/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  167. ksadk/server/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  168. ksadk/server/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  169. ksadk/server/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  170. ksadk/server/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  171. ksadk/server/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  172. ksadk/server/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  173. ksadk/server/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  174. ksadk/server/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  175. ksadk/server/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  176. ksadk/server/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  177. ksadk/server/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  178. ksadk/server/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  179. ksadk/server/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  180. ksadk/server/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  181. ksadk/server/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  182. ksadk/server/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  183. ksadk/server/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  184. ksadk/server/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  185. ksadk/server/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  186. ksadk/server/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  187. ksadk/server/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  188. ksadk/server/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  189. ksadk/server/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  190. ksadk/server/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  191. ksadk/server/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  192. ksadk/server/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  193. ksadk/server/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  194. ksadk/server/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  195. ksadk/server/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  196. ksadk/server/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  197. ksadk/server/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  198. ksadk/server/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  199. ksadk/server/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  200. ksadk/server/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  201. ksadk/server/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  202. ksadk/server/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  203. ksadk/server/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  204. ksadk/server/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  205. ksadk/server/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  206. ksadk/server/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  207. ksadk/server/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  208. ksadk/server/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  209. ksadk/server/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  210. ksadk/server/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  211. ksadk/server/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  212. ksadk/server/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  213. ksadk/server/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  214. ksadk/server/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  215. ksadk/server/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  216. ksadk/server/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  217. ksadk/server/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  218. ksadk/server/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  219. ksadk/server/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  220. ksadk/server/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  221. ksadk/server/static/assets/MathMessageMarkdown-DMLQYoLp.js +8 -0
  222. ksadk/server/static/assets/MathMessageMarkdown-DeYh8QPr.css +1 -0
  223. ksadk/server/static/assets/MermaidBlock-DCR8WZ17.js +303 -0
  224. ksadk/server/static/assets/NativeTerminalPanel-CHOd7Rch.css +1 -0
  225. ksadk/server/static/assets/NativeTerminalPanel-CKUvWhUz.js +1 -0
  226. ksadk/server/static/assets/addon-fit-DthTIhi3.js +1 -0
  227. ksadk/server/static/assets/apl-CnwPGSsG.js +1 -0
  228. ksadk/server/static/assets/arc-bj2rZnP5.js +1 -0
  229. ksadk/server/static/assets/architecture-7EHR7CIX-Cr6Xz1_c.js +1 -0
  230. ksadk/server/static/assets/architectureDiagram-3BPJPVTR-2u1ltDIj.js +36 -0
  231. ksadk/server/static/assets/asciiarmor-qTkVPQu6.js +1 -0
  232. ksadk/server/static/assets/asn1-Dr8qZg38.js +1 -0
  233. ksadk/server/static/assets/asterisk-Bja0s6e1.js +1 -0
  234. ksadk/server/static/assets/blockDiagram-GPEHLZMM-D6fvdK20.js +132 -0
  235. ksadk/server/static/assets/brainfuck-D5EjA2JK.js +1 -0
  236. ksadk/server/static/assets/c4Diagram-AAUBKEIU-BK82L5n5.js +10 -0
  237. ksadk/server/static/assets/channel-BSSOwFIb.js +1 -0
  238. ksadk/server/static/assets/chunk-2J33WTMH-DLVdkeyQ.js +1 -0
  239. ksadk/server/static/assets/chunk-4BX2VUAB-BbwsYcbW.js +1 -0
  240. ksadk/server/static/assets/chunk-55IACEB6-DOairxzw.js +1 -0
  241. ksadk/server/static/assets/chunk-727SXJPM-hCO487gs.js +206 -0
  242. ksadk/server/static/assets/chunk-AQP2D5EJ-XU8vDOuG.js +231 -0
  243. ksadk/server/static/assets/chunk-FMBD7UC4-PrapyfMu.js +15 -0
  244. ksadk/server/static/assets/chunk-ND2GUHAM-CboKhTZ3.js +1 -0
  245. ksadk/server/static/assets/chunk-QZHKN3VN-XXpdiwUL.js +1 -0
  246. ksadk/server/static/assets/classDiagram-4FO5ZUOK-C5HvGiyK.js +1 -0
  247. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-C5HvGiyK.js +1 -0
  248. ksadk/server/static/assets/clike-DHH8Ad3s.js +1 -0
  249. ksadk/server/static/assets/clojure-BflJGmDX.js +1 -0
  250. ksadk/server/static/assets/cmake-CyVbuzPu.js +1 -0
  251. ksadk/server/static/assets/cobol-B_OZ4V-R.js +1 -0
  252. ksadk/server/static/assets/coffeescript-B2pKbFk8.js +1 -0
  253. ksadk/server/static/assets/commonlisp-CcllspGY.js +1 -0
  254. ksadk/server/static/assets/cose-bilkent-S5V4N54A-_N8bJ-EU.js +1 -0
  255. ksadk/server/static/assets/crystal-M5-qzICs.js +1 -0
  256. ksadk/server/static/assets/css-CYpP4FRV.js +1 -0
  257. ksadk/server/static/assets/cypher-p9eYesGn.js +1 -0
  258. ksadk/server/static/assets/cytoscape.esm-DelgaX4f.js +321 -0
  259. ksadk/server/static/assets/d-BhMjBjQP.js +1 -0
  260. ksadk/server/static/assets/dagre-BM42HDAG-FoDlo5J2.js +4 -0
  261. ksadk/server/static/assets/dagre-Bx709z4p.js +1 -0
  262. ksadk/server/static/assets/defaultLocale-C8Fc0cco.js +1 -0
  263. ksadk/server/static/assets/diagram-2AECGRRQ-BBp6ebBw.js +43 -0
  264. ksadk/server/static/assets/diagram-5GNKFQAL-CqOvG1PR.js +10 -0
  265. ksadk/server/static/assets/diagram-KO2AKTUF-O9T3t6ba.js +3 -0
  266. ksadk/server/static/assets/diagram-LMA3HP47-CGG8ekBO.js +24 -0
  267. ksadk/server/static/assets/diagram-OG6HWLK6-4BmH1EEg.js +24 -0
  268. ksadk/server/static/assets/diff-ChtP43wD.js +1 -0
  269. ksadk/server/static/assets/dist-6kSZvCZh.js +1 -0
  270. ksadk/server/static/assets/dist-B67uF2uK.js +1 -0
  271. ksadk/server/static/assets/dist-BMKM66B4.js +1 -0
  272. ksadk/server/static/assets/dist-BVzcWa6D.js +1 -0
  273. ksadk/server/static/assets/dist-Bk1l05Ni.js +1 -0
  274. ksadk/server/static/assets/dist-BkJjNih3.js +1 -0
  275. ksadk/server/static/assets/dist-BmuTWcm7.js +9 -0
  276. ksadk/server/static/assets/dist-C2DKRFVK.js +1 -0
  277. ksadk/server/static/assets/dist-CWiTDN_V.js +6 -0
  278. ksadk/server/static/assets/dist-CZ6C3x8B.js +23 -0
  279. ksadk/server/static/assets/dist-CiZHB6ZW.js +1 -0
  280. ksadk/server/static/assets/dist-Cp1_jKwf.js +1 -0
  281. ksadk/server/static/assets/dist-Cwhk4lwI.js +2 -0
  282. ksadk/server/static/assets/dist-D5JGcxBx.js +11 -0
  283. ksadk/server/static/assets/dist-D99wf6_X.js +1 -0
  284. ksadk/server/static/assets/dist-DCpZrjaK.js +1 -0
  285. ksadk/server/static/assets/dist-DI-oVAXw.js +1 -0
  286. ksadk/server/static/assets/dist-DLMjL01k.js +1 -0
  287. ksadk/server/static/assets/dist-DLeM1OCK.js +1 -0
  288. ksadk/server/static/assets/dist-DM0rEQPk.js +1 -0
  289. ksadk/server/static/assets/dist-DbaH3p5h.js +1 -0
  290. ksadk/server/static/assets/dist-S4klkK5W.js +1 -0
  291. ksadk/server/static/assets/dockerfile-BLkNEvjs.js +1 -0
  292. ksadk/server/static/assets/dtd-DTF72YNO.js +1 -0
  293. ksadk/server/static/assets/dylan-C6jEEEk-.js +1 -0
  294. ksadk/server/static/assets/ebnf-D4c0_ac3.js +1 -0
  295. ksadk/server/static/assets/ecl-BSZaIHnp.js +1 -0
  296. ksadk/server/static/assets/eiffel-BEjRio4Q.js +1 -0
  297. ksadk/server/static/assets/elm-Cshzl8qu.js +1 -0
  298. ksadk/server/static/assets/erDiagram-TEJ5UH35-oJhaci7S.js +85 -0
  299. ksadk/server/static/assets/erlang-DaB2Rkuy.js +1 -0
  300. ksadk/server/static/assets/eventmodeling-FCH6USID-CIgwHJLL.js +1 -0
  301. ksadk/server/static/assets/factor-D7LVTn2l.js +1 -0
  302. ksadk/server/static/assets/fcl-ClOhbFR7.js +1 -0
  303. ksadk/server/static/assets/flowDiagram-I6XJVG4X-DtTmUan-.js +162 -0
  304. ksadk/server/static/assets/forth-Cezjo90N.js +1 -0
  305. ksadk/server/static/assets/fortran-Bt6PBEDR.js +1 -0
  306. ksadk/server/static/assets/ganttDiagram-6RSMTGT7-BSsb_eij.js +292 -0
  307. ksadk/server/static/assets/gas-BHEdbvp9.js +1 -0
  308. ksadk/server/static/assets/gherkin-oBAE_ms0.js +1 -0
  309. ksadk/server/static/assets/gitGraph-WXDBUCRP-BVAJ7uo0.js +1 -0
  310. ksadk/server/static/assets/gitGraphDiagram-PVQCEYII-BR5s6Pws.js +106 -0
  311. ksadk/server/static/assets/graphlib-B8gBHxth.js +1 -0
  312. ksadk/server/static/assets/groovy-BC3IOsC8.js +1 -0
  313. ksadk/server/static/assets/haskell-BRsoo5mP.js +1 -0
  314. ksadk/server/static/assets/haxe-DIz0ZZqd.js +1 -0
  315. ksadk/server/static/assets/http-CLVgA2GD.js +1 -0
  316. ksadk/server/static/assets/idl-BUZw3wgd.js +1 -0
  317. ksadk/server/static/assets/index-BH7-pC0B.js +165 -0
  318. ksadk/server/static/assets/index-DAURbCsP.css +1 -0
  319. ksadk/server/static/assets/info-J43DQDTF-MT80V4ge.js +1 -0
  320. ksadk/server/static/assets/infoDiagram-5YYISTIA-DYPc5cvh.js +2 -0
  321. ksadk/server/static/assets/init-D6jRqBbL.js +1 -0
  322. ksadk/server/static/assets/ishikawaDiagram-YF4QCWOH-MoBdALDg.js +70 -0
  323. ksadk/server/static/assets/javascript-9Tg8ixDm.js +1 -0
  324. ksadk/server/static/assets/journeyDiagram-JHISSGLW-DgLiQT8l.js +139 -0
  325. ksadk/server/static/assets/julia-CQfgDRfP.js +1 -0
  326. ksadk/server/static/assets/kanban-definition-UN3LZRKU-CsFx112F.js +89 -0
  327. ksadk/server/static/assets/katex-DRsiwpM2.js +265 -0
  328. ksadk/server/static/assets/linear-TNRQ5Orv.js +1 -0
  329. ksadk/server/static/assets/livescript-BXxG0Yva.js +1 -0
  330. ksadk/server/static/assets/lua-CiA1ziua.js +1 -0
  331. ksadk/server/static/assets/mathematica-u8YMmbU0.js +1 -0
  332. ksadk/server/static/assets/mbox-BEMVcqjs.js +1 -0
  333. ksadk/server/static/assets/mermaid-parser.core-D2eMVOgo.js +161 -0
  334. ksadk/server/static/assets/mindmap-definition-RKZ34NQL-BtByYFhL.js +96 -0
  335. ksadk/server/static/assets/mirc-C9zpzAZU.js +1 -0
  336. ksadk/server/static/assets/mllike-B45xgp2S.js +1 -0
  337. ksadk/server/static/assets/modelica-CqHI_q-D.js +1 -0
  338. ksadk/server/static/assets/mscgen-qgSLujhx.js +1 -0
  339. ksadk/server/static/assets/mumps-CW_TBmxz.js +1 -0
  340. ksadk/server/static/assets/nginx-BMaDbqW3.js +1 -0
  341. ksadk/server/static/assets/nsis-AQ_alPln.js +1 -0
  342. ksadk/server/static/assets/ntriples-d28e9m0E.js +1 -0
  343. ksadk/server/static/assets/octave-D2Q8cUp1.js +1 -0
  344. ksadk/server/static/assets/ordinal-hYBb2elL.js +1 -0
  345. ksadk/server/static/assets/oz-BwTct_5T.js +1 -0
  346. ksadk/server/static/assets/packet-YPE3B663-BLKhbXZB.js +1 -0
  347. ksadk/server/static/assets/pascal-Dp_KdFgX.js +1 -0
  348. ksadk/server/static/assets/perl-CdEHsPfU.js +1 -0
  349. ksadk/server/static/assets/pie-LRSECV5Y-DFOikjSY.js +1 -0
  350. ksadk/server/static/assets/pieDiagram-4H26LBE5-DPnuCB8c.js +30 -0
  351. ksadk/server/static/assets/pig-AF4Hwhju.js +1 -0
  352. ksadk/server/static/assets/powershell-CzG71I4L.js +1 -0
  353. ksadk/server/static/assets/properties-C357ku5U.js +1 -0
  354. ksadk/server/static/assets/protobuf-D1ij_kNL.js +1 -0
  355. ksadk/server/static/assets/pug-Bugr-47h.js +1 -0
  356. ksadk/server/static/assets/puppet-B4hC2X4m.js +1 -0
  357. ksadk/server/static/assets/python-BqYw9LYJ.js +1 -0
  358. ksadk/server/static/assets/q-B9NhS7L_.js +1 -0
  359. ksadk/server/static/assets/quadrantDiagram-W4KKPZXB-miFpCPv4.js +7 -0
  360. ksadk/server/static/assets/r-DADKenSm.js +1 -0
  361. ksadk/server/static/assets/radar-GUYGQ44K-095b1Ikw.js +1 -0
  362. ksadk/server/static/assets/requirementDiagram-4Y6WPE33-mjB_yYkW.js +84 -0
  363. ksadk/server/static/assets/rpm-gpDK5sbt.js +1 -0
  364. ksadk/server/static/assets/ruby-DsYpTuWg.js +1 -0
  365. ksadk/server/static/assets/sankeyDiagram-5OEKKPKP-D7hSlZZZ.js +40 -0
  366. ksadk/server/static/assets/sas-C2OyD2Y6.js +1 -0
  367. ksadk/server/static/assets/scheme-CyDdOh7e.js +1 -0
  368. ksadk/server/static/assets/sequenceDiagram-3UESZ5HK-D9Cx6hC3.js +162 -0
  369. ksadk/server/static/assets/shell-Du5Qg9im.js +1 -0
  370. ksadk/server/static/assets/sieve-BAxa3IjF.js +1 -0
  371. ksadk/server/static/assets/simple-mode-DRpGK0lJ.js +1 -0
  372. ksadk/server/static/assets/smalltalk-BLp5-jwC.js +1 -0
  373. ksadk/server/static/assets/solr-BBopId9w.js +1 -0
  374. ksadk/server/static/assets/sparql-Dz-mlCAC.js +1 -0
  375. ksadk/server/static/assets/spreadsheet-CTIncYxj.js +1 -0
  376. ksadk/server/static/assets/sql-DcCJ9jop.js +1 -0
  377. ksadk/server/static/assets/stateDiagram-AJRCARHV-D_zrg47o.js +1 -0
  378. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-J9kCb0nC.js +1 -0
  379. ksadk/server/static/assets/stex-sDrdk2BW.js +1 -0
  380. ksadk/server/static/assets/stylus-DLLZ8dgm.js +1 -0
  381. ksadk/server/static/assets/swift-DKB6_1j6.js +1 -0
  382. ksadk/server/static/assets/tcl-BGfruO1u.js +1 -0
  383. ksadk/server/static/assets/textile-DWBqthVk.js +1 -0
  384. ksadk/server/static/assets/tiddlywiki-VlUiK86J.js +1 -0
  385. ksadk/server/static/assets/tiki-CxVlf5KZ.js +1 -0
  386. ksadk/server/static/assets/timeline-definition-PNZ67QCA-CwSec0Wa.js +120 -0
  387. ksadk/server/static/assets/toml-LvoKBwCs.js +1 -0
  388. ksadk/server/static/assets/treeView-BLDUP644-yinfJEY6.js +1 -0
  389. ksadk/server/static/assets/treemap-LRROVOQU-DosUqN1Q.js +1 -0
  390. ksadk/server/static/assets/troff-C2dAgh7P.js +1 -0
  391. ksadk/server/static/assets/ttcn-BePYh-hG.js +1 -0
  392. ksadk/server/static/assets/ttcn-cfg-CY0aot7Q.js +1 -0
  393. ksadk/server/static/assets/turtle-BDLWE2cD.js +1 -0
  394. ksadk/server/static/assets/vb-BthdM_4N.js +1 -0
  395. ksadk/server/static/assets/vbscript-D9f6rSsU.js +1 -0
  396. ksadk/server/static/assets/velocity-CqftRsjg.js +1 -0
  397. ksadk/server/static/assets/vennDiagram-CIIHVFJN-agCVobiF.js +34 -0
  398. ksadk/server/static/assets/verilog-D-x6ne0Z.js +1 -0
  399. ksadk/server/static/assets/vhdl-CMrOq2q1.js +1 -0
  400. ksadk/server/static/assets/wardley-L42UT6IY-Bvp7-5uH.js +1 -0
  401. ksadk/server/static/assets/wardleyDiagram-YWT4CUSO-TmrlhWNt.js +78 -0
  402. ksadk/server/static/assets/webidl-D5jiJvOU.js +1 -0
  403. ksadk/server/static/assets/xquery-fj_R-kMw.js +1 -0
  404. ksadk/server/static/assets/xterm-DooSxjI5.js +36 -0
  405. ksadk/server/static/assets/xychartDiagram-2RQKCTM6-DV71V-ah.js +7 -0
  406. ksadk/server/static/assets/yacas-CXXgtw0p.js +1 -0
  407. ksadk/server/static/assets/z80-CL1naaMV.js +1 -0
  408. ksadk/server/static/favicon.svg +1 -0
  409. ksadk/server/static/icons.svg +24 -0
  410. ksadk/server/static/index.html +14 -0
  411. ksadk/server/terminal_sessions.py +551 -0
  412. ksadk/sessions/__init__.py +283 -0
  413. ksadk/sessions/base.py +354 -0
  414. ksadk/sessions/continuity.py +313 -0
  415. ksadk/sessions/errors.py +7 -0
  416. ksadk/sessions/in_memory.py +273 -0
  417. ksadk/sessions/local_service.py +949 -0
  418. ksadk/sessions/postgres_service.py +838 -0
  419. ksadk/sessions/sqlite_service.py +13 -0
  420. ksadk/skills/__init__.py +9 -0
  421. ksadk/skills/loader.py +46 -0
  422. ksadk/skills/models.py +120 -0
  423. ksadk/skills/package_store.py +104 -0
  424. ksadk/skills/runtime/__init__.py +17 -0
  425. ksadk/skills/runtime/agent.py +183 -0
  426. ksadk/skills/runtime/artifacts.py +39 -0
  427. ksadk/skills/runtime/backends/__init__.py +1 -0
  428. ksadk/skills/runtime/backends/disabled.py +8 -0
  429. ksadk/skills/runtime/backends/e2b.py +168 -0
  430. ksadk/skills/runtime/backends/local.py +95 -0
  431. ksadk/skills/runtime/base.py +103 -0
  432. ksadk/skills/runtime/executor.py +219 -0
  433. ksadk/skills/runtime/factory.py +37 -0
  434. ksadk/skills/runtime/loader.py +131 -0
  435. ksadk/skills/runtime/registry.py +150 -0
  436. ksadk/skills/runtime/request.py +67 -0
  437. ksadk/skills/service_client.py +239 -0
  438. ksadk/skills/service_env.py +58 -0
  439. ksadk/skills/tool_defs.py +221 -0
  440. ksadk/terminal_client.py +69 -0
  441. ksadk/terminal_exec_policy.py +229 -0
  442. ksadk/tools/__init__.py +17 -0
  443. ksadk/tools/gateway.py +230 -0
  444. ksadk/tools/result_budget.py +154 -0
  445. ksadk/toolsets/__init__.py +852 -0
  446. ksadk/toolsets/_langchain.py +11 -0
  447. ksadk/toolsets/platform.py +93 -0
  448. ksadk/toolsets/sandbox.py +310 -0
  449. ksadk/toolsets/skills.py +217 -0
  450. ksadk/toolsets/web.py +266 -0
  451. ksadk/toolsets/workspace.py +878 -0
  452. ksadk/toolsets/workspace_state.py +44 -0
  453. ksadk/tracing/__init__.py +37 -0
  454. ksadk/tracing/exporters/__init__.py +1 -0
  455. ksadk/tracing/exporters/inmemory_exporter.py +116 -0
  456. ksadk/tracing/exporters/langfuse_exporter.py +445 -0
  457. ksadk/tracing/setup.py +829 -0
  458. ksadk/tracing/span_utils.py +114 -0
  459. ksadk/tui/__init__.py +12 -0
  460. ksadk/tui/app.py +400 -0
  461. ksadk/tui/clipboard.py +100 -0
  462. ksadk/tui/widgets/__init__.py +22 -0
  463. ksadk/tui/widgets/assistant.py +89 -0
  464. ksadk/tui/widgets/base.py +54 -0
  465. ksadk/tui/widgets/chat_input.py +246 -0
  466. ksadk/tui/widgets/history.py +156 -0
  467. ksadk/tui/widgets/system.py +87 -0
  468. ksadk/tui/widgets/thinking.py +110 -0
  469. ksadk/tui/widgets/tool_call.py +161 -0
  470. ksadk/tui/widgets/user.py +37 -0
  471. ksadk/ui_config.py +168 -0
  472. ksadk/version.py +4 -0
  473. ksadk_runtime_common/__init__.py +20 -0
  474. ksadk_runtime_common/memory_backend/__init__.py +29 -0
  475. ksadk_runtime_common/memory_backend/manifest.py +79 -0
  476. ksadk_runtime_common/memory_backend/providers/__init__.py +6 -0
  477. ksadk_runtime_common/memory_backend/providers/lancedb.py +151 -0
  478. ksadk_runtime_common/memory_backend/providers/mem0.py +74 -0
  479. ksadk_runtime_common/memory_backend/registry.py +56 -0
  480. ksadk_runtime_common/memory_backend/render.py +71 -0
  481. ksadk_runtime_common/schemas/memory_backend_manifest.schema.json +148 -0
  482. ksadk_runtime_common/workspace_files/__init__.py +29 -0
  483. ksadk_runtime_common/workspace_files/bootstrap.py +56 -0
  484. ksadk_runtime_common/workspace_files/constants.py +9 -0
  485. ksadk_runtime_common/workspace_files/path_utils.py +71 -0
  486. ksadk_runtime_common/workspace_files/preview.py +96 -0
  487. ksadk_runtime_common/workspace_files/router.py +254 -0
  488. agentengine_sdk_python-0.6.2.dist-info/METADATA +0 -46
  489. agentengine_sdk_python-0.6.2.dist-info/RECORD +0 -4
  490. agentengine_sdk_python-0.6.2.dist-info/top_level.txt +0 -1
@@ -0,0 +1,878 @@
1
+ from __future__ import annotations
2
+
3
+ import ast
4
+ import difflib
5
+ import fnmatch
6
+ import json
7
+ import re
8
+ import shutil
9
+ import subprocess
10
+ from pathlib import Path
11
+ from typing import Any
12
+
13
+ from ksadk.sessions.local_service import resolve_local_session_dir
14
+ from ksadk.tools.gateway import ToolPolicy, default_tool_gateway
15
+ from ksadk.tools.result_budget import budget_tool_output, default_tool_result_budget
16
+ from ksadk.toolsets._langchain import as_tool
17
+ from ksadk.toolsets.workspace_state import WorkspaceReadState, get_read_state, record_read_state
18
+
19
+
20
+ _WORKSPACE_TOOL_POLICIES = {
21
+ "workspace_status": ToolPolicy(risk_level="low"),
22
+ "list_workspace_files": ToolPolicy(risk_level="low"),
23
+ "read_workspace_file": ToolPolicy(risk_level="low"),
24
+ "write_workspace_file": ToolPolicy(risk_level="medium", side_effects=("workspace_write",)),
25
+ "write_workspace_files": ToolPolicy(risk_level="medium", side_effects=("workspace_write",)),
26
+ "edit_workspace_file": ToolPolicy(risk_level="medium", side_effects=("workspace_edit",)),
27
+ "multi_edit_workspace_file": ToolPolicy(risk_level="medium", side_effects=("workspace_edit",)),
28
+ "lint_workspace_file": ToolPolicy(risk_level="low"),
29
+ "search_workspace_files": ToolPolicy(risk_level="low"),
30
+ "delete_workspace_file": ToolPolicy(risk_level="high", side_effects=("workspace_delete",)),
31
+ }
32
+
33
+
34
+ def _gateway():
35
+ return default_tool_gateway(_WORKSPACE_TOOL_POLICIES)
36
+
37
+
38
+ def workspace_root() -> Path:
39
+ return resolve_local_session_dir() / "workspace"
40
+
41
+
42
+ def resolve_workspace_path(relative_path: str) -> Path:
43
+ root = workspace_root().resolve()
44
+ raw = str(relative_path or "").strip().replace("\\", "/").lstrip("/") or "."
45
+ target = (root / raw).resolve()
46
+ if target != root and root not in target.parents:
47
+ raise ValueError("workspace path must stay inside the workspace root")
48
+ return target
49
+
50
+
51
+ def workspace_relative(path: Path) -> str:
52
+ return path.resolve().relative_to(workspace_root().resolve()).as_posix()
53
+
54
+
55
+ def workspace_status() -> dict[str, Any]:
56
+ """Return current AgentEngine workspace status."""
57
+
58
+ return _gateway().invoke("workspace_status", _workspace_status_impl)
59
+
60
+
61
+ def _workspace_status_impl() -> dict[str, Any]:
62
+ root = workspace_root()
63
+ root.mkdir(parents=True, exist_ok=True)
64
+ files = [
65
+ {"path": workspace_relative(path), "size": path.stat().st_size}
66
+ for path in sorted(item for item in root.rglob("*") if item.is_file())[:50]
67
+ ]
68
+ return {
69
+ "ok": True,
70
+ "workspace_root": str(root),
71
+ "file_count_sampled": len(files),
72
+ "files": files,
73
+ }
74
+
75
+
76
+ def list_workspace_files(
77
+ path: str = ".",
78
+ glob: str | None = None,
79
+ recursive: bool = False,
80
+ include_dirs: bool = True,
81
+ max_results: int = 500,
82
+ sort_by: str = "name",
83
+ ) -> dict[str, Any]:
84
+ """List files under the AgentEngine workspace."""
85
+
86
+ return _gateway().invoke(
87
+ "list_workspace_files",
88
+ _list_workspace_files_impl,
89
+ path,
90
+ glob,
91
+ recursive,
92
+ include_dirs,
93
+ max_results,
94
+ sort_by,
95
+ )
96
+
97
+
98
+ def _list_workspace_files_impl(
99
+ path: str = ".",
100
+ glob: str | None = None,
101
+ recursive: bool = False,
102
+ include_dirs: bool = True,
103
+ max_results: int = 500,
104
+ sort_by: str = "name",
105
+ ) -> dict[str, Any]:
106
+ target = resolve_workspace_path(path)
107
+ if not target.exists():
108
+ return {"ok": False, "error_message": f"workspace path not found: {path}"}
109
+ if target.is_file():
110
+ items = [target]
111
+ elif recursive:
112
+ items = [item for item in target.rglob("*") if item != target]
113
+ else:
114
+ items = list(target.iterdir())
115
+ pattern = str(glob or "").strip()
116
+ if pattern:
117
+ items = [
118
+ item
119
+ for item in items
120
+ if fnmatch.fnmatch(workspace_relative(item), pattern) or fnmatch.fnmatch(item.name, pattern)
121
+ ]
122
+ if not include_dirs:
123
+ items = [item for item in items if item.is_file()]
124
+ sort_key = str(sort_by or "name").strip().lower()
125
+ if sort_key == "mtime":
126
+ key = lambda candidate: (candidate.stat().st_mtime_ns, workspace_relative(candidate))
127
+ elif sort_key == "size":
128
+ key = lambda candidate: (candidate.stat().st_size if candidate.is_file() else 0, workspace_relative(candidate))
129
+ else:
130
+ key = lambda candidate: (workspace_relative(candidate).lower(), candidate.is_file())
131
+ limit = max(1, min(int(max_results or 500), 5000))
132
+ sorted_items = sorted(items, key=key)
133
+ entries = [
134
+ {
135
+ "name": item.name,
136
+ "path": workspace_relative(item),
137
+ "type": "directory" if item.is_dir() else "file",
138
+ "size": item.stat().st_size if item.is_file() else 0,
139
+ "mtime_ns": item.stat().st_mtime_ns,
140
+ }
141
+ for item in sorted_items[:limit]
142
+ ]
143
+ return {"ok": True, "path": path, "recursive": recursive, "entries": entries, "truncated": len(sorted_items) > len(entries)}
144
+
145
+
146
+ def read_workspace_file(
147
+ path: str,
148
+ start_line: int | None = None,
149
+ end_line: int | None = None,
150
+ max_chars: int | None = None,
151
+ include_line_numbers: bool = True,
152
+ ) -> dict[str, Any]:
153
+ """Read a UTF-8 text file from the AgentEngine workspace."""
154
+
155
+ return _gateway().invoke(
156
+ "read_workspace_file",
157
+ _read_workspace_file_impl,
158
+ path,
159
+ start_line,
160
+ end_line,
161
+ max_chars,
162
+ include_line_numbers,
163
+ )
164
+
165
+
166
+ def _read_workspace_file_impl(
167
+ path: str,
168
+ start_line: int | None = None,
169
+ end_line: int | None = None,
170
+ max_chars: int | None = None,
171
+ include_line_numbers: bool = True,
172
+ ) -> dict[str, Any]:
173
+ target = resolve_workspace_path(path)
174
+ if not target.is_file():
175
+ return {"ok": False, "error_message": f"workspace file not found: {path}"}
176
+ stat = target.stat()
177
+ if stat.st_size > 2 * 1024 * 1024:
178
+ return {"ok": False, "error_message": "file is larger than 2MB"}
179
+ try:
180
+ text = target.read_text(encoding="utf-8")
181
+ except UnicodeDecodeError:
182
+ return {"ok": False, "error_message": "file is not utf-8 text"}
183
+ lines = text.splitlines()
184
+ total_lines = len(lines)
185
+ start = max(1, int(start_line or 1))
186
+ end = min(total_lines, int(end_line or total_lines)) if total_lines else 0
187
+ if end and start > end:
188
+ return {"ok": False, "error_message": "start_line must be <= end_line"}
189
+ selected = lines[start - 1 : end] if total_lines else []
190
+ content = "\n".join(
191
+ f"{line_no} | {line}" if include_line_numbers else line
192
+ for line_no, line in enumerate(selected, start=start)
193
+ )
194
+ limit = max(1000, min(int(max_chars or 20000), 100000))
195
+ relative = workspace_relative(target)
196
+ budgeted = budget_tool_output(
197
+ tool_name="read_workspace_file",
198
+ field_name="content",
199
+ value=content,
200
+ metadata={"tool_use_id": f"read_{relative.replace('/', '_')}"},
201
+ budget=default_tool_result_budget(max_chars=limit, preview_chars=limit, persist_threshold_chars=limit),
202
+ )
203
+ partial = start != 1 or end != total_lines
204
+ record_read_state(
205
+ WorkspaceReadState(
206
+ path=relative,
207
+ mtime_ns=stat.st_mtime_ns,
208
+ size_bytes=stat.st_size,
209
+ start_line=start,
210
+ end_line=end,
211
+ total_lines=total_lines,
212
+ partial=partial or bool(budgeted.get("truncated")),
213
+ )
214
+ )
215
+ return {
216
+ "ok": True,
217
+ "path": relative,
218
+ "size": stat.st_size,
219
+ "size_bytes": stat.st_size,
220
+ "mtime_ns": stat.st_mtime_ns,
221
+ "start_line": start,
222
+ "end_line": end,
223
+ "line_count": len(selected),
224
+ "read_range": {"start": start, "end": end},
225
+ "total_lines": total_lines,
226
+ "suggested_action": "",
227
+ "partial": partial or bool(budgeted.get("truncated")),
228
+ **budgeted,
229
+ }
230
+
231
+
232
+ def _read_workspace_text(target: Path) -> tuple[str | None, dict[str, Any] | None]:
233
+ if not target.is_file():
234
+ return None, {"ok": False, "error_message": "workspace file not found"}
235
+ if target.stat().st_size > 2 * 1024 * 1024:
236
+ return None, {"ok": False, "error_message": "file is larger than 2MB"}
237
+ try:
238
+ return target.read_text(encoding="utf-8"), None
239
+ except UnicodeDecodeError:
240
+ return None, {"ok": False, "error_message": "file is not utf-8 text"}
241
+
242
+
243
+ def write_workspace_file(
244
+ path: str,
245
+ content: str,
246
+ overwrite: bool = True,
247
+ approval: dict[str, Any] | None = None,
248
+ ) -> dict[str, Any]:
249
+ """Write a UTF-8 text file inside the AgentEngine workspace."""
250
+
251
+ return _gateway().invoke(
252
+ "write_workspace_file",
253
+ _write_workspace_file_impl,
254
+ path,
255
+ content,
256
+ overwrite,
257
+ approval=approval,
258
+ )
259
+
260
+
261
+ def _write_workspace_file_impl(path: str, content: str, overwrite: bool = True) -> dict[str, Any]:
262
+ target = resolve_workspace_path(path)
263
+ target.parent.mkdir(parents=True, exist_ok=True)
264
+ if target.exists() and not overwrite:
265
+ return {"ok": False, "error_message": f"workspace file already exists: {path}"}
266
+ target.write_text(content or "", encoding="utf-8")
267
+ return {
268
+ "ok": True,
269
+ "path": workspace_relative(target),
270
+ "absolute_path": str(target),
271
+ "size": target.stat().st_size,
272
+ }
273
+
274
+
275
+ def write_workspace_files(
276
+ files: list[dict[str, Any]],
277
+ overwrite: bool = True,
278
+ approval: dict[str, Any] | None = None,
279
+ ) -> dict[str, Any]:
280
+ """Write multiple UTF-8 text files inside the AgentEngine workspace."""
281
+
282
+ return _gateway().invoke(
283
+ "write_workspace_files",
284
+ _write_workspace_files_impl,
285
+ files,
286
+ overwrite,
287
+ approval=approval,
288
+ )
289
+
290
+
291
+ def _write_workspace_files_impl(files: list[dict[str, Any]], overwrite: bool = True) -> dict[str, Any]:
292
+ if not isinstance(files, list) or not files:
293
+ return {"ok": False, "error_message": "files must be a non-empty list"}
294
+ written = []
295
+ for item in files[:100]:
296
+ if not isinstance(item, dict):
297
+ return {"ok": False, "error_message": "each file item must be an object"}
298
+ path = str(item.get("path") or "").strip()
299
+ if not path:
300
+ return {"ok": False, "error_message": "each file item requires path"}
301
+ result = _write_workspace_file_impl(path, str(item.get("content") or ""), overwrite=overwrite)
302
+ if not result.get("ok"):
303
+ return result
304
+ written.append({"path": result["path"], "size": result["size"]})
305
+ return {"ok": True, "written": written, "truncated": len(files) > len(written)}
306
+
307
+
308
+ def edit_workspace_file(
309
+ path: str,
310
+ old_text: str,
311
+ new_text: str,
312
+ expected_replacements: int = 1,
313
+ replace_all: bool = False,
314
+ approval: dict[str, Any] | None = None,
315
+ ) -> dict[str, Any]:
316
+ """Replace an exact text snippet inside a UTF-8 workspace file."""
317
+
318
+ return _gateway().invoke(
319
+ "edit_workspace_file",
320
+ _edit_workspace_file_impl,
321
+ path,
322
+ old_text,
323
+ new_text,
324
+ expected_replacements,
325
+ replace_all,
326
+ approval=approval,
327
+ )
328
+
329
+
330
+ def _edit_workspace_file_impl(
331
+ path: str,
332
+ old_text: str,
333
+ new_text: str,
334
+ expected_replacements: int = 1,
335
+ replace_all: bool = False,
336
+ ) -> dict[str, Any]:
337
+ target = resolve_workspace_path(path)
338
+ relative = workspace_relative(target)
339
+ read_error = _validate_read_state(target, relative)
340
+ if read_error is not None:
341
+ return read_error
342
+ text, error = _read_workspace_text(target)
343
+ if error is not None:
344
+ return {**error, "path": path}
345
+ edit_result = _apply_single_edit(
346
+ text,
347
+ old_text,
348
+ new_text,
349
+ expected_replacements=expected_replacements,
350
+ replace_all=replace_all,
351
+ relative=relative,
352
+ )
353
+ if not edit_result["ok"]:
354
+ return edit_result
355
+ updated = str(edit_result["updated"])
356
+ diff = _build_diff(relative, text, updated)
357
+ target.write_text(updated, encoding="utf-8")
358
+ new_stat = target.stat()
359
+ _refresh_read_state(relative, new_stat, updated)
360
+ diff_budget = _budget_diff("edit_workspace_file", diff, relative)
361
+ return {
362
+ "ok": True,
363
+ "path": relative,
364
+ "absolute_path": str(target),
365
+ "replacements": edit_result["replacements"],
366
+ "used_quote_normalization": edit_result["used_quote_normalization"],
367
+ **diff_budget,
368
+ "size": new_stat.st_size,
369
+ "mtime_ns": new_stat.st_mtime_ns,
370
+ }
371
+
372
+
373
+ def multi_edit_workspace_file(
374
+ path: str,
375
+ edits: list[dict[str, Any]],
376
+ replace_all: bool = False,
377
+ approval: dict[str, Any] | None = None,
378
+ ) -> dict[str, Any]:
379
+ """Apply multiple exact snippet edits atomically inside one workspace file."""
380
+
381
+ return _gateway().invoke(
382
+ "multi_edit_workspace_file",
383
+ _multi_edit_workspace_file_impl,
384
+ path,
385
+ edits,
386
+ replace_all,
387
+ approval=approval,
388
+ )
389
+
390
+
391
+ def _multi_edit_workspace_file_impl(
392
+ path: str,
393
+ edits: list[dict[str, Any]],
394
+ replace_all: bool = False,
395
+ ) -> dict[str, Any]:
396
+ target = resolve_workspace_path(path)
397
+ relative = workspace_relative(target)
398
+ read_error = _validate_read_state(target, relative)
399
+ if read_error is not None:
400
+ return read_error
401
+ text, error = _read_workspace_text(target)
402
+ if error is not None:
403
+ return {**error, "path": path}
404
+ if not isinstance(edits, list) or not edits:
405
+ return {"ok": False, "path": relative, "error_type": "invalid_edits", "error_message": "edits must be a non-empty list"}
406
+ original = text or ""
407
+ updated = original
408
+ diagnostics: list[dict[str, Any]] = []
409
+ total_replacements = 0
410
+ for index, edit in enumerate(edits):
411
+ if not isinstance(edit, dict):
412
+ return {"ok": False, "path": relative, "error_type": "invalid_edit", "error_message": f"edit at index {index} must be an object", "failed_edit_index": index}
413
+ edit_result = _apply_single_edit(
414
+ updated,
415
+ edit.get("old_text"),
416
+ edit.get("new_text"),
417
+ expected_replacements=int(edit.get("expected_replacements") or 1),
418
+ replace_all=bool(edit.get("replace_all", replace_all)),
419
+ relative=relative,
420
+ )
421
+ if not edit_result["ok"]:
422
+ return {**edit_result, "failed_edit_index": index, "edit_count": len(edits)}
423
+ updated = str(edit_result["updated"])
424
+ total_replacements += int(edit_result["replacements"])
425
+ diagnostics.append(
426
+ {
427
+ "index": index,
428
+ "replacements": edit_result["replacements"],
429
+ "used_quote_normalization": edit_result["used_quote_normalization"],
430
+ "matches": edit_result.get("matches", []),
431
+ }
432
+ )
433
+ diff = _build_diff(relative, original, updated)
434
+ target.write_text(updated, encoding="utf-8")
435
+ new_stat = target.stat()
436
+ _refresh_read_state(relative, new_stat, updated)
437
+ diff_budget = _budget_diff("multi_edit_workspace_file", diff, relative)
438
+ return {
439
+ "ok": True,
440
+ "path": relative,
441
+ "absolute_path": str(target),
442
+ "edit_count": len(edits),
443
+ "replacements": total_replacements,
444
+ "edits": diagnostics,
445
+ **diff_budget,
446
+ "size": new_stat.st_size,
447
+ "mtime_ns": new_stat.st_mtime_ns,
448
+ }
449
+
450
+
451
+ def lint_workspace_file(path: str, language: str = "auto") -> dict[str, Any]:
452
+ """Run lightweight built-in lint checks for a UTF-8 workspace text file."""
453
+
454
+ return _gateway().invoke("lint_workspace_file", _lint_workspace_file_impl, path, language)
455
+
456
+
457
+ def _lint_workspace_file_impl(path: str, language: str = "auto") -> dict[str, Any]:
458
+ target = resolve_workspace_path(path)
459
+ text, error = _read_workspace_text(target)
460
+ if error is not None:
461
+ return {**error, "path": path}
462
+ language_name = _detect_language(target, language)
463
+ issues: list[dict[str, Any]] = []
464
+ if language_name == "python":
465
+ try:
466
+ ast.parse(text or "", filename=workspace_relative(target))
467
+ except SyntaxError as exc:
468
+ issues.append(
469
+ {
470
+ "severity": "error",
471
+ "line": exc.lineno or 0,
472
+ "column": exc.offset or 0,
473
+ "message": exc.msg,
474
+ }
475
+ )
476
+ elif language_name == "json":
477
+ try:
478
+ json.loads(text or "")
479
+ except json.JSONDecodeError as exc:
480
+ issues.append(
481
+ {
482
+ "severity": "error",
483
+ "line": exc.lineno,
484
+ "column": exc.colno,
485
+ "message": exc.msg,
486
+ }
487
+ )
488
+ else:
489
+ for line_no, line in enumerate((text or "").splitlines(), start=1):
490
+ if "\x00" in line:
491
+ issues.append({"severity": "error", "line": line_no, "column": line.index("\x00") + 1, "message": "NUL byte found"})
492
+ if line.rstrip("\n\r") != line.rstrip():
493
+ issues.append({"severity": "warning", "line": line_no, "column": len(line), "message": "trailing whitespace"})
494
+ if len(issues) >= 20:
495
+ break
496
+ return {
497
+ "ok": not any(issue["severity"] == "error" for issue in issues),
498
+ "path": workspace_relative(target),
499
+ "language": language_name,
500
+ "issues": issues,
501
+ "lint_model": "built_in_lightweight",
502
+ }
503
+
504
+
505
+ def _detect_language(path: Path, language: str = "auto") -> str:
506
+ value = str(language or "auto").strip().lower()
507
+ if value and value != "auto":
508
+ aliases = {"py": "python", "python3": "python", "js": "javascript"}
509
+ return aliases.get(value, value)
510
+ suffix = path.suffix.lower()
511
+ if suffix == ".py":
512
+ return "python"
513
+ if suffix == ".json":
514
+ return "json"
515
+ if suffix in {".md", ".markdown"}:
516
+ return "markdown"
517
+ if suffix in {".txt", ".log"}:
518
+ return "text"
519
+ return "text"
520
+
521
+
522
+ def search_workspace_files(
523
+ query: str,
524
+ path: str = ".",
525
+ is_regex: bool = False,
526
+ glob: str | None = None,
527
+ case_sensitive: bool = False,
528
+ context_lines: int = 0,
529
+ max_results: int = 100,
530
+ ) -> dict[str, Any]:
531
+ """Search UTF-8 text files in the AgentEngine workspace."""
532
+
533
+ return _gateway().invoke(
534
+ "search_workspace_files",
535
+ _search_workspace_files_impl,
536
+ query,
537
+ path,
538
+ is_regex,
539
+ glob,
540
+ case_sensitive,
541
+ context_lines,
542
+ max_results,
543
+ )
544
+
545
+
546
+ def _search_workspace_files_impl(
547
+ query: str,
548
+ path: str = ".",
549
+ is_regex: bool = False,
550
+ glob: str | None = None,
551
+ case_sensitive: bool = False,
552
+ context_lines: int = 0,
553
+ max_results: int = 100,
554
+ ) -> dict[str, Any]:
555
+ needle = str(query or "").strip()
556
+ if not needle:
557
+ return {"ok": False, "error_message": "query is required"}
558
+ base = resolve_workspace_path(path)
559
+ if shutil.which("rg") and not context_lines:
560
+ rg_result = _search_with_rg(needle, base, is_regex, glob, case_sensitive, context_lines, max_results)
561
+ if rg_result is not None:
562
+ return _search_payload(query, path, rg_result["results"], rg_result["truncated"], context_lines, "rg")
563
+ candidates = [base] if base.is_file() else [item for item in base.rglob("*") if item.is_file()]
564
+ if glob:
565
+ candidates = [item for item in candidates if fnmatch.fnmatch(workspace_relative(item), glob) or fnmatch.fnmatch(item.name, glob)]
566
+ flags = 0 if case_sensitive else re.IGNORECASE
567
+ pattern = re.compile(needle if is_regex else re.escape(needle), flags)
568
+ results = []
569
+ limit = max(1, min(int(max_results or 100), 5000))
570
+ context_limit = max(0, min(int(context_lines or 0), 20))
571
+ truncated = False
572
+ for item in candidates:
573
+ if item.stat().st_size > 1024 * 1024:
574
+ continue
575
+ try:
576
+ lines = item.read_text(encoding="utf-8").splitlines()
577
+ except UnicodeDecodeError:
578
+ continue
579
+ for line_no, line in enumerate(lines, start=1):
580
+ match = pattern.search(line)
581
+ if not match:
582
+ continue
583
+ if len(results) >= limit:
584
+ truncated = True
585
+ break
586
+ before = lines[max(0, line_no - 1 - context_limit) : line_no - 1]
587
+ after = lines[line_no : line_no + context_limit]
588
+ results.append(
589
+ {
590
+ "path": workspace_relative(item),
591
+ "line": line_no,
592
+ "text": line,
593
+ "snippet": line,
594
+ "context_before": before,
595
+ "context_after": after,
596
+ }
597
+ )
598
+ if truncated:
599
+ break
600
+ payload = _search_payload(query, path, results, truncated, context_lines, "python")
601
+ budgeted = budget_tool_output(
602
+ tool_name="search_workspace_files",
603
+ field_name="results_json",
604
+ value=results,
605
+ metadata={"tool_use_id": "search_workspace_files"},
606
+ )
607
+ if budgeted.get("persisted"):
608
+ payload["results_preview"] = budgeted["results_json"]
609
+ payload["persisted_outputs"] = {"results": budgeted["persisted"]}
610
+ payload["results_truncated"] = budgeted["truncated"]
611
+ payload["results_original_chars"] = budgeted["original_chars"]
612
+ payload["results_preview_chars"] = budgeted["preview_chars"]
613
+ return payload
614
+
615
+
616
+ def _search_with_rg(
617
+ needle: str,
618
+ base: Path,
619
+ is_regex: bool,
620
+ glob: str | None,
621
+ case_sensitive: bool,
622
+ context_lines: int,
623
+ max_results: int,
624
+ ) -> dict[str, Any] | None:
625
+ command = ["rg", "--line-number", "--color", "never", "--no-heading"]
626
+ if not is_regex:
627
+ command.append("--fixed-strings")
628
+ if not case_sensitive:
629
+ command.append("--ignore-case")
630
+ if glob:
631
+ command.extend(["--glob", glob])
632
+ if context_lines:
633
+ command.extend(["--context", str(max(0, min(int(context_lines), 20)))])
634
+ command.extend([needle, str(base)])
635
+ try:
636
+ completed = subprocess.run(command, capture_output=True, text=True, timeout=10, check=False)
637
+ except Exception:
638
+ return None
639
+ if completed.returncode not in {0, 1}:
640
+ return None
641
+ results = []
642
+ limit = max(1, min(int(max_results or 100), 5000))
643
+ truncated = False
644
+ for raw_line in completed.stdout.splitlines():
645
+ separator = ":" if ":" in raw_line else "-"
646
+ parts = raw_line.split(separator, 2)
647
+ if len(parts) < 3 or not parts[1].isdigit():
648
+ continue
649
+ path = Path(parts[0])
650
+ try:
651
+ relative = workspace_relative(path)
652
+ except ValueError:
653
+ continue
654
+ if len(results) >= limit:
655
+ truncated = True
656
+ break
657
+ results.append(
658
+ {
659
+ "path": relative,
660
+ "line": int(parts[1]),
661
+ "text": parts[2],
662
+ "snippet": parts[2],
663
+ "context_before": [],
664
+ "context_after": [],
665
+ }
666
+ )
667
+ return {"ok": True, "query": needle, "results": results, "truncated": truncated}
668
+
669
+
670
+ def _search_payload(
671
+ query: str,
672
+ path: str,
673
+ results: list[dict[str, Any]],
674
+ truncated: bool,
675
+ context_lines: int,
676
+ backend: str,
677
+ ) -> dict[str, Any]:
678
+ return {
679
+ "ok": True,
680
+ "query": query,
681
+ "searched_path": path,
682
+ "results": results,
683
+ "match_count": len(results),
684
+ "truncated": truncated,
685
+ "context_lines": max(0, min(int(context_lines or 0), 20)),
686
+ "search_backend": backend,
687
+ }
688
+
689
+
690
+ def _validate_read_state(target: Path, relative: str) -> dict[str, Any] | None:
691
+ read_state = get_read_state(relative)
692
+ if read_state is None:
693
+ return {
694
+ "ok": False,
695
+ "path": relative,
696
+ "error_type": "file_not_read",
697
+ "error_message": "file must be read before editing",
698
+ "suggested_action": f"Call read_workspace_file(path={relative!r}) before editing.",
699
+ }
700
+ stat = target.stat() if target.exists() else None
701
+ if stat is None or stat.st_mtime_ns != read_state.mtime_ns or stat.st_size != read_state.size_bytes:
702
+ return {
703
+ "ok": False,
704
+ "path": relative,
705
+ "error_type": "file_modified_since_read",
706
+ "error_message": "workspace file changed since last read",
707
+ "suggested_action": f"Re-read the file with read_workspace_file(path={relative!r}) and retry.",
708
+ }
709
+ return None
710
+
711
+
712
+ def _apply_single_edit(
713
+ text: str,
714
+ old_text: Any,
715
+ new_text: Any,
716
+ *,
717
+ expected_replacements: int = 1,
718
+ replace_all: bool = False,
719
+ relative: str,
720
+ ) -> dict[str, Any]:
721
+ old = str(old_text or "")
722
+ if not old:
723
+ return {"ok": False, "path": relative, "error_type": "missing_old_text", "error_message": "old_text is required"}
724
+ expected = max(1, int(expected_replacements or 1))
725
+ actual_old, used_quote_normalization = _find_actual_string(text, old)
726
+ matches = _match_previews(text, actual_old or old) if actual_old else []
727
+ occurrences = text.count(actual_old) if actual_old else 0
728
+ if occurrences == 0:
729
+ return {
730
+ "ok": False,
731
+ "path": relative,
732
+ "error_type": "snippet_not_found",
733
+ "error_message": "old_text was not found in the workspace file",
734
+ "occurrences": 0,
735
+ "nearby_candidates": _nearby_candidates(text, old),
736
+ "suggested_action": "Use one nearby candidate as old_text, or call read_workspace_file with line numbers and retry.",
737
+ }
738
+ if not replace_all and occurrences != expected:
739
+ return {
740
+ "ok": False,
741
+ "path": relative,
742
+ "error_type": "ambiguous_edit",
743
+ "error_message": f"old_text matched {occurrences} occurrences, expected {expected}",
744
+ "occurrences": occurrences,
745
+ "expected_replacements": expected,
746
+ "matches": matches,
747
+ "suggested_action": "Provide a larger unique old_text snippet, set expected_replacements, or use replace_all=true.",
748
+ }
749
+ replacements = occurrences if replace_all else expected
750
+ return {
751
+ "ok": True,
752
+ "updated": text.replace(actual_old, str(new_text or ""), replacements),
753
+ "replacements": replacements,
754
+ "used_quote_normalization": used_quote_normalization,
755
+ "matches": matches,
756
+ }
757
+
758
+
759
+ def _match_previews(text: str, needle: str, limit: int = 20) -> list[dict[str, Any]]:
760
+ if not needle:
761
+ return []
762
+ matches: list[dict[str, Any]] = []
763
+ for line_no, line in enumerate(text.splitlines(), start=1):
764
+ if needle in line:
765
+ matches.append({"line": line_no, "text": line.strip(), "snippet": line})
766
+ if len(matches) >= limit:
767
+ break
768
+ return matches
769
+
770
+
771
+ def _nearby_candidates(text: str, old: str, limit: int = 5) -> list[dict[str, Any]]:
772
+ old_norm = _normalize_quotes(old).strip()
773
+ candidates = []
774
+ for line_no, line in enumerate(text.splitlines(), start=1):
775
+ ratio = difflib.SequenceMatcher(None, old_norm, _normalize_quotes(line).strip()).ratio()
776
+ if old_norm and (old_norm[: max(1, min(4, len(old_norm)))] in _normalize_quotes(line) or ratio > 0.45):
777
+ candidates.append({"line": line_no, "text": line.strip(), "snippet": line, "score": round(ratio, 3)})
778
+ candidates.sort(key=lambda item: (-float(item["score"]), int(item["line"])))
779
+ return candidates[:limit]
780
+
781
+
782
+ def _build_diff(relative: str, before: str, after: str) -> str:
783
+ return "".join(
784
+ difflib.unified_diff(
785
+ before.splitlines(keepends=True),
786
+ after.splitlines(keepends=True),
787
+ fromfile=f"{relative} before",
788
+ tofile=f"{relative} after",
789
+ )
790
+ )
791
+
792
+
793
+ def _budget_diff(tool_name: str, diff: str, relative: str) -> dict[str, Any]:
794
+ return budget_tool_output(
795
+ tool_name=tool_name,
796
+ field_name="diff",
797
+ value=diff,
798
+ metadata={"tool_use_id": f"{tool_name}_{relative.replace('/', '_')}"},
799
+ )
800
+
801
+
802
+ def _refresh_read_state(relative: str, stat: Any, text: str) -> None:
803
+ lines = text.splitlines()
804
+ record_read_state(
805
+ WorkspaceReadState(
806
+ path=relative,
807
+ mtime_ns=stat.st_mtime_ns,
808
+ size_bytes=stat.st_size,
809
+ start_line=1,
810
+ end_line=len(lines),
811
+ total_lines=len(lines),
812
+ partial=False,
813
+ )
814
+ )
815
+
816
+
817
+ def _find_actual_string(text: str, old: str) -> tuple[str | None, bool]:
818
+ if old in text:
819
+ return old, False
820
+ normalized_old = _normalize_quotes(old)
821
+ for candidate in {normalized_old, old.translate(str.maketrans({'"': "“", "'": "‘"}))}:
822
+ if candidate and candidate in text:
823
+ return candidate, True
824
+ normalized_text = _normalize_quotes(text)
825
+ index = normalized_text.find(normalized_old)
826
+ if index < 0:
827
+ return None, False
828
+ return text[index : index + len(old)], True
829
+
830
+
831
+ def _normalize_quotes(value: str) -> str:
832
+ return value.translate(
833
+ str.maketrans(
834
+ {
835
+ "“": '"',
836
+ "”": '"',
837
+ "„": '"',
838
+ "‘": "'",
839
+ "’": "'",
840
+ "‚": "'",
841
+ }
842
+ )
843
+ )
844
+
845
+
846
+ def delete_workspace_file(path: str, approval: dict[str, Any] | None = None) -> dict[str, Any]:
847
+ """Delete a file or empty directory inside the AgentEngine workspace."""
848
+
849
+ return _gateway().invoke("delete_workspace_file", _delete_workspace_file_impl, path, approval=approval)
850
+
851
+
852
+ def _delete_workspace_file_impl(path: str) -> dict[str, Any]:
853
+ target = resolve_workspace_path(path)
854
+ root = workspace_root().resolve()
855
+ if target == root:
856
+ return {"ok": False, "error_message": "refuse to delete workspace root"}
857
+ if not target.exists():
858
+ return {"ok": False, "error_message": f"workspace path not found: {path}"}
859
+ if target.is_dir():
860
+ target.rmdir()
861
+ else:
862
+ target.unlink()
863
+ return {"ok": True, "deleted": path}
864
+
865
+
866
+ def get_workspace_tools() -> list:
867
+ return [
868
+ as_tool(workspace_status),
869
+ as_tool(list_workspace_files),
870
+ as_tool(read_workspace_file),
871
+ as_tool(write_workspace_file),
872
+ as_tool(write_workspace_files),
873
+ as_tool(edit_workspace_file),
874
+ as_tool(multi_edit_workspace_file),
875
+ as_tool(lint_workspace_file),
876
+ as_tool(search_workspace_files),
877
+ as_tool(delete_workspace_file),
878
+ ]